Overlay 2.0 (#12)

This commit is contained in:
2026-03-01 02:36:51 -08:00
committed by GitHub
parent 45df3c466b
commit 44c7761c7c
397 changed files with 15139 additions and 7127 deletions

View File

@@ -42,11 +42,13 @@ export function composeAppReadyRuntime(options: AppReadyComposerOptions): AppRea
createBuildAppReadyRuntimeMainDepsHandler({
...options.appReadyRuntimeMainDeps,
reloadConfig,
createImmersionTracker: createImmersionTrackerStartupHandler(
createBuildImmersionTrackerStartupMainDepsHandler(
options.immersionTrackerStartupMainDeps,
)(),
),
createImmersionTracker:
options.appReadyRuntimeMainDeps.createImmersionTracker ??
createImmersionTrackerStartupHandler(
createBuildImmersionTrackerStartupMainDepsHandler(
options.immersionTrackerStartupMainDeps,
)(),
),
onCriticalConfigErrors: criticalConfigError,
})(),
);

View File

@@ -32,10 +32,8 @@ test('composeIpcRuntimeHandlers returns callable IPC handlers and registration b
showMpvOsd: () => {},
},
mainDeps: {
getInvisibleWindow: () => null,
getMainWindow: () => null,
getVisibleOverlayVisibility: () => false,
getInvisibleOverlayVisibility: () => false,
focusMainWindow: () => {},
onOverlayModalClosed: () => {},
openYomitanSettings: () => {},
@@ -44,7 +42,7 @@ test('composeIpcRuntimeHandlers returns callable IPC handlers and registration b
tokenizeCurrentSubtitle: async () => null,
getCurrentSubtitleRaw: () => '',
getCurrentSubtitleAss: () => '',
getMpvSubtitleRenderMetrics: () => ({}) as never,
getPlaybackPaused: () => null,
getSubtitlePosition: () => ({}) as never,
getSubtitleStyle: () => ({}) as never,
saveSubtitlePosition: () => {},
@@ -56,7 +54,6 @@ test('composeIpcRuntimeHandlers returns callable IPC handlers and registration b
getAnkiConnectStatus: () => false,
getRuntimeOptions: () => [],
reportOverlayContentBounds: () => {},
reportHoveredSubtitleToken: () => {},
getAnilistStatus: () => ({}) as never,
clearAnilistToken: () => {},
openAnilistSetup: () => {},

View File

@@ -6,7 +6,8 @@ test('composeJellyfinRemoteHandlers returns callable jellyfin remote handlers',
let lastProgressAt = 0;
const composed = composeJellyfinRemoteHandlers({
getConfiguredSession: () => null,
getClientInfo: () => ({ clientName: 'SubMiner', clientVersion: 'test', deviceId: 'dev' }) as never,
getClientInfo: () =>
({ clientName: 'SubMiner', clientVersion: 'test', deviceId: 'dev' }) as never,
getJellyfinConfig: () => ({ enabled: false }) as never,
playJellyfinItem: async () => {},
logWarn: () => {},

View File

@@ -26,6 +26,7 @@ test('composeMpvRuntimeHandlers returns callable handlers and forwards to inject
const calls: string[] = [];
let started = false;
let metrics = BASE_METRICS;
let mecabTokenizer: { id: string } | null = null;
class FakeMpvClient {
connected = false;
@@ -68,12 +69,15 @@ test('composeMpvRuntimeHandlers returns callable handlers and forwards to inject
scheduleQuitCheck: () => {},
quitApp: () => {},
reportJellyfinRemoteStopped: () => {},
syncOverlayMpvSubtitleSuppression: () => {},
maybeRunAnilistPostWatchUpdate: async () => {},
logSubtitleTimingError: () => {},
broadcastToOverlayWindows: () => {},
onSubtitleChange: () => {},
refreshDiscordPresence: () => {},
ensureImmersionTrackerInitialized: () => {},
updateCurrentMediaPath: () => {},
restoreMpvSubVisibility: () => {},
getCurrentAnilistMediaKey: () => null,
resetAnilistMediaTracking: () => {},
maybeProbeAnilistDuration: () => {},
@@ -90,7 +94,6 @@ test('composeMpvRuntimeHandlers returns callable handlers and forwards to inject
getResolvedConfig: () => ({ auto_start_overlay: false }),
isAutoStartOverlayEnabled: () => true,
setOverlayVisible: () => {},
shouldBindVisibleOverlayToMpvSubVisibility: () => true,
isVisibleOverlayVisible: () => false,
getReconnectTimer: () => null,
setReconnectTimer: () => {},
@@ -125,6 +128,7 @@ test('composeMpvRuntimeHandlers returns callable handlers and forwards to inject
getJlptLevel: () => null,
getJlptEnabled: () => true,
getFrequencyDictionaryEnabled: () => true,
getFrequencyDictionaryMatchMode: () => 'headword',
getFrequencyRank: () => null,
getYomitanGroupDebugEnabled: () => false,
getMecabTokenizer: () => null,
@@ -139,9 +143,15 @@ test('composeMpvRuntimeHandlers returns callable handlers and forwards to inject
return { text };
},
createMecabTokenizerAndCheckMainDeps: {
getMecabTokenizer: () => ({ id: 'mecab' }),
setMecabTokenizer: () => {},
createMecabTokenizer: () => ({ id: 'mecab' }),
getMecabTokenizer: () => mecabTokenizer,
setMecabTokenizer: (next) => {
mecabTokenizer = next as { id: string };
calls.push('set-mecab');
},
createMecabTokenizer: () => {
calls.push('create-mecab');
return { id: 'mecab' };
},
checkAvailability: async () => {
calls.push('check-mecab');
},
@@ -175,6 +185,10 @@ test('composeMpvRuntimeHandlers returns callable handlers and forwards to inject
ensureYomitanExtensionLoaded: async () => {
calls.push('warmup-yomitan');
},
shouldWarmupMecab: () => true,
shouldWarmupYomitanExtension: () => true,
shouldWarmupSubtitleDictionaries: () => true,
shouldWarmupJellyfinRemoteSession: () => true,
shouldAutoConnectJellyfinRemote: () => false,
startJellyfinRemoteSession: async () => {
calls.push('warmup-jellyfin');
@@ -189,6 +203,7 @@ test('composeMpvRuntimeHandlers returns callable handlers and forwards to inject
assert.equal(typeof composed.tokenizeSubtitle, 'function');
assert.equal(typeof composed.createMecabTokenizerAndCheck, 'function');
assert.equal(typeof composed.prewarmSubtitleDictionaries, 'function');
assert.equal(typeof composed.startTokenizationWarmups, 'function');
assert.equal(typeof composed.launchBackgroundWarmupTask, 'function');
assert.equal(typeof composed.startBackgroundWarmups, 'function');
@@ -196,6 +211,7 @@ test('composeMpvRuntimeHandlers returns callable handlers and forwards to inject
assert.equal(client.connected, true);
composed.updateMpvSubtitleRenderMetrics({ subPos: 90 });
await composed.startTokenizationWarmups();
const tokenized = await composed.tokenizeSubtitle('subtitle text');
await composed.createMecabTokenizerAndCheck();
await composed.prewarmSubtitleDictionaries();
@@ -211,9 +227,12 @@ test('composeMpvRuntimeHandlers returns callable handlers and forwards to inject
assert.ok(calls.includes('broadcast-metrics'));
assert.ok(calls.includes('create-tokenizer-runtime-deps'));
assert.ok(calls.includes('tokenize:subtitle text'));
assert.ok(calls.includes('create-mecab'));
assert.ok(calls.includes('set-mecab'));
assert.ok(calls.includes('check-mecab'));
assert.ok(calls.includes('prewarm-jlpt'));
assert.ok(calls.includes('prewarm-frequency'));
assert.ok(calls.includes('set-started:true'));
assert.ok(calls.includes('warmup-yomitan'));
assert.ok(calls.indexOf('create-mecab') < calls.indexOf('set-started:true'));
});

View File

@@ -87,6 +87,7 @@ export type MpvRuntimeComposerResult<
tokenizeSubtitle: (text: string) => Promise<TTokenizedSubtitle>;
createMecabTokenizerAndCheck: () => Promise<void>;
prewarmSubtitleDictionaries: () => Promise<void>;
startTokenizationWarmups: () => Promise<void>;
launchBackgroundWarmupTask: ReturnType<typeof createLaunchBackgroundWarmupTaskFromStartup>;
startBackgroundWarmups: ReturnType<typeof createStartBackgroundWarmupsFromStartup>;
}>;
@@ -132,8 +133,23 @@ export function composeMpvRuntimeHandlers<
const prewarmSubtitleDictionaries = createPrewarmSubtitleDictionariesMainHandler(
options.tokenizer.prewarmSubtitleDictionariesMainDeps,
);
let tokenizationWarmupInFlight: Promise<void> | null = null;
const startTokenizationWarmups = (): Promise<void> => {
if (!tokenizationWarmupInFlight) {
tokenizationWarmupInFlight = (async () => {
await options.warmups.startBackgroundWarmupsMainDeps.ensureYomitanExtensionLoaded();
if (!options.tokenizer.createMecabTokenizerAndCheckMainDeps.getMecabTokenizer()) {
await createMecabTokenizerAndCheck().catch(() => {});
}
await prewarmSubtitleDictionaries({ showLoadingOsd: true });
})().finally(() => {
tokenizationWarmupInFlight = null;
});
}
return tokenizationWarmupInFlight;
};
const tokenizeSubtitle = async (text: string): Promise<TTokenizedSubtitle> => {
await prewarmSubtitleDictionaries();
await startTokenizationWarmups();
return options.tokenizer.tokenizeSubtitle(
text,
options.tokenizer.createTokenizerRuntimeDeps(buildTokenizerDepsHandler()),
@@ -161,6 +177,7 @@ export function composeMpvRuntimeHandlers<
tokenizeSubtitle,
createMecabTokenizerAndCheck: () => createMecabTokenizerAndCheck(),
prewarmSubtitleDictionaries: () => prewarmSubtitleDictionaries(),
startTokenizationWarmups,
launchBackgroundWarmupTask: (label, task) => launchBackgroundWarmupTask(label, task),
startBackgroundWarmups: () => startBackgroundWarmups(),
};

View File

@@ -14,7 +14,6 @@ test('composeShortcutRuntimes returns callable shortcut runtime handlers', () =>
getConfiguredShortcuts: () => ({}) as never,
registerGlobalShortcutsCore: () => {},
toggleVisibleOverlay: () => {},
toggleInvisibleOverlay: () => {},
openYomitanSettings: () => {},
isDev: false,
getMainWindow: () => null,

View File

@@ -16,6 +16,7 @@ test('composeStartupLifecycleHandlers returns callable startup lifecycle handler
destroyTray: () => {},
stopConfigHotReload: () => {},
restorePreviousSecondarySubVisibility: () => {},
restoreMpvSubVisibility: () => {},
unregisterAllGlobalShortcuts: () => {},
stopSubtitleWebsocket: () => {},
stopTexthookerService: () => {},
@@ -43,9 +44,8 @@ test('composeStartupLifecycleHandlers returns callable startup lifecycle handler
},
restoreWindowsOnActivateMainDeps: {
createMainWindow: () => {},
createInvisibleWindow: () => {},
updateVisibleOverlayVisibility: () => {},
updateInvisibleOverlayVisibility: () => {},
syncOverlayMpvSubtitleSuppression: () => {},
},
});