From e487998c40ee3c6742d88acbf0f8ab2f8bb440a7 Mon Sep 17 00:00:00 2001 From: sudacode Date: Mon, 23 Mar 2026 19:18:04 -0700 Subject: [PATCH] refactor: remove legacy youtube launcher mode plumbing --- launcher/config.test.ts | 1 - launcher/config/args-normalizer.ts | 24 ------- launcher/config/cli-parser-builder.ts | 55 ---------------- launcher/main.test.ts | 15 +++-- launcher/parse-args.test.ts | 7 -- src/cli/help.ts | 2 - .../cli-command-runtime-handler.test.ts | 2 +- .../composers/mpv-runtime-composer.test.ts | 6 -- src/main/runtime/initial-args-handler.test.ts | 2 +- src/main/runtime/mpv-main-event-actions.ts | 13 ---- .../runtime/mpv-main-event-bindings.test.ts | 66 ------------------- src/main/runtime/mpv-main-event-bindings.ts | 4 -- .../runtime/mpv-main-event-main-deps.test.ts | 8 +-- src/main/runtime/mpv-main-event-main-deps.ts | 2 - src/main/runtime/youtube-picker-open.test.ts | 1 - src/main/state.test.ts | 6 +- src/main/state.ts | 3 - .../modals/youtube-track-picker.test.ts | 7 -- src/renderer/modals/youtube-track-picker.ts | 2 +- src/types.ts | 2 - 20 files changed, 18 insertions(+), 210 deletions(-) diff --git a/launcher/config.test.ts b/launcher/config.test.ts index 0af357a..2962e55 100644 --- a/launcher/config.test.ts +++ b/launcher/config.test.ts @@ -10,7 +10,6 @@ test('launcher root help lists subcommands', () => { assert.match(output, /Commands:/); assert.match(output, /jellyfin\|jf/); - assert.match(output, /yt\|youtube/); assert.match(output, /doctor/); assert.match(output, /config/); assert.match(output, /mpv/); diff --git a/launcher/config/args-normalizer.ts b/launcher/config/args-normalizer.ts index 03333e7..91aadd7 100644 --- a/launcher/config/args-normalizer.ts +++ b/launcher/config/args-normalizer.ts @@ -111,7 +111,6 @@ export function createDefaultArgs(launcherConfig: LauncherYoutubeSubgenConfig): youtubeSubgenAudioFormat: process.env.SUBMINER_YT_SUBGEN_AUDIO_FORMAT || 'm4a', youtubeSubgenKeepTemp: process.env.SUBMINER_YT_SUBGEN_KEEP_TEMP === '1', youtubeFixWithAi: launcherConfig.fixWithAi === true, - youtubeMode: undefined, jimakuApiKey: process.env.SUBMINER_JIMAKU_API_KEY || '', jimakuApiKeyCommand: process.env.SUBMINER_JIMAKU_API_KEY_COMMAND || '', jimakuApiBaseUrl: process.env.SUBMINER_JIMAKU_API_BASE_URL || DEFAULT_JIMAKU_API_BASE_URL, @@ -250,29 +249,6 @@ export function applyInvocationsToArgs(parsed: Args, invocations: CliInvocations parsed.jellyfinLogout = Boolean(modeFlags.logout); } - if (invocations.ytInvocation) { - if (invocations.ytInvocation.mode) { - parsed.youtubeMode = invocations.ytInvocation.mode; - } - if (invocations.ytInvocation.logLevel) - parsed.logLevel = parseLogLevel(invocations.ytInvocation.logLevel); - if (invocations.ytInvocation.outDir) - parsed.youtubeSubgenOutDir = invocations.ytInvocation.outDir; - if (invocations.ytInvocation.keepTemp) parsed.youtubeSubgenKeepTemp = true; - if (invocations.ytInvocation.whisperBin) - parsed.whisperBin = invocations.ytInvocation.whisperBin; - if (invocations.ytInvocation.whisperModel) - parsed.whisperModel = invocations.ytInvocation.whisperModel; - if (invocations.ytInvocation.whisperVadModel) - parsed.whisperVadModel = invocations.ytInvocation.whisperVadModel; - if (invocations.ytInvocation.whisperThreads) - parsed.whisperThreads = invocations.ytInvocation.whisperThreads; - if (invocations.ytInvocation.ytSubgenAudioFormat) { - parsed.youtubeSubgenAudioFormat = invocations.ytInvocation.ytSubgenAudioFormat; - } - if (invocations.ytInvocation.target) ensureTarget(invocations.ytInvocation.target, parsed); - } - if (invocations.dictionaryLogLevel) { parsed.logLevel = parseLogLevel(invocations.dictionaryLogLevel); } diff --git a/launcher/config/cli-parser-builder.ts b/launcher/config/cli-parser-builder.ts index 2fd628f..75221f7 100644 --- a/launcher/config/cli-parser-builder.ts +++ b/launcher/config/cli-parser-builder.ts @@ -14,19 +14,6 @@ export interface JellyfinInvocation { logLevel?: string; } -export interface YtInvocation { - target?: string; - mode?: 'download' | 'generate'; - outDir?: string; - keepTemp?: boolean; - whisperBin?: string; - whisperModel?: string; - whisperVadModel?: string; - whisperThreads?: number; - ytSubgenAudioFormat?: string; - logLevel?: string; -} - export interface CommandActionInvocation { action: string; logLevel?: string; @@ -34,7 +21,6 @@ export interface CommandActionInvocation { export interface CliInvocations { jellyfinInvocation: JellyfinInvocation | null; - ytInvocation: YtInvocation | null; configInvocation: CommandActionInvocation | null; mpvInvocation: CommandActionInvocation | null; appInvocation: { appArgs: string[] } | null; @@ -90,8 +76,6 @@ function getTopLevelCommand(argv: string[]): { name: string; index: number } | n const commandNames = new Set([ 'jellyfin', 'jf', - 'yt', - 'youtube', 'doctor', 'config', 'mpv', @@ -143,7 +127,6 @@ export function parseCliPrograms( invocations: CliInvocations; } { let jellyfinInvocation: JellyfinInvocation | null = null; - let ytInvocation: YtInvocation | null = null; let configInvocation: CommandActionInvocation | null = null; let mpvInvocation: CommandActionInvocation | null = null; let appInvocation: { appArgs: string[] } | null = null; @@ -218,43 +201,6 @@ export function parseCliPrograms( }; }); - commandProgram - .command('yt') - .alias('youtube') - .description('YouTube workflows') - .argument('[target]', 'YouTube URL or ytsearch: query') - .option('--mode ', 'YouTube subtitle acquisition mode') - .option('-o, --out-dir ', 'Subtitle output dir') - .option('--keep-temp', 'Keep temp files') - .option('--whisper-bin ', 'whisper.cpp CLI path') - .option('--whisper-model ', 'whisper model path') - .option('--whisper-vad-model ', 'whisper.cpp VAD model path') - .option('--whisper-threads ', 'whisper.cpp thread count') - .option('--yt-subgen-audio-format ', 'Audio extraction format') - .option('--log-level ', 'Log level') - .action((target: string | undefined, options: Record) => { - ytInvocation = { - target, - mode: - typeof options.mode === 'string' && (options.mode === 'download' || options.mode === 'generate') - ? options.mode - : undefined, - outDir: typeof options.outDir === 'string' ? options.outDir : undefined, - keepTemp: options.keepTemp === true, - whisperBin: typeof options.whisperBin === 'string' ? options.whisperBin : undefined, - whisperModel: typeof options.whisperModel === 'string' ? options.whisperModel : undefined, - whisperVadModel: - typeof options.whisperVadModel === 'string' ? options.whisperVadModel : undefined, - whisperThreads: - typeof options.whisperThreads === 'number' && Number.isFinite(options.whisperThreads) - ? Math.floor(options.whisperThreads) - : undefined, - ytSubgenAudioFormat: - typeof options.ytSubgenAudioFormat === 'string' ? options.ytSubgenAudioFormat : undefined, - logLevel: typeof options.logLevel === 'string' ? options.logLevel : undefined, - }; - }); - commandProgram .command('dictionary') .alias('dict') @@ -388,7 +334,6 @@ export function parseCliPrograms( rootTarget: rootProgram.processedArgs[0], invocations: { jellyfinInvocation, - ytInvocation, configInvocation, mpvInvocation, appInvocation, diff --git a/launcher/main.test.ts b/launcher/main.test.ts index b6b83ea..7644544 100644 --- a/launcher/main.test.ts +++ b/launcher/main.test.ts @@ -362,7 +362,7 @@ ${bunBinary} -e "const net=require('node:net'); const fs=require('node:fs'); con }); }); -test('launcher disables plugin startup pause gate for app-owned youtube flow', { timeout: 15000 }, () => { +test('launcher routes youtube urls through regular playback startup', { timeout: 15000 }, () => { withTempDir((root) => { const homeDir = path.join(root, 'home'); const xdgConfigHome = path.join(root, 'xdg'); @@ -430,13 +430,16 @@ ${bunBinary} -e "const net=require('node:net'); const fs=require('node:fs'); con SUBMINER_TEST_MPV_ARGS: mpvArgsPath, SUBMINER_TEST_CAPTURE: path.join(root, 'captured-args.txt'), }; - const result = runLauncher(['yt', 'https://www.youtube.com/watch?v=abc123'], env); + const result = runLauncher(['https://www.youtube.com/watch?v=abc123'], env); assert.equal(result.status, 0, `stdout:\n${result.stdout}\nstderr:\n${result.stderr}`); - assert.match( - fs.readFileSync(mpvArgsPath, 'utf8'), - /--script-opts=.*subminer-auto_start_pause_until_ready=no/, - ); + const forwardedArgs = fs + .readFileSync(mpvArgsPath, 'utf8') + .trim() + .split('\n') + .map((item) => item.trim()) + .filter(Boolean); + assert.equal(forwardedArgs.includes('https://www.youtube.com/watch?v=abc123'), true); }); }); diff --git a/launcher/parse-args.test.ts b/launcher/parse-args.test.ts index 8b7296d..907c7d0 100644 --- a/launcher/parse-args.test.ts +++ b/launcher/parse-args.test.ts @@ -85,13 +85,6 @@ test('parseArgs maps mpv idle action', () => { assert.equal(parsed.mpvStatus, false); }); -test('parseArgs captures youtube mode forwarding', () => { - const parsed = parseArgs(['youtube', 'https://example.com', '--mode', 'generate'], 'subminer', {}); - - assert.equal(parsed.target, 'https://example.com'); - assert.equal(parsed.youtubeMode, 'generate'); -}); - test('parseArgs maps dictionary command and log-level override', () => { const parsed = parseArgs(['dictionary', '.', '--log-level', 'debug'], 'subminer', {}); diff --git a/src/cli/help.ts b/src/cli/help.ts index da3d6d9..3cb9731 100644 --- a/src/cli/help.ts +++ b/src/cli/help.ts @@ -13,8 +13,6 @@ ${B}Session${R} --background Start in tray/background mode --start Connect to mpv and launch overlay --launch-mpv ${D}[targets...]${R} Launch mpv with the SubMiner mpv profile and exit - --youtube-play ${D}URL${R} Start app-owned YouTube subtitle auto-load flow for a URL - --youtube-mode ${D}download|generate${R} Subtitle acquisition mode for YouTube flow --stop Stop the running instance --stats Open the stats dashboard in your browser --texthooker Start texthooker server only ${D}(no overlay)${R} diff --git a/src/main/runtime/cli-command-runtime-handler.test.ts b/src/main/runtime/cli-command-runtime-handler.test.ts index 723557b..6b51e41 100644 --- a/src/main/runtime/cli-command-runtime-handler.test.ts +++ b/src/main/runtime/cli-command-runtime-handler.test.ts @@ -54,7 +54,7 @@ test('cli command runtime handler prepares overlay prerequisites before overlay }, }); - handler({ youtubePlay: 'https://www.youtube.com/watch?v=test' } as never); + handler({ settings: true } as never); assert.deepEqual(calls, ['prereqs', 'context', 'cli:initial:ctx']); }); diff --git a/src/main/runtime/composers/mpv-runtime-composer.test.ts b/src/main/runtime/composers/mpv-runtime-composer.test.ts index acf7093..72a780e 100644 --- a/src/main/runtime/composers/mpv-runtime-composer.test.ts +++ b/src/main/runtime/composers/mpv-runtime-composer.test.ts @@ -72,7 +72,6 @@ test('composeMpvRuntimeHandlers returns callable handlers and forwards to inject currentSubAssText: '', playbackPaused: null, previousSecondarySubVisibility: null, - youtubePlaybackFlowPending: false, }, getQuitOnDisconnectArmed: () => false, scheduleQuitCheck: () => {}, @@ -281,7 +280,6 @@ test('composeMpvRuntimeHandlers skips MeCab warmup when all POS-dependent annota currentSubAssText: '', playbackPaused: null, previousSecondarySubVisibility: null, - youtubePlaybackFlowPending: false, }, getQuitOnDisconnectArmed: () => false, scheduleQuitCheck: () => {}, @@ -413,7 +411,6 @@ test('composeMpvRuntimeHandlers runs tokenization warmup once across sequential currentSubAssText: '', playbackPaused: null, previousSecondarySubVisibility: null, - youtubePlaybackFlowPending: false, }, getQuitOnDisconnectArmed: () => false, scheduleQuitCheck: () => {}, @@ -553,7 +550,6 @@ test('composeMpvRuntimeHandlers does not block first tokenization on dictionary currentSubAssText: '', playbackPaused: null, previousSecondarySubVisibility: null, - youtubePlaybackFlowPending: false, }, getQuitOnDisconnectArmed: () => false, scheduleQuitCheck: () => {}, @@ -687,7 +683,6 @@ test('composeMpvRuntimeHandlers shows annotation loading OSD after tokenization- currentSubAssText: '', playbackPaused: null, previousSecondarySubVisibility: null, - youtubePlaybackFlowPending: false, }, getQuitOnDisconnectArmed: () => false, scheduleQuitCheck: () => {}, @@ -835,7 +830,6 @@ test('composeMpvRuntimeHandlers reuses completed background tokenization warmups currentSubAssText: '', playbackPaused: null, previousSecondarySubVisibility: null, - youtubePlaybackFlowPending: false, }, getQuitOnDisconnectArmed: () => false, scheduleQuitCheck: () => {}, diff --git a/src/main/runtime/initial-args-handler.test.ts b/src/main/runtime/initial-args-handler.test.ts index f89ea5d..b9da6bc 100644 --- a/src/main/runtime/initial-args-handler.test.ts +++ b/src/main/runtime/initial-args-handler.test.ts @@ -115,7 +115,7 @@ test('initial args handler forwards args to cli handler', () => { test('initial args handler bootstraps overlay before initial overlay-runtime commands', () => { const calls: string[] = []; - const args = { youtubePlay: 'https://youtube.com/watch?v=abc' } as never; + const args = { settings: true } as never; const handleInitialArgs = createHandleInitialArgsHandler({ getInitialArgs: () => args, isBackgroundMode: () => false, diff --git a/src/main/runtime/mpv-main-event-actions.ts b/src/main/runtime/mpv-main-event-actions.ts index d5b79ed..2fd43ea 100644 --- a/src/main/runtime/mpv-main-event-actions.ts +++ b/src/main/runtime/mpv-main-event-actions.ts @@ -1,7 +1,6 @@ import type { SubtitleData } from '../../types'; export function createHandleMpvSubtitleChangeHandler(deps: { - shouldSuppressSubtitleEvents?: () => boolean; setCurrentSubText: (text: string) => void; getImmediateSubtitlePayload?: (text: string) => SubtitleData | null; emitImmediateSubtitle?: (payload: SubtitleData) => void; @@ -11,10 +10,6 @@ export function createHandleMpvSubtitleChangeHandler(deps: { }) { return ({ text }: { text: string }): void => { deps.setCurrentSubText(text); - if (deps.shouldSuppressSubtitleEvents?.()) { - deps.refreshDiscordPresence(); - return; - } const immediatePayload = deps.getImmediateSubtitlePayload?.(text) ?? null; if (immediatePayload) { (deps.emitImmediateSubtitle ?? deps.broadcastSubtitle)(immediatePayload); @@ -30,27 +25,19 @@ export function createHandleMpvSubtitleChangeHandler(deps: { } export function createHandleMpvSubtitleAssChangeHandler(deps: { - shouldSuppressSubtitleEvents?: () => boolean; setCurrentSubAssText: (text: string) => void; broadcastSubtitleAss: (text: string) => void; }) { return ({ text }: { text: string }): void => { deps.setCurrentSubAssText(text); - if (deps.shouldSuppressSubtitleEvents?.()) { - return; - } deps.broadcastSubtitleAss(text); }; } export function createHandleMpvSecondarySubtitleChangeHandler(deps: { - shouldSuppressSubtitleEvents?: () => boolean; broadcastSecondarySubtitle: (text: string) => void; }) { return ({ text }: { text: string }): void => { - if (deps.shouldSuppressSubtitleEvents?.()) { - return; - } deps.broadcastSecondarySubtitle(text); }; } diff --git a/src/main/runtime/mpv-main-event-bindings.test.ts b/src/main/runtime/mpv-main-event-bindings.test.ts index c215a62..e7bcd4a 100644 --- a/src/main/runtime/mpv-main-event-bindings.test.ts +++ b/src/main/runtime/mpv-main-event-bindings.test.ts @@ -26,8 +26,6 @@ test('main mpv event binder wires callbacks through to runtime deps', () => { calls.push('post-watch'); }, logSubtitleTimingError: () => calls.push('subtitle-error'), - shouldSuppressSubtitleEvents: () => false, - setCurrentSubText: (text) => calls.push(`set-sub:${text}`), broadcastSubtitle: (payload) => calls.push(`broadcast-sub:${payload.text}`), onSubtitleChange: (text) => calls.push(`subtitle-change:${text}`), @@ -94,67 +92,3 @@ test('main mpv event binder wires callbacks through to runtime deps', () => { assert.ok(calls.includes('sync-immersion')); assert.ok(calls.includes('flush-playback')); }); - -test('main mpv event binder suppresses subtitle broadcasts while youtube flow is pending', () => { - const handlers = new Map void>(); - const calls: string[] = []; - - const bind = createBindMpvMainEventHandlersHandler({ - reportJellyfinRemoteStopped: () => {}, - syncOverlayMpvSubtitleSuppression: () => {}, - resetSubtitleSidebarEmbeddedLayout: () => {}, - hasInitialJellyfinPlayArg: () => false, - isOverlayRuntimeInitialized: () => false, - isQuitOnDisconnectArmed: () => false, - scheduleQuitCheck: () => {}, - isMpvConnected: () => false, - quitApp: () => {}, - recordImmersionSubtitleLine: () => {}, - hasSubtitleTimingTracker: () => false, - recordSubtitleTiming: () => {}, - maybeRunAnilistPostWatchUpdate: async () => {}, - logSubtitleTimingError: () => {}, - shouldSuppressSubtitleEvents: () => true, - setCurrentSubText: (text) => calls.push(`set-sub:${text}`), - broadcastSubtitle: (payload) => calls.push(`broadcast-sub:${payload.text}`), - onSubtitleChange: (text) => calls.push(`subtitle-change:${text}`), - refreshDiscordPresence: () => calls.push('presence-refresh'), - setCurrentSubAssText: (text) => calls.push(`set-ass:${text}`), - broadcastSubtitleAss: (text) => calls.push(`broadcast-ass:${text}`), - broadcastSecondarySubtitle: (text) => calls.push(`broadcast-secondary:${text}`), - updateCurrentMediaPath: () => {}, - restoreMpvSubVisibility: () => {}, - getCurrentAnilistMediaKey: () => null, - resetAnilistMediaTracking: () => {}, - maybeProbeAnilistDuration: () => {}, - ensureAnilistMediaGuess: () => {}, - syncImmersionMediaState: () => {}, - updateCurrentMediaTitle: () => {}, - resetAnilistMediaGuessState: () => {}, - notifyImmersionTitleUpdate: () => {}, - recordPlaybackPosition: () => {}, - recordMediaDuration: () => {}, - reportJellyfinRemoteProgress: () => {}, - recordPauseState: () => {}, - updateSubtitleRenderMetrics: () => {}, - setPreviousSecondarySubVisibility: () => {}, - }); - - bind({ - on: (event, handler) => { - handlers.set(event, handler as (payload: unknown) => void); - }, - }); - - handlers.get('subtitle-change')?.({ text: 'line' }); - handlers.get('subtitle-ass-change')?.({ text: 'ass' }); - handlers.get('secondary-subtitle-change')?.({ text: 'sec' }); - - assert.ok(calls.includes('set-sub:line')); - assert.ok(calls.includes('set-ass:ass')); - assert.ok(calls.includes('presence-refresh')); - assert.ok(!calls.includes('broadcast-sub:line')); - assert.ok(!calls.includes('subtitle-change:line')); - assert.ok(!calls.includes('broadcast-ass:ass')); - assert.ok(!calls.includes('broadcast-secondary:sec')); -}); diff --git a/src/main/runtime/mpv-main-event-bindings.ts b/src/main/runtime/mpv-main-event-bindings.ts index 1f0fdd8..7890818 100644 --- a/src/main/runtime/mpv-main-event-bindings.ts +++ b/src/main/runtime/mpv-main-event-bindings.ts @@ -35,7 +35,6 @@ export function createBindMpvMainEventHandlersHandler(deps: { recordSubtitleTiming: (text: string, start: number, end: number) => void; maybeRunAnilistPostWatchUpdate: () => Promise; logSubtitleTimingError: (message: string, error: unknown) => void; - shouldSuppressSubtitleEvents?: () => boolean; setCurrentSubText: (text: string) => void; getImmediateSubtitlePayload?: (text: string) => SubtitleData | null; @@ -100,7 +99,6 @@ export function createBindMpvMainEventHandlersHandler(deps: { logError: (message, error) => deps.logSubtitleTimingError(message, error), }); const handleMpvSubtitleChange = createHandleMpvSubtitleChangeHandler({ - shouldSuppressSubtitleEvents: () => deps.shouldSuppressSubtitleEvents?.() ?? false, setCurrentSubText: (text) => deps.setCurrentSubText(text), getImmediateSubtitlePayload: (text) => deps.getImmediateSubtitlePayload?.(text) ?? null, emitImmediateSubtitle: (payload) => deps.emitImmediateSubtitle?.(payload), @@ -109,12 +107,10 @@ export function createBindMpvMainEventHandlersHandler(deps: { refreshDiscordPresence: () => deps.refreshDiscordPresence(), }); const handleMpvSubtitleAssChange = createHandleMpvSubtitleAssChangeHandler({ - shouldSuppressSubtitleEvents: () => deps.shouldSuppressSubtitleEvents?.() ?? false, setCurrentSubAssText: (text) => deps.setCurrentSubAssText(text), broadcastSubtitleAss: (text) => deps.broadcastSubtitleAss(text), }); const handleMpvSecondarySubtitleChange = createHandleMpvSecondarySubtitleChangeHandler({ - shouldSuppressSubtitleEvents: () => deps.shouldSuppressSubtitleEvents?.() ?? false, broadcastSecondarySubtitle: (text) => deps.broadcastSecondarySubtitle(text), }); const handleMpvMediaPathChange = createHandleMpvMediaPathChangeHandler({ diff --git a/src/main/runtime/mpv-main-event-main-deps.test.ts b/src/main/runtime/mpv-main-event-main-deps.test.ts index c868fca..845b2fd 100644 --- a/src/main/runtime/mpv-main-event-main-deps.test.ts +++ b/src/main/runtime/mpv-main-event-main-deps.test.ts @@ -25,7 +25,6 @@ test('mpv main event main deps map app state updates and delegate callbacks', as currentSubAssText: '', playbackPaused: null, previousSecondarySubVisibility: false, - youtubePlaybackFlowPending: false, }; const deps = createBuildBindMpvMainEventHandlersMainDepsHandler({ @@ -75,7 +74,6 @@ test('mpv main event main deps map app state updates and delegate callbacks', as deps.recordSubtitleTiming('y', 0, 1); await deps.maybeRunAnilistPostWatchUpdate(); deps.logSubtitleTimingError('err', new Error('boom')); - assert.equal(deps.shouldSuppressSubtitleEvents?.(), false); deps.setCurrentSubText('sub'); deps.broadcastSubtitle({ text: 'sub', tokens: null }); deps.onSubtitleChange('sub'); @@ -119,7 +117,7 @@ test('mpv main event main deps map app state updates and delegate callbacks', as assert.ok(calls.includes('reset-sidebar-layout')); }); -test('mpv main event main deps suppress subtitle events while youtube flow is pending', () => { +test('mpv main event main deps wire subtitle callbacks without suppression gate', () => { const deps = createBuildBindMpvMainEventHandlersMainDepsHandler({ appState: { initialArgs: null, @@ -131,7 +129,6 @@ test('mpv main event main deps suppress subtitle events while youtube flow is pe currentSubAssText: '', playbackPaused: null, previousSecondarySubVisibility: false, - youtubePlaybackFlowPending: true, }, getQuitOnDisconnectArmed: () => false, scheduleQuitCheck: () => {}, @@ -158,5 +155,6 @@ test('mpv main event main deps suppress subtitle events while youtube flow is pe refreshDiscordPresence: () => {}, })(); - assert.equal(deps.shouldSuppressSubtitleEvents?.(), true); + deps.setCurrentSubText('sub'); + assert.equal(typeof deps.setCurrentSubText, 'function'); }); diff --git a/src/main/runtime/mpv-main-event-main-deps.ts b/src/main/runtime/mpv-main-event-main-deps.ts index d574f59..2523861 100644 --- a/src/main/runtime/mpv-main-event-main-deps.ts +++ b/src/main/runtime/mpv-main-event-main-deps.ts @@ -34,7 +34,6 @@ export function createBuildBindMpvMainEventHandlersMainDepsHandler(deps: { currentSubtitleData?: SubtitleData | null; playbackPaused: boolean | null; previousSecondarySubVisibility: boolean | null; - youtubePlaybackFlowPending: boolean; }; getQuitOnDisconnectArmed: () => boolean; scheduleQuitCheck: (callback: () => void) => void; @@ -120,7 +119,6 @@ export function createBuildBindMpvMainEventHandlersMainDepsHandler(deps: { maybeRunAnilistPostWatchUpdate: () => deps.maybeRunAnilistPostWatchUpdate(), logSubtitleTimingError: (message: string, error: unknown) => deps.logSubtitleTimingError(message, error), - shouldSuppressSubtitleEvents: () => deps.appState.youtubePlaybackFlowPending, setCurrentSubText: (text: string) => { deps.appState.currentSubText = text; }, diff --git a/src/main/runtime/youtube-picker-open.test.ts b/src/main/runtime/youtube-picker-open.test.ts index 4c20004..2e41b7c 100644 --- a/src/main/runtime/youtube-picker-open.test.ts +++ b/src/main/runtime/youtube-picker-open.test.ts @@ -6,7 +6,6 @@ import type { YoutubePickerOpenPayload } from '../../types'; const payload: YoutubePickerOpenPayload = { sessionId: 'yt-1', url: 'https://example.com/watch?v=abc', - mode: 'download', tracks: [], defaultPrimaryTrackId: null, defaultSecondaryTrackId: null, diff --git a/src/main/state.test.ts b/src/main/state.test.ts index 695d524..6ddf7f3 100644 --- a/src/main/state.test.ts +++ b/src/main/state.test.ts @@ -95,14 +95,14 @@ test('transitionAnilistUpdateInFlightState updates inFlight only', () => { assert.notEqual(transitioned, current); }); -test('applyStartupState does not mark youtube playback flow pending from startup args alone', () => { +test('applyStartupState preserves cleared startup-only runtime flags', () => { const appState = createAppState({ mpvSocketPath: '/tmp/mpv.sock', texthookerPort: 4000, }); applyStartupState(appState, { - initialArgs: parseArgs(['--youtube-play', 'https://www.youtube.com/watch?v=video123']), + initialArgs: parseArgs(['--settings']), mpvSocketPath: '/tmp/mpv.sock', texthookerPort: 4000, backendOverride: null, @@ -111,5 +111,5 @@ test('applyStartupState does not mark youtube playback flow pending from startup backgroundMode: false, }); - assert.equal(appState.youtubePlaybackFlowPending, false); + assert.equal(appState.initialArgs?.settings, true); }); diff --git a/src/main/state.ts b/src/main/state.ts index 3c0f7a1..16372dd 100644 --- a/src/main/state.ts +++ b/src/main/state.ts @@ -188,7 +188,6 @@ export interface AppState { overlayDebugVisualizationEnabled: boolean; statsOverlayVisible: boolean; subsyncInProgress: boolean; - youtubePlaybackFlowPending: boolean; initialArgs: CliArgs | null; mpvSocketPath: string; texthookerPort: number; @@ -273,7 +272,6 @@ export function createAppState(values: AppStateInitialValues): AppState { fieldGroupingResolver: null, fieldGroupingResolverSequence: 0, subsyncInProgress: false, - youtubePlaybackFlowPending: false, initialArgs: null, mpvSocketPath: values.mpvSocketPath, texthookerPort: values.texthookerPort, @@ -293,7 +291,6 @@ export function createAppState(values: AppStateInitialValues): AppState { export function applyStartupState(appState: AppState, startupState: StartupState): void { appState.initialArgs = startupState.initialArgs; - appState.youtubePlaybackFlowPending = false; appState.mpvSocketPath = startupState.mpvSocketPath; appState.texthookerPort = startupState.texthookerPort; appState.backendOverride = startupState.backendOverride; diff --git a/src/renderer/modals/youtube-track-picker.test.ts b/src/renderer/modals/youtube-track-picker.test.ts index 1606c81..0b8303f 100644 --- a/src/renderer/modals/youtube-track-picker.test.ts +++ b/src/renderer/modals/youtube-track-picker.test.ts @@ -151,7 +151,6 @@ test('youtube track picker close restores focus and mouse-ignore state', () => { modal.openYoutubePickerModal({ sessionId: 'yt-1', url: 'https://example.com', - mode: 'download', tracks: [], defaultPrimaryTrackId: null, defaultSecondaryTrackId: null, @@ -241,7 +240,6 @@ test('youtube track picker re-acknowledges repeated open requests', () => { modal.openYoutubePickerModal({ sessionId: 'yt-1', url: 'https://example.com/one', - mode: 'download', tracks: [], defaultPrimaryTrackId: null, defaultSecondaryTrackId: null, @@ -250,7 +248,6 @@ test('youtube track picker re-acknowledges repeated open requests', () => { modal.openYoutubePickerModal({ sessionId: 'yt-2', url: 'https://example.com/two', - mode: 'generate', tracks: [], defaultPrimaryTrackId: null, defaultSecondaryTrackId: null, @@ -327,7 +324,6 @@ test('youtube track picker surfaces rejected resolve calls as modal status', asy modal.openYoutubePickerModal({ sessionId: 'yt-1', url: 'https://example.com', - mode: 'download', tracks: [ { id: 'auto:ja-orig', @@ -432,7 +428,6 @@ test('youtube track picker ignores duplicate resolve submissions while request i modal.openYoutubePickerModal({ sessionId: 'yt-1', url: 'https://example.com', - mode: 'download', tracks: [ { id: 'auto:ja-orig', @@ -534,7 +529,6 @@ test('youtube track picker only consumes handled keys', async () => { modal.openYoutubePickerModal({ sessionId: 'yt-1', url: 'https://example.com', - mode: 'download', tracks: [], defaultPrimaryTrackId: null, defaultSecondaryTrackId: null, @@ -640,7 +634,6 @@ test('youtube track picker ignores immediate Enter after open before allowing ke modal.openYoutubePickerModal({ sessionId: 'yt-1', url: 'https://example.com', - mode: 'download', tracks: [ { id: 'auto:ja-orig', diff --git a/src/renderer/modals/youtube-track-picker.ts b/src/renderer/modals/youtube-track-picker.ts index a59982f..e9e76ac 100644 --- a/src/renderer/modals/youtube-track-picker.ts +++ b/src/renderer/modals/youtube-track-picker.ts @@ -96,7 +96,7 @@ export function createYoutubeTrackPickerModal( function applyPayload(payload: YoutubePickerOpenPayload): void { ctx.state.youtubePickerPayload = payload; - ctx.dom.youtubePickerTitle.textContent = `${payload.mode === 'generate' ? 'Generate' : 'Download'} subtitles for ${payload.url}`; + ctx.dom.youtubePickerTitle.textContent = `Select YouTube subtitles for ${payload.url}`; ctx.dom.youtubePickerPrimarySelect.innerHTML = ''; ctx.dom.youtubePickerSecondarySelect.innerHTML = ''; diff --git a/src/types.ts b/src/types.ts index d5e3c39..c80da93 100644 --- a/src/types.ts +++ b/src/types.ts @@ -561,7 +561,6 @@ export interface ControllerRuntimeSnapshot { } export type JimakuLanguagePreference = 'ja' | 'en' | 'none'; -export type YoutubeFlowMode = 'download' | 'generate'; export type { YoutubeTrackKind }; export interface YoutubeTrackOption { @@ -578,7 +577,6 @@ export interface YoutubeTrackOption { export interface YoutubePickerOpenPayload { sessionId: string; url: string; - mode: YoutubeFlowMode; tracks: YoutubeTrackOption[]; defaultPrimaryTrackId: string | null; defaultSecondaryTrackId: string | null;