mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-10-20 17:01:24 -07:00
refactor(ipc): remove dead preload methods and the ASS line broadcast (#293)
Drop 16 overlay preload methods with no caller (onSubtitleAss, getCurrentSubtitleAss, onVisibility, getOverlayVisibility, openYomitanSettings, quitApp, toggleOverlay, getMecabStatus, setMecabEnabled, getKeybindings, saveControllerPreference, getAnkiConnectStatus, setAnkiConnectEnabled, clearAnkiConnectHistory, cycleRuntimeOption, appendClipboardVideoToQueue) along with their IPC_CHANNELS keys, handlers, validators and dep plumbing. With both ASS consumers gone, stop observing sub-text/ass in mpv and remove the subtitle-ass-change event chain and appState.currentSubAssText. The main-process functions these wrapped stay; tray, CLI, shortcuts and session actions still use them.
This commit is contained in:
1 parent
8fc20b2499
commit
7909fcd2cf
32 files changed
+8
-876
No files matched your search
@@ -0,0 +1,4 @@
|
||||
type: internal
|
||||
area: overlay
|
||||
|
||||
- Removed 16 overlay preload/IPC methods that nothing called, plus their main-process handlers. SubMiner no longer observes mpv's `sub-text/ass` or sends every ASS line to overlay windows that never listened.
|
||||
@@ -32,8 +32,6 @@ test('anki/jimaku IPC handlers reject malformed invoke payloads', async () => {
|
||||
let previewCalls = 0;
|
||||
registerAnkiJimakuIpcHandlers(
|
||||
{
|
||||
setAnkiConnectEnabled: () => {},
|
||||
clearAnkiHistory: () => {},
|
||||
refreshKnownWords: async () => {},
|
||||
respondFieldGrouping: () => {},
|
||||
buildKikuMergePreview: async () => {
|
||||
@@ -138,8 +136,6 @@ test('tsukihime downloads always route Japanese as primary', async () => {
|
||||
const secondaryLoads: string[] = [];
|
||||
registerAnkiJimakuIpcHandlers(
|
||||
{
|
||||
setAnkiConnectEnabled: () => {},
|
||||
clearAnkiHistory: () => {},
|
||||
refreshKnownWords: async () => {},
|
||||
respondFieldGrouping: () => {},
|
||||
buildKikuMergePreview: async () => ({ ok: true }),
|
||||
@@ -205,13 +201,8 @@ test('tsukihime downloads always route Japanese as primary', async () => {
|
||||
test('anki/jimaku IPC command handlers ignore malformed payloads', () => {
|
||||
const { registrar, onHandlers } = createFakeRegistrar();
|
||||
const fieldGroupingChoices: unknown[] = [];
|
||||
const enabledStates: boolean[] = [];
|
||||
registerAnkiJimakuIpcHandlers(
|
||||
{
|
||||
setAnkiConnectEnabled: (enabled) => {
|
||||
enabledStates.push(enabled);
|
||||
},
|
||||
clearAnkiHistory: () => {},
|
||||
refreshKnownWords: async () => {},
|
||||
respondFieldGrouping: (choice) => {
|
||||
fieldGroupingChoices.push(choice);
|
||||
@@ -241,10 +232,6 @@ test('anki/jimaku IPC command handlers ignore malformed payloads', () => {
|
||||
registrar,
|
||||
);
|
||||
|
||||
onHandlers.get(IPC_CHANNELS.command.setAnkiConnectEnabled)!({}, 'true');
|
||||
onHandlers.get(IPC_CHANNELS.command.setAnkiConnectEnabled)!({}, true);
|
||||
assert.deepEqual(enabledStates, [true]);
|
||||
|
||||
onHandlers.get(IPC_CHANNELS.command.kikuFieldGroupingRespond)!({}, null);
|
||||
onHandlers.get(IPC_CHANNELS.command.kikuFieldGroupingRespond)!(
|
||||
{},
|
||||
|
||||
@@ -40,8 +40,6 @@ const { ipcMain } = electron;
|
||||
const logger = createLogger('main:anki-jimaku-ipc');
|
||||
|
||||
export interface AnkiJimakuIpcDeps {
|
||||
setAnkiConnectEnabled: (enabled: boolean) => void;
|
||||
clearAnkiHistory: () => void;
|
||||
refreshKnownWords: () => Promise<void> | void;
|
||||
respondFieldGrouping: (choice: KikuFieldGroupingChoice) => void;
|
||||
buildKikuMergePreview: (request: KikuMergePreviewRequest) => Promise<KikuMergePreviewResponse>;
|
||||
@@ -77,15 +75,6 @@ export function registerAnkiJimakuIpcHandlers(
|
||||
deps: AnkiJimakuIpcDeps,
|
||||
ipc: IpcMainRegistrar = ipcMain,
|
||||
): void {
|
||||
ipc.on(IPC_CHANNELS.command.setAnkiConnectEnabled, (_event: unknown, enabled: unknown) => {
|
||||
if (typeof enabled !== 'boolean') return;
|
||||
deps.setAnkiConnectEnabled(enabled);
|
||||
});
|
||||
|
||||
ipc.on(IPC_CHANNELS.command.clearAnkiConnectHistory, () => {
|
||||
deps.clearAnkiHistory();
|
||||
});
|
||||
|
||||
ipc.on(IPC_CHANNELS.command.refreshKnownWords, async () => {
|
||||
await deps.refreshKnownWords();
|
||||
});
|
||||
|
||||
@@ -8,8 +8,6 @@ interface RuntimeHarness {
|
||||
state: {
|
||||
ankiIntegration: unknown;
|
||||
fieldGroupingResolver: ((choice: unknown) => void) | null;
|
||||
patches: boolean[];
|
||||
broadcasts: number;
|
||||
fetchCalls: Array<{ endpoint: string; query?: Record<string, unknown> }>;
|
||||
tsukihimeFetchCalls: Array<{ endpoint: string; query?: Record<string, unknown> }>;
|
||||
sentCommands: Array<{ command: (string | number)[] }>;
|
||||
@@ -20,8 +18,6 @@ function createHarness(): RuntimeHarness {
|
||||
const state = {
|
||||
ankiIntegration: null as unknown,
|
||||
fieldGroupingResolver: null as ((choice: unknown) => void) | null,
|
||||
patches: [] as boolean[],
|
||||
broadcasts: 0,
|
||||
fetchCalls: [] as Array<{
|
||||
endpoint: string;
|
||||
query?: Record<string, unknown>;
|
||||
@@ -34,11 +30,7 @@ function createHarness(): RuntimeHarness {
|
||||
};
|
||||
|
||||
const options: AnkiJimakuIpcRuntimeOptions = {
|
||||
patchAnkiConnectEnabled: (enabled) => {
|
||||
state.patches.push(enabled);
|
||||
},
|
||||
getResolvedConfig: () => ({ tsukihime: { maxSearchResults: 2 } }),
|
||||
getRuntimeOptionsManager: () => null,
|
||||
tsukihimeFetchJson: async (endpoint, query) => {
|
||||
state.tsukihimeFetchCalls.push({
|
||||
endpoint,
|
||||
@@ -76,7 +68,6 @@ function createHarness(): RuntimeHarness {
|
||||
} as never,
|
||||
};
|
||||
},
|
||||
getSubtitleTimingTracker: () => null,
|
||||
getMpvClient: () => ({
|
||||
connected: true,
|
||||
send: (payload) => {
|
||||
@@ -99,20 +90,6 @@ function createHarness(): RuntimeHarness {
|
||||
},
|
||||
}),
|
||||
getAnkiIntegration: () => state.ankiIntegration as never,
|
||||
setAnkiIntegration: (integration) => {
|
||||
state.ankiIntegration = integration;
|
||||
},
|
||||
getKnownWordCacheStatePath: () => '/tmp/subminer-known-words-cache.json',
|
||||
showDesktopNotification: () => {},
|
||||
createFieldGroupingCallback: () => async () => ({
|
||||
keepNoteId: 1,
|
||||
deleteNoteId: 2,
|
||||
deleteDuplicate: false,
|
||||
cancelled: false,
|
||||
}),
|
||||
broadcastRuntimeOptionsChanged: () => {
|
||||
state.broadcasts += 1;
|
||||
},
|
||||
getFieldGroupingResolver: () => state.fieldGroupingResolver as never,
|
||||
setFieldGroupingResolver: (resolver) => {
|
||||
state.fieldGroupingResolver = resolver as never;
|
||||
@@ -169,51 +146,21 @@ test('refreshKnownWords throws when integration is unavailable', async () => {
|
||||
);
|
||||
});
|
||||
|
||||
test('setAnkiConnectEnabled disables active integration and broadcasts changes', () => {
|
||||
test('respondFieldGrouping resolves and clears the pending resolver', () => {
|
||||
const { registered, state } = createHarness();
|
||||
let destroyed = 0;
|
||||
state.ankiIntegration = {
|
||||
destroy: () => {
|
||||
destroyed += 1;
|
||||
},
|
||||
};
|
||||
|
||||
registered.setAnkiConnectEnabled!(false);
|
||||
|
||||
assert.deepEqual(state.patches, [false]);
|
||||
assert.equal(destroyed, 1);
|
||||
assert.equal(state.ankiIntegration, null);
|
||||
assert.equal(state.broadcasts, 1);
|
||||
});
|
||||
|
||||
test('clearAnkiHistory and respondFieldGrouping execute runtime callbacks', () => {
|
||||
const { registered, state, options } = createHarness();
|
||||
let cleaned = 0;
|
||||
let resolvedChoice: unknown = null;
|
||||
state.fieldGroupingResolver = (choice) => {
|
||||
resolvedChoice = choice;
|
||||
};
|
||||
|
||||
const originalGetTracker = options.getSubtitleTimingTracker;
|
||||
options.getSubtitleTimingTracker = () =>
|
||||
({
|
||||
cleanup: () => {
|
||||
cleaned += 1;
|
||||
},
|
||||
}) as never;
|
||||
|
||||
const choice = {
|
||||
keepNoteId: 10,
|
||||
deleteNoteId: 11,
|
||||
deleteDuplicate: true,
|
||||
cancelled: false,
|
||||
};
|
||||
registered.clearAnkiHistory!();
|
||||
registered.respondFieldGrouping!(choice);
|
||||
|
||||
options.getSubtitleTimingTracker = originalGetTracker;
|
||||
|
||||
assert.equal(cleaned, 1);
|
||||
assert.deepEqual(resolvedChoice, choice);
|
||||
assert.equal(state.fieldGroupingResolver, null);
|
||||
});
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
import * as fs from 'fs';
|
||||
import { AnkiIntegration } from '../../anki-integration';
|
||||
import { mergeAiConfig } from '../../ai/config';
|
||||
import type { AnkiIntegration } from '../../anki-integration';
|
||||
import {
|
||||
AiConfig,
|
||||
TsukihimeApiResponse,
|
||||
TsukihimeConfig,
|
||||
AnkiConnectConfig,
|
||||
JimakuApiResponse,
|
||||
JimakuEntry,
|
||||
JimakuFileEntry,
|
||||
JimakuLanguagePreference,
|
||||
JimakuMediaInfo,
|
||||
KikuFieldGroupingChoice,
|
||||
KikuFieldGroupingRequestData,
|
||||
OverlayNotificationPayload,
|
||||
} from '../../types';
|
||||
import { sortJimakuFiles } from '../../jimaku/utils';
|
||||
import {
|
||||
@@ -35,41 +30,13 @@ interface MpvClientLike {
|
||||
request?: (command: unknown[]) => Promise<{ data?: unknown }>;
|
||||
}
|
||||
|
||||
interface RuntimeOptionsManagerLike {
|
||||
getEffectiveAnkiConnectConfig: (config?: AnkiConnectConfig) => AnkiConnectConfig;
|
||||
}
|
||||
|
||||
interface SubtitleTimingTrackerLike {
|
||||
cleanup: () => void;
|
||||
}
|
||||
|
||||
export interface AnkiJimakuIpcRuntimeOptions {
|
||||
patchAnkiConnectEnabled: (enabled: boolean) => void;
|
||||
getResolvedConfig: () => {
|
||||
ankiConnect?: AnkiConnectConfig;
|
||||
ai?: AiConfig;
|
||||
tsukihime?: TsukihimeConfig;
|
||||
secondarySub?: { secondarySubLanguages?: string[] };
|
||||
};
|
||||
getRuntimeOptionsManager: () => RuntimeOptionsManagerLike | null;
|
||||
getSubtitleTimingTracker: () => SubtitleTimingTrackerLike | null;
|
||||
getMpvClient: () => MpvClientLike | null;
|
||||
getAnkiIntegration: () => AnkiIntegration | null;
|
||||
setAnkiIntegration: (integration: AnkiIntegration | null) => void;
|
||||
getKnownWordCacheStatePath: () => string;
|
||||
getCachedMediaPath?: (
|
||||
currentVideoPath: string,
|
||||
kind: 'audio' | 'video',
|
||||
) => Promise<string | null>;
|
||||
shouldRequireRemoteMediaCache?: () => boolean;
|
||||
getYoutubeMediaSourceUrl?: () => Promise<string | null | undefined> | string | null | undefined;
|
||||
showDesktopNotification: (title: string, options: { body?: string; icon?: string }) => void;
|
||||
showOverlayNotification?: (payload: OverlayNotificationPayload) => void;
|
||||
dismissOverlayNotification?: (id: string) => void;
|
||||
createFieldGroupingCallback: () => (
|
||||
data: KikuFieldGroupingRequestData,
|
||||
) => Promise<KikuFieldGroupingChoice>;
|
||||
broadcastRuntimeOptionsChanged: () => void;
|
||||
getFieldGroupingResolver: () => ((choice: KikuFieldGroupingChoice) => void) | null;
|
||||
setFieldGroupingResolver: (resolver: ((choice: KikuFieldGroupingChoice) => void) | null) => void;
|
||||
parseMediaInfo: (mediaPath: string | null) => JimakuMediaInfo;
|
||||
@@ -135,58 +102,6 @@ export function registerAnkiJimakuIpcRuntime(
|
||||
registerHandlers: RegisterAnkiJimakuIpcRuntimeHandler,
|
||||
): void {
|
||||
registerHandlers({
|
||||
setAnkiConnectEnabled: (enabled) => {
|
||||
options.patchAnkiConnectEnabled(enabled);
|
||||
const config = options.getResolvedConfig();
|
||||
const subtitleTimingTracker = options.getSubtitleTimingTracker();
|
||||
const mpvClient = options.getMpvClient();
|
||||
const ankiIntegration = options.getAnkiIntegration();
|
||||
|
||||
if (enabled && !ankiIntegration && subtitleTimingTracker && mpvClient) {
|
||||
const runtimeOptionsManager = options.getRuntimeOptionsManager();
|
||||
const effectiveAnkiConfig = runtimeOptionsManager
|
||||
? runtimeOptionsManager.getEffectiveAnkiConnectConfig(config.ankiConnect)
|
||||
: config.ankiConnect;
|
||||
const integration = new AnkiIntegration(
|
||||
effectiveAnkiConfig as never,
|
||||
subtitleTimingTracker as never,
|
||||
mpvClient as never,
|
||||
(text: string) => {
|
||||
if (mpvClient) {
|
||||
mpvClient.send({
|
||||
command: ['show-text', text, '3000'],
|
||||
});
|
||||
}
|
||||
},
|
||||
options.showDesktopNotification,
|
||||
options.createFieldGroupingCallback(),
|
||||
options.getKnownWordCacheStatePath(),
|
||||
mergeAiConfig(config.ai, config.ankiConnect?.ai) as AiConfig,
|
||||
undefined,
|
||||
options.showOverlayNotification,
|
||||
options.getCachedMediaPath,
|
||||
options.shouldRequireRemoteMediaCache,
|
||||
options.getYoutubeMediaSourceUrl,
|
||||
options.dismissOverlayNotification,
|
||||
);
|
||||
integration.start();
|
||||
options.setAnkiIntegration(integration);
|
||||
logger.info('AnkiConnect integration enabled');
|
||||
} else if (!enabled && ankiIntegration) {
|
||||
ankiIntegration.destroy();
|
||||
options.setAnkiIntegration(null);
|
||||
logger.info('AnkiConnect integration disabled');
|
||||
}
|
||||
|
||||
options.broadcastRuntimeOptionsChanged();
|
||||
},
|
||||
clearAnkiHistory: () => {
|
||||
const subtitleTimingTracker = options.getSubtitleTimingTracker();
|
||||
if (subtitleTimingTracker) {
|
||||
subtitleTimingTracker.cleanup();
|
||||
logger.info('AnkiConnect subtitle timing history cleared');
|
||||
}
|
||||
},
|
||||
refreshKnownWords: async () => {
|
||||
const integration = options.getAnkiIntegration();
|
||||
if (!integration) {
|
||||
|
||||
@@ -114,23 +114,15 @@ function createSubtitleSidebarSnapshotFixture(): SubtitleSidebarSnapshot {
|
||||
function createRegisterIpcDeps(overrides: Partial<IpcServiceDeps> = {}): IpcServiceDeps {
|
||||
return {
|
||||
onOverlayModalClosed: () => {},
|
||||
openYomitanSettings: () => {},
|
||||
quitApp: () => {},
|
||||
toggleDevTools: () => {},
|
||||
getVisibleOverlayVisibility: () => false,
|
||||
toggleVisibleOverlay: () => {},
|
||||
tokenizeCurrentSubtitle: async () => null,
|
||||
getCurrentSubtitleRaw: () => '',
|
||||
getCurrentSubtitleAss: () => '',
|
||||
getSubtitleSidebarSnapshot: async () => createSubtitleSidebarSnapshotFixture(),
|
||||
getPlaybackPaused: () => false,
|
||||
getSubtitlePosition: () => null,
|
||||
getSubtitleStyle: () => null,
|
||||
saveSubtitlePosition: () => {},
|
||||
getMecabStatus: () => ({ available: false, enabled: false, path: null }),
|
||||
setMecabEnabled: () => {},
|
||||
handleMpvCommand: () => {},
|
||||
getKeybindings: () => [],
|
||||
getSessionBindings: () => [],
|
||||
getConfiguredShortcuts: () => ({}),
|
||||
dispatchSessionAction: async () => {},
|
||||
@@ -139,23 +131,19 @@ function createRegisterIpcDeps(overrides: Partial<IpcServiceDeps> = {}): IpcServ
|
||||
getOverlayNotificationPosition: () => 'top-right',
|
||||
getControllerConfig: () => createControllerConfigFixture(),
|
||||
saveControllerConfig: async () => {},
|
||||
saveControllerPreference: async () => {},
|
||||
getSecondarySubMode: () => 'hover',
|
||||
getCurrentSecondarySub: () => '',
|
||||
focusMainWindow: () => {},
|
||||
activatePlaybackWindowForOverlayInteraction: () => false,
|
||||
runSubsyncManual: async () => ({ ok: true, message: 'ok' }),
|
||||
getAnkiConnectStatus: () => false,
|
||||
getRuntimeOptions: () => [],
|
||||
setRuntimeOption: () => ({ ok: true }),
|
||||
cycleRuntimeOption: () => ({ ok: true }),
|
||||
reportOverlayContentBounds: () => {},
|
||||
getAnilistStatus: () => ({}),
|
||||
clearAnilistToken: () => {},
|
||||
openAnilistSetup: () => {},
|
||||
getAnilistQueueStatus: () => ({}),
|
||||
retryAnilistQueueNow: async () => ({ ok: true, message: 'ok' }),
|
||||
appendClipboardVideoToQueue: () => ({ ok: true, message: 'ok' }),
|
||||
getPlaylistBrowserSnapshot: async () => ({
|
||||
directoryPath: null,
|
||||
directoryAvailable: false,
|
||||
@@ -312,26 +300,17 @@ test('registerIpcHandlers skips AniList update when manual mark watched has no a
|
||||
test('registerIpcHandlers rejects malformed runtime-option payloads', async () => {
|
||||
const { registrar, handlers } = createFakeIpcRegistrar();
|
||||
const calls: Array<{ id: string; value: unknown }> = [];
|
||||
const cycles: Array<{ id: string; direction: 1 | -1 }> = [];
|
||||
registerIpcHandlers(
|
||||
{
|
||||
onOverlayModalClosed: () => {},
|
||||
openYomitanSettings: () => {},
|
||||
quitApp: () => {},
|
||||
toggleDevTools: () => {},
|
||||
getVisibleOverlayVisibility: () => false,
|
||||
toggleVisibleOverlay: () => {},
|
||||
tokenizeCurrentSubtitle: async () => null,
|
||||
getCurrentSubtitleRaw: () => '',
|
||||
getCurrentSubtitleAss: () => '',
|
||||
getPlaybackPaused: () => null,
|
||||
getSubtitlePosition: () => null,
|
||||
getSubtitleStyle: () => null,
|
||||
saveSubtitlePosition: () => {},
|
||||
getMecabStatus: () => ({ available: false, enabled: false, path: null }),
|
||||
setMecabEnabled: () => {},
|
||||
handleMpvCommand: () => {},
|
||||
getKeybindings: () => [],
|
||||
getSessionBindings: () => [],
|
||||
getConfiguredShortcuts: () => ({}),
|
||||
dispatchSessionAction: async () => {},
|
||||
@@ -340,21 +319,15 @@ test('registerIpcHandlers rejects malformed runtime-option payloads', async () =
|
||||
getOverlayNotificationPosition: () => 'top-right',
|
||||
getControllerConfig: () => createControllerConfigFixture(),
|
||||
saveControllerConfig: () => {},
|
||||
saveControllerPreference: () => {},
|
||||
getSecondarySubMode: () => 'hover',
|
||||
getCurrentSecondarySub: () => '',
|
||||
focusMainWindow: () => {},
|
||||
runSubsyncManual: async () => ({ ok: true, message: 'ok' }),
|
||||
getAnkiConnectStatus: () => false,
|
||||
getRuntimeOptions: () => [],
|
||||
setRuntimeOption: (id, value) => {
|
||||
calls.push({ id, value });
|
||||
return { ok: true };
|
||||
},
|
||||
cycleRuntimeOption: (id, direction) => {
|
||||
cycles.push({ id, direction });
|
||||
return { ok: true };
|
||||
},
|
||||
getSubtitleSidebarSnapshot: async () => createSubtitleSidebarSnapshotFixture(),
|
||||
reportOverlayContentBounds: () => {},
|
||||
getAnilistStatus: () => ({}),
|
||||
@@ -362,7 +335,6 @@ test('registerIpcHandlers rejects malformed runtime-option payloads', async () =
|
||||
openAnilistSetup: () => {},
|
||||
getAnilistQueueStatus: () => ({}),
|
||||
retryAnilistQueueNow: async () => ({ ok: true, message: 'ok' }),
|
||||
appendClipboardVideoToQueue: () => ({ ok: true, message: 'ok' }),
|
||||
getPlaylistBrowserSnapshot: async () => ({
|
||||
directoryPath: null,
|
||||
directoryAvailable: false,
|
||||
@@ -399,16 +371,6 @@ test('registerIpcHandlers rejects malformed runtime-option payloads', async () =
|
||||
{ id: 'subtitle.annotation.jlpt', value: false },
|
||||
]);
|
||||
|
||||
const cycleHandler = handlers.handle.get(IPC_CHANNELS.request.cycleRuntimeOption);
|
||||
assert.ok(cycleHandler);
|
||||
const invalidDirection = await cycleHandler!({}, 'anki.kikuFieldGrouping', 2);
|
||||
assert.deepEqual(invalidDirection, {
|
||||
ok: false,
|
||||
error: 'Invalid runtime option cycle direction',
|
||||
});
|
||||
await cycleHandler!({}, 'anki.kikuFieldGrouping', -1);
|
||||
assert.deepEqual(cycles, [{ id: 'anki.kikuFieldGrouping', direction: -1 }]);
|
||||
|
||||
const getPlaybackPausedHandler = handlers.handle.get(IPC_CHANNELS.request.getPlaybackPaused);
|
||||
assert.ok(getPlaybackPausedHandler);
|
||||
assert.equal(getPlaybackPausedHandler!({}), null);
|
||||
@@ -609,7 +571,6 @@ test('registerIpcHandlers records yomitan lookup when subtitle context recording
|
||||
test('registerIpcHandlers ignores malformed fire-and-forget payloads', () => {
|
||||
const { registrar, handlers } = createFakeIpcRegistrar();
|
||||
const saves: unknown[] = [];
|
||||
const controllerSaves: unknown[] = [];
|
||||
const closedModals: unknown[] = [];
|
||||
const openedModals: unknown[] = [];
|
||||
registerIpcHandlers(
|
||||
@@ -620,14 +581,9 @@ test('registerIpcHandlers ignores malformed fire-and-forget payloads', () => {
|
||||
onOverlayModalOpened: (modal) => {
|
||||
openedModals.push(modal);
|
||||
},
|
||||
openYomitanSettings: () => {},
|
||||
quitApp: () => {},
|
||||
toggleDevTools: () => {},
|
||||
getVisibleOverlayVisibility: () => false,
|
||||
toggleVisibleOverlay: () => {},
|
||||
tokenizeCurrentSubtitle: async () => null,
|
||||
getCurrentSubtitleRaw: () => '',
|
||||
getCurrentSubtitleAss: () => '',
|
||||
getSubtitleSidebarSnapshot: async () => createSubtitleSidebarSnapshotFixture(),
|
||||
getPlaybackPaused: () => false,
|
||||
getSubtitlePosition: () => null,
|
||||
@@ -635,10 +591,7 @@ test('registerIpcHandlers ignores malformed fire-and-forget payloads', () => {
|
||||
saveSubtitlePosition: (position) => {
|
||||
saves.push(position);
|
||||
},
|
||||
getMecabStatus: () => ({ available: false, enabled: false, path: null }),
|
||||
setMecabEnabled: () => {},
|
||||
handleMpvCommand: () => {},
|
||||
getKeybindings: () => [],
|
||||
getSessionBindings: () => [],
|
||||
getConfiguredShortcuts: () => ({}),
|
||||
dispatchSessionAction: async () => {},
|
||||
@@ -647,24 +600,18 @@ test('registerIpcHandlers ignores malformed fire-and-forget payloads', () => {
|
||||
getOverlayNotificationPosition: () => 'top-right',
|
||||
getControllerConfig: () => createControllerConfigFixture(),
|
||||
saveControllerConfig: () => {},
|
||||
saveControllerPreference: (update) => {
|
||||
controllerSaves.push(update);
|
||||
},
|
||||
getSecondarySubMode: () => 'hover',
|
||||
getCurrentSecondarySub: () => '',
|
||||
focusMainWindow: () => {},
|
||||
runSubsyncManual: async () => ({ ok: true, message: 'ok' }),
|
||||
getAnkiConnectStatus: () => false,
|
||||
getRuntimeOptions: () => [],
|
||||
setRuntimeOption: () => ({ ok: true }),
|
||||
cycleRuntimeOption: () => ({ ok: true }),
|
||||
reportOverlayContentBounds: () => {},
|
||||
getAnilistStatus: () => ({}),
|
||||
clearAnilistToken: () => {},
|
||||
openAnilistSetup: () => {},
|
||||
getAnilistQueueStatus: () => ({}),
|
||||
retryAnilistQueueNow: async () => ({ ok: true, message: 'ok' }),
|
||||
appendClipboardVideoToQueue: () => ({ ok: true, message: 'ok' }),
|
||||
getPlaylistBrowserSnapshot: async () => ({
|
||||
directoryPath: null,
|
||||
directoryAvailable: false,
|
||||
@@ -698,96 +645,6 @@ test('registerIpcHandlers ignores malformed fire-and-forget payloads', () => {
|
||||
assert.deepEqual(openedModals, ['subsync', 'runtime-options']);
|
||||
});
|
||||
|
||||
test('registerIpcHandlers awaits saveControllerPreference through request-response IPC', async () => {
|
||||
const { registrar, handlers } = createFakeIpcRegistrar();
|
||||
const controllerSaves: unknown[] = [];
|
||||
registerIpcHandlers(
|
||||
{
|
||||
onOverlayModalClosed: () => {},
|
||||
openYomitanSettings: () => {},
|
||||
quitApp: () => {},
|
||||
toggleDevTools: () => {},
|
||||
getVisibleOverlayVisibility: () => false,
|
||||
toggleVisibleOverlay: () => {},
|
||||
tokenizeCurrentSubtitle: async () => null,
|
||||
getCurrentSubtitleRaw: () => '',
|
||||
getCurrentSubtitleAss: () => '',
|
||||
getSubtitleSidebarSnapshot: async () => createSubtitleSidebarSnapshotFixture(),
|
||||
getPlaybackPaused: () => false,
|
||||
getSubtitlePosition: () => null,
|
||||
getSubtitleStyle: () => null,
|
||||
saveSubtitlePosition: () => {},
|
||||
getMecabStatus: () => ({ available: false, enabled: false, path: null }),
|
||||
setMecabEnabled: () => {},
|
||||
handleMpvCommand: () => {},
|
||||
getKeybindings: () => [],
|
||||
getSessionBindings: () => [],
|
||||
getConfiguredShortcuts: () => ({}),
|
||||
dispatchSessionAction: async () => {},
|
||||
getStatsToggleKey: () => 'Backquote',
|
||||
getMarkWatchedKey: () => 'KeyW',
|
||||
getOverlayNotificationPosition: () => 'top-right',
|
||||
getControllerConfig: () => createControllerConfigFixture(),
|
||||
saveControllerConfig: async () => {},
|
||||
saveControllerPreference: async (update) => {
|
||||
await Promise.resolve();
|
||||
controllerSaves.push(update);
|
||||
},
|
||||
getSecondarySubMode: () => 'hover',
|
||||
getCurrentSecondarySub: () => '',
|
||||
focusMainWindow: () => {},
|
||||
runSubsyncManual: async () => ({ ok: true, message: 'ok' }),
|
||||
getAnkiConnectStatus: () => false,
|
||||
getRuntimeOptions: () => [],
|
||||
setRuntimeOption: () => ({ ok: true }),
|
||||
cycleRuntimeOption: () => ({ ok: true }),
|
||||
reportOverlayContentBounds: () => {},
|
||||
getAnilistStatus: () => ({}),
|
||||
clearAnilistToken: () => {},
|
||||
openAnilistSetup: () => {},
|
||||
getAnilistQueueStatus: () => ({}),
|
||||
retryAnilistQueueNow: async () => ({ ok: true, message: 'ok' }),
|
||||
appendClipboardVideoToQueue: () => ({ ok: true, message: 'ok' }),
|
||||
getPlaylistBrowserSnapshot: async () => ({
|
||||
directoryPath: null,
|
||||
directoryAvailable: false,
|
||||
directoryStatus: '',
|
||||
directoryItems: [],
|
||||
playlistItems: [],
|
||||
playingIndex: null,
|
||||
currentFilePath: null,
|
||||
}),
|
||||
appendPlaylistBrowserFile: async () => ({ ok: true, message: 'ok' }),
|
||||
playPlaylistBrowserIndex: async () => ({ ok: true, message: 'ok' }),
|
||||
removePlaylistBrowserIndex: async () => ({ ok: true, message: 'ok' }),
|
||||
movePlaylistBrowserIndex: async () => ({ ok: true, message: 'ok' }),
|
||||
onYoutubePickerResolve: async () => ({ ok: true, message: 'ok' }),
|
||||
},
|
||||
registrar,
|
||||
);
|
||||
|
||||
const saveHandler = handlers.handle.get(IPC_CHANNELS.command.saveControllerPreference);
|
||||
assert.ok(saveHandler);
|
||||
|
||||
await assert.rejects(async () => {
|
||||
await saveHandler!({}, { preferredGamepadId: 12 });
|
||||
}, /Invalid controller preference payload/);
|
||||
await saveHandler!(
|
||||
{},
|
||||
{
|
||||
preferredGamepadId: 'pad-1',
|
||||
preferredGamepadLabel: 'Pad 1',
|
||||
},
|
||||
);
|
||||
|
||||
assert.deepEqual(controllerSaves, [
|
||||
{
|
||||
preferredGamepadId: 'pad-1',
|
||||
preferredGamepadLabel: 'Pad 1',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('registerIpcHandlers accepts per-controller profile config updates', async () => {
|
||||
const { registrar, handlers } = createFakeIpcRegistrar();
|
||||
const controllerSaves: unknown[] = [];
|
||||
@@ -975,76 +832,6 @@ test('registerIpcHandlers forwards valid overlay notification actions', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
test('registerIpcHandlers rejects malformed controller preference payloads', async () => {
|
||||
const { registrar, handlers } = createFakeIpcRegistrar();
|
||||
registerIpcHandlers(
|
||||
{
|
||||
onOverlayModalClosed: () => {},
|
||||
openYomitanSettings: () => {},
|
||||
quitApp: () => {},
|
||||
toggleDevTools: () => {},
|
||||
getVisibleOverlayVisibility: () => false,
|
||||
toggleVisibleOverlay: () => {},
|
||||
tokenizeCurrentSubtitle: async () => null,
|
||||
getCurrentSubtitleRaw: () => '',
|
||||
getCurrentSubtitleAss: () => '',
|
||||
getSubtitleSidebarSnapshot: async () => createSubtitleSidebarSnapshotFixture(),
|
||||
getPlaybackPaused: () => false,
|
||||
getSubtitlePosition: () => null,
|
||||
getSubtitleStyle: () => null,
|
||||
saveSubtitlePosition: () => {},
|
||||
getMecabStatus: () => ({ available: false, enabled: false, path: null }),
|
||||
setMecabEnabled: () => {},
|
||||
handleMpvCommand: () => {},
|
||||
getKeybindings: () => [],
|
||||
getSessionBindings: () => [],
|
||||
getConfiguredShortcuts: () => ({}),
|
||||
dispatchSessionAction: async () => {},
|
||||
getStatsToggleKey: () => 'Backquote',
|
||||
getMarkWatchedKey: () => 'KeyW',
|
||||
getOverlayNotificationPosition: () => 'top-right',
|
||||
getControllerConfig: () => createControllerConfigFixture(),
|
||||
saveControllerConfig: async () => {},
|
||||
saveControllerPreference: async () => {},
|
||||
getSecondarySubMode: () => 'hover',
|
||||
getCurrentSecondarySub: () => '',
|
||||
focusMainWindow: () => {},
|
||||
runSubsyncManual: async () => ({ ok: true, message: 'ok' }),
|
||||
getAnkiConnectStatus: () => false,
|
||||
getRuntimeOptions: () => [],
|
||||
setRuntimeOption: () => ({ ok: true }),
|
||||
cycleRuntimeOption: () => ({ ok: true }),
|
||||
reportOverlayContentBounds: () => {},
|
||||
getAnilistStatus: () => ({}),
|
||||
clearAnilistToken: () => {},
|
||||
openAnilistSetup: () => {},
|
||||
getAnilistQueueStatus: () => ({}),
|
||||
retryAnilistQueueNow: async () => ({ ok: true, message: 'ok' }),
|
||||
appendClipboardVideoToQueue: () => ({ ok: true, message: 'ok' }),
|
||||
getPlaylistBrowserSnapshot: async () => ({
|
||||
directoryPath: null,
|
||||
directoryAvailable: false,
|
||||
directoryStatus: '',
|
||||
directoryItems: [],
|
||||
playlistItems: [],
|
||||
playingIndex: null,
|
||||
currentFilePath: null,
|
||||
}),
|
||||
appendPlaylistBrowserFile: async () => ({ ok: true, message: 'ok' }),
|
||||
playPlaylistBrowserIndex: async () => ({ ok: true, message: 'ok' }),
|
||||
removePlaylistBrowserIndex: async () => ({ ok: true, message: 'ok' }),
|
||||
movePlaylistBrowserIndex: async () => ({ ok: true, message: 'ok' }),
|
||||
onYoutubePickerResolve: async () => ({ ok: true, message: 'ok' }),
|
||||
},
|
||||
registrar,
|
||||
);
|
||||
|
||||
const saveHandler = handlers.handle.get(IPC_CHANNELS.command.saveControllerPreference);
|
||||
await assert.rejects(async () => {
|
||||
await saveHandler!({}, { preferredGamepadId: 12 });
|
||||
}, /Invalid controller preference payload/);
|
||||
});
|
||||
|
||||
test('registerIpcHandlers exposes character dictionary selection handlers', async () => {
|
||||
const { registrar, handlers } = createFakeIpcRegistrar();
|
||||
const calls: number[] = [];
|
||||
|
||||
@@ -7,7 +7,6 @@ import type {
|
||||
ControllerConfigUpdate,
|
||||
PlaylistBrowserMutationResult,
|
||||
PlaylistBrowserSnapshot,
|
||||
ControllerPreferenceUpdate,
|
||||
ResolvedControllerConfig,
|
||||
RuntimeOptionId,
|
||||
RuntimeOptionValue,
|
||||
@@ -34,10 +33,8 @@ import { IPC_CHANNELS, type OverlayHostedModal } from '../../shared/ipc/contract
|
||||
import {
|
||||
parseMpvCommand,
|
||||
parseControllerConfigUpdate,
|
||||
parseControllerPreferenceUpdate,
|
||||
parseOptionalForwardingOptions,
|
||||
parseOverlayHostedModal,
|
||||
parseRuntimeOptionDirection,
|
||||
parseRuntimeOptionId,
|
||||
parseRuntimeOptionValue,
|
||||
parseSessionActionDispatchRequest,
|
||||
@@ -71,14 +68,9 @@ export interface IpcServiceDeps {
|
||||
actionId: string,
|
||||
noteId?: number,
|
||||
) => void | Promise<void>;
|
||||
openYomitanSettings: () => void;
|
||||
quitApp: () => void;
|
||||
toggleDevTools: () => void;
|
||||
getVisibleOverlayVisibility: () => boolean;
|
||||
toggleVisibleOverlay: () => void;
|
||||
tokenizeCurrentSubtitle: () => Promise<unknown>;
|
||||
getCurrentSubtitleRaw: () => string;
|
||||
getCurrentSubtitleAss: () => string;
|
||||
getSubtitleSidebarSnapshot?: () => Promise<SubtitleSidebarSnapshot>;
|
||||
getSubtitleSidebarPlaybackState?: () => SubtitleSidebarPlaybackState;
|
||||
getSubtitleSidebarOpen?: () => boolean;
|
||||
@@ -86,14 +78,7 @@ export interface IpcServiceDeps {
|
||||
getSubtitlePosition: () => unknown;
|
||||
getSubtitleStyle: () => unknown;
|
||||
saveSubtitlePosition: (position: SubtitlePosition) => void;
|
||||
getMecabStatus: () => {
|
||||
available: boolean;
|
||||
enabled: boolean;
|
||||
path: string | null;
|
||||
};
|
||||
setMecabEnabled: (enabled: boolean) => void;
|
||||
handleMpvCommand: (command: Array<string | number>) => void;
|
||||
getKeybindings: () => unknown;
|
||||
getMpvInputBindings?: () => Promise<MpvInputBindingsSnapshot>;
|
||||
getSessionBindings?: () => CompiledSessionBinding[] | Promise<CompiledSessionBinding[]>;
|
||||
getConfiguredShortcuts: () => unknown;
|
||||
@@ -103,7 +88,6 @@ export interface IpcServiceDeps {
|
||||
getOverlayNotificationPosition: () => string;
|
||||
getControllerConfig: () => ResolvedControllerConfig;
|
||||
saveControllerConfig: (update: ControllerConfigUpdate) => void | Promise<void>;
|
||||
saveControllerPreference: (update: ControllerPreferenceUpdate) => void | Promise<void>;
|
||||
getSecondarySubMode: () => unknown;
|
||||
getCurrentSecondarySub: () => string;
|
||||
focusMainWindow: () => void;
|
||||
@@ -125,10 +109,8 @@ export interface IpcServiceDeps {
|
||||
resolveMediaTimingReview?: (
|
||||
request: MediaTimingReviewResolveRequest,
|
||||
) => MediaTimingReviewActionResult | Promise<MediaTimingReviewActionResult>;
|
||||
getAnkiConnectStatus: () => boolean;
|
||||
getRuntimeOptions: () => unknown;
|
||||
setRuntimeOption: (id: RuntimeOptionId, value: RuntimeOptionValue) => unknown;
|
||||
cycleRuntimeOption: (id: RuntimeOptionId, direction: 1 | -1) => unknown;
|
||||
reportOverlayContentBounds: (
|
||||
payload: unknown,
|
||||
senderWindow: ElectronBrowserWindow | null,
|
||||
@@ -149,7 +131,6 @@ export interface IpcServiceDeps {
|
||||
getCharacterDictionaryManagerSnapshot?: () => Promise<unknown>;
|
||||
removeCharacterDictionaryManagedEntry?: (mediaId: number) => Promise<unknown>;
|
||||
moveCharacterDictionaryManagedEntry?: (mediaId: number, direction: 1 | -1) => Promise<unknown>;
|
||||
appendClipboardVideoToQueue: () => { ok: boolean; message: string };
|
||||
getChangelogSnapshot?: (options?: { refresh?: boolean }) => Promise<ChangelogSnapshot>;
|
||||
getPlaylistBrowserSnapshot: () => Promise<PlaylistBrowserSnapshot>;
|
||||
appendPlaylistBrowserFile: (filePath: string) => Promise<PlaylistBrowserMutationResult>;
|
||||
@@ -174,15 +155,6 @@ interface WindowLike {
|
||||
};
|
||||
}
|
||||
|
||||
interface MecabTokenizerLike {
|
||||
getStatus: () => {
|
||||
available: boolean;
|
||||
enabled: boolean;
|
||||
path: string | null;
|
||||
};
|
||||
setEnabled: (enabled: boolean) => void;
|
||||
}
|
||||
|
||||
interface MpvClientLike {
|
||||
currentSecondarySubText?: string;
|
||||
}
|
||||
@@ -342,7 +314,6 @@ function parseMediaTimingReviewResolveRequest(
|
||||
|
||||
export interface IpcDepsRuntimeOptions {
|
||||
getMainWindow: () => WindowLike | null;
|
||||
getVisibleOverlayVisibility: () => boolean;
|
||||
onOverlayModalClosed: (
|
||||
modal: OverlayHostedModal,
|
||||
senderWindow: ElectronBrowserWindow | null,
|
||||
@@ -364,12 +335,8 @@ export interface IpcDepsRuntimeOptions {
|
||||
actionId: string,
|
||||
noteId?: number,
|
||||
) => void | Promise<void>;
|
||||
openYomitanSettings: () => void;
|
||||
quitApp: () => void;
|
||||
toggleVisibleOverlay: () => void;
|
||||
tokenizeCurrentSubtitle: () => Promise<unknown>;
|
||||
getCurrentSubtitleRaw: () => string;
|
||||
getCurrentSubtitleAss: () => string;
|
||||
getSubtitleSidebarSnapshot?: () => Promise<SubtitleSidebarSnapshot>;
|
||||
getSubtitleSidebarPlaybackState?: () => SubtitleSidebarPlaybackState;
|
||||
getSubtitleSidebarOpen?: () => boolean;
|
||||
@@ -377,9 +344,7 @@ export interface IpcDepsRuntimeOptions {
|
||||
getSubtitlePosition: () => unknown;
|
||||
getSubtitleStyle: () => unknown;
|
||||
saveSubtitlePosition: (position: SubtitlePosition) => void;
|
||||
getMecabTokenizer: () => MecabTokenizerLike | null;
|
||||
handleMpvCommand: (command: Array<string | number>) => void;
|
||||
getKeybindings: () => unknown;
|
||||
getMpvInputBindings?: () => Promise<MpvInputBindingsSnapshot>;
|
||||
getSessionBindings?: () => CompiledSessionBinding[] | Promise<CompiledSessionBinding[]>;
|
||||
getConfiguredShortcuts: () => unknown;
|
||||
@@ -389,7 +354,6 @@ export interface IpcDepsRuntimeOptions {
|
||||
getOverlayNotificationPosition: () => string;
|
||||
getControllerConfig: () => ResolvedControllerConfig;
|
||||
saveControllerConfig: (update: ControllerConfigUpdate) => void | Promise<void>;
|
||||
saveControllerPreference: (update: ControllerPreferenceUpdate) => void | Promise<void>;
|
||||
getSecondarySubMode: () => unknown;
|
||||
getMpvClient: () => MpvClientLike | null;
|
||||
focusMainWindow: () => void;
|
||||
@@ -403,10 +367,8 @@ export interface IpcDepsRuntimeOptions {
|
||||
getMediaTimingReviewWaveform?: IpcServiceDeps['getMediaTimingReviewWaveform'];
|
||||
stopMediaTimingReviewPreview?: IpcServiceDeps['stopMediaTimingReviewPreview'];
|
||||
resolveMediaTimingReview?: IpcServiceDeps['resolveMediaTimingReview'];
|
||||
getAnkiConnectStatus: () => boolean;
|
||||
getRuntimeOptions: () => unknown;
|
||||
setRuntimeOption: (id: RuntimeOptionId, value: RuntimeOptionValue) => unknown;
|
||||
cycleRuntimeOption: (id: RuntimeOptionId, direction: 1 | -1) => unknown;
|
||||
reportOverlayContentBounds: (payload: unknown) => void;
|
||||
getAnilistStatus: () => unknown;
|
||||
clearAnilistToken: () => void;
|
||||
@@ -424,7 +386,6 @@ export interface IpcDepsRuntimeOptions {
|
||||
getCharacterDictionaryManagerSnapshot?: () => Promise<unknown>;
|
||||
removeCharacterDictionaryManagedEntry?: (mediaId: number) => Promise<unknown>;
|
||||
moveCharacterDictionaryManagedEntry?: (mediaId: number, direction: 1 | -1) => Promise<unknown>;
|
||||
appendClipboardVideoToQueue: () => { ok: boolean; message: string };
|
||||
getChangelogSnapshot?: (options?: { refresh?: boolean }) => Promise<ChangelogSnapshot>;
|
||||
getPlaylistBrowserSnapshot: () => Promise<PlaylistBrowserSnapshot>;
|
||||
appendPlaylistBrowserFile: (filePath: string) => Promise<PlaylistBrowserMutationResult>;
|
||||
@@ -444,19 +405,14 @@ export function createIpcDepsRuntime(options: IpcDepsRuntimeOptions): IpcService
|
||||
onOverlayMouseInteractionChanged: options.onOverlayMouseInteractionChanged,
|
||||
onOverlayInteractiveHint: options.onOverlayInteractiveHint,
|
||||
handleOverlayNotificationAction: options.handleOverlayNotificationAction,
|
||||
openYomitanSettings: options.openYomitanSettings,
|
||||
recordSubtitleMiningContext: options.recordSubtitleMiningContext,
|
||||
quitApp: options.quitApp,
|
||||
toggleDevTools: () => {
|
||||
const mainWindow = options.getMainWindow();
|
||||
if (!mainWindow || mainWindow.isDestroyed()) return;
|
||||
mainWindow.webContents.toggleDevTools();
|
||||
},
|
||||
getVisibleOverlayVisibility: options.getVisibleOverlayVisibility,
|
||||
toggleVisibleOverlay: options.toggleVisibleOverlay,
|
||||
tokenizeCurrentSubtitle: options.tokenizeCurrentSubtitle,
|
||||
getCurrentSubtitleRaw: options.getCurrentSubtitleRaw,
|
||||
getCurrentSubtitleAss: options.getCurrentSubtitleAss,
|
||||
getSubtitleSidebarSnapshot: options.getSubtitleSidebarSnapshot,
|
||||
getSubtitleSidebarPlaybackState: options.getSubtitleSidebarPlaybackState,
|
||||
getSubtitleSidebarOpen: options.getSubtitleSidebarOpen ?? (() => false),
|
||||
@@ -464,19 +420,7 @@ export function createIpcDepsRuntime(options: IpcDepsRuntimeOptions): IpcService
|
||||
getSubtitlePosition: options.getSubtitlePosition,
|
||||
getSubtitleStyle: options.getSubtitleStyle,
|
||||
saveSubtitlePosition: options.saveSubtitlePosition,
|
||||
getMecabStatus: () => {
|
||||
const mecabTokenizer = options.getMecabTokenizer();
|
||||
return mecabTokenizer
|
||||
? mecabTokenizer.getStatus()
|
||||
: { available: false, enabled: false, path: null };
|
||||
},
|
||||
setMecabEnabled: (enabled) => {
|
||||
const mecabTokenizer = options.getMecabTokenizer();
|
||||
if (!mecabTokenizer) return;
|
||||
mecabTokenizer.setEnabled(enabled);
|
||||
},
|
||||
handleMpvCommand: options.handleMpvCommand,
|
||||
getKeybindings: options.getKeybindings,
|
||||
getMpvInputBindings: options.getMpvInputBindings,
|
||||
getSessionBindings: options.getSessionBindings ?? (() => []),
|
||||
getConfiguredShortcuts: options.getConfiguredShortcuts,
|
||||
@@ -486,7 +430,6 @@ export function createIpcDepsRuntime(options: IpcDepsRuntimeOptions): IpcService
|
||||
getOverlayNotificationPosition: options.getOverlayNotificationPosition,
|
||||
getControllerConfig: options.getControllerConfig,
|
||||
saveControllerConfig: options.saveControllerConfig,
|
||||
saveControllerPreference: options.saveControllerPreference,
|
||||
getSecondarySubMode: options.getSecondarySubMode,
|
||||
getCurrentSecondarySub: () => options.getMpvClient()?.currentSecondarySubText || '',
|
||||
focusMainWindow: () => {
|
||||
@@ -503,10 +446,8 @@ export function createIpcDepsRuntime(options: IpcDepsRuntimeOptions): IpcService
|
||||
getMediaTimingReviewWaveform: options.getMediaTimingReviewWaveform,
|
||||
stopMediaTimingReviewPreview: options.stopMediaTimingReviewPreview,
|
||||
resolveMediaTimingReview: options.resolveMediaTimingReview,
|
||||
getAnkiConnectStatus: options.getAnkiConnectStatus,
|
||||
getRuntimeOptions: options.getRuntimeOptions,
|
||||
setRuntimeOption: options.setRuntimeOption,
|
||||
cycleRuntimeOption: options.cycleRuntimeOption,
|
||||
reportOverlayContentBounds: (payload, senderWindow) => {
|
||||
const mainWindow = options.getMainWindow();
|
||||
if (!mainWindow || mainWindow.isDestroyed()) return;
|
||||
@@ -553,7 +494,6 @@ export function createIpcDepsRuntime(options: IpcDepsRuntimeOptions): IpcService
|
||||
message: 'Character dictionary manager unavailable.',
|
||||
entries: [],
|
||||
})),
|
||||
appendClipboardVideoToQueue: options.appendClipboardVideoToQueue,
|
||||
getChangelogSnapshot: options.getChangelogSnapshot,
|
||||
getPlaylistBrowserSnapshot: options.getPlaylistBrowserSnapshot,
|
||||
appendPlaylistBrowserFile: options.appendPlaylistBrowserFile,
|
||||
@@ -680,10 +620,6 @@ export function registerIpcHandlers(deps: IpcServiceDeps, ipc: IpcMainRegistrar
|
||||
},
|
||||
);
|
||||
|
||||
ipc.on(IPC_CHANNELS.command.openYomitanSettings, () => {
|
||||
deps.openYomitanSettings();
|
||||
});
|
||||
|
||||
ipc.on(IPC_CHANNELS.command.recordYomitanLookup, (_event: unknown, payload: unknown) => {
|
||||
try {
|
||||
deps.recordSubtitleMiningContext?.(parseSubtitleMiningContext(payload));
|
||||
@@ -711,22 +647,10 @@ export function registerIpcHandlers(deps: IpcServiceDeps, ipc: IpcMainRegistrar
|
||||
return marked;
|
||||
});
|
||||
|
||||
ipc.on(IPC_CHANNELS.command.quitApp, () => {
|
||||
deps.quitApp();
|
||||
});
|
||||
|
||||
ipc.on(IPC_CHANNELS.command.toggleDevTools, () => {
|
||||
deps.toggleDevTools();
|
||||
});
|
||||
|
||||
ipc.on(IPC_CHANNELS.command.toggleOverlay, () => {
|
||||
deps.toggleVisibleOverlay();
|
||||
});
|
||||
|
||||
ipc.handle(IPC_CHANNELS.request.getVisibleOverlayVisibility, () => {
|
||||
return deps.getVisibleOverlayVisibility();
|
||||
});
|
||||
|
||||
ipc.handle(IPC_CHANNELS.request.getCurrentSubtitle, async () => {
|
||||
return await deps.tokenizeCurrentSubtitle();
|
||||
});
|
||||
@@ -735,10 +659,6 @@ export function registerIpcHandlers(deps: IpcServiceDeps, ipc: IpcMainRegistrar
|
||||
return deps.getCurrentSubtitleRaw();
|
||||
});
|
||||
|
||||
ipc.handle(IPC_CHANNELS.request.getCurrentSubtitleAss, () => {
|
||||
return deps.getCurrentSubtitleAss();
|
||||
});
|
||||
|
||||
ipc.handle(IPC_CHANNELS.request.getSubtitleSidebarSnapshot, async () => {
|
||||
if (!deps.getSubtitleSidebarSnapshot) {
|
||||
throw new Error('Subtitle sidebar snapshot is unavailable.');
|
||||
@@ -775,17 +695,6 @@ export function registerIpcHandlers(deps: IpcServiceDeps, ipc: IpcMainRegistrar
|
||||
deps.saveSubtitlePosition(parsedPosition);
|
||||
});
|
||||
|
||||
ipc.handle(
|
||||
IPC_CHANNELS.command.saveControllerPreference,
|
||||
async (_event: unknown, update: unknown) => {
|
||||
const parsedUpdate = parseControllerPreferenceUpdate(update);
|
||||
if (!parsedUpdate) {
|
||||
throw new Error('Invalid controller preference payload');
|
||||
}
|
||||
await deps.saveControllerPreference(parsedUpdate);
|
||||
},
|
||||
);
|
||||
|
||||
ipc.handle(
|
||||
IPC_CHANNELS.command.saveControllerConfig,
|
||||
async (_event: unknown, update: unknown) => {
|
||||
@@ -797,15 +706,6 @@ export function registerIpcHandlers(deps: IpcServiceDeps, ipc: IpcMainRegistrar
|
||||
},
|
||||
);
|
||||
|
||||
ipc.handle(IPC_CHANNELS.request.getMecabStatus, () => {
|
||||
return deps.getMecabStatus();
|
||||
});
|
||||
|
||||
ipc.on(IPC_CHANNELS.command.setMecabEnabled, (_event: unknown, enabled: unknown) => {
|
||||
if (typeof enabled !== 'boolean') return;
|
||||
deps.setMecabEnabled(enabled);
|
||||
});
|
||||
|
||||
ipc.on(IPC_CHANNELS.command.mpvCommand, (_event: unknown, command: unknown) => {
|
||||
const parsedCommand = parseMpvCommand(command);
|
||||
if (!parsedCommand) return;
|
||||
@@ -823,10 +723,6 @@ export function registerIpcHandlers(deps: IpcServiceDeps, ipc: IpcMainRegistrar
|
||||
},
|
||||
);
|
||||
|
||||
ipc.handle(IPC_CHANNELS.request.getKeybindings, () => {
|
||||
return deps.getKeybindings();
|
||||
});
|
||||
|
||||
ipc.handle(IPC_CHANNELS.request.getMpvInputBindings, () => {
|
||||
return deps.getMpvInputBindings?.() ?? { keys: [], blockedKeys: [] };
|
||||
});
|
||||
@@ -879,10 +775,6 @@ export function registerIpcHandlers(deps: IpcServiceDeps, ipc: IpcMainRegistrar
|
||||
return await deps.runSubsyncManual(parsedRequest);
|
||||
});
|
||||
|
||||
ipc.handle(IPC_CHANNELS.request.getAnkiConnectStatus, () => {
|
||||
return deps.getAnkiConnectStatus();
|
||||
});
|
||||
|
||||
ipc.handle(IPC_CHANNELS.request.getRuntimeOptions, () => {
|
||||
return deps.getRuntimeOptions();
|
||||
});
|
||||
@@ -899,18 +791,6 @@ export function registerIpcHandlers(deps: IpcServiceDeps, ipc: IpcMainRegistrar
|
||||
return deps.setRuntimeOption(parsedId, parsedValue);
|
||||
});
|
||||
|
||||
ipc.handle(IPC_CHANNELS.request.cycleRuntimeOption, (_event, id: unknown, direction: unknown) => {
|
||||
const parsedId = parseRuntimeOptionId(id);
|
||||
if (!parsedId) {
|
||||
return { ok: false, error: 'Invalid runtime option id' };
|
||||
}
|
||||
const parsedDirection = parseRuntimeOptionDirection(direction);
|
||||
if (!parsedDirection) {
|
||||
return { ok: false, error: 'Invalid runtime option cycle direction' };
|
||||
}
|
||||
return deps.cycleRuntimeOption(parsedId, parsedDirection);
|
||||
});
|
||||
|
||||
ipc.on(IPC_CHANNELS.command.reportOverlayContentBounds, (event: unknown, payload: unknown) => {
|
||||
const senderWindow =
|
||||
electron.BrowserWindow?.fromWebContents((event as IpcMainEvent).sender) ?? null;
|
||||
@@ -1020,10 +900,6 @@ export function registerIpcHandlers(deps: IpcServiceDeps, ipc: IpcMainRegistrar
|
||||
},
|
||||
);
|
||||
|
||||
ipc.handle(IPC_CHANNELS.request.appendClipboardVideoToQueue, () => {
|
||||
return deps.appendClipboardVideoToQueue();
|
||||
});
|
||||
|
||||
ipc.handle(IPC_CHANNELS.request.getChangelogSnapshot, async (_event, payload: unknown) => {
|
||||
const refresh =
|
||||
typeof payload === 'object' && payload !== null && 'refresh' in payload
|
||||
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
MPV_REQUEST_ID_SUB_SHADOW_OFFSET,
|
||||
MPV_REQUEST_ID_SUB_SPACING,
|
||||
MPV_REQUEST_ID_SUBTEXT,
|
||||
MPV_REQUEST_ID_SUBTEXT_ASS,
|
||||
MPV_REQUEST_ID_SUB_USE_MARGINS,
|
||||
MPV_REQUEST_ID_PAUSE,
|
||||
} from './mpv-protocol';
|
||||
@@ -54,7 +53,6 @@ const MPV_SUBTITLE_PROPERTY_OBSERVATIONS: string[] = [
|
||||
'sub-scale-by-window',
|
||||
'osd-height',
|
||||
'osd-dimensions',
|
||||
'sub-text/ass',
|
||||
'sub-border-size',
|
||||
'sub-shadow-offset',
|
||||
'sub-ass-override',
|
||||
@@ -76,10 +74,6 @@ const MPV_INITIAL_PROPERTY_REQUESTS: Array<MpvProtocolCommand> = [
|
||||
command: ['get_property', 'sub-text'],
|
||||
request_id: MPV_REQUEST_ID_SUBTEXT,
|
||||
},
|
||||
{
|
||||
command: ['get_property', 'sub-text/ass'],
|
||||
request_id: MPV_REQUEST_ID_SUBTEXT_ASS,
|
||||
},
|
||||
{
|
||||
command: ['get_property', 'path'],
|
||||
request_id: MPV_REQUEST_ID_PATH,
|
||||
|
||||
@@ -59,7 +59,6 @@ function createDeps(overrides: Partial<MpvProtocolHandleMessageDeps> = {}): {
|
||||
getSubtitleMetrics: () => metrics,
|
||||
isVisibleOverlayVisible: () => false,
|
||||
emitSubtitleChange: (payload) => state.events.push(payload),
|
||||
emitSubtitleAssChange: (payload) => state.events.push(payload),
|
||||
emitSubtitleTiming: (payload) => state.events.push(payload),
|
||||
emitSecondarySubtitleChange: (payload) => state.events.push(payload),
|
||||
emitSubtitleTrackChange: (payload) => state.events.push(payload),
|
||||
@@ -132,28 +131,6 @@ test('dispatchMpvProtocolMessage emits subtitle text on property change', async
|
||||
assert.deepEqual(state.events, [{ text: '字幕', isOverlayVisible: false }]);
|
||||
});
|
||||
|
||||
test('dispatchMpvProtocolMessage emits ASS subtitle text from the current mpv property', async () => {
|
||||
const { deps, state } = createDeps();
|
||||
|
||||
await dispatchMpvProtocolMessage(
|
||||
{ event: 'property-change', name: 'sub-text/ass', data: '{\\b1}字幕' },
|
||||
deps,
|
||||
);
|
||||
|
||||
assert.deepEqual(state.events, [{ text: '{\\b1}字幕' }]);
|
||||
});
|
||||
|
||||
test('dispatchMpvProtocolMessage emits ASS subtitle text from the legacy mpv property', async () => {
|
||||
const { deps, state } = createDeps();
|
||||
|
||||
await dispatchMpvProtocolMessage(
|
||||
{ event: 'property-change', name: 'sub-text-ass', data: '{\\b1}字幕' },
|
||||
deps,
|
||||
);
|
||||
|
||||
assert.deepEqual(state.events, [{ text: '{\\b1}字幕' }]);
|
||||
});
|
||||
|
||||
test('dispatchMpvProtocolMessage emits subtitle track changes', async () => {
|
||||
const { deps, state } = createDeps({
|
||||
emitSubtitleTrackChange: (payload) => state.events.push(payload),
|
||||
|
||||
@@ -24,7 +24,6 @@ export const MPV_REQUEST_ID_SUB_FONT = 111;
|
||||
export const MPV_REQUEST_ID_SUB_SCALE_BY_WINDOW = 112;
|
||||
export const MPV_REQUEST_ID_OSD_HEIGHT = 113;
|
||||
export const MPV_REQUEST_ID_OSD_DIMENSIONS = 114;
|
||||
export const MPV_REQUEST_ID_SUBTEXT_ASS = 115;
|
||||
export const MPV_REQUEST_ID_SUB_SPACING = 116;
|
||||
export const MPV_REQUEST_ID_SUB_BOLD = 117;
|
||||
export const MPV_REQUEST_ID_SUB_ITALIC = 118;
|
||||
@@ -52,7 +51,6 @@ export interface MpvProtocolHandleMessageDeps {
|
||||
getSubtitleMetrics: () => MpvSubtitleRenderMetrics;
|
||||
isVisibleOverlayVisible: () => boolean;
|
||||
emitSubtitleChange: (payload: { text: string; isOverlayVisible: boolean }) => void;
|
||||
emitSubtitleAssChange: (payload: { text: string }) => void;
|
||||
emitSubtitleTiming: (payload: { text: string; start: number; end: number }) => void;
|
||||
emitSecondarySubtitleChange: (payload: { text: string }) => void;
|
||||
emitSubtitleTrackChange: (payload: { sid: number | null }) => void;
|
||||
@@ -253,8 +251,6 @@ export async function dispatchMpvProtocolMessage(
|
||||
isOverlayVisible: overlayVisible,
|
||||
});
|
||||
deps.setCurrentSubText(nextSubText);
|
||||
} else if (msg.name === 'sub-text/ass' || msg.name === 'sub-text-ass') {
|
||||
deps.emitSubtitleAssChange({ text: (msg.data as string) || '' });
|
||||
} else if (msg.name === 'sub-start') {
|
||||
deps.setCurrentSubStart((msg.data as number) || 0);
|
||||
deps.emitSubtitleTiming({
|
||||
@@ -468,8 +464,6 @@ export async function dispatchMpvProtocolMessage(
|
||||
text: nextSubText,
|
||||
isOverlayVisible: deps.isVisibleOverlayVisible(),
|
||||
});
|
||||
} else if (msg.request_id === MPV_REQUEST_ID_SUBTEXT_ASS) {
|
||||
deps.emitSubtitleAssChange({ text: (msg.data as string) || '' });
|
||||
} else if (msg.request_id === MPV_REQUEST_ID_PATH) {
|
||||
deps.emitMediaPathChange({ path: (msg.data as string) || '' });
|
||||
} else if (msg.request_id === MPV_REQUEST_ID_MEDIA_TITLE) {
|
||||
|
||||
@@ -444,17 +444,6 @@ test('MpvIpcClient reconnect replays property subscriptions and initial state re
|
||||
(command as { command: unknown[] }).command[1] === 1 &&
|
||||
(command as { command: unknown[] }).command[2] === 'sub-text',
|
||||
);
|
||||
const hasAssSubtitleSubscription = commands.some(
|
||||
(command) =>
|
||||
Array.isArray((command as { command: unknown[] }).command) &&
|
||||
(command as { command: unknown[] }).command[0] === 'observe_property' &&
|
||||
(command as { command: unknown[] }).command[2] === 'sub-text/ass',
|
||||
);
|
||||
const hasDeprecatedAssSubtitleProperty = commands.some(
|
||||
(command) =>
|
||||
Array.isArray((command as { command: unknown[] }).command) &&
|
||||
(command as { command: unknown[] }).command.includes('sub-text-ass'),
|
||||
);
|
||||
const hasPathRequest = commands.some(
|
||||
(command) =>
|
||||
Array.isArray((command as { command: unknown[] }).command) &&
|
||||
@@ -464,8 +453,6 @@ test('MpvIpcClient reconnect replays property subscriptions and initial state re
|
||||
|
||||
assert.equal(hasSecondaryVisibilityReset, true);
|
||||
assert.equal(hasTrackSubscription, true);
|
||||
assert.equal(hasAssSubtitleSubscription, true);
|
||||
assert.equal(hasDeprecatedAssSubtitleProperty, false);
|
||||
assert.equal(hasPathRequest, true);
|
||||
});
|
||||
|
||||
|
||||
@@ -123,7 +123,6 @@ export interface MpvIpcClientDeps extends MpvIpcClientProtocolDeps {}
|
||||
export interface MpvIpcClientEventMap {
|
||||
'connection-change': { connected: boolean };
|
||||
'subtitle-change': { text: string; isOverlayVisible: boolean };
|
||||
'subtitle-ass-change': { text: string };
|
||||
'subtitle-timing': { text: string; start: number; end: number };
|
||||
'time-pos-change': { time: number };
|
||||
'duration-change': { duration: number };
|
||||
@@ -417,9 +416,6 @@ export class MpvIpcClient implements MpvClient {
|
||||
emitSubtitleChange: (payload) => {
|
||||
this.emit('subtitle-change', payload);
|
||||
},
|
||||
emitSubtitleAssChange: (payload) => {
|
||||
this.emit('subtitle-ass-change', payload);
|
||||
},
|
||||
emitSubtitleTiming: (payload) => {
|
||||
this.emit('subtitle-timing', payload);
|
||||
},
|
||||
|
||||
@@ -2,7 +2,6 @@ import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {
|
||||
applyRuntimeOptionResultRuntime,
|
||||
cycleRuntimeOptionFromIpcRuntime,
|
||||
setRuntimeOptionFromIpcRuntime,
|
||||
} from './runtime-options-ipc';
|
||||
|
||||
@@ -25,21 +24,3 @@ test('setRuntimeOptionFromIpcRuntime returns unavailable when manager missing',
|
||||
assert.equal(result.error, 'Runtime options manager unavailable');
|
||||
assert.deepEqual(osd, []);
|
||||
});
|
||||
|
||||
test('cycleRuntimeOptionFromIpcRuntime reports errors once', () => {
|
||||
const osd: string[] = [];
|
||||
const result = cycleRuntimeOptionFromIpcRuntime(
|
||||
{
|
||||
setOptionValue: () => ({ ok: true }),
|
||||
cycleOption: () => ({ ok: false, error: 'bad option' }),
|
||||
},
|
||||
'anki.kikuFieldGrouping',
|
||||
1,
|
||||
(text) => {
|
||||
osd.push(text);
|
||||
},
|
||||
);
|
||||
assert.equal(result.ok, false);
|
||||
assert.equal(result.error, 'bad option');
|
||||
assert.deepEqual(osd, ['bad option']);
|
||||
});
|
||||
@@ -34,19 +34,3 @@ export function setRuntimeOptionFromIpcRuntime(
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function cycleRuntimeOptionFromIpcRuntime(
|
||||
manager: RuntimeOptionsManagerLike | null,
|
||||
id: RuntimeOptionId,
|
||||
direction: 1 | -1,
|
||||
showMpvOsd: (text: string) => void,
|
||||
): RuntimeOptionApplyResult {
|
||||
if (!manager) {
|
||||
return { ok: false, error: 'Runtime options manager unavailable' };
|
||||
}
|
||||
const result = applyRuntimeOptionResultRuntime(manager.cycleOption(id, direction), showMpvOsd);
|
||||
if (!result.ok && result.error) {
|
||||
showMpvOsd(result.error);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
-50
@@ -4767,7 +4767,6 @@ const {
|
||||
autoplaySubtitlePrimingRuntime.resetAutoplaySubtitlePrime();
|
||||
lastObservedTimePos = 0;
|
||||
appState.currentSubText = '';
|
||||
appState.currentSubAssText = '';
|
||||
appState.currentSubtitleData = null;
|
||||
if (!preserveParsedSubtitleCues) {
|
||||
appState.activeParsedSubtitleCues = [];
|
||||
@@ -5723,7 +5722,6 @@ const { registerIpcRuntimeHandlers } = composeIpcRuntimeHandlers({
|
||||
stopMediaTimingReviewPreview: (reviewId) => mediaTimingReviewRuntime.stopPreview(reviewId),
|
||||
resolveMediaTimingReview: (request) => mediaTimingReviewRuntime.resolveReview(request),
|
||||
getMainWindow: () => overlayManager.getMainWindow(),
|
||||
getVisibleOverlayVisibility: () => overlayManager.getVisibleOverlayVisible(),
|
||||
focusMainWindow: () => {
|
||||
const mainWindow = overlayManager.getMainWindow();
|
||||
if (!mainWindow || mainWindow.isDestroyed()) return;
|
||||
@@ -5837,14 +5835,11 @@ const { registerIpcRuntimeHandlers } = composeIpcRuntimeHandlers({
|
||||
}
|
||||
},
|
||||
onYoutubePickerResolve: (request) => youtubeFlowRuntime.resolveActivePicker(request),
|
||||
openYomitanSettings: () => openYomitanSettings(),
|
||||
// Overlay lookups carry no cue context of their own; fall back to snapshotting the
|
||||
// live mpv sub timings at lookup time so media generation clips the mined line even
|
||||
// when extraction finishes long after playback has moved on.
|
||||
recordSubtitleMiningContext: (context) =>
|
||||
recordSubtitleMiningContext(context ?? captureCurrentPrimarySubtitleMiningContext()),
|
||||
quitApp: () => requestAppQuit(),
|
||||
toggleVisibleOverlay: () => toggleVisibleOverlay(),
|
||||
tokenizeCurrentSubtitle: async () => {
|
||||
const tokenizeSubtitleForCurrent = tokenizeSubtitleDeferred;
|
||||
return resolveCurrentSubtitleForRenderer({
|
||||
@@ -5862,7 +5857,6 @@ const { registerIpcRuntimeHandlers } = composeIpcRuntimeHandlers({
|
||||
});
|
||||
},
|
||||
getCurrentSubtitleRaw: () => appState.currentSubText,
|
||||
getCurrentSubtitleAss: () => appState.currentSubAssText,
|
||||
getSubtitleSidebarOpen: () => subtitleSidebarRequestedOpen,
|
||||
getSubtitleSidebarPlaybackState: () => getSubtitleSidebarPlaybackState(),
|
||||
// Full snapshot: resolves the active source over mpv IPC and re-parses it when it changed.
|
||||
@@ -5942,8 +5936,6 @@ const { registerIpcRuntimeHandlers } = composeIpcRuntimeHandlers({
|
||||
return resolveSubtitleStyleForRenderer(resolvedConfig);
|
||||
},
|
||||
saveSubtitlePosition: (position) => saveSubtitlePosition(position),
|
||||
getMecabTokenizer: () => appState.mecabTokenizer,
|
||||
getKeybindings: () => appState.keybindings,
|
||||
getMpvInputBindings: async () => {
|
||||
await refreshMpvSessionBindings();
|
||||
return readMpvInputBindings({
|
||||
@@ -5973,17 +5965,8 @@ const { registerIpcRuntimeHandlers } = composeIpcRuntimeHandlers({
|
||||
controller: applyControllerConfigUpdate(currentRawConfig.controller, update),
|
||||
});
|
||||
},
|
||||
saveControllerPreference: ({ preferredGamepadId, preferredGamepadLabel }) => {
|
||||
configService.patchRawConfig({
|
||||
controller: {
|
||||
preferredGamepadId,
|
||||
preferredGamepadLabel,
|
||||
},
|
||||
});
|
||||
},
|
||||
getSecondarySubMode: () => appState.secondarySubMode,
|
||||
getMpvClient: () => appState.mpvClient,
|
||||
getAnkiConnectStatus: () => appState.ankiIntegration !== null,
|
||||
getRuntimeOptions: () => getRuntimeOptionsStateHandler(),
|
||||
reportOverlayContentBounds: (payload: unknown) => {
|
||||
if (overlayContentMeasurementStore.report(payload)) {
|
||||
@@ -6071,47 +6054,14 @@ const { registerIpcRuntimeHandlers } = composeIpcRuntimeHandlers({
|
||||
}
|
||||
return result;
|
||||
},
|
||||
appendClipboardVideoToQueue: () => appendClipboardVideoToQueueHandler(),
|
||||
getChangelogSnapshot: (options) => getChangelogSnapshot(options),
|
||||
...playlistBrowserMainDeps,
|
||||
getImmersionTracker: () => appState.immersionTracker,
|
||||
},
|
||||
ankiJimakuDeps: createAnkiJimakuIpcRuntimeServiceDeps({
|
||||
patchAnkiConnectEnabled: (enabled: boolean) => {
|
||||
configService.patchRawConfig({ ankiConnect: { enabled } });
|
||||
},
|
||||
getResolvedConfig: () => configService.getConfig(),
|
||||
getRuntimeOptionsManager: () => appState.runtimeOptionsManager,
|
||||
getSubtitleTimingTracker: () => appState.subtitleTimingTracker,
|
||||
getMpvClient: () => appState.mpvClient,
|
||||
getAnkiIntegration: () => appState.ankiIntegration,
|
||||
setAnkiIntegration: (integration: AnkiIntegration | null) => {
|
||||
appState.ankiIntegration = integration;
|
||||
appState.ankiIntegration?.setRecordCardsMinedCallback(recordTrackedCardsMined);
|
||||
appState.ankiIntegration?.setKnownWordCacheUpdatedCallback(
|
||||
refreshCurrentSubtitleAnnotations,
|
||||
);
|
||||
appState.ankiIntegration?.setSubtitleMiningContextConsumer(
|
||||
consumePendingSubtitleMiningContext,
|
||||
);
|
||||
appState.ankiIntegration?.setMediaTimingReviewCallback(
|
||||
mediaTimingReviewRuntime.requestReview,
|
||||
);
|
||||
appState.ankiIntegration?.setSentenceFuriganaGenerator(generateMiningSentenceFurigana);
|
||||
},
|
||||
getKnownWordCacheStatePath: () => path.join(USER_DATA_PATH, 'known-words-cache.json'),
|
||||
getCachedMediaPath: (currentVideoPath, kind) =>
|
||||
getCachedYoutubeMediaPathForCurrentPlayback(currentVideoPath, kind),
|
||||
shouldRequireRemoteMediaCache: () => shouldRequireYoutubeMediaCacheForCurrentPlayback(),
|
||||
getYoutubeMediaSourceUrl: () => getCurrentYoutubeMediaCacheSourceUrl(),
|
||||
showDesktopNotification,
|
||||
showOverlayNotification: (payload) =>
|
||||
overlayNotificationsRuntime.showOverlayNotification(payload),
|
||||
dismissOverlayNotification: (id) =>
|
||||
overlayNotificationsRuntime.dismissOverlayNotification(id),
|
||||
createFieldGroupingCallback: () => createFieldGroupingCallback(),
|
||||
broadcastRuntimeOptionsChanged: () =>
|
||||
overlayVisibilityComposer.broadcastRuntimeOptionsChanged(),
|
||||
getFieldGroupingResolver: () => getFieldGroupingResolverHandler(),
|
||||
setFieldGroupingResolver: (resolver: ((choice: KikuFieldGroupingChoice) => void) | null) =>
|
||||
setFieldGroupingResolverHandler(resolver),
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import { RuntimeOptionId, RuntimeOptionValue, SubsyncManualPayload } from '../types';
|
||||
import type { OverlayNotificationPayload } from '../types/notification';
|
||||
import { SubsyncResolvedConfig } from '../subsync/utils';
|
||||
import type { SubsyncRuntimeDeps } from '../core/services/subsync-runner';
|
||||
import type { IpcDepsRuntimeOptions } from '../core/services/ipc';
|
||||
import type { AnkiJimakuIpcRuntimeOptions } from '../core/services/anki-jimaku';
|
||||
import type { CliCommandDepsRuntimeOptions } from '../core/services/cli-command';
|
||||
import type { HandleMpvCommandFromIpcOptions } from '../core/services/ipc-command';
|
||||
import {
|
||||
cycleRuntimeOptionFromIpcRuntime,
|
||||
setRuntimeOptionFromIpcRuntime,
|
||||
} from '../core/services/runtime-options-ipc';
|
||||
import { setRuntimeOptionFromIpcRuntime } from '../core/services/runtime-options-ipc';
|
||||
import { RuntimeOptionsManager } from '../runtime-options';
|
||||
|
||||
export interface RuntimeOptionsIpcDepsParams {
|
||||
@@ -28,17 +24,12 @@ export interface SubsyncRuntimeDepsParams {
|
||||
|
||||
export function createRuntimeOptionsIpcDeps(params: RuntimeOptionsIpcDepsParams): {
|
||||
setRuntimeOption: (id: RuntimeOptionId, value: RuntimeOptionValue) => unknown;
|
||||
cycleRuntimeOption: (id: RuntimeOptionId, direction: 1 | -1) => unknown;
|
||||
} {
|
||||
return {
|
||||
setRuntimeOption: (id, value) =>
|
||||
setRuntimeOptionFromIpcRuntime(params.getRuntimeOptionsManager(), id, value, (text) =>
|
||||
params.showMpvOsd(text),
|
||||
),
|
||||
cycleRuntimeOption: (id, direction) =>
|
||||
cycleRuntimeOptionFromIpcRuntime(params.getRuntimeOptionsManager(), id, direction, (text) =>
|
||||
params.showMpvOsd(text),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -55,7 +46,6 @@ export function createSubsyncRuntimeDeps(params: SubsyncRuntimeDepsParams): Subs
|
||||
|
||||
export interface MainIpcRuntimeServiceDepsParams {
|
||||
getMainWindow: IpcDepsRuntimeOptions['getMainWindow'];
|
||||
getVisibleOverlayVisibility: IpcDepsRuntimeOptions['getVisibleOverlayVisibility'];
|
||||
onOverlayModalClosed: IpcDepsRuntimeOptions['onOverlayModalClosed'];
|
||||
onOverlayModalOpened?: IpcDepsRuntimeOptions['onOverlayModalOpened'];
|
||||
onOverlayMouseInteractionChanged?: IpcDepsRuntimeOptions['onOverlayMouseInteractionChanged'];
|
||||
@@ -67,12 +57,8 @@ export interface MainIpcRuntimeServiceDepsParams {
|
||||
getMediaTimingReviewWaveform?: IpcDepsRuntimeOptions['getMediaTimingReviewWaveform'];
|
||||
stopMediaTimingReviewPreview?: IpcDepsRuntimeOptions['stopMediaTimingReviewPreview'];
|
||||
resolveMediaTimingReview?: IpcDepsRuntimeOptions['resolveMediaTimingReview'];
|
||||
openYomitanSettings: IpcDepsRuntimeOptions['openYomitanSettings'];
|
||||
quitApp: IpcDepsRuntimeOptions['quitApp'];
|
||||
toggleVisibleOverlay: IpcDepsRuntimeOptions['toggleVisibleOverlay'];
|
||||
tokenizeCurrentSubtitle: IpcDepsRuntimeOptions['tokenizeCurrentSubtitle'];
|
||||
getCurrentSubtitleRaw: IpcDepsRuntimeOptions['getCurrentSubtitleRaw'];
|
||||
getCurrentSubtitleAss: IpcDepsRuntimeOptions['getCurrentSubtitleAss'];
|
||||
getSubtitleSidebarSnapshot?: IpcDepsRuntimeOptions['getSubtitleSidebarSnapshot'];
|
||||
getSubtitleSidebarPlaybackState?: IpcDepsRuntimeOptions['getSubtitleSidebarPlaybackState'];
|
||||
getSubtitleSidebarOpen?: IpcDepsRuntimeOptions['getSubtitleSidebarOpen'];
|
||||
@@ -82,9 +68,7 @@ export interface MainIpcRuntimeServiceDepsParams {
|
||||
getSubtitlePosition: IpcDepsRuntimeOptions['getSubtitlePosition'];
|
||||
getSubtitleStyle: IpcDepsRuntimeOptions['getSubtitleStyle'];
|
||||
saveSubtitlePosition: IpcDepsRuntimeOptions['saveSubtitlePosition'];
|
||||
getMecabTokenizer: IpcDepsRuntimeOptions['getMecabTokenizer'];
|
||||
handleMpvCommand: IpcDepsRuntimeOptions['handleMpvCommand'];
|
||||
getKeybindings: IpcDepsRuntimeOptions['getKeybindings'];
|
||||
getMpvInputBindings?: IpcDepsRuntimeOptions['getMpvInputBindings'];
|
||||
getSessionBindings: IpcDepsRuntimeOptions['getSessionBindings'];
|
||||
getConfiguredShortcuts: IpcDepsRuntimeOptions['getConfiguredShortcuts'];
|
||||
@@ -94,14 +78,11 @@ export interface MainIpcRuntimeServiceDepsParams {
|
||||
getOverlayNotificationPosition: IpcDepsRuntimeOptions['getOverlayNotificationPosition'];
|
||||
getControllerConfig: IpcDepsRuntimeOptions['getControllerConfig'];
|
||||
saveControllerConfig: IpcDepsRuntimeOptions['saveControllerConfig'];
|
||||
saveControllerPreference: IpcDepsRuntimeOptions['saveControllerPreference'];
|
||||
getSecondarySubMode: IpcDepsRuntimeOptions['getSecondarySubMode'];
|
||||
getMpvClient: IpcDepsRuntimeOptions['getMpvClient'];
|
||||
runSubsyncManual: IpcDepsRuntimeOptions['runSubsyncManual'];
|
||||
getAnkiConnectStatus: IpcDepsRuntimeOptions['getAnkiConnectStatus'];
|
||||
getRuntimeOptions: IpcDepsRuntimeOptions['getRuntimeOptions'];
|
||||
setRuntimeOption: IpcDepsRuntimeOptions['setRuntimeOption'];
|
||||
cycleRuntimeOption: IpcDepsRuntimeOptions['cycleRuntimeOption'];
|
||||
reportOverlayContentBounds: IpcDepsRuntimeOptions['reportOverlayContentBounds'];
|
||||
getAnilistStatus: IpcDepsRuntimeOptions['getAnilistStatus'];
|
||||
clearAnilistToken: IpcDepsRuntimeOptions['clearAnilistToken'];
|
||||
@@ -115,7 +96,6 @@ export interface MainIpcRuntimeServiceDepsParams {
|
||||
getCharacterDictionaryManagerSnapshot?: IpcDepsRuntimeOptions['getCharacterDictionaryManagerSnapshot'];
|
||||
removeCharacterDictionaryManagedEntry?: IpcDepsRuntimeOptions['removeCharacterDictionaryManagedEntry'];
|
||||
moveCharacterDictionaryManagedEntry?: IpcDepsRuntimeOptions['moveCharacterDictionaryManagedEntry'];
|
||||
appendClipboardVideoToQueue: IpcDepsRuntimeOptions['appendClipboardVideoToQueue'];
|
||||
getChangelogSnapshot?: IpcDepsRuntimeOptions['getChangelogSnapshot'];
|
||||
getPlaylistBrowserSnapshot: IpcDepsRuntimeOptions['getPlaylistBrowserSnapshot'];
|
||||
appendPlaylistBrowserFile: IpcDepsRuntimeOptions['appendPlaylistBrowserFile'];
|
||||
@@ -126,22 +106,9 @@ export interface MainIpcRuntimeServiceDepsParams {
|
||||
}
|
||||
|
||||
export interface AnkiJimakuIpcRuntimeServiceDepsParams {
|
||||
patchAnkiConnectEnabled: AnkiJimakuIpcRuntimeOptions['patchAnkiConnectEnabled'];
|
||||
getResolvedConfig: AnkiJimakuIpcRuntimeOptions['getResolvedConfig'];
|
||||
getRuntimeOptionsManager: AnkiJimakuIpcRuntimeOptions['getRuntimeOptionsManager'];
|
||||
getSubtitleTimingTracker: AnkiJimakuIpcRuntimeOptions['getSubtitleTimingTracker'];
|
||||
getMpvClient: AnkiJimakuIpcRuntimeOptions['getMpvClient'];
|
||||
getAnkiIntegration: AnkiJimakuIpcRuntimeOptions['getAnkiIntegration'];
|
||||
setAnkiIntegration: AnkiJimakuIpcRuntimeOptions['setAnkiIntegration'];
|
||||
getKnownWordCacheStatePath: AnkiJimakuIpcRuntimeOptions['getKnownWordCacheStatePath'];
|
||||
getCachedMediaPath?: AnkiJimakuIpcRuntimeOptions['getCachedMediaPath'];
|
||||
shouldRequireRemoteMediaCache?: AnkiJimakuIpcRuntimeOptions['shouldRequireRemoteMediaCache'];
|
||||
getYoutubeMediaSourceUrl?: AnkiJimakuIpcRuntimeOptions['getYoutubeMediaSourceUrl'];
|
||||
showDesktopNotification: AnkiJimakuIpcRuntimeOptions['showDesktopNotification'];
|
||||
showOverlayNotification?: (payload: OverlayNotificationPayload) => void;
|
||||
dismissOverlayNotification?: (id: string) => void;
|
||||
createFieldGroupingCallback: AnkiJimakuIpcRuntimeOptions['createFieldGroupingCallback'];
|
||||
broadcastRuntimeOptionsChanged: AnkiJimakuIpcRuntimeOptions['broadcastRuntimeOptionsChanged'];
|
||||
getFieldGroupingResolver: AnkiJimakuIpcRuntimeOptions['getFieldGroupingResolver'];
|
||||
setFieldGroupingResolver: AnkiJimakuIpcRuntimeOptions['setFieldGroupingResolver'];
|
||||
parseMediaInfo: AnkiJimakuIpcRuntimeOptions['parseMediaInfo'];
|
||||
@@ -258,7 +225,6 @@ export function createMainIpcRuntimeServiceDeps(
|
||||
): IpcDepsRuntimeOptions {
|
||||
return {
|
||||
getMainWindow: params.getMainWindow,
|
||||
getVisibleOverlayVisibility: params.getVisibleOverlayVisibility,
|
||||
onOverlayModalClosed: params.onOverlayModalClosed,
|
||||
onOverlayModalOpened: params.onOverlayModalOpened,
|
||||
onOverlayMouseInteractionChanged: params.onOverlayMouseInteractionChanged,
|
||||
@@ -270,12 +236,8 @@ export function createMainIpcRuntimeServiceDeps(
|
||||
getMediaTimingReviewWaveform: params.getMediaTimingReviewWaveform,
|
||||
stopMediaTimingReviewPreview: params.stopMediaTimingReviewPreview,
|
||||
resolveMediaTimingReview: params.resolveMediaTimingReview,
|
||||
openYomitanSettings: params.openYomitanSettings,
|
||||
quitApp: params.quitApp,
|
||||
toggleVisibleOverlay: params.toggleVisibleOverlay,
|
||||
tokenizeCurrentSubtitle: params.tokenizeCurrentSubtitle,
|
||||
getCurrentSubtitleRaw: params.getCurrentSubtitleRaw,
|
||||
getCurrentSubtitleAss: params.getCurrentSubtitleAss,
|
||||
getSubtitleSidebarSnapshot: params.getSubtitleSidebarSnapshot,
|
||||
getSubtitleSidebarPlaybackState: params.getSubtitleSidebarPlaybackState,
|
||||
getSubtitleSidebarOpen: params.getSubtitleSidebarOpen,
|
||||
@@ -283,9 +245,7 @@ export function createMainIpcRuntimeServiceDeps(
|
||||
getSubtitlePosition: params.getSubtitlePosition,
|
||||
getSubtitleStyle: params.getSubtitleStyle,
|
||||
saveSubtitlePosition: params.saveSubtitlePosition,
|
||||
getMecabTokenizer: params.getMecabTokenizer,
|
||||
handleMpvCommand: params.handleMpvCommand,
|
||||
getKeybindings: params.getKeybindings,
|
||||
getMpvInputBindings: params.getMpvInputBindings,
|
||||
getSessionBindings: params.getSessionBindings,
|
||||
getConfiguredShortcuts: params.getConfiguredShortcuts,
|
||||
@@ -295,16 +255,13 @@ export function createMainIpcRuntimeServiceDeps(
|
||||
getOverlayNotificationPosition: params.getOverlayNotificationPosition,
|
||||
getControllerConfig: params.getControllerConfig,
|
||||
saveControllerConfig: params.saveControllerConfig,
|
||||
saveControllerPreference: params.saveControllerPreference,
|
||||
focusMainWindow: params.focusMainWindow ?? (() => {}),
|
||||
activatePlaybackWindowForOverlayInteraction: params.activatePlaybackWindowForOverlayInteraction,
|
||||
getSecondarySubMode: params.getSecondarySubMode,
|
||||
getMpvClient: params.getMpvClient,
|
||||
runSubsyncManual: params.runSubsyncManual,
|
||||
getAnkiConnectStatus: params.getAnkiConnectStatus,
|
||||
getRuntimeOptions: params.getRuntimeOptions,
|
||||
setRuntimeOption: params.setRuntimeOption,
|
||||
cycleRuntimeOption: params.cycleRuntimeOption,
|
||||
reportOverlayContentBounds: params.reportOverlayContentBounds,
|
||||
getAnilistStatus: params.getAnilistStatus,
|
||||
clearAnilistToken: params.clearAnilistToken,
|
||||
@@ -318,7 +275,6 @@ export function createMainIpcRuntimeServiceDeps(
|
||||
getCharacterDictionaryManagerSnapshot: params.getCharacterDictionaryManagerSnapshot,
|
||||
removeCharacterDictionaryManagedEntry: params.removeCharacterDictionaryManagedEntry,
|
||||
moveCharacterDictionaryManagedEntry: params.moveCharacterDictionaryManagedEntry,
|
||||
appendClipboardVideoToQueue: params.appendClipboardVideoToQueue,
|
||||
getChangelogSnapshot: params.getChangelogSnapshot,
|
||||
getPlaylistBrowserSnapshot: params.getPlaylistBrowserSnapshot,
|
||||
appendPlaylistBrowserFile: params.appendPlaylistBrowserFile,
|
||||
@@ -333,26 +289,9 @@ export function createAnkiJimakuIpcRuntimeServiceDeps(
|
||||
params: AnkiJimakuIpcRuntimeServiceDepsParams,
|
||||
): AnkiJimakuIpcRuntimeOptions {
|
||||
return {
|
||||
patchAnkiConnectEnabled: params.patchAnkiConnectEnabled,
|
||||
getResolvedConfig: params.getResolvedConfig,
|
||||
getRuntimeOptionsManager: params.getRuntimeOptionsManager,
|
||||
getSubtitleTimingTracker: params.getSubtitleTimingTracker,
|
||||
getMpvClient: params.getMpvClient,
|
||||
getAnkiIntegration: params.getAnkiIntegration,
|
||||
setAnkiIntegration: params.setAnkiIntegration,
|
||||
getKnownWordCacheStatePath: params.getKnownWordCacheStatePath,
|
||||
...(params.getCachedMediaPath ? { getCachedMediaPath: params.getCachedMediaPath } : {}),
|
||||
...(params.shouldRequireRemoteMediaCache
|
||||
? { shouldRequireRemoteMediaCache: params.shouldRequireRemoteMediaCache }
|
||||
: {}),
|
||||
...(params.getYoutubeMediaSourceUrl
|
||||
? { getYoutubeMediaSourceUrl: params.getYoutubeMediaSourceUrl }
|
||||
: {}),
|
||||
showDesktopNotification: params.showDesktopNotification,
|
||||
showOverlayNotification: params.showOverlayNotification,
|
||||
dismissOverlayNotification: params.dismissOverlayNotification,
|
||||
createFieldGroupingCallback: params.createFieldGroupingCallback,
|
||||
broadcastRuntimeOptionsChanged: params.broadcastRuntimeOptionsChanged,
|
||||
getFieldGroupingResolver: params.getFieldGroupingResolver,
|
||||
setFieldGroupingResolver: params.setFieldGroupingResolver,
|
||||
parseMediaInfo: params.parseMediaInfo,
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
|
||||
export interface RegisterIpcRuntimeServicesParams {
|
||||
runtimeOptions: RuntimeOptionsIpcDepsParams;
|
||||
mainDeps: Omit<MainIpcRuntimeServiceDepsParams, 'setRuntimeOption' | 'cycleRuntimeOption'>;
|
||||
mainDeps: Omit<MainIpcRuntimeServiceDepsParams, 'setRuntimeOption'>;
|
||||
ankiJimakuDeps: AnkiJimakuIpcRuntimeServiceDepsParams;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,6 @@ export function registerIpcRuntimeServices(params: RegisterIpcRuntimeServicesPar
|
||||
registerMainIpcRuntimeServices({
|
||||
...params.mainDeps,
|
||||
setRuntimeOption: runtimeOptionsIpcDeps.setRuntimeOption,
|
||||
cycleRuntimeOption: runtimeOptionsIpcDeps.cycleRuntimeOption,
|
||||
});
|
||||
registerAnkiJimakuIpcRuntimeServices(params.ankiJimakuDeps);
|
||||
}
|
||||
@@ -37,7 +37,6 @@ test('media path changes clear rendered subtitle state without clearing same-you
|
||||
|
||||
assert.ok(actionBlock);
|
||||
assert.match(actionBlock, /appState\.currentSubText = '';/);
|
||||
assert.match(actionBlock, /appState\.currentSubAssText = '';/);
|
||||
assert.match(actionBlock, /appState\.currentSubtitleData = null;/);
|
||||
assert.match(actionBlock, /isSameYoutubeMediaPath\(/);
|
||||
assert.match(actionBlock, /if \(!preserveParsedSubtitleCues\)/);
|
||||
|
||||
@@ -45,7 +45,6 @@ function createDefaultMpvFixture() {
|
||||
immersionTracker: null,
|
||||
subtitleTimingTracker: null,
|
||||
currentSubText: '',
|
||||
currentSubAssText: '',
|
||||
playbackPaused: null,
|
||||
previousSecondarySubVisibility: null,
|
||||
},
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
type MpvBindingEventName =
|
||||
| 'connection-change'
|
||||
| 'subtitle-change'
|
||||
| 'subtitle-ass-change'
|
||||
| 'secondary-subtitle-change'
|
||||
| 'subtitle-track-change'
|
||||
| 'secondary-subtitle-track-change'
|
||||
@@ -89,7 +88,6 @@ export function createHandleMpvSubtitleTimingHandler(deps: {
|
||||
export function createBindMpvClientEventHandlers(deps: {
|
||||
onConnectionChange: (payload: { connected: boolean }) => void;
|
||||
onSubtitleChange: (payload: { text: string }) => void;
|
||||
onSubtitleAssChange: (payload: { text: string }) => void;
|
||||
onSecondarySubtitleChange: (payload: { text: string }) => void;
|
||||
onSubtitleTrackChange: (payload: { sid: number | null }) => void;
|
||||
onSecondarySubtitleTrackChange: (payload: { sid: number | null }) => void;
|
||||
@@ -108,7 +106,6 @@ export function createBindMpvClientEventHandlers(deps: {
|
||||
return (mpvClient: MpvEventClient): void => {
|
||||
mpvClient.on('connection-change', deps.onConnectionChange);
|
||||
mpvClient.on('subtitle-change', deps.onSubtitleChange);
|
||||
mpvClient.on('subtitle-ass-change', deps.onSubtitleAssChange);
|
||||
mpvClient.on('secondary-subtitle-change', deps.onSecondarySubtitleChange);
|
||||
mpvClient.on('subtitle-track-change', deps.onSubtitleTrackChange);
|
||||
mpvClient.on('secondary-subtitle-track-change', deps.onSecondarySubtitleTrackChange);
|
||||
|
||||
@@ -49,16 +49,6 @@ export function createHandleMpvSubtitleChangeHandler(deps: {
|
||||
};
|
||||
}
|
||||
|
||||
export function createHandleMpvSubtitleAssChangeHandler(deps: {
|
||||
setCurrentSubAssText: (text: string) => void;
|
||||
broadcastSubtitleAss: (text: string) => void;
|
||||
}) {
|
||||
return ({ text }: { text: string }): void => {
|
||||
deps.setCurrentSubAssText(text);
|
||||
deps.broadcastSubtitleAss(text);
|
||||
};
|
||||
}
|
||||
|
||||
export function createHandleMpvSecondarySubtitleChangeHandler(deps: {
|
||||
broadcastSecondarySubtitle: (text: string) => void;
|
||||
}) {
|
||||
|
||||
@@ -29,8 +29,6 @@ function createDeps(overrides: Partial<Deps> = {}): Deps {
|
||||
onSubtitleChange: () => {},
|
||||
refreshDiscordPresence: () => {},
|
||||
|
||||
setCurrentSubAssText: () => {},
|
||||
broadcastSubtitleAss: () => {},
|
||||
broadcastSecondarySubtitle: () => {},
|
||||
|
||||
updateCurrentMediaPath: () => {},
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
createHandleMpvPauseChangeHandler,
|
||||
createHandleMpvSecondarySubtitleChangeHandler,
|
||||
createHandleMpvSecondarySubtitleVisibilityHandler,
|
||||
createHandleMpvSubtitleAssChangeHandler,
|
||||
createHandleMpvSubtitleChangeHandler,
|
||||
createHandleMpvSubtitleMetricsChangeHandler,
|
||||
createHandleMpvTimePosChangeHandler,
|
||||
@@ -52,8 +51,6 @@ export function createBindMpvMainEventHandlersHandler(deps: {
|
||||
logSubtitleProcessingDebug?: (message: string) => void;
|
||||
refreshDiscordPresence: () => void;
|
||||
|
||||
setCurrentSubAssText: (text: string) => void;
|
||||
broadcastSubtitleAss: (text: string) => void;
|
||||
broadcastSecondarySubtitle: (text: string) => void;
|
||||
onSubtitleTrackChange?: (sid: number | null) => void;
|
||||
onSecondarySubtitleTrackChange?: (sid: number | null) => void;
|
||||
@@ -135,10 +132,6 @@ export function createBindMpvMainEventHandlersHandler(deps: {
|
||||
: undefined,
|
||||
refreshDiscordPresence: () => deps.refreshDiscordPresence(),
|
||||
});
|
||||
const handleMpvSubtitleAssChange = createHandleMpvSubtitleAssChangeHandler({
|
||||
setCurrentSubAssText: (text) => deps.setCurrentSubAssText(text),
|
||||
broadcastSubtitleAss: (text) => deps.broadcastSubtitleAss(text),
|
||||
});
|
||||
const handleMpvSecondarySubtitleChange = createHandleMpvSecondarySubtitleChangeHandler({
|
||||
broadcastSecondarySubtitle: (text) => deps.broadcastSecondarySubtitle(text),
|
||||
});
|
||||
@@ -200,7 +193,6 @@ export function createBindMpvMainEventHandlersHandler(deps: {
|
||||
createBindMpvClientEventHandlers({
|
||||
onConnectionChange: handleMpvConnectionChangeWithSidebarReset,
|
||||
onSubtitleChange: handleMpvSubtitleChange,
|
||||
onSubtitleAssChange: handleMpvSubtitleAssChange,
|
||||
onSecondarySubtitleChange: handleMpvSecondarySubtitleChange,
|
||||
onSubtitleTrackChange: ({ sid }) => deps.onSubtitleTrackChange?.(sid),
|
||||
onSecondarySubtitleTrackChange: ({ sid }) => deps.onSecondarySubtitleTrackChange?.(sid),
|
||||
|
||||
@@ -20,7 +20,6 @@ function buildMainDeps(
|
||||
immersionTracker: null,
|
||||
subtitleTimingTracker: null,
|
||||
currentSubText: '',
|
||||
currentSubAssText: '',
|
||||
playbackPaused: null,
|
||||
previousSecondarySubVisibility: false,
|
||||
...appStateOverrides,
|
||||
|
||||
@@ -46,7 +46,6 @@ export function createBuildBindMpvMainEventHandlersMainDepsHandler(deps: {
|
||||
activeParsedSubtitleSource?: string | null;
|
||||
currentMediaPath?: string | null;
|
||||
currentSubText: string;
|
||||
currentSubAssText: string;
|
||||
currentSubtitleData?: SubtitleData | null;
|
||||
playbackPaused: boolean | null;
|
||||
previousSecondarySubVisibility: boolean | null;
|
||||
@@ -300,11 +299,6 @@ export function createBuildBindMpvMainEventHandlersMainDepsHandler(deps: {
|
||||
? (trackList: unknown[] | null) => deps.onSubtitleTrackListChange!(trackList)
|
||||
: undefined,
|
||||
refreshDiscordPresence: () => deps.refreshDiscordPresence(),
|
||||
setCurrentSubAssText: (text: string) => {
|
||||
deps.appState.currentSubAssText = text;
|
||||
},
|
||||
broadcastSubtitleAss: (text: string) =>
|
||||
deps.broadcastToOverlayWindows('subtitle-ass:set', text),
|
||||
broadcastSecondarySubtitle: (text: string) => {
|
||||
if (deps.onSecondarySubtitleChange) {
|
||||
deps.onSecondarySubtitleChange(text);
|
||||
|
||||
@@ -160,7 +160,6 @@ export interface AppState {
|
||||
discordPresenceService: ReturnType<typeof createDiscordPresenceService> | null;
|
||||
reconnectTimer: ReturnType<typeof setTimeout> | null;
|
||||
currentSubText: string;
|
||||
currentSubAssText: string;
|
||||
currentSubtitleData: SubtitleData | null;
|
||||
activeParsedSubtitleCues: SubtitleCue[];
|
||||
activeParsedSubtitleSource: string | null;
|
||||
@@ -247,7 +246,6 @@ export function createAppState(values: AppStateInitialValues): AppState {
|
||||
discordPresenceService: null,
|
||||
reconnectTimer: null,
|
||||
currentSubText: '',
|
||||
currentSubAssText: '',
|
||||
currentSubtitleData: null,
|
||||
activeParsedSubtitleCues: [],
|
||||
activeParsedSubtitleSource: null,
|
||||
|
||||
@@ -399,29 +399,6 @@ export class MecabTokenizer {
|
||||
path: this.mecabPath,
|
||||
};
|
||||
}
|
||||
|
||||
setEnabled(enabled: boolean): void {
|
||||
this.enabled = enabled;
|
||||
if (!enabled) {
|
||||
const pending: MecabQueuedRequest[] = [];
|
||||
if (this.activeRequest) {
|
||||
pending.push({
|
||||
text: this.activeRequest.text,
|
||||
retryCount: MecabTokenizer.MAX_RETRY_COUNT,
|
||||
resolve: this.activeRequest.resolve,
|
||||
});
|
||||
}
|
||||
if (this.requestQueue.length > 0) {
|
||||
pending.push(...this.requestQueue);
|
||||
}
|
||||
this.activeRequest = null;
|
||||
this.requestQueue = [];
|
||||
for (const request of pending) {
|
||||
request.resolve(null);
|
||||
}
|
||||
this.stopPersistentProcess();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { mapMecabPos1ToPartOfSpeech as mapPartOfSpeech };
|
||||
@@ -22,8 +22,6 @@ import { createIpcListenerFactories } from './preload-ipc-listeners';
|
||||
import type {
|
||||
SubtitleData,
|
||||
SubtitlePosition,
|
||||
MecabStatus,
|
||||
Keybinding,
|
||||
ElectronAPI,
|
||||
SecondarySubMode,
|
||||
SubtitleStyleConfig,
|
||||
@@ -45,7 +43,6 @@ import type {
|
||||
SubsyncManualPayload,
|
||||
SubsyncManualRunRequest,
|
||||
SubsyncResult,
|
||||
ClipboardAppendResult,
|
||||
PlaylistBrowserMutationResult,
|
||||
PlaylistBrowserSnapshot,
|
||||
KikuFieldGroupingRequestData,
|
||||
@@ -60,7 +57,6 @@ import type {
|
||||
ShortcutsConfig,
|
||||
ConfigHotReloadPayload,
|
||||
ControllerConfigUpdate,
|
||||
ControllerPreferenceUpdate,
|
||||
ResolvedControllerConfig,
|
||||
SessionHelpOpenPayload,
|
||||
SessionNumericSelectionStartPayload,
|
||||
@@ -176,10 +172,6 @@ const onOverlayNotificationEvent =
|
||||
const onNotificationHistoryToggleEvent = createQueuedIpcListener(
|
||||
IPC_CHANNELS.event.notificationHistoryToggle,
|
||||
);
|
||||
const onSubtitleVisibilityEvent = createLatestValueIpcListenerWithPayload<boolean>(
|
||||
IPC_CHANNELS.event.subtitleVisibility,
|
||||
(payload) => payload === true,
|
||||
);
|
||||
const onSubtitlePositionSetEvent = createLatestValueIpcListenerWithPayload<SubtitlePosition | null>(
|
||||
IPC_CHANNELS.event.subtitlePositionSet,
|
||||
(payload) => payload as SubtitlePosition | null,
|
||||
@@ -236,22 +228,14 @@ const electronAPI: ElectronAPI = {
|
||||
},
|
||||
onNotificationHistoryToggle: onNotificationHistoryToggleEvent,
|
||||
|
||||
onVisibility: (callback: (visible: boolean) => void) => {
|
||||
onSubtitleVisibilityEvent(callback);
|
||||
},
|
||||
|
||||
onSubtitlePosition: (callback: (position: SubtitlePosition | null) => void) => {
|
||||
onSubtitlePositionSetEvent(callback);
|
||||
},
|
||||
|
||||
getOverlayVisibility: (): Promise<boolean> =>
|
||||
ipcRenderer.invoke(IPC_CHANNELS.request.getVisibleOverlayVisibility),
|
||||
getCurrentSubtitle: (): Promise<SubtitleData> =>
|
||||
ipcRenderer.invoke(IPC_CHANNELS.request.getCurrentSubtitle),
|
||||
getCurrentSubtitleRaw: (): Promise<string> =>
|
||||
ipcRenderer.invoke(IPC_CHANNELS.request.getCurrentSubtitleRaw),
|
||||
getCurrentSubtitleAss: (): Promise<string> =>
|
||||
ipcRenderer.invoke(IPC_CHANNELS.request.getCurrentSubtitleAss),
|
||||
getSubtitleSidebarOpen: (): Promise<boolean> =>
|
||||
ipcRenderer.invoke(IPC_CHANNELS.request.getSubtitleSidebarOpen),
|
||||
getSubtitleSidebarSnapshot: () =>
|
||||
@@ -264,14 +248,6 @@ const electronAPI: ElectronAPI = {
|
||||
},
|
||||
getPlaybackPaused: (): Promise<boolean | null> =>
|
||||
ipcRenderer.invoke(IPC_CHANNELS.request.getPlaybackPaused),
|
||||
onSubtitleAss: (callback: (assText: string) => void) => {
|
||||
ipcRenderer.on(
|
||||
IPC_CHANNELS.event.subtitleAssSet,
|
||||
(_event: IpcRendererEvent, assText: string) => {
|
||||
callback(assText);
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
setIgnoreMouseEvents: (ignore: boolean, options?: { forward?: boolean }) => {
|
||||
ipcRenderer.send(IPC_CHANNELS.command.setIgnoreMouseEvents, ignore, options);
|
||||
@@ -281,10 +257,6 @@ const electronAPI: ElectronAPI = {
|
||||
ipcRenderer.send(IPC_CHANNELS.command.reportOverlayInteractive, interactive);
|
||||
},
|
||||
|
||||
openYomitanSettings: () => {
|
||||
ipcRenderer.send(IPC_CHANNELS.command.openYomitanSettings);
|
||||
},
|
||||
|
||||
recordYomitanLookup: (context?: SubtitleMiningContext | null) => {
|
||||
ipcRenderer.send(IPC_CHANNELS.command.recordYomitanLookup, context ?? null);
|
||||
},
|
||||
@@ -295,18 +267,10 @@ const electronAPI: ElectronAPI = {
|
||||
ipcRenderer.send(IPC_CHANNELS.command.saveSubtitlePosition, position);
|
||||
},
|
||||
|
||||
getMecabStatus: (): Promise<MecabStatus> =>
|
||||
ipcRenderer.invoke(IPC_CHANNELS.request.getMecabStatus),
|
||||
setMecabEnabled: (enabled: boolean) => {
|
||||
ipcRenderer.send(IPC_CHANNELS.command.setMecabEnabled, enabled);
|
||||
},
|
||||
|
||||
sendMpvCommand: (command: (string | number)[]) => {
|
||||
ipcRenderer.send(IPC_CHANNELS.command.mpvCommand, command);
|
||||
},
|
||||
|
||||
getKeybindings: (): Promise<Keybinding[]> =>
|
||||
ipcRenderer.invoke(IPC_CHANNELS.request.getKeybindings),
|
||||
getMpvInputBindings: () => ipcRenderer.invoke(IPC_CHANNELS.request.getMpvInputBindings),
|
||||
getSessionBindings: () => ipcRenderer.invoke(IPC_CHANNELS.request.getSessionBindings),
|
||||
getConfiguredShortcuts: (): Promise<Required<ShortcutsConfig>> =>
|
||||
@@ -325,8 +289,6 @@ const electronAPI: ElectronAPI = {
|
||||
ipcRenderer.invoke(IPC_CHANNELS.request.getControllerConfig),
|
||||
saveControllerConfig: (update: ControllerConfigUpdate): Promise<void> =>
|
||||
ipcRenderer.invoke(IPC_CHANNELS.command.saveControllerConfig, update),
|
||||
saveControllerPreference: (update: ControllerPreferenceUpdate): Promise<void> =>
|
||||
ipcRenderer.invoke(IPC_CHANNELS.command.saveControllerPreference, update),
|
||||
|
||||
getJimakuMediaInfo: (): Promise<JimakuMediaInfo> =>
|
||||
ipcRenderer.invoke(IPC_CHANNELS.request.jimakuGetMediaInfo),
|
||||
@@ -350,31 +312,14 @@ const electronAPI: ElectronAPI = {
|
||||
tsukihimeGetSecondaryLanguages: (): Promise<string[]> =>
|
||||
ipcRenderer.invoke(IPC_CHANNELS.request.tsukihimeGetSecondaryLanguages),
|
||||
|
||||
quitApp: () => {
|
||||
ipcRenderer.send(IPC_CHANNELS.command.quitApp);
|
||||
},
|
||||
|
||||
toggleDevTools: () => {
|
||||
ipcRenderer.send(IPC_CHANNELS.command.toggleDevTools);
|
||||
},
|
||||
|
||||
toggleOverlay: () => {
|
||||
ipcRenderer.send(IPC_CHANNELS.command.toggleOverlay);
|
||||
},
|
||||
|
||||
toggleStatsOverlay: () => {
|
||||
ipcRenderer.send(IPC_CHANNELS.command.toggleStatsOverlay);
|
||||
},
|
||||
|
||||
getAnkiConnectStatus: (): Promise<boolean> =>
|
||||
ipcRenderer.invoke(IPC_CHANNELS.request.getAnkiConnectStatus),
|
||||
setAnkiConnectEnabled: (enabled: boolean) => {
|
||||
ipcRenderer.send(IPC_CHANNELS.command.setAnkiConnectEnabled, enabled);
|
||||
},
|
||||
clearAnkiConnectHistory: () => {
|
||||
ipcRenderer.send(IPC_CHANNELS.command.clearAnkiConnectHistory);
|
||||
},
|
||||
|
||||
onSecondarySub: (callback: (text: string) => void) => {
|
||||
onSecondarySubtitleSetEvent(callback);
|
||||
},
|
||||
@@ -419,8 +364,6 @@ const electronAPI: ElectronAPI = {
|
||||
value: RuntimeOptionValue,
|
||||
): Promise<RuntimeOptionApplyResult> =>
|
||||
ipcRenderer.invoke(IPC_CHANNELS.request.setRuntimeOption, id, value),
|
||||
cycleRuntimeOption: (id: RuntimeOptionId, direction: 1 | -1): Promise<RuntimeOptionApplyResult> =>
|
||||
ipcRenderer.invoke(IPC_CHANNELS.request.cycleRuntimeOption, id, direction),
|
||||
onRuntimeOptionsChanged: (callback: (options: RuntimeOptionState[]) => void) => {
|
||||
ipcRenderer.on(
|
||||
IPC_CHANNELS.event.runtimeOptionsChanged,
|
||||
@@ -485,8 +428,6 @@ const electronAPI: ElectronAPI = {
|
||||
onSessionNumericSelectionStart: onSessionNumericSelectionStartEvent,
|
||||
onKeyboardModeToggleRequested: onKeyboardModeToggleRequestedEvent,
|
||||
onLookupWindowToggleRequested: onLookupWindowToggleRequestedEvent,
|
||||
appendClipboardVideoToQueue: (): Promise<ClipboardAppendResult> =>
|
||||
ipcRenderer.invoke(IPC_CHANNELS.request.appendClipboardVideoToQueue),
|
||||
getPlaylistBrowserSnapshot: (): Promise<PlaylistBrowserSnapshot> =>
|
||||
ipcRenderer.invoke(IPC_CHANNELS.request.getPlaylistBrowserSnapshot),
|
||||
appendPlaylistBrowserFile: (pathValue: string): Promise<PlaylistBrowserMutationResult> =>
|
||||
|
||||
@@ -242,7 +242,6 @@ function installKeyboardTestGlobals() {
|
||||
windowFocusCalls += 1;
|
||||
},
|
||||
electronAPI: {
|
||||
getKeybindings: async () => [],
|
||||
getMpvInputBindings: () => getMpvInputBindings(),
|
||||
getSessionBindings: () => getSessionBindingsImpl(),
|
||||
getConfiguredShortcuts: async () => configuredShortcuts,
|
||||
|
||||
@@ -25,22 +25,15 @@ export const IPC_CHANNELS = {
|
||||
command: {
|
||||
setIgnoreMouseEvents: 'set-ignore-mouse-events',
|
||||
overlayModalClosed: 'overlay:modal-closed',
|
||||
openYomitanSettings: 'open-yomitan-settings',
|
||||
recordYomitanLookup: 'record-yomitan-lookup',
|
||||
quitApp: 'quit-app',
|
||||
youtubePickerResolve: 'youtube:picker-resolve',
|
||||
toggleDevTools: 'toggle-dev-tools',
|
||||
toggleOverlay: 'toggle-overlay',
|
||||
saveSubtitlePosition: 'save-subtitle-position',
|
||||
statsNativeConfirmDialog: 'stats:native-confirm-dialog',
|
||||
statsNativeDialogOpened: 'stats:native-dialog-opened',
|
||||
statsNativeDialogClosed: 'stats:native-dialog-closed',
|
||||
saveControllerConfig: 'save-controller-config',
|
||||
saveControllerPreference: 'save-controller-preference',
|
||||
setMecabEnabled: 'set-mecab-enabled',
|
||||
mpvCommand: 'mpv-command',
|
||||
setAnkiConnectEnabled: 'set-anki-connect-enabled',
|
||||
clearAnkiConnectHistory: 'clear-anki-connect-history',
|
||||
refreshKnownWords: 'anki:refresh-known-words',
|
||||
kikuFieldGroupingRespond: 'kiku:field-grouping-respond',
|
||||
reportOverlayContentBounds: 'overlay-content-bounds:report',
|
||||
@@ -63,18 +56,14 @@ export const IPC_CHANNELS = {
|
||||
downloadSubtitleGenerationVadModel: 'subtitle-generation:download-vad',
|
||||
setSubtitleGenerationVadEnabled: 'subtitle-generation:set-vad-enabled',
|
||||
cancelSubtitleGeneration: 'subtitle-generation:cancel',
|
||||
getVisibleOverlayVisibility: 'get-visible-overlay-visibility',
|
||||
getCurrentSubtitle: 'get-current-subtitle',
|
||||
getCurrentSubtitleRaw: 'get-current-subtitle-raw',
|
||||
getCurrentSubtitleAss: 'get-current-subtitle-ass',
|
||||
getSubtitleSidebarSnapshot: 'get-subtitle-sidebar-snapshot',
|
||||
getSubtitleSidebarPlaybackState: 'get-subtitle-sidebar-playback-state',
|
||||
getSubtitleSidebarOpen: 'get-subtitle-sidebar-open',
|
||||
getPlaybackPaused: 'get-playback-paused',
|
||||
getSubtitlePosition: 'get-subtitle-position',
|
||||
getSubtitleStyle: 'get-subtitle-style',
|
||||
getMecabStatus: 'get-mecab-status',
|
||||
getKeybindings: 'get-keybindings',
|
||||
getMpvInputBindings: 'get-mpv-input-bindings',
|
||||
getSessionBindings: 'get-session-bindings',
|
||||
getConfigShortcuts: 'get-config-shortcuts',
|
||||
@@ -88,10 +77,8 @@ export const IPC_CHANNELS = {
|
||||
focusMainWindow: 'focus-main-window',
|
||||
activatePlaybackWindowForOverlayInteraction: 'overlay:activate-playback-window',
|
||||
runSubsyncManual: 'subsync:run-manual',
|
||||
getAnkiConnectStatus: 'get-anki-connect-status',
|
||||
getRuntimeOptions: 'runtime-options:get',
|
||||
setRuntimeOption: 'runtime-options:set',
|
||||
cycleRuntimeOption: 'runtime-options:cycle',
|
||||
getAnilistStatus: 'anilist:get-status',
|
||||
clearAnilistToken: 'anilist:clear-token',
|
||||
openAnilistSetup: 'anilist:open-setup',
|
||||
@@ -103,7 +90,6 @@ export const IPC_CHANNELS = {
|
||||
getCharacterDictionaryManagerSnapshot: 'character-dictionary:get-manager-snapshot',
|
||||
removeCharacterDictionaryManagedEntry: 'character-dictionary:remove-managed-entry',
|
||||
moveCharacterDictionaryManagedEntry: 'character-dictionary:move-managed-entry',
|
||||
appendClipboardVideoToQueue: 'clipboard:append-video-to-queue',
|
||||
getPlaylistBrowserSnapshot: 'playlist-browser:get-snapshot',
|
||||
appendPlaylistBrowserFile: 'playlist-browser:append-file',
|
||||
playPlaylistBrowserIndex: 'playlist-browser:play-index',
|
||||
@@ -153,9 +139,7 @@ export const IPC_CHANNELS = {
|
||||
subtitleGenerationProgress: 'subtitle-generation:progress',
|
||||
subtitleSet: 'subtitle:set',
|
||||
overlayPointerRecoveryRequest: 'overlay:pointer-recovery-request',
|
||||
subtitleVisibility: 'mpv:subVisibility',
|
||||
subtitlePositionSet: 'subtitle-position:set',
|
||||
subtitleAssSet: 'subtitle-ass:set',
|
||||
secondarySubtitleSet: 'secondary-subtitle:set',
|
||||
secondarySubtitleMode: 'secondary-subtitle:mode',
|
||||
subsyncOpenManual: 'subsync:open-manual',
|
||||
@@ -197,11 +181,6 @@ export type RuntimeOptionsSetRequest = {
|
||||
value: RuntimeOptionValue;
|
||||
};
|
||||
|
||||
export type RuntimeOptionsCycleRequest = {
|
||||
id: RuntimeOptionId;
|
||||
direction: 1 | -1;
|
||||
};
|
||||
|
||||
export type OverlayContentBoundsReportRequest = {
|
||||
measurement: OverlayContentMeasurement;
|
||||
};
|
||||
@@ -10,7 +10,6 @@ import type {
|
||||
} from '../../types/integrations';
|
||||
import type {
|
||||
ControllerConfigUpdate,
|
||||
ControllerPreferenceUpdate,
|
||||
SessionActionDispatchRequest,
|
||||
SubsyncManualRunRequest,
|
||||
} from '../../types/runtime';
|
||||
@@ -131,16 +130,6 @@ export function parseSubtitlePosition(value: unknown): SubtitlePosition | null {
|
||||
};
|
||||
}
|
||||
|
||||
export function parseControllerPreferenceUpdate(value: unknown): ControllerPreferenceUpdate | null {
|
||||
if (!isObject(value)) return null;
|
||||
if (typeof value.preferredGamepadId !== 'string') return null;
|
||||
if (typeof value.preferredGamepadLabel !== 'string') return null;
|
||||
return {
|
||||
preferredGamepadId: value.preferredGamepadId,
|
||||
preferredGamepadLabel: value.preferredGamepadLabel,
|
||||
};
|
||||
}
|
||||
|
||||
function parseDiscreteBinding(value: unknown) {
|
||||
if (!isObject(value) || typeof value.kind !== 'string') return null;
|
||||
if (value.kind === 'none') {
|
||||
@@ -328,10 +317,6 @@ export function parseRuntimeOptionId(value: unknown): RuntimeOptionId | null {
|
||||
return RUNTIME_OPTION_IDS.includes(value as RuntimeOptionId) ? (value as RuntimeOptionId) : null;
|
||||
}
|
||||
|
||||
export function parseRuntimeOptionDirection(value: unknown): 1 | -1 | null {
|
||||
return value === 1 || value === -1 ? value : null;
|
||||
}
|
||||
|
||||
export function parseRuntimeOptionValue(value: unknown): RuntimeOptionValue | null {
|
||||
return typeof value === 'boolean' || typeof value === 'string'
|
||||
? (value as RuntimeOptionValue)
|
||||
|
||||
@@ -297,11 +297,6 @@ export interface ControllerConfig {
|
||||
profiles?: Record<string, ControllerProfileConfig>;
|
||||
}
|
||||
|
||||
export interface ControllerPreferenceUpdate {
|
||||
preferredGamepadId: string;
|
||||
preferredGamepadLabel: string;
|
||||
}
|
||||
|
||||
export type ControllerConfigUpdate = ControllerConfig;
|
||||
|
||||
export interface ControllerDeviceInfo {
|
||||
@@ -470,28 +465,20 @@ export interface ElectronAPI {
|
||||
options?: Pick<OverlayNotificationAction, 'noteId'>,
|
||||
) => void;
|
||||
onNotificationHistoryToggle: (callback: () => void) => void;
|
||||
onVisibility: (callback: (visible: boolean) => void) => void;
|
||||
onSubtitlePosition: (callback: (position: SubtitlePosition | null) => void) => void;
|
||||
getOverlayVisibility: () => Promise<boolean>;
|
||||
getCurrentSubtitle: () => Promise<SubtitleData>;
|
||||
getCurrentSubtitleRaw: () => Promise<string>;
|
||||
getCurrentSubtitleAss: () => Promise<string>;
|
||||
getSubtitleSidebarSnapshot: () => Promise<SubtitleSidebarSnapshot>;
|
||||
getSubtitleSidebarPlaybackState: () => Promise<SubtitleSidebarPlaybackState>;
|
||||
copySubtitleSidebarSelection: (text: string) => Promise<void>;
|
||||
getSubtitleSidebarOpen: () => Promise<boolean>;
|
||||
getPlaybackPaused: () => Promise<boolean | null>;
|
||||
onSubtitleAss: (callback: (assText: string) => void) => void;
|
||||
setIgnoreMouseEvents: (ignore: boolean, options?: { forward?: boolean }) => void;
|
||||
reportOverlayInteractive: (interactive: boolean) => void;
|
||||
openYomitanSettings: () => void;
|
||||
recordYomitanLookup: (context?: SubtitleMiningContext | null) => void;
|
||||
getSubtitlePosition: () => Promise<SubtitlePosition | null>;
|
||||
saveSubtitlePosition: (position: SubtitlePosition) => void;
|
||||
getMecabStatus: () => Promise<MecabStatus>;
|
||||
setMecabEnabled: (enabled: boolean) => void;
|
||||
sendMpvCommand: (command: (string | number)[]) => void;
|
||||
getKeybindings: () => Promise<Keybinding[]>;
|
||||
getMpvInputBindings: () => Promise<MpvInputBindingsSnapshot>;
|
||||
getSessionBindings: () => Promise<CompiledSessionBinding[]>;
|
||||
getConfiguredShortcuts: () => Promise<Required<ShortcutsConfig>>;
|
||||
@@ -505,7 +492,6 @@ export interface ElectronAPI {
|
||||
markActiveVideoWatched: () => Promise<boolean>;
|
||||
getControllerConfig: () => Promise<ResolvedControllerConfig>;
|
||||
saveControllerConfig: (update: ControllerConfigUpdate) => Promise<void>;
|
||||
saveControllerPreference: (update: ControllerPreferenceUpdate) => Promise<void>;
|
||||
getJimakuMediaInfo: () => Promise<JimakuMediaInfo>;
|
||||
jimakuSearchEntries: (query: JimakuSearchQuery) => Promise<JimakuApiResponse<JimakuEntry[]>>;
|
||||
jimakuListFiles: (query: JimakuFilesQuery) => Promise<JimakuApiResponse<JimakuFileEntry[]>>;
|
||||
@@ -518,13 +504,8 @@ export interface ElectronAPI {
|
||||
) => Promise<TsukihimeApiResponse<TsukihimeSubtitleFile[]>>;
|
||||
tsukihimeDownloadFile: (query: TsukihimeDownloadQuery) => Promise<TsukihimeDownloadResult>;
|
||||
tsukihimeGetSecondaryLanguages: () => Promise<string[]>;
|
||||
quitApp: () => void;
|
||||
toggleDevTools: () => void;
|
||||
toggleOverlay: () => void;
|
||||
toggleStatsOverlay: () => void;
|
||||
getAnkiConnectStatus: () => Promise<boolean>;
|
||||
setAnkiConnectEnabled: (enabled: boolean) => void;
|
||||
clearAnkiConnectHistory: () => void;
|
||||
onSecondarySub: (callback: (text: string) => void) => void;
|
||||
onSecondarySubMode: (callback: (mode: SecondarySubMode) => void) => void;
|
||||
getSecondarySubMode: () => Promise<SecondarySubMode>;
|
||||
@@ -550,7 +531,6 @@ export interface ElectronAPI {
|
||||
id: RuntimeOptionId,
|
||||
value: RuntimeOptionValue,
|
||||
) => Promise<RuntimeOptionApplyResult>;
|
||||
cycleRuntimeOption: (id: RuntimeOptionId, direction: 1 | -1) => Promise<RuntimeOptionApplyResult>;
|
||||
onRuntimeOptionsChanged: (callback: (options: RuntimeOptionState[]) => void) => void;
|
||||
onOpenRuntimeOptions: (callback: () => void) => void;
|
||||
onOpenSessionHelp: (callback: (payload: SessionHelpOpenPayload) => void) => void;
|
||||
@@ -589,7 +569,6 @@ export interface ElectronAPI {
|
||||
) => void;
|
||||
onKeyboardModeToggleRequested: (callback: () => void) => void;
|
||||
onLookupWindowToggleRequested: (callback: () => void) => void;
|
||||
appendClipboardVideoToQueue: () => Promise<ClipboardAppendResult>;
|
||||
getPlaylistBrowserSnapshot: () => Promise<PlaylistBrowserSnapshot>;
|
||||
appendPlaylistBrowserFile: (path: string) => Promise<PlaylistBrowserMutationResult>;
|
||||
playPlaylistBrowserIndex: (index: number) => Promise<PlaylistBrowserMutationResult>;
|
||||
|
||||
Reference in new issue
Block a user