/* * SubMiner - All-in-one sentence mining overlay * Copyright (C) 2024 sudacode * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import { contextBridge, ipcRenderer, IpcRendererEvent } from 'electron'; import type { SubtitleData, SubtitlePosition, MecabStatus, Keybinding, ElectronAPI, SecondarySubMode, SubtitleStyleConfig, JimakuMediaInfo, JimakuSearchQuery, JimakuFilesQuery, JimakuDownloadQuery, JimakuEntry, JimakuFileEntry, JimakuApiResponse, JimakuDownloadResult, SubsyncManualPayload, SubsyncManualRunRequest, SubsyncResult, ClipboardAppendResult, KikuFieldGroupingRequestData, KikuFieldGroupingChoice, KikuMergePreviewRequest, KikuMergePreviewResponse, RuntimeOptionApplyResult, RuntimeOptionId, RuntimeOptionState, RuntimeOptionValue, MpvSubtitleRenderMetrics, OverlayContentMeasurement, ShortcutsConfig, ConfigHotReloadPayload, } from './types'; const overlayLayerArg = process.argv.find((arg) => arg.startsWith('--overlay-layer=')); const overlayLayerFromArg = overlayLayerArg?.slice('--overlay-layer='.length); const overlayLayer = overlayLayerFromArg === 'visible' || overlayLayerFromArg === 'invisible' ? overlayLayerFromArg : null; const electronAPI: ElectronAPI = { getOverlayLayer: () => overlayLayer, onSubtitle: (callback: (data: SubtitleData) => void) => { ipcRenderer.on('subtitle:set', (_event: IpcRendererEvent, data: SubtitleData) => callback(data), ); }, onVisibility: (callback: (visible: boolean) => void) => { ipcRenderer.on('mpv:subVisibility', (_event: IpcRendererEvent, visible: boolean) => callback(visible), ); }, onSubtitlePosition: (callback: (position: SubtitlePosition | null) => void) => { ipcRenderer.on( 'subtitle-position:set', (_event: IpcRendererEvent, position: SubtitlePosition | null) => { callback(position); }, ); }, getOverlayVisibility: (): Promise => ipcRenderer.invoke('get-overlay-visibility'), getCurrentSubtitle: (): Promise => ipcRenderer.invoke('get-current-subtitle'), getCurrentSubtitleRaw: (): Promise => ipcRenderer.invoke('get-current-subtitle-raw'), getCurrentSubtitleAss: (): Promise => ipcRenderer.invoke('get-current-subtitle-ass'), getMpvSubtitleRenderMetrics: () => ipcRenderer.invoke('get-mpv-subtitle-render-metrics'), onMpvSubtitleRenderMetrics: (callback: (metrics: MpvSubtitleRenderMetrics) => void) => { ipcRenderer.on( 'mpv-subtitle-render-metrics:set', (_event: IpcRendererEvent, metrics: MpvSubtitleRenderMetrics) => { callback(metrics); }, ); }, onSubtitleAss: (callback: (assText: string) => void) => { ipcRenderer.on('subtitle-ass:set', (_event: IpcRendererEvent, assText: string) => { callback(assText); }); }, onOverlayDebugVisualization: (callback: (enabled: boolean) => void) => { ipcRenderer.on( 'overlay-debug-visualization:set', (_event: IpcRendererEvent, enabled: boolean) => { callback(enabled); }, ); }, setIgnoreMouseEvents: (ignore: boolean, options?: { forward?: boolean }) => { ipcRenderer.send('set-ignore-mouse-events', ignore, options); }, openYomitanSettings: () => { ipcRenderer.send('open-yomitan-settings'); }, getSubtitlePosition: (): Promise => ipcRenderer.invoke('get-subtitle-position'), saveSubtitlePosition: (position: SubtitlePosition) => { ipcRenderer.send('save-subtitle-position', position); }, getMecabStatus: (): Promise => ipcRenderer.invoke('get-mecab-status'), setMecabEnabled: (enabled: boolean) => { ipcRenderer.send('set-mecab-enabled', enabled); }, sendMpvCommand: (command: (string | number)[]) => { ipcRenderer.send('mpv-command', command); }, getKeybindings: (): Promise => ipcRenderer.invoke('get-keybindings'), getConfiguredShortcuts: (): Promise> => ipcRenderer.invoke('get-config-shortcuts'), getJimakuMediaInfo: (): Promise => ipcRenderer.invoke('jimaku:get-media-info'), jimakuSearchEntries: (query: JimakuSearchQuery): Promise> => ipcRenderer.invoke('jimaku:search-entries', query), jimakuListFiles: (query: JimakuFilesQuery): Promise> => ipcRenderer.invoke('jimaku:list-files', query), jimakuDownloadFile: (query: JimakuDownloadQuery): Promise => ipcRenderer.invoke('jimaku:download-file', query), quitApp: () => { ipcRenderer.send('quit-app'); }, toggleDevTools: () => { ipcRenderer.send('toggle-dev-tools'); }, toggleOverlay: () => { ipcRenderer.send('toggle-overlay'); }, getAnkiConnectStatus: (): Promise => ipcRenderer.invoke('get-anki-connect-status'), setAnkiConnectEnabled: (enabled: boolean) => { ipcRenderer.send('set-anki-connect-enabled', enabled); }, clearAnkiConnectHistory: () => { ipcRenderer.send('clear-anki-connect-history'); }, onSecondarySub: (callback: (text: string) => void) => { ipcRenderer.on('secondary-subtitle:set', (_event: IpcRendererEvent, text: string) => callback(text), ); }, onSecondarySubMode: (callback: (mode: SecondarySubMode) => void) => { ipcRenderer.on('secondary-subtitle:mode', (_event: IpcRendererEvent, mode: SecondarySubMode) => callback(mode), ); }, getSecondarySubMode: (): Promise => ipcRenderer.invoke('get-secondary-sub-mode'), getCurrentSecondarySub: (): Promise => ipcRenderer.invoke('get-current-secondary-sub'), focusMainWindow: () => ipcRenderer.invoke('focus-main-window') as Promise, getSubtitleStyle: (): Promise => ipcRenderer.invoke('get-subtitle-style'), onSubsyncManualOpen: (callback: (payload: SubsyncManualPayload) => void) => { ipcRenderer.on( 'subsync:open-manual', (_event: IpcRendererEvent, payload: SubsyncManualPayload) => { callback(payload); }, ); }, runSubsyncManual: (request: SubsyncManualRunRequest): Promise => ipcRenderer.invoke('subsync:run-manual', request), onKikuFieldGroupingRequest: (callback: (data: KikuFieldGroupingRequestData) => void) => { ipcRenderer.on( 'kiku:field-grouping-request', (_event: IpcRendererEvent, data: KikuFieldGroupingRequestData) => callback(data), ); }, kikuBuildMergePreview: (request: KikuMergePreviewRequest): Promise => ipcRenderer.invoke('kiku:build-merge-preview', request), kikuFieldGroupingRespond: (choice: KikuFieldGroupingChoice) => { ipcRenderer.send('kiku:field-grouping-respond', choice); }, getRuntimeOptions: (): Promise => ipcRenderer.invoke('runtime-options:get'), setRuntimeOptionValue: ( id: RuntimeOptionId, value: RuntimeOptionValue, ): Promise => ipcRenderer.invoke('runtime-options:set', id, value), cycleRuntimeOption: (id: RuntimeOptionId, direction: 1 | -1): Promise => ipcRenderer.invoke('runtime-options:cycle', id, direction), onRuntimeOptionsChanged: (callback: (options: RuntimeOptionState[]) => void) => { ipcRenderer.on( 'runtime-options:changed', (_event: IpcRendererEvent, options: RuntimeOptionState[]) => { callback(options); }, ); }, onOpenRuntimeOptions: (callback: () => void) => { ipcRenderer.on('runtime-options:open', () => { callback(); }); }, onOpenJimaku: (callback: () => void) => { ipcRenderer.on('jimaku:open', () => { callback(); }); }, appendClipboardVideoToQueue: (): Promise => ipcRenderer.invoke('clipboard:append-video-to-queue'), notifyOverlayModalClosed: (modal: 'runtime-options' | 'subsync' | 'jimaku') => { ipcRenderer.send('overlay:modal-closed', modal); }, reportOverlayContentBounds: (measurement: OverlayContentMeasurement) => { ipcRenderer.send('overlay-content-bounds:report', measurement); }, onConfigHotReload: (callback: (payload: ConfigHotReloadPayload) => void) => { ipcRenderer.on( 'config:hot-reload', (_event: IpcRendererEvent, payload: ConfigHotReloadPayload) => { callback(payload); }, ); }, }; contextBridge.exposeInMainWorld('electronAPI', electronAPI);