feat(jellyfin): add remote playback and config plumbing

This commit is contained in:
2026-02-17 19:00:18 -08:00
parent a6a28f52f3
commit e38a1c945e
42 changed files with 5608 additions and 1013 deletions

View File

@@ -1,6 +1,12 @@
import { handleCliCommand, createCliCommandDepsRuntime } from "../core/services";
import {
handleCliCommand,
createCliCommandDepsRuntime,
} from "../core/services";
import type { CliArgs, CliCommandSource } from "../cli/args";
import { createCliCommandRuntimeServiceDeps, CliCommandRuntimeServiceDepsParams } from "./dependencies";
import {
createCliCommandRuntimeServiceDeps,
CliCommandRuntimeServiceDepsParams,
} from "./dependencies";
export interface CliCommandRuntimeServiceContext {
getSocketPath: () => string;
@@ -31,6 +37,8 @@ export interface CliCommandRuntimeServiceContext {
openAnilistSetup: CliCommandRuntimeServiceDepsParams["anilist"]["openSetup"];
getAnilistQueueStatus: CliCommandRuntimeServiceDepsParams["anilist"]["getQueueStatus"];
retryAnilistQueueNow: CliCommandRuntimeServiceDepsParams["anilist"]["retryQueueNow"];
openJellyfinSetup: CliCommandRuntimeServiceDepsParams["jellyfin"]["openSetup"];
runJellyfinCommand: CliCommandRuntimeServiceDepsParams["jellyfin"]["runCommand"];
openYomitanSettings: () => void;
cycleSecondarySubMode: () => void;
openRuntimeOptionsPalette: () => void;
@@ -49,7 +57,8 @@ export interface CliCommandRuntimeServiceContextHandlers {
}
function createCliCommandDepsFromContext(
context: CliCommandRuntimeServiceContext & CliCommandRuntimeServiceContextHandlers,
context: CliCommandRuntimeServiceContext &
CliCommandRuntimeServiceContextHandlers,
): CliCommandRuntimeServiceDepsParams {
return {
mpv: {
@@ -77,7 +86,8 @@ function createCliCommandDepsFromContext(
copyCurrentSubtitle: context.copyCurrentSubtitle,
startPendingMultiCopy: context.startPendingMultiCopy,
mineSentenceCard: context.mineSentenceCard,
startPendingMineSentenceMultiple: context.startPendingMineSentenceMultiple,
startPendingMineSentenceMultiple:
context.startPendingMineSentenceMultiple,
updateLastCardFromClipboard: context.updateLastCardFromClipboard,
refreshKnownWords: context.refreshKnownWordCache,
triggerFieldGrouping: context.triggerFieldGrouping,
@@ -91,6 +101,10 @@ function createCliCommandDepsFromContext(
getQueueStatus: context.getAnilistQueueStatus,
retryQueueNow: context.retryAnilistQueueNow,
},
jellyfin: {
openSetup: context.openJellyfinSetup,
runCommand: context.runJellyfinCommand,
},
ui: {
openYomitanSettings: context.openYomitanSettings,
cycleSecondarySubMode: context.cycleSecondarySubMode,
@@ -123,7 +137,12 @@ export function handleCliCommandRuntimeService(
export function handleCliCommandRuntimeServiceWithContext(
args: CliArgs,
source: CliCommandSource,
context: CliCommandRuntimeServiceContext & CliCommandRuntimeServiceContextHandlers,
context: CliCommandRuntimeServiceContext &
CliCommandRuntimeServiceContextHandlers,
): void {
handleCliCommandRuntimeService(args, source, createCliCommandDepsFromContext(context));
handleCliCommandRuntimeService(
args,
source,
createCliCommandDepsFromContext(context),
);
}

View File

@@ -29,7 +29,9 @@ export interface SubsyncRuntimeDepsParams {
openManualPicker: (payload: SubsyncManualPayload) => void;
}
export function createRuntimeOptionsIpcDeps(params: RuntimeOptionsIpcDepsParams): {
export function createRuntimeOptionsIpcDeps(
params: RuntimeOptionsIpcDepsParams,
): {
setRuntimeOption: (id: string, value: unknown) => unknown;
cycleRuntimeOption: (id: string, direction: 1 | -1) => unknown;
} {
@@ -51,7 +53,9 @@ export function createRuntimeOptionsIpcDeps(params: RuntimeOptionsIpcDepsParams)
};
}
export function createSubsyncRuntimeDeps(params: SubsyncRuntimeDepsParams): SubsyncRuntimeDeps {
export function createSubsyncRuntimeDeps(
params: SubsyncRuntimeDepsParams,
): SubsyncRuntimeDeps {
return {
getMpvClient: params.getMpvClient,
getResolvedSubsyncConfig: params.getResolvedSubsyncConfig,
@@ -145,19 +149,14 @@ export interface CliCommandRuntimeServiceDepsParams {
};
mining: {
copyCurrentSubtitle: CliCommandDepsRuntimeOptions["mining"]["copyCurrentSubtitle"];
startPendingMultiCopy:
CliCommandDepsRuntimeOptions["mining"]["startPendingMultiCopy"];
startPendingMultiCopy: CliCommandDepsRuntimeOptions["mining"]["startPendingMultiCopy"];
mineSentenceCard: CliCommandDepsRuntimeOptions["mining"]["mineSentenceCard"];
startPendingMineSentenceMultiple:
CliCommandDepsRuntimeOptions["mining"]["startPendingMineSentenceMultiple"];
updateLastCardFromClipboard:
CliCommandDepsRuntimeOptions["mining"]["updateLastCardFromClipboard"];
startPendingMineSentenceMultiple: CliCommandDepsRuntimeOptions["mining"]["startPendingMineSentenceMultiple"];
updateLastCardFromClipboard: CliCommandDepsRuntimeOptions["mining"]["updateLastCardFromClipboard"];
refreshKnownWords: CliCommandDepsRuntimeOptions["mining"]["refreshKnownWords"];
triggerFieldGrouping: CliCommandDepsRuntimeOptions["mining"]["triggerFieldGrouping"];
triggerSubsyncFromConfig:
CliCommandDepsRuntimeOptions["mining"]["triggerSubsyncFromConfig"];
markLastCardAsAudioCard:
CliCommandDepsRuntimeOptions["mining"]["markLastCardAsAudioCard"];
triggerSubsyncFromConfig: CliCommandDepsRuntimeOptions["mining"]["triggerSubsyncFromConfig"];
markLastCardAsAudioCard: CliCommandDepsRuntimeOptions["mining"]["markLastCardAsAudioCard"];
};
anilist: {
getStatus: CliCommandDepsRuntimeOptions["anilist"]["getStatus"];
@@ -166,11 +165,14 @@ export interface CliCommandRuntimeServiceDepsParams {
getQueueStatus: CliCommandDepsRuntimeOptions["anilist"]["getQueueStatus"];
retryQueueNow: CliCommandDepsRuntimeOptions["anilist"]["retryQueueNow"];
};
jellyfin: {
openSetup: CliCommandDepsRuntimeOptions["jellyfin"]["openSetup"];
runCommand: CliCommandDepsRuntimeOptions["jellyfin"]["runCommand"];
};
ui: {
openYomitanSettings: CliCommandDepsRuntimeOptions["ui"]["openYomitanSettings"];
cycleSecondarySubMode: CliCommandDepsRuntimeOptions["ui"]["cycleSecondarySubMode"];
openRuntimeOptionsPalette:
CliCommandDepsRuntimeOptions["ui"]["openRuntimeOptionsPalette"];
openRuntimeOptionsPalette: CliCommandDepsRuntimeOptions["ui"]["openRuntimeOptionsPalette"];
printHelp: CliCommandDepsRuntimeOptions["ui"]["printHelp"];
};
app: {
@@ -293,7 +295,8 @@ export function createCliCommandRuntimeServiceDeps(
copyCurrentSubtitle: params.mining.copyCurrentSubtitle,
startPendingMultiCopy: params.mining.startPendingMultiCopy,
mineSentenceCard: params.mining.mineSentenceCard,
startPendingMineSentenceMultiple: params.mining.startPendingMineSentenceMultiple,
startPendingMineSentenceMultiple:
params.mining.startPendingMineSentenceMultiple,
updateLastCardFromClipboard: params.mining.updateLastCardFromClipboard,
refreshKnownWords: params.mining.refreshKnownWords,
triggerFieldGrouping: params.mining.triggerFieldGrouping,
@@ -307,6 +310,10 @@ export function createCliCommandRuntimeServiceDeps(
getQueueStatus: params.anilist.getQueueStatus,
retryQueueNow: params.anilist.retryQueueNow,
},
jellyfin: {
openSetup: params.jellyfin.openSetup,
runCommand: params.jellyfin.runCommand,
},
ui: {
openYomitanSettings: params.ui.openYomitanSettings,
cycleSecondarySubMode: params.ui.cycleSecondarySubMode,

View File

@@ -14,6 +14,7 @@ import type { SubtitleTimingTracker } from "../subtitle-timing-tracker";
import type { AnkiIntegration } from "../anki-integration";
import type { ImmersionTrackerService } from "../core/services";
import type { MpvIpcClient } from "../core/services";
import type { JellyfinRemoteSessionService } from "../core/services";
import { DEFAULT_MPV_SUBTITLE_RENDER_METRICS } from "../core/services";
import type { RuntimeOptionsManager } from "../runtime-options";
import type { MecabTokenizer } from "../mecab-tokenizer";
@@ -40,9 +41,11 @@ export interface AppState {
yomitanSettingsWindow: BrowserWindow | null;
yomitanParserWindow: BrowserWindow | null;
anilistSetupWindow: BrowserWindow | null;
jellyfinSetupWindow: BrowserWindow | null;
yomitanParserReadyPromise: Promise<void> | null;
yomitanParserInitPromise: Promise<boolean> | null;
mpvClient: MpvIpcClient | null;
jellyfinRemoteSession: JellyfinRemoteSessionService | null;
reconnectTimer: ReturnType<typeof setTimeout> | null;
currentSubText: string;
currentSubAssText: string;
@@ -104,9 +107,11 @@ export function createAppState(values: AppStateInitialValues): AppState {
yomitanSettingsWindow: null,
yomitanParserWindow: null,
anilistSetupWindow: null,
jellyfinSetupWindow: null,
yomitanParserReadyPromise: null,
yomitanParserInitPromise: null,
mpvClient: null,
jellyfinRemoteSession: null,
reconnectTimer: null,
currentSubText: "",
currentSubAssText: "",