From 6ca07abbc29d3350c8f2f7fba21ec60782cc302c Mon Sep 17 00:00:00 2001 From: sudacode Date: Tue, 10 Feb 2026 00:09:06 -0800 Subject: [PATCH] refactor: extract mpv ipc dependency builder service --- package.json | 2 +- .../mpv-client-deps-runtime-service.test.ts | 51 ++++++++++++++++ .../mpv-client-deps-runtime-service.ts | 61 +++++++++++++++++++ src/main.ts | 58 ++++++++++++++---- 4 files changed, 158 insertions(+), 14 deletions(-) create mode 100644 src/core/services/mpv-client-deps-runtime-service.test.ts create mode 100644 src/core/services/mpv-client-deps-runtime-service.ts diff --git a/package.json b/package.json index 9acee4f..3ece10b 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "docs:build": "vitepress build docs", "docs:preview": "vitepress preview docs --host 0.0.0.0 --port 4173 --strictPort", "test:config": "pnpm run build && node --test dist/config/config.test.js", - "test:core": "pnpm run build && node --test dist/cli/args.test.js dist/cli/help.test.js dist/core/services/cli-command-service.test.js dist/core/services/numeric-shortcut-session-service.test.js dist/core/services/secondary-subtitle-service.test.js dist/core/services/mpv-render-metrics-service.test.js dist/core/services/mpv-runtime-service.test.js dist/core/services/runtime-options-runtime-service.test.js dist/core/services/overlay-modal-restore-service.test.js dist/core/services/runtime-config-service.test.js dist/core/services/overlay-bridge-runtime-service.test.js dist/core/services/overlay-visibility-facade-service.test.js dist/core/services/overlay-broadcast-runtime-service.test.js dist/core/services/app-ready-runtime-service.test.js dist/core/services/app-shutdown-runtime-service.test.js", + "test:core": "pnpm run build && node --test dist/cli/args.test.js dist/cli/help.test.js dist/core/services/cli-command-service.test.js dist/core/services/numeric-shortcut-session-service.test.js dist/core/services/secondary-subtitle-service.test.js dist/core/services/mpv-render-metrics-service.test.js dist/core/services/mpv-runtime-service.test.js dist/core/services/runtime-options-runtime-service.test.js dist/core/services/overlay-modal-restore-service.test.js dist/core/services/runtime-config-service.test.js dist/core/services/overlay-bridge-runtime-service.test.js dist/core/services/overlay-visibility-facade-service.test.js dist/core/services/overlay-broadcast-runtime-service.test.js dist/core/services/app-ready-runtime-service.test.js dist/core/services/app-shutdown-runtime-service.test.js dist/core/services/mpv-client-deps-runtime-service.test.js", "test:subtitle": "pnpm run build && node --test dist/subtitle/stages.test.js dist/subtitle/pipeline.test.js", "generate:config-example": "pnpm run build && node dist/generate-config-example.js", "start": "pnpm run build && electron . --start", diff --git a/src/core/services/mpv-client-deps-runtime-service.test.ts b/src/core/services/mpv-client-deps-runtime-service.test.ts new file mode 100644 index 0000000..7076f21 --- /dev/null +++ b/src/core/services/mpv-client-deps-runtime-service.test.ts @@ -0,0 +1,51 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import { createMpvIpcClientDepsRuntimeService } from "./mpv-client-deps-runtime-service"; + +test("createMpvIpcClientDepsRuntimeService returns passthrough dep object", async () => { + const marker = { + getResolvedConfig: () => ({ auto_start_overlay: false } as never), + autoStartOverlay: true, + setOverlayVisible: () => {}, + shouldBindVisibleOverlayToMpvSubVisibility: () => true, + isVisibleOverlayVisible: () => false, + getReconnectTimer: () => null, + setReconnectTimer: () => {}, + getCurrentSubText: () => "x", + setCurrentSubText: () => {}, + setCurrentSubAssText: () => {}, + getSubtitleTimingTracker: () => null, + subtitleWsBroadcast: () => {}, + getOverlayWindowsCount: () => 0, + tokenizeSubtitle: async () => ({ text: "x", tokens: [], mergedTokens: [] }), + broadcastToOverlayWindows: () => {}, + updateCurrentMediaPath: () => {}, + updateMpvSubtitleRenderMetrics: () => {}, + getMpvSubtitleRenderMetrics: () => ({ + subPos: 100, + subFontSize: 40, + subScale: 1, + subMarginY: 0, + subMarginX: 0, + subFont: "sans", + subSpacing: 0, + subBold: false, + subItalic: false, + subBorderSize: 0, + subShadowOffset: 0, + subAssOverride: "yes", + subScaleByWindow: true, + subUseMargins: true, + osdHeight: 720, + osdDimensions: null, + }), + setPreviousSecondarySubVisibility: () => {}, + showMpvOsd: () => {}, + }; + + const deps = createMpvIpcClientDepsRuntimeService(marker); + assert.equal(deps.autoStartOverlay, true); + assert.equal(deps.getCurrentSubText(), "x"); + assert.equal(deps.getOverlayWindowsCount(), 0); + assert.equal(deps.shouldBindVisibleOverlayToMpvSubVisibility(), true); +}); diff --git a/src/core/services/mpv-client-deps-runtime-service.ts b/src/core/services/mpv-client-deps-runtime-service.ts new file mode 100644 index 0000000..1fd8841 --- /dev/null +++ b/src/core/services/mpv-client-deps-runtime-service.ts @@ -0,0 +1,61 @@ +import { + MpvIpcClientDeps, +} from "./mpv-service"; +import { Config, MpvSubtitleRenderMetrics, SubtitleData } from "../../types"; + +interface SubtitleTimingTrackerLike { + recordSubtitle: (text: string, start: number, end: number) => void; +} + +export interface MpvClientDepsRuntimeOptions { + getResolvedConfig: () => Config; + autoStartOverlay: boolean; + setOverlayVisible: (visible: boolean) => void; + shouldBindVisibleOverlayToMpvSubVisibility: () => boolean; + isVisibleOverlayVisible: () => boolean; + getReconnectTimer: () => ReturnType | null; + setReconnectTimer: (timer: ReturnType | null) => void; + getCurrentSubText: () => string; + setCurrentSubText: (text: string) => void; + setCurrentSubAssText: (text: string) => void; + getSubtitleTimingTracker: () => SubtitleTimingTrackerLike | null; + subtitleWsBroadcast: (text: string) => void; + getOverlayWindowsCount: () => number; + tokenizeSubtitle: (text: string) => Promise; + broadcastToOverlayWindows: (channel: string, ...args: unknown[]) => void; + updateCurrentMediaPath: (mediaPath: unknown) => void; + updateMpvSubtitleRenderMetrics: ( + patch: Partial, + ) => void; + getMpvSubtitleRenderMetrics: () => MpvSubtitleRenderMetrics; + setPreviousSecondarySubVisibility: (value: boolean | null) => void; + showMpvOsd: (text: string) => void; +} + +export function createMpvIpcClientDepsRuntimeService( + options: MpvClientDepsRuntimeOptions, +): MpvIpcClientDeps { + return { + getResolvedConfig: options.getResolvedConfig, + autoStartOverlay: options.autoStartOverlay, + setOverlayVisible: options.setOverlayVisible, + shouldBindVisibleOverlayToMpvSubVisibility: + options.shouldBindVisibleOverlayToMpvSubVisibility, + isVisibleOverlayVisible: options.isVisibleOverlayVisible, + getReconnectTimer: options.getReconnectTimer, + setReconnectTimer: options.setReconnectTimer, + getCurrentSubText: options.getCurrentSubText, + setCurrentSubText: options.setCurrentSubText, + setCurrentSubAssText: options.setCurrentSubAssText, + getSubtitleTimingTracker: options.getSubtitleTimingTracker, + subtitleWsBroadcast: options.subtitleWsBroadcast, + getOverlayWindowsCount: options.getOverlayWindowsCount, + tokenizeSubtitle: options.tokenizeSubtitle, + broadcastToOverlayWindows: options.broadcastToOverlayWindows, + updateCurrentMediaPath: options.updateCurrentMediaPath, + updateMpvSubtitleRenderMetrics: options.updateMpvSubtitleRenderMetrics, + getMpvSubtitleRenderMetrics: options.getMpvSubtitleRenderMetrics, + setPreviousSecondarySubVisibility: options.setPreviousSecondarySubVisibility, + showMpvOsd: options.showMpvOsd, + }; +} diff --git a/src/main.ts b/src/main.ts index 2083c8c..b03b5cb 100644 --- a/src/main.ts +++ b/src/main.ts @@ -203,6 +203,7 @@ import { } from "./core/services/overlay-broadcast-runtime-service"; import { runAppReadyRuntimeService } from "./core/services/app-ready-runtime-service"; import { runAppShutdownRuntimeService } from "./core/services/app-shutdown-runtime-service"; +import { createMpvIpcClientDepsRuntimeService } from "./core/services/mpv-client-deps-runtime-service"; import { runSubsyncManualFromIpcRuntimeService, triggerSubsyncFromConfigRuntimeService, @@ -495,19 +496,50 @@ if (initialArgs.generateConfig && !shouldStartApp(initialArgs)) { keybindings = resolveKeybindings(getResolvedConfig(), DEFAULT_KEYBINDINGS); }, createMpvClient: () => { - mpvClient = new MpvIpcClient(mpvSocketPath, { - getResolvedConfig: () => getResolvedConfig(), autoStartOverlay, - setOverlayVisible: (visible) => setOverlayVisible(visible), - shouldBindVisibleOverlayToMpvSubVisibility: () => shouldBindVisibleOverlayToMpvSubVisibility(), - isVisibleOverlayVisible: () => visibleOverlayVisible, - getReconnectTimer: () => reconnectTimer, setReconnectTimer: (timer) => { reconnectTimer = timer; }, - getCurrentSubText: () => currentSubText, setCurrentSubText: (text) => { currentSubText = text; }, setCurrentSubAssText: (text) => { currentSubAssText = text; }, - getSubtitleTimingTracker: () => subtitleTimingTracker, subtitleWsBroadcast: (text) => { subtitleWsService.broadcast(text); }, - getOverlayWindowsCount: () => getOverlayWindows().length, tokenizeSubtitle: (text) => tokenizeSubtitle(text), - broadcastToOverlayWindows: (channel, ...args) => { broadcastToOverlayWindows(channel, ...args); }, - updateCurrentMediaPath: (mediaPath) => { updateCurrentMediaPath(mediaPath); }, updateMpvSubtitleRenderMetrics: (patch) => { updateMpvSubtitleRenderMetrics(patch); }, - getMpvSubtitleRenderMetrics: () => mpvSubtitleRenderMetrics, setPreviousSecondarySubVisibility: (value) => { previousSecondarySubVisibility = value; }, showMpvOsd: (text) => { showMpvOsd(text); }, - }); + mpvClient = new MpvIpcClient( + mpvSocketPath, + createMpvIpcClientDepsRuntimeService({ + getResolvedConfig: () => getResolvedConfig(), + autoStartOverlay, + setOverlayVisible: (visible) => setOverlayVisible(visible), + shouldBindVisibleOverlayToMpvSubVisibility: () => + shouldBindVisibleOverlayToMpvSubVisibility(), + isVisibleOverlayVisible: () => visibleOverlayVisible, + getReconnectTimer: () => reconnectTimer, + setReconnectTimer: (timer) => { + reconnectTimer = timer; + }, + getCurrentSubText: () => currentSubText, + setCurrentSubText: (text) => { + currentSubText = text; + }, + setCurrentSubAssText: (text) => { + currentSubAssText = text; + }, + getSubtitleTimingTracker: () => subtitleTimingTracker, + subtitleWsBroadcast: (text) => { + subtitleWsService.broadcast(text); + }, + getOverlayWindowsCount: () => getOverlayWindows().length, + tokenizeSubtitle: (text) => tokenizeSubtitle(text), + broadcastToOverlayWindows: (channel, ...args) => { + broadcastToOverlayWindows(channel, ...args); + }, + updateCurrentMediaPath: (mediaPath) => { + updateCurrentMediaPath(mediaPath); + }, + updateMpvSubtitleRenderMetrics: (patch) => { + updateMpvSubtitleRenderMetrics(patch); + }, + getMpvSubtitleRenderMetrics: () => mpvSubtitleRenderMetrics, + setPreviousSecondarySubVisibility: (value) => { + previousSecondarySubVisibility = value; + }, + showMpvOsd: (text) => { + showMpvOsd(text); + }, + }), + ); }, reloadConfig: () => { configService.reloadConfig();