mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-02-27 18:22:41 -08:00
refactor(mpv): emit media/path/title events for app-level handlers
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import * as net from "net";
|
import * as net from "net";
|
||||||
|
import { EventEmitter } from "events";
|
||||||
import {
|
import {
|
||||||
Config,
|
Config,
|
||||||
MpvClient,
|
MpvClient,
|
||||||
@@ -44,7 +45,7 @@ interface SubtitleTimingTrackerLike {
|
|||||||
recordSubtitle: (text: string, start: number, end: number) => void;
|
recordSubtitle: (text: string, start: number, end: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MpvIpcClientDeps {
|
export interface MpvIpcClientProtocolDeps {
|
||||||
getResolvedConfig: () => Config;
|
getResolvedConfig: () => Config;
|
||||||
autoStartOverlay: boolean;
|
autoStartOverlay: boolean;
|
||||||
setOverlayVisible: (visible: boolean) => void;
|
setOverlayVisible: (visible: boolean) => void;
|
||||||
@@ -52,29 +53,47 @@ export interface MpvIpcClientDeps {
|
|||||||
isVisibleOverlayVisible: () => boolean;
|
isVisibleOverlayVisible: () => boolean;
|
||||||
getReconnectTimer: () => ReturnType<typeof setTimeout> | null;
|
getReconnectTimer: () => ReturnType<typeof setTimeout> | null;
|
||||||
setReconnectTimer: (timer: ReturnType<typeof setTimeout> | null) => void;
|
setReconnectTimer: (timer: ReturnType<typeof setTimeout> | null) => void;
|
||||||
getCurrentSubText: () => string;
|
}
|
||||||
setCurrentSubText: (text: string) => void;
|
|
||||||
setCurrentSubAssText: (text: string) => void;
|
export interface MpvIpcClientRuntimeDeps {
|
||||||
getSubtitleTimingTracker: () => SubtitleTimingTrackerLike | null;
|
getCurrentSubText?: () => string;
|
||||||
subtitleWsBroadcast: (text: string) => void;
|
setCurrentSubText?: (text: string) => void;
|
||||||
getOverlayWindowsCount: () => number;
|
setCurrentSubAssText?: (text: string) => void;
|
||||||
tokenizeSubtitle: (text: string) => Promise<SubtitleData>;
|
getSubtitleTimingTracker?: () => SubtitleTimingTrackerLike | null;
|
||||||
broadcastToOverlayWindows: (channel: string, ...args: unknown[]) => void;
|
subtitleWsBroadcast?: (text: string) => void;
|
||||||
updateCurrentMediaPath: (mediaPath: unknown) => void;
|
getOverlayWindowsCount?: () => number;
|
||||||
updateMpvSubtitleRenderMetrics: (
|
tokenizeSubtitle?: (text: string) => Promise<SubtitleData>;
|
||||||
|
broadcastToOverlayWindows?: (channel: string, ...args: unknown[]) => void;
|
||||||
|
updateCurrentMediaPath?: (mediaPath: unknown) => void;
|
||||||
|
updateMpvSubtitleRenderMetrics?: (
|
||||||
patch: Partial<MpvSubtitleRenderMetrics>,
|
patch: Partial<MpvSubtitleRenderMetrics>,
|
||||||
) => void;
|
) => void;
|
||||||
getMpvSubtitleRenderMetrics: () => MpvSubtitleRenderMetrics;
|
getMpvSubtitleRenderMetrics?: () => MpvSubtitleRenderMetrics;
|
||||||
getPreviousSecondarySubVisibility: () => boolean | null;
|
getPreviousSecondarySubVisibility?: () => boolean | null;
|
||||||
setPreviousSecondarySubVisibility: (value: boolean | null) => void;
|
setPreviousSecondarySubVisibility?: (value: boolean | null) => void;
|
||||||
showMpvOsd: (text: string) => void;
|
showMpvOsd?: (text: string) => void;
|
||||||
updateCurrentMediaTitle?: (mediaTitle: unknown) => void;
|
updateCurrentMediaTitle?: (mediaTitle: unknown) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MpvIpcClientDeps extends MpvIpcClientProtocolDeps, MpvIpcClientRuntimeDeps {}
|
||||||
|
|
||||||
|
export interface MpvIpcClientEventMap {
|
||||||
|
"subtitle-change": { text: string; isOverlayVisible: boolean };
|
||||||
|
"subtitle-ass-change": { text: string };
|
||||||
|
"secondary-subtitle-change": { text: string };
|
||||||
|
"media-path-change": { path: string };
|
||||||
|
"media-title-change": { title: string | null };
|
||||||
|
"subtitle-metrics-change": { patch: Partial<MpvSubtitleRenderMetrics> };
|
||||||
|
"secondary-subtitle-visibility": { visible: boolean };
|
||||||
|
}
|
||||||
|
|
||||||
|
type MpvIpcClientEventName = keyof MpvIpcClientEventMap;
|
||||||
|
|
||||||
export class MpvIpcClient implements MpvClient {
|
export class MpvIpcClient implements MpvClient {
|
||||||
private socketPath: string;
|
private socketPath: string;
|
||||||
private deps: MpvIpcClientDeps;
|
private deps: MpvIpcClientProtocolDeps & Required<MpvIpcClientRuntimeDeps>;
|
||||||
public socket: net.Socket | null = null;
|
public socket: net.Socket | null = null;
|
||||||
|
private eventBus = new EventEmitter();
|
||||||
private buffer = "";
|
private buffer = "";
|
||||||
public connected = false;
|
public connected = false;
|
||||||
private connecting = false;
|
private connecting = false;
|
||||||
@@ -96,7 +115,62 @@ export class MpvIpcClient implements MpvClient {
|
|||||||
|
|
||||||
constructor(socketPath: string, deps: MpvIpcClientDeps) {
|
constructor(socketPath: string, deps: MpvIpcClientDeps) {
|
||||||
this.socketPath = socketPath;
|
this.socketPath = socketPath;
|
||||||
this.deps = deps;
|
this.deps = {
|
||||||
|
getCurrentSubText: () => "",
|
||||||
|
setCurrentSubText: () => undefined,
|
||||||
|
setCurrentSubAssText: () => undefined,
|
||||||
|
getSubtitleTimingTracker: () => null,
|
||||||
|
subtitleWsBroadcast: () => undefined,
|
||||||
|
getOverlayWindowsCount: () => 0,
|
||||||
|
tokenizeSubtitle: async (text) => ({ text, tokens: null }),
|
||||||
|
broadcastToOverlayWindows: () => undefined,
|
||||||
|
updateCurrentMediaPath: () => undefined,
|
||||||
|
updateCurrentMediaTitle: () => undefined,
|
||||||
|
updateMpvSubtitleRenderMetrics: () => undefined,
|
||||||
|
getMpvSubtitleRenderMetrics: () => ({
|
||||||
|
subPos: 100,
|
||||||
|
subFontSize: 36,
|
||||||
|
subScale: 1,
|
||||||
|
subMarginY: 0,
|
||||||
|
subMarginX: 0,
|
||||||
|
subFont: "",
|
||||||
|
subSpacing: 0,
|
||||||
|
subBold: false,
|
||||||
|
subItalic: false,
|
||||||
|
subBorderSize: 0,
|
||||||
|
subShadowOffset: 0,
|
||||||
|
subAssOverride: "yes",
|
||||||
|
subScaleByWindow: true,
|
||||||
|
subUseMargins: true,
|
||||||
|
osdHeight: 0,
|
||||||
|
osdDimensions: null,
|
||||||
|
}),
|
||||||
|
getPreviousSecondarySubVisibility: () => null,
|
||||||
|
setPreviousSecondarySubVisibility: () => undefined,
|
||||||
|
showMpvOsd: () => undefined,
|
||||||
|
...deps,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
on<EventName extends MpvIpcClientEventName>(
|
||||||
|
event: EventName,
|
||||||
|
listener: (payload: MpvIpcClientEventMap[EventName]) => void,
|
||||||
|
): void {
|
||||||
|
this.eventBus.on(event as string, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
off<EventName extends MpvIpcClientEventName>(
|
||||||
|
event: EventName,
|
||||||
|
listener: (payload: MpvIpcClientEventMap[EventName]) => void,
|
||||||
|
): void {
|
||||||
|
this.eventBus.off(event as string, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private emit<EventName extends MpvIpcClientEventName>(
|
||||||
|
event: EventName,
|
||||||
|
payload: MpvIpcClientEventMap[EventName],
|
||||||
|
): void {
|
||||||
|
this.eventBus.emit(event as string, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
setSocketPath(socketPath: string): void {
|
setSocketPath(socketPath: string): void {
|
||||||
@@ -219,6 +293,11 @@ export class MpvIpcClient implements MpvClient {
|
|||||||
if (msg.event === "property-change") {
|
if (msg.event === "property-change") {
|
||||||
if (msg.name === "sub-text") {
|
if (msg.name === "sub-text") {
|
||||||
const nextSubText = (msg.data as string) || "";
|
const nextSubText = (msg.data as string) || "";
|
||||||
|
const overlayVisible = this.deps.isVisibleOverlayVisible();
|
||||||
|
this.emit("subtitle-change", {
|
||||||
|
text: nextSubText,
|
||||||
|
isOverlayVisible: overlayVisible,
|
||||||
|
});
|
||||||
this.deps.setCurrentSubText(nextSubText);
|
this.deps.setCurrentSubText(nextSubText);
|
||||||
this.currentSubText = nextSubText;
|
this.currentSubText = nextSubText;
|
||||||
const subtitleTimingTracker = this.deps.getSubtitleTimingTracker();
|
const subtitleTimingTracker = this.deps.getSubtitleTimingTracker();
|
||||||
@@ -240,6 +319,9 @@ export class MpvIpcClient implements MpvClient {
|
|||||||
}
|
}
|
||||||
} else if (msg.name === "sub-text-ass") {
|
} else if (msg.name === "sub-text-ass") {
|
||||||
const nextSubAssText = (msg.data as string) || "";
|
const nextSubAssText = (msg.data as string) || "";
|
||||||
|
this.emit("subtitle-ass-change", {
|
||||||
|
text: nextSubAssText,
|
||||||
|
});
|
||||||
this.deps.setCurrentSubAssText(nextSubAssText);
|
this.deps.setCurrentSubAssText(nextSubAssText);
|
||||||
this.deps.broadcastToOverlayWindows("subtitle-ass:set", nextSubAssText);
|
this.deps.broadcastToOverlayWindows("subtitle-ass:set", nextSubAssText);
|
||||||
} else if (msg.name === "sub-start") {
|
} else if (msg.name === "sub-start") {
|
||||||
@@ -269,6 +351,9 @@ export class MpvIpcClient implements MpvClient {
|
|||||||
}
|
}
|
||||||
} else if (msg.name === "secondary-sub-text") {
|
} else if (msg.name === "secondary-sub-text") {
|
||||||
this.currentSecondarySubText = (msg.data as string) || "";
|
this.currentSecondarySubText = (msg.data as string) || "";
|
||||||
|
this.emit("secondary-subtitle-change", {
|
||||||
|
text: this.currentSecondarySubText,
|
||||||
|
});
|
||||||
this.deps.broadcastToOverlayWindows(
|
this.deps.broadcastToOverlayWindows(
|
||||||
"secondary-subtitle:set",
|
"secondary-subtitle:set",
|
||||||
this.currentSecondarySubText,
|
this.currentSecondarySubText,
|
||||||
@@ -287,13 +372,21 @@ export class MpvIpcClient implements MpvClient {
|
|||||||
this.send({ command: ["set_property", "pause", true] });
|
this.send({ command: ["set_property", "pause", true] });
|
||||||
}
|
}
|
||||||
} else if (msg.name === "media-title") {
|
} else if (msg.name === "media-title") {
|
||||||
|
this.emit("media-title-change", {
|
||||||
|
title: typeof msg.data === "string" ? msg.data.trim() : null,
|
||||||
|
});
|
||||||
this.deps.updateCurrentMediaTitle?.(msg.data);
|
this.deps.updateCurrentMediaTitle?.(msg.data);
|
||||||
} else if (msg.name === "path") {
|
} else if (msg.name === "path") {
|
||||||
this.currentVideoPath = (msg.data as string) || "";
|
this.currentVideoPath = (msg.data as string) || "";
|
||||||
|
this.emit("media-path-change", {
|
||||||
|
path: (msg.data as string) || "",
|
||||||
|
});
|
||||||
this.deps.updateCurrentMediaPath(msg.data);
|
this.deps.updateCurrentMediaPath(msg.data);
|
||||||
this.autoLoadSecondarySubTrack();
|
this.autoLoadSecondarySubTrack();
|
||||||
this.syncCurrentAudioStreamIndex();
|
this.syncCurrentAudioStreamIndex();
|
||||||
} else if (msg.name === "sub-pos") {
|
} else if (msg.name === "sub-pos") {
|
||||||
|
const patch = { subPos: msg.data as number };
|
||||||
|
this.emit("subtitle-metrics-change", { patch });
|
||||||
this.deps.updateMpvSubtitleRenderMetrics({ subPos: msg.data as number });
|
this.deps.updateMpvSubtitleRenderMetrics({ subPos: msg.data as number });
|
||||||
} else if (msg.name === "sub-font-size") {
|
} else if (msg.name === "sub-font-size") {
|
||||||
this.deps.updateMpvSubtitleRenderMetrics({
|
this.deps.updateMpvSubtitleRenderMetrics({
|
||||||
@@ -423,6 +516,10 @@ export class MpvIpcClient implements MpvClient {
|
|||||||
const nextSubText = (msg.data as string) || "";
|
const nextSubText = (msg.data as string) || "";
|
||||||
this.deps.setCurrentSubText(nextSubText);
|
this.deps.setCurrentSubText(nextSubText);
|
||||||
this.currentSubText = nextSubText;
|
this.currentSubText = nextSubText;
|
||||||
|
this.emit("subtitle-change", {
|
||||||
|
text: nextSubText,
|
||||||
|
isOverlayVisible: this.deps.isVisibleOverlayVisible(),
|
||||||
|
});
|
||||||
this.deps.subtitleWsBroadcast(nextSubText);
|
this.deps.subtitleWsBroadcast(nextSubText);
|
||||||
if (this.deps.getOverlayWindowsCount() > 0) {
|
if (this.deps.getOverlayWindowsCount() > 0) {
|
||||||
this.deps.tokenizeSubtitle(nextSubText).then((subtitleData) => {
|
this.deps.tokenizeSubtitle(nextSubText).then((subtitleData) => {
|
||||||
@@ -431,9 +528,15 @@ export class MpvIpcClient implements MpvClient {
|
|||||||
}
|
}
|
||||||
} else if (msg.request_id === MPV_REQUEST_ID_SUBTEXT_ASS) {
|
} else if (msg.request_id === MPV_REQUEST_ID_SUBTEXT_ASS) {
|
||||||
const nextSubAssText = (msg.data as string) || "";
|
const nextSubAssText = (msg.data as string) || "";
|
||||||
|
this.emit("subtitle-ass-change", {
|
||||||
|
text: nextSubAssText,
|
||||||
|
});
|
||||||
this.deps.setCurrentSubAssText(nextSubAssText);
|
this.deps.setCurrentSubAssText(nextSubAssText);
|
||||||
this.deps.broadcastToOverlayWindows("subtitle-ass:set", nextSubAssText);
|
this.deps.broadcastToOverlayWindows("subtitle-ass:set", nextSubAssText);
|
||||||
} else if (msg.request_id === MPV_REQUEST_ID_PATH) {
|
} else if (msg.request_id === MPV_REQUEST_ID_PATH) {
|
||||||
|
this.emit("media-path-change", {
|
||||||
|
path: (msg.data as string) || "",
|
||||||
|
});
|
||||||
this.deps.updateCurrentMediaPath(msg.data);
|
this.deps.updateCurrentMediaPath(msg.data);
|
||||||
} else if (msg.request_id === MPV_REQUEST_ID_AID) {
|
} else if (msg.request_id === MPV_REQUEST_ID_AID) {
|
||||||
this.currentAudioTrackId =
|
this.currentAudioTrackId =
|
||||||
|
|||||||
466
src/main.ts
466
src/main.ts
@@ -265,39 +265,6 @@ process.on("SIGTERM", () => {
|
|||||||
app.quit();
|
app.quit();
|
||||||
});
|
});
|
||||||
|
|
||||||
let yomitanExt: Extension | null = null;
|
|
||||||
let yomitanSettingsWindow: BrowserWindow | null = null;
|
|
||||||
let yomitanParserWindow: BrowserWindow | null = null;
|
|
||||||
let yomitanParserReadyPromise: Promise<void> | null = null;
|
|
||||||
let yomitanParserInitPromise: Promise<boolean> | null = null;
|
|
||||||
let mpvClient: MpvIpcClient | null = null;
|
|
||||||
let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
|
||||||
let currentSubText = "";
|
|
||||||
let currentSubAssText = "";
|
|
||||||
let windowTracker: BaseWindowTracker | null = null;
|
|
||||||
let subtitlePosition: SubtitlePosition | null = null;
|
|
||||||
let currentMediaPath: string | null = null;
|
|
||||||
let currentMediaTitle: string | null = null;
|
|
||||||
let pendingSubtitlePosition: SubtitlePosition | null = null;
|
|
||||||
let mecabTokenizer: MecabTokenizer | null = null;
|
|
||||||
let keybindings: Keybinding[] = [];
|
|
||||||
let subtitleTimingTracker: SubtitleTimingTracker | null = null;
|
|
||||||
let ankiIntegration: AnkiIntegration | null = null;
|
|
||||||
let secondarySubMode: SecondarySubMode = "hover";
|
|
||||||
let lastSecondarySubToggleAtMs = 0;
|
|
||||||
let previousSecondarySubVisibility: boolean | null = null;
|
|
||||||
let mpvSubtitleRenderMetrics: MpvSubtitleRenderMetrics = {
|
|
||||||
...DEFAULT_MPV_SUBTITLE_RENDER_METRICS,
|
|
||||||
};
|
|
||||||
|
|
||||||
let shortcutsRegistered = false;
|
|
||||||
let overlayRuntimeInitialized = false;
|
|
||||||
let fieldGroupingResolver: ((choice: KikuFieldGroupingChoice) => void) | null =
|
|
||||||
null;
|
|
||||||
let fieldGroupingResolverSequence = 0;
|
|
||||||
let runtimeOptionsManager: RuntimeOptionsManager | null = null;
|
|
||||||
let trackerNotReadyWarningShown = false;
|
|
||||||
let overlayDebugVisualizationEnabled = false;
|
|
||||||
const overlayManager = createOverlayManagerService();
|
const overlayManager = createOverlayManagerService();
|
||||||
const overlayContentMeasurementStore = createOverlayContentMeasurementStoreService({
|
const overlayContentMeasurementStore = createOverlayContentMeasurementStoreService({
|
||||||
now: () => Date.now(),
|
now: () => Date.now(),
|
||||||
@@ -310,23 +277,103 @@ type OverlayHostLayer = "visible" | "invisible";
|
|||||||
const restoreVisibleOverlayOnModalClose = new Set<OverlayHostedModal>();
|
const restoreVisibleOverlayOnModalClose = new Set<OverlayHostedModal>();
|
||||||
const overlayModalAutoShownLayer = new Map<OverlayHostedModal, OverlayHostLayer>();
|
const overlayModalAutoShownLayer = new Map<OverlayHostedModal, OverlayHostLayer>();
|
||||||
|
|
||||||
|
interface AppState {
|
||||||
|
yomitanExt: Extension | null;
|
||||||
|
yomitanSettingsWindow: BrowserWindow | null;
|
||||||
|
yomitanParserWindow: BrowserWindow | null;
|
||||||
|
yomitanParserReadyPromise: Promise<void> | null;
|
||||||
|
yomitanParserInitPromise: Promise<boolean> | null;
|
||||||
|
mpvClient: MpvIpcClient | null;
|
||||||
|
reconnectTimer: ReturnType<typeof setTimeout> | null;
|
||||||
|
currentSubText: string;
|
||||||
|
currentSubAssText: string;
|
||||||
|
windowTracker: BaseWindowTracker | null;
|
||||||
|
subtitlePosition: SubtitlePosition | null;
|
||||||
|
currentMediaPath: string | null;
|
||||||
|
currentMediaTitle: string | null;
|
||||||
|
pendingSubtitlePosition: SubtitlePosition | null;
|
||||||
|
mecabTokenizer: MecabTokenizer | null;
|
||||||
|
keybindings: Keybinding[];
|
||||||
|
subtitleTimingTracker: SubtitleTimingTracker | null;
|
||||||
|
ankiIntegration: AnkiIntegration | null;
|
||||||
|
secondarySubMode: SecondarySubMode;
|
||||||
|
lastSecondarySubToggleAtMs: number;
|
||||||
|
previousSecondarySubVisibility: boolean | null;
|
||||||
|
mpvSubtitleRenderMetrics: MpvSubtitleRenderMetrics;
|
||||||
|
shortcutsRegistered: boolean;
|
||||||
|
overlayRuntimeInitialized: boolean;
|
||||||
|
fieldGroupingResolver: ((choice: KikuFieldGroupingChoice) => void) | null;
|
||||||
|
fieldGroupingResolverSequence: number;
|
||||||
|
runtimeOptionsManager: RuntimeOptionsManager | null;
|
||||||
|
trackerNotReadyWarningShown: boolean;
|
||||||
|
overlayDebugVisualizationEnabled: boolean;
|
||||||
|
subsyncInProgress: boolean;
|
||||||
|
initialArgs: CliArgs | null;
|
||||||
|
mpvSocketPath: string;
|
||||||
|
texthookerPort: number;
|
||||||
|
backendOverride: string | null;
|
||||||
|
autoStartOverlay: boolean;
|
||||||
|
texthookerOnlyMode: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const appState: AppState = {
|
||||||
|
yomitanExt: null,
|
||||||
|
yomitanSettingsWindow: null,
|
||||||
|
yomitanParserWindow: null,
|
||||||
|
yomitanParserReadyPromise: null,
|
||||||
|
yomitanParserInitPromise: null,
|
||||||
|
mpvClient: null,
|
||||||
|
reconnectTimer: null,
|
||||||
|
currentSubText: "",
|
||||||
|
currentSubAssText: "",
|
||||||
|
windowTracker: null,
|
||||||
|
subtitlePosition: null,
|
||||||
|
currentMediaPath: null,
|
||||||
|
currentMediaTitle: null,
|
||||||
|
pendingSubtitlePosition: null,
|
||||||
|
mecabTokenizer: null,
|
||||||
|
keybindings: [],
|
||||||
|
subtitleTimingTracker: null,
|
||||||
|
ankiIntegration: null,
|
||||||
|
secondarySubMode: "hover",
|
||||||
|
lastSecondarySubToggleAtMs: 0,
|
||||||
|
previousSecondarySubVisibility: null,
|
||||||
|
mpvSubtitleRenderMetrics: {
|
||||||
|
...DEFAULT_MPV_SUBTITLE_RENDER_METRICS,
|
||||||
|
},
|
||||||
|
shortcutsRegistered: false,
|
||||||
|
overlayRuntimeInitialized: false,
|
||||||
|
fieldGroupingResolver: null,
|
||||||
|
fieldGroupingResolverSequence: 0,
|
||||||
|
runtimeOptionsManager: null,
|
||||||
|
trackerNotReadyWarningShown: false,
|
||||||
|
overlayDebugVisualizationEnabled: false,
|
||||||
|
subsyncInProgress: false,
|
||||||
|
initialArgs: null,
|
||||||
|
mpvSocketPath: getDefaultSocketPath(),
|
||||||
|
texthookerPort: DEFAULT_TEXTHOOKER_PORT,
|
||||||
|
backendOverride: null,
|
||||||
|
autoStartOverlay: false,
|
||||||
|
texthookerOnlyMode: false,
|
||||||
|
};
|
||||||
|
|
||||||
function getFieldGroupingResolver(): ((choice: KikuFieldGroupingChoice) => void) | null {
|
function getFieldGroupingResolver(): ((choice: KikuFieldGroupingChoice) => void) | null {
|
||||||
return fieldGroupingResolver;
|
return appState.fieldGroupingResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setFieldGroupingResolver(
|
function setFieldGroupingResolver(
|
||||||
resolver: ((choice: KikuFieldGroupingChoice) => void) | null,
|
resolver: ((choice: KikuFieldGroupingChoice) => void) | null,
|
||||||
): void {
|
): void {
|
||||||
if (!resolver) {
|
if (!resolver) {
|
||||||
fieldGroupingResolver = null;
|
appState.fieldGroupingResolver = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const sequence = ++fieldGroupingResolverSequence;
|
const sequence = ++appState.fieldGroupingResolverSequence;
|
||||||
const wrappedResolver = (choice: KikuFieldGroupingChoice): void => {
|
const wrappedResolver = (choice: KikuFieldGroupingChoice): void => {
|
||||||
if (sequence !== fieldGroupingResolverSequence) return;
|
if (sequence !== appState.fieldGroupingResolverSequence) return;
|
||||||
resolver(choice);
|
resolver(choice);
|
||||||
};
|
};
|
||||||
fieldGroupingResolver = wrappedResolver;
|
appState.fieldGroupingResolver = wrappedResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fieldGroupingOverlayRuntime = createFieldGroupingOverlayRuntimeService<OverlayHostedModal>({
|
const fieldGroupingOverlayRuntime = createFieldGroupingOverlayRuntimeService<OverlayHostedModal>({
|
||||||
@@ -348,15 +395,15 @@ const createFieldGroupingCallback =
|
|||||||
|
|
||||||
const SUBTITLE_POSITIONS_DIR = path.join(CONFIG_DIR, "subtitle-positions");
|
const SUBTITLE_POSITIONS_DIR = path.join(CONFIG_DIR, "subtitle-positions");
|
||||||
|
|
||||||
function getRuntimeOptionsState(): RuntimeOptionState[] { if (!runtimeOptionsManager) return []; return runtimeOptionsManager.listOptions(); }
|
function getRuntimeOptionsState(): RuntimeOptionState[] { if (!appState.runtimeOptionsManager) return []; return appState.runtimeOptionsManager.listOptions(); }
|
||||||
|
|
||||||
function getOverlayWindows(): BrowserWindow[] {
|
function getOverlayWindows(): BrowserWindow[] {
|
||||||
return overlayManager.getOverlayWindows();
|
return overlayManager.getOverlayWindows();
|
||||||
}
|
}
|
||||||
|
|
||||||
function restorePreviousSecondarySubVisibility(): void {
|
function restorePreviousSecondarySubVisibility(): void {
|
||||||
if (!mpvClient || !mpvClient.connected) return;
|
if (!appState.mpvClient || !appState.mpvClient.connected) return;
|
||||||
mpvClient.restorePreviousSecondarySubVisibility();
|
appState.mpvClient.restorePreviousSecondarySubVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
function broadcastToOverlayWindows(channel: string, ...args: unknown[]): void {
|
function broadcastToOverlayWindows(channel: string, ...args: unknown[]): void {
|
||||||
@@ -445,10 +492,10 @@ function sendToActiveOverlayWindow(
|
|||||||
|
|
||||||
function setOverlayDebugVisualizationEnabled(enabled: boolean): void {
|
function setOverlayDebugVisualizationEnabled(enabled: boolean): void {
|
||||||
setOverlayDebugVisualizationEnabledRuntimeService(
|
setOverlayDebugVisualizationEnabledRuntimeService(
|
||||||
overlayDebugVisualizationEnabled,
|
appState.overlayDebugVisualizationEnabled,
|
||||||
enabled,
|
enabled,
|
||||||
(next) => {
|
(next) => {
|
||||||
overlayDebugVisualizationEnabled = next;
|
appState.overlayDebugVisualizationEnabled = next;
|
||||||
},
|
},
|
||||||
(channel, ...args) => broadcastToOverlayWindows(channel, ...args),
|
(channel, ...args) => broadcastToOverlayWindows(channel, ...args),
|
||||||
);
|
);
|
||||||
@@ -480,7 +527,7 @@ function shouldBindVisibleOverlayToMpvSubVisibility(): boolean {
|
|||||||
function isAutoUpdateEnabledRuntime(): boolean {
|
function isAutoUpdateEnabledRuntime(): boolean {
|
||||||
return isAutoUpdateEnabledRuntimeService(
|
return isAutoUpdateEnabledRuntimeService(
|
||||||
getResolvedConfig(),
|
getResolvedConfig(),
|
||||||
runtimeOptionsManager,
|
appState.runtimeOptionsManager,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,47 +550,47 @@ async function jimakuFetchJson<T>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function loadSubtitlePosition(): SubtitlePosition | null {
|
function loadSubtitlePosition(): SubtitlePosition | null {
|
||||||
subtitlePosition = loadSubtitlePositionService({
|
appState.subtitlePosition = loadSubtitlePositionService({
|
||||||
currentMediaPath,
|
currentMediaPath: appState.currentMediaPath,
|
||||||
fallbackPosition: getResolvedConfig().subtitlePosition,
|
fallbackPosition: getResolvedConfig().subtitlePosition,
|
||||||
subtitlePositionsDir: SUBTITLE_POSITIONS_DIR,
|
subtitlePositionsDir: SUBTITLE_POSITIONS_DIR,
|
||||||
});
|
});
|
||||||
return subtitlePosition;
|
return appState.subtitlePosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveSubtitlePosition(position: SubtitlePosition): void {
|
function saveSubtitlePosition(position: SubtitlePosition): void {
|
||||||
subtitlePosition = position;
|
appState.subtitlePosition = position;
|
||||||
saveSubtitlePositionService({
|
saveSubtitlePositionService({
|
||||||
position,
|
position,
|
||||||
currentMediaPath,
|
currentMediaPath: appState.currentMediaPath,
|
||||||
subtitlePositionsDir: SUBTITLE_POSITIONS_DIR,
|
subtitlePositionsDir: SUBTITLE_POSITIONS_DIR,
|
||||||
onQueuePending: (queued) => {
|
onQueuePending: (queued) => {
|
||||||
pendingSubtitlePosition = queued;
|
appState.pendingSubtitlePosition = queued;
|
||||||
},
|
},
|
||||||
onPersisted: () => {
|
onPersisted: () => {
|
||||||
pendingSubtitlePosition = null;
|
appState.pendingSubtitlePosition = null;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateCurrentMediaPath(mediaPath: unknown): void {
|
function updateCurrentMediaPath(mediaPath: unknown): void {
|
||||||
if (typeof mediaPath !== "string" || !isRemoteMediaPath(mediaPath)) {
|
if (typeof mediaPath !== "string" || !isRemoteMediaPath(mediaPath)) {
|
||||||
currentMediaTitle = null;
|
appState.currentMediaTitle = null;
|
||||||
}
|
}
|
||||||
updateCurrentMediaPathService({
|
updateCurrentMediaPathService({
|
||||||
mediaPath,
|
mediaPath,
|
||||||
currentMediaPath,
|
currentMediaPath: appState.currentMediaPath,
|
||||||
pendingSubtitlePosition,
|
pendingSubtitlePosition: appState.pendingSubtitlePosition,
|
||||||
subtitlePositionsDir: SUBTITLE_POSITIONS_DIR,
|
subtitlePositionsDir: SUBTITLE_POSITIONS_DIR,
|
||||||
loadSubtitlePosition: () => loadSubtitlePosition(),
|
loadSubtitlePosition: () => loadSubtitlePosition(),
|
||||||
setCurrentMediaPath: (nextPath) => {
|
setCurrentMediaPath: (nextPath) => {
|
||||||
currentMediaPath = nextPath;
|
appState.currentMediaPath = nextPath;
|
||||||
},
|
},
|
||||||
clearPendingSubtitlePosition: () => {
|
clearPendingSubtitlePosition: () => {
|
||||||
pendingSubtitlePosition = null;
|
appState.pendingSubtitlePosition = null;
|
||||||
},
|
},
|
||||||
setSubtitlePosition: (position) => {
|
setSubtitlePosition: (position) => {
|
||||||
subtitlePosition = position;
|
appState.subtitlePosition = position;
|
||||||
},
|
},
|
||||||
broadcastSubtitlePosition: (position) => {
|
broadcastSubtitlePosition: (position) => {
|
||||||
broadcastToOverlayWindows("subtitle-position:set", position);
|
broadcastToOverlayWindows("subtitle-position:set", position);
|
||||||
@@ -554,26 +601,18 @@ function updateCurrentMediaPath(mediaPath: unknown): void {
|
|||||||
function updateCurrentMediaTitle(mediaTitle: unknown): void {
|
function updateCurrentMediaTitle(mediaTitle: unknown): void {
|
||||||
if (typeof mediaTitle === "string") {
|
if (typeof mediaTitle === "string") {
|
||||||
const sanitized = mediaTitle.trim();
|
const sanitized = mediaTitle.trim();
|
||||||
currentMediaTitle = sanitized.length > 0 ? sanitized : null;
|
appState.currentMediaTitle = sanitized.length > 0 ? sanitized : null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
currentMediaTitle = null;
|
appState.currentMediaTitle = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveMediaPathForJimaku(mediaPath: string | null): string | null {
|
function resolveMediaPathForJimaku(mediaPath: string | null): string | null {
|
||||||
return mediaPath && isRemoteMediaPath(mediaPath) && currentMediaTitle
|
return mediaPath && isRemoteMediaPath(mediaPath) && appState.currentMediaTitle
|
||||||
? currentMediaTitle
|
? appState.currentMediaTitle
|
||||||
: mediaPath;
|
: mediaPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
let subsyncInProgress = false;
|
|
||||||
let initialArgs: CliArgs;
|
|
||||||
let mpvSocketPath = getDefaultSocketPath();
|
|
||||||
let texthookerPort = DEFAULT_TEXTHOOKER_PORT;
|
|
||||||
let backendOverride: string | null = null;
|
|
||||||
let autoStartOverlay = false;
|
|
||||||
let texthookerOnlyMode = false;
|
|
||||||
|
|
||||||
const startupState = runStartupBootstrapRuntimeService({
|
const startupState = runStartupBootstrapRuntimeService({
|
||||||
argv: process.argv,
|
argv: process.argv,
|
||||||
parseArgs: (argv) => parseArgs(argv),
|
parseArgs: (argv) => parseArgs(argv),
|
||||||
@@ -624,31 +663,31 @@ const startupState = runStartupBootstrapRuntimeService({
|
|||||||
await runAppReadyRuntimeService({
|
await runAppReadyRuntimeService({
|
||||||
loadSubtitlePosition: () => loadSubtitlePosition(),
|
loadSubtitlePosition: () => loadSubtitlePosition(),
|
||||||
resolveKeybindings: () => {
|
resolveKeybindings: () => {
|
||||||
keybindings = resolveKeybindings(getResolvedConfig(), DEFAULT_KEYBINDINGS);
|
appState.keybindings = resolveKeybindings(getResolvedConfig(), DEFAULT_KEYBINDINGS);
|
||||||
},
|
},
|
||||||
createMpvClient: () => {
|
createMpvClient: () => {
|
||||||
mpvClient = new MpvIpcClient(
|
appState.mpvClient = new MpvIpcClient(
|
||||||
mpvSocketPath,
|
appState.mpvSocketPath,
|
||||||
{
|
{
|
||||||
getResolvedConfig: () => getResolvedConfig(),
|
getResolvedConfig: () => getResolvedConfig(),
|
||||||
autoStartOverlay,
|
autoStartOverlay: appState.autoStartOverlay,
|
||||||
setOverlayVisible: (visible) => setOverlayVisible(visible),
|
setOverlayVisible: (visible) => setOverlayVisible(visible),
|
||||||
shouldBindVisibleOverlayToMpvSubVisibility: () =>
|
shouldBindVisibleOverlayToMpvSubVisibility: () =>
|
||||||
shouldBindVisibleOverlayToMpvSubVisibility(),
|
shouldBindVisibleOverlayToMpvSubVisibility(),
|
||||||
isVisibleOverlayVisible: () =>
|
isVisibleOverlayVisible: () =>
|
||||||
overlayManager.getVisibleOverlayVisible(),
|
overlayManager.getVisibleOverlayVisible(),
|
||||||
getReconnectTimer: () => reconnectTimer,
|
getReconnectTimer: () => appState.reconnectTimer,
|
||||||
setReconnectTimer: (timer) => {
|
setReconnectTimer: (timer) => {
|
||||||
reconnectTimer = timer;
|
appState.reconnectTimer = timer;
|
||||||
},
|
},
|
||||||
getCurrentSubText: () => currentSubText,
|
getCurrentSubText: () => appState.currentSubText,
|
||||||
setCurrentSubText: (text) => {
|
setCurrentSubText: (text) => {
|
||||||
currentSubText = text;
|
appState.currentSubText = text;
|
||||||
},
|
},
|
||||||
setCurrentSubAssText: (text) => {
|
setCurrentSubAssText: (text) => {
|
||||||
currentSubAssText = text;
|
appState.currentSubAssText = text;
|
||||||
},
|
},
|
||||||
getSubtitleTimingTracker: () => subtitleTimingTracker,
|
getSubtitleTimingTracker: () => appState.subtitleTimingTracker,
|
||||||
subtitleWsBroadcast: (text) => {
|
subtitleWsBroadcast: (text) => {
|
||||||
subtitleWsService.broadcast(text);
|
subtitleWsService.broadcast(text);
|
||||||
},
|
},
|
||||||
@@ -657,26 +696,21 @@ const startupState = runStartupBootstrapRuntimeService({
|
|||||||
broadcastToOverlayWindows: (channel, ...channelArgs) => {
|
broadcastToOverlayWindows: (channel, ...channelArgs) => {
|
||||||
broadcastToOverlayWindows(channel, ...channelArgs);
|
broadcastToOverlayWindows(channel, ...channelArgs);
|
||||||
},
|
},
|
||||||
updateCurrentMediaPath: (mediaPath) => {
|
|
||||||
updateCurrentMediaPath(mediaPath);
|
|
||||||
},
|
|
||||||
updateCurrentMediaTitle: (mediaTitle) => {
|
|
||||||
updateCurrentMediaTitle(mediaTitle);
|
|
||||||
},
|
|
||||||
updateMpvSubtitleRenderMetrics: (patch) => {
|
updateMpvSubtitleRenderMetrics: (patch) => {
|
||||||
updateMpvSubtitleRenderMetrics(patch);
|
updateMpvSubtitleRenderMetrics(patch);
|
||||||
},
|
},
|
||||||
getMpvSubtitleRenderMetrics: () => mpvSubtitleRenderMetrics,
|
getMpvSubtitleRenderMetrics: () => appState.mpvSubtitleRenderMetrics,
|
||||||
getPreviousSecondarySubVisibility: () =>
|
getPreviousSecondarySubVisibility: () =>
|
||||||
previousSecondarySubVisibility,
|
appState.previousSecondarySubVisibility,
|
||||||
setPreviousSecondarySubVisibility: (value) => {
|
setPreviousSecondarySubVisibility: (value) => {
|
||||||
previousSecondarySubVisibility = value;
|
appState.previousSecondarySubVisibility = value;
|
||||||
},
|
},
|
||||||
showMpvOsd: (text) => {
|
showMpvOsd: (text) => {
|
||||||
showMpvOsd(text);
|
showMpvOsd(text);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
bindMpvClientEventHandlers(appState.mpvClient);
|
||||||
},
|
},
|
||||||
reloadConfig: () => {
|
reloadConfig: () => {
|
||||||
configService.reloadConfig();
|
configService.reloadConfig();
|
||||||
@@ -686,12 +720,12 @@ const startupState = runStartupBootstrapRuntimeService({
|
|||||||
getConfigWarnings: () => configService.getWarnings(),
|
getConfigWarnings: () => configService.getWarnings(),
|
||||||
logConfigWarning: (warning) => appLogger.logConfigWarning(warning),
|
logConfigWarning: (warning) => appLogger.logConfigWarning(warning),
|
||||||
initRuntimeOptionsManager: () => {
|
initRuntimeOptionsManager: () => {
|
||||||
runtimeOptionsManager = new RuntimeOptionsManager(
|
appState.runtimeOptionsManager = new RuntimeOptionsManager(
|
||||||
() => configService.getConfig().ankiConnect,
|
() => configService.getConfig().ankiConnect,
|
||||||
{
|
{
|
||||||
applyAnkiPatch: (patch) => {
|
applyAnkiPatch: (patch) => {
|
||||||
if (ankiIntegration) {
|
if (appState.ankiIntegration) {
|
||||||
ankiIntegration.applyRuntimeConfigPatch(patch);
|
appState.ankiIntegration.applyRuntimeConfigPatch(patch);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onOptionsChanged: () => {
|
onOptionsChanged: () => {
|
||||||
@@ -702,28 +736,28 @@ const startupState = runStartupBootstrapRuntimeService({
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
setSecondarySubMode: (mode) => {
|
setSecondarySubMode: (mode) => {
|
||||||
secondarySubMode = mode;
|
appState.secondarySubMode = mode;
|
||||||
},
|
},
|
||||||
defaultSecondarySubMode: "hover",
|
defaultSecondarySubMode: "hover",
|
||||||
defaultWebsocketPort: DEFAULT_CONFIG.websocket.port,
|
defaultWebsocketPort: DEFAULT_CONFIG.websocket.port,
|
||||||
hasMpvWebsocketPlugin: () => hasMpvWebsocketPlugin(),
|
hasMpvWebsocketPlugin: () => hasMpvWebsocketPlugin(),
|
||||||
startSubtitleWebsocket: (port) => {
|
startSubtitleWebsocket: (port) => {
|
||||||
subtitleWsService.start(port, () => currentSubText);
|
subtitleWsService.start(port, () => appState.currentSubText);
|
||||||
},
|
},
|
||||||
log: (message) => appLogger.logInfo(message),
|
log: (message) => appLogger.logInfo(message),
|
||||||
createMecabTokenizerAndCheck: async () => {
|
createMecabTokenizerAndCheck: async () => {
|
||||||
const tokenizer = new MecabTokenizer();
|
const tokenizer = new MecabTokenizer();
|
||||||
mecabTokenizer = tokenizer;
|
appState.mecabTokenizer = tokenizer;
|
||||||
await tokenizer.checkAvailability();
|
await tokenizer.checkAvailability();
|
||||||
},
|
},
|
||||||
createSubtitleTimingTracker: () => {
|
createSubtitleTimingTracker: () => {
|
||||||
const tracker = new SubtitleTimingTracker();
|
const tracker = new SubtitleTimingTracker();
|
||||||
subtitleTimingTracker = tracker;
|
appState.subtitleTimingTracker = tracker;
|
||||||
},
|
},
|
||||||
loadYomitanExtension: async () => {
|
loadYomitanExtension: async () => {
|
||||||
await loadYomitanExtension();
|
await loadYomitanExtension();
|
||||||
},
|
},
|
||||||
texthookerOnlyMode,
|
texthookerOnlyMode: appState.texthookerOnlyMode,
|
||||||
shouldAutoInitializeOverlayRuntimeFromConfig: () =>
|
shouldAutoInitializeOverlayRuntimeFromConfig: () =>
|
||||||
shouldAutoInitializeOverlayRuntimeFromConfig(),
|
shouldAutoInitializeOverlayRuntimeFromConfig(),
|
||||||
initializeOverlayRuntime: () => initializeOverlayRuntime(),
|
initializeOverlayRuntime: () => initializeOverlayRuntime(),
|
||||||
@@ -735,30 +769,30 @@ const startupState = runStartupBootstrapRuntimeService({
|
|||||||
globalShortcut.unregisterAll();
|
globalShortcut.unregisterAll();
|
||||||
subtitleWsService.stop();
|
subtitleWsService.stop();
|
||||||
texthookerService.stop();
|
texthookerService.stop();
|
||||||
if (yomitanParserWindow && !yomitanParserWindow.isDestroyed()) {
|
if (appState.yomitanParserWindow && !appState.yomitanParserWindow.isDestroyed()) {
|
||||||
yomitanParserWindow.destroy();
|
appState.yomitanParserWindow.destroy();
|
||||||
}
|
}
|
||||||
yomitanParserWindow = null;
|
appState.yomitanParserWindow = null;
|
||||||
yomitanParserReadyPromise = null;
|
appState.yomitanParserReadyPromise = null;
|
||||||
yomitanParserInitPromise = null;
|
appState.yomitanParserInitPromise = null;
|
||||||
if (windowTracker) {
|
if (appState.windowTracker) {
|
||||||
windowTracker.stop();
|
appState.windowTracker.stop();
|
||||||
}
|
}
|
||||||
if (mpvClient && mpvClient.socket) {
|
if (appState.mpvClient && appState.mpvClient.socket) {
|
||||||
mpvClient.socket.destroy();
|
appState.mpvClient.socket.destroy();
|
||||||
}
|
}
|
||||||
if (reconnectTimer) {
|
if (appState.reconnectTimer) {
|
||||||
clearTimeout(reconnectTimer);
|
clearTimeout(appState.reconnectTimer);
|
||||||
}
|
}
|
||||||
if (subtitleTimingTracker) {
|
if (appState.subtitleTimingTracker) {
|
||||||
subtitleTimingTracker.destroy();
|
appState.subtitleTimingTracker.destroy();
|
||||||
}
|
}
|
||||||
if (ankiIntegration) {
|
if (appState.ankiIntegration) {
|
||||||
ankiIntegration.destroy();
|
appState.ankiIntegration.destroy();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
shouldRestoreWindowsOnActivate: () =>
|
shouldRestoreWindowsOnActivate: () =>
|
||||||
overlayRuntimeInitialized && BrowserWindow.getAllWindows().length === 0,
|
appState.overlayRuntimeInitialized && BrowserWindow.getAllWindows().length === 0,
|
||||||
restoreWindowsOnActivate: () => {
|
restoreWindowsOnActivate: () => {
|
||||||
createMainWindow();
|
createMainWindow();
|
||||||
createInvisibleWindow();
|
createInvisibleWindow();
|
||||||
@@ -769,12 +803,12 @@ const startupState = runStartupBootstrapRuntimeService({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
initialArgs = startupState.initialArgs;
|
appState.initialArgs = startupState.initialArgs;
|
||||||
mpvSocketPath = startupState.mpvSocketPath;
|
appState.mpvSocketPath = startupState.mpvSocketPath;
|
||||||
texthookerPort = startupState.texthookerPort;
|
appState.texthookerPort = startupState.texthookerPort;
|
||||||
backendOverride = startupState.backendOverride;
|
appState.backendOverride = startupState.backendOverride;
|
||||||
autoStartOverlay = startupState.autoStartOverlay;
|
appState.autoStartOverlay = startupState.autoStartOverlay;
|
||||||
texthookerOnlyMode = startupState.texthookerOnlyMode;
|
appState.texthookerOnlyMode = startupState.texthookerOnlyMode;
|
||||||
|
|
||||||
function handleCliCommand(
|
function handleCliCommand(
|
||||||
args: CliArgs,
|
args: CliArgs,
|
||||||
@@ -782,18 +816,18 @@ function handleCliCommand(
|
|||||||
): void {
|
): void {
|
||||||
const deps = createCliCommandDepsRuntimeService({
|
const deps = createCliCommandDepsRuntimeService({
|
||||||
mpv: {
|
mpv: {
|
||||||
getSocketPath: () => mpvSocketPath,
|
getSocketPath: () => appState.mpvSocketPath,
|
||||||
setSocketPath: (socketPath) => {
|
setSocketPath: (socketPath) => {
|
||||||
mpvSocketPath = socketPath;
|
appState.mpvSocketPath = socketPath;
|
||||||
},
|
},
|
||||||
getClient: () => mpvClient,
|
getClient: () => appState.mpvClient,
|
||||||
showOsd: (text) => showMpvOsd(text),
|
showOsd: (text) => showMpvOsd(text),
|
||||||
},
|
},
|
||||||
texthooker: {
|
texthooker: {
|
||||||
service: texthookerService,
|
service: texthookerService,
|
||||||
getPort: () => texthookerPort,
|
getPort: () => appState.texthookerPort,
|
||||||
setPort: (port) => {
|
setPort: (port) => {
|
||||||
texthookerPort = port;
|
appState.texthookerPort = port;
|
||||||
},
|
},
|
||||||
shouldOpenBrowser: () => getResolvedConfig().texthooker?.openBrowser !== false,
|
shouldOpenBrowser: () => getResolvedConfig().texthooker?.openBrowser !== false,
|
||||||
openInBrowser: (url) => {
|
openInBrowser: (url) => {
|
||||||
@@ -803,7 +837,7 @@ function handleCliCommand(
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
overlay: {
|
overlay: {
|
||||||
isInitialized: () => overlayRuntimeInitialized,
|
isInitialized: () => appState.overlayRuntimeInitialized,
|
||||||
initialize: () => initializeOverlayRuntime(),
|
initialize: () => initializeOverlayRuntime(),
|
||||||
toggleVisible: () => toggleVisibleOverlay(),
|
toggleVisible: () => toggleVisibleOverlay(),
|
||||||
toggleInvisible: () => toggleInvisibleOverlay(),
|
toggleInvisible: () => toggleInvisibleOverlay(),
|
||||||
@@ -847,21 +881,37 @@ function handleCliCommand(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleInitialArgs(): void {
|
function handleInitialArgs(): void {
|
||||||
handleCliCommand(initialArgs, "initial");
|
if (!appState.initialArgs) return;
|
||||||
|
handleCliCommand(appState.initialArgs, "initial");
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindMpvClientEventHandlers(mpvClient: MpvIpcClient): void {
|
||||||
|
mpvClient.on("media-path-change", ({ path }) => {
|
||||||
|
updateCurrentMediaPath(path);
|
||||||
|
});
|
||||||
|
mpvClient.on("media-title-change", ({ title }) => {
|
||||||
|
updateCurrentMediaTitle(title);
|
||||||
|
});
|
||||||
|
mpvClient.on("subtitle-ass-change", ({ text }) => {
|
||||||
|
appState.currentSubAssText = text;
|
||||||
|
});
|
||||||
|
mpvClient.on("subtitle-change", ({ text }) => {
|
||||||
|
appState.currentSubText = text;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateMpvSubtitleRenderMetrics(
|
function updateMpvSubtitleRenderMetrics(
|
||||||
patch: Partial<MpvSubtitleRenderMetrics>,
|
patch: Partial<MpvSubtitleRenderMetrics>,
|
||||||
): void {
|
): void {
|
||||||
const { next, changed } = applyMpvSubtitleRenderMetricsPatchService(
|
const { next, changed } = applyMpvSubtitleRenderMetricsPatchService(
|
||||||
mpvSubtitleRenderMetrics,
|
appState.mpvSubtitleRenderMetrics,
|
||||||
patch,
|
patch,
|
||||||
);
|
);
|
||||||
if (!changed) return;
|
if (!changed) return;
|
||||||
mpvSubtitleRenderMetrics = next;
|
appState.mpvSubtitleRenderMetrics = next;
|
||||||
broadcastToOverlayWindows(
|
broadcastToOverlayWindows(
|
||||||
"mpv-subtitle-render-metrics:set",
|
"mpv-subtitle-render-metrics:set",
|
||||||
mpvSubtitleRenderMetrics,
|
appState.mpvSubtitleRenderMetrics,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -869,20 +919,20 @@ async function tokenizeSubtitle(text: string): Promise<SubtitleData> {
|
|||||||
return tokenizeSubtitleService(
|
return tokenizeSubtitleService(
|
||||||
text,
|
text,
|
||||||
createTokenizerDepsRuntimeService({
|
createTokenizerDepsRuntimeService({
|
||||||
getYomitanExt: () => yomitanExt,
|
getYomitanExt: () => appState.yomitanExt,
|
||||||
getYomitanParserWindow: () => yomitanParserWindow,
|
getYomitanParserWindow: () => appState.yomitanParserWindow,
|
||||||
setYomitanParserWindow: (window) => {
|
setYomitanParserWindow: (window) => {
|
||||||
yomitanParserWindow = window;
|
appState.yomitanParserWindow = window;
|
||||||
},
|
},
|
||||||
getYomitanParserReadyPromise: () => yomitanParserReadyPromise,
|
getYomitanParserReadyPromise: () => appState.yomitanParserReadyPromise,
|
||||||
setYomitanParserReadyPromise: (promise) => {
|
setYomitanParserReadyPromise: (promise) => {
|
||||||
yomitanParserReadyPromise = promise;
|
appState.yomitanParserReadyPromise = promise;
|
||||||
},
|
},
|
||||||
getYomitanParserInitPromise: () => yomitanParserInitPromise,
|
getYomitanParserInitPromise: () => appState.yomitanParserInitPromise,
|
||||||
setYomitanParserInitPromise: (promise) => {
|
setYomitanParserInitPromise: (promise) => {
|
||||||
yomitanParserInitPromise = promise;
|
appState.yomitanParserInitPromise = promise;
|
||||||
},
|
},
|
||||||
getMecabTokenizer: () => mecabTokenizer,
|
getMecabTokenizer: () => appState.mecabTokenizer,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -912,18 +962,18 @@ function enforceOverlayLayerOrder(): void {
|
|||||||
async function loadYomitanExtension(): Promise<Extension | null> {
|
async function loadYomitanExtension(): Promise<Extension | null> {
|
||||||
return loadYomitanExtensionService({
|
return loadYomitanExtensionService({
|
||||||
userDataPath: USER_DATA_PATH,
|
userDataPath: USER_DATA_PATH,
|
||||||
getYomitanParserWindow: () => yomitanParserWindow,
|
getYomitanParserWindow: () => appState.yomitanParserWindow,
|
||||||
setYomitanParserWindow: (window) => {
|
setYomitanParserWindow: (window) => {
|
||||||
yomitanParserWindow = window;
|
appState.yomitanParserWindow = window;
|
||||||
},
|
},
|
||||||
setYomitanParserReadyPromise: (promise) => {
|
setYomitanParserReadyPromise: (promise) => {
|
||||||
yomitanParserReadyPromise = promise;
|
appState.yomitanParserReadyPromise = promise;
|
||||||
},
|
},
|
||||||
setYomitanParserInitPromise: (promise) => {
|
setYomitanParserInitPromise: (promise) => {
|
||||||
yomitanParserInitPromise = promise;
|
appState.yomitanParserInitPromise = promise;
|
||||||
},
|
},
|
||||||
setYomitanExtension: (extension) => {
|
setYomitanExtension: (extension) => {
|
||||||
yomitanExt = extension;
|
appState.yomitanExt = extension;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -933,7 +983,7 @@ function createOverlayWindow(kind: "visible" | "invisible"): BrowserWindow {
|
|||||||
kind,
|
kind,
|
||||||
{
|
{
|
||||||
isDev,
|
isDev,
|
||||||
overlayDebugVisualizationEnabled,
|
overlayDebugVisualizationEnabled: appState.overlayDebugVisualizationEnabled,
|
||||||
ensureOverlayWindowLevel: (window) => ensureOverlayWindowLevel(window),
|
ensureOverlayWindowLevel: (window) => ensureOverlayWindowLevel(window),
|
||||||
onRuntimeOptionsChanged: () => broadcastRuntimeOptionsChanged(),
|
onRuntimeOptionsChanged: () => broadcastRuntimeOptionsChanged(),
|
||||||
setOverlayDebugVisualizationEnabled: (enabled) =>
|
setOverlayDebugVisualizationEnabled: (enabled) =>
|
||||||
@@ -967,12 +1017,12 @@ function createInvisibleWindow(): BrowserWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function initializeOverlayRuntime(): void {
|
function initializeOverlayRuntime(): void {
|
||||||
if (overlayRuntimeInitialized) {
|
if (appState.overlayRuntimeInitialized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const result = initializeOverlayRuntimeService(
|
const result = initializeOverlayRuntimeService(
|
||||||
{
|
{
|
||||||
backendOverride,
|
backendOverride: appState.backendOverride,
|
||||||
getInitialInvisibleOverlayVisibility: () =>
|
getInitialInvisibleOverlayVisibility: () =>
|
||||||
getInitialInvisibleOverlayVisibility(),
|
getInitialInvisibleOverlayVisibility(),
|
||||||
createMainWindow: () => {
|
createMainWindow: () => {
|
||||||
@@ -1004,30 +1054,30 @@ function initializeOverlayRuntime(): void {
|
|||||||
syncOverlayShortcuts();
|
syncOverlayShortcuts();
|
||||||
},
|
},
|
||||||
setWindowTracker: (tracker) => {
|
setWindowTracker: (tracker) => {
|
||||||
windowTracker = tracker;
|
appState.windowTracker = tracker;
|
||||||
},
|
},
|
||||||
getResolvedConfig: () => getResolvedConfig(),
|
getResolvedConfig: () => getResolvedConfig(),
|
||||||
getSubtitleTimingTracker: () => subtitleTimingTracker,
|
getSubtitleTimingTracker: () => appState.subtitleTimingTracker,
|
||||||
getMpvClient: () => mpvClient,
|
getMpvClient: () => appState.mpvClient,
|
||||||
getRuntimeOptionsManager: () => runtimeOptionsManager,
|
getRuntimeOptionsManager: () => appState.runtimeOptionsManager,
|
||||||
setAnkiIntegration: (integration) => {
|
setAnkiIntegration: (integration) => {
|
||||||
ankiIntegration = integration as AnkiIntegration | null;
|
appState.ankiIntegration = integration as AnkiIntegration | null;
|
||||||
},
|
},
|
||||||
showDesktopNotification,
|
showDesktopNotification,
|
||||||
createFieldGroupingCallback: () => createFieldGroupingCallback(),
|
createFieldGroupingCallback: () => createFieldGroupingCallback(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
overlayManager.setInvisibleOverlayVisible(result.invisibleOverlayVisible);
|
overlayManager.setInvisibleOverlayVisible(result.invisibleOverlayVisible);
|
||||||
overlayRuntimeInitialized = true;
|
appState.overlayRuntimeInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function openYomitanSettings(): void {
|
function openYomitanSettings(): void {
|
||||||
openYomitanSettingsWindow(
|
openYomitanSettingsWindow(
|
||||||
{
|
{
|
||||||
yomitanExt,
|
yomitanExt: appState.yomitanExt,
|
||||||
getExistingWindow: () => yomitanSettingsWindow,
|
getExistingWindow: () => appState.yomitanSettingsWindow,
|
||||||
setWindow: (window: BrowserWindow | null) => {
|
setWindow: (window: BrowserWindow | null) => {
|
||||||
yomitanSettingsWindow = window;
|
appState.yomitanSettingsWindow = window;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -1090,13 +1140,13 @@ function tryHandleOverlayShortcutLocalFallback(input: Electron.Input): boolean {
|
|||||||
function cycleSecondarySubMode(): void {
|
function cycleSecondarySubMode(): void {
|
||||||
cycleSecondarySubModeService(
|
cycleSecondarySubModeService(
|
||||||
{
|
{
|
||||||
getSecondarySubMode: () => secondarySubMode,
|
getSecondarySubMode: () => appState.secondarySubMode,
|
||||||
setSecondarySubMode: (mode: SecondarySubMode) => {
|
setSecondarySubMode: (mode: SecondarySubMode) => {
|
||||||
secondarySubMode = mode;
|
appState.secondarySubMode = mode;
|
||||||
},
|
},
|
||||||
getLastSecondarySubToggleAtMs: () => lastSecondarySubToggleAtMs,
|
getLastSecondarySubToggleAtMs: () => appState.lastSecondarySubToggleAtMs,
|
||||||
setLastSecondarySubToggleAtMs: (timestampMs: number) => {
|
setLastSecondarySubToggleAtMs: (timestampMs: number) => {
|
||||||
lastSecondarySubToggleAtMs = timestampMs;
|
appState.lastSecondarySubToggleAtMs = timestampMs;
|
||||||
},
|
},
|
||||||
broadcastSecondarySubMode: (mode: SecondarySubMode) => {
|
broadcastSecondarySubMode: (mode: SecondarySubMode) => {
|
||||||
broadcastToOverlayWindows("secondary-subtitle:mode", mode);
|
broadcastToOverlayWindows("secondary-subtitle:mode", mode);
|
||||||
@@ -1109,7 +1159,7 @@ function cycleSecondarySubMode(): void {
|
|||||||
function showMpvOsd(text: string): void {
|
function showMpvOsd(text: string): void {
|
||||||
appendToMpvLog(`[OSD] ${text}`);
|
appendToMpvLog(`[OSD] ${text}`);
|
||||||
showMpvOsdRuntimeService(
|
showMpvOsdRuntimeService(
|
||||||
mpvClient,
|
appState.mpvClient,
|
||||||
text,
|
text,
|
||||||
(line) => {
|
(line) => {
|
||||||
console.log(line);
|
console.log(line);
|
||||||
@@ -1141,11 +1191,11 @@ const mineSentenceSession = numericShortcutRuntime.createSession();
|
|||||||
|
|
||||||
function getSubsyncRuntimeDeps() {
|
function getSubsyncRuntimeDeps() {
|
||||||
return {
|
return {
|
||||||
getMpvClient: () => mpvClient,
|
getMpvClient: () => appState.mpvClient,
|
||||||
getResolvedSubsyncConfig: () => getSubsyncConfig(getResolvedConfig().subsync),
|
getResolvedSubsyncConfig: () => getSubsyncConfig(getResolvedConfig().subsync),
|
||||||
isSubsyncInProgress: () => subsyncInProgress,
|
isSubsyncInProgress: () => appState.subsyncInProgress,
|
||||||
setSubsyncInProgress: (inProgress: boolean) => {
|
setSubsyncInProgress: (inProgress: boolean) => {
|
||||||
subsyncInProgress = inProgress;
|
appState.subsyncInProgress = inProgress;
|
||||||
},
|
},
|
||||||
showMpvOsd: (text: string) => showMpvOsd(text),
|
showMpvOsd: (text: string) => showMpvOsd(text),
|
||||||
openManualPicker: (payload: SubsyncManualPayload) => {
|
openManualPicker: (payload: SubsyncManualPayload) => {
|
||||||
@@ -1180,7 +1230,7 @@ function handleMultiCopyDigit(count: number): void {
|
|||||||
handleMultiCopyDigitService(
|
handleMultiCopyDigitService(
|
||||||
count,
|
count,
|
||||||
{
|
{
|
||||||
subtitleTimingTracker,
|
subtitleTimingTracker: appState.subtitleTimingTracker,
|
||||||
writeClipboardText: (text) => clipboard.writeText(text),
|
writeClipboardText: (text) => clipboard.writeText(text),
|
||||||
showMpvOsd: (text) => showMpvOsd(text),
|
showMpvOsd: (text) => showMpvOsd(text),
|
||||||
},
|
},
|
||||||
@@ -1190,7 +1240,7 @@ function handleMultiCopyDigit(count: number): void {
|
|||||||
function copyCurrentSubtitle(): void {
|
function copyCurrentSubtitle(): void {
|
||||||
copyCurrentSubtitleService(
|
copyCurrentSubtitleService(
|
||||||
{
|
{
|
||||||
subtitleTimingTracker,
|
subtitleTimingTracker: appState.subtitleTimingTracker,
|
||||||
writeClipboardText: (text) => clipboard.writeText(text),
|
writeClipboardText: (text) => clipboard.writeText(text),
|
||||||
showMpvOsd: (text) => showMpvOsd(text),
|
showMpvOsd: (text) => showMpvOsd(text),
|
||||||
},
|
},
|
||||||
@@ -1200,7 +1250,7 @@ function copyCurrentSubtitle(): void {
|
|||||||
async function updateLastCardFromClipboard(): Promise<void> {
|
async function updateLastCardFromClipboard(): Promise<void> {
|
||||||
await updateLastCardFromClipboardService(
|
await updateLastCardFromClipboardService(
|
||||||
{
|
{
|
||||||
ankiIntegration,
|
ankiIntegration: appState.ankiIntegration,
|
||||||
readClipboardText: () => clipboard.readText(),
|
readClipboardText: () => clipboard.readText(),
|
||||||
showMpvOsd: (text) => showMpvOsd(text),
|
showMpvOsd: (text) => showMpvOsd(text),
|
||||||
},
|
},
|
||||||
@@ -1210,7 +1260,7 @@ async function updateLastCardFromClipboard(): Promise<void> {
|
|||||||
async function triggerFieldGrouping(): Promise<void> {
|
async function triggerFieldGrouping(): Promise<void> {
|
||||||
await triggerFieldGroupingService(
|
await triggerFieldGroupingService(
|
||||||
{
|
{
|
||||||
ankiIntegration,
|
ankiIntegration: appState.ankiIntegration,
|
||||||
showMpvOsd: (text) => showMpvOsd(text),
|
showMpvOsd: (text) => showMpvOsd(text),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -1219,7 +1269,7 @@ async function triggerFieldGrouping(): Promise<void> {
|
|||||||
async function markLastCardAsAudioCard(): Promise<void> {
|
async function markLastCardAsAudioCard(): Promise<void> {
|
||||||
await markLastCardAsAudioCardService(
|
await markLastCardAsAudioCardService(
|
||||||
{
|
{
|
||||||
ankiIntegration,
|
ankiIntegration: appState.ankiIntegration,
|
||||||
showMpvOsd: (text) => showMpvOsd(text),
|
showMpvOsd: (text) => showMpvOsd(text),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -1228,8 +1278,8 @@ async function markLastCardAsAudioCard(): Promise<void> {
|
|||||||
async function mineSentenceCard(): Promise<void> {
|
async function mineSentenceCard(): Promise<void> {
|
||||||
await mineSentenceCardService(
|
await mineSentenceCardService(
|
||||||
{
|
{
|
||||||
ankiIntegration,
|
ankiIntegration: appState.ankiIntegration,
|
||||||
mpvClient,
|
mpvClient: appState.mpvClient,
|
||||||
showMpvOsd: (text) => showMpvOsd(text),
|
showMpvOsd: (text) => showMpvOsd(text),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -1255,10 +1305,10 @@ function handleMineSentenceDigit(count: number): void {
|
|||||||
handleMineSentenceDigitService(
|
handleMineSentenceDigitService(
|
||||||
count,
|
count,
|
||||||
{
|
{
|
||||||
subtitleTimingTracker,
|
subtitleTimingTracker: appState.subtitleTimingTracker,
|
||||||
ankiIntegration,
|
ankiIntegration: appState.ankiIntegration,
|
||||||
getCurrentSecondarySubText: () =>
|
getCurrentSecondarySubText: () =>
|
||||||
mpvClient?.currentSecondarySubText || undefined,
|
appState.mpvClient?.currentSecondarySubText || undefined,
|
||||||
showMpvOsd: (text) => showMpvOsd(text),
|
showMpvOsd: (text) => showMpvOsd(text),
|
||||||
logError: (message, err) => {
|
logError: (message, err) => {
|
||||||
console.error(message, err);
|
console.error(message, err);
|
||||||
@@ -1268,7 +1318,7 @@ function handleMineSentenceDigit(count: number): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function registerOverlayShortcuts(): void {
|
function registerOverlayShortcuts(): void {
|
||||||
shortcutsRegistered = registerOverlayShortcutsService(
|
appState.shortcutsRegistered = registerOverlayShortcutsService(
|
||||||
getConfiguredShortcuts(),
|
getConfiguredShortcuts(),
|
||||||
getOverlayShortcutRuntimeHandlers().overlayHandlers,
|
getOverlayShortcutRuntimeHandlers().overlayHandlers,
|
||||||
);
|
);
|
||||||
@@ -1284,24 +1334,24 @@ function getOverlayShortcutLifecycleDeps() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function unregisterOverlayShortcuts(): void {
|
function unregisterOverlayShortcuts(): void {
|
||||||
shortcutsRegistered = unregisterOverlayShortcutsRuntimeService(
|
appState.shortcutsRegistered = unregisterOverlayShortcutsRuntimeService(
|
||||||
shortcutsRegistered,
|
appState.shortcutsRegistered,
|
||||||
getOverlayShortcutLifecycleDeps(),
|
getOverlayShortcutLifecycleDeps(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldOverlayShortcutsBeActive(): boolean { return overlayRuntimeInitialized; }
|
function shouldOverlayShortcutsBeActive(): boolean { return appState.overlayRuntimeInitialized; }
|
||||||
function syncOverlayShortcuts(): void {
|
function syncOverlayShortcuts(): void {
|
||||||
shortcutsRegistered = syncOverlayShortcutsRuntimeService(
|
appState.shortcutsRegistered = syncOverlayShortcutsRuntimeService(
|
||||||
shouldOverlayShortcutsBeActive(),
|
shouldOverlayShortcutsBeActive(),
|
||||||
shortcutsRegistered,
|
appState.shortcutsRegistered,
|
||||||
getOverlayShortcutLifecycleDeps(),
|
getOverlayShortcutLifecycleDeps(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
function refreshOverlayShortcuts(): void {
|
function refreshOverlayShortcuts(): void {
|
||||||
shortcutsRegistered = refreshOverlayShortcutsRuntimeService(
|
appState.shortcutsRegistered = refreshOverlayShortcutsRuntimeService(
|
||||||
shouldOverlayShortcutsBeActive(),
|
shouldOverlayShortcutsBeActive(),
|
||||||
shortcutsRegistered,
|
appState.shortcutsRegistered,
|
||||||
getOverlayShortcutLifecycleDeps(),
|
getOverlayShortcutLifecycleDeps(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1311,10 +1361,10 @@ function updateVisibleOverlayVisibility(): void {
|
|||||||
{
|
{
|
||||||
visibleOverlayVisible: overlayManager.getVisibleOverlayVisible(),
|
visibleOverlayVisible: overlayManager.getVisibleOverlayVisible(),
|
||||||
mainWindow: overlayManager.getMainWindow(),
|
mainWindow: overlayManager.getMainWindow(),
|
||||||
windowTracker,
|
windowTracker: appState.windowTracker,
|
||||||
trackerNotReadyWarningShown,
|
trackerNotReadyWarningShown: appState.trackerNotReadyWarningShown,
|
||||||
setTrackerNotReadyWarningShown: (shown) => {
|
setTrackerNotReadyWarningShown: (shown) => {
|
||||||
trackerNotReadyWarningShown = shown;
|
appState.trackerNotReadyWarningShown = shown;
|
||||||
},
|
},
|
||||||
updateVisibleOverlayBounds: (geometry) => updateVisibleOverlayBounds(geometry),
|
updateVisibleOverlayBounds: (geometry) => updateVisibleOverlayBounds(geometry),
|
||||||
ensureOverlayWindowLevel: (window) => ensureOverlayWindowLevel(window),
|
ensureOverlayWindowLevel: (window) => ensureOverlayWindowLevel(window),
|
||||||
@@ -1330,7 +1380,7 @@ function updateInvisibleOverlayVisibility(): void {
|
|||||||
invisibleWindow: overlayManager.getInvisibleWindow(),
|
invisibleWindow: overlayManager.getInvisibleWindow(),
|
||||||
visibleOverlayVisible: overlayManager.getVisibleOverlayVisible(),
|
visibleOverlayVisible: overlayManager.getVisibleOverlayVisible(),
|
||||||
invisibleOverlayVisible: overlayManager.getInvisibleOverlayVisible(),
|
invisibleOverlayVisible: overlayManager.getInvisibleOverlayVisible(),
|
||||||
windowTracker,
|
windowTracker: appState.windowTracker,
|
||||||
updateInvisibleOverlayBounds: (geometry) => updateInvisibleOverlayBounds(geometry),
|
updateInvisibleOverlayBounds: (geometry) => updateInvisibleOverlayBounds(geometry),
|
||||||
ensureOverlayWindowLevel: (window) => ensureOverlayWindowLevel(window),
|
ensureOverlayWindowLevel: (window) => ensureOverlayWindowLevel(window),
|
||||||
enforceOverlayLayerOrder: () => enforceOverlayLayerOrder(),
|
enforceOverlayLayerOrder: () => enforceOverlayLayerOrder(),
|
||||||
@@ -1367,9 +1417,9 @@ function setVisibleOverlayVisible(visible: boolean): void {
|
|||||||
syncInvisibleOverlayMousePassthrough(),
|
syncInvisibleOverlayMousePassthrough(),
|
||||||
shouldBindVisibleOverlayToMpvSubVisibility: () =>
|
shouldBindVisibleOverlayToMpvSubVisibility: () =>
|
||||||
shouldBindVisibleOverlayToMpvSubVisibility(),
|
shouldBindVisibleOverlayToMpvSubVisibility(),
|
||||||
isMpvConnected: () => Boolean(mpvClient && mpvClient.connected),
|
isMpvConnected: () => Boolean(appState.mpvClient && appState.mpvClient.connected),
|
||||||
setMpvSubVisibility: (mpvSubVisible) => {
|
setMpvSubVisibility: (mpvSubVisible) => {
|
||||||
setMpvSubVisibilityRuntimeService(mpvClient, mpvSubVisible);
|
setMpvSubVisibilityRuntimeService(appState.mpvClient, mpvSubVisible);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1426,21 +1476,21 @@ function handleMpvCommandFromIpc(command: (string | number)[]): void {
|
|||||||
triggerSubsyncFromConfig: () => triggerSubsyncFromConfig(),
|
triggerSubsyncFromConfig: () => triggerSubsyncFromConfig(),
|
||||||
openRuntimeOptionsPalette: () => openRuntimeOptionsPalette(),
|
openRuntimeOptionsPalette: () => openRuntimeOptionsPalette(),
|
||||||
runtimeOptionsCycle: (id, direction) => {
|
runtimeOptionsCycle: (id, direction) => {
|
||||||
if (!runtimeOptionsManager) {
|
if (!appState.runtimeOptionsManager) {
|
||||||
return { ok: false, error: "Runtime options manager unavailable" };
|
return { ok: false, error: "Runtime options manager unavailable" };
|
||||||
}
|
}
|
||||||
return applyRuntimeOptionResultRuntimeService(
|
return applyRuntimeOptionResultRuntimeService(
|
||||||
runtimeOptionsManager.cycleOption(id, direction),
|
appState.runtimeOptionsManager.cycleOption(id, direction),
|
||||||
(text) => showMpvOsd(text),
|
(text) => showMpvOsd(text),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
showMpvOsd: (text) => showMpvOsd(text),
|
showMpvOsd: (text) => showMpvOsd(text),
|
||||||
mpvReplaySubtitle: () => replayCurrentSubtitleRuntimeService(mpvClient),
|
mpvReplaySubtitle: () => replayCurrentSubtitleRuntimeService(appState.mpvClient),
|
||||||
mpvPlayNextSubtitle: () => playNextSubtitleRuntimeService(mpvClient),
|
mpvPlayNextSubtitle: () => playNextSubtitleRuntimeService(appState.mpvClient),
|
||||||
mpvSendCommand: (rawCommand) =>
|
mpvSendCommand: (rawCommand) =>
|
||||||
sendMpvCommandRuntimeService(mpvClient, rawCommand),
|
sendMpvCommandRuntimeService(appState.mpvClient, rawCommand),
|
||||||
isMpvConnected: () => Boolean(mpvClient && mpvClient.connected),
|
isMpvConnected: () => Boolean(appState.mpvClient && appState.mpvClient.connected),
|
||||||
hasRuntimeOptionsManager: () => runtimeOptionsManager !== null,
|
hasRuntimeOptionsManager: () => appState.runtimeOptionsManager !== null,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1454,14 +1504,14 @@ async function runSubsyncManualFromIpc(
|
|||||||
const runtimeOptionsIpcDeps = {
|
const runtimeOptionsIpcDeps = {
|
||||||
setRuntimeOption: (id: string, value: unknown) =>
|
setRuntimeOption: (id: string, value: unknown) =>
|
||||||
setRuntimeOptionFromIpcRuntimeService(
|
setRuntimeOptionFromIpcRuntimeService(
|
||||||
runtimeOptionsManager,
|
appState.runtimeOptionsManager,
|
||||||
id as RuntimeOptionId,
|
id as RuntimeOptionId,
|
||||||
value as RuntimeOptionValue,
|
value as RuntimeOptionValue,
|
||||||
(text) => showMpvOsd(text),
|
(text) => showMpvOsd(text),
|
||||||
),
|
),
|
||||||
cycleRuntimeOption: (id: string, direction: 1 | -1) =>
|
cycleRuntimeOption: (id: string, direction: 1 | -1) =>
|
||||||
cycleRuntimeOptionFromIpcRuntimeService(
|
cycleRuntimeOptionFromIpcRuntimeService(
|
||||||
runtimeOptionsManager,
|
appState.runtimeOptionsManager,
|
||||||
id as RuntimeOptionId,
|
id as RuntimeOptionId,
|
||||||
direction,
|
direction,
|
||||||
(text) => showMpvOsd(text),
|
(text) => showMpvOsd(text),
|
||||||
@@ -1480,21 +1530,21 @@ registerIpcHandlersService(
|
|||||||
openYomitanSettings: () => openYomitanSettings(),
|
openYomitanSettings: () => openYomitanSettings(),
|
||||||
quitApp: () => app.quit(),
|
quitApp: () => app.quit(),
|
||||||
toggleVisibleOverlay: () => toggleVisibleOverlay(),
|
toggleVisibleOverlay: () => toggleVisibleOverlay(),
|
||||||
tokenizeCurrentSubtitle: () => tokenizeSubtitle(currentSubText),
|
tokenizeCurrentSubtitle: () => tokenizeSubtitle(appState.currentSubText),
|
||||||
getCurrentSubtitleAss: () => currentSubAssText,
|
getCurrentSubtitleAss: () => appState.currentSubAssText,
|
||||||
getMpvSubtitleRenderMetrics: () => mpvSubtitleRenderMetrics,
|
getMpvSubtitleRenderMetrics: () => appState.mpvSubtitleRenderMetrics,
|
||||||
getSubtitlePosition: () => loadSubtitlePosition(),
|
getSubtitlePosition: () => loadSubtitlePosition(),
|
||||||
getSubtitleStyle: () => getResolvedConfig().subtitleStyle ?? null,
|
getSubtitleStyle: () => getResolvedConfig().subtitleStyle ?? null,
|
||||||
saveSubtitlePosition: (position) =>
|
saveSubtitlePosition: (position) =>
|
||||||
saveSubtitlePosition(position as SubtitlePosition),
|
saveSubtitlePosition(position as SubtitlePosition),
|
||||||
getMecabTokenizer: () => mecabTokenizer,
|
getMecabTokenizer: () => appState.mecabTokenizer,
|
||||||
handleMpvCommand: (command) => handleMpvCommandFromIpc(command),
|
handleMpvCommand: (command) => handleMpvCommandFromIpc(command),
|
||||||
getKeybindings: () => keybindings,
|
getKeybindings: () => appState.keybindings,
|
||||||
getSecondarySubMode: () => secondarySubMode,
|
getSecondarySubMode: () => appState.secondarySubMode,
|
||||||
getMpvClient: () => mpvClient,
|
getMpvClient: () => appState.mpvClient,
|
||||||
runSubsyncManual: (request) =>
|
runSubsyncManual: (request) =>
|
||||||
runSubsyncManualFromIpc(request as SubsyncManualRunRequest),
|
runSubsyncManualFromIpc(request as SubsyncManualRunRequest),
|
||||||
getAnkiConnectStatus: () => ankiIntegration !== null,
|
getAnkiConnectStatus: () => appState.ankiIntegration !== null,
|
||||||
getRuntimeOptions: () => getRuntimeOptionsState(),
|
getRuntimeOptions: () => getRuntimeOptionsState(),
|
||||||
setRuntimeOption: runtimeOptionsIpcDeps.setRuntimeOption,
|
setRuntimeOption: runtimeOptionsIpcDeps.setRuntimeOption,
|
||||||
cycleRuntimeOption: runtimeOptionsIpcDeps.cycleRuntimeOption,
|
cycleRuntimeOption: runtimeOptionsIpcDeps.cycleRuntimeOption,
|
||||||
@@ -1510,12 +1560,12 @@ registerAnkiJimakuIpcRuntimeService(
|
|||||||
configService.patchRawConfig({ ankiConnect: { enabled } });
|
configService.patchRawConfig({ ankiConnect: { enabled } });
|
||||||
},
|
},
|
||||||
getResolvedConfig: () => getResolvedConfig(),
|
getResolvedConfig: () => getResolvedConfig(),
|
||||||
getRuntimeOptionsManager: () => runtimeOptionsManager,
|
getRuntimeOptionsManager: () => appState.runtimeOptionsManager,
|
||||||
getSubtitleTimingTracker: () => subtitleTimingTracker,
|
getSubtitleTimingTracker: () => appState.subtitleTimingTracker,
|
||||||
getMpvClient: () => mpvClient,
|
getMpvClient: () => appState.mpvClient,
|
||||||
getAnkiIntegration: () => ankiIntegration,
|
getAnkiIntegration: () => appState.ankiIntegration,
|
||||||
setAnkiIntegration: (integration) => {
|
setAnkiIntegration: (integration) => {
|
||||||
ankiIntegration = integration;
|
appState.ankiIntegration = integration;
|
||||||
},
|
},
|
||||||
showDesktopNotification,
|
showDesktopNotification,
|
||||||
createFieldGroupingCallback: () => createFieldGroupingCallback(),
|
createFieldGroupingCallback: () => createFieldGroupingCallback(),
|
||||||
@@ -1523,7 +1573,7 @@ registerAnkiJimakuIpcRuntimeService(
|
|||||||
getFieldGroupingResolver: () => getFieldGroupingResolver(),
|
getFieldGroupingResolver: () => getFieldGroupingResolver(),
|
||||||
setFieldGroupingResolver: (resolver) => setFieldGroupingResolver(resolver),
|
setFieldGroupingResolver: (resolver) => setFieldGroupingResolver(resolver),
|
||||||
parseMediaInfo: (mediaPath) => parseMediaInfo(resolveMediaPathForJimaku(mediaPath)),
|
parseMediaInfo: (mediaPath) => parseMediaInfo(resolveMediaPathForJimaku(mediaPath)),
|
||||||
getCurrentMediaPath: () => currentMediaPath,
|
getCurrentMediaPath: () => appState.currentMediaPath,
|
||||||
jimakuFetchJson: (endpoint, query) => jimakuFetchJson(endpoint, query),
|
jimakuFetchJson: (endpoint, query) => jimakuFetchJson(endpoint, query),
|
||||||
getJimakuMaxEntryResults: () => getJimakuMaxEntryResults(),
|
getJimakuMaxEntryResults: () => getJimakuMaxEntryResults(),
|
||||||
getJimakuLanguagePreference: () => getJimakuLanguagePreference(),
|
getJimakuLanguagePreference: () => getJimakuLanguagePreference(),
|
||||||
|
|||||||
Reference in New Issue
Block a user