From 1e5e98482ab93ff54c536217e450751fb6297b69 Mon Sep 17 00:00:00 2001 From: sudacode Date: Sun, 29 Mar 2026 15:59:12 -0700 Subject: [PATCH] fix: lazily resolve youtube playback socket path --- changes/252-youtube-playback-socket-path.md | 5 ++ src/main.ts | 2 +- .../runtime/youtube-playback-runtime.test.ts | 70 ++++++++++++++++++- src/main/runtime/youtube-playback-runtime.ts | 5 +- 4 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 changes/252-youtube-playback-socket-path.md diff --git a/changes/252-youtube-playback-socket-path.md b/changes/252-youtube-playback-socket-path.md new file mode 100644 index 0000000..71e099b --- /dev/null +++ b/changes/252-youtube-playback-socket-path.md @@ -0,0 +1,5 @@ +type: fixed +area: main + +- Resolve the YouTube playback socket path lazily so startup honors CLI and config overrides. +- Add regression coverage for the lazy socket-path lookup during Windows mpv startup. diff --git a/src/main.ts b/src/main.ts index 9a65f43..4fc5f03 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1003,7 +1003,7 @@ const youtubePlaybackRuntime = createYoutubePlaybackRuntime({ mpvYtdlFormat: YOUTUBE_MPV_YTDL_FORMAT, autoLaunchTimeoutMs: YOUTUBE_MPV_AUTO_LAUNCH_TIMEOUT_MS, connectTimeoutMs: YOUTUBE_MPV_CONNECT_TIMEOUT_MS, - socketPath: appState.mpvSocketPath, + getSocketPath: () => appState.mpvSocketPath, getMpvConnected: () => Boolean(appState.mpvClient?.connected), invalidatePendingAutoplayReadyFallbacks: () => autoplayReadyGate.invalidatePendingAutoplayReadyFallbacks(), diff --git a/src/main/runtime/youtube-playback-runtime.test.ts b/src/main/runtime/youtube-playback-runtime.test.ts index e97d3db..5208bfc 100644 --- a/src/main/runtime/youtube-playback-runtime.test.ts +++ b/src/main/runtime/youtube-playback-runtime.test.ts @@ -6,6 +6,7 @@ test('youtube playback runtime resets flow ownership after a successful run', as const calls: string[] = []; let appOwnedFlowInFlight = false; let timeoutCallback: (() => void) | null = null; + let socketPath = '/tmp/mpv.sock'; const runtime = createYoutubePlaybackRuntime({ platform: 'linux', @@ -13,7 +14,7 @@ test('youtube playback runtime resets flow ownership after a successful run', as mpvYtdlFormat: 'bestvideo+bestaudio', autoLaunchTimeoutMs: 2_000, connectTimeoutMs: 1_000, - socketPath: '/tmp/mpv.sock', + getSocketPath: () => socketPath, getMpvConnected: () => true, invalidatePendingAutoplayReadyFallbacks: () => { calls.push('invalidate-autoplay'); @@ -78,3 +79,70 @@ test('youtube playback runtime resets flow ownership after a successful run', as scheduledCallback(); assert.equal(runtime.getQuitOnDisconnectArmed(), true); }); + +test('youtube playback runtime resolves the socket path lazily for windows startup', async () => { + const calls: string[] = []; + let socketPath = '/tmp/initial.sock'; + + const runtime = createYoutubePlaybackRuntime({ + platform: 'win32', + directPlaybackFormat: 'best', + mpvYtdlFormat: 'bestvideo+bestaudio', + autoLaunchTimeoutMs: 2_000, + connectTimeoutMs: 1_000, + getSocketPath: () => socketPath, + getMpvConnected: () => false, + invalidatePendingAutoplayReadyFallbacks: () => { + calls.push('invalidate-autoplay'); + }, + setAppOwnedFlowInFlight: (next) => { + calls.push(`app-owned:${next}`); + }, + ensureYoutubePlaybackRuntimeReady: async () => { + calls.push('ensure-runtime-ready'); + }, + resolveYoutubePlaybackUrl: async (url, format) => { + calls.push(`resolve:${url}:${format}`); + return 'https://example.com/direct'; + }, + launchWindowsMpv: (_playbackUrl, args) => { + calls.push(`launch:${args.join(' ')}`); + return { ok: true, mpvPath: '/usr/bin/mpv' }; + }, + waitForYoutubeMpvConnected: async (timeoutMs) => { + calls.push(`wait-connected:${timeoutMs}`); + return true; + }, + prepareYoutubePlaybackInMpv: async ({ url }) => { + calls.push(`prepare:${url}`); + return true; + }, + runYoutubePlaybackFlow: async ({ url, mode }) => { + calls.push(`run-flow:${url}:${mode}`); + }, + logInfo: (message) => { + calls.push(`info:${message}`); + }, + logWarn: (message) => { + calls.push(`warn:${message}`); + }, + schedule: (callback) => { + calls.push('schedule-arm'); + callback(); + return 1 as never; + }, + clearScheduled: () => { + calls.push('clear-scheduled'); + }, + }); + + socketPath = '/tmp/updated.sock'; + + await runtime.runYoutubePlaybackFlow({ + url: 'https://youtu.be/demo', + mode: 'download', + source: 'initial', + }); + + assert.ok(calls.some((entry) => entry.includes('--input-ipc-server=/tmp/updated.sock'))); +}); diff --git a/src/main/runtime/youtube-playback-runtime.ts b/src/main/runtime/youtube-playback-runtime.ts index 8999728..0bf0815 100644 --- a/src/main/runtime/youtube-playback-runtime.ts +++ b/src/main/runtime/youtube-playback-runtime.ts @@ -11,7 +11,7 @@ export type YoutubePlaybackRuntimeDeps = { mpvYtdlFormat: string; autoLaunchTimeoutMs: number; connectTimeoutMs: number; - socketPath: string; + getSocketPath: () => string; getMpvConnected: () => boolean; invalidatePendingAutoplayReadyFallbacks: () => void; setAppOwnedFlowInFlight: (next: boolean) => void; @@ -76,6 +76,7 @@ export function createYoutubePlaybackRuntime(deps: YoutubePlaybackRuntimeDeps) { } if (deps.platform === 'win32' && !deps.getMpvConnected()) { + const socketPath = deps.getSocketPath(); const launchResult = deps.launchWindowsMpv(playbackUrl, [ '--pause=yes', '--ytdl=yes', @@ -87,7 +88,7 @@ export function createYoutubePlaybackRuntime(deps: YoutubePlaybackRuntimeDeps) { '--secondary-sub-visibility=no', '--alang=ja,jp,jpn,japanese,en,eng,english,enus,en-us', '--slang=ja,jp,jpn,japanese,en,eng,english,enus,en-us', - `--input-ipc-server=${deps.socketPath}`, + `--input-ipc-server=${socketPath}`, ]); launchedWindowsMpv = launchResult.ok; if (launchResult.ok && launchResult.mpvPath) {