mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-04-10 04:19:25 -07:00
feat(stats): add v1 immersion stats dashboard (#19)
This commit is contained in:
@@ -2,6 +2,7 @@ import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {
|
||||
hasExplicitCommand,
|
||||
isHeadlessInitialCommand,
|
||||
parseArgs,
|
||||
shouldRunSettingsOnlyStartup,
|
||||
shouldStartApp,
|
||||
@@ -101,7 +102,8 @@ test('hasExplicitCommand and shouldStartApp preserve command intent', () => {
|
||||
const refreshKnownWords = parseArgs(['--refresh-known-words']);
|
||||
assert.equal(refreshKnownWords.help, false);
|
||||
assert.equal(hasExplicitCommand(refreshKnownWords), true);
|
||||
assert.equal(shouldStartApp(refreshKnownWords), false);
|
||||
assert.equal(shouldStartApp(refreshKnownWords), true);
|
||||
assert.equal(isHeadlessInitialCommand(refreshKnownWords), true);
|
||||
|
||||
const settings = parseArgs(['--settings']);
|
||||
assert.equal(settings.settings, true);
|
||||
@@ -143,6 +145,50 @@ test('hasExplicitCommand and shouldStartApp preserve command intent', () => {
|
||||
assert.equal(dictionaryTarget.dictionary, true);
|
||||
assert.equal(dictionaryTarget.dictionaryTarget, '/tmp/example.mkv');
|
||||
|
||||
const stats = parseArgs([
|
||||
'--stats',
|
||||
'--stats-response-path',
|
||||
'/tmp/subminer-stats-response.json',
|
||||
'--stats-cleanup-lifetime',
|
||||
]);
|
||||
assert.equal(stats.stats, true);
|
||||
assert.equal(stats.statsResponsePath, '/tmp/subminer-stats-response.json');
|
||||
assert.equal(stats.statsCleanup, false);
|
||||
assert.equal(stats.statsCleanupVocab, false);
|
||||
assert.equal(stats.statsCleanupLifetime, true);
|
||||
assert.equal(hasExplicitCommand(stats), true);
|
||||
assert.equal(shouldStartApp(stats), true);
|
||||
|
||||
const statsBackground = parseArgs(['--stats', '--stats-background']) as typeof stats & {
|
||||
statsBackground?: boolean;
|
||||
statsStop?: boolean;
|
||||
};
|
||||
assert.equal(statsBackground.stats, true);
|
||||
assert.equal(statsBackground.statsBackground, true);
|
||||
assert.equal(statsBackground.statsStop, false);
|
||||
assert.equal(hasExplicitCommand(statsBackground), true);
|
||||
assert.equal(shouldStartApp(statsBackground), true);
|
||||
|
||||
const statsStop = parseArgs(['--stats', '--stats-stop']) as typeof stats & {
|
||||
statsBackground?: boolean;
|
||||
statsStop?: boolean;
|
||||
};
|
||||
assert.equal(statsStop.stats, true);
|
||||
assert.equal(statsStop.statsStop, true);
|
||||
assert.equal(statsStop.statsBackground, false);
|
||||
assert.equal(hasExplicitCommand(statsStop), true);
|
||||
assert.equal(shouldStartApp(statsStop), true);
|
||||
|
||||
const statsLifetimeRebuild = parseArgs([
|
||||
'--stats',
|
||||
'--stats-cleanup',
|
||||
'--stats-cleanup-lifetime',
|
||||
]);
|
||||
assert.equal(statsLifetimeRebuild.stats, true);
|
||||
assert.equal(statsLifetimeRebuild.statsCleanup, true);
|
||||
assert.equal(statsLifetimeRebuild.statsCleanupLifetime, true);
|
||||
assert.equal(statsLifetimeRebuild.statsCleanupVocab, false);
|
||||
|
||||
const jellyfinLibraries = parseArgs(['--jellyfin-libraries']);
|
||||
assert.equal(jellyfinLibraries.jellyfinLibraries, true);
|
||||
assert.equal(hasExplicitCommand(jellyfinLibraries), true);
|
||||
|
||||
@@ -29,6 +29,13 @@ export interface CliArgs {
|
||||
anilistRetryQueue: boolean;
|
||||
dictionary: boolean;
|
||||
dictionaryTarget?: string;
|
||||
stats: boolean;
|
||||
statsBackground?: boolean;
|
||||
statsStop?: boolean;
|
||||
statsCleanup?: boolean;
|
||||
statsCleanupVocab?: boolean;
|
||||
statsCleanupLifetime?: boolean;
|
||||
statsResponsePath?: string;
|
||||
jellyfin: boolean;
|
||||
jellyfinLogin: boolean;
|
||||
jellyfinLogout: boolean;
|
||||
@@ -97,6 +104,12 @@ export function parseArgs(argv: string[]): CliArgs {
|
||||
anilistSetup: false,
|
||||
anilistRetryQueue: false,
|
||||
dictionary: false,
|
||||
stats: false,
|
||||
statsBackground: false,
|
||||
statsStop: false,
|
||||
statsCleanup: false,
|
||||
statsCleanupVocab: false,
|
||||
statsCleanupLifetime: false,
|
||||
jellyfin: false,
|
||||
jellyfinLogin: false,
|
||||
jellyfinLogout: false,
|
||||
@@ -162,6 +175,22 @@ export function parseArgs(argv: string[]): CliArgs {
|
||||
} else if (arg === '--dictionary-target') {
|
||||
const value = readValue(argv[i + 1]);
|
||||
if (value) args.dictionaryTarget = value;
|
||||
} else if (arg === '--stats') args.stats = true;
|
||||
else if (arg === '--stats-background') {
|
||||
args.stats = true;
|
||||
args.statsBackground = true;
|
||||
} else if (arg === '--stats-stop') {
|
||||
args.stats = true;
|
||||
args.statsStop = true;
|
||||
} else if (arg === '--stats-cleanup') args.statsCleanup = true;
|
||||
else if (arg === '--stats-cleanup-vocab') args.statsCleanupVocab = true;
|
||||
else if (arg === '--stats-cleanup-lifetime') args.statsCleanupLifetime = true;
|
||||
else if (arg.startsWith('--stats-response-path=')) {
|
||||
const value = arg.split('=', 2)[1];
|
||||
if (value) args.statsResponsePath = value;
|
||||
} else if (arg === '--stats-response-path') {
|
||||
const value = readValue(argv[i + 1]);
|
||||
if (value) args.statsResponsePath = value;
|
||||
} else if (arg === '--jellyfin') args.jellyfin = true;
|
||||
else if (arg === '--jellyfin-login') args.jellyfinLogin = true;
|
||||
else if (arg === '--jellyfin-logout') args.jellyfinLogout = true;
|
||||
@@ -331,6 +360,7 @@ export function hasExplicitCommand(args: CliArgs): boolean {
|
||||
args.anilistSetup ||
|
||||
args.anilistRetryQueue ||
|
||||
args.dictionary ||
|
||||
args.stats ||
|
||||
args.jellyfin ||
|
||||
args.jellyfinLogin ||
|
||||
args.jellyfinLogout ||
|
||||
@@ -346,6 +376,10 @@ export function hasExplicitCommand(args: CliArgs): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
export function isHeadlessInitialCommand(args: CliArgs): boolean {
|
||||
return args.refreshKnownWords;
|
||||
}
|
||||
|
||||
export function shouldStartApp(args: CliArgs): boolean {
|
||||
if (args.stop && !args.start) return false;
|
||||
if (
|
||||
@@ -361,12 +395,14 @@ export function shouldStartApp(args: CliArgs): boolean {
|
||||
args.mineSentence ||
|
||||
args.mineSentenceMultiple ||
|
||||
args.updateLastCardFromClipboard ||
|
||||
args.refreshKnownWords ||
|
||||
args.toggleSecondarySub ||
|
||||
args.triggerFieldGrouping ||
|
||||
args.triggerSubsync ||
|
||||
args.markAudioCard ||
|
||||
args.openRuntimeOptions ||
|
||||
args.dictionary ||
|
||||
args.stats ||
|
||||
args.jellyfin ||
|
||||
args.jellyfinPlay ||
|
||||
args.texthooker
|
||||
@@ -408,6 +444,7 @@ export function shouldRunSettingsOnlyStartup(args: CliArgs): boolean {
|
||||
!args.anilistSetup &&
|
||||
!args.anilistRetryQueue &&
|
||||
!args.dictionary &&
|
||||
!args.stats &&
|
||||
!args.jellyfin &&
|
||||
!args.jellyfinLogin &&
|
||||
!args.jellyfinLogout &&
|
||||
|
||||
@@ -18,7 +18,8 @@ test('printHelp includes configured texthooker port', () => {
|
||||
assert.match(output, /--help\s+Show this help/);
|
||||
assert.match(output, /default: 7777/);
|
||||
assert.match(output, /--launch-mpv/);
|
||||
assert.match(output, /--refresh-known-words/);
|
||||
assert.match(output, /--stats\s+Open the stats dashboard in your browser/);
|
||||
assert.doesNotMatch(output, /--refresh-known-words/);
|
||||
assert.match(output, /--setup\s+Open first-run setup window/);
|
||||
assert.match(output, /--anilist-status/);
|
||||
assert.match(output, /--anilist-retry-queue/);
|
||||
|
||||
@@ -14,6 +14,7 @@ ${B}Session${R}
|
||||
--start Connect to mpv and launch overlay
|
||||
--launch-mpv ${D}[targets...]${R} Launch mpv with the SubMiner mpv profile and exit
|
||||
--stop Stop the running instance
|
||||
--stats Open the stats dashboard in your browser
|
||||
--texthooker Start texthooker server only ${D}(no overlay)${R}
|
||||
|
||||
${B}Overlay${R}
|
||||
@@ -34,7 +35,6 @@ ${B}Mining${R}
|
||||
--trigger-field-grouping Run Kiku field grouping
|
||||
--trigger-subsync Run subtitle sync
|
||||
--toggle-secondary-sub Cycle secondary subtitle mode
|
||||
--refresh-known-words Refresh known words cache
|
||||
--open-runtime-options Open runtime options palette
|
||||
|
||||
${B}AniList${R}
|
||||
|
||||
Reference in New Issue
Block a user