fix: delay youtube overlay bootstrap until yomitan loads

This commit is contained in:
2026-03-25 20:36:50 -07:00
parent 55300e2d8c
commit 9dca83acd9
16 changed files with 143 additions and 24 deletions

View File

@@ -7,14 +7,14 @@ test('cli prechecks main deps builder maps transition handlers', () => {
const deps = createBuildHandleTexthookerOnlyModeTransitionMainDepsHandler({
isTexthookerOnlyMode: () => true,
setTexthookerOnlyMode: (enabled) => calls.push(`set:${enabled}`),
commandNeedsOverlayRuntime: () => true,
commandNeedsOverlayStartupPrereqs: () => true,
ensureOverlayStartupPrereqs: () => calls.push('prereqs'),
startBackgroundWarmups: () => calls.push('warmups'),
logInfo: (message) => calls.push(`info:${message}`),
})();
assert.equal(deps.isTexthookerOnlyMode(), true);
assert.equal(deps.commandNeedsOverlayRuntime({} as never), true);
assert.equal(deps.commandNeedsOverlayStartupPrereqs({} as never), true);
deps.setTexthookerOnlyMode(false);
deps.ensureOverlayStartupPrereqs();
deps.startBackgroundWarmups();

View File

@@ -3,7 +3,7 @@ import type { CliArgs } from '../../cli/args';
export function createBuildHandleTexthookerOnlyModeTransitionMainDepsHandler(deps: {
isTexthookerOnlyMode: () => boolean;
setTexthookerOnlyMode: (enabled: boolean) => void;
commandNeedsOverlayRuntime: (args: CliArgs) => boolean;
commandNeedsOverlayStartupPrereqs: (args: CliArgs) => boolean;
ensureOverlayStartupPrereqs: () => void;
startBackgroundWarmups: () => void;
logInfo: (message: string) => void;
@@ -11,7 +11,8 @@ export function createBuildHandleTexthookerOnlyModeTransitionMainDepsHandler(dep
return () => ({
isTexthookerOnlyMode: () => deps.isTexthookerOnlyMode(),
setTexthookerOnlyMode: (enabled: boolean) => deps.setTexthookerOnlyMode(enabled),
commandNeedsOverlayRuntime: (args: CliArgs) => deps.commandNeedsOverlayRuntime(args),
commandNeedsOverlayStartupPrereqs: (args: CliArgs) =>
deps.commandNeedsOverlayStartupPrereqs(args),
ensureOverlayStartupPrereqs: () => deps.ensureOverlayStartupPrereqs(),
startBackgroundWarmups: () => deps.startBackgroundWarmups(),
logInfo: (message: string) => deps.logInfo(message),

View File

@@ -7,7 +7,7 @@ test('texthooker precheck no-ops when mode is disabled', () => {
const handlePrecheck = createHandleTexthookerOnlyModeTransitionHandler({
isTexthookerOnlyMode: () => false,
setTexthookerOnlyMode: () => {},
commandNeedsOverlayRuntime: () => true,
commandNeedsOverlayStartupPrereqs: () => true,
ensureOverlayStartupPrereqs: () => {},
startBackgroundWarmups: () => {
warmups += 1;
@@ -29,7 +29,7 @@ test('texthooker precheck disables mode and warms up on start command', () => {
setTexthookerOnlyMode: (enabled) => {
mode = enabled;
},
commandNeedsOverlayRuntime: () => false,
commandNeedsOverlayStartupPrereqs: () => false,
ensureOverlayStartupPrereqs: () => {
prereqs += 1;
},
@@ -55,7 +55,7 @@ test('texthooker precheck no-ops for texthooker command', () => {
setTexthookerOnlyMode: (enabled) => {
mode = enabled;
},
commandNeedsOverlayRuntime: () => true,
commandNeedsOverlayStartupPrereqs: () => true,
ensureOverlayStartupPrereqs: () => {},
startBackgroundWarmups: () => {},
logInfo: () => {},
@@ -64,3 +64,25 @@ test('texthooker precheck no-ops for texthooker command', () => {
handlePrecheck({ start: true, texthooker: true } as never);
assert.equal(mode, true);
});
test('texthooker precheck transitions for youtube playback startup prereqs', () => {
let mode = true;
let prereqs = 0;
const handlePrecheck = createHandleTexthookerOnlyModeTransitionHandler({
isTexthookerOnlyMode: () => mode,
setTexthookerOnlyMode: (enabled) => {
mode = enabled;
},
commandNeedsOverlayStartupPrereqs: () => true,
ensureOverlayStartupPrereqs: () => {
prereqs += 1;
},
startBackgroundWarmups: () => {},
logInfo: () => {},
});
handlePrecheck({ youtubePlay: 'https://youtube.com/watch?v=abc', texthooker: false } as never);
assert.equal(mode, false);
assert.equal(prereqs, 1);
});

View File

@@ -3,7 +3,7 @@ import type { CliArgs } from '../../cli/args';
export function createHandleTexthookerOnlyModeTransitionHandler(deps: {
isTexthookerOnlyMode: () => boolean;
setTexthookerOnlyMode: (enabled: boolean) => void;
commandNeedsOverlayRuntime: (args: CliArgs) => boolean;
commandNeedsOverlayStartupPrereqs: (args: CliArgs) => boolean;
ensureOverlayStartupPrereqs: () => void;
startBackgroundWarmups: () => void;
logInfo: (message: string) => void;
@@ -12,7 +12,7 @@ export function createHandleTexthookerOnlyModeTransitionHandler(deps: {
if (
deps.isTexthookerOnlyMode() &&
!args.texthooker &&
(args.start || deps.commandNeedsOverlayRuntime(args))
(args.start || deps.commandNeedsOverlayStartupPrereqs(args))
) {
deps.ensureOverlayStartupPrereqs();
deps.setTexthookerOnlyMode(false);

View File

@@ -8,7 +8,7 @@ test('cli command runtime handler applies precheck and forwards command with con
handleTexthookerOnlyModeTransitionMainDeps: {
isTexthookerOnlyMode: () => true,
setTexthookerOnlyMode: () => calls.push('set-mode'),
commandNeedsOverlayRuntime: () => true,
commandNeedsOverlayStartupPrereqs: () => true,
ensureOverlayStartupPrereqs: () => calls.push('prereqs'),
startBackgroundWarmups: () => calls.push('warmups'),
logInfo: (message) => calls.push(`log:${message}`),
@@ -40,7 +40,7 @@ test('cli command runtime handler prepares overlay prerequisites before overlay
handleTexthookerOnlyModeTransitionMainDeps: {
isTexthookerOnlyMode: () => false,
setTexthookerOnlyMode: () => calls.push('set-mode'),
commandNeedsOverlayRuntime: () => true,
commandNeedsOverlayStartupPrereqs: () => true,
ensureOverlayStartupPrereqs: () => calls.push('prereqs'),
startBackgroundWarmups: () => calls.push('warmups'),
logInfo: (message) => calls.push(`log:${message}`),
@@ -58,3 +58,28 @@ test('cli command runtime handler prepares overlay prerequisites before overlay
assert.deepEqual(calls, ['prereqs', 'context', 'cli:initial:ctx']);
});
test('cli command runtime handler skips generic overlay prerequisites for youtube playback', () => {
const calls: string[] = [];
const handler = createCliCommandRuntimeHandler({
handleTexthookerOnlyModeTransitionMainDeps: {
isTexthookerOnlyMode: () => false,
setTexthookerOnlyMode: () => calls.push('set-mode'),
commandNeedsOverlayStartupPrereqs: () => false,
ensureOverlayStartupPrereqs: () => calls.push('prereqs'),
startBackgroundWarmups: () => calls.push('warmups'),
logInfo: (message) => calls.push(`log:${message}`),
},
createCliCommandContext: () => {
calls.push('context');
return { id: 'ctx' };
},
handleCliCommandRuntimeServiceWithContext: (_args, source, context) => {
calls.push(`cli:${source}:${context.id}`);
},
});
handler({ youtubePlay: 'https://youtube.com/watch?v=abc' } as never);
assert.deepEqual(calls, ['context', 'cli:initial:ctx']);
});

View File

@@ -25,7 +25,7 @@ export function createCliCommandRuntimeHandler<TCliContext>(deps: {
handleTexthookerOnlyModeTransitionHandler(args);
if (
!deps.handleTexthookerOnlyModeTransitionMainDeps.isTexthookerOnlyMode() &&
deps.handleTexthookerOnlyModeTransitionMainDeps.commandNeedsOverlayRuntime(args)
deps.handleTexthookerOnlyModeTransitionMainDeps.commandNeedsOverlayStartupPrereqs(args)
) {
deps.handleTexthookerOnlyModeTransitionMainDeps.ensureOverlayStartupPrereqs();
}

View File

@@ -13,6 +13,7 @@ test('initial args handler no-ops without initial args', () => {
isTexthookerOnlyMode: () => false,
hasImmersionTracker: () => false,
getMpvClient: () => null,
commandNeedsOverlayStartupPrereqs: () => false,
commandNeedsOverlayRuntime: () => false,
ensureOverlayStartupPrereqs: () => {},
isOverlayRuntimeInitialized: () => false,
@@ -40,6 +41,7 @@ test('initial args handler ensures tray in background mode', () => {
isTexthookerOnlyMode: () => true,
hasImmersionTracker: () => false,
getMpvClient: () => null,
commandNeedsOverlayStartupPrereqs: () => false,
commandNeedsOverlayRuntime: () => false,
ensureOverlayStartupPrereqs: () => {},
isOverlayRuntimeInitialized: () => false,
@@ -69,6 +71,7 @@ test('initial args handler auto-connects mpv when needed', () => {
connectCalls += 1;
},
}),
commandNeedsOverlayStartupPrereqs: () => false,
commandNeedsOverlayRuntime: () => false,
ensureOverlayStartupPrereqs: () => {},
isOverlayRuntimeInitialized: () => false,
@@ -95,6 +98,7 @@ test('initial args handler forwards args to cli handler', () => {
isTexthookerOnlyMode: () => false,
hasImmersionTracker: () => false,
getMpvClient: () => null,
commandNeedsOverlayStartupPrereqs: () => false,
commandNeedsOverlayRuntime: () => false,
ensureOverlayStartupPrereqs: () => {
seenSources.push('prereqs');
@@ -125,6 +129,7 @@ test('initial args handler bootstraps overlay before initial overlay-runtime com
isTexthookerOnlyMode: () => false,
hasImmersionTracker: () => false,
getMpvClient: () => null,
commandNeedsOverlayStartupPrereqs: (inputArgs) => inputArgs === args,
commandNeedsOverlayRuntime: (inputArgs) => inputArgs === args,
ensureOverlayStartupPrereqs: () => {
calls.push('prereqs');
@@ -144,6 +149,38 @@ test('initial args handler bootstraps overlay before initial overlay-runtime com
assert.deepEqual(calls, ['prereqs', 'init-overlay', 'cli:initial']);
});
test('initial args handler prepares prereqs but skips eager overlay bootstrap for youtube playback', () => {
const calls: string[] = [];
const args = { youtubePlay: 'https://youtube.com/watch?v=abc' } as never;
const handleInitialArgs = createHandleInitialArgsHandler({
getInitialArgs: () => args,
isBackgroundMode: () => false,
shouldEnsureTrayOnStartup: () => false,
shouldRunHeadlessInitialCommand: () => false,
ensureTray: () => {},
isTexthookerOnlyMode: () => false,
hasImmersionTracker: () => false,
getMpvClient: () => null,
commandNeedsOverlayStartupPrereqs: () => true,
commandNeedsOverlayRuntime: () => false,
ensureOverlayStartupPrereqs: () => {
calls.push('prereqs');
},
isOverlayRuntimeInitialized: () => false,
initializeOverlayRuntime: () => {
calls.push('init-overlay');
},
logInfo: () => {},
handleCliCommand: (_args, source) => {
calls.push(`cli:${source}`);
},
});
handleInitialArgs();
assert.deepEqual(calls, ['prereqs', 'cli:initial']);
});
test('initial args handler can ensure tray outside background mode when requested', () => {
let ensuredTray = false;
const handleInitialArgs = createHandleInitialArgsHandler({
@@ -157,6 +194,7 @@ test('initial args handler can ensure tray outside background mode when requeste
isTexthookerOnlyMode: () => true,
hasImmersionTracker: () => false,
getMpvClient: () => null,
commandNeedsOverlayStartupPrereqs: () => false,
commandNeedsOverlayRuntime: () => false,
ensureOverlayStartupPrereqs: () => {},
isOverlayRuntimeInitialized: () => false,
@@ -188,6 +226,7 @@ test('initial args handler skips tray and mpv auto-connect for headless refresh'
connectCalls += 1;
},
}),
commandNeedsOverlayStartupPrereqs: () => true,
commandNeedsOverlayRuntime: () => true,
ensureOverlayStartupPrereqs: () => {},
isOverlayRuntimeInitialized: () => false,

View File

@@ -14,6 +14,7 @@ export function createHandleInitialArgsHandler(deps: {
isTexthookerOnlyMode: () => boolean;
hasImmersionTracker: () => boolean;
getMpvClient: () => MpvClientLike | null;
commandNeedsOverlayStartupPrereqs: (args: CliArgs) => boolean;
commandNeedsOverlayRuntime: (args: CliArgs) => boolean;
ensureOverlayStartupPrereqs: () => void;
isOverlayRuntimeInitialized: () => boolean;
@@ -43,8 +44,10 @@ export function createHandleInitialArgsHandler(deps: {
mpvClient.connect();
}
if (!runHeadless && deps.commandNeedsOverlayRuntime(initialArgs)) {
if (!runHeadless && deps.commandNeedsOverlayStartupPrereqs(initialArgs)) {
deps.ensureOverlayStartupPrereqs();
}
if (!runHeadless && deps.commandNeedsOverlayRuntime(initialArgs)) {
if (!deps.isOverlayRuntimeInitialized()) {
deps.initializeOverlayRuntime();
}

View File

@@ -15,6 +15,7 @@ test('initial args main deps builder maps runtime callbacks and state readers',
isTexthookerOnlyMode: () => false,
hasImmersionTracker: () => true,
getMpvClient: () => mpvClient,
commandNeedsOverlayStartupPrereqs: () => true,
commandNeedsOverlayRuntime: () => true,
ensureOverlayStartupPrereqs: () => calls.push('prereqs'),
isOverlayRuntimeInitialized: () => false,
@@ -30,6 +31,7 @@ test('initial args main deps builder maps runtime callbacks and state readers',
assert.equal(deps.isTexthookerOnlyMode(), false);
assert.equal(deps.hasImmersionTracker(), true);
assert.equal(deps.getMpvClient(), mpvClient);
assert.equal(deps.commandNeedsOverlayStartupPrereqs(args), true);
assert.equal(deps.commandNeedsOverlayRuntime(args), true);
assert.equal(deps.isOverlayRuntimeInitialized(), false);

View File

@@ -9,6 +9,7 @@ export function createBuildHandleInitialArgsMainDepsHandler(deps: {
isTexthookerOnlyMode: () => boolean;
hasImmersionTracker: () => boolean;
getMpvClient: () => { connected: boolean; connect: () => void } | null;
commandNeedsOverlayStartupPrereqs: (args: CliArgs) => boolean;
commandNeedsOverlayRuntime: (args: CliArgs) => boolean;
ensureOverlayStartupPrereqs: () => void;
isOverlayRuntimeInitialized: () => boolean;
@@ -25,6 +26,8 @@ export function createBuildHandleInitialArgsMainDepsHandler(deps: {
isTexthookerOnlyMode: () => deps.isTexthookerOnlyMode(),
hasImmersionTracker: () => deps.hasImmersionTracker(),
getMpvClient: () => deps.getMpvClient(),
commandNeedsOverlayStartupPrereqs: (args: CliArgs) =>
deps.commandNeedsOverlayStartupPrereqs(args),
commandNeedsOverlayRuntime: (args: CliArgs) => deps.commandNeedsOverlayRuntime(args),
ensureOverlayStartupPrereqs: () => deps.ensureOverlayStartupPrereqs(),
isOverlayRuntimeInitialized: () => deps.isOverlayRuntimeInitialized(),

View File

@@ -16,6 +16,7 @@ test('initial args runtime handler composes main deps and runs initial command f
connected: false,
connect: () => calls.push('connect'),
}),
commandNeedsOverlayStartupPrereqs: () => false,
commandNeedsOverlayRuntime: () => false,
ensureOverlayStartupPrereqs: () => calls.push('prereqs'),
isOverlayRuntimeInitialized: () => false,
@@ -48,6 +49,7 @@ test('initial args runtime handler skips mpv auto-connect for stats mode', () =>
connected: false,
connect: () => calls.push('connect'),
}),
commandNeedsOverlayStartupPrereqs: () => false,
commandNeedsOverlayRuntime: () => false,
ensureOverlayStartupPrereqs: () => calls.push('prereqs'),
isOverlayRuntimeInitialized: () => false,
@@ -75,6 +77,7 @@ test('initial args runtime handler skips tray and mpv auto-connect for headless
connected: false,
connect: () => calls.push('connect'),
}),
commandNeedsOverlayStartupPrereqs: () => true,
commandNeedsOverlayRuntime: () => true,
ensureOverlayStartupPrereqs: () => calls.push('prereqs'),
isOverlayRuntimeInitialized: () => false,