diff --git a/package.json b/package.json index 2e67017..af185f4 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", + "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", "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-runtime-service.test.ts b/src/core/services/mpv-runtime-service.test.ts new file mode 100644 index 0000000..64c3fab --- /dev/null +++ b/src/core/services/mpv-runtime-service.test.ts @@ -0,0 +1,69 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import { + playNextSubtitleRuntimeService, + replayCurrentSubtitleRuntimeService, + sendMpvCommandRuntimeService, + setMpvSubVisibilityRuntimeService, + showMpvOsdRuntimeService, +} from "./mpv-runtime-service"; + +test("showMpvOsdRuntimeService sends show-text when connected", () => { + const commands: (string | number)[][] = []; + showMpvOsdRuntimeService( + { + connected: true, + send: ({ command }) => { + commands.push(command); + }, + }, + "hello", + ); + assert.deepEqual(commands, [["show-text", "hello", "3000"]]); +}); + +test("showMpvOsdRuntimeService logs fallback when disconnected", () => { + const logs: string[] = []; + showMpvOsdRuntimeService( + { + connected: false, + send: () => {}, + }, + "hello", + (line) => { + logs.push(line); + }, + ); + assert.deepEqual(logs, ["OSD (MPV not connected): hello"]); +}); + +test("mpv runtime command wrappers call expected client methods", () => { + const calls: string[] = []; + const client = { + connected: true, + send: ({ command }: { command: (string | number)[] }) => { + calls.push(`send:${command.join(",")}`); + }, + replayCurrentSubtitle: () => { + calls.push("replay"); + }, + playNextSubtitle: () => { + calls.push("next"); + }, + setSubVisibility: (visible: boolean) => { + calls.push(`subVisible:${visible}`); + }, + }; + + replayCurrentSubtitleRuntimeService(client); + playNextSubtitleRuntimeService(client); + sendMpvCommandRuntimeService(client, ["script-message", "x"]); + setMpvSubVisibilityRuntimeService(client, false); + + assert.deepEqual(calls, [ + "replay", + "next", + "send:script-message,x", + "subVisible:false", + ]); +}); diff --git a/src/core/services/mpv-runtime-service.ts b/src/core/services/mpv-runtime-service.ts new file mode 100644 index 0000000..3226826 --- /dev/null +++ b/src/core/services/mpv-runtime-service.ts @@ -0,0 +1,49 @@ +export interface MpvRuntimeClientLike { + connected: boolean; + send: (payload: { command: (string | number)[] }) => void; + replayCurrentSubtitle?: () => void; + playNextSubtitle?: () => void; + setSubVisibility?: (visible: boolean) => void; +} + +export function showMpvOsdRuntimeService( + mpvClient: MpvRuntimeClientLike | null, + text: string, + fallbackLog: (text: string) => void = console.log, +): void { + if (mpvClient && mpvClient.connected) { + mpvClient.send({ command: ["show-text", text, "3000"] }); + return; + } + fallbackLog(`OSD (MPV not connected): ${text}`); +} + +export function replayCurrentSubtitleRuntimeService( + mpvClient: MpvRuntimeClientLike | null, +): void { + if (!mpvClient?.replayCurrentSubtitle) return; + mpvClient.replayCurrentSubtitle(); +} + +export function playNextSubtitleRuntimeService( + mpvClient: MpvRuntimeClientLike | null, +): void { + if (!mpvClient?.playNextSubtitle) return; + mpvClient.playNextSubtitle(); +} + +export function sendMpvCommandRuntimeService( + mpvClient: MpvRuntimeClientLike | null, + command: (string | number)[], +): void { + if (!mpvClient) return; + mpvClient.send({ command }); +} + +export function setMpvSubVisibilityRuntimeService( + mpvClient: MpvRuntimeClientLike | null, + visible: boolean, +): void { + if (!mpvClient?.setSubVisibility) return; + mpvClient.setSubVisibility(visible); +} diff --git a/src/main.ts b/src/main.ts index 8832b42..a893189 100644 --- a/src/main.ts +++ b/src/main.ts @@ -132,6 +132,13 @@ import { updateLastCardFromClipboardService, } from "./core/services/mining-runtime-service"; import { startAppLifecycleService } from "./core/services/app-lifecycle-service"; +import { + playNextSubtitleRuntimeService, + replayCurrentSubtitleRuntimeService, + sendMpvCommandRuntimeService, + setMpvSubVisibilityRuntimeService, + showMpvOsdRuntimeService, +} from "./core/services/mpv-runtime-service"; import { showDesktopNotification } from "./core/utils/notification"; import { openYomitanSettingsWindow } from "./core/services/yomitan-settings-service"; import { tokenizeSubtitleService } from "./core/services/tokenizer-service"; @@ -851,13 +858,13 @@ function cycleSecondarySubMode(): void { } function showMpvOsd(text: string): void { - if (mpvClient && mpvClient.connected && mpvClient.send) { - mpvClient.send({ - command: ["show-text", text, "3000"], - }); - } else { - console.log("OSD (MPV not connected):", text); - } + showMpvOsdRuntimeService( + mpvClient, + text, + (line) => { + console.log(line); + }, + ); } const multiCopySession = createNumericShortcutSessionService({ @@ -1095,9 +1102,7 @@ function setVisibleOverlayVisible(visible: boolean): void { shouldBindVisibleOverlayToMpvSubVisibility(), isMpvConnected: () => Boolean(mpvClient && mpvClient.connected), setMpvSubVisibility: (mpvSubVisible) => { - if (mpvClient) { - mpvClient.setSubVisibility(mpvSubVisible); - } + setMpvSubVisibilityRuntimeService(mpvClient, mpvSubVisible); }, }); } @@ -1134,16 +1139,10 @@ function handleMpvCommandFromIpc(command: (string | number)[]): void { ); }, showMpvOsd: (text) => showMpvOsd(text), - mpvReplaySubtitle: () => { - if (mpvClient) mpvClient.replayCurrentSubtitle(); - }, - mpvPlayNextSubtitle: () => { - if (mpvClient) mpvClient.playNextSubtitle(); - }, - mpvSendCommand: (rawCommand) => { - if (!mpvClient) return; - mpvClient.send({ command: rawCommand }); - }, + mpvReplaySubtitle: () => replayCurrentSubtitleRuntimeService(mpvClient), + mpvPlayNextSubtitle: () => playNextSubtitleRuntimeService(mpvClient), + mpvSendCommand: (rawCommand) => + sendMpvCommandRuntimeService(mpvClient, rawCommand), isMpvConnected: () => Boolean(mpvClient && mpvClient.connected), hasRuntimeOptionsManager: () => runtimeOptionsManager !== null, });