From 2a0e9313254da06df28e77c34caa0f19fae2882c Mon Sep 17 00:00:00 2001 From: sudacode Date: Sat, 31 Oct 2026 00:50:33 -0700 Subject: [PATCH] refactor(main): drop pass-through main-deps builders (#306) * refactor(main): drop mining/anki main-deps builders * refactor(anilist): drop main-deps builders * refactor(jellyfin): drop main-deps builders * refactor(overlay): drop overlay and shortcut main-deps builders * refactor(main): drop ipc, tray, and osd main-deps builders * refactor(startup): drop startup main-deps builders * test(scripts): ignore stray local requests in AUR download test Also apply prettier to launcher/mpv.test.ts so format:check:src passes. * fix(shortcuts): forward overlay numeric selection hooks through the composer composeShortcutRuntimes only passed the digit callbacks, so the visible-overlay selection added in #78 never ran from the multi-copy/mine-sentence shortcuts. * fix(stats): skip mpv auto-connect when stats starts the immersion tracker The immersion-startup main-deps builder never forwarded shouldAutoConnectMpv, so background stats and stats CLI startup always connected to mpv. Drop the builder and pass the deps straight to the handler. * build(deps): bump follow-redirects override to 1.16.2 Resolves GHSA-mrwp-f3q8-3g9p (high), which fails the quality-gate bun audit --audit-level high step. * test(subtitles): cover the null-tokenization settle path in prefetch test The mock ignored the value the test resolved, so the null case never ran. * test(e2e): surface mpv crashes and install current mpv on macOS CI The harness IPC client never handled socket close, so a crashed mpv left commands pending until the 120s test timeout with an empty mpv.log. Reject pending commands on close, write mpv's own log via --log-file, and refresh Homebrew on macOS CI so the installed mpv build no longer depends on the runner image. * test(e2e): render mpv through cocoa-cb OpenGL on macOS mpv 0.41.0_12 with libplacebo 7.387.0 segfaults in gpu-next's MoltenVK path on GitHub's paravirtual macOS GPU on the first frame, so the E2E session never started. * test(e2e): name mpv-stdio.log in the mpv close diagnostic --- .github/workflows/ci.yml | 7 +- bun.lock | 4 +- docs/architecture/layering.md | 2 + docs/workflow/verification.md | 3 +- e2e/harness/mpv.ts | 43 +- e2e/harness/session.ts | 14 +- launcher/mpv.test.ts | 47 +- package.json | 2 +- scripts/aur-release-download.test.ts | 4 + src/main.ts | 604 +++++++----------- src/main/main-wiring.test.ts | 19 - .../runtime/anilist-media-guess-main-deps.ts | 37 -- .../runtime/anilist-media-state-main-deps.ts | 90 --- .../runtime/anilist-post-watch-main-deps.ts | 86 --- .../anilist-setup-protocol-main-deps.ts | 69 -- .../runtime/anilist-setup-window-main-deps.ts | 27 - .../anilist-token-refresh-main-deps.ts | 23 - src/main/runtime/anki-actions-main-deps.ts | 98 --- src/main/runtime/app-ready-main-deps.ts | 51 -- .../cli-command-prechecks-main-deps.ts | 20 - .../runtime/cli-command-runtime-handler.ts | 7 +- .../composers/anilist-setup-composer.ts | 24 +- .../composers/anilist-tracking-composer.ts | 79 +-- .../runtime/composers/app-ready-composer.ts | 55 +- .../runtime/composers/ipc-runtime-composer.ts | 13 +- .../composers/jellyfin-runtime-composer.ts | 164 ++--- .../runtime/composers/mpv-runtime-composer.ts | 93 ++- .../overlay-visibility-runtime-composer.ts | 37 +- .../shortcuts-runtime-composer.test.ts | 69 ++ .../composers/shortcuts-runtime-composer.ts | 13 +- src/main/runtime/domains/anilist.ts | 6 - src/main/runtime/domains/ipc.ts | 1 - src/main/runtime/domains/jellyfin.ts | 7 - src/main/runtime/domains/mining.ts | 2 - src/main/runtime/domains/mpv.ts | 1 - src/main/runtime/domains/overlay.ts | 6 - src/main/runtime/domains/shortcuts.ts | 1 - src/main/runtime/domains/startup.ts | 7 - .../field-grouping-overlay-main-deps.ts | 46 -- .../field-grouping-resolver-main-deps.ts | 25 - .../runtime/global-shortcuts-main-deps.ts | 47 -- .../global-shortcuts-runtime-handlers.ts | 31 +- .../runtime/immersion-startup-main-deps.ts | 22 - src/main/runtime/initial-args-main-deps.ts | 38 -- .../runtime/initial-args-runtime-handler.ts | 7 +- .../runtime/ipc-bridge-actions-main-deps.ts | 21 - src/main/runtime/ipc-mpv-command-main-deps.ts | 30 - src/main/runtime/ipc-runtime-handlers.ts | 29 +- src/main/runtime/jellyfin-cli-main-deps.ts | 59 -- .../jellyfin-command-dispatch-main-deps.ts | 59 -- .../jellyfin-playback-launch-main-deps.ts | 30 - .../jellyfin-remote-connection-main-deps.ts | 50 -- .../jellyfin-remote-session-main-deps.ts | 44 -- .../jellyfin-subtitle-preload-main-deps.ts | 23 - src/main/runtime/media-runtime-main-deps.ts | 30 - src/main/runtime/mining-actions-main-deps.ts | 88 --- .../mpv-jellyfin-defaults-main-deps.ts | 22 - src/main/runtime/mpv-osd-log-main-deps.ts | 25 - src/main/runtime/mpv-osd-runtime-handlers.ts | 18 +- .../mpv-subtitle-render-metrics-main-deps.ts | 16 - .../numeric-shortcut-runtime-main-deps.ts | 12 - .../numeric-shortcut-session-main-deps.ts | 29 - ...meric-shortcut-session-runtime-handlers.ts | 52 +- .../runtime/overlay-bootstrap-main-deps.ts | 25 - .../runtime/overlay-main-actions-main-deps.ts | 46 -- .../overlay-runtime-main-actions-main-deps.ts | 85 --- .../overlay-shortcuts-lifecycle-main-deps.ts | 43 -- .../overlay-shortcuts-runtime-handlers.ts | 30 +- .../runtime/overlay-visibility-runtime.ts | 14 +- .../runtime/runtime-bootstrap-main-deps.ts | 53 -- .../runtime/secondary-sub-mode-main-deps.ts | 21 - .../secondary-sub-mode-runtime-handler.ts | 16 +- src/main/runtime/startup-config-main-deps.ts | 45 -- .../runtime/startup-lifecycle-main-deps.ts | 21 - src/main/runtime/startup-runtime-handlers.ts | 14 +- src/main/runtime/startup-warmups-main-deps.ts | 52 -- .../runtime/subtitle-position-main-deps.ts | 21 - .../runtime/subtitle-processing-main-deps.ts | 13 - .../subtitle-processing-runtime.test.ts | 62 ++ .../runtime/subtitle-processing-runtime.ts | 33 + src/main/runtime/tray-main-deps.ts | 108 ---- src/main/runtime/tray-runtime-handlers.ts | 18 +- .../yomitan-extension-loader-main-deps.ts | 34 - src/main/runtime/yomitan-extension-runtime.ts | 31 +- 84 files changed, 719 insertions(+), 2754 deletions(-) delete mode 100644 src/main/runtime/anilist-media-guess-main-deps.ts delete mode 100644 src/main/runtime/anilist-media-state-main-deps.ts delete mode 100644 src/main/runtime/anilist-post-watch-main-deps.ts delete mode 100644 src/main/runtime/anilist-setup-protocol-main-deps.ts delete mode 100644 src/main/runtime/anilist-setup-window-main-deps.ts delete mode 100644 src/main/runtime/anilist-token-refresh-main-deps.ts delete mode 100644 src/main/runtime/anki-actions-main-deps.ts delete mode 100644 src/main/runtime/app-ready-main-deps.ts delete mode 100644 src/main/runtime/cli-command-prechecks-main-deps.ts create mode 100644 src/main/runtime/composers/shortcuts-runtime-composer.test.ts delete mode 100644 src/main/runtime/field-grouping-overlay-main-deps.ts delete mode 100644 src/main/runtime/field-grouping-resolver-main-deps.ts delete mode 100644 src/main/runtime/global-shortcuts-main-deps.ts delete mode 100644 src/main/runtime/immersion-startup-main-deps.ts delete mode 100644 src/main/runtime/initial-args-main-deps.ts delete mode 100644 src/main/runtime/ipc-bridge-actions-main-deps.ts delete mode 100644 src/main/runtime/ipc-mpv-command-main-deps.ts delete mode 100644 src/main/runtime/jellyfin-cli-main-deps.ts delete mode 100644 src/main/runtime/jellyfin-command-dispatch-main-deps.ts delete mode 100644 src/main/runtime/jellyfin-playback-launch-main-deps.ts delete mode 100644 src/main/runtime/jellyfin-remote-connection-main-deps.ts delete mode 100644 src/main/runtime/jellyfin-remote-session-main-deps.ts delete mode 100644 src/main/runtime/jellyfin-subtitle-preload-main-deps.ts delete mode 100644 src/main/runtime/media-runtime-main-deps.ts delete mode 100644 src/main/runtime/mining-actions-main-deps.ts delete mode 100644 src/main/runtime/mpv-jellyfin-defaults-main-deps.ts delete mode 100644 src/main/runtime/mpv-osd-log-main-deps.ts delete mode 100644 src/main/runtime/mpv-subtitle-render-metrics-main-deps.ts delete mode 100644 src/main/runtime/numeric-shortcut-runtime-main-deps.ts delete mode 100644 src/main/runtime/numeric-shortcut-session-main-deps.ts delete mode 100644 src/main/runtime/overlay-bootstrap-main-deps.ts delete mode 100644 src/main/runtime/overlay-main-actions-main-deps.ts delete mode 100644 src/main/runtime/overlay-runtime-main-actions-main-deps.ts delete mode 100644 src/main/runtime/overlay-shortcuts-lifecycle-main-deps.ts delete mode 100644 src/main/runtime/runtime-bootstrap-main-deps.ts delete mode 100644 src/main/runtime/secondary-sub-mode-main-deps.ts delete mode 100644 src/main/runtime/startup-config-main-deps.ts delete mode 100644 src/main/runtime/startup-lifecycle-main-deps.ts delete mode 100644 src/main/runtime/startup-warmups-main-deps.ts delete mode 100644 src/main/runtime/subtitle-position-main-deps.ts delete mode 100644 src/main/runtime/subtitle-processing-main-deps.ts create mode 100644 src/main/runtime/subtitle-processing-runtime.test.ts create mode 100644 src/main/runtime/subtitle-processing-runtime.ts delete mode 100644 src/main/runtime/tray-main-deps.ts delete mode 100644 src/main/runtime/yomitan-extension-loader-main-deps.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 04f6861e..3f94d7ea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,7 +56,12 @@ jobs: - name: Install mpv and ffmpeg (macOS) if: runner.os == 'macOS' - run: brew install mpv ffmpeg + run: | + # Runner images bake in different Homebrew metadata, so without an update the + # installed mpv build depends on which image the job lands on. + brew update + brew install mpv ffmpeg + mpv --version - name: Install mpv and ffmpeg (Windows) if: runner.os == 'Windows' diff --git a/bun.lock b/bun.lock index 01b147e0..d45aaeaa 100644 --- a/bun.lock +++ b/bun.lock @@ -37,7 +37,7 @@ "brace-expansion": "5.0.11", "electron-builder-squirrel-windows": "26.16.1", "fast-uri": "3.1.7", - "follow-redirects": "1.16.1", + "follow-redirects": "1.16.2", "form-data": "4.0.6", "ip-address": "10.2.0", "js-yaml": "4.3.2", @@ -351,7 +351,7 @@ "filelist": ["filelist@1.0.6", "", { "dependencies": { "minimatch": "^5.0.1" } }, "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA=="], - "follow-redirects": ["follow-redirects@1.16.1", "", { "peerDependencies": { "debug": "*" }, "optionalPeers": ["debug"] }, "sha512-FNvFGzoMLWmE6Yj9spb/zjd7yiNCHiAW9/Tg9CXrQ8wuu32HtlJOwWO11OJafl5FfY3DxTdQ0vj42zU1kvv5jg=="], + "follow-redirects": ["follow-redirects@1.16.2", "", {}, "sha512-/kVP2U8z596Ia0gwFrwyIeo74ta5aVmSeU1FfAUMAmcEP5EP7zydmag3SLiN64NaRKef3lxsq7i/g7ltJQDGow=="], "form-data": ["form-data@4.0.6", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.4", "mime-types": "^2.1.35" } }, "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ=="], diff --git a/docs/architecture/layering.md b/docs/architecture/layering.md index 794f3c3e..8dc4b78b 100644 --- a/docs/architecture/layering.md +++ b/docs/architecture/layering.md @@ -19,6 +19,8 @@ Renderer, launcher, plugin, and stats each keep their own local layering and sho ## Rules - Keep `src/main.ts` thin; wire, do not implement. +- Pass deps straight to a handler factory (`createXHandler({...})`). Do not add + `*-main-deps.ts` builders that only re-wrap deps; existing ones are being removed. - Prefer injecting dependencies from `src/main/` instead of reaching outward from core services. - Keep side effects explicit and close to composition boundaries. - Put reusable business logic in focused services, not in top-level lifecycle files. diff --git a/docs/workflow/verification.md b/docs/workflow/verification.md index 06a6960e..a58cfba8 100644 --- a/docs/workflow/verification.md +++ b/docs/workflow/verification.md @@ -125,7 +125,8 @@ whenever the behavior does not depend on a specific compositor. - Scenarios live in `e2e/*.e2e.test.ts` and share one session per file through `startE2eSession()`. Pass `config` to override settings for a scenario file. - On a failed startup the temp dir is kept and its path is in the error. Set - `SUBMINER_E2E_KEEP=1` to keep it after any run (`app-stdio.log`, `app.log`, `mpv.log`). + `SUBMINER_E2E_KEEP=1` to keep it after any run (`app-stdio.log`, `app.log`, `mpv.log`, + `mpv-stdio.log`). For exploratory checks, keep one session up and poke at it: diff --git a/e2e/harness/mpv.ts b/e2e/harness/mpv.ts index d9b3b9a2..3a92be0f 100644 --- a/e2e/harness/mpv.ts +++ b/e2e/harness/mpv.ts @@ -15,25 +15,38 @@ export type MpvClient = { type MpvReply = { request_id?: number; error?: string; data?: unknown }; +function videoOutputArgs(display: E2eDisplay): string[] { + // Xvfb has no GPU; the software X11 output always works there. + if (display.kind === 'xvfb') return ['--vo=x11']; + // gpu-next's Vulkan path (libplacebo over MoltenVK) segfaults on the first frame + // on GitHub's paravirtual macOS GPU with libplacebo 7.387. `--vo=libmpv` is mpv's + // cocoa-cb OpenGL output, which avoids it and still opens the window the overlay tracks. + if (process.platform === 'darwin') return ['--vo=libmpv']; + return []; +} + export async function startMpv(options: { socketPath: string; mediaPath: string; subtitlePath: string; + /** mpv's own verbose log (`--log-file`); `--no-terminal` silences its console output. */ logPath: string; + /** Process stdout/stderr, which still catches crash output from mpv's libraries. */ + stdioLogPath: string; display: E2eDisplay; }): Promise { - const log = fs.openSync(options.logPath, 'w'); + const log = fs.openSync(options.stdioLogPath, 'w'); const child = spawn( 'mpv', [ '--no-config', '--no-terminal', + `--log-file=${options.logPath}`, '--pause', '--keep-open=yes', '--ao=null', `--geometry=${E2E_SCREEN.width}x${E2E_SCREEN.height}+0+0`, - // Xvfb has no GPU; the software X11 output always works there. - ...(options.display.kind === 'xvfb' ? ['--vo=x11'] : []), + ...videoOutputArgs(options.display), `--input-ipc-server=${options.socketPath}`, `--sub-file=${options.subtitlePath}`, options.mediaPath, @@ -58,13 +71,31 @@ function tryConnect(socketPath: string): Promise { }); } -export async function connectMpv(socketPath: string): Promise { +/** + * Connects to mpv's JSON IPC socket. Once the socket closes (mpv exited or + * crashed), pending and later commands reject instead of hanging until the test + * timeout; `describeClose` adds context such as mpv's exit status. + */ +export async function connectMpv( + socketPath: string, + describeClose: () => string = () => 'mpv IPC connection closed', +): Promise { const socket = await waitUntil(() => tryConnect(socketPath), { description: `mpv IPC socket ${socketPath}`, }); const pending = new Map void>(); let nextRequestId = 1; let buffer = ''; + let closedError: Error | null = null; + + const failPending = () => { + closedError ??= new Error(describeClose()); + for (const settle of pending.values()) settle({ error: closedError.message }); + pending.clear(); + }; + socket.on('close', failPending); + // 'close' always follows 'error'; listening here keeps the error from being thrown. + socket.on('error', () => {}); socket.setEncoding('utf8'); socket.on('data', (chunk: string) => { @@ -83,6 +114,10 @@ export async function connectMpv(socketPath: string): Promise { return { command: (...args) => new Promise((resolve, reject) => { + if (closedError) { + reject(new Error(`mpv ${JSON.stringify(args)} failed: ${closedError.message}`)); + return; + } const requestId = nextRequestId++; pending.set(requestId, (reply) => { if (reply.error === 'success') resolve(reply.data); diff --git a/e2e/harness/session.ts b/e2e/harness/session.ts index cc153491..8022535c 100644 --- a/e2e/harness/session.ts +++ b/e2e/harness/session.ts @@ -265,15 +265,25 @@ export async function startE2eSession(options: E2eSessionOptions = {}): Promise< settings.close(); await stopProcess(seeder.child); + const mpvLogPath = path.join(root, 'mpv.log'); + const mpvStdioLogPath = path.join(root, 'mpv-stdio.log'); const mpvProcess = await startMpv({ socketPath: mpvSocketPath, mediaPath, subtitlePath, - logPath: path.join(root, 'mpv.log'), + logPath: mpvLogPath, + stdioLogPath: mpvStdioLogPath, display, }); teardown.push(() => stopProcess(mpvProcess)); - const mpv = await connectMpv(mpvSocketPath); + const mpv = await connectMpv(mpvSocketPath, () => { + const { exitCode, signalCode } = mpvProcess; + const status = + exitCode === null && signalCode === null + ? 'mpv IPC connection closed' + : `mpv exited (code ${String(exitCode)}, signal ${String(signalCode)})`; + return `${status}; see ${mpvLogPath} and ${mpvStdioLogPath}`; + }); teardown.push(mpv.close); const app = await launchApp(['--start']); diff --git a/launcher/mpv.test.ts b/launcher/mpv.test.ts index 5d00f9cb..e4f45e3b 100644 --- a/launcher/mpv.test.ts +++ b/launcher/mpv.test.ts @@ -172,33 +172,30 @@ test('runAppCommandCaptureOutput strips ELECTRON_RUN_AS_NODE from app child env' }); }); -test( - 'runAppCommandCaptureOutput transports Linux AppImage args through environment', - async () => { - // Bun's node:test shim ignores the `skip` option, so gate the platform here. - if (process.platform !== 'linux') return; - await withTempCase(({ dir }) => { - const appPath = writeFakeApp( - dir, - [ - 'printf "args:%s\\n" "$*"', - 'printf "argc:%s\\n" "$SUBMINER_APP_ARGC"', - 'printf "arg0:%s\\n" "$SUBMINER_APP_ARG_0"', - 'printf "arg1:%s\\n" "$SUBMINER_APP_ARG_1"', - ], - 'SubMiner.AppImage', - ); +test('runAppCommandCaptureOutput transports Linux AppImage args through environment', async () => { + // Bun's node:test shim ignores the `skip` option, so gate the platform here. + if (process.platform !== 'linux') return; + await withTempCase(({ dir }) => { + const appPath = writeFakeApp( + dir, + [ + 'printf "args:%s\\n" "$*"', + 'printf "argc:%s\\n" "$SUBMINER_APP_ARGC"', + 'printf "arg0:%s\\n" "$SUBMINER_APP_ARG_0"', + 'printf "arg1:%s\\n" "$SUBMINER_APP_ARG_1"', + ], + 'SubMiner.AppImage', + ); - const result = runAppCommandCaptureOutput(appPath, ['--app-ping', '--socket']); + const result = runAppCommandCaptureOutput(appPath, ['--app-ping', '--socket']); - assert.equal(result.status, 0); - assert.match(result.stdout, /^args:\n/m); - assert.match(result.stdout, /^argc:2\n/m); - assert.match(result.stdout, /^arg0:--app-ping\n/m); - assert.match(result.stdout, /^arg1:--socket\n/m); - }); - }, -); + assert.equal(result.status, 0); + assert.match(result.stdout, /^args:\n/m); + assert.match(result.stdout, /^argc:2\n/m); + assert.match(result.stdout, /^arg0:--app-ping\n/m); + assert.match(result.stdout, /^arg1:--socket\n/m); + }); +}); test('runAppCommandCaptureOutput runs Linux AppImage sync in Node-only mode', async () => { await withTempCase(({ dir }) => { diff --git a/package.json b/package.json index 1e5206d9..350a98de 100644 --- a/package.json +++ b/package.json @@ -93,7 +93,7 @@ "brace-expansion": "5.0.11", "electron-builder-squirrel-windows": "26.16.1", "fast-uri": "3.1.7", - "follow-redirects": "1.16.1", + "follow-redirects": "1.16.2", "form-data": "4.0.6", "ip-address": "10.2.0", "js-yaml": "4.3.2", diff --git a/scripts/aur-release-download.test.ts b/scripts/aur-release-download.test.ts index 4dfaa76a..3b673883 100644 --- a/scripts/aur-release-download.test.ts +++ b/scripts/aur-release-download.test.ts @@ -29,6 +29,10 @@ test.each([false, true])( port: 0, fetch(request) { const pathname = new URL(request.url).pathname; + // Ignore stray local probes of the ephemeral port; only count the script's downloads. + if (!pathname.startsWith('/ksyasuda/SubMiner/releases/download/')) { + return new Response('not found', { status: 404 }); + } requests.push(pathname); if (unavailable || requests.length === 1) return new Response('try again', { status: 503 }); const name = pathname.split('/').at(-1); diff --git a/src/main.ts b/src/main.ts index df743732..af92e93d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -155,7 +155,6 @@ import { buildAnilistSetupUrl, consumeAnilistSetupCallbackUrl, createAnilistStateRuntime, - createBuildOpenAnilistSetupWindowMainDepsHandler, createMaybeFocusExistingAnilistSetupWindowHandler, createOpenAnilistSetupWindowHandler, findAnilistSetupDeepLinkArgvUrl, @@ -168,8 +167,6 @@ import { DEFAULT_MIN_WATCH_RATIO } from './shared/watch-threshold'; import { shouldShowTexthookerTrayEntry } from './main/runtime/tray-main-actions'; import { createApplyJellyfinMpvDefaultsHandler, - createBuildApplyJellyfinMpvDefaultsMainDepsHandler, - createBuildGetDefaultSocketPathMainDepsHandler, createGetDefaultSocketPathHandler, buildJellyfinSetupFormHtml, parseJellyfinSetupSubmissionUrl, @@ -184,22 +181,7 @@ import { createBuildConfigHotReloadRuntimeMainDepsHandler, createBuildWatchConfigPathMainDepsHandler, createWatchConfigPathHandler, - createBuildOverlayContentMeasurementStoreMainDepsHandler, - createBuildOverlayModalRuntimeMainDepsHandler, - createBuildAppendClipboardVideoToQueueMainDepsHandler, - createBuildHandleOverlayModalClosedMainDepsHandler, - createBuildLoadSubtitlePositionMainDepsHandler, - createBuildSaveSubtitlePositionMainDepsHandler, - createBuildFieldGroupingOverlayMainDepsHandler, - createBuildGetFieldGroupingResolverMainDepsHandler, - createBuildSetFieldGroupingResolverMainDepsHandler, createBuildOverlayVisibilityRuntimeMainDepsHandler, - createBuildBroadcastRuntimeOptionsChangedMainDepsHandler, - createBuildGetRuntimeOptionsStateMainDepsHandler, - createBuildOpenRuntimeOptionsPaletteMainDepsHandler, - createBuildRestorePreviousSecondarySubVisibilityMainDepsHandler, - createBuildSendToActiveOverlayWindowMainDepsHandler, - createBuildSetOverlayDebugVisualizationEnabledMainDepsHandler, createTrayRuntimeHandlers, createOverlayVisibilityRuntime, createGetRuntimeOptionsStateHandler, @@ -219,12 +201,6 @@ import { resolveSubtitleStyleForRenderer, } from './main/runtime/domains/overlay'; import { - createBuildAnilistStateRuntimeMainDepsHandler, - createBuildConfigDerivedRuntimeMainDepsHandler, - createBuildImmersionMediaRuntimeMainDepsHandler, - createBuildMainSubsyncRuntimeMainDepsHandler, - createBuildSubtitleProcessingControllerMainDepsHandler, - createBuildMediaRuntimeMainDepsHandler, createBuildDictionaryRootsMainHandler, createBuildFrequencyDictionaryRootsMainHandler, createBuildFrequencyDictionaryRuntimeMainDepsHandler, @@ -244,14 +220,6 @@ import { resolvePrimarySubtitle, } from './main/runtime/primary-subtitle-text'; import { - createBuildCopyCurrentSubtitleMainDepsHandler, - createBuildHandleMineSentenceDigitMainDepsHandler, - createBuildHandleMultiCopyDigitMainDepsHandler, - createBuildMarkLastCardAsAudioCardMainDepsHandler, - createBuildMineSentenceCardMainDepsHandler, - createBuildRefreshKnownWordCacheMainDepsHandler, - createBuildTriggerFieldGroupingMainDepsHandler, - createBuildUpdateLastCardFromClipboardMainDepsHandler, createMarkLastCardAsAudioCardHandler, createMineSentenceCardHandler, createRefreshKnownWordCacheHandler, @@ -291,7 +259,6 @@ import { createOverlayContentMeasurementStore, createOverlayManager, createOverlayWindow as createOverlayWindowCore, - createSubtitleProcessingController, createTokenizerDepsRuntime, cycleSecondarySubMode as cycleSecondarySubModeCore, deleteYomitanDictionaryByTitle, @@ -428,8 +395,10 @@ import { shouldQuitOnWindowAllClosedForTrayState, } from './main/runtime/startup-tray-policy'; import { createLogExportTrayRuntime } from './main/runtime/log-export-tray'; -import { createImmersionTrackerStartupHandler } from './main/runtime/immersion-startup'; -import { createBuildImmersionTrackerStartupMainDepsHandler } from './main/runtime/immersion-startup-main-deps'; +import { + createImmersionTrackerStartupHandler, + type ImmersionTrackerStartupDeps, +} from './main/runtime/immersion-startup'; import { createRunStatsCliCommandHandler, writeStatsCliCommandResponse, @@ -529,6 +498,7 @@ import { getJlptDictionarySearchPaths, } from './main/jlpt-runtime'; import { createMediaRuntimeService } from './main/media-runtime'; +import { createPrefetchAwareSubtitleProcessingController } from './main/runtime/subtitle-processing-runtime'; import { createOverlayVisibilityRuntimeService } from './main/overlay-visibility-runtime'; import { createDiscordPresenceRuntime } from './main/runtime/discord-presence-runtime'; import { createCharacterDictionaryRuntimeService } from './main/character-dictionary-runtime'; @@ -707,15 +677,10 @@ let backgroundWarmupsStarted = false; let yomitanLoadInFlight: Promise | null = null; let notifyAnilistTokenStoreWarning: (message: string) => void = () => {}; -const buildApplyJellyfinMpvDefaultsMainDepsHandler = - createBuildApplyJellyfinMpvDefaultsMainDepsHandler({ - sendMpvCommandRuntime: (client, command) => sendMpvCommandRuntime(client, command), - jellyfinLangPref: JELLYFIN_LANG_PREF, - }); -const applyJellyfinMpvDefaultsMainDeps = buildApplyJellyfinMpvDefaultsMainDepsHandler(); -const applyJellyfinMpvDefaultsHandler = createApplyJellyfinMpvDefaultsHandler( - applyJellyfinMpvDefaultsMainDeps, -); +const applyJellyfinMpvDefaultsHandler = createApplyJellyfinMpvDefaultsHandler({ + sendMpvCommandRuntime: (client, command) => sendMpvCommandRuntime(client, command), + jellyfinLangPref: JELLYFIN_LANG_PREF, +}); const isDev = process.argv.includes('--dev') || process.argv.includes('--debug'); const texthookerService = new Texthooker(() => { @@ -758,11 +723,9 @@ const texthookerService = new Texthooker(() => { }); let syncOverlayShortcutsForModal: (isActive: boolean) => void = () => {}; let syncOverlayVisibilityForModal: () => void = () => {}; -const buildGetDefaultSocketPathMainDepsHandler = createBuildGetDefaultSocketPathMainDepsHandler({ +const getDefaultSocketPathHandler = createGetDefaultSocketPathHandler({ platform: process.platform, }); -const getDefaultSocketPathMainDeps = buildGetDefaultSocketPathMainDepsHandler(); -const getDefaultSocketPathHandler = createGetDefaultSocketPathHandler(getDefaultSocketPathMainDeps); type BootServices = MainBootServicesResult< ConfigService, @@ -852,45 +815,46 @@ const bootServices = createMainBootServices({ }), createOverlayModalInputState, createOverlayContentMeasurementStore: ({ logger }) => { - const buildHandler = createBuildOverlayContentMeasurementStoreMainDepsHandler({ + return createOverlayContentMeasurementStore({ now: () => Date.now(), warn: (message: string) => logger.warn(message), }); - return createOverlayContentMeasurementStore(buildHandler()); }, getSyncOverlayShortcutsForModal: () => syncOverlayShortcutsForModal, getSyncOverlayVisibilityForModal: () => syncOverlayVisibilityForModal, createOverlayModalRuntime: ({ overlayManager, overlayModalInputState }) => { - const buildHandler = createBuildOverlayModalRuntimeMainDepsHandler({ - getMainWindow: () => overlayManager.getMainWindow(), - getModalWindow: () => overlayManager.getModalWindow(), - createModalWindow: () => createModalWindow(), - getModalGeometry: () => overlayGeometryRuntime.getCurrentOverlayGeometry(), - setModalWindowBounds: (geometry) => overlayManager.setModalWindowBounds(geometry), - }); - return createOverlayModalRuntimeService(buildHandler(), { - onModalStateChange: (isActive: boolean) => - overlayModalInputState.handleModalInputStateChange(isActive), - // On macOS, after the last modal closes the post-close visibility sync hides the overlay - // when neither it nor mpv is focused, and keyboard focus is left in limbo (mpv keys like - // shift-to-unpause stop working). Programmatically focusing mpv from our background helper - // is refused by macOS (most visibly in native fullscreen), so instead resign SubMiner's - // active status — exactly what a manual click does — handing focus back to the previously - // active app (mpv). The overlay is already hidden at this point, so app.hide() hides nothing - // visible; once mpv is focused the tracker re-shows the overlay above it. - onFinalModalClosed: () => { - void restoreMacOSMpvFocusAfterModalClose({ - platform: process.platform, - focusMpv: async () => { - app.hide(); - }, - getWindowTracker: () => appState.windowTracker, - updateVisibleOverlayVisibility: () => - overlayVisibilityRuntime.updateVisibleOverlayVisibility(), - warn: (message, details) => logger.warn(message, details), - }); + return createOverlayModalRuntimeService( + { + getMainWindow: () => overlayManager.getMainWindow(), + getModalWindow: () => overlayManager.getModalWindow(), + createModalWindow: () => createModalWindow(), + getModalGeometry: () => overlayGeometryRuntime.getCurrentOverlayGeometry(), + setModalWindowBounds: (geometry) => overlayManager.setModalWindowBounds(geometry), }, - }); + { + onModalStateChange: (isActive: boolean) => + overlayModalInputState.handleModalInputStateChange(isActive), + // On macOS, after the last modal closes the post-close visibility sync hides the overlay + // when neither it nor mpv is focused, and keyboard focus is left in limbo (mpv keys like + // shift-to-unpause stop working). Programmatically focusing mpv from our background helper + // is refused by macOS (most visibly in native fullscreen), so instead resign SubMiner's + // active status — exactly what a manual click does — handing focus back to the previously + // active app (mpv). The overlay is already hidden at this point, so app.hide() hides nothing + // visible; once mpv is focused the tracker re-shows the overlay above it. + onFinalModalClosed: () => { + void restoreMacOSMpvFocusAfterModalClose({ + platform: process.platform, + focusMpv: async () => { + app.hide(); + }, + getWindowTracker: () => appState.windowTracker, + updateVisibleOverlayVisibility: () => + overlayVisibilityRuntime.updateVisibleOverlayVisibility(), + warn: (message, details) => logger.warn(message, details), + }); + }, + }, + ); }, createAppState, }) as BootServices; @@ -1717,7 +1681,7 @@ function syncOverlayMpvSubtitleSuppression(): void { restoreOverlayMpvSubtitles(); } -const buildImmersionMediaRuntimeMainDepsHandler = createBuildImmersionMediaRuntimeMainDepsHandler({ +const immersionMediaRuntime = createImmersionMediaRuntime({ getResolvedConfig: () => configService.getConfig(), defaultImmersionDbPath: DEFAULT_IMMERSION_DB_PATH, getTracker: () => appState.immersionTracker, @@ -1727,7 +1691,23 @@ const buildImmersionMediaRuntimeMainDepsHandler = createBuildImmersionMediaRunti logDebug: (message) => logger.debug(message), logInfo: (message) => logger.info(message), }); -const buildAnilistStateRuntimeMainDepsHandler = createBuildAnilistStateRuntimeMainDepsHandler({ +const anilistRateLimiter = createAnilistRateLimiter(); +const statsCoverArtFetcher = createCoverArtFetcher( + anilistRateLimiter, + createLogger('main:stats-cover-art'), + { + liveAction: createLiveActionMetadataResolver( + createTmdbClient({ + resolveApiKey: createTmdbApiKeyResolver( + () => configService.getConfig().tmdb, + () => bundledTmdbApiKey, + ), + }), + createLogger('main:tmdb'), + ), + }, +); +const anilistStateRuntime = createAnilistStateRuntime({ getClientSecretState: () => appState.anilistClientSecretState, setClientSecretState: (next) => { appState.anilistClientSecretState = transitionAnilistClientSecretState( @@ -1748,14 +1728,14 @@ const buildAnilistStateRuntimeMainDepsHandler = createBuildAnilistStateRuntimeMa anilistCachedAccessToken = null; }, }); -const buildConfigDerivedRuntimeMainDepsHandler = createBuildConfigDerivedRuntimeMainDepsHandler({ +const configDerivedRuntime = createConfigDerivedRuntime({ getResolvedConfig: () => configService.getConfig(), getRuntimeOptionsManager: () => appState.runtimeOptionsManager, defaultJimakuLanguagePreference: DEFAULT_CONFIG.jimaku.languagePreference, defaultJimakuMaxEntryResults: DEFAULT_CONFIG.jimaku.maxEntryResults, defaultJimakuApiBaseUrl: DEFAULT_CONFIG.jimaku.apiBaseUrl, }); -const buildMainSubsyncRuntimeMainDepsHandler = createBuildMainSubsyncRuntimeMainDepsHandler({ +const subsyncRuntime = createMainSubsyncRuntime({ getMpvClient: () => appState.mpvClient, getResolvedConfig: () => configService.getConfig(), getSubsyncInProgress: () => appState.subsyncInProgress, @@ -1771,28 +1751,6 @@ const buildMainSubsyncRuntimeMainDepsHandler = createBuildMainSubsyncRuntimeMain ); }, }); -const immersionMediaRuntime = createImmersionMediaRuntime( - buildImmersionMediaRuntimeMainDepsHandler(), -); -const anilistRateLimiter = createAnilistRateLimiter(); -const statsCoverArtFetcher = createCoverArtFetcher( - anilistRateLimiter, - createLogger('main:stats-cover-art'), - { - liveAction: createLiveActionMetadataResolver( - createTmdbClient({ - resolveApiKey: createTmdbApiKeyResolver( - () => configService.getConfig().tmdb, - () => bundledTmdbApiKey, - ), - }), - createLogger('main:tmdb'), - ), - }, -); -const anilistStateRuntime = createAnilistStateRuntime(buildAnilistStateRuntimeMainDepsHandler()); -const configDerivedRuntime = createConfigDerivedRuntime(buildConfigDerivedRuntimeMainDepsHandler()); -const subsyncRuntime = createMainSubsyncRuntime(buildMainSubsyncRuntimeMainDepsHandler()); const currentMediaTokenizationGate = createCurrentMediaTokenizationGate(); const startupOsdSequencer = createStartupOsdSequencer({ getNotificationType: () => overlayNotificationsRuntime.getConfiguredStatusNotificationType(), @@ -2061,30 +2019,18 @@ function signalCurrentSubtitleAutoplayReady(): void { ); } } -const buildSubtitleProcessingControllerMainDepsHandler = - createBuildSubtitleProcessingControllerMainDepsHandler({ - tokenizeSubtitle: async (text: string) => - tokenizeSubtitleDeferred ? await tokenizeSubtitleDeferred(text) : { text, tokens: null }, - // Controller emits never release the prefetch pause: the first emit for an - // uncached line is the provisional plain payload, sent before tokenization - // starts, so resuming on it would put prefetch back in contention with the - // on-screen line for the single parser window. - emitSubtitle: (payload) => emitSubtitlePayload(payload, { resumePrefetch: false }), - // The pause is released once the controller has no work left, which covers - // the runs that end without an emit (suppressed duplicate, failed - // tokenization) as well as the ones that deliver a payload. - onProcessingSettled: () => { - subtitlePrefetchService?.resume(); - }, - logDebug: (message) => { - logger.debug(`[subtitle-processing] ${message}`); - }, - now: () => Date.now(), - }); -const subtitleProcessingControllerMainDeps = buildSubtitleProcessingControllerMainDepsHandler(); -const subtitleProcessingController = createSubtitleProcessingController( - subtitleProcessingControllerMainDeps, -); +const subtitleProcessingController = createPrefetchAwareSubtitleProcessingController({ + tokenizeSubtitle: async (text: string) => + tokenizeSubtitleDeferred ? await tokenizeSubtitleDeferred(text) : { text, tokens: null }, + emitSubtitlePayload: (payload, options) => emitSubtitlePayload(payload, options), + resumePrefetch: () => { + subtitlePrefetchService?.resume(); + }, + logDebug: (message) => { + logger.debug(`[subtitle-processing] ${message}`); + }, + now: () => Date.now(), +}); let subtitlePrefetchService: SubtitlePrefetchService | null = null; let lastObservedTimePos = 0; @@ -2645,79 +2591,68 @@ const frequencyDictionaryRuntime = createFrequencyDictionaryRuntimeService( })(), ); -const buildGetFieldGroupingResolverMainDepsHandler = - createBuildGetFieldGroupingResolverMainDepsHandler({ - getResolver: () => appState.fieldGroupingResolver, - }); -const getFieldGroupingResolverMainDeps = buildGetFieldGroupingResolverMainDepsHandler(); -const getFieldGroupingResolverHandler = createGetFieldGroupingResolverHandler( - getFieldGroupingResolverMainDeps, -); +const getFieldGroupingResolverHandler = createGetFieldGroupingResolverHandler({ + getResolver: () => appState.fieldGroupingResolver, +}); -const buildSetFieldGroupingResolverMainDepsHandler = - createBuildSetFieldGroupingResolverMainDepsHandler({ - setResolver: (resolver) => { - appState.fieldGroupingResolver = resolver; - }, - nextSequence: () => { - appState.fieldGroupingResolverSequence += 1; - return appState.fieldGroupingResolverSequence; - }, - getSequence: () => appState.fieldGroupingResolverSequence, - }); -const setFieldGroupingResolverMainDeps = buildSetFieldGroupingResolverMainDepsHandler(); -const setFieldGroupingResolverHandler = createSetFieldGroupingResolverHandler( - setFieldGroupingResolverMainDeps, -); +const setFieldGroupingResolverHandler = createSetFieldGroupingResolverHandler({ + setResolver: (resolver) => { + appState.fieldGroupingResolver = resolver; + }, + nextSequence: () => { + appState.fieldGroupingResolverSequence += 1; + return appState.fieldGroupingResolverSequence; + }, + getSequence: () => appState.fieldGroupingResolverSequence, +}); -const fieldGroupingOverlayRuntime = createFieldGroupingOverlayRuntime( - createBuildFieldGroupingOverlayMainDepsHandler({ - getMainWindow: () => overlayManager.getMainWindow(), - getVisibleOverlayVisible: () => overlayManager.getVisibleOverlayVisible(), - setVisibleOverlayVisible: (visible) => setVisibleOverlayVisible(visible), - getResolver: () => getFieldGroupingResolverHandler(), - setResolver: (resolver) => setFieldGroupingResolverHandler(resolver), - getRestoreVisibleOverlayOnModalClose: () => - overlayModalRuntime.getRestoreVisibleOverlayOnModalClose(), - waitForModalOpen: (modal, timeoutMs) => overlayModalRuntime.waitForModalOpen(modal, timeoutMs), - handleOverlayModalClosed: (modal) => overlayModalRuntime.handleOverlayModalClosed(modal), - logWarn: (message) => logger.warn(message), - ensureOverlayStartupPrereqs: () => ensureOverlayStartupPrereqs(), - ensureOverlayWindowsReadyForVisibilityActions: () => - ensureOverlayWindowsReadyForVisibilityActions(), - sendToActiveOverlayWindow: (channel, payload, runtimeOptions) => - overlayModalRuntime.sendToActiveOverlayWindow(channel, payload, runtimeOptions), - })(), -); +const fieldGroupingOverlayRuntime = createFieldGroupingOverlayRuntime({ + getMainWindow: () => overlayManager.getMainWindow(), + getVisibleOverlayVisible: () => overlayManager.getVisibleOverlayVisible(), + setVisibleOverlayVisible: (visible) => setVisibleOverlayVisible(visible), + getResolver: () => getFieldGroupingResolverHandler(), + setResolver: (resolver) => setFieldGroupingResolverHandler(resolver), + getRestoreVisibleOverlayOnModalClose: () => + overlayModalRuntime.getRestoreVisibleOverlayOnModalClose(), + // These are optional on the runtime options, so a missing forward compiles silently, but + // dropping them leaves the field grouping modal with no modal-open ack/retry, no teardown on + // failure, and no warn logging. + waitForModalOpen: (modal, timeoutMs) => overlayModalRuntime.waitForModalOpen(modal, timeoutMs), + handleOverlayModalClosed: (modal) => overlayModalRuntime.handleOverlayModalClosed(modal), + logWarn: (message) => logger.warn(message), + ensureOverlayStartupPrereqs: () => ensureOverlayStartupPrereqs(), + ensureOverlayWindowsReadyForVisibilityActions: () => + ensureOverlayWindowsReadyForVisibilityActions(), + sendToVisibleOverlay: (channel, payload, runtimeOptions) => + overlayModalRuntime.sendToActiveOverlayWindow(channel, payload, runtimeOptions), +}); const createFieldGroupingCallback = fieldGroupingOverlayRuntime.createFieldGroupingCallback; const SUBTITLE_POSITIONS_DIR = path.join(CONFIG_DIR, 'subtitle-positions'); -const mediaRuntime = createMediaRuntimeService( - createBuildMediaRuntimeMainDepsHandler({ - isRemoteMediaPath: (mediaPath) => isRemoteMediaPath(mediaPath), - loadSubtitlePosition: () => loadSubtitlePosition(), - getCurrentMediaPath: () => appState.currentMediaPath, - getPendingSubtitlePosition: () => appState.pendingSubtitlePosition, - getSubtitlePositionsDir: () => SUBTITLE_POSITIONS_DIR, - setCurrentMediaPath: (nextPath: string | null) => { - appState.currentMediaPath = nextPath; - }, - clearPendingSubtitlePosition: () => { - appState.pendingSubtitlePosition = null; - }, - setSubtitlePosition: (position: SubtitlePosition | null) => { - appState.subtitlePosition = position; - }, - broadcastToOverlayWindows: (channel, payload) => { - overlayManager.broadcastToOverlayWindows(channel, payload); - }, - getCurrentMediaTitle: () => appState.currentMediaTitle, - setCurrentMediaTitle: (title) => { - appState.currentMediaTitle = title; - }, - })(), -); +const mediaRuntime = createMediaRuntimeService({ + isRemoteMediaPath: (mediaPath) => isRemoteMediaPath(mediaPath), + loadSubtitlePosition: () => loadSubtitlePosition(), + getCurrentMediaPath: () => appState.currentMediaPath, + getPendingSubtitlePosition: () => appState.pendingSubtitlePosition, + getSubtitlePositionsDir: () => SUBTITLE_POSITIONS_DIR, + setCurrentMediaPath: (nextPath: string | null) => { + appState.currentMediaPath = nextPath; + }, + clearPendingSubtitlePosition: () => { + appState.pendingSubtitlePosition = null; + }, + setSubtitlePosition: (position: SubtitlePosition | null) => { + appState.subtitlePosition = position; + }, + broadcastSubtitlePosition: (position) => { + overlayManager.broadcastToOverlayWindows('subtitle-position:set', position); + }, + getCurrentMediaTitle: () => appState.currentMediaTitle, + setCurrentMediaTitle: (title) => { + appState.currentMediaTitle = title; + }, +}); const characterDictionaryRuntime = createCharacterDictionaryRuntimeService({ userDataPath: USER_DATA_PATH, @@ -2993,20 +2928,10 @@ const visibleOverlayInteractionRuntime = createVisibleOverlayInteractionRuntime( getOverlayForegroundSeparateWindows: () => getOverlayForegroundSeparateWindows(), }); -const buildGetRuntimeOptionsStateMainDepsHandler = createBuildGetRuntimeOptionsStateMainDepsHandler( - { - getRuntimeOptionsManager: () => appState.runtimeOptionsManager, - }, -); -const getRuntimeOptionsStateMainDeps = buildGetRuntimeOptionsStateMainDepsHandler(); -const getRuntimeOptionsStateHandler = createGetRuntimeOptionsStateHandler( - getRuntimeOptionsStateMainDeps, -); +const getRuntimeOptionsStateHandler = createGetRuntimeOptionsStateHandler({ + getRuntimeOptionsManager: () => appState.runtimeOptionsManager, +}); -const buildRestorePreviousSecondarySubVisibilityMainDepsHandler = - createBuildRestorePreviousSecondarySubVisibilityMainDepsHandler({ - getMpvClient: () => appState.mpvClient, - }); syncOverlayVisibilityForModal = () => { overlayVisibilityRuntime.updateVisibleOverlayVisibility(); }; @@ -3031,42 +2956,31 @@ const { maybeStartOverlayLoadingOsd, } = overlayNotificationsRuntime; -const buildBroadcastRuntimeOptionsChangedMainDepsHandler = - createBuildBroadcastRuntimeOptionsChangedMainDepsHandler({ +const overlayVisibilityComposer = composeOverlayVisibilityRuntime({ + overlayVisibilityRuntime, + restorePreviousSecondarySubVisibilityMainDeps: { + getMpvClient: () => appState.mpvClient, + }, + broadcastRuntimeOptionsChangedMainDeps: { broadcastRuntimeOptionsChangedRuntime, getRuntimeOptionsState: () => getRuntimeOptionsStateHandler(), broadcastToOverlayWindows: (channel, ...args) => overlayManager.broadcastToOverlayWindows(channel, ...args), - }); - -const buildSendToActiveOverlayWindowMainDepsHandler = - createBuildSendToActiveOverlayWindowMainDepsHandler({ + }, + sendToActiveOverlayWindowMainDeps: { sendToActiveOverlayWindowRuntime: (channel, payload, runtimeOptions) => overlayModalRuntime.sendToActiveOverlayWindow(channel, payload, runtimeOptions), - }); - -const buildSetOverlayDebugVisualizationEnabledMainDepsHandler = - createBuildSetOverlayDebugVisualizationEnabledMainDepsHandler({ + }, + setOverlayDebugVisualizationEnabledMainDeps: { setOverlayDebugVisualizationEnabledRuntime, getCurrentEnabled: () => appState.overlayDebugVisualizationEnabled, setCurrentEnabled: (next) => { appState.overlayDebugVisualizationEnabled = next; }, - }); - -const buildOpenRuntimeOptionsPaletteMainDepsHandler = - createBuildOpenRuntimeOptionsPaletteMainDepsHandler({ + }, + openRuntimeOptionsPaletteMainDeps: { openRuntimeOptionsPaletteRuntime: () => overlayModalRuntime.openRuntimeOptionsPalette(), - }); -const overlayVisibilityComposer = composeOverlayVisibilityRuntime({ - overlayVisibilityRuntime, - restorePreviousSecondarySubVisibilityMainDeps: - buildRestorePreviousSecondarySubVisibilityMainDepsHandler(), - broadcastRuntimeOptionsChangedMainDeps: buildBroadcastRuntimeOptionsChangedMainDepsHandler(), - sendToActiveOverlayWindowMainDeps: buildSendToActiveOverlayWindowMainDepsHandler(), - setOverlayDebugVisualizationEnabledMainDeps: - buildSetOverlayDebugVisualizationEnabledMainDepsHandler(), - openRuntimeOptionsPaletteMainDeps: buildOpenRuntimeOptionsPaletteMainDepsHandler(), + }, }); function createOverlayHostedModalOpenDeps(): { @@ -3881,56 +3795,50 @@ const { const maybeFocusExistingAnilistSetupWindow = createMaybeFocusExistingAnilistSetupWindowHandler({ getSetupWindow: () => appState.anilistSetupWindow, }); -const buildOpenAnilistSetupWindowMainDepsHandler = createBuildOpenAnilistSetupWindowMainDepsHandler( - { - maybeFocusExistingSetupWindow: maybeFocusExistingAnilistSetupWindow, - createSetupWindow: createCreateAnilistSetupWindowHandler({ - createBrowserWindow: (options) => new BrowserWindow(options), +const openAnilistSetupWindowHandler = createOpenAnilistSetupWindowHandler({ + maybeFocusExistingSetupWindow: maybeFocusExistingAnilistSetupWindow, + createSetupWindow: createCreateAnilistSetupWindowHandler({ + createBrowserWindow: (options) => new BrowserWindow(options), + }), + buildAuthorizeUrl: () => + buildAnilistSetupUrl({ + authorizeUrl: ANILIST_SETUP_CLIENT_ID_URL, + clientId: ANILIST_DEFAULT_CLIENT_ID, + responseType: ANILIST_SETUP_RESPONSE_TYPE, }), - buildAuthorizeUrl: () => - buildAnilistSetupUrl({ - authorizeUrl: ANILIST_SETUP_CLIENT_ID_URL, - clientId: ANILIST_DEFAULT_CLIENT_ID, - responseType: ANILIST_SETUP_RESPONSE_TYPE, - }), - consumeCallbackUrl: (rawUrl) => consumeAnilistSetupTokenFromUrl(rawUrl), - openSetupInBrowser: (authorizeUrl) => - openAnilistSetupInBrowser({ - authorizeUrl, - openExternal: (url) => shell.openExternal(url), - logError: (message, error) => logger.error(message, error), - }), - loadManualTokenEntry: (setupWindow, authorizeUrl) => - loadAnilistManualTokenEntry({ - setupWindow: setupWindow as BrowserWindow, - authorizeUrl, - developerSettingsUrl: ANILIST_DEVELOPER_SETTINGS_URL, - logWarn: (message, data) => logger.warn(message, data), - }), - redirectUri: ANILIST_REDIRECT_URI, - developerSettingsUrl: ANILIST_DEVELOPER_SETTINGS_URL, - isAllowedExternalUrl: (url) => isAllowedAnilistExternalUrl(url), - isAllowedNavigationUrl: (url) => isAllowedAnilistSetupNavigationUrl(url), - logWarn: (message, details) => logger.warn(message, details), - logError: (message, details) => logger.error(message, details), - clearSetupWindow: () => { - appState.anilistSetupWindow = null; - }, - setSetupPageOpened: (opened) => { - appState.anilistSetupPageOpened = opened; - }, - setSetupWindow: (setupWindow) => { - appState.anilistSetupWindow = setupWindow as BrowserWindow; - }, - openExternal: (url) => { - void shell.openExternal(url); - }, + consumeCallbackUrl: (rawUrl) => consumeAnilistSetupTokenFromUrl(rawUrl), + openSetupInBrowser: (authorizeUrl) => + openAnilistSetupInBrowser({ + authorizeUrl, + openExternal: (url) => shell.openExternal(url), + logError: (message, error) => logger.error(message, error), + }), + loadManualTokenEntry: (setupWindow, authorizeUrl) => + loadAnilistManualTokenEntry({ + setupWindow: setupWindow as BrowserWindow, + authorizeUrl, + developerSettingsUrl: ANILIST_DEVELOPER_SETTINGS_URL, + logWarn: (message, data) => logger.warn(message, data), + }), + redirectUri: ANILIST_REDIRECT_URI, + developerSettingsUrl: ANILIST_DEVELOPER_SETTINGS_URL, + isAllowedExternalUrl: (url) => isAllowedAnilistExternalUrl(url), + isAllowedNavigationUrl: (url) => isAllowedAnilistSetupNavigationUrl(url), + logWarn: (message, details) => logger.warn(message, details), + logError: (message, details) => logger.error(message, details), + clearSetupWindow: () => { + appState.anilistSetupWindow = null; }, -); - -const openAnilistSetupWindowHandler = createOpenAnilistSetupWindowHandler( - buildOpenAnilistSetupWindowMainDepsHandler(), -); + setSetupPageOpened: (opened) => { + appState.anilistSetupPageOpened = opened; + }, + setSetupWindow: (setupWindow) => { + appState.anilistSetupWindow = setupWindow as BrowserWindow; + }, + openExternal: (url) => { + void shell.openExternal(url); + }, +}); const { refreshAnilistClientSecretState, @@ -4191,7 +4099,7 @@ const rememberAnilistAttemptedUpdate = (key: string): void => { ); }; -const buildLoadSubtitlePositionMainDepsHandler = createBuildLoadSubtitlePositionMainDepsHandler({ +const loadSubtitlePosition = createLoadSubtitlePositionHandler({ loadSubtitlePositionCore: () => loadSubtitlePositionCore({ currentMediaPath: appState.currentMediaPath, @@ -4202,10 +4110,8 @@ const buildLoadSubtitlePositionMainDepsHandler = createBuildLoadSubtitlePosition appState.subtitlePosition = position; }, }); -const loadSubtitlePositionMainDeps = buildLoadSubtitlePositionMainDepsHandler(); -const loadSubtitlePosition = createLoadSubtitlePositionHandler(loadSubtitlePositionMainDeps); -const buildSaveSubtitlePositionMainDepsHandler = createBuildSaveSubtitlePositionMainDepsHandler({ +const saveSubtitlePosition = createSaveSubtitlePositionHandler({ saveSubtitlePositionCore: (position) => { saveSubtitlePositionCore({ position, @@ -4223,8 +4129,6 @@ const buildSaveSubtitlePositionMainDepsHandler = createBuildSaveSubtitlePosition appState.subtitlePosition = position; }, }); -const saveSubtitlePositionMainDeps = buildSaveSubtitlePositionMainDepsHandler(); -const saveSubtitlePosition = createSaveSubtitlePositionHandler(saveSubtitlePositionMainDeps); registerSubminerProtocolClient(); let flushPendingMpvLogWrites = (): void => {}; @@ -4377,9 +4281,7 @@ const resolveLegacyVocabularyPos = async (row: { return null; }; -const immersionTrackerStartupMainDeps: Parameters< - typeof createBuildImmersionTrackerStartupMainDepsHandler ->[0] = { +const immersionTrackerStartupMainDeps: ImmersionTrackerStartupDeps = { getResolvedConfig: () => configService.getConfig(), getConfiguredDbPath: () => immersionMediaRuntime.getConfiguredDbPath(), createTrackerService: (params) => @@ -4437,7 +4339,7 @@ const immersionTrackerStartupMainDeps: Parameters< logWarn: (message, details) => logger.warn(message, details), }; const createImmersionTrackerStartup = createImmersionTrackerStartupHandler( - createBuildImmersionTrackerStartupMainDepsHandler(immersionTrackerStartupMainDeps)(), + immersionTrackerStartupMainDeps, ); const recordSubtitleMiningContext = (context: SubtitleMiningContext | null): void => { pendingSubtitleMiningContext = context; @@ -5538,8 +5440,8 @@ const { buildRegisterGlobalShortcutsMainDeps: (getConfiguredShortcutsHandler) => ({ getConfiguredShortcuts: () => getConfiguredShortcutsHandler(), registerGlobalShortcutsCore, - toggleVisibleOverlay: () => toggleVisibleOverlay(), - openYomitanSettings: () => openDictionarySettings(), + onToggleVisibleOverlay: () => toggleVisibleOverlay(), + onOpenYomitanSettings: () => openDictionarySettings(), isDev, getMainWindow: () => overlayManager.getMainWindow(), }), @@ -5668,8 +5570,8 @@ const cycleSecondarySubMode = createCycleSecondarySubModeRuntimeHandler({ setLastSecondarySubToggleAtMs: (timestampMs: number) => { appState.lastSecondarySubToggleAtMs = timestampMs; }, - broadcastToOverlayWindows: (channel, mode) => { - overlayManager.broadcastToOverlayWindows(channel, mode); + broadcastSecondarySubMode: (mode) => { + overlayManager.broadcastToOverlayWindows('secondary-subtitle:mode', mode); }, showMpvOsd: (text: string) => showConfiguredPlaybackFeedback(text), }, @@ -5680,90 +5582,62 @@ function setSecondarySubMode(mode: SecondarySubMode): void { appState.secondarySubMode = mode; } -const buildUpdateLastCardFromClipboardMainDepsHandler = - createBuildUpdateLastCardFromClipboardMainDepsHandler({ - getAnkiIntegration: () => appState.ankiIntegration, - readClipboardText: () => clipboard.readText(), - showMpvOsd: (text) => overlayNotificationsRuntime.showConfiguredStatusNotification(text), - updateLastCardFromClipboardCore, - }); -const updateLastCardFromClipboardMainDeps = buildUpdateLastCardFromClipboardMainDepsHandler(); -const updateLastCardFromClipboardHandler = createUpdateLastCardFromClipboardHandler( - updateLastCardFromClipboardMainDeps, -); +const updateLastCardFromClipboardHandler = createUpdateLastCardFromClipboardHandler({ + getAnkiIntegration: () => appState.ankiIntegration, + readClipboardText: () => clipboard.readText(), + showMpvOsd: (text) => overlayNotificationsRuntime.showConfiguredStatusNotification(text), + updateLastCardFromClipboardCore, +}); -const buildRefreshKnownWordCacheMainDepsHandler = createBuildRefreshKnownWordCacheMainDepsHandler({ +const refreshKnownWordCacheHandler = createRefreshKnownWordCacheHandler({ getAnkiIntegration: () => appState.ankiIntegration, missingIntegrationMessage: 'AnkiConnect integration not enabled', }); -const refreshKnownWordCacheMainDeps = buildRefreshKnownWordCacheMainDepsHandler(); -const refreshKnownWordCacheHandler = createRefreshKnownWordCacheHandler( - refreshKnownWordCacheMainDeps, -); -const buildTriggerFieldGroupingMainDepsHandler = createBuildTriggerFieldGroupingMainDepsHandler({ +const triggerFieldGroupingHandler = createTriggerFieldGroupingHandler({ getAnkiIntegration: () => appState.ankiIntegration, showMpvOsd: (text) => overlayNotificationsRuntime.showConfiguredStatusNotification(text), triggerFieldGroupingCore, }); -const triggerFieldGroupingMainDeps = buildTriggerFieldGroupingMainDepsHandler(); -const triggerFieldGroupingHandler = createTriggerFieldGroupingHandler(triggerFieldGroupingMainDeps); -const buildMarkLastCardAsAudioCardMainDepsHandler = - createBuildMarkLastCardAsAudioCardMainDepsHandler({ - getAnkiIntegration: () => appState.ankiIntegration, - showMpvOsd: (text) => overlayNotificationsRuntime.showConfiguredStatusNotification(text), - markLastCardAsAudioCardCore, - }); -const markLastCardAsAudioCardMainDeps = buildMarkLastCardAsAudioCardMainDepsHandler(); -const markLastCardAsAudioCardHandler = createMarkLastCardAsAudioCardHandler( - markLastCardAsAudioCardMainDeps, -); +const markLastCardAsAudioCardHandler = createMarkLastCardAsAudioCardHandler({ + getAnkiIntegration: () => appState.ankiIntegration, + showMpvOsd: (text) => overlayNotificationsRuntime.showConfiguredStatusNotification(text), + markLastCardAsAudioCardCore, +}); -const buildMineSentenceCardMainDepsHandler = createBuildMineSentenceCardMainDepsHandler({ +const mineSentenceCardHandler = createMineSentenceCardHandler({ getAnkiIntegration: () => appState.ankiIntegration, getMpvClient: () => appState.mpvClient, getPrimarySubtitle: () => captureCurrentPrimarySubtitleMiningContext(), showMpvOsd: (text) => overlayNotificationsRuntime.showConfiguredStatusNotification(text), mineSentenceCardCore, }); -const mineSentenceCardHandler = createMineSentenceCardHandler( - buildMineSentenceCardMainDepsHandler(), -); -const buildHandleMultiCopyDigitMainDepsHandler = createBuildHandleMultiCopyDigitMainDepsHandler({ +const handleMultiCopyDigitHandler = createHandleMultiCopyDigitHandler({ getSubtitleTimingTracker: () => appState.subtitleTimingTracker, writeClipboardText: (text) => clipboard.writeText(text), showMpvOsd: (text) => showConfiguredPlaybackFeedback(text), handleMultiCopyDigitCore, }); -const handleMultiCopyDigitMainDeps = buildHandleMultiCopyDigitMainDepsHandler(); -const handleMultiCopyDigitHandler = createHandleMultiCopyDigitHandler(handleMultiCopyDigitMainDeps); -const buildCopyCurrentSubtitleMainDepsHandler = createBuildCopyCurrentSubtitleMainDepsHandler({ +const copyCurrentSubtitleHandler = createCopyCurrentSubtitleHandler({ getSubtitleTimingTracker: () => appState.subtitleTimingTracker, writeClipboardText: (text) => clipboard.writeText(text), showMpvOsd: (text) => overlayNotificationsRuntime.showConfiguredStatusNotification(text), copyCurrentSubtitleCore, }); -const copyCurrentSubtitleMainDeps = buildCopyCurrentSubtitleMainDepsHandler(); -const copyCurrentSubtitleHandler = createCopyCurrentSubtitleHandler(copyCurrentSubtitleMainDeps); -const buildHandleMineSentenceDigitMainDepsHandler = - createBuildHandleMineSentenceDigitMainDepsHandler({ - getSubtitleTimingTracker: () => appState.subtitleTimingTracker, - getAnkiIntegration: () => appState.ankiIntegration, - getCurrentSecondarySubText: () => appState.mpvClient?.currentSecondarySubText || undefined, - showMpvOsd: (text) => overlayNotificationsRuntime.showConfiguredStatusNotification(text), - logError: (message, err) => { - logger.error(message, err); - }, - handleMineSentenceDigitCore, - }); -const handleMineSentenceDigitMainDeps = buildHandleMineSentenceDigitMainDepsHandler(); -const handleMineSentenceDigitHandler = createHandleMineSentenceDigitHandler( - handleMineSentenceDigitMainDeps, -); +const handleMineSentenceDigitHandler = createHandleMineSentenceDigitHandler({ + getSubtitleTimingTracker: () => appState.subtitleTimingTracker, + getAnkiIntegration: () => appState.ankiIntegration, + getCurrentSecondarySubText: () => appState.mpvClient?.currentSecondarySubText || undefined, + showMpvOsd: (text) => overlayNotificationsRuntime.showConfiguredStatusNotification(text), + logError: (message, err) => { + logger.error(message, err); + }, + handleMineSentenceDigitCore, +}); const { setVisibleOverlayVisible: setVisibleOverlayVisibleHandler, toggleVisibleOverlay: toggleVisibleOverlayHandler, @@ -5779,29 +5653,19 @@ const { getVisibleOverlayVisible: () => overlayManager.getVisibleOverlayVisible(), }); -const buildHandleOverlayModalClosedMainDepsHandler = - createBuildHandleOverlayModalClosedMainDepsHandler({ - handleOverlayModalClosedRuntime: (modal) => overlayModalRuntime.handleOverlayModalClosed(modal), - }); -const handleOverlayModalClosedMainDeps = buildHandleOverlayModalClosedMainDepsHandler(); -const handleOverlayModalClosedHandler = createHandleOverlayModalClosedHandler( - handleOverlayModalClosedMainDeps, -); +const handleOverlayModalClosedHandler = createHandleOverlayModalClosedHandler({ + handleOverlayModalClosedRuntime: (modal) => overlayModalRuntime.handleOverlayModalClosed(modal), +}); -const buildAppendClipboardVideoToQueueMainDepsHandler = - createBuildAppendClipboardVideoToQueueMainDepsHandler({ - appendClipboardVideoToQueueRuntime, - getMpvClient: () => appState.mpvClient, - readClipboardText: () => clipboard.readText(), - showMpvOsd: (text) => overlayNotificationsRuntime.showConfiguredStatusNotification(text), - sendMpvCommand: (command) => { - sendMpvCommandRuntime(appState.mpvClient, command); - }, - }); -const appendClipboardVideoToQueueMainDeps = buildAppendClipboardVideoToQueueMainDepsHandler(); -const appendClipboardVideoToQueueHandler = createAppendClipboardVideoToQueueHandler( - appendClipboardVideoToQueueMainDeps, -); +const appendClipboardVideoToQueueHandler = createAppendClipboardVideoToQueueHandler({ + appendClipboardVideoToQueueRuntime, + getMpvClient: () => appState.mpvClient, + readClipboardText: () => clipboard.readText(), + showMpvOsd: (text) => overlayNotificationsRuntime.showConfiguredStatusNotification(text), + sendMpvCommand: (command) => { + sendMpvCommandRuntime(appState.mpvClient, command); + }, +}); async function dispatchSessionAction(request: SessionActionDispatchRequest): Promise { await dispatchSessionActionCore(request, { diff --git a/src/main/main-wiring.test.ts b/src/main/main-wiring.test.ts index 51953089..18c7492f 100644 --- a/src/main/main-wiring.test.ts +++ b/src/main/main-wiring.test.ts @@ -425,25 +425,6 @@ test('subtitle annotation updates invalidate prefetched tokenizations before ref ); }); -test('subtitle processing controller resumes prefetch on settle, not on its emits', () => { - const source = readMainSource(); - const depsBlock = source.match( - /createBuildSubtitleProcessingControllerMainDepsHandler\(\{(?[\s\S]*?)\n \}\);/, - )?.groups?.body; - - assert.ok(depsBlock); - // A controller emit can be the provisional plain payload sent before the - // scan runs, so it must not release the prefetch pause. - assert.match( - depsBlock, - /emitSubtitle: \(payload\) => emitSubtitlePayload\(payload, \{ resumePrefetch: false \}\),/, - ); - assert.match( - depsBlock, - /onProcessingSettled: \(\) => \{\s+subtitlePrefetchService\?\.resume\(\);/, - ); -}); - test('annotation upgrades skip the duplicate basic websocket event', () => { const source = readMainSource(); const emitBlock = source.match( diff --git a/src/main/runtime/anilist-media-guess-main-deps.ts b/src/main/runtime/anilist-media-guess-main-deps.ts deleted file mode 100644 index 58274a23..00000000 --- a/src/main/runtime/anilist-media-guess-main-deps.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { - createEnsureAnilistMediaGuessHandler, - createMaybeProbeAnilistDurationHandler, -} from './anilist-media-guess'; - -type MaybeProbeAnilistDurationMainDeps = Parameters< - typeof createMaybeProbeAnilistDurationHandler ->[0]; -type EnsureAnilistMediaGuessMainDeps = Parameters[0]; - -export function createBuildMaybeProbeAnilistDurationMainDepsHandler( - deps: MaybeProbeAnilistDurationMainDeps, -) { - return (): MaybeProbeAnilistDurationMainDeps => ({ - getState: () => deps.getState(), - setState: (state) => deps.setState(state), - durationRetryIntervalMs: deps.durationRetryIntervalMs, - now: () => deps.now(), - requestMpvDuration: () => deps.requestMpvDuration(), - logWarn: (message: string, error: unknown) => deps.logWarn(message, error), - }); -} - -export function createBuildEnsureAnilistMediaGuessMainDepsHandler( - deps: EnsureAnilistMediaGuessMainDeps, -) { - return (): EnsureAnilistMediaGuessMainDeps => ({ - getState: () => deps.getState(), - setState: (state) => deps.setState(state), - resolveMediaPathForJimaku: (currentMediaPath) => - deps.resolveMediaPathForJimaku(currentMediaPath), - getCurrentMediaPath: () => deps.getCurrentMediaPath(), - getCurrentMediaTitle: () => deps.getCurrentMediaTitle(), - guessAnilistMediaInfo: (mediaPath, mediaTitle) => - deps.guessAnilistMediaInfo(mediaPath, mediaTitle), - }); -} diff --git a/src/main/runtime/anilist-media-state-main-deps.ts b/src/main/runtime/anilist-media-state-main-deps.ts deleted file mode 100644 index 01194458..00000000 --- a/src/main/runtime/anilist-media-state-main-deps.ts +++ /dev/null @@ -1,90 +0,0 @@ -import type { - createGetAnilistMediaGuessRuntimeStateHandler, - createGetCurrentAnilistMediaKeyHandler, - createRecordAnilistMediaDurationHandler, - createResetAnilistMediaGuessStateHandler, - createResetAnilistMediaTrackingHandler, - createSetAnilistMediaGuessRuntimeStateHandler, -} from './anilist-media-state'; - -type GetCurrentAnilistMediaKeyMainDeps = Parameters< - typeof createGetCurrentAnilistMediaKeyHandler ->[0]; -type ResetAnilistMediaTrackingMainDeps = Parameters< - typeof createResetAnilistMediaTrackingHandler ->[0]; -type GetAnilistMediaGuessRuntimeStateMainDeps = Parameters< - typeof createGetAnilistMediaGuessRuntimeStateHandler ->[0]; -type SetAnilistMediaGuessRuntimeStateMainDeps = Parameters< - typeof createSetAnilistMediaGuessRuntimeStateHandler ->[0]; -type RecordAnilistMediaDurationMainDeps = Parameters< - typeof createRecordAnilistMediaDurationHandler ->[0]; -type ResetAnilistMediaGuessStateMainDeps = Parameters< - typeof createResetAnilistMediaGuessStateHandler ->[0]; - -export function createBuildGetCurrentAnilistMediaKeyMainDepsHandler( - deps: GetCurrentAnilistMediaKeyMainDeps, -) { - return (): GetCurrentAnilistMediaKeyMainDeps => ({ - getCurrentMediaPath: () => deps.getCurrentMediaPath(), - }); -} - -export function createBuildResetAnilistMediaTrackingMainDepsHandler( - deps: ResetAnilistMediaTrackingMainDeps, -) { - return (): ResetAnilistMediaTrackingMainDeps => ({ - setMediaKey: (value) => deps.setMediaKey(value), - setMediaDurationSec: (value) => deps.setMediaDurationSec(value), - setMediaGuess: (value) => deps.setMediaGuess(value), - setMediaGuessPromise: (value) => deps.setMediaGuessPromise(value), - setLastDurationProbeAtMs: (value: number) => deps.setLastDurationProbeAtMs(value), - }); -} - -export function createBuildGetAnilistMediaGuessRuntimeStateMainDepsHandler( - deps: GetAnilistMediaGuessRuntimeStateMainDeps, -) { - return (): GetAnilistMediaGuessRuntimeStateMainDeps => ({ - getMediaKey: () => deps.getMediaKey(), - getMediaDurationSec: () => deps.getMediaDurationSec(), - getMediaGuess: () => deps.getMediaGuess(), - getMediaGuessPromise: () => deps.getMediaGuessPromise(), - getLastDurationProbeAtMs: () => deps.getLastDurationProbeAtMs(), - }); -} - -export function createBuildSetAnilistMediaGuessRuntimeStateMainDepsHandler( - deps: SetAnilistMediaGuessRuntimeStateMainDeps, -) { - return (): SetAnilistMediaGuessRuntimeStateMainDeps => ({ - setMediaKey: (value) => deps.setMediaKey(value), - setMediaDurationSec: (value) => deps.setMediaDurationSec(value), - setMediaGuess: (value) => deps.setMediaGuess(value), - setMediaGuessPromise: (value) => deps.setMediaGuessPromise(value), - setLastDurationProbeAtMs: (value: number) => deps.setLastDurationProbeAtMs(value), - }); -} - -export function createBuildRecordAnilistMediaDurationMainDepsHandler( - deps: RecordAnilistMediaDurationMainDeps, -) { - return (): RecordAnilistMediaDurationMainDeps => ({ - getCurrentMediaKey: () => deps.getCurrentMediaKey(), - getState: () => deps.getState(), - setState: (state) => deps.setState(state), - }); -} - -export function createBuildResetAnilistMediaGuessStateMainDepsHandler( - deps: ResetAnilistMediaGuessStateMainDeps, -) { - return (): ResetAnilistMediaGuessStateMainDeps => ({ - setMediaGuess: (value) => deps.setMediaGuess(value), - setMediaGuessPromise: (value) => deps.setMediaGuessPromise(value), - }); -} diff --git a/src/main/runtime/anilist-post-watch-main-deps.ts b/src/main/runtime/anilist-post-watch-main-deps.ts deleted file mode 100644 index 6b11ce6b..00000000 --- a/src/main/runtime/anilist-post-watch-main-deps.ts +++ /dev/null @@ -1,86 +0,0 @@ -import type { - createMaybeRunAnilistPostWatchUpdateHandler, - createProcessNextAnilistRetryUpdateHandler, -} from './anilist-post-watch'; - -type ProcessNextAnilistRetryUpdateMainDeps = Parameters< - typeof createProcessNextAnilistRetryUpdateHandler ->[0]; -type MaybeRunAnilistPostWatchUpdateMainDeps = Parameters< - typeof createMaybeRunAnilistPostWatchUpdateHandler ->[0]; - -export function createBuildProcessNextAnilistRetryUpdateMainDepsHandler( - deps: ProcessNextAnilistRetryUpdateMainDeps, -) { - return (): ProcessNextAnilistRetryUpdateMainDeps => ({ - nextReady: () => deps.nextReady(), - refreshRetryQueueState: () => deps.refreshRetryQueueState(), - setLastAttemptAt: (value: number) => deps.setLastAttemptAt(value), - setLastError: (value: string | null) => deps.setLastError(value), - refreshAnilistClientSecretState: () => deps.refreshAnilistClientSecretState(), - updateAnilistPostWatchProgress: ( - accessToken: string, - title: string, - episode: number, - season?: number | null, - mediaId?: number | null, - ) => deps.updateAnilistPostWatchProgress(accessToken, title, episode, season, mediaId), - markSuccess: (key: string) => deps.markSuccess(key), - rememberAttemptedUpdateKey: (key: string) => deps.rememberAttemptedUpdateKey(key), - markFailure: (key: string, message: string) => deps.markFailure(key, message), - logInfo: (message: string) => deps.logInfo(message), - now: () => deps.now(), - }); -} - -export function createBuildMaybeRunAnilistPostWatchUpdateMainDepsHandler( - deps: MaybeRunAnilistPostWatchUpdateMainDeps, -) { - return (): MaybeRunAnilistPostWatchUpdateMainDeps => ({ - getInFlight: () => deps.getInFlight(), - setInFlight: (value: boolean) => deps.setInFlight(value), - getResolvedConfig: () => deps.getResolvedConfig(), - isAnilistTrackingEnabled: (config) => deps.isAnilistTrackingEnabled(config), - getCurrentMediaKey: () => deps.getCurrentMediaKey(), - hasMpvClient: () => deps.hasMpvClient(), - getTrackedMediaKey: () => deps.getTrackedMediaKey(), - resetTrackedMedia: (mediaKey) => deps.resetTrackedMedia(mediaKey), - getWatchedSeconds: () => deps.getWatchedSeconds(), - maybeProbeAnilistDuration: (mediaKey: string, options) => - deps.maybeProbeAnilistDuration(mediaKey, options), - ensureAnilistMediaGuess: (mediaKey: string) => deps.ensureAnilistMediaGuess(mediaKey), - hasAttemptedUpdateKey: (key: string) => deps.hasAttemptedUpdateKey(key), - processNextAnilistRetryUpdate: () => deps.processNextAnilistRetryUpdate(), - refreshAnilistClientSecretState: () => deps.refreshAnilistClientSecretState(), - enqueueRetry: ( - key: string, - title: string, - episode: number, - season?: number | null, - mediaId?: number | null, - ) => deps.enqueueRetry(key, title, episode, season, mediaId), - getCurrentMediaTitle: deps.getCurrentMediaTitle - ? () => deps.getCurrentMediaTitle!() - : undefined, - resolvePinnedAnilistMediaId: deps.resolvePinnedAnilistMediaId - ? (input) => deps.resolvePinnedAnilistMediaId!(input) - : undefined, - markRetryFailure: (key: string, message: string) => deps.markRetryFailure(key, message), - markRetrySuccess: (key: string) => deps.markRetrySuccess(key), - refreshRetryQueueState: () => deps.refreshRetryQueueState(), - updateAnilistPostWatchProgress: ( - accessToken: string, - title: string, - episode: number, - season?: number | null, - mediaId?: number | null, - ) => deps.updateAnilistPostWatchProgress(accessToken, title, episode, season, mediaId), - rememberAttemptedUpdateKey: (key: string) => deps.rememberAttemptedUpdateKey(key), - showMpvOsd: (message: string) => deps.showMpvOsd(message), - logInfo: (message: string) => deps.logInfo(message), - logWarn: (message: string) => deps.logWarn(message), - minWatchSeconds: deps.minWatchSeconds, - minWatchRatio: deps.minWatchRatio, - }); -} diff --git a/src/main/runtime/anilist-setup-protocol-main-deps.ts b/src/main/runtime/anilist-setup-protocol-main-deps.ts deleted file mode 100644 index a9201ec7..00000000 --- a/src/main/runtime/anilist-setup-protocol-main-deps.ts +++ /dev/null @@ -1,69 +0,0 @@ -import type { - createConsumeAnilistSetupTokenFromUrlHandler, - createHandleAnilistSetupProtocolUrlHandler, - createNotifyAnilistSetupHandler, - createRegisterSubminerProtocolClientHandler, -} from './anilist-setup-protocol'; - -type NotifyAnilistSetupMainDeps = Parameters[0]; -type ConsumeAnilistSetupTokenMainDeps = Parameters< - typeof createConsumeAnilistSetupTokenFromUrlHandler ->[0]; -type HandleAnilistSetupProtocolUrlMainDeps = Parameters< - typeof createHandleAnilistSetupProtocolUrlHandler ->[0]; -type RegisterSubminerProtocolClientMainDeps = Parameters< - typeof createRegisterSubminerProtocolClientHandler ->[0]; - -export function createBuildNotifyAnilistSetupMainDepsHandler(deps: NotifyAnilistSetupMainDeps) { - return (): NotifyAnilistSetupMainDeps => ({ - getNotificationType: deps.getNotificationType ? () => deps.getNotificationType?.() : undefined, - hasMpvClient: () => deps.hasMpvClient(), - showMpvOsd: (message: string) => deps.showMpvOsd(message), - showOverlayNotification: deps.showOverlayNotification - ? (payload) => deps.showOverlayNotification?.(payload) - : undefined, - showDesktopNotification: (title: string, options: { body: string }) => - deps.showDesktopNotification(title, options), - logInfo: (message: string) => deps.logInfo(message), - }); -} - -export function createBuildConsumeAnilistSetupTokenFromUrlMainDepsHandler( - deps: ConsumeAnilistSetupTokenMainDeps, -) { - return (): ConsumeAnilistSetupTokenMainDeps => ({ - consumeAnilistSetupCallbackUrl: (input) => deps.consumeAnilistSetupCallbackUrl(input), - saveToken: (token: string) => deps.saveToken(token), - setCachedToken: (token: string) => deps.setCachedToken(token), - setResolvedState: (resolvedAt: number) => deps.setResolvedState(resolvedAt), - setSetupPageOpened: (opened: boolean) => deps.setSetupPageOpened(opened), - onSuccess: () => deps.onSuccess(), - closeWindow: () => deps.closeWindow(), - }); -} - -export function createBuildHandleAnilistSetupProtocolUrlMainDepsHandler( - deps: HandleAnilistSetupProtocolUrlMainDeps, -) { - return (): HandleAnilistSetupProtocolUrlMainDeps => ({ - consumeAnilistSetupTokenFromUrl: (rawUrl: string) => - deps.consumeAnilistSetupTokenFromUrl(rawUrl), - logWarn: (message: string, details: unknown) => deps.logWarn(message, details), - }); -} - -export function createBuildRegisterSubminerProtocolClientMainDepsHandler( - deps: RegisterSubminerProtocolClientMainDeps, -) { - return (): RegisterSubminerProtocolClientMainDeps => ({ - isDefaultApp: () => deps.isDefaultApp(), - getArgv: () => deps.getArgv(), - execPath: deps.execPath, - resolvePath: (value: string) => deps.resolvePath(value), - setAsDefaultProtocolClient: (scheme: string, path?: string, args?: string[]) => - deps.setAsDefaultProtocolClient(scheme, path, args), - logDebug: (message: string, details?: unknown) => deps.logDebug(message, details), - }); -} diff --git a/src/main/runtime/anilist-setup-window-main-deps.ts b/src/main/runtime/anilist-setup-window-main-deps.ts deleted file mode 100644 index 1718d285..00000000 --- a/src/main/runtime/anilist-setup-window-main-deps.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { createOpenAnilistSetupWindowHandler } from './anilist-setup-window'; - -type OpenAnilistSetupWindowMainDeps = Parameters[0]; - -export function createBuildOpenAnilistSetupWindowMainDepsHandler( - deps: OpenAnilistSetupWindowMainDeps, -) { - return (): OpenAnilistSetupWindowMainDeps => ({ - maybeFocusExistingSetupWindow: () => deps.maybeFocusExistingSetupWindow(), - createSetupWindow: () => deps.createSetupWindow(), - buildAuthorizeUrl: () => deps.buildAuthorizeUrl(), - consumeCallbackUrl: (rawUrl: string) => deps.consumeCallbackUrl(rawUrl), - openSetupInBrowser: (authorizeUrl: string) => deps.openSetupInBrowser(authorizeUrl), - loadManualTokenEntry: (setupWindow, authorizeUrl: string) => - deps.loadManualTokenEntry(setupWindow, authorizeUrl), - redirectUri: deps.redirectUri, - developerSettingsUrl: deps.developerSettingsUrl, - isAllowedExternalUrl: (url: string) => deps.isAllowedExternalUrl(url), - isAllowedNavigationUrl: (url: string) => deps.isAllowedNavigationUrl(url), - logWarn: (message: string, details?: unknown) => deps.logWarn(message, details), - logError: (message: string, details: unknown) => deps.logError(message, details), - clearSetupWindow: () => deps.clearSetupWindow(), - setSetupPageOpened: (opened: boolean) => deps.setSetupPageOpened(opened), - setSetupWindow: (setupWindow) => deps.setSetupWindow(setupWindow), - openExternal: (url: string) => deps.openExternal(url), - }); -} diff --git a/src/main/runtime/anilist-token-refresh-main-deps.ts b/src/main/runtime/anilist-token-refresh-main-deps.ts deleted file mode 100644 index 40e8dadd..00000000 --- a/src/main/runtime/anilist-token-refresh-main-deps.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { createRefreshAnilistClientSecretStateHandler } from './anilist-token-refresh'; - -type RefreshAnilistClientSecretStateMainDeps = Parameters< - typeof createRefreshAnilistClientSecretStateHandler ->[0]; - -export function createBuildRefreshAnilistClientSecretStateMainDepsHandler( - deps: RefreshAnilistClientSecretStateMainDeps, -) { - return (): RefreshAnilistClientSecretStateMainDeps => ({ - getResolvedConfig: () => deps.getResolvedConfig(), - isAnilistTrackingEnabled: (config) => deps.isAnilistTrackingEnabled(config), - getCachedAccessToken: () => deps.getCachedAccessToken(), - setCachedAccessToken: (token) => deps.setCachedAccessToken(token), - saveStoredToken: (token: string) => deps.saveStoredToken(token), - loadStoredToken: () => deps.loadStoredToken(), - setClientSecretState: (state) => deps.setClientSecretState(state), - getAnilistSetupPageOpened: () => deps.getAnilistSetupPageOpened(), - setAnilistSetupPageOpened: (opened: boolean) => deps.setAnilistSetupPageOpened(opened), - openAnilistSetupWindow: () => deps.openAnilistSetupWindow(), - now: () => deps.now(), - }); -} diff --git a/src/main/runtime/anki-actions-main-deps.ts b/src/main/runtime/anki-actions-main-deps.ts deleted file mode 100644 index 948bc40e..00000000 --- a/src/main/runtime/anki-actions-main-deps.ts +++ /dev/null @@ -1,98 +0,0 @@ -import type { createRefreshKnownWordCacheHandler, PrimarySubtitle } from './anki-actions'; - -type RefreshKnownWordCacheMainDeps = Parameters[0]; - -export function createBuildUpdateLastCardFromClipboardMainDepsHandler(deps: { - getAnkiIntegration: () => TAnki; - readClipboardText: () => string; - showMpvOsd: (text: string) => void; - updateLastCardFromClipboardCore: (options: { - ankiIntegration: TAnki; - readClipboardText: () => string; - showMpvOsd: (text: string) => void; - }) => Promise; -}) { - return () => ({ - getAnkiIntegration: () => deps.getAnkiIntegration(), - readClipboardText: () => deps.readClipboardText(), - showMpvOsd: (text: string) => deps.showMpvOsd(text), - updateLastCardFromClipboardCore: (options: { - ankiIntegration: TAnki; - readClipboardText: () => string; - showMpvOsd: (text: string) => void; - }) => deps.updateLastCardFromClipboardCore(options), - }); -} - -export function createBuildRefreshKnownWordCacheMainDepsHandler( - deps: RefreshKnownWordCacheMainDeps, -) { - return (): RefreshKnownWordCacheMainDeps => ({ - getAnkiIntegration: () => deps.getAnkiIntegration(), - missingIntegrationMessage: deps.missingIntegrationMessage, - }); -} - -export function createBuildTriggerFieldGroupingMainDepsHandler(deps: { - getAnkiIntegration: () => TAnki; - showMpvOsd: (text: string) => void; - triggerFieldGroupingCore: (options: { - ankiIntegration: TAnki; - showMpvOsd: (text: string) => void; - }) => Promise; -}) { - return () => ({ - getAnkiIntegration: () => deps.getAnkiIntegration(), - showMpvOsd: (text: string) => deps.showMpvOsd(text), - triggerFieldGroupingCore: (options: { - ankiIntegration: TAnki; - showMpvOsd: (text: string) => void; - }) => deps.triggerFieldGroupingCore(options), - }); -} - -export function createBuildMarkLastCardAsAudioCardMainDepsHandler(deps: { - getAnkiIntegration: () => TAnki; - showMpvOsd: (text: string) => void; - markLastCardAsAudioCardCore: (options: { - ankiIntegration: TAnki; - showMpvOsd: (text: string) => void; - }) => Promise; -}) { - return () => ({ - getAnkiIntegration: () => deps.getAnkiIntegration(), - showMpvOsd: (text: string) => deps.showMpvOsd(text), - markLastCardAsAudioCardCore: (options: { - ankiIntegration: TAnki; - showMpvOsd: (text: string) => void; - }) => deps.markLastCardAsAudioCardCore(options), - }); -} - -export function createBuildMineSentenceCardMainDepsHandler(deps: { - getAnkiIntegration: () => TAnki; - getMpvClient: () => TMpv; - getPrimarySubtitle?: () => PrimarySubtitle | null; - showMpvOsd: (text: string) => void; - mineSentenceCardCore: (options: { - ankiIntegration: TAnki; - mpvClient: TMpv; - primarySubtitle?: PrimarySubtitle; - showMpvOsd: (text: string) => void; - }) => Promise; -}) { - return () => ({ - getAnkiIntegration: () => deps.getAnkiIntegration(), - getMpvClient: () => deps.getMpvClient(), - ...(deps.getPrimarySubtitle - ? { getPrimarySubtitle: () => deps.getPrimarySubtitle?.() ?? null } - : {}), - showMpvOsd: (text: string) => deps.showMpvOsd(text), - mineSentenceCardCore: (options: { - ankiIntegration: TAnki; - mpvClient: TMpv; - primarySubtitle?: PrimarySubtitle; - showMpvOsd: (text: string) => void; - }) => deps.mineSentenceCardCore(options), - }); -} diff --git a/src/main/runtime/app-ready-main-deps.ts b/src/main/runtime/app-ready-main-deps.ts deleted file mode 100644 index 6de17228..00000000 --- a/src/main/runtime/app-ready-main-deps.ts +++ /dev/null @@ -1,51 +0,0 @@ -import type { AppReadyRuntimeDepsFactoryInput } from '../app-lifecycle'; - -export function createBuildAppReadyRuntimeMainDepsHandler(deps: AppReadyRuntimeDepsFactoryInput) { - return (): AppReadyRuntimeDepsFactoryInput => ({ - ensureDefaultConfigBootstrap: deps.ensureDefaultConfigBootstrap, - loadSubtitlePosition: deps.loadSubtitlePosition, - resolveKeybindings: deps.resolveKeybindings, - createMpvClient: deps.createMpvClient, - reloadConfig: deps.reloadConfig, - getResolvedConfig: deps.getResolvedConfig, - getConfigWarnings: deps.getConfigWarnings, - logConfigWarning: deps.logConfigWarning, - initRuntimeOptionsManager: deps.initRuntimeOptionsManager, - setSecondarySubMode: deps.setSecondarySubMode, - defaultSecondarySubMode: deps.defaultSecondarySubMode, - defaultWebsocketPort: deps.defaultWebsocketPort, - defaultAnnotationWebsocketPort: deps.defaultAnnotationWebsocketPort, - defaultTexthookerPort: deps.defaultTexthookerPort, - hasMpvWebsocketPlugin: deps.hasMpvWebsocketPlugin, - startSubtitleWebsocket: deps.startSubtitleWebsocket, - startAnnotationWebsocket: deps.startAnnotationWebsocket, - startTexthooker: deps.startTexthooker, - log: deps.log, - setLogLevel: deps.setLogLevel, - setLogRotation: deps.setLogRotation, - setLogFileToggles: deps.setLogFileToggles, - createMecabTokenizerAndCheck: deps.createMecabTokenizerAndCheck, - createSubtitleTimingTracker: deps.createSubtitleTimingTracker, - createImmersionTracker: deps.createImmersionTracker, - startJellyfinRemoteSession: deps.startJellyfinRemoteSession, - loadYomitanExtension: deps.loadYomitanExtension, - ensureYomitanExtensionLoaded: deps.ensureYomitanExtensionLoaded, - handleFirstRunSetup: deps.handleFirstRunSetup, - prewarmSubtitleDictionaries: deps.prewarmSubtitleDictionaries, - startBackgroundWarmups: deps.startBackgroundWarmups, - texthookerOnlyMode: deps.texthookerOnlyMode, - shouldAutoInitializeOverlayRuntimeFromConfig: deps.shouldAutoInitializeOverlayRuntimeFromConfig, - setVisibleOverlayVisible: deps.setVisibleOverlayVisible, - initializeOverlayRuntime: deps.initializeOverlayRuntime, - runHeadlessInitialCommand: deps.runHeadlessInitialCommand, - handleInitialArgs: deps.handleInitialArgs, - onCriticalConfigErrors: deps.onCriticalConfigErrors, - logDebug: deps.logDebug, - now: deps.now, - shouldRunHeadlessInitialCommand: deps.shouldRunHeadlessInitialCommand, - shouldUseMinimalStartup: deps.shouldUseMinimalStartup, - shouldSkipHeavyStartup: deps.shouldSkipHeavyStartup, - shouldHandleInitialArgsBeforeDeferredOverlayWarmup: - deps.shouldHandleInitialArgsBeforeDeferredOverlayWarmup, - }); -} diff --git a/src/main/runtime/cli-command-prechecks-main-deps.ts b/src/main/runtime/cli-command-prechecks-main-deps.ts deleted file mode 100644 index c8cabcea..00000000 --- a/src/main/runtime/cli-command-prechecks-main-deps.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { CliArgs } from '../../cli/args'; - -export function createBuildHandleTexthookerOnlyModeTransitionMainDepsHandler(deps: { - isTexthookerOnlyMode: () => boolean; - setTexthookerOnlyMode: (enabled: boolean) => void; - commandNeedsOverlayStartupPrereqs: (args: CliArgs) => boolean; - ensureOverlayStartupPrereqs: () => void; - startBackgroundWarmups: () => void; - logInfo: (message: string) => void; -}) { - return () => ({ - isTexthookerOnlyMode: () => deps.isTexthookerOnlyMode(), - setTexthookerOnlyMode: (enabled: boolean) => deps.setTexthookerOnlyMode(enabled), - commandNeedsOverlayStartupPrereqs: (args: CliArgs) => - deps.commandNeedsOverlayStartupPrereqs(args), - ensureOverlayStartupPrereqs: () => deps.ensureOverlayStartupPrereqs(), - startBackgroundWarmups: () => deps.startBackgroundWarmups(), - logInfo: (message: string) => deps.logInfo(message), - }); -} diff --git a/src/main/runtime/cli-command-runtime-handler.ts b/src/main/runtime/cli-command-runtime-handler.ts index ed6d20b3..66400fa1 100644 --- a/src/main/runtime/cli-command-runtime-handler.ts +++ b/src/main/runtime/cli-command-runtime-handler.ts @@ -1,9 +1,8 @@ import type { CliArgs, CliCommandSource } from '../../cli/args'; import { createHandleTexthookerOnlyModeTransitionHandler } from './cli-command-prechecks'; -import { createBuildHandleTexthookerOnlyModeTransitionMainDepsHandler } from './cli-command-prechecks-main-deps'; type HandleTexthookerOnlyModeTransitionMainDeps = Parameters< - typeof createBuildHandleTexthookerOnlyModeTransitionMainDepsHandler + typeof createHandleTexthookerOnlyModeTransitionHandler >[0]; export function createCliCommandRuntimeHandler(deps: { @@ -17,9 +16,7 @@ export function createCliCommandRuntimeHandler(deps: { ) => void; }) { const handleTexthookerOnlyModeTransitionHandler = createHandleTexthookerOnlyModeTransitionHandler( - createBuildHandleTexthookerOnlyModeTransitionMainDepsHandler( - deps.handleTexthookerOnlyModeTransitionMainDeps, - )(), + deps.handleTexthookerOnlyModeTransitionMainDeps, ); return (args: CliArgs, source: CliCommandSource = 'initial'): void => { diff --git a/src/main/runtime/composers/anilist-setup-composer.ts b/src/main/runtime/composers/anilist-setup-composer.ts index b2eb9117..a5bf22b4 100644 --- a/src/main/runtime/composers/anilist-setup-composer.ts +++ b/src/main/runtime/composers/anilist-setup-composer.ts @@ -1,8 +1,4 @@ import { - createBuildConsumeAnilistSetupTokenFromUrlMainDepsHandler, - createBuildHandleAnilistSetupProtocolUrlMainDepsHandler, - createBuildNotifyAnilistSetupMainDepsHandler, - createBuildRegisterSubminerProtocolClientMainDepsHandler, createConsumeAnilistSetupTokenFromUrlHandler, createHandleAnilistSetupProtocolUrlHandler, createNotifyAnilistSetupHandler, @@ -16,12 +12,10 @@ type HandleProtocolHandler = ReturnType; export type AnilistSetupComposerOptions = ComposerInputs<{ - notifyDeps: Parameters[0]; - consumeTokenDeps: Parameters[0]; - handleProtocolDeps: Parameters[0]; - registerProtocolClientDeps: Parameters< - typeof createBuildRegisterSubminerProtocolClientMainDepsHandler - >[0]; + notifyDeps: Parameters[0]; + consumeTokenDeps: Parameters[0]; + handleProtocolDeps: Parameters[0]; + registerProtocolClientDeps: Parameters[0]; }>; export type AnilistSetupComposerResult = ComposerOutputs<{ @@ -34,17 +28,15 @@ export type AnilistSetupComposerResult = ComposerOutputs<{ export function composeAnilistSetupHandlers( options: AnilistSetupComposerOptions, ): AnilistSetupComposerResult { - const notifyAnilistSetup = createNotifyAnilistSetupHandler( - createBuildNotifyAnilistSetupMainDepsHandler(options.notifyDeps)(), - ); + const notifyAnilistSetup = createNotifyAnilistSetupHandler(options.notifyDeps); const consumeAnilistSetupTokenFromUrl = createConsumeAnilistSetupTokenFromUrlHandler( - createBuildConsumeAnilistSetupTokenFromUrlMainDepsHandler(options.consumeTokenDeps)(), + options.consumeTokenDeps, ); const handleAnilistSetupProtocolUrl = createHandleAnilistSetupProtocolUrlHandler( - createBuildHandleAnilistSetupProtocolUrlMainDepsHandler(options.handleProtocolDeps)(), + options.handleProtocolDeps, ); const registerSubminerProtocolClient = createRegisterSubminerProtocolClientHandler( - createBuildRegisterSubminerProtocolClientMainDepsHandler(options.registerProtocolClientDeps)(), + options.registerProtocolClientDeps, ); return { diff --git a/src/main/runtime/composers/anilist-tracking-composer.ts b/src/main/runtime/composers/anilist-tracking-composer.ts index 74205fda..36b69f17 100644 --- a/src/main/runtime/composers/anilist-tracking-composer.ts +++ b/src/main/runtime/composers/anilist-tracking-composer.ts @@ -1,15 +1,4 @@ import { - createBuildEnsureAnilistMediaGuessMainDepsHandler, - createBuildGetAnilistMediaGuessRuntimeStateMainDepsHandler, - createBuildGetCurrentAnilistMediaKeyMainDepsHandler, - createBuildMaybeProbeAnilistDurationMainDepsHandler, - createBuildMaybeRunAnilistPostWatchUpdateMainDepsHandler, - createBuildProcessNextAnilistRetryUpdateMainDepsHandler, - createBuildRecordAnilistMediaDurationMainDepsHandler, - createBuildRefreshAnilistClientSecretStateMainDepsHandler, - createBuildResetAnilistMediaGuessStateMainDepsHandler, - createBuildResetAnilistMediaTrackingMainDepsHandler, - createBuildSetAnilistMediaGuessRuntimeStateMainDepsHandler, createEnsureAnilistMediaGuessHandler, createGetAnilistMediaGuessRuntimeStateHandler, createGetCurrentAnilistMediaKeyHandler, @@ -25,36 +14,22 @@ import { import type { ComposerInputs, ComposerOutputs } from './contracts'; export type AnilistTrackingComposerOptions = ComposerInputs<{ - refreshClientSecretMainDeps: Parameters< - typeof createBuildRefreshAnilistClientSecretStateMainDepsHandler - >[0]; - getCurrentMediaKeyMainDeps: Parameters< - typeof createBuildGetCurrentAnilistMediaKeyMainDepsHandler - >[0]; - resetMediaTrackingMainDeps: Parameters< - typeof createBuildResetAnilistMediaTrackingMainDepsHandler - >[0]; + refreshClientSecretMainDeps: Parameters[0]; + getCurrentMediaKeyMainDeps: Parameters[0]; + resetMediaTrackingMainDeps: Parameters[0]; getMediaGuessRuntimeStateMainDeps: Parameters< - typeof createBuildGetAnilistMediaGuessRuntimeStateMainDepsHandler + typeof createGetAnilistMediaGuessRuntimeStateHandler >[0]; setMediaGuessRuntimeStateMainDeps: Parameters< - typeof createBuildSetAnilistMediaGuessRuntimeStateMainDepsHandler - >[0]; - recordMediaDurationMainDeps: Parameters< - typeof createBuildRecordAnilistMediaDurationMainDepsHandler - >[0]; - resetMediaGuessStateMainDeps: Parameters< - typeof createBuildResetAnilistMediaGuessStateMainDepsHandler - >[0]; - maybeProbeDurationMainDeps: Parameters< - typeof createBuildMaybeProbeAnilistDurationMainDepsHandler - >[0]; - ensureMediaGuessMainDeps: Parameters[0]; - processNextRetryUpdateMainDeps: Parameters< - typeof createBuildProcessNextAnilistRetryUpdateMainDepsHandler + typeof createSetAnilistMediaGuessRuntimeStateHandler >[0]; + recordMediaDurationMainDeps: Parameters[0]; + resetMediaGuessStateMainDeps: Parameters[0]; + maybeProbeDurationMainDeps: Parameters[0]; + ensureMediaGuessMainDeps: Parameters[0]; + processNextRetryUpdateMainDeps: Parameters[0]; maybeRunPostWatchUpdateMainDeps: Parameters< - typeof createBuildMaybeRunAnilistPostWatchUpdateMainDepsHandler + typeof createMaybeRunAnilistPostWatchUpdateHandler >[0]; }>; @@ -80,47 +55,37 @@ export function composeAnilistTrackingHandlers( options: AnilistTrackingComposerOptions, ): AnilistTrackingComposerResult { const refreshAnilistClientSecretState = createRefreshAnilistClientSecretStateHandler( - createBuildRefreshAnilistClientSecretStateMainDepsHandler( - options.refreshClientSecretMainDeps, - )(), + options.refreshClientSecretMainDeps, ); const getCurrentAnilistMediaKey = createGetCurrentAnilistMediaKeyHandler( - createBuildGetCurrentAnilistMediaKeyMainDepsHandler(options.getCurrentMediaKeyMainDeps)(), + options.getCurrentMediaKeyMainDeps, ); const resetAnilistMediaTracking = createResetAnilistMediaTrackingHandler( - createBuildResetAnilistMediaTrackingMainDepsHandler(options.resetMediaTrackingMainDeps)(), + options.resetMediaTrackingMainDeps, ); const getAnilistMediaGuessRuntimeState = createGetAnilistMediaGuessRuntimeStateHandler( - createBuildGetAnilistMediaGuessRuntimeStateMainDepsHandler( - options.getMediaGuessRuntimeStateMainDeps, - )(), + options.getMediaGuessRuntimeStateMainDeps, ); const setAnilistMediaGuessRuntimeState = createSetAnilistMediaGuessRuntimeStateHandler( - createBuildSetAnilistMediaGuessRuntimeStateMainDepsHandler( - options.setMediaGuessRuntimeStateMainDeps, - )(), + options.setMediaGuessRuntimeStateMainDeps, ); const recordAnilistMediaDuration = createRecordAnilistMediaDurationHandler( - createBuildRecordAnilistMediaDurationMainDepsHandler(options.recordMediaDurationMainDeps)(), + options.recordMediaDurationMainDeps, ); const resetAnilistMediaGuessState = createResetAnilistMediaGuessStateHandler( - createBuildResetAnilistMediaGuessStateMainDepsHandler(options.resetMediaGuessStateMainDeps)(), + options.resetMediaGuessStateMainDeps, ); const maybeProbeAnilistDuration = createMaybeProbeAnilistDurationHandler( - createBuildMaybeProbeAnilistDurationMainDepsHandler(options.maybeProbeDurationMainDeps)(), + options.maybeProbeDurationMainDeps, ); const ensureAnilistMediaGuess = createEnsureAnilistMediaGuessHandler( - createBuildEnsureAnilistMediaGuessMainDepsHandler(options.ensureMediaGuessMainDeps)(), + options.ensureMediaGuessMainDeps, ); const processNextAnilistRetryUpdate = createProcessNextAnilistRetryUpdateHandler( - createBuildProcessNextAnilistRetryUpdateMainDepsHandler( - options.processNextRetryUpdateMainDeps, - )(), + options.processNextRetryUpdateMainDeps, ); const maybeRunAnilistPostWatchUpdate = createMaybeRunAnilistPostWatchUpdateHandler( - createBuildMaybeRunAnilistPostWatchUpdateMainDepsHandler( - options.maybeRunPostWatchUpdateMainDeps, - )(), + options.maybeRunPostWatchUpdateMainDeps, ); return { diff --git a/src/main/runtime/composers/app-ready-composer.ts b/src/main/runtime/composers/app-ready-composer.ts index 832989e6..c81f6e99 100644 --- a/src/main/runtime/composers/app-ready-composer.ts +++ b/src/main/runtime/composers/app-ready-composer.ts @@ -1,27 +1,22 @@ -import { createAppReadyRuntimeRunner } from '../../app-lifecycle'; -import { createBuildAppReadyRuntimeMainDepsHandler } from '../app-ready-main-deps'; import { - createBuildCriticalConfigErrorMainDepsHandler, - createBuildReloadConfigMainDepsHandler, -} from '../startup-config-main-deps'; + createAppReadyRuntimeRunner, + type AppReadyRuntimeDepsFactoryInput, +} from '../../app-lifecycle'; import { createCriticalConfigErrorHandler, createReloadConfigHandler } from '../startup-config'; -import { createBuildImmersionTrackerStartupMainDepsHandler } from '../immersion-startup-main-deps'; import { createImmersionTrackerStartupHandler } from '../immersion-startup'; import type { ComposerInputs, ComposerOutputs } from './contracts'; -type ReloadConfigMainDeps = Parameters[0]; -type CriticalConfigErrorMainDeps = Parameters< - typeof createBuildCriticalConfigErrorMainDepsHandler ->[0]; -type AppReadyRuntimeMainDeps = Parameters[0]; +type ReloadConfigMainDeps = Parameters[0]; +type CriticalConfigErrorMainDeps = Parameters[0]; export type AppReadyComposerOptions = ComposerInputs<{ reloadConfigMainDeps: ReloadConfigMainDeps; criticalConfigErrorMainDeps: CriticalConfigErrorMainDeps; - appReadyRuntimeMainDeps: Omit; - immersionTrackerStartupMainDeps: Parameters< - typeof createBuildImmersionTrackerStartupMainDepsHandler - >[0]; + appReadyRuntimeMainDeps: Omit< + AppReadyRuntimeDepsFactoryInput, + 'reloadConfig' | 'onCriticalConfigErrors' + >; + immersionTrackerStartupMainDeps: Parameters[0]; }>; export type AppReadyComposerResult = ComposerOutputs<{ @@ -31,27 +26,17 @@ export type AppReadyComposerResult = ComposerOutputs<{ }>; export function composeAppReadyRuntime(options: AppReadyComposerOptions): AppReadyComposerResult { - const reloadConfig = createReloadConfigHandler( - createBuildReloadConfigMainDepsHandler(options.reloadConfigMainDeps)(), - ); - const criticalConfigError = createCriticalConfigErrorHandler( - createBuildCriticalConfigErrorMainDepsHandler(options.criticalConfigErrorMainDeps)(), - ); + const reloadConfig = createReloadConfigHandler(options.reloadConfigMainDeps); + const criticalConfigError = createCriticalConfigErrorHandler(options.criticalConfigErrorMainDeps); - const appReadyRuntimeRunner = createAppReadyRuntimeRunner( - createBuildAppReadyRuntimeMainDepsHandler({ - ...options.appReadyRuntimeMainDeps, - reloadConfig, - createImmersionTracker: - options.appReadyRuntimeMainDeps.createImmersionTracker ?? - createImmersionTrackerStartupHandler( - createBuildImmersionTrackerStartupMainDepsHandler( - options.immersionTrackerStartupMainDeps, - )(), - ), - onCriticalConfigErrors: criticalConfigError, - })(), - ); + const appReadyRuntimeRunner = createAppReadyRuntimeRunner({ + ...options.appReadyRuntimeMainDeps, + reloadConfig, + createImmersionTracker: + options.appReadyRuntimeMainDeps.createImmersionTracker ?? + createImmersionTrackerStartupHandler(options.immersionTrackerStartupMainDeps), + onCriticalConfigErrors: criticalConfigError, + }); return { reloadConfig, diff --git a/src/main/runtime/composers/ipc-runtime-composer.ts b/src/main/runtime/composers/ipc-runtime-composer.ts index f0bec722..569fd1fe 100644 --- a/src/main/runtime/composers/ipc-runtime-composer.ts +++ b/src/main/runtime/composers/ipc-runtime-composer.ts @@ -1,9 +1,7 @@ import type { RegisterIpcRuntimeServicesParams } from '../../ipc-runtime'; +import type { MpvCommandFromIpcRuntimeDeps } from '../../ipc-mpv-command'; import type { SubsyncManualRunRequest, SubsyncResult } from '../../../types'; -import { - createBuildMpvCommandFromIpcRuntimeMainDepsHandler, - createIpcRuntimeHandlers, -} from '../domains/ipc'; +import { createIpcRuntimeHandlers } from '../domains/ipc'; import type { ComposerInputs, ComposerOutputs } from './contracts'; type MpvCommand = (string | number)[]; @@ -17,7 +15,7 @@ type IpcRuntimeDeps = Parameters< >[0]; export type IpcRuntimeComposerOptions = ComposerInputs<{ - mpvCommandMainDeps: Parameters[0]; + mpvCommandMainDeps: MpvCommandFromIpcRuntimeDeps; handleMpvCommandFromIpcRuntime: IpcRuntimeDeps['handleMpvCommandFromIpcDeps']['handleMpvCommandFromIpcRuntime']; runSubsyncManualFromIpc: RunSubsyncManual; registration: { @@ -37,16 +35,13 @@ export type IpcRuntimeComposerResult = ComposerOutputs<{ export function composeIpcRuntimeHandlers( options: IpcRuntimeComposerOptions, ): IpcRuntimeComposerResult { - const mpvCommandFromIpcRuntimeMainDeps = createBuildMpvCommandFromIpcRuntimeMainDepsHandler( - options.mpvCommandMainDeps, - )(); const { handleMpvCommandFromIpc, runSubsyncManualFromIpc } = createIpcRuntimeHandlers< SubsyncManualRunRequest, SubsyncResult >({ handleMpvCommandFromIpcDeps: { handleMpvCommandFromIpcRuntime: options.handleMpvCommandFromIpcRuntime, - buildMpvCommandDeps: () => mpvCommandFromIpcRuntimeMainDeps, + buildMpvCommandDeps: () => options.mpvCommandMainDeps, }, runSubsyncManualFromIpcDeps: { runManualFromIpc: (request) => options.runSubsyncManualFromIpc(request), diff --git a/src/main/runtime/composers/jellyfin-runtime-composer.ts b/src/main/runtime/composers/jellyfin-runtime-composer.ts index 97548c9d..59eb64c9 100644 --- a/src/main/runtime/composers/jellyfin-runtime-composer.ts +++ b/src/main/runtime/composers/jellyfin-runtime-composer.ts @@ -1,21 +1,9 @@ import { buildJellyfinSetupFormHtml, createEnsureMpvConnectedForJellyfinPlaybackHandler, - createBuildEnsureMpvConnectedForJellyfinPlaybackMainDepsHandler, createBuildGetJellyfinClientInfoMainDepsHandler, createBuildGetResolvedJellyfinConfigMainDepsHandler, - createBuildHandleJellyfinAuthCommandsMainDepsHandler, - createBuildHandleJellyfinListCommandsMainDepsHandler, - createBuildHandleJellyfinPlayCommandMainDepsHandler, - createBuildHandleJellyfinRemoteAnnounceCommandMainDepsHandler, - createBuildLaunchMpvIdleForJellyfinPlaybackMainDepsHandler, createBuildOpenJellyfinSetupWindowMainDepsHandler, - createBuildPlayJellyfinItemInMpvMainDepsHandler, - createBuildPreloadJellyfinExternalSubtitlesMainDepsHandler, - createBuildRunJellyfinCommandMainDepsHandler, - createBuildStartJellyfinRemoteSessionMainDepsHandler, - createBuildStopJellyfinRemoteSessionMainDepsHandler, - createBuildWaitForMpvConnectedMainDepsHandler, createGetJellyfinClientInfoHandler, createGetResolvedJellyfinConfigHandler, createHandleJellyfinAuthCommands, @@ -40,23 +28,13 @@ import { import type { ComposerInputs, ComposerOutputs } from './contracts'; type EnsureMpvConnectedMainDeps = Parameters< - typeof createBuildEnsureMpvConnectedForJellyfinPlaybackMainDepsHandler ->[0]; -type PlayJellyfinItemMainDeps = Parameters< - typeof createBuildPlayJellyfinItemInMpvMainDepsHandler ->[0]; -type HandlePlayCommandMainDeps = Parameters< - typeof createBuildHandleJellyfinPlayCommandMainDepsHandler ->[0]; -type HandleRemoteAnnounceMainDeps = Parameters< - typeof createBuildHandleJellyfinRemoteAnnounceCommandMainDepsHandler ->[0]; -type StartRemoteSessionMainDeps = Parameters< - typeof createBuildStartJellyfinRemoteSessionMainDepsHandler ->[0]; -type RunJellyfinCommandMainDeps = Parameters< - typeof createBuildRunJellyfinCommandMainDepsHandler + typeof createEnsureMpvConnectedForJellyfinPlaybackHandler >[0]; +type PlayJellyfinItemMainDeps = Parameters[0]; +type HandlePlayCommandMainDeps = Parameters[0]; +type HandleRemoteAnnounceMainDeps = Parameters[0]; +type StartRemoteSessionMainDeps = Parameters[0]; +type RunJellyfinCommandMainDeps = Parameters[0]; type OpenJellyfinSetupWindowMainDeps = Parameters< typeof createBuildOpenJellyfinSetupWindowMainDepsHandler >[0]; @@ -68,16 +46,16 @@ export type JellyfinRuntimeComposerOptions = ComposerInputs<{ getJellyfinClientInfoMainDeps: Parameters< typeof createBuildGetJellyfinClientInfoMainDepsHandler >[0]; - waitForMpvConnectedMainDeps: Parameters[0]; + waitForMpvConnectedMainDeps: Parameters[0]; launchMpvIdleForJellyfinPlaybackMainDeps: Parameters< - typeof createBuildLaunchMpvIdleForJellyfinPlaybackMainDepsHandler + typeof createLaunchMpvIdleForJellyfinPlaybackHandler >[0]; ensureMpvConnectedForJellyfinPlaybackMainDeps: Omit< EnsureMpvConnectedMainDeps, 'waitForMpvConnected' | 'launchMpvIdleForJellyfinPlayback' >; preloadJellyfinExternalSubtitlesMainDeps: Parameters< - typeof createBuildPreloadJellyfinExternalSubtitlesMainDepsHandler + typeof createPreloadJellyfinExternalSubtitlesHandler >[0]; playJellyfinItemInMpvMainDeps: Omit< PlayJellyfinItemMainDeps, @@ -87,12 +65,8 @@ export type JellyfinRuntimeComposerOptions = ComposerInputs<{ JellyfinRemoteComposerOptions, 'getClientInfo' | 'getJellyfinConfig' | 'playJellyfinItem' >; - handleJellyfinAuthCommandsMainDeps: Parameters< - typeof createBuildHandleJellyfinAuthCommandsMainDepsHandler - >[0]; - handleJellyfinListCommandsMainDeps: Parameters< - typeof createBuildHandleJellyfinListCommandsMainDepsHandler - >[0]; + handleJellyfinAuthCommandsMainDeps: Parameters[0]; + handleJellyfinListCommandsMainDeps: Parameters[0]; handleJellyfinPlayCommandMainDeps: Omit; handleJellyfinRemoteAnnounceCommandMainDeps: Omit< HandleRemoteAnnounceMainDeps, @@ -106,9 +80,7 @@ export type JellyfinRuntimeComposerOptions = ComposerInputs<{ | 'handlePlaystate' | 'handleGeneralCommand' >; - stopJellyfinRemoteSessionMainDeps: Parameters< - typeof createBuildStopJellyfinRemoteSessionMainDepsHandler - >[0]; + stopJellyfinRemoteSessionMainDeps: Parameters[0]; runJellyfinCommandMainDeps: Omit< RunJellyfinCommandMainDeps, | 'getJellyfinConfig' @@ -177,36 +149,26 @@ export function composeJellyfinRuntimeHandlers( createBuildGetJellyfinClientInfoMainDepsHandler(options.getJellyfinClientInfoMainDeps)(), ); - const waitForMpvConnected = createWaitForMpvConnectedHandler( - createBuildWaitForMpvConnectedMainDepsHandler(options.waitForMpvConnectedMainDeps)(), - ); + const waitForMpvConnected = createWaitForMpvConnectedHandler(options.waitForMpvConnectedMainDeps); const launchMpvIdleForJellyfinPlayback = createLaunchMpvIdleForJellyfinPlaybackHandler( - createBuildLaunchMpvIdleForJellyfinPlaybackMainDepsHandler( - options.launchMpvIdleForJellyfinPlaybackMainDeps, - )(), - ); - const ensureMpvConnectedForJellyfinPlayback = createEnsureMpvConnectedForJellyfinPlaybackHandler( - createBuildEnsureMpvConnectedForJellyfinPlaybackMainDepsHandler({ - ...options.ensureMpvConnectedForJellyfinPlaybackMainDeps, - waitForMpvConnected: (timeoutMs) => waitForMpvConnected(timeoutMs), - launchMpvIdleForJellyfinPlayback: () => launchMpvIdleForJellyfinPlayback(), - })(), + options.launchMpvIdleForJellyfinPlaybackMainDeps, ); + const ensureMpvConnectedForJellyfinPlayback = createEnsureMpvConnectedForJellyfinPlaybackHandler({ + ...options.ensureMpvConnectedForJellyfinPlaybackMainDeps, + waitForMpvConnected: (timeoutMs) => waitForMpvConnected(timeoutMs), + launchMpvIdleForJellyfinPlayback: () => launchMpvIdleForJellyfinPlayback(), + }); const preloadJellyfinExternalSubtitles = createPreloadJellyfinExternalSubtitlesHandler( - createBuildPreloadJellyfinExternalSubtitlesMainDepsHandler( - options.preloadJellyfinExternalSubtitlesMainDeps, - )(), - ); - const playJellyfinItemInMpv = createPlayJellyfinItemInMpvHandler( - createBuildPlayJellyfinItemInMpvMainDepsHandler({ - ...options.playJellyfinItemInMpvMainDeps, - ensureMpvConnectedForPlayback: () => ensureMpvConnectedForJellyfinPlayback(), - preloadExternalSubtitles: (params) => { - void preloadJellyfinExternalSubtitles(params); - }, - })(), + options.preloadJellyfinExternalSubtitlesMainDeps, ); + const playJellyfinItemInMpv = createPlayJellyfinItemInMpvHandler({ + ...options.playJellyfinItemInMpvMainDeps, + ensureMpvConnectedForPlayback: () => ensureMpvConnectedForJellyfinPlayback(), + preloadExternalSubtitles: (params) => { + void preloadJellyfinExternalSubtitles(params); + }, + }); const { reportJellyfinRemoteProgress, @@ -223,59 +185,45 @@ export function composeJellyfinRuntimeHandlers( }); const handleJellyfinAuthCommands = createHandleJellyfinAuthCommands( - createBuildHandleJellyfinAuthCommandsMainDepsHandler( - options.handleJellyfinAuthCommandsMainDeps, - )(), + options.handleJellyfinAuthCommandsMainDeps, ); const handleJellyfinListCommands = createHandleJellyfinListCommands( - createBuildHandleJellyfinListCommandsMainDepsHandler( - options.handleJellyfinListCommandsMainDeps, - )(), - ); - const handleJellyfinPlayCommand = createHandleJellyfinPlayCommand( - createBuildHandleJellyfinPlayCommandMainDepsHandler({ - ...options.handleJellyfinPlayCommandMainDeps, - playJellyfinItemInMpv: (params) => - playJellyfinItemInMpv(params as Parameters[0]), - })(), + options.handleJellyfinListCommandsMainDeps, ); + const handleJellyfinPlayCommand = createHandleJellyfinPlayCommand({ + ...options.handleJellyfinPlayCommandMainDeps, + playJellyfinItemInMpv: (params) => + playJellyfinItemInMpv(params as Parameters[0]), + }); let startJellyfinRemoteSession!: ReturnType; - const handleJellyfinRemoteAnnounceCommand = createHandleJellyfinRemoteAnnounceCommand( - createBuildHandleJellyfinRemoteAnnounceCommandMainDepsHandler({ - ...options.handleJellyfinRemoteAnnounceCommandMainDeps, - startJellyfinRemoteSession: (startOptions) => startJellyfinRemoteSession(startOptions), - })(), - ); + const handleJellyfinRemoteAnnounceCommand = createHandleJellyfinRemoteAnnounceCommand({ + ...options.handleJellyfinRemoteAnnounceCommandMainDeps, + startJellyfinRemoteSession: (startOptions) => startJellyfinRemoteSession(startOptions), + }); - startJellyfinRemoteSession = createStartJellyfinRemoteSessionHandler( - createBuildStartJellyfinRemoteSessionMainDepsHandler({ - ...options.startJellyfinRemoteSessionMainDeps, - getJellyfinConfig: () => getResolvedJellyfinConfig(), - getClientInfo: () => getJellyfinClientInfo(), - handlePlay: (payload) => handleJellyfinRemotePlay(payload), - handlePlaystate: (payload) => handleJellyfinRemotePlaystate(payload), - handleGeneralCommand: (payload) => handleJellyfinRemoteGeneralCommand(payload), - })(), - ); + startJellyfinRemoteSession = createStartJellyfinRemoteSessionHandler({ + ...options.startJellyfinRemoteSessionMainDeps, + getJellyfinConfig: () => getResolvedJellyfinConfig(), + getClientInfo: () => getJellyfinClientInfo(), + handlePlay: (payload) => handleJellyfinRemotePlay(payload), + handlePlaystate: (payload) => handleJellyfinRemotePlaystate(payload), + handleGeneralCommand: (payload) => handleJellyfinRemoteGeneralCommand(payload), + }); const stopJellyfinRemoteSession = createStopJellyfinRemoteSessionHandler( - createBuildStopJellyfinRemoteSessionMainDepsHandler( - options.stopJellyfinRemoteSessionMainDeps, - )(), + options.stopJellyfinRemoteSessionMainDeps, ); - const runJellyfinCommand = createRunJellyfinCommandHandler( - createBuildRunJellyfinCommandMainDepsHandler({ - ...options.runJellyfinCommandMainDeps, - getJellyfinConfig: () => getResolvedJellyfinConfig(), - getJellyfinClientInfo: (jellyfinConfig) => getJellyfinClientInfo(jellyfinConfig), - handleAuthCommands: (params) => handleJellyfinAuthCommands(params), - handleRemoteAnnounceCommand: (args) => handleJellyfinRemoteAnnounceCommand(args), - handleListCommands: (params) => handleJellyfinListCommands(params), - handlePlayCommand: (params) => handleJellyfinPlayCommand(params), - })(), - ); + const runJellyfinCommand = createRunJellyfinCommandHandler({ + ...options.runJellyfinCommandMainDeps, + getJellyfinConfig: () => getResolvedJellyfinConfig(), + getJellyfinClientInfo: (jellyfinConfig) => getJellyfinClientInfo(jellyfinConfig), + handleAuthCommands: (params) => handleJellyfinAuthCommands(params), + handleRemoteAnnounceCommand: (args) => handleJellyfinRemoteAnnounceCommand(args), + handleListCommands: (params) => handleJellyfinListCommands(params), + handlePlayCommand: (params) => handleJellyfinPlayCommand(params), + }); const maybeFocusExistingJellyfinSetupWindow = createMaybeFocusExistingJellyfinSetupWindowHandler( options.maybeFocusExistingJellyfinSetupWindowMainDeps, diff --git a/src/main/runtime/composers/mpv-runtime-composer.ts b/src/main/runtime/composers/mpv-runtime-composer.ts index c2744e61..43183cf7 100644 --- a/src/main/runtime/composers/mpv-runtime-composer.ts +++ b/src/main/runtime/composers/mpv-runtime-composer.ts @@ -4,17 +4,12 @@ import { createBuildMpvClientRuntimeServiceFactoryDepsHandler } from '../mpv-cli import { createMpvClientRuntimeServiceFactory } from '../mpv-client-runtime-service'; import type { MpvClientRuntimeServiceOptions } from '../mpv-client-runtime-service'; import type { Config } from '../../../types'; -import { createBuildUpdateMpvSubtitleRenderMetricsMainDepsHandler } from '../mpv-subtitle-render-metrics-main-deps'; import { createUpdateMpvSubtitleRenderMetricsHandler } from '../mpv-subtitle-render-metrics'; import { createBuildTokenizerDepsMainHandler, createCreateMecabTokenizerAndCheckMainHandler, createPrewarmSubtitleDictionariesMainHandler, } from '../subtitle-tokenization-main-deps'; -import { - createBuildLaunchBackgroundWarmupTaskMainDepsHandler, - createBuildStartBackgroundWarmupsMainDepsHandler, -} from '../startup-warmups-main-deps'; import { createLaunchBackgroundWarmupTaskHandler as createLaunchBackgroundWarmupTaskFromStartup, createStartBackgroundWarmupsHandler as createStartBackgroundWarmupsFromStartup, @@ -38,7 +33,7 @@ type MpvClientRuntimeServiceFactoryMainDeps 'bindEventHandlers' >; type UpdateMpvSubtitleRenderMetricsMainDeps = Parameters< - typeof createBuildUpdateMpvSubtitleRenderMetricsMainDepsHandler + typeof createUpdateMpvSubtitleRenderMetricsHandler >[0]; type BuildTokenizerDepsMainDeps = Parameters[0]; type TokenizerMainDeps = BuiltMainDeps; @@ -49,10 +44,10 @@ type PrewarmSubtitleDictionariesMainDeps = Parameters< typeof createPrewarmSubtitleDictionariesMainHandler >[0]; type LaunchBackgroundWarmupTaskMainDeps = Parameters< - typeof createBuildLaunchBackgroundWarmupTaskMainDepsHandler + typeof createLaunchBackgroundWarmupTaskFromStartup >[0]; type StartBackgroundWarmupsMainDeps = Omit< - Parameters[0], + Parameters[0], 'launchTask' | 'createMecabTokenizerAndCheck' | 'prewarmSubtitleDictionaries' >; @@ -120,9 +115,7 @@ export function composeMpvRuntimeHandlers< createMpvClientRuntimeServiceFactory(buildMpvClientRuntimeServiceFactoryMainDepsHandler())(); const updateMpvSubtitleRenderMetrics = createUpdateMpvSubtitleRenderMetricsHandler( - createBuildUpdateMpvSubtitleRenderMetricsMainDepsHandler( - options.updateMpvSubtitleRenderMetricsMainDeps, - )(), + options.updateMpvSubtitleRenderMetricsMainDeps, ); const buildTokenizerDepsHandler = createBuildTokenizerDepsMainHandler( @@ -257,48 +250,44 @@ export function composeMpvRuntimeHandlers< }; const launchBackgroundWarmupTask = createLaunchBackgroundWarmupTaskFromStartup( - createBuildLaunchBackgroundWarmupTaskMainDepsHandler( - options.warmups.launchBackgroundWarmupTaskMainDeps, - )(), - ); - const startBackgroundWarmups = createStartBackgroundWarmupsFromStartup( - createBuildStartBackgroundWarmupsMainDepsHandler({ - ...options.warmups.startBackgroundWarmupsMainDeps, - launchTask: (label, task) => launchBackgroundWarmupTask(label, task), - createMecabTokenizerAndCheck: () => createMecabTokenizerAndCheck(), - prewarmSubtitleDictionaries: () => prewarmSubtitleDictionaries(), - onYomitanExtensionWarmupScheduled: (promise) => { - if (tokenizationPrerequisiteWarmupCompleted) { - return; - } - const finalizedPromise = promise - .then(() => { - markTokenizationPrerequisiteWarmupCompleted(); - }) - .finally(() => { - if (tokenizationPrerequisiteWarmupInFlight === finalizedPromise) { - tokenizationPrerequisiteWarmupInFlight = null; - } - }); - tokenizationPrerequisiteWarmupInFlight = finalizedPromise; - }, - onTokenizationWarmupScheduled: (promise) => { - if (tokenizationWarmupCompleted || !backgroundWarmupCoversOnDemandTokenization()) { - return; - } - const finalizedPromise = promise - .then(() => { - markTokenizationWarmupCompleted(); - }) - .finally(() => { - if (tokenizationWarmupInFlight === finalizedPromise) { - tokenizationWarmupInFlight = null; - } - }); - tokenizationWarmupInFlight = finalizedPromise; - }, - })(), + options.warmups.launchBackgroundWarmupTaskMainDeps, ); + const startBackgroundWarmups = createStartBackgroundWarmupsFromStartup({ + ...options.warmups.startBackgroundWarmupsMainDeps, + launchTask: (label, task) => launchBackgroundWarmupTask(label, task), + createMecabTokenizerAndCheck: () => createMecabTokenizerAndCheck(), + prewarmSubtitleDictionaries: () => prewarmSubtitleDictionaries(), + onYomitanExtensionWarmupScheduled: (promise) => { + if (tokenizationPrerequisiteWarmupCompleted) { + return; + } + const finalizedPromise = promise + .then(() => { + markTokenizationPrerequisiteWarmupCompleted(); + }) + .finally(() => { + if (tokenizationPrerequisiteWarmupInFlight === finalizedPromise) { + tokenizationPrerequisiteWarmupInFlight = null; + } + }); + tokenizationPrerequisiteWarmupInFlight = finalizedPromise; + }, + onTokenizationWarmupScheduled: (promise) => { + if (tokenizationWarmupCompleted || !backgroundWarmupCoversOnDemandTokenization()) { + return; + } + const finalizedPromise = promise + .then(() => { + markTokenizationWarmupCompleted(); + }) + .finally(() => { + if (tokenizationWarmupInFlight === finalizedPromise) { + tokenizationWarmupInFlight = null; + } + }); + tokenizationWarmupInFlight = finalizedPromise; + }, + }); return { bindMpvClientEventHandlers: (client) => bindMpvClientEventHandlers(client), diff --git a/src/main/runtime/composers/overlay-visibility-runtime-composer.ts b/src/main/runtime/composers/overlay-visibility-runtime-composer.ts index 2d971963..cb7b5603 100644 --- a/src/main/runtime/composers/overlay-visibility-runtime-composer.ts +++ b/src/main/runtime/composers/overlay-visibility-runtime-composer.ts @@ -5,29 +5,22 @@ import { createSendToActiveOverlayWindowHandler, createSetOverlayDebugVisualizationEnabledHandler, } from '../overlay-runtime-main-actions'; -import { - createBuildBroadcastRuntimeOptionsChangedMainDepsHandler, - createBuildOpenRuntimeOptionsPaletteMainDepsHandler, - createBuildRestorePreviousSecondarySubVisibilityMainDepsHandler, - createBuildSendToActiveOverlayWindowMainDepsHandler, - createBuildSetOverlayDebugVisualizationEnabledMainDepsHandler, -} from '../overlay-runtime-main-actions-main-deps'; import type { ComposerInputs, ComposerOutputs } from './contracts'; type RestorePreviousSecondarySubVisibilityMainDeps = Parameters< - typeof createBuildRestorePreviousSecondarySubVisibilityMainDepsHandler + typeof createRestorePreviousSecondarySubVisibilityHandler >[0]; type BroadcastRuntimeOptionsChangedMainDeps = Parameters< - typeof createBuildBroadcastRuntimeOptionsChangedMainDepsHandler + typeof createBroadcastRuntimeOptionsChangedHandler >[0]; type SendToActiveOverlayWindowMainDeps = Parameters< - typeof createBuildSendToActiveOverlayWindowMainDepsHandler + typeof createSendToActiveOverlayWindowHandler >[0]; type SetOverlayDebugVisualizationEnabledMainDeps = Parameters< - typeof createBuildSetOverlayDebugVisualizationEnabledMainDepsHandler + typeof createSetOverlayDebugVisualizationEnabledHandler >[0]; type OpenRuntimeOptionsPaletteMainDeps = Parameters< - typeof createBuildOpenRuntimeOptionsPaletteMainDepsHandler + typeof createOpenRuntimeOptionsPaletteHandler >[0]; export type OverlayVisibilityRuntimeComposerOptions = ComposerInputs<{ @@ -61,29 +54,19 @@ export function composeOverlayVisibilityRuntime( updateVisibleOverlayVisibility: () => options.overlayVisibilityRuntime.updateVisibleOverlayVisibility(), restorePreviousSecondarySubVisibility: createRestorePreviousSecondarySubVisibilityHandler( - createBuildRestorePreviousSecondarySubVisibilityMainDepsHandler( - options.restorePreviousSecondarySubVisibilityMainDeps, - )(), + options.restorePreviousSecondarySubVisibilityMainDeps, ), broadcastRuntimeOptionsChanged: createBroadcastRuntimeOptionsChangedHandler( - createBuildBroadcastRuntimeOptionsChangedMainDepsHandler( - options.broadcastRuntimeOptionsChangedMainDeps, - )(), + options.broadcastRuntimeOptionsChangedMainDeps, ), sendToActiveOverlayWindow: createSendToActiveOverlayWindowHandler( - createBuildSendToActiveOverlayWindowMainDepsHandler( - options.sendToActiveOverlayWindowMainDeps, - )(), + options.sendToActiveOverlayWindowMainDeps, ), setOverlayDebugVisualizationEnabled: createSetOverlayDebugVisualizationEnabledHandler( - createBuildSetOverlayDebugVisualizationEnabledMainDepsHandler( - options.setOverlayDebugVisualizationEnabledMainDeps, - )(), + options.setOverlayDebugVisualizationEnabledMainDeps, ), openRuntimeOptionsPalette: createOpenRuntimeOptionsPaletteHandler( - createBuildOpenRuntimeOptionsPaletteMainDepsHandler( - options.openRuntimeOptionsPaletteMainDeps, - )(), + options.openRuntimeOptionsPaletteMainDeps, ), }; } diff --git a/src/main/runtime/composers/shortcuts-runtime-composer.test.ts b/src/main/runtime/composers/shortcuts-runtime-composer.test.ts new file mode 100644 index 00000000..79536ecb --- /dev/null +++ b/src/main/runtime/composers/shortcuts-runtime-composer.test.ts @@ -0,0 +1,69 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { + composeShortcutRuntimes, + type ShortcutsRuntimeComposerOptions, +} from './shortcuts-runtime-composer'; + +type GlobalShortcutsOptions = ShortcutsRuntimeComposerOptions['globalShortcuts']; + +function createComposerOptions(tryBeginOverlaySelection: (timeoutMs: number) => boolean) { + const registeredAccelerators: string[] = []; + const options: ShortcutsRuntimeComposerOptions = { + // Global and overlay shortcut handlers are only constructed here, never invoked. + globalShortcuts: { + getConfiguredShortcutsMainDeps: + {} as GlobalShortcutsOptions['getConfiguredShortcutsMainDeps'], + buildRegisterGlobalShortcutsMainDeps: () => + ({}) as ReturnType, + buildRefreshGlobalAndOverlayShortcutsMainDeps: () => + ({}) as ReturnType, + }, + overlayShortcutsRuntimeMainDeps: + {} as ShortcutsRuntimeComposerOptions['overlayShortcutsRuntimeMainDeps'], + numericShortcutRuntimeMainDeps: { + globalShortcut: { + register: (accelerator) => { + registeredAccelerators.push(accelerator); + return true; + }, + unregister: () => {}, + }, + showMpvOsd: () => {}, + setTimer: () => setTimeout(() => {}, 0), + clearTimer: (timer) => clearTimeout(timer), + }, + numericSessions: { + onMultiCopyDigit: () => {}, + onMineSentenceDigit: () => {}, + tryBeginMultiCopyOverlaySelection: tryBeginOverlaySelection, + tryBeginMineSentenceOverlaySelection: tryBeginOverlaySelection, + }, + }; + return { options, registeredAccelerators }; +} + +test('numeric sessions delegate to the visible overlay when it accepts the selection', () => { + const timeouts: number[] = []; + const { options, registeredAccelerators } = createComposerOptions((timeoutMs) => { + timeouts.push(timeoutMs); + return true; + }); + const runtimes = composeShortcutRuntimes(options); + + runtimes.startPendingMultiCopy(3000); + runtimes.startPendingMineSentenceMultiple(4000); + + assert.deepEqual(timeouts, [3000, 4000]); + assert.deepEqual(registeredAccelerators, []); +}); + +test('numeric sessions fall back to global digit shortcuts when the overlay declines', () => { + const { options, registeredAccelerators } = createComposerOptions(() => false); + const runtimes = composeShortcutRuntimes(options); + + runtimes.startPendingMultiCopy(3000); + + assert.ok(registeredAccelerators.length > 0); + runtimes.cancelPendingMultiCopy(); +}); diff --git a/src/main/runtime/composers/shortcuts-runtime-composer.ts b/src/main/runtime/composers/shortcuts-runtime-composer.ts index cd9b53f9..1f7e0384 100644 --- a/src/main/runtime/composers/shortcuts-runtime-composer.ts +++ b/src/main/runtime/composers/shortcuts-runtime-composer.ts @@ -1,6 +1,5 @@ import { createNumericShortcutRuntime } from '../../../core/services/numeric-shortcut'; import { - createBuildNumericShortcutRuntimeMainDepsHandler, createGlobalShortcutsRuntimeHandlers, createNumericShortcutSessionRuntimeHandlers, createOverlayShortcutsRuntimeHandlers, @@ -8,9 +7,7 @@ import { import type { ComposerInputs, ComposerOutputs } from './contracts'; type GlobalShortcutsOptions = Parameters[0]; -type NumericShortcutRuntimeMainDeps = Parameters< - typeof createBuildNumericShortcutRuntimeMainDepsHandler ->[0]; +type NumericShortcutRuntimeMainDeps = Parameters[0]; type NumericSessionOptions = Omit< Parameters[0], 'multiCopySession' | 'mineSentenceSession' @@ -37,15 +34,13 @@ export function composeShortcutRuntimes( ): ShortcutsRuntimeComposerResult { const globalShortcuts = createGlobalShortcutsRuntimeHandlers(options.globalShortcuts); - const numericShortcutRuntimeMainDeps = createBuildNumericShortcutRuntimeMainDepsHandler( + const numericShortcutRuntime = createNumericShortcutRuntime( options.numericShortcutRuntimeMainDeps, - )(); - const numericShortcutRuntime = createNumericShortcutRuntime(numericShortcutRuntimeMainDeps); + ); const numericSessions = createNumericShortcutSessionRuntimeHandlers({ + ...options.numericSessions, multiCopySession: numericShortcutRuntime.createSession(), mineSentenceSession: numericShortcutRuntime.createSession(), - onMultiCopyDigit: options.numericSessions.onMultiCopyDigit, - onMineSentenceDigit: options.numericSessions.onMineSentenceDigit, }); const overlayShortcuts = createOverlayShortcutsRuntimeHandlers({ diff --git a/src/main/runtime/domains/anilist.ts b/src/main/runtime/domains/anilist.ts index 7cee9eda..a46842d2 100644 --- a/src/main/runtime/domains/anilist.ts +++ b/src/main/runtime/domains/anilist.ts @@ -1,15 +1,9 @@ export * from '../anilist-media-guess'; -export * from '../anilist-media-guess-main-deps'; export * from '../anilist-media-state'; -export * from '../anilist-media-state-main-deps'; export * from '../anilist-post-watch'; -export * from '../anilist-post-watch-main-deps'; export * from '../anilist-setup'; export * from '../anilist-setup-protocol'; -export * from '../anilist-setup-protocol-main-deps'; export * from '../anilist-setup-window'; -export * from '../anilist-setup-window-main-deps'; export * from '../anilist-state'; export * from '../anilist-token-refresh'; -export * from '../anilist-token-refresh-main-deps'; export * from '../protocol-url-handlers'; diff --git a/src/main/runtime/domains/ipc.ts b/src/main/runtime/domains/ipc.ts index eebc2663..d29ab724 100644 --- a/src/main/runtime/domains/ipc.ts +++ b/src/main/runtime/domains/ipc.ts @@ -1,5 +1,4 @@ export * from '../cli-command-context-factory'; export * from '../cli-command-runtime-handler'; export * from '../initial-args-runtime-handler'; -export * from '../ipc-mpv-command-main-deps'; export * from '../ipc-runtime-handlers'; diff --git a/src/main/runtime/domains/jellyfin.ts b/src/main/runtime/domains/jellyfin.ts index 9c5f9d33..ab408ecb 100644 --- a/src/main/runtime/domains/jellyfin.ts +++ b/src/main/runtime/domains/jellyfin.ts @@ -1,25 +1,18 @@ export * from '../jellyfin-cli-auth'; export * from '../jellyfin-cli-list'; -export * from '../jellyfin-cli-main-deps'; export * from '../jellyfin-cli-play'; export * from '../jellyfin-cli-remote-announce'; export * from '../jellyfin-client-info'; export * from '../jellyfin-client-info-main-deps'; export * from '../jellyfin-command-dispatch'; -export * from '../jellyfin-command-dispatch-main-deps'; export * from '../jellyfin-device-identity'; export * from '../jellyfin-playback-launch'; -export * from '../jellyfin-playback-launch-main-deps'; export * from '../jellyfin-remote-commands'; export * from '../jellyfin-remote-connection'; -export * from '../jellyfin-remote-connection-main-deps'; export * from '../jellyfin-remote-main-deps'; export * from '../jellyfin-remote-playback'; export * from '../jellyfin-remote-session-lifecycle'; -export * from '../jellyfin-remote-session-main-deps'; export * from '../jellyfin-setup-window'; export * from '../jellyfin-setup-window-main-deps'; export * from '../jellyfin-subtitle-preload'; -export * from '../jellyfin-subtitle-preload-main-deps'; export * from '../mpv-jellyfin-defaults'; -export * from '../mpv-jellyfin-defaults-main-deps'; diff --git a/src/main/runtime/domains/mining.ts b/src/main/runtime/domains/mining.ts index de5644a0..2caee6cb 100644 --- a/src/main/runtime/domains/mining.ts +++ b/src/main/runtime/domains/mining.ts @@ -1,4 +1,2 @@ export * from '../anki-actions'; -export * from '../anki-actions-main-deps'; export * from '../mining-actions'; -export * from '../mining-actions-main-deps'; diff --git a/src/main/runtime/domains/mpv.ts b/src/main/runtime/domains/mpv.ts index b3430550..77c79da8 100644 --- a/src/main/runtime/domains/mpv.ts +++ b/src/main/runtime/domains/mpv.ts @@ -4,6 +4,5 @@ export * from '../mpv-main-event-bindings'; export * from '../mpv-main-event-main-deps'; export * from '../mpv-osd-runtime-handlers'; export * from '../mpv-subtitle-render-metrics'; -export * from '../mpv-subtitle-render-metrics-main-deps'; export * from '../secondary-sub-mode-runtime-handler'; export * from '../subtitle-tokenization-main-deps'; diff --git a/src/main/runtime/domains/overlay.ts b/src/main/runtime/domains/overlay.ts index 0869a2c3..3cb9df43 100644 --- a/src/main/runtime/domains/overlay.ts +++ b/src/main/runtime/domains/overlay.ts @@ -1,21 +1,15 @@ export * from '../config-hot-reload-handlers'; export * from '../config-hot-reload-main-deps'; -export * from '../field-grouping-overlay-main-deps'; export * from '../field-grouping-resolver'; -export * from '../field-grouping-resolver-main-deps'; -export * from '../overlay-bootstrap-main-deps'; export * from '../overlay-main-actions'; -export * from '../overlay-main-actions-main-deps'; export * from '../overlay-runtime-bootstrap-handlers'; export * from '../overlay-runtime-main-actions'; -export * from '../overlay-runtime-main-actions-main-deps'; export * from '../overlay-visibility-runtime'; export * from '../overlay-visibility-runtime-main-deps'; export * from '../overlay-window-layout'; export * from '../overlay-window-layout-main-deps'; export * from '../overlay-window-runtime-handlers'; export * from '../subtitle-position'; -export * from '../subtitle-position-main-deps'; export * from '../tray-runtime'; export * from '../tray-runtime-handlers'; export * from '../yomitan-extension-runtime'; diff --git a/src/main/runtime/domains/shortcuts.ts b/src/main/runtime/domains/shortcuts.ts index 30048f4f..5278ec1f 100644 --- a/src/main/runtime/domains/shortcuts.ts +++ b/src/main/runtime/domains/shortcuts.ts @@ -1,5 +1,4 @@ export * from '../global-shortcuts-runtime-handlers'; -export * from '../numeric-shortcut-runtime-main-deps'; export * from '../numeric-shortcut-session-runtime-handlers'; export * from '../overlay-shortcuts-runtime-handlers'; export * from '../overlay-shortcuts-runtime-main-deps'; diff --git a/src/main/runtime/domains/startup.ts b/src/main/runtime/domains/startup.ts index 599d063f..30b12dca 100644 --- a/src/main/runtime/domains/startup.ts +++ b/src/main/runtime/domains/startup.ts @@ -1,19 +1,12 @@ export * from '../app-lifecycle-actions'; export * from '../app-lifecycle-main-activate'; export * from '../app-lifecycle-main-cleanup'; -export * from '../app-ready-main-deps'; export * from '../clipboard-queue'; export * from '../config-derived'; export * from '../dictionary-runtime-main-deps'; export * from '../immersion-media'; export * from '../immersion-startup'; -export * from '../immersion-startup-main-deps'; -export * from '../media-runtime-main-deps'; -export * from '../runtime-bootstrap-main-deps'; export * from '../startup-config'; -export * from '../startup-config-main-deps'; export * from '../startup-runtime-handlers'; export * from '../startup-warmups'; -export * from '../startup-warmups-main-deps'; -export * from '../subtitle-processing-main-deps'; export * from '../subsync-runtime'; diff --git a/src/main/runtime/field-grouping-overlay-main-deps.ts b/src/main/runtime/field-grouping-overlay-main-deps.ts deleted file mode 100644 index 3de132cc..00000000 --- a/src/main/runtime/field-grouping-overlay-main-deps.ts +++ /dev/null @@ -1,46 +0,0 @@ -import type { FieldGroupingOverlayRuntimeOptions } from '../../core/services/field-grouping-overlay'; - -type FieldGroupingOverlayMainDeps = Omit< - FieldGroupingOverlayRuntimeOptions, - 'sendToVisibleOverlay' -> & { - sendToActiveOverlayWindow: ( - channel: string, - payload?: unknown, - runtimeOptions?: { restoreOnModalClose?: TModal; preferModalWindow?: boolean }, - ) => boolean; -}; - -type BuiltFieldGroupingOverlayMainDeps = - FieldGroupingOverlayRuntimeOptions & { - sendToVisibleOverlay: NonNullable< - FieldGroupingOverlayRuntimeOptions['sendToVisibleOverlay'] - >; - }; - -export function createBuildFieldGroupingOverlayMainDepsHandler( - deps: FieldGroupingOverlayMainDeps, -) { - return (): BuiltFieldGroupingOverlayMainDeps => ({ - getMainWindow: () => deps.getMainWindow(), - getVisibleOverlayVisible: () => deps.getVisibleOverlayVisible(), - setVisibleOverlayVisible: (visible: boolean) => deps.setVisibleOverlayVisible(visible), - getResolver: () => deps.getResolver(), - setResolver: (resolver) => deps.setResolver(resolver), - getRestoreVisibleOverlayOnModalClose: () => deps.getRestoreVisibleOverlayOnModalClose(), - // These are optional on the runtime options, so a missing forward compiles silently — but - // dropping them left the field grouping modal with no modal-open ack/retry, no teardown on - // failure, and no warn logging, which is why the earlier recovery fixes never took effect. - waitForModalOpen: deps.waitForModalOpen, - handleOverlayModalClosed: deps.handleOverlayModalClosed, - logWarn: deps.logWarn, - ensureOverlayStartupPrereqs: deps.ensureOverlayStartupPrereqs, - ensureOverlayWindowsReadyForVisibilityActions: - deps.ensureOverlayWindowsReadyForVisibilityActions, - sendToVisibleOverlay: ( - channel: string, - payload?: unknown, - runtimeOptions?: { restoreOnModalClose?: TModal; preferModalWindow?: boolean }, - ) => deps.sendToActiveOverlayWindow(channel, payload, runtimeOptions), - }); -} diff --git a/src/main/runtime/field-grouping-resolver-main-deps.ts b/src/main/runtime/field-grouping-resolver-main-deps.ts deleted file mode 100644 index 73d54cfc..00000000 --- a/src/main/runtime/field-grouping-resolver-main-deps.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { - createGetFieldGroupingResolverHandler, - createSetFieldGroupingResolverHandler, -} from './field-grouping-resolver'; - -type GetFieldGroupingResolverMainDeps = Parameters[0]; -type SetFieldGroupingResolverMainDeps = Parameters[0]; - -export function createBuildGetFieldGroupingResolverMainDepsHandler( - deps: GetFieldGroupingResolverMainDeps, -) { - return (): GetFieldGroupingResolverMainDeps => ({ - getResolver: () => deps.getResolver(), - }); -} - -export function createBuildSetFieldGroupingResolverMainDepsHandler( - deps: SetFieldGroupingResolverMainDeps, -) { - return (): SetFieldGroupingResolverMainDeps => ({ - setResolver: (resolver) => deps.setResolver(resolver), - nextSequence: () => deps.nextSequence(), - getSequence: () => deps.getSequence(), - }); -} diff --git a/src/main/runtime/global-shortcuts-main-deps.ts b/src/main/runtime/global-shortcuts-main-deps.ts deleted file mode 100644 index 5909d18e..00000000 --- a/src/main/runtime/global-shortcuts-main-deps.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { Config } from '../../types'; -import type { ConfiguredShortcuts } from '../../core/utils/shortcut-config'; -import type { RegisterGlobalShortcutsServiceOptions } from '../../core/services/shortcut'; - -export function createBuildGetConfiguredShortcutsMainDepsHandler(deps: { - getResolvedConfig: () => Config; - defaultConfig: Config; - resolveConfiguredShortcuts: (config: Config, defaultConfig: Config) => ConfiguredShortcuts; -}) { - return () => ({ - getResolvedConfig: () => deps.getResolvedConfig(), - defaultConfig: deps.defaultConfig, - resolveConfiguredShortcuts: (config: Config, defaultConfig: Config) => - deps.resolveConfiguredShortcuts(config, defaultConfig), - }); -} - -export function createBuildRegisterGlobalShortcutsMainDepsHandler(deps: { - getConfiguredShortcuts: () => RegisterGlobalShortcutsServiceOptions['shortcuts']; - registerGlobalShortcutsCore: (options: RegisterGlobalShortcutsServiceOptions) => void; - toggleVisibleOverlay: () => void; - openYomitanSettings: () => void; - isDev: boolean; - getMainWindow: RegisterGlobalShortcutsServiceOptions['getMainWindow']; -}) { - return () => ({ - getConfiguredShortcuts: () => deps.getConfiguredShortcuts(), - registerGlobalShortcutsCore: (options: RegisterGlobalShortcutsServiceOptions) => - deps.registerGlobalShortcutsCore(options), - onToggleVisibleOverlay: () => deps.toggleVisibleOverlay(), - onOpenYomitanSettings: () => deps.openYomitanSettings(), - isDev: deps.isDev, - getMainWindow: deps.getMainWindow, - }); -} - -export function createBuildRefreshGlobalAndOverlayShortcutsMainDepsHandler(deps: { - unregisterAllGlobalShortcuts: () => void; - registerGlobalShortcuts: () => void; - syncOverlayShortcuts: () => void; -}) { - return () => ({ - unregisterAllGlobalShortcuts: () => deps.unregisterAllGlobalShortcuts(), - registerGlobalShortcuts: () => deps.registerGlobalShortcuts(), - syncOverlayShortcuts: () => deps.syncOverlayShortcuts(), - }); -} diff --git a/src/main/runtime/global-shortcuts-runtime-handlers.ts b/src/main/runtime/global-shortcuts-runtime-handlers.ts index 2ad6553b..7f9e6a97 100644 --- a/src/main/runtime/global-shortcuts-runtime-handlers.ts +++ b/src/main/runtime/global-shortcuts-runtime-handlers.ts @@ -4,20 +4,11 @@ import { createRefreshGlobalAndOverlayShortcutsHandler, createRegisterGlobalShortcutsHandler, } from './global-shortcuts'; -import { - createBuildGetConfiguredShortcutsMainDepsHandler, - createBuildRefreshGlobalAndOverlayShortcutsMainDepsHandler, - createBuildRegisterGlobalShortcutsMainDepsHandler, -} from './global-shortcuts-main-deps'; -type GetConfiguredShortcutsMainDeps = Parameters< - typeof createBuildGetConfiguredShortcutsMainDepsHandler ->[0]; -type RegisterGlobalShortcutsMainDeps = Parameters< - typeof createBuildRegisterGlobalShortcutsMainDepsHandler ->[0]; +type GetConfiguredShortcutsMainDeps = Parameters[0]; +type RegisterGlobalShortcutsMainDeps = Parameters[0]; type RefreshGlobalAndOverlayShortcutsMainDeps = Parameters< - typeof createBuildRefreshGlobalAndOverlayShortcutsMainDepsHandler + typeof createRefreshGlobalAndOverlayShortcutsHandler >[0]; export function createGlobalShortcutsRuntimeHandlers(deps: { @@ -29,28 +20,18 @@ export function createGlobalShortcutsRuntimeHandlers(deps: { registerGlobalShortcuts: () => void, ) => RefreshGlobalAndOverlayShortcutsMainDeps; }) { - const getConfiguredShortcutsMainDeps = createBuildGetConfiguredShortcutsMainDepsHandler( - deps.getConfiguredShortcutsMainDeps, - )(); const getConfiguredShortcutsHandler = createGetConfiguredShortcutsHandler( - getConfiguredShortcutsMainDeps, + deps.getConfiguredShortcutsMainDeps, ); const getConfiguredShortcuts = () => getConfiguredShortcutsHandler(); - const registerGlobalShortcutsMainDeps = createBuildRegisterGlobalShortcutsMainDepsHandler( - deps.buildRegisterGlobalShortcutsMainDeps(getConfiguredShortcuts), - )(); const registerGlobalShortcutsHandler = createRegisterGlobalShortcutsHandler( - registerGlobalShortcutsMainDeps, + deps.buildRegisterGlobalShortcutsMainDeps(getConfiguredShortcuts), ); const registerGlobalShortcuts = () => registerGlobalShortcutsHandler(); - const refreshGlobalAndOverlayShortcutsMainDeps = - createBuildRefreshGlobalAndOverlayShortcutsMainDepsHandler( - deps.buildRefreshGlobalAndOverlayShortcutsMainDeps(registerGlobalShortcuts), - )(); const refreshGlobalAndOverlayShortcutsHandler = createRefreshGlobalAndOverlayShortcutsHandler( - refreshGlobalAndOverlayShortcutsMainDeps, + deps.buildRefreshGlobalAndOverlayShortcutsMainDeps(registerGlobalShortcuts), ); const refreshGlobalAndOverlayShortcuts = () => refreshGlobalAndOverlayShortcutsHandler(); diff --git a/src/main/runtime/immersion-startup-main-deps.ts b/src/main/runtime/immersion-startup-main-deps.ts deleted file mode 100644 index 7df1844d..00000000 --- a/src/main/runtime/immersion-startup-main-deps.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { - ImmersionTrackerStartupDeps, - createImmersionTrackerStartupHandler, -} from './immersion-startup'; - -type ImmersionTrackerStartupMainDeps = Parameters[0]; - -export function createBuildImmersionTrackerStartupMainDepsHandler( - deps: ImmersionTrackerStartupMainDeps, -) { - return (): ImmersionTrackerStartupDeps => ({ - getResolvedConfig: () => deps.getResolvedConfig(), - getConfiguredDbPath: () => deps.getConfiguredDbPath(), - createTrackerService: (params) => deps.createTrackerService(params), - setTracker: (tracker) => deps.setTracker(tracker), - getMpvClient: () => deps.getMpvClient(), - seedTrackerFromCurrentMedia: () => deps.seedTrackerFromCurrentMedia(), - logInfo: (message: string) => deps.logInfo(message), - logDebug: (message: string) => deps.logDebug(message), - logWarn: (message: string, details: unknown) => deps.logWarn(message, details), - }); -} diff --git a/src/main/runtime/initial-args-main-deps.ts b/src/main/runtime/initial-args-main-deps.ts deleted file mode 100644 index 9da01e5b..00000000 --- a/src/main/runtime/initial-args-main-deps.ts +++ /dev/null @@ -1,38 +0,0 @@ -import type { CliArgs } from '../../cli/args'; - -export function createBuildHandleInitialArgsMainDepsHandler(deps: { - getInitialArgs: () => CliArgs | null; - isBackgroundMode: () => boolean; - shouldEnsureTrayOnStartup: () => boolean; - shouldRunHeadlessInitialCommand: (args: CliArgs) => boolean; - ensureTray: () => void; - isTexthookerOnlyMode: () => boolean; - hasImmersionTracker: () => boolean; - getMpvClient: () => { connected: boolean; connect: () => void } | null; - commandNeedsOverlayStartupPrereqs: (args: CliArgs) => boolean; - commandNeedsOverlayRuntime: (args: CliArgs) => boolean; - ensureOverlayStartupPrereqs: () => void; - isOverlayRuntimeInitialized: () => boolean; - initializeOverlayRuntime: () => void; - logInfo: (message: string) => void; - handleCliCommand: (args: CliArgs, source: 'initial') => void; -}) { - return () => ({ - getInitialArgs: () => deps.getInitialArgs(), - isBackgroundMode: () => deps.isBackgroundMode(), - shouldEnsureTrayOnStartup: () => deps.shouldEnsureTrayOnStartup(), - shouldRunHeadlessInitialCommand: (args: CliArgs) => deps.shouldRunHeadlessInitialCommand(args), - ensureTray: () => deps.ensureTray(), - 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(), - initializeOverlayRuntime: () => deps.initializeOverlayRuntime(), - logInfo: (message: string) => deps.logInfo(message), - handleCliCommand: (args: CliArgs, source: 'initial') => deps.handleCliCommand(args, source), - }); -} diff --git a/src/main/runtime/initial-args-runtime-handler.ts b/src/main/runtime/initial-args-runtime-handler.ts index dc2da8af..eb9408e3 100644 --- a/src/main/runtime/initial-args-runtime-handler.ts +++ b/src/main/runtime/initial-args-runtime-handler.ts @@ -1,10 +1,7 @@ import { createHandleInitialArgsHandler } from './initial-args-handler'; -import { createBuildHandleInitialArgsMainDepsHandler } from './initial-args-main-deps'; -type InitialArgsMainDeps = Parameters[0]; +type InitialArgsMainDeps = Parameters[0]; export function createInitialArgsRuntimeHandler(deps: InitialArgsMainDeps) { - const buildHandleInitialArgsMainDepsHandler = createBuildHandleInitialArgsMainDepsHandler(deps); - const handleInitialArgsMainDeps = buildHandleInitialArgsMainDepsHandler(); - return createHandleInitialArgsHandler(handleInitialArgsMainDeps); + return createHandleInitialArgsHandler(deps); } diff --git a/src/main/runtime/ipc-bridge-actions-main-deps.ts b/src/main/runtime/ipc-bridge-actions-main-deps.ts deleted file mode 100644 index 256455c3..00000000 --- a/src/main/runtime/ipc-bridge-actions-main-deps.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { createHandleMpvCommandFromIpcHandler } from './ipc-bridge-actions'; - -type HandleMpvCommandFromIpcMainDeps = Parameters[0]; - -export function createBuildHandleMpvCommandFromIpcMainDepsHandler( - deps: HandleMpvCommandFromIpcMainDeps, -) { - return (): HandleMpvCommandFromIpcMainDeps => ({ - handleMpvCommandFromIpcRuntime: (command, options) => - deps.handleMpvCommandFromIpcRuntime(command, options), - buildMpvCommandDeps: () => deps.buildMpvCommandDeps(), - }); -} - -export function createBuildRunSubsyncManualFromIpcMainDepsHandler(deps: { - runManualFromIpc: (request: TRequest) => Promise; -}) { - return () => ({ - runManualFromIpc: (request: TRequest) => deps.runManualFromIpc(request), - }); -} diff --git a/src/main/runtime/ipc-mpv-command-main-deps.ts b/src/main/runtime/ipc-mpv-command-main-deps.ts deleted file mode 100644 index 189e0b5b..00000000 --- a/src/main/runtime/ipc-mpv-command-main-deps.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { MpvCommandFromIpcRuntimeDeps } from '../ipc-mpv-command'; - -export function createBuildMpvCommandFromIpcRuntimeMainDepsHandler( - deps: MpvCommandFromIpcRuntimeDeps, -) { - return (): MpvCommandFromIpcRuntimeDeps => { - const showPlaybackFeedback = deps.showPlaybackFeedback; - const showRawMpvOsd = deps.showRawMpvOsd; - return { - triggerSubsyncFromConfig: () => deps.triggerSubsyncFromConfig(), - openRuntimeOptionsPalette: () => deps.openRuntimeOptionsPalette(), - openJimaku: () => deps.openJimaku(), - openTsukihime: () => deps.openTsukihime(), - openYoutubeTrackPicker: () => deps.openYoutubeTrackPicker(), - openPlaylistBrowser: () => deps.openPlaylistBrowser(), - cycleRuntimeOption: (id, direction) => deps.cycleRuntimeOption(id, direction), - showMpvOsd: (text: string) => deps.showMpvOsd(text), - ...(showRawMpvOsd ? { showRawMpvOsd: (text: string) => showRawMpvOsd(text) } : {}), - ...(showPlaybackFeedback - ? { showPlaybackFeedback: (text: string) => showPlaybackFeedback(text) } - : {}), - replayCurrentSubtitle: () => deps.replayCurrentSubtitle(), - playNextSubtitle: () => deps.playNextSubtitle(), - sendMpvCommand: (command: (string | number)[]) => deps.sendMpvCommand(command), - getMpvClient: () => deps.getMpvClient(), - isMpvConnected: () => deps.isMpvConnected(), - hasRuntimeOptionsManager: () => deps.hasRuntimeOptionsManager(), - }; - }; -} diff --git a/src/main/runtime/ipc-runtime-handlers.ts b/src/main/runtime/ipc-runtime-handlers.ts index 867b0577..ab7e6b76 100644 --- a/src/main/runtime/ipc-runtime-handlers.ts +++ b/src/main/runtime/ipc-runtime-handlers.ts @@ -2,35 +2,18 @@ import { createHandleMpvCommandFromIpcHandler, createRunSubsyncManualFromIpcHandler, } from './ipc-bridge-actions'; -import { - createBuildHandleMpvCommandFromIpcMainDepsHandler, - createBuildRunSubsyncManualFromIpcMainDepsHandler, -} from './ipc-bridge-actions-main-deps'; - -type HandleMpvCommandFromIpcMainDeps = Parameters< - typeof createBuildHandleMpvCommandFromIpcMainDepsHandler ->[0]; -type RunSubsyncManualFromIpcMainDeps = Parameters< - typeof createBuildRunSubsyncManualFromIpcMainDepsHandler ->[0]; export function createIpcRuntimeHandlers(deps: { - handleMpvCommandFromIpcDeps: HandleMpvCommandFromIpcMainDeps; - runSubsyncManualFromIpcDeps: RunSubsyncManualFromIpcMainDeps; + handleMpvCommandFromIpcDeps: Parameters[0]; + runSubsyncManualFromIpcDeps: Parameters< + typeof createRunSubsyncManualFromIpcHandler + >[0]; }) { - const handleMpvCommandFromIpcMainDeps = createBuildHandleMpvCommandFromIpcMainDepsHandler( - deps.handleMpvCommandFromIpcDeps, - )(); const handleMpvCommandFromIpc = createHandleMpvCommandFromIpcHandler( - handleMpvCommandFromIpcMainDeps, + deps.handleMpvCommandFromIpcDeps, ); - - const runSubsyncManualFromIpcMainDeps = createBuildRunSubsyncManualFromIpcMainDepsHandler< - TRequest, - TResult - >(deps.runSubsyncManualFromIpcDeps)(); const runSubsyncManualFromIpc = createRunSubsyncManualFromIpcHandler( - runSubsyncManualFromIpcMainDeps, + deps.runSubsyncManualFromIpcDeps, ); return { diff --git a/src/main/runtime/jellyfin-cli-main-deps.ts b/src/main/runtime/jellyfin-cli-main-deps.ts deleted file mode 100644 index 50203161..00000000 --- a/src/main/runtime/jellyfin-cli-main-deps.ts +++ /dev/null @@ -1,59 +0,0 @@ -import type { createHandleJellyfinAuthCommands } from './jellyfin-cli-auth'; -import type { createHandleJellyfinListCommands } from './jellyfin-cli-list'; -import type { createHandleJellyfinPlayCommand } from './jellyfin-cli-play'; -import type { createHandleJellyfinRemoteAnnounceCommand } from './jellyfin-cli-remote-announce'; - -type HandleJellyfinAuthCommandsMainDeps = Parameters[0]; -type HandleJellyfinListCommandsMainDeps = Parameters[0]; -type HandleJellyfinPlayCommandMainDeps = Parameters[0]; -type HandleJellyfinRemoteAnnounceCommandMainDeps = Parameters< - typeof createHandleJellyfinRemoteAnnounceCommand ->[0]; - -export function createBuildHandleJellyfinAuthCommandsMainDepsHandler( - deps: HandleJellyfinAuthCommandsMainDeps, -) { - return (): HandleJellyfinAuthCommandsMainDeps => ({ - patchRawConfig: (patch) => deps.patchRawConfig(patch), - authenticateWithPassword: (serverUrl, username, password, clientInfo) => - deps.authenticateWithPassword(serverUrl, username, password, clientInfo), - saveStoredSession: (session) => deps.saveStoredSession(session), - clearStoredSession: () => deps.clearStoredSession(), - logInfo: (message: string) => deps.logInfo(message), - }); -} - -export function createBuildHandleJellyfinListCommandsMainDepsHandler( - deps: HandleJellyfinListCommandsMainDeps, -) { - return (): HandleJellyfinListCommandsMainDeps => ({ - listJellyfinLibraries: (session, clientInfo) => deps.listJellyfinLibraries(session, clientInfo), - listJellyfinItems: (session, clientInfo, params) => - deps.listJellyfinItems(session, clientInfo, params), - listJellyfinSubtitleTracks: (session, clientInfo, itemId) => - deps.listJellyfinSubtitleTracks(session, clientInfo, itemId), - writeJellyfinResponse: (responsePath, response) => - deps.writeJellyfinResponse(responsePath, response), - logInfo: (message: string) => deps.logInfo(message), - }); -} - -export function createBuildHandleJellyfinPlayCommandMainDepsHandler( - deps: HandleJellyfinPlayCommandMainDeps, -) { - return (): HandleJellyfinPlayCommandMainDeps => ({ - playJellyfinItemInMpv: (params) => deps.playJellyfinItemInMpv(params), - logWarn: (message: string) => deps.logWarn(message), - }); -} - -export function createBuildHandleJellyfinRemoteAnnounceCommandMainDepsHandler( - deps: HandleJellyfinRemoteAnnounceCommandMainDeps, -) { - return (): HandleJellyfinRemoteAnnounceCommandMainDeps => ({ - startJellyfinRemoteSession: (options) => deps.startJellyfinRemoteSession(options), - getRemoteSession: () => deps.getRemoteSession(), - logInfo: (message: string) => deps.logInfo(message), - logWarn: (message: string) => deps.logWarn(message), - }); -} diff --git a/src/main/runtime/jellyfin-command-dispatch-main-deps.ts b/src/main/runtime/jellyfin-command-dispatch-main-deps.ts deleted file mode 100644 index 50495808..00000000 --- a/src/main/runtime/jellyfin-command-dispatch-main-deps.ts +++ /dev/null @@ -1,59 +0,0 @@ -import type { CliArgs } from '../../cli/args'; -import type { JellyfinCliResponse } from '../../shared/jellyfin-cli-response'; - -type JellyfinConfigBase = { - serverUrl?: string; - accessToken?: string; - userId?: string; - username?: string; -}; - -type JellyfinSession = { - serverUrl: string; - accessToken: string; - userId: string; - username: string; -}; - -export type RunJellyfinCommandMainDeps = { - getJellyfinConfig: () => TConfig; - defaultServerUrl: string; - getJellyfinClientInfo: (config: TConfig) => TClientInfo; - handleAuthCommands: (params: { - args: CliArgs; - jellyfinConfig: TConfig; - serverUrl: string; - clientInfo: TClientInfo; - }) => Promise; - handleRemoteAnnounceCommand: (args: CliArgs) => Promise; - handleListCommands: (params: { - args: CliArgs; - session: JellyfinSession; - clientInfo: TClientInfo; - jellyfinConfig: TConfig; - }) => Promise; - handlePlayCommand: (params: { - args: CliArgs; - session: JellyfinSession; - clientInfo: TClientInfo; - jellyfinConfig: TConfig; - }) => Promise; - writeJellyfinResponse: (responsePath: string, response: JellyfinCliResponse) => void; -}; - -export function createBuildRunJellyfinCommandMainDepsHandler< - TClientInfo, - TConfig extends JellyfinConfigBase, ->(deps: RunJellyfinCommandMainDeps) { - return (): RunJellyfinCommandMainDeps => ({ - getJellyfinConfig: () => deps.getJellyfinConfig(), - defaultServerUrl: deps.defaultServerUrl, - getJellyfinClientInfo: (config: TConfig) => deps.getJellyfinClientInfo(config), - handleAuthCommands: (params) => deps.handleAuthCommands(params), - handleRemoteAnnounceCommand: (args: CliArgs) => deps.handleRemoteAnnounceCommand(args), - handleListCommands: (params) => deps.handleListCommands(params), - handlePlayCommand: (params) => deps.handlePlayCommand(params), - writeJellyfinResponse: (responsePath, response) => - deps.writeJellyfinResponse(responsePath, response), - }); -} diff --git a/src/main/runtime/jellyfin-playback-launch-main-deps.ts b/src/main/runtime/jellyfin-playback-launch-main-deps.ts deleted file mode 100644 index 1ded69a6..00000000 --- a/src/main/runtime/jellyfin-playback-launch-main-deps.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { createPlayJellyfinItemInMpvHandler } from './jellyfin-playback-launch'; - -type PlayJellyfinItemInMpvMainDeps = Parameters[0]; - -export function createBuildPlayJellyfinItemInMpvMainDepsHandler( - deps: PlayJellyfinItemInMpvMainDeps, -) { - return (): PlayJellyfinItemInMpvMainDeps => ({ - ensureMpvConnectedForPlayback: () => deps.ensureMpvConnectedForPlayback(), - getMpvClient: () => deps.getMpvClient(), - resolvePlaybackPlan: (params) => deps.resolvePlaybackPlan(params), - applyJellyfinMpvDefaults: (mpvClient) => deps.applyJellyfinMpvDefaults(mpvClient), - showVisibleOverlay: () => deps.showVisibleOverlay(), - sendMpvCommand: (command: Array) => deps.sendMpvCommand(command), - armQuitOnDisconnect: () => deps.armQuitOnDisconnect(), - schedule: (callback: () => void, delayMs: number) => deps.schedule(callback, delayMs), - convertTicksToSeconds: (ticks: number) => deps.convertTicksToSeconds(ticks), - preloadExternalSubtitles: (params) => deps.preloadExternalSubtitles(params), - setActivePlayback: (state) => deps.setActivePlayback(state), - setLastProgressAtMs: (value: number) => deps.setLastProgressAtMs(value), - createPlaybackReporter: (params) => deps.createPlaybackReporter(params), - showMpvOsd: (text: string) => deps.showMpvOsd(text), - recordJellyfinPlaybackMetadata: deps.recordJellyfinPlaybackMetadata - ? (metadata) => deps.recordJellyfinPlaybackMetadata!(metadata) - : undefined, - updateCurrentMediaTitle: deps.updateCurrentMediaTitle - ? (title) => deps.updateCurrentMediaTitle!(title) - : undefined, - }); -} diff --git a/src/main/runtime/jellyfin-remote-connection-main-deps.ts b/src/main/runtime/jellyfin-remote-connection-main-deps.ts deleted file mode 100644 index 80fbd0c3..00000000 --- a/src/main/runtime/jellyfin-remote-connection-main-deps.ts +++ /dev/null @@ -1,50 +0,0 @@ -import type { - EnsureMpvConnectedDeps, - LaunchMpvForJellyfinDeps, - WaitForMpvConnectedDeps, -} from './jellyfin-remote-connection'; - -export function createBuildWaitForMpvConnectedMainDepsHandler(deps: WaitForMpvConnectedDeps) { - return (): WaitForMpvConnectedDeps => ({ - getMpvClient: () => deps.getMpvClient(), - now: () => deps.now(), - sleep: (delayMs: number) => deps.sleep(delayMs), - }); -} - -export function createBuildLaunchMpvIdleForJellyfinPlaybackMainDepsHandler( - deps: LaunchMpvForJellyfinDeps, -) { - return (): LaunchMpvForJellyfinDeps => ({ - getMpvExecutablePath: () => deps.getMpvExecutablePath(), - getSocketPath: () => deps.getSocketPath(), - getLaunchMode: () => deps.getLaunchMode(), - platform: deps.platform, - execPath: deps.execPath, - getRuntimePluginEntrypoint: deps.getRuntimePluginEntrypoint, - getInstalledPluginDetection: deps.getInstalledPluginDetection, - getPluginRuntimeConfig: deps.getPluginRuntimeConfig, - getDefaultMpvLogPath: () => deps.getDefaultMpvLogPath(), - defaultMpvArgs: deps.defaultMpvArgs, - removeSocketPath: (socketPath: string) => deps.removeSocketPath(socketPath), - spawnMpv: (executablePath, args) => deps.spawnMpv(executablePath, args), - logWarn: (message: string, error: unknown) => deps.logWarn(message, error), - logInfo: (message: string) => deps.logInfo(message), - }); -} - -export function createBuildEnsureMpvConnectedForJellyfinPlaybackMainDepsHandler( - deps: EnsureMpvConnectedDeps, -) { - return (): EnsureMpvConnectedDeps => ({ - getMpvClient: () => deps.getMpvClient(), - setMpvClient: (client) => deps.setMpvClient(client), - createMpvClient: () => deps.createMpvClient(), - waitForMpvConnected: (timeoutMs: number) => deps.waitForMpvConnected(timeoutMs), - launchMpvIdleForJellyfinPlayback: () => deps.launchMpvIdleForJellyfinPlayback(), - getAutoLaunchInFlight: () => deps.getAutoLaunchInFlight(), - setAutoLaunchInFlight: (promise) => deps.setAutoLaunchInFlight(promise), - connectTimeoutMs: deps.connectTimeoutMs, - autoLaunchTimeoutMs: deps.autoLaunchTimeoutMs, - }); -} diff --git a/src/main/runtime/jellyfin-remote-session-main-deps.ts b/src/main/runtime/jellyfin-remote-session-main-deps.ts deleted file mode 100644 index 47accb1b..00000000 --- a/src/main/runtime/jellyfin-remote-session-main-deps.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { - createStartJellyfinRemoteSessionHandler, - createStopJellyfinRemoteSessionHandler, -} from './jellyfin-remote-session-lifecycle'; - -type StartJellyfinRemoteSessionMainDeps = Parameters< - typeof createStartJellyfinRemoteSessionHandler ->[0]; -type StopJellyfinRemoteSessionMainDeps = Parameters< - typeof createStopJellyfinRemoteSessionHandler ->[0]; - -export function createBuildStartJellyfinRemoteSessionMainDepsHandler( - deps: StartJellyfinRemoteSessionMainDeps, -) { - return (): StartJellyfinRemoteSessionMainDeps => ({ - getJellyfinConfig: () => deps.getJellyfinConfig(), - getCurrentSession: () => deps.getCurrentSession(), - setCurrentSession: (session) => deps.setCurrentSession(session), - createRemoteSessionService: (options) => deps.createRemoteSessionService(options), - getClientInfo: () => deps.getClientInfo(), - getHostName: () => deps.getHostName(), - defaultDeviceId: deps.defaultDeviceId, - defaultClientName: deps.defaultClientName, - defaultClientVersion: deps.defaultClientVersion, - handlePlay: (payload) => deps.handlePlay(payload), - handlePlaystate: (payload) => deps.handlePlaystate(payload), - handleGeneralCommand: (payload) => deps.handleGeneralCommand(payload), - logInfo: (message: string) => deps.logInfo(message), - logWarn: (message: string, details?: unknown) => deps.logWarn(message, details), - onSessionStateChanged: deps.onSessionStateChanged, - }); -} - -export function createBuildStopJellyfinRemoteSessionMainDepsHandler( - deps: StopJellyfinRemoteSessionMainDeps, -) { - return (): StopJellyfinRemoteSessionMainDeps => ({ - getCurrentSession: () => deps.getCurrentSession(), - setCurrentSession: (session) => deps.setCurrentSession(session), - clearActivePlayback: () => deps.clearActivePlayback(), - onSessionStateChanged: deps.onSessionStateChanged, - }); -} diff --git a/src/main/runtime/jellyfin-subtitle-preload-main-deps.ts b/src/main/runtime/jellyfin-subtitle-preload-main-deps.ts deleted file mode 100644 index b00f08ff..00000000 --- a/src/main/runtime/jellyfin-subtitle-preload-main-deps.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { createPreloadJellyfinExternalSubtitlesHandler } from './jellyfin-subtitle-preload'; - -type PreloadJellyfinExternalSubtitlesMainDeps = Parameters< - typeof createPreloadJellyfinExternalSubtitlesHandler ->[0]; - -export function createBuildPreloadJellyfinExternalSubtitlesMainDepsHandler( - deps: PreloadJellyfinExternalSubtitlesMainDeps, -) { - return (): PreloadJellyfinExternalSubtitlesMainDeps => ({ - listJellyfinSubtitleTracks: (session, clientInfo, itemId) => - deps.listJellyfinSubtitleTracks(session, clientInfo, itemId), - getMpvClient: () => deps.getMpvClient(), - sendMpvCommand: (command) => deps.sendMpvCommand(command), - wait: (ms: number) => deps.wait(ms), - cacheSubtitleTrack: (track) => deps.cacheSubtitleTrack(track), - cleanupCachedSubtitles: (dirs) => deps.cleanupCachedSubtitles(dirs), - initSubtitlePrefetch: deps.initSubtitlePrefetch - ? (sourcePath) => deps.initSubtitlePrefetch!(sourcePath) - : undefined, - logDebug: (message: string, error: unknown) => deps.logDebug(message, error), - }); -} diff --git a/src/main/runtime/media-runtime-main-deps.ts b/src/main/runtime/media-runtime-main-deps.ts deleted file mode 100644 index 1d259085..00000000 --- a/src/main/runtime/media-runtime-main-deps.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { SubtitlePosition } from '../../types'; - -export function createBuildMediaRuntimeMainDepsHandler(deps: { - isRemoteMediaPath: (mediaPath: string) => boolean; - loadSubtitlePosition: () => SubtitlePosition | null; - getCurrentMediaPath: () => string | null; - getPendingSubtitlePosition: () => SubtitlePosition | null; - getSubtitlePositionsDir: () => string; - setCurrentMediaPath: (mediaPath: string | null) => void; - clearPendingSubtitlePosition: () => void; - setSubtitlePosition: (position: SubtitlePosition | null) => void; - broadcastToOverlayWindows: (channel: string, payload: unknown) => void; - getCurrentMediaTitle: () => string | null; - setCurrentMediaTitle: (title: string | null) => void; -}) { - return () => ({ - isRemoteMediaPath: (mediaPath: string) => deps.isRemoteMediaPath(mediaPath), - loadSubtitlePosition: () => deps.loadSubtitlePosition(), - getCurrentMediaPath: () => deps.getCurrentMediaPath(), - getPendingSubtitlePosition: () => deps.getPendingSubtitlePosition(), - getSubtitlePositionsDir: () => deps.getSubtitlePositionsDir(), - setCurrentMediaPath: (nextPath: string | null) => deps.setCurrentMediaPath(nextPath), - clearPendingSubtitlePosition: () => deps.clearPendingSubtitlePosition(), - setSubtitlePosition: (position: SubtitlePosition | null) => deps.setSubtitlePosition(position), - broadcastSubtitlePosition: (position: SubtitlePosition | null) => - deps.broadcastToOverlayWindows('subtitle-position:set', position), - getCurrentMediaTitle: () => deps.getCurrentMediaTitle(), - setCurrentMediaTitle: (title: string | null) => deps.setCurrentMediaTitle(title), - }); -} diff --git a/src/main/runtime/mining-actions-main-deps.ts b/src/main/runtime/mining-actions-main-deps.ts deleted file mode 100644 index 4281eeba..00000000 --- a/src/main/runtime/mining-actions-main-deps.ts +++ /dev/null @@ -1,88 +0,0 @@ -export function createBuildHandleMultiCopyDigitMainDepsHandler(deps: { - getSubtitleTimingTracker: () => TSubtitleTimingTracker; - writeClipboardText: (text: string) => void; - showMpvOsd: (text: string) => void; - handleMultiCopyDigitCore: ( - count: number, - options: { - subtitleTimingTracker: TSubtitleTimingTracker; - writeClipboardText: (text: string) => void; - showMpvOsd: (text: string) => void; - }, - ) => void; -}) { - return () => ({ - getSubtitleTimingTracker: () => deps.getSubtitleTimingTracker(), - writeClipboardText: (text: string) => deps.writeClipboardText(text), - showMpvOsd: (text: string) => deps.showMpvOsd(text), - handleMultiCopyDigitCore: ( - count: number, - options: { - subtitleTimingTracker: TSubtitleTimingTracker; - writeClipboardText: (text: string) => void; - showMpvOsd: (text: string) => void; - }, - ) => deps.handleMultiCopyDigitCore(count, options), - }); -} - -export function createBuildCopyCurrentSubtitleMainDepsHandler(deps: { - getSubtitleTimingTracker: () => TSubtitleTimingTracker; - writeClipboardText: (text: string) => void; - showMpvOsd: (text: string) => void; - copyCurrentSubtitleCore: (options: { - subtitleTimingTracker: TSubtitleTimingTracker; - writeClipboardText: (text: string) => void; - showMpvOsd: (text: string) => void; - }) => void; -}) { - return () => ({ - getSubtitleTimingTracker: () => deps.getSubtitleTimingTracker(), - writeClipboardText: (text: string) => deps.writeClipboardText(text), - showMpvOsd: (text: string) => deps.showMpvOsd(text), - copyCurrentSubtitleCore: (options: { - subtitleTimingTracker: TSubtitleTimingTracker; - writeClipboardText: (text: string) => void; - showMpvOsd: (text: string) => void; - }) => deps.copyCurrentSubtitleCore(options), - }); -} - -export function createBuildHandleMineSentenceDigitMainDepsHandler< - TSubtitleTimingTracker, - TAnkiIntegration, ->(deps: { - getSubtitleTimingTracker: () => TSubtitleTimingTracker; - getAnkiIntegration: () => TAnkiIntegration; - getCurrentSecondarySubText: () => string | undefined; - showMpvOsd: (text: string) => void; - logError: (message: string, err: unknown) => void; - handleMineSentenceDigitCore: ( - count: number, - options: { - subtitleTimingTracker: TSubtitleTimingTracker; - ankiIntegration: TAnkiIntegration; - getCurrentSecondarySubText: () => string | undefined; - showMpvOsd: (text: string) => void; - logError: (message: string, err: unknown) => void; - }, - ) => void; -}) { - return () => ({ - getSubtitleTimingTracker: () => deps.getSubtitleTimingTracker(), - getAnkiIntegration: () => deps.getAnkiIntegration(), - getCurrentSecondarySubText: () => deps.getCurrentSecondarySubText(), - showMpvOsd: (text: string) => deps.showMpvOsd(text), - logError: (message: string, err: unknown) => deps.logError(message, err), - handleMineSentenceDigitCore: ( - count: number, - options: { - subtitleTimingTracker: TSubtitleTimingTracker; - ankiIntegration: TAnkiIntegration; - getCurrentSecondarySubText: () => string | undefined; - showMpvOsd: (text: string) => void; - logError: (message: string, err: unknown) => void; - }, - ) => deps.handleMineSentenceDigitCore(count, options), - }); -} diff --git a/src/main/runtime/mpv-jellyfin-defaults-main-deps.ts b/src/main/runtime/mpv-jellyfin-defaults-main-deps.ts deleted file mode 100644 index d2ca031f..00000000 --- a/src/main/runtime/mpv-jellyfin-defaults-main-deps.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { - createApplyJellyfinMpvDefaultsHandler, - createGetDefaultSocketPathHandler, -} from './mpv-jellyfin-defaults'; - -type ApplyJellyfinMpvDefaultsMainDeps = Parameters[0]; -type GetDefaultSocketPathMainDeps = Parameters[0]; - -export function createBuildApplyJellyfinMpvDefaultsMainDepsHandler( - deps: ApplyJellyfinMpvDefaultsMainDeps, -) { - return (): ApplyJellyfinMpvDefaultsMainDeps => ({ - sendMpvCommandRuntime: (client, command) => deps.sendMpvCommandRuntime(client, command), - jellyfinLangPref: deps.jellyfinLangPref, - }); -} - -export function createBuildGetDefaultSocketPathMainDepsHandler(deps: GetDefaultSocketPathMainDeps) { - return (): GetDefaultSocketPathMainDeps => ({ - platform: deps.platform, - }); -} diff --git a/src/main/runtime/mpv-osd-log-main-deps.ts b/src/main/runtime/mpv-osd-log-main-deps.ts deleted file mode 100644 index b7ac773b..00000000 --- a/src/main/runtime/mpv-osd-log-main-deps.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { createAppendToMpvLogHandler, createShowMpvOsdHandler } from './mpv-osd-log'; - -type AppendToMpvLogMainDeps = Parameters[0]; -type ShowMpvOsdMainDeps = Parameters[0]; - -export function createBuildAppendToMpvLogMainDepsHandler(deps: AppendToMpvLogMainDeps) { - return (): AppendToMpvLogMainDeps => ({ - getLogPath: () => deps.getLogPath(), - dirname: (targetPath: string) => deps.dirname(targetPath), - mkdir: (targetPath: string, options: { recursive: boolean }) => deps.mkdir(targetPath, options), - appendFile: (targetPath: string, data: string, options: { encoding: 'utf8' }) => - deps.appendFile(targetPath, data, options), - now: () => deps.now(), - }); -} - -export function createBuildShowMpvOsdMainDepsHandler(deps: ShowMpvOsdMainDeps) { - return (): ShowMpvOsdMainDeps => ({ - appendToMpvLog: (message: string) => deps.appendToMpvLog(message), - showMpvOsdRuntime: (mpvClient, text, fallbackLog) => - deps.showMpvOsdRuntime(mpvClient, text, fallbackLog), - getMpvClient: () => deps.getMpvClient(), - logInfo: (line: string) => deps.logInfo(line), - }); -} diff --git a/src/main/runtime/mpv-osd-runtime-handlers.ts b/src/main/runtime/mpv-osd-runtime-handlers.ts index 310582de..445e26e6 100644 --- a/src/main/runtime/mpv-osd-runtime-handlers.ts +++ b/src/main/runtime/mpv-osd-runtime-handlers.ts @@ -1,27 +1,17 @@ import { createAppendToMpvLogHandler, createShowMpvOsdHandler } from './mpv-osd-log'; -import { - createBuildAppendToMpvLogMainDepsHandler, - createBuildShowMpvOsdMainDepsHandler, -} from './mpv-osd-log-main-deps'; -type AppendToMpvLogMainDeps = Parameters[0]; -type ShowMpvOsdMainDeps = Parameters[0]; +type AppendToMpvLogMainDeps = Parameters[0]; +type ShowMpvOsdMainDeps = Parameters[0]; export function createMpvOsdRuntimeHandlers(deps: { appendToMpvLogMainDeps: AppendToMpvLogMainDeps; buildShowMpvOsdMainDeps: (appendToMpvLog: (message: string) => void) => ShowMpvOsdMainDeps; }) { - const appendToMpvLogMainDeps = createBuildAppendToMpvLogMainDepsHandler( - deps.appendToMpvLogMainDeps, - )(); - const appendToMpvLogRuntime = createAppendToMpvLogHandler(appendToMpvLogMainDeps); + const appendToMpvLogRuntime = createAppendToMpvLogHandler(deps.appendToMpvLogMainDeps); const appendToMpvLog = (message: string) => appendToMpvLogRuntime.appendToMpvLog(message); const flushMpvLog = async () => appendToMpvLogRuntime.flushMpvLog(); - const showMpvOsdMainDeps = createBuildShowMpvOsdMainDepsHandler( - deps.buildShowMpvOsdMainDeps(appendToMpvLog), - )(); - const showMpvOsdHandler = createShowMpvOsdHandler(showMpvOsdMainDeps); + const showMpvOsdHandler = createShowMpvOsdHandler(deps.buildShowMpvOsdMainDeps(appendToMpvLog)); const showMpvOsd = (text: string) => showMpvOsdHandler(text); return { diff --git a/src/main/runtime/mpv-subtitle-render-metrics-main-deps.ts b/src/main/runtime/mpv-subtitle-render-metrics-main-deps.ts deleted file mode 100644 index fffb68d2..00000000 --- a/src/main/runtime/mpv-subtitle-render-metrics-main-deps.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { createUpdateMpvSubtitleRenderMetricsHandler } from './mpv-subtitle-render-metrics'; - -type UpdateMpvSubtitleRenderMetricsMainDeps = Parameters< - typeof createUpdateMpvSubtitleRenderMetricsHandler ->[0]; - -export function createBuildUpdateMpvSubtitleRenderMetricsMainDepsHandler( - deps: UpdateMpvSubtitleRenderMetricsMainDeps, -) { - return (): UpdateMpvSubtitleRenderMetricsMainDeps => ({ - getCurrentMetrics: () => deps.getCurrentMetrics(), - setCurrentMetrics: (metrics) => deps.setCurrentMetrics(metrics), - applyPatch: (current, patch) => deps.applyPatch(current, patch), - broadcastMetrics: (metrics) => deps.broadcastMetrics(metrics), - }); -} diff --git a/src/main/runtime/numeric-shortcut-runtime-main-deps.ts b/src/main/runtime/numeric-shortcut-runtime-main-deps.ts deleted file mode 100644 index d099cbe7..00000000 --- a/src/main/runtime/numeric-shortcut-runtime-main-deps.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { NumericShortcutRuntimeOptions } from '../../core/services/numeric-shortcut'; - -export function createBuildNumericShortcutRuntimeMainDepsHandler( - deps: NumericShortcutRuntimeOptions, -) { - return (): NumericShortcutRuntimeOptions => ({ - globalShortcut: deps.globalShortcut, - showMpvOsd: (text: string) => deps.showMpvOsd(text), - setTimer: (handler: () => void, timeoutMs: number) => deps.setTimer(handler, timeoutMs), - clearTimer: (timer: ReturnType) => deps.clearTimer(timer), - }); -} diff --git a/src/main/runtime/numeric-shortcut-session-main-deps.ts b/src/main/runtime/numeric-shortcut-session-main-deps.ts deleted file mode 100644 index d2ecd652..00000000 --- a/src/main/runtime/numeric-shortcut-session-main-deps.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { - createCancelNumericShortcutSessionHandler, - createStartNumericShortcutSessionHandler, -} from './numeric-shortcut-session-handlers'; - -type CancelNumericShortcutSessionMainDeps = Parameters< - typeof createCancelNumericShortcutSessionHandler ->[0]; -type StartNumericShortcutSessionMainDeps = Parameters< - typeof createStartNumericShortcutSessionHandler ->[0]; - -export function createBuildCancelNumericShortcutSessionMainDepsHandler( - deps: CancelNumericShortcutSessionMainDeps, -) { - return (): CancelNumericShortcutSessionMainDeps => ({ - session: deps.session, - }); -} - -export function createBuildStartNumericShortcutSessionMainDepsHandler( - deps: StartNumericShortcutSessionMainDeps, -) { - return (): StartNumericShortcutSessionMainDeps => ({ - session: deps.session, - onDigit: (digit: number) => deps.onDigit(digit), - messages: deps.messages, - }); -} diff --git a/src/main/runtime/numeric-shortcut-session-runtime-handlers.ts b/src/main/runtime/numeric-shortcut-session-runtime-handlers.ts index 77a0932b..4cd3ea52 100644 --- a/src/main/runtime/numeric-shortcut-session-runtime-handlers.ts +++ b/src/main/runtime/numeric-shortcut-session-runtime-handlers.ts @@ -2,13 +2,9 @@ import { createCancelNumericShortcutSessionHandler, createStartNumericShortcutSessionHandler, } from './numeric-shortcut-session-handlers'; -import { - createBuildCancelNumericShortcutSessionMainDepsHandler, - createBuildStartNumericShortcutSessionMainDepsHandler, -} from './numeric-shortcut-session-main-deps'; type CancelNumericShortcutSessionMainDeps = Parameters< - typeof createBuildCancelNumericShortcutSessionMainDepsHandler + typeof createCancelNumericShortcutSessionHandler >[0]; export function createNumericShortcutSessionRuntimeHandlers(deps: { @@ -19,14 +15,11 @@ export function createNumericShortcutSessionRuntimeHandlers(deps: { tryBeginMultiCopyOverlaySelection?: (timeoutMs: number) => boolean; tryBeginMineSentenceOverlaySelection?: (timeoutMs: number) => boolean; }) { - const cancelPendingMultiCopyMainDeps = createBuildCancelNumericShortcutSessionMainDepsHandler({ + const cancelPendingMultiCopyHandler = createCancelNumericShortcutSessionHandler({ session: deps.multiCopySession, - })(); - const cancelPendingMultiCopyHandler = createCancelNumericShortcutSessionHandler( - cancelPendingMultiCopyMainDeps, - ); + }); - const startPendingMultiCopyMainDeps = createBuildStartNumericShortcutSessionMainDepsHandler({ + const startPendingMultiCopyHandler = createStartNumericShortcutSessionHandler({ session: deps.multiCopySession, onDigit: deps.onMultiCopyDigit, messages: { @@ -34,32 +27,21 @@ export function createNumericShortcutSessionRuntimeHandlers(deps: { timeout: 'Copy timeout', cancelled: 'Cancelled', }, - })(); - const startPendingMultiCopyHandler = createStartNumericShortcutSessionHandler( - startPendingMultiCopyMainDeps, - ); + }); - const cancelPendingMineSentenceMultipleMainDeps = - createBuildCancelNumericShortcutSessionMainDepsHandler({ - session: deps.mineSentenceSession, - })(); - const cancelPendingMineSentenceMultipleHandler = createCancelNumericShortcutSessionHandler( - cancelPendingMineSentenceMultipleMainDeps, - ); + const cancelPendingMineSentenceMultipleHandler = createCancelNumericShortcutSessionHandler({ + session: deps.mineSentenceSession, + }); - const startPendingMineSentenceMultipleMainDeps = - createBuildStartNumericShortcutSessionMainDepsHandler({ - session: deps.mineSentenceSession, - onDigit: deps.onMineSentenceDigit, - messages: { - prompt: 'Mine how many lines? Press 1-9 (Esc to cancel)', - timeout: 'Mine sentence timeout', - cancelled: 'Cancelled', - }, - })(); - const startPendingMineSentenceMultipleHandler = createStartNumericShortcutSessionHandler( - startPendingMineSentenceMultipleMainDeps, - ); + const startPendingMineSentenceMultipleHandler = createStartNumericShortcutSessionHandler({ + session: deps.mineSentenceSession, + onDigit: deps.onMineSentenceDigit, + messages: { + prompt: 'Mine how many lines? Press 1-9 (Esc to cancel)', + timeout: 'Mine sentence timeout', + cancelled: 'Cancelled', + }, + }); return { cancelPendingMultiCopy: () => cancelPendingMultiCopyHandler(), diff --git a/src/main/runtime/overlay-bootstrap-main-deps.ts b/src/main/runtime/overlay-bootstrap-main-deps.ts deleted file mode 100644 index ec74f9f3..00000000 --- a/src/main/runtime/overlay-bootstrap-main-deps.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { OverlayWindowResolver } from '../overlay-runtime'; - -type OverlayContentMeasurementStoreMainDeps = { - now: () => number; - warn: (message: string) => void; -}; - -export function createBuildOverlayContentMeasurementStoreMainDepsHandler( - deps: OverlayContentMeasurementStoreMainDeps, -) { - return (): OverlayContentMeasurementStoreMainDeps => ({ - now: () => deps.now(), - warn: (message: string) => deps.warn(message), - }); -} - -export function createBuildOverlayModalRuntimeMainDepsHandler(deps: OverlayWindowResolver) { - return (): OverlayWindowResolver => ({ - getMainWindow: () => deps.getMainWindow(), - getModalWindow: () => deps.getModalWindow(), - createModalWindow: () => deps.createModalWindow(), - getModalGeometry: () => deps.getModalGeometry(), - setModalWindowBounds: (geometry) => deps.setModalWindowBounds(geometry), - }); -} diff --git a/src/main/runtime/overlay-main-actions-main-deps.ts b/src/main/runtime/overlay-main-actions-main-deps.ts deleted file mode 100644 index 94edc6b0..00000000 --- a/src/main/runtime/overlay-main-actions-main-deps.ts +++ /dev/null @@ -1,46 +0,0 @@ -import type { - createAppendClipboardVideoToQueueHandler, - createHandleOverlayModalClosedHandler, - createSetOverlayVisibleHandler, - createToggleOverlayHandler, -} from './overlay-main-actions'; - -type SetOverlayVisibleMainDeps = Parameters[0]; -type ToggleOverlayMainDeps = Parameters[0]; -type HandleOverlayModalClosedMainDeps = Parameters[0]; -type AppendClipboardVideoToQueueMainDeps = Parameters< - typeof createAppendClipboardVideoToQueueHandler ->[0]; - -export function createBuildSetOverlayVisibleMainDepsHandler(deps: SetOverlayVisibleMainDeps) { - return (): SetOverlayVisibleMainDeps => ({ - setVisibleOverlayVisible: (visible: boolean) => deps.setVisibleOverlayVisible(visible), - }); -} - -export function createBuildToggleOverlayMainDepsHandler(deps: ToggleOverlayMainDeps) { - return (): ToggleOverlayMainDeps => ({ - toggleVisibleOverlay: () => deps.toggleVisibleOverlay(), - }); -} - -export function createBuildHandleOverlayModalClosedMainDepsHandler( - deps: HandleOverlayModalClosedMainDeps, -) { - return (): HandleOverlayModalClosedMainDeps => ({ - handleOverlayModalClosedRuntime: (modal) => deps.handleOverlayModalClosedRuntime(modal), - }); -} - -export function createBuildAppendClipboardVideoToQueueMainDepsHandler( - deps: AppendClipboardVideoToQueueMainDeps, -) { - return (): AppendClipboardVideoToQueueMainDeps => ({ - appendClipboardVideoToQueueRuntime: (options) => - deps.appendClipboardVideoToQueueRuntime(options), - getMpvClient: () => deps.getMpvClient(), - readClipboardText: () => deps.readClipboardText(), - showMpvOsd: (text: string) => deps.showMpvOsd(text), - sendMpvCommand: (command: (string | number)[]) => deps.sendMpvCommand(command), - }); -} diff --git a/src/main/runtime/overlay-runtime-main-actions-main-deps.ts b/src/main/runtime/overlay-runtime-main-actions-main-deps.ts deleted file mode 100644 index 90766852..00000000 --- a/src/main/runtime/overlay-runtime-main-actions-main-deps.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { - createBroadcastRuntimeOptionsChangedHandler, - createGetRuntimeOptionsStateHandler, - createOpenRuntimeOptionsPaletteHandler, - createRestorePreviousSecondarySubVisibilityHandler, - createSendToActiveOverlayWindowHandler, - createSetOverlayDebugVisualizationEnabledHandler, -} from './overlay-runtime-main-actions'; - -type GetRuntimeOptionsStateMainDeps = Parameters[0]; -type RestorePreviousSecondarySubVisibilityMainDeps = Parameters< - typeof createRestorePreviousSecondarySubVisibilityHandler ->[0]; -type BroadcastRuntimeOptionsChangedMainDeps = Parameters< - typeof createBroadcastRuntimeOptionsChangedHandler ->[0]; -type SendToActiveOverlayWindowMainDeps = Parameters< - typeof createSendToActiveOverlayWindowHandler ->[0]; -type SetOverlayDebugVisualizationEnabledMainDeps = Parameters< - typeof createSetOverlayDebugVisualizationEnabledHandler ->[0]; -type OpenRuntimeOptionsPaletteMainDeps = Parameters< - typeof createOpenRuntimeOptionsPaletteHandler ->[0]; - -export function createBuildGetRuntimeOptionsStateMainDepsHandler( - deps: GetRuntimeOptionsStateMainDeps, -) { - return (): GetRuntimeOptionsStateMainDeps => ({ - getRuntimeOptionsManager: () => deps.getRuntimeOptionsManager(), - }); -} - -export function createBuildRestorePreviousSecondarySubVisibilityMainDepsHandler( - deps: RestorePreviousSecondarySubVisibilityMainDeps, -) { - return (): RestorePreviousSecondarySubVisibilityMainDeps => ({ - getMpvClient: () => deps.getMpvClient(), - }); -} - -export function createBuildBroadcastRuntimeOptionsChangedMainDepsHandler( - deps: BroadcastRuntimeOptionsChangedMainDeps, -) { - return (): BroadcastRuntimeOptionsChangedMainDeps => ({ - broadcastRuntimeOptionsChangedRuntime: (getRuntimeOptionsState, broadcastToOverlayWindows) => - deps.broadcastRuntimeOptionsChangedRuntime(getRuntimeOptionsState, broadcastToOverlayWindows), - getRuntimeOptionsState: () => deps.getRuntimeOptionsState(), - broadcastToOverlayWindows: (channel: string, ...args: unknown[]) => - deps.broadcastToOverlayWindows(channel, ...args), - }); -} - -export function createBuildSendToActiveOverlayWindowMainDepsHandler( - deps: SendToActiveOverlayWindowMainDeps, -) { - return (): SendToActiveOverlayWindowMainDeps => ({ - sendToActiveOverlayWindowRuntime: (channel, payload, runtimeOptions) => - deps.sendToActiveOverlayWindowRuntime(channel, payload, runtimeOptions), - }); -} - -export function createBuildSetOverlayDebugVisualizationEnabledMainDepsHandler( - deps: SetOverlayDebugVisualizationEnabledMainDeps, -) { - return (): SetOverlayDebugVisualizationEnabledMainDeps => ({ - setOverlayDebugVisualizationEnabledRuntime: (currentEnabled, nextEnabled, setCurrentEnabled) => - deps.setOverlayDebugVisualizationEnabledRuntime( - currentEnabled, - nextEnabled, - setCurrentEnabled, - ), - getCurrentEnabled: () => deps.getCurrentEnabled(), - setCurrentEnabled: (enabled: boolean) => deps.setCurrentEnabled(enabled), - }); -} - -export function createBuildOpenRuntimeOptionsPaletteMainDepsHandler( - deps: OpenRuntimeOptionsPaletteMainDeps, -) { - return (): OpenRuntimeOptionsPaletteMainDeps => ({ - openRuntimeOptionsPaletteRuntime: () => deps.openRuntimeOptionsPaletteRuntime(), - }); -} diff --git a/src/main/runtime/overlay-shortcuts-lifecycle-main-deps.ts b/src/main/runtime/overlay-shortcuts-lifecycle-main-deps.ts deleted file mode 100644 index 41a207b4..00000000 --- a/src/main/runtime/overlay-shortcuts-lifecycle-main-deps.ts +++ /dev/null @@ -1,43 +0,0 @@ -import type { - createRefreshOverlayShortcutsHandler, - createRegisterOverlayShortcutsHandler, - createSyncOverlayShortcutsHandler, - createUnregisterOverlayShortcutsHandler, -} from './overlay-shortcuts-lifecycle'; - -type RegisterOverlayShortcutsMainDeps = Parameters[0]; -type UnregisterOverlayShortcutsMainDeps = Parameters< - typeof createUnregisterOverlayShortcutsHandler ->[0]; -type SyncOverlayShortcutsMainDeps = Parameters[0]; -type RefreshOverlayShortcutsMainDeps = Parameters[0]; - -export function createBuildRegisterOverlayShortcutsMainDepsHandler( - deps: RegisterOverlayShortcutsMainDeps, -) { - return (): RegisterOverlayShortcutsMainDeps => ({ - overlayShortcutsRuntime: deps.overlayShortcutsRuntime, - }); -} - -export function createBuildUnregisterOverlayShortcutsMainDepsHandler( - deps: UnregisterOverlayShortcutsMainDeps, -) { - return (): UnregisterOverlayShortcutsMainDeps => ({ - overlayShortcutsRuntime: deps.overlayShortcutsRuntime, - }); -} - -export function createBuildSyncOverlayShortcutsMainDepsHandler(deps: SyncOverlayShortcutsMainDeps) { - return (): SyncOverlayShortcutsMainDeps => ({ - overlayShortcutsRuntime: deps.overlayShortcutsRuntime, - }); -} - -export function createBuildRefreshOverlayShortcutsMainDepsHandler( - deps: RefreshOverlayShortcutsMainDeps, -) { - return (): RefreshOverlayShortcutsMainDeps => ({ - overlayShortcutsRuntime: deps.overlayShortcutsRuntime, - }); -} diff --git a/src/main/runtime/overlay-shortcuts-runtime-handlers.ts b/src/main/runtime/overlay-shortcuts-runtime-handlers.ts index 365a462c..1f2c8e5b 100644 --- a/src/main/runtime/overlay-shortcuts-runtime-handlers.ts +++ b/src/main/runtime/overlay-shortcuts-runtime-handlers.ts @@ -4,46 +4,26 @@ import { createSyncOverlayShortcutsHandler, createUnregisterOverlayShortcutsHandler, } from './overlay-shortcuts-lifecycle'; -import { - createBuildRefreshOverlayShortcutsMainDepsHandler, - createBuildRegisterOverlayShortcutsMainDepsHandler, - createBuildSyncOverlayShortcutsMainDepsHandler, - createBuildUnregisterOverlayShortcutsMainDepsHandler, -} from './overlay-shortcuts-lifecycle-main-deps'; -type RegisterOverlayShortcutsMainDeps = Parameters< - typeof createBuildRegisterOverlayShortcutsMainDepsHandler ->[0]; +type RegisterOverlayShortcutsMainDeps = Parameters[0]; export function createOverlayShortcutsRuntimeHandlers(deps: { overlayShortcutsRuntimeMainDeps: RegisterOverlayShortcutsMainDeps; }) { - const registerOverlayShortcutsMainDeps = createBuildRegisterOverlayShortcutsMainDepsHandler( - deps.overlayShortcutsRuntimeMainDeps, - )(); const registerOverlayShortcutsHandler = createRegisterOverlayShortcutsHandler( - registerOverlayShortcutsMainDeps, + deps.overlayShortcutsRuntimeMainDeps, ); - const unregisterOverlayShortcutsMainDeps = createBuildUnregisterOverlayShortcutsMainDepsHandler( - deps.overlayShortcutsRuntimeMainDeps, - )(); const unregisterOverlayShortcutsHandler = createUnregisterOverlayShortcutsHandler( - unregisterOverlayShortcutsMainDeps, + deps.overlayShortcutsRuntimeMainDeps, ); - const syncOverlayShortcutsMainDeps = createBuildSyncOverlayShortcutsMainDepsHandler( - deps.overlayShortcutsRuntimeMainDeps, - )(); const syncOverlayShortcutsHandler = createSyncOverlayShortcutsHandler( - syncOverlayShortcutsMainDeps, + deps.overlayShortcutsRuntimeMainDeps, ); - const refreshOverlayShortcutsMainDeps = createBuildRefreshOverlayShortcutsMainDepsHandler( - deps.overlayShortcutsRuntimeMainDeps, - )(); const refreshOverlayShortcutsHandler = createRefreshOverlayShortcutsHandler( - refreshOverlayShortcutsMainDeps, + deps.overlayShortcutsRuntimeMainDeps, ); return { diff --git a/src/main/runtime/overlay-visibility-runtime.ts b/src/main/runtime/overlay-visibility-runtime.ts index e17ef7c5..bf6babb6 100644 --- a/src/main/runtime/overlay-visibility-runtime.ts +++ b/src/main/runtime/overlay-visibility-runtime.ts @@ -7,10 +7,6 @@ import { createBuildToggleVisibleOverlayMainDepsHandler, } from './overlay-visibility-actions-main-deps'; import { createSetOverlayVisibleHandler, createToggleOverlayHandler } from './overlay-main-actions'; -import { - createBuildSetOverlayVisibleMainDepsHandler, - createBuildToggleOverlayMainDepsHandler, -} from './overlay-main-actions-main-deps'; type SetVisibleOverlayVisibleMainDeps = Parameters< typeof createBuildSetVisibleOverlayVisibleMainDepsHandler @@ -36,15 +32,13 @@ export function createOverlayVisibilityRuntime(deps: OverlayVisibilityRuntimeDep })(); const toggleVisibleOverlay = createToggleVisibleOverlayHandler(toggleVisibleOverlayMainDeps); - const setOverlayVisibleMainDeps = createBuildSetOverlayVisibleMainDepsHandler({ + const setOverlayVisible = createSetOverlayVisibleHandler({ setVisibleOverlayVisible: (visible) => setVisibleOverlayVisible(visible), - })(); - const setOverlayVisible = createSetOverlayVisibleHandler(setOverlayVisibleMainDeps); + }); - const toggleOverlayMainDeps = createBuildToggleOverlayMainDepsHandler({ + const toggleOverlay = createToggleOverlayHandler({ toggleVisibleOverlay: () => toggleVisibleOverlay(), - })(); - const toggleOverlay = createToggleOverlayHandler(toggleOverlayMainDeps); + }); return { setVisibleOverlayVisible, diff --git a/src/main/runtime/runtime-bootstrap-main-deps.ts b/src/main/runtime/runtime-bootstrap-main-deps.ts deleted file mode 100644 index 6fe76009..00000000 --- a/src/main/runtime/runtime-bootstrap-main-deps.ts +++ /dev/null @@ -1,53 +0,0 @@ -import type { AnilistStateRuntimeDeps } from './anilist-state'; -import type { ConfigDerivedRuntimeDeps } from './config-derived'; -import type { ImmersionMediaRuntimeDeps } from './immersion-media'; -import type { MainSubsyncRuntimeDeps } from './subsync-runtime'; - -export function createBuildImmersionMediaRuntimeMainDepsHandler(deps: ImmersionMediaRuntimeDeps) { - return (): ImmersionMediaRuntimeDeps => ({ - getResolvedConfig: () => deps.getResolvedConfig(), - defaultImmersionDbPath: deps.defaultImmersionDbPath, - getTracker: () => deps.getTracker(), - getMpvClient: () => deps.getMpvClient(), - getCurrentMediaPath: () => deps.getCurrentMediaPath(), - getCurrentMediaTitle: () => deps.getCurrentMediaTitle(), - sleep: deps.sleep, - seedWaitMs: deps.seedWaitMs, - seedAttempts: deps.seedAttempts, - logDebug: (message: string) => deps.logDebug(message), - logInfo: (message: string) => deps.logInfo(message), - }); -} - -export function createBuildAnilistStateRuntimeMainDepsHandler(deps: AnilistStateRuntimeDeps) { - return (): AnilistStateRuntimeDeps => ({ - getClientSecretState: () => deps.getClientSecretState(), - setClientSecretState: (next) => deps.setClientSecretState(next), - getRetryQueueState: () => deps.getRetryQueueState(), - setRetryQueueState: (next) => deps.setRetryQueueState(next), - getUpdateQueueSnapshot: () => deps.getUpdateQueueSnapshot(), - clearStoredToken: () => deps.clearStoredToken(), - clearCachedAccessToken: () => deps.clearCachedAccessToken(), - }); -} - -export function createBuildConfigDerivedRuntimeMainDepsHandler(deps: ConfigDerivedRuntimeDeps) { - return (): ConfigDerivedRuntimeDeps => ({ - getResolvedConfig: () => deps.getResolvedConfig(), - getRuntimeOptionsManager: () => deps.getRuntimeOptionsManager(), - defaultJimakuLanguagePreference: deps.defaultJimakuLanguagePreference, - defaultJimakuMaxEntryResults: deps.defaultJimakuMaxEntryResults, - defaultJimakuApiBaseUrl: deps.defaultJimakuApiBaseUrl, - }); -} - -export function createBuildMainSubsyncRuntimeMainDepsHandler(deps: MainSubsyncRuntimeDeps) { - return (): MainSubsyncRuntimeDeps => ({ - getMpvClient: () => deps.getMpvClient(), - getResolvedConfig: () => deps.getResolvedConfig(), - getSubsyncInProgress: () => deps.getSubsyncInProgress(), - setSubsyncInProgress: (inProgress: boolean) => deps.setSubsyncInProgress(inProgress), - showMpvOsd: (text: string) => deps.showMpvOsd(text), - openManualPicker: (payload) => deps.openManualPicker(payload), - }); -} diff --git a/src/main/runtime/secondary-sub-mode-main-deps.ts b/src/main/runtime/secondary-sub-mode-main-deps.ts deleted file mode 100644 index d9960000..00000000 --- a/src/main/runtime/secondary-sub-mode-main-deps.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { SecondarySubMode } from '../../types'; - -export function createBuildCycleSecondarySubModeMainDepsHandler(deps: { - getSecondarySubMode: () => SecondarySubMode; - setSecondarySubMode: (mode: SecondarySubMode) => void; - getLastSecondarySubToggleAtMs: () => number; - setLastSecondarySubToggleAtMs: (timestampMs: number) => void; - broadcastToOverlayWindows: (channel: string, mode: SecondarySubMode) => void; - showMpvOsd: (text: string) => void; -}) { - return () => ({ - getSecondarySubMode: () => deps.getSecondarySubMode(), - setSecondarySubMode: (mode: SecondarySubMode) => deps.setSecondarySubMode(mode), - getLastSecondarySubToggleAtMs: () => deps.getLastSecondarySubToggleAtMs(), - setLastSecondarySubToggleAtMs: (timestampMs: number) => - deps.setLastSecondarySubToggleAtMs(timestampMs), - broadcastSecondarySubMode: (mode: SecondarySubMode) => - deps.broadcastToOverlayWindows('secondary-subtitle:mode', mode), - showMpvOsd: (text: string) => deps.showMpvOsd(text), - }); -} diff --git a/src/main/runtime/secondary-sub-mode-runtime-handler.ts b/src/main/runtime/secondary-sub-mode-runtime-handler.ts index 688d11cf..9ec29ecf 100644 --- a/src/main/runtime/secondary-sub-mode-runtime-handler.ts +++ b/src/main/runtime/secondary-sub-mode-runtime-handler.ts @@ -1,18 +1,8 @@ -import { createBuildCycleSecondarySubModeMainDepsHandler } from './secondary-sub-mode-main-deps'; - -type CycleSecondarySubModeMainDeps = Parameters< - typeof createBuildCycleSecondarySubModeMainDepsHandler ->[0]; -type CycleSecondarySubModeDeps = ReturnType< - ReturnType ->; +import type { CycleSecondarySubModeDeps } from '../../core/services/subtitle-position'; export function createCycleSecondarySubModeRuntimeHandler(deps: { - cycleSecondarySubModeMainDeps: CycleSecondarySubModeMainDeps; + cycleSecondarySubModeMainDeps: CycleSecondarySubModeDeps; cycleSecondarySubMode: (deps: CycleSecondarySubModeDeps) => void; }) { - const buildCycleSecondarySubModeMainDepsHandler = createBuildCycleSecondarySubModeMainDepsHandler( - deps.cycleSecondarySubModeMainDeps, - ); - return () => deps.cycleSecondarySubMode(buildCycleSecondarySubModeMainDepsHandler()); + return () => deps.cycleSecondarySubMode(deps.cycleSecondarySubModeMainDeps); } diff --git a/src/main/runtime/startup-config-main-deps.ts b/src/main/runtime/startup-config-main-deps.ts deleted file mode 100644 index c4ac0837..00000000 --- a/src/main/runtime/startup-config-main-deps.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { createCriticalConfigErrorHandler, createReloadConfigHandler } from './startup-config'; - -type ReloadConfigMainDeps = Parameters[0]; -type CriticalConfigErrorMainDeps = Parameters[0]; - -export function createBuildReloadConfigMainDepsHandler(deps: ReloadConfigMainDeps) { - return (): ReloadConfigMainDeps => ({ - reloadConfigStrict: () => deps.reloadConfigStrict(), - logInfo: (message: string) => deps.logInfo(message), - logDebug: (message: string) => deps.logDebug(message), - logWarning: (message: string) => deps.logWarning(message), - showDesktopNotification: (title: string, options: { body: string }) => - deps.showDesktopNotification(title, options), - startConfigHotReload: () => deps.startConfigHotReload(), - shouldRefreshAnilistClientSecretState: deps.shouldRefreshAnilistClientSecretState - ? () => deps.shouldRefreshAnilistClientSecretState?.() !== false - : undefined, - refreshAnilistClientSecretState: (options: { force: boolean }) => - deps.refreshAnilistClientSecretState(options), - failHandlers: { - logError: (details: string) => deps.failHandlers.logError(details), - showErrorBox: (title: string, details: string) => - deps.failHandlers.showErrorBox(title, details), - setExitCode: deps.failHandlers.setExitCode - ? (code: number) => deps.failHandlers.setExitCode?.(code) - : undefined, - quit: () => deps.failHandlers.quit(), - }, - }); -} - -export function createBuildCriticalConfigErrorMainDepsHandler(deps: CriticalConfigErrorMainDeps) { - return (): CriticalConfigErrorMainDeps => ({ - getConfigPath: () => deps.getConfigPath(), - failHandlers: { - logError: (details: string) => deps.failHandlers.logError(details), - showErrorBox: (title: string, details: string) => - deps.failHandlers.showErrorBox(title, details), - setExitCode: deps.failHandlers.setExitCode - ? (code: number) => deps.failHandlers.setExitCode?.(code) - : undefined, - quit: () => deps.failHandlers.quit(), - }, - }); -} diff --git a/src/main/runtime/startup-lifecycle-main-deps.ts b/src/main/runtime/startup-lifecycle-main-deps.ts deleted file mode 100644 index f02f8f69..00000000 --- a/src/main/runtime/startup-lifecycle-main-deps.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { AppLifecycleRuntimeRunnerParams } from '../startup-lifecycle'; - -export function createBuildAppLifecycleRuntimeRunnerMainDepsHandler( - deps: AppLifecycleRuntimeRunnerParams, -) { - return (): AppLifecycleRuntimeRunnerParams => ({ - app: deps.app, - platform: deps.platform, - shouldStartApp: deps.shouldStartApp, - parseArgs: deps.parseArgs, - handleCliCommand: deps.handleCliCommand, - printHelp: deps.printHelp, - logNoRunningInstance: deps.logNoRunningInstance, - startControlServer: deps.startControlServer, - onReady: deps.onReady, - onWillQuitCleanup: deps.onWillQuitCleanup, - shouldRestoreWindowsOnActivate: deps.shouldRestoreWindowsOnActivate, - restoreWindowsOnActivate: deps.restoreWindowsOnActivate, - shouldQuitOnWindowAllClosed: deps.shouldQuitOnWindowAllClosed, - }); -} diff --git a/src/main/runtime/startup-runtime-handlers.ts b/src/main/runtime/startup-runtime-handlers.ts index 69fcc8ba..4c544160 100644 --- a/src/main/runtime/startup-runtime-handlers.ts +++ b/src/main/runtime/startup-runtime-handlers.ts @@ -1,10 +1,7 @@ import { createBuildStartupBootstrapRuntimeFactoryDepsHandler } from './startup-bootstrap-deps-builder'; -import { createBuildAppLifecycleRuntimeRunnerMainDepsHandler } from './startup-lifecycle-main-deps'; +import type { AppLifecycleRuntimeRunnerParams } from '../startup-lifecycle'; import { createBuildStartupBootstrapMainDepsHandler } from './startup-bootstrap-main-deps'; -type AppLifecycleRuntimeRunnerMainDeps = Parameters< - typeof createBuildAppLifecycleRuntimeRunnerMainDepsHandler ->[0]; type StartupBootstrapMainDeps = Parameters[0]; type StartupBootstrapRuntimeFactoryDeps = Parameters< typeof createBuildStartupBootstrapRuntimeFactoryDepsHandler @@ -15,9 +12,9 @@ export function createStartupRuntimeHandlers< TStartupState, TStartupBootstrapRuntimeDeps = unknown, >(deps: { - appLifecycleRuntimeRunnerMainDeps: AppLifecycleRuntimeRunnerMainDeps; + appLifecycleRuntimeRunnerMainDeps: AppLifecycleRuntimeRunnerParams; createAppLifecycleRuntimeRunner: ( - params: AppLifecycleRuntimeRunnerMainDeps, + params: AppLifecycleRuntimeRunnerParams, ) => (args: TCliArgs) => void; buildStartupBootstrapMainDeps: ( startAppLifecycle: (args: TCliArgs) => void, @@ -28,11 +25,8 @@ export function createStartupRuntimeHandlers< runStartupBootstrapRuntime: (deps: TStartupBootstrapRuntimeDeps) => TStartupState; applyStartupState: (startupState: TStartupState) => void; }) { - const appLifecycleRuntimeRunnerMainDeps = createBuildAppLifecycleRuntimeRunnerMainDepsHandler( - deps.appLifecycleRuntimeRunnerMainDeps, - )(); const appLifecycleRuntimeRunner = deps.createAppLifecycleRuntimeRunner( - appLifecycleRuntimeRunnerMainDeps, + deps.appLifecycleRuntimeRunnerMainDeps, ); const startupBootstrapMainDeps = createBuildStartupBootstrapMainDepsHandler( diff --git a/src/main/runtime/startup-warmups-main-deps.ts b/src/main/runtime/startup-warmups-main-deps.ts deleted file mode 100644 index 7766f177..00000000 --- a/src/main/runtime/startup-warmups-main-deps.ts +++ /dev/null @@ -1,52 +0,0 @@ -import type { - createLaunchBackgroundWarmupTaskHandler, - createStartBackgroundWarmupsHandler, -} from './startup-warmups'; - -type LaunchBackgroundWarmupTaskMainDeps = Parameters< - typeof createLaunchBackgroundWarmupTaskHandler ->[0]; -type StartBackgroundWarmupsMainDeps = Parameters[0]; - -export function createBuildLaunchBackgroundWarmupTaskMainDepsHandler( - deps: LaunchBackgroundWarmupTaskMainDeps, -) { - return (): LaunchBackgroundWarmupTaskMainDeps => ({ - now: () => deps.now(), - logDebug: (message: string) => deps.logDebug(message), - logWarn: (message: string) => deps.logWarn(message), - }); -} - -export function createBuildStartBackgroundWarmupsMainDepsHandler( - deps: StartBackgroundWarmupsMainDeps, -) { - return (): StartBackgroundWarmupsMainDeps => ({ - getStarted: () => deps.getStarted(), - setStarted: (started: boolean) => deps.setStarted(started), - isTexthookerOnlyMode: () => deps.isTexthookerOnlyMode(), - launchTask: (label: string, task: () => Promise) => deps.launchTask(label, task), - createMecabTokenizerAndCheck: () => deps.createMecabTokenizerAndCheck(), - ensureYomitanExtensionLoaded: () => deps.ensureYomitanExtensionLoaded(), - prewarmSubtitleDictionaries: () => deps.prewarmSubtitleDictionaries(), - shouldWarmupMecab: () => deps.shouldWarmupMecab(), - shouldWarmupYomitanExtension: () => deps.shouldWarmupYomitanExtension(), - shouldWarmupSubtitleDictionaries: () => deps.shouldWarmupSubtitleDictionaries(), - shouldWarmupJellyfinRemoteSession: () => deps.shouldWarmupJellyfinRemoteSession(), - shouldAutoConnectJellyfinRemote: () => deps.shouldAutoConnectJellyfinRemote(), - startJellyfinRemoteSession: () => deps.startJellyfinRemoteSession(), - ...(deps.onYomitanExtensionWarmupScheduled - ? { - onYomitanExtensionWarmupScheduled: (promise: Promise) => - deps.onYomitanExtensionWarmupScheduled!(promise), - } - : {}), - ...(deps.onTokenizationWarmupScheduled - ? { - onTokenizationWarmupScheduled: (promise: Promise) => - deps.onTokenizationWarmupScheduled!(promise), - } - : {}), - logDebug: deps.logDebug, - }); -} diff --git a/src/main/runtime/subtitle-position-main-deps.ts b/src/main/runtime/subtitle-position-main-deps.ts deleted file mode 100644 index 9e01ba82..00000000 --- a/src/main/runtime/subtitle-position-main-deps.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { - createLoadSubtitlePositionHandler, - createSaveSubtitlePositionHandler, -} from './subtitle-position'; - -type LoadSubtitlePositionMainDeps = Parameters[0]; -type SaveSubtitlePositionMainDeps = Parameters[0]; - -export function createBuildLoadSubtitlePositionMainDepsHandler(deps: LoadSubtitlePositionMainDeps) { - return (): LoadSubtitlePositionMainDeps => ({ - loadSubtitlePositionCore: () => deps.loadSubtitlePositionCore(), - setSubtitlePosition: (position) => deps.setSubtitlePosition(position), - }); -} - -export function createBuildSaveSubtitlePositionMainDepsHandler(deps: SaveSubtitlePositionMainDeps) { - return (): SaveSubtitlePositionMainDeps => ({ - saveSubtitlePositionCore: (position) => deps.saveSubtitlePositionCore(position), - setSubtitlePosition: (position) => deps.setSubtitlePosition(position), - }); -} diff --git a/src/main/runtime/subtitle-processing-main-deps.ts b/src/main/runtime/subtitle-processing-main-deps.ts deleted file mode 100644 index 3a78fa4f..00000000 --- a/src/main/runtime/subtitle-processing-main-deps.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { SubtitleProcessingControllerDeps } from '../../core/services/subtitle-processing-controller'; - -export function createBuildSubtitleProcessingControllerMainDepsHandler( - deps: SubtitleProcessingControllerDeps, -) { - return (): SubtitleProcessingControllerDeps => ({ - tokenizeSubtitle: (text: string) => deps.tokenizeSubtitle(text), - emitSubtitle: (payload) => deps.emitSubtitle(payload), - onProcessingSettled: () => deps.onProcessingSettled?.(), - logDebug: deps.logDebug, - now: deps.now, - }); -} diff --git a/src/main/runtime/subtitle-processing-runtime.test.ts b/src/main/runtime/subtitle-processing-runtime.test.ts new file mode 100644 index 00000000..b066b034 --- /dev/null +++ b/src/main/runtime/subtitle-processing-runtime.test.ts @@ -0,0 +1,62 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { createPrefetchAwareSubtitleProcessingController } from './subtitle-processing-runtime'; +import type { SubtitleData } from '../../types'; + +function flushMicrotasks(): Promise { + return new Promise((resolve) => setTimeout(resolve, 0)); +} + +function createHarness() { + const events: string[] = []; + let resolveTokenization: ((value: SubtitleData | null) => void) | undefined; + const controller = createPrefetchAwareSubtitleProcessingController({ + tokenizeSubtitle: () => + new Promise((resolve) => { + resolveTokenization = resolve; + }), + emitSubtitlePayload: (payload, options) => + events.push( + `emit:${payload.text}:tokens=${payload.tokens ? 'yes' : 'no'}:resume=${options.resumePrefetch}`, + ), + resumePrefetch: () => events.push('resume'), + logDebug: () => {}, + now: () => 0, + }); + return { + events, + controller, + resolveTokenization: (value: SubtitleData | null) => resolveTokenization?.(value), + }; +} + +test('subtitle processing resumes prefetch after the tokenized emit settles', async () => { + const { events, controller, resolveTokenization } = createHarness(); + + controller.onSubtitleChange('一行目'); + await flushMicrotasks(); + // The provisional plain payload is out, but tokenization is still pending. + assert.deepEqual(events, ['emit:一行目:tokens=no:resume=false']); + + resolveTokenization({ text: '一行目', tokens: [] }); + await flushMicrotasks(); + await flushMicrotasks(); + assert.deepEqual(events, [ + 'emit:一行目:tokens=no:resume=false', + 'emit:一行目:tokens=yes:resume=false', + 'resume', + ]); +}); + +test('subtitle processing resumes prefetch when tokenization fails without another emit', async () => { + const { events, controller, resolveTokenization } = createHarness(); + + controller.onSubtitleChange('一行目'); + await flushMicrotasks(); + assert.deepEqual(events, ['emit:一行目:tokens=no:resume=false']); + + resolveTokenization(null); + await flushMicrotasks(); + await flushMicrotasks(); + assert.deepEqual(events, ['emit:一行目:tokens=no:resume=false', 'resume']); +}); diff --git a/src/main/runtime/subtitle-processing-runtime.ts b/src/main/runtime/subtitle-processing-runtime.ts new file mode 100644 index 00000000..545b667c --- /dev/null +++ b/src/main/runtime/subtitle-processing-runtime.ts @@ -0,0 +1,33 @@ +import { + createSubtitleProcessingController, + type SubtitleProcessingControllerDeps, +} from '../../core/services/subtitle-processing-controller'; +import type { SubtitleData } from '../../types'; + +/** + * Creates the main-process subtitle processing controller and ties it to the + * subtitle prefetch pause. + * + * Controller emits never release the prefetch pause: the first emit for an + * uncached line is the provisional plain payload, sent before tokenization + * starts, so resuming on it would put prefetch back in contention with the + * on-screen line for the single parser window. The pause is released once the + * controller has no work left, which covers the runs that end without an emit + * (suppressed duplicate, failed tokenization) as well as the ones that deliver + * a payload. + */ +export function createPrefetchAwareSubtitleProcessingController(deps: { + tokenizeSubtitle: SubtitleProcessingControllerDeps['tokenizeSubtitle']; + emitSubtitlePayload: (payload: SubtitleData, options: { resumePrefetch: boolean }) => void; + resumePrefetch: () => void; + logDebug: (message: string) => void; + now: () => number; +}) { + return createSubtitleProcessingController({ + tokenizeSubtitle: deps.tokenizeSubtitle, + emitSubtitle: (payload) => deps.emitSubtitlePayload(payload, { resumePrefetch: false }), + onProcessingSettled: () => deps.resumePrefetch(), + logDebug: deps.logDebug, + now: deps.now, + }); +} diff --git a/src/main/runtime/tray-main-deps.ts b/src/main/runtime/tray-main-deps.ts deleted file mode 100644 index d18c0555..00000000 --- a/src/main/runtime/tray-main-deps.ts +++ /dev/null @@ -1,108 +0,0 @@ -import type { DictionaryBackend } from '../../types/config'; - -export function createBuildResolveTrayIconPathMainDepsHandler(deps: { - resolveTrayIconPathRuntime: (options: { - platform: string; - resourcesPath: string; - appPath: string; - dirname: string; - joinPath: (...parts: string[]) => string; - fileExists: (path: string) => boolean; - }) => string | null; - platform: string; - resourcesPath: string; - appPath: string; - dirname: string; - joinPath: (...parts: string[]) => string; - fileExists: (path: string) => boolean; -}) { - return () => ({ - resolveTrayIconPathRuntime: deps.resolveTrayIconPathRuntime, - platform: deps.platform, - resourcesPath: deps.resourcesPath, - appPath: deps.appPath, - dirname: deps.dirname, - joinPath: deps.joinPath, - fileExists: deps.fileExists, - }); -} - -export function createBuildTrayMenuTemplateMainDepsHandler(deps: { - buildTrayMenuTemplateRuntime: (handlers: { - platform?: string; - openSessionHelp: () => void; - openChangelog: () => void; - openTexthookerInBrowser: () => void; - showTexthookerPage: boolean; - openFirstRunSetup: () => void; - showFirstRunSetup: boolean; - openWindowsMpvLauncherSetup: () => void; - showWindowsMpvLauncherSetup: boolean; - dictionaryBackend: DictionaryBackend; - openHachidoriSettings: () => void; - openYomitanSettings: () => void; - openConfigSettings: () => void; - openSyncUi: () => void; - openYoutubeBrowser: () => void; - exportLogs: () => void; - openJellyfinSetup: () => void; - showJellyfinDiscovery: boolean; - jellyfinDiscoveryActive: boolean; - toggleJellyfinDiscovery: (checked: boolean) => void; - openAnilistSetup: () => void; - checkForUpdates: () => void; - quitApp: () => void; - }) => TMenuItem[]; - initializeOverlayRuntime: () => void; - isOverlayRuntimeInitialized: () => boolean; - openSessionHelpModal: () => void; - openChangelogModal: () => void; - openTexthookerInBrowser: () => void; - showTexthookerPage: () => boolean; - showFirstRunSetup: () => boolean; - openFirstRunSetupWindow: (force?: boolean) => void; - showWindowsMpvLauncherSetup: () => boolean; - getDictionaryBackend: () => DictionaryBackend; - openHachidoriSettings: () => void; - openYomitanSettings: () => void; - openConfigSettingsWindow: () => void; - openSyncUiWindow: () => void; - openYoutubeBrowserWindow: () => void; - exportLogs: () => void; - openJellyfinSetupWindow: () => void; - isJellyfinConfigured: () => boolean; - isJellyfinDiscoveryActive: () => boolean; - toggleJellyfinDiscovery: (checked: boolean) => void | Promise; - platform?: string; - openAnilistSetupWindow: () => void; - checkForUpdates: () => void; - quitApp: () => void; -}) { - return () => ({ - buildTrayMenuTemplateRuntime: deps.buildTrayMenuTemplateRuntime, - platform: deps.platform, - initializeOverlayRuntime: deps.initializeOverlayRuntime, - isOverlayRuntimeInitialized: deps.isOverlayRuntimeInitialized, - openSessionHelpModal: deps.openSessionHelpModal, - openChangelogModal: deps.openChangelogModal, - openTexthookerInBrowser: deps.openTexthookerInBrowser, - showTexthookerPage: deps.showTexthookerPage, - showFirstRunSetup: deps.showFirstRunSetup, - openFirstRunSetupWindow: deps.openFirstRunSetupWindow, - showWindowsMpvLauncherSetup: deps.showWindowsMpvLauncherSetup, - getDictionaryBackend: deps.getDictionaryBackend, - openHachidoriSettings: deps.openHachidoriSettings, - openYomitanSettings: deps.openYomitanSettings, - openConfigSettingsWindow: deps.openConfigSettingsWindow, - openSyncUiWindow: deps.openSyncUiWindow, - openYoutubeBrowserWindow: deps.openYoutubeBrowserWindow, - exportLogs: deps.exportLogs, - openJellyfinSetupWindow: deps.openJellyfinSetupWindow, - isJellyfinConfigured: deps.isJellyfinConfigured, - isJellyfinDiscoveryActive: deps.isJellyfinDiscoveryActive, - toggleJellyfinDiscovery: deps.toggleJellyfinDiscovery, - openAnilistSetupWindow: deps.openAnilistSetupWindow, - checkForUpdates: deps.checkForUpdates, - quitApp: deps.quitApp, - }); -} diff --git a/src/main/runtime/tray-runtime-handlers.ts b/src/main/runtime/tray-runtime-handlers.ts index 92f60c1e..b4ac5dfd 100644 --- a/src/main/runtime/tray-runtime-handlers.ts +++ b/src/main/runtime/tray-runtime-handlers.ts @@ -7,16 +7,10 @@ import { createBuildTrayMenuTemplateHandler, createResolveTrayIconPathHandler, } from './tray-main-actions'; -import { - createBuildResolveTrayIconPathMainDepsHandler, - createBuildTrayMenuTemplateMainDepsHandler, -} from './tray-main-deps'; -type ResolveTrayIconPathMainDeps = Parameters< - typeof createBuildResolveTrayIconPathMainDepsHandler ->[0]; +type ResolveTrayIconPathMainDeps = Parameters[0]; type BuildTrayMenuTemplateMainDeps = Parameters< - typeof createBuildTrayMenuTemplateMainDepsHandler + typeof createBuildTrayMenuTemplateHandler >[0]; type EnsureTrayMainDeps = Parameters< typeof createBuildEnsureTrayMainDepsHandler @@ -32,12 +26,8 @@ export function createTrayRuntimeHandlers(deps: { destroyTrayDeps: DestroyTrayMainDeps; buildMenuFromTemplate: (template: TMenuItem[]) => TMenu; }) { - const resolveTrayIconPath = createResolveTrayIconPathHandler( - createBuildResolveTrayIconPathMainDepsHandler(deps.resolveTrayIconPathDeps)(), - ); - const buildTrayMenuTemplate = createBuildTrayMenuTemplateHandler( - createBuildTrayMenuTemplateMainDepsHandler(deps.buildTrayMenuTemplateDeps)(), - ); + const resolveTrayIconPath = createResolveTrayIconPathHandler(deps.resolveTrayIconPathDeps); + const buildTrayMenuTemplate = createBuildTrayMenuTemplateHandler(deps.buildTrayMenuTemplateDeps); const buildTrayMenu = () => deps.buildMenuFromTemplate(buildTrayMenuTemplate()); const ensureTray = createEnsureTrayHandler( diff --git a/src/main/runtime/yomitan-extension-loader-main-deps.ts b/src/main/runtime/yomitan-extension-loader-main-deps.ts deleted file mode 100644 index 0980b154..00000000 --- a/src/main/runtime/yomitan-extension-loader-main-deps.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { - createEnsureYomitanExtensionLoadedHandler, - createLoadYomitanExtensionHandler, -} from './yomitan-extension-loader'; - -type LoadYomitanExtensionMainDeps = Parameters[0]; -type EnsureYomitanExtensionLoadedMainDeps = Parameters< - typeof createEnsureYomitanExtensionLoadedHandler ->[0]; - -export function createBuildLoadYomitanExtensionMainDepsHandler(deps: LoadYomitanExtensionMainDeps) { - return (): LoadYomitanExtensionMainDeps => ({ - loadYomitanExtensionCore: (options) => deps.loadYomitanExtensionCore(options), - userDataPath: deps.userDataPath, - externalProfilePath: deps.externalProfilePath, - getYomitanParserWindow: () => deps.getYomitanParserWindow(), - setYomitanParserWindow: (window) => deps.setYomitanParserWindow(window), - setYomitanParserReadyPromise: (promise) => deps.setYomitanParserReadyPromise(promise), - setYomitanParserInitPromise: (promise) => deps.setYomitanParserInitPromise(promise), - setYomitanExtension: (extension) => deps.setYomitanExtension(extension), - setYomitanSession: (session) => deps.setYomitanSession(session), - }); -} - -export function createBuildEnsureYomitanExtensionLoadedMainDepsHandler( - deps: EnsureYomitanExtensionLoadedMainDeps, -) { - return (): EnsureYomitanExtensionLoadedMainDeps => ({ - getYomitanExtension: () => deps.getYomitanExtension(), - getLoadInFlight: () => deps.getLoadInFlight(), - setLoadInFlight: (promise) => deps.setLoadInFlight(promise), - loadYomitanExtension: () => deps.loadYomitanExtension(), - }); -} diff --git a/src/main/runtime/yomitan-extension-runtime.ts b/src/main/runtime/yomitan-extension-runtime.ts index f3bda06c..ebfb80dd 100644 --- a/src/main/runtime/yomitan-extension-runtime.ts +++ b/src/main/runtime/yomitan-extension-runtime.ts @@ -3,17 +3,11 @@ import { createLoadYomitanExtensionHandler, } from './yomitan-extension-loader'; import type { Extension } from 'electron'; -import { - createBuildEnsureYomitanExtensionLoadedMainDepsHandler, - createBuildLoadYomitanExtensionMainDepsHandler, -} from './yomitan-extension-loader-main-deps'; -type LoadYomitanExtensionMainDeps = Parameters< - typeof createBuildLoadYomitanExtensionMainDepsHandler ->[0]; +type LoadYomitanExtensionMainDeps = Parameters[0]; type EnsureYomitanExtensionLoadedMainDeps = Omit< - Parameters[0], + Parameters[0], 'loadYomitanExtension' >; @@ -23,7 +17,7 @@ export type YomitanExtensionRuntimeDeps = LoadYomitanExtensionMainDeps & }; export function createYomitanExtensionRuntime(deps: YomitanExtensionRuntimeDeps) { - const buildLoadYomitanExtensionMainDepsHandler = createBuildLoadYomitanExtensionMainDepsHandler({ + const loadYomitanExtensionHandler = createLoadYomitanExtensionHandler({ loadYomitanExtensionCore: deps.loadYomitanExtensionCore, userDataPath: deps.userDataPath, externalProfilePath: deps.externalProfilePath, @@ -34,20 +28,13 @@ export function createYomitanExtensionRuntime(deps: YomitanExtensionRuntimeDeps) setYomitanExtension: deps.setYomitanExtension, setYomitanSession: deps.setYomitanSession, }); - const loadYomitanExtensionHandler = createLoadYomitanExtensionHandler( - buildLoadYomitanExtensionMainDepsHandler(), - ); - const buildEnsureYomitanExtensionLoadedMainDepsHandler = - createBuildEnsureYomitanExtensionLoadedMainDepsHandler({ - getYomitanExtension: deps.getYomitanExtension, - getLoadInFlight: deps.getLoadInFlight, - setLoadInFlight: deps.setLoadInFlight, - loadYomitanExtension: () => loadYomitanExtensionHandler(), - }); - const ensureYomitanExtensionLoadedHandler = createEnsureYomitanExtensionLoadedHandler( - buildEnsureYomitanExtensionLoadedMainDepsHandler(), - ); + const ensureYomitanExtensionLoadedHandler = createEnsureYomitanExtensionLoadedHandler({ + getYomitanExtension: deps.getYomitanExtension, + getLoadInFlight: deps.getLoadInFlight, + setLoadInFlight: deps.setLoadInFlight, + loadYomitanExtension: () => loadYomitanExtensionHandler(), + }); let lastNotifiedExtension: Extension | null = null; let notifyingExtension: Extension | null = null;