feat(subtitles): add local Japanese subtitle generation (#240)

This commit is contained in:
2026-09-15 21:43:11 -07:00
committed by GitHub
parent 6841a37a22
commit 2831e05124
90 changed files with 4934 additions and 57 deletions
+34
View File
@@ -463,6 +463,9 @@ import {
} from './main/early-single-instance';
import { handleMpvCommandFromIpcRuntime } from './main/ipc-mpv-command';
import { registerIpcRuntimeServices } from './main/ipc-runtime';
import { createSubtitleGenerationRuntime } from './main/runtime/subtitle-generation-runtime';
import { registerSubtitleGenerationIpc } from './main/runtime/subtitle-generation-ipc';
import { openSubtitleGenerationModal } from './main/runtime/subtitle-generation-open';
import { createAnkiJimakuIpcRuntimeServiceDeps } from './main/dependencies';
import { createMainBootServices, type MainBootServicesResult } from './main/boot/services';
import { handleCliCommandRuntimeServiceWithContext } from './main/cli-runtime';
@@ -2990,6 +2993,14 @@ function openTsukihimeOverlay(): void {
);
}
function openSubtitleGenerationOverlay(): void {
openOverlayHostedModalWithOsd(
openSubtitleGenerationModal,
'Subtitle generation overlay unavailable.',
'Failed to open subtitle generation overlay.',
);
}
function openSessionHelpOverlay(): void {
openOverlayHostedModalWithOsd(
openSessionHelpModalRuntime,
@@ -5510,6 +5521,7 @@ async function dispatchSessionAction(request: SessionActionDispatchRequest): Pro
openJimaku: () => openJimakuOverlay(),
openTsukihime: () => openTsukihimeOverlay(),
openSessionHelp: () => openSessionHelpOverlay(),
openSubtitleGeneration: () => openSubtitleGenerationOverlay(),
openCharacterDictionaryManager: () => openCharacterDictionaryManagerOverlay(),
openControllerSelect: () => openControllerSelectOverlay(),
openControllerDebug: () => openControllerDebugOverlay(),
@@ -6654,3 +6666,25 @@ function setOverlayVisible(visible: boolean): void {
}
registerIpcRuntimeHandlers();
const subtitleGenerationRuntime = createSubtitleGenerationRuntime({
getConfig: () => configService.getConfig().subtitleGeneration,
getModelDirectory: () =>
path.join(path.dirname(configService.getConfigPath()), 'models', 'whisper'),
getMpvClient: () => appState.mpvClient,
onProgress: (progress) => {
for (const window of [overlayManager.getMainWindow(), overlayManager.getModalWindow()]) {
if (window && !window.isDestroyed())
window.webContents.send(IPC_CHANNELS.event.subtitleGenerationProgress, progress);
}
},
});
registerSubtitleGenerationIpc({
ipc: ipcMain,
isAllowedSender: (sender) =>
[overlayManager.getMainWindow(), overlayManager.getModalWindow()].some(
(window) => window && !window.isDestroyed() && window.webContents === sender,
),
runtime: subtitleGenerationRuntime,
openModal: () => openSubtitleGenerationModal(createOverlayHostedModalOpenDeps()),
});
app.on('before-quit', () => subtitleGenerationRuntime.cancel());