From c446f846bda1dfe64465413d9e2ffa657976c4e3 Mon Sep 17 00:00:00 2001 From: himeditator Date: Thu, 19 Jun 2025 22:22:17 +0800 Subject: [PATCH] =?UTF-8?q?feat(main):=20=E5=AE=9E=E7=8E=B0=E5=AD=97?= =?UTF-8?q?=E5=B9=95=E5=BC=95=E6=93=8E=E6=8E=A7=E5=88=B6=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增字幕引擎启动和停止功能 - 实现控制窗口的字幕引擎状态显示 - 优化字幕日志的发送逻辑 - 重构子进程相关代码 --- src/main/caption.ts | 2 +- src/main/control.ts | 29 +++++-- src/main/index.ts | 5 -- src/main/types/index.ts | 1 + src/main/utils/config.ts | 27 +++++-- src/main/utils/engine.ts | 79 +++++++++++++++++++ src/main/utils/pythonProcess.ts | 42 ---------- .../src/components/CaptionControl.vue | 11 ++- src/renderer/src/components/CaptionData.vue | 25 ++++-- src/renderer/src/stores/captionControl.ts | 57 ++++++++++++- src/renderer/src/stores/captionLog.ts | 9 +++ src/renderer/src/stores/captionStyle.ts | 2 + 12 files changed, 218 insertions(+), 71 deletions(-) create mode 100644 src/main/utils/engine.ts delete mode 100644 src/main/utils/pythonProcess.ts diff --git a/src/main/caption.ts b/src/main/caption.ts index ad7130c..ea3010d 100644 --- a/src/main/caption.ts +++ b/src/main/caption.ts @@ -30,7 +30,7 @@ class CaptionWindow { setTimeout(() => { if (this.window) { sendStyles(this.window); - sendCaptionLog(this.window); + sendCaptionLog(this.window, 'set'); } }, 1000); diff --git a/src/main/control.ts b/src/main/control.ts index e45de90..aa828fd 100644 --- a/src/main/control.ts +++ b/src/main/control.ts @@ -4,15 +4,18 @@ import { is } from '@electron-toolkit/utils' import icon from '../../resources/icon.png?asset' import { captionWindow } from './caption' import { + captionEngine, + controls, setStyles, sendStyles, sendCaptionLog, - setControls + setControls, + sendControls } from './utils/config' class ControlWindow { window: BrowserWindow | undefined; - + public createWindow(): void { this.window = new BrowserWindow({ icon: icon, @@ -32,8 +35,9 @@ class ControlWindow { setTimeout(() => { if (this.window) { - sendStyles(this.window); - sendCaptionLog(this.window); + sendStyles(this.window) // 配置初始样式 + sendCaptionLog(this.window, 'set') // 配置当前字幕记录 + sendControls(this.window) // 配置字幕引擎配置 } }, 1000); @@ -75,10 +79,25 @@ class ControlWindow { captionWindow.window.show() } }) - // 字幕引擎控制配置更新 + // 字幕引擎控制配置更新并启动引擎 ipcMain.on('control.control.change', (_, args) => { setControls(args) }) + // 启动字幕引擎 + ipcMain.on('control.engine.start', () => { + if(controls.engineEnabled){ + this.window?.webContents.send('control.engine.already') + } + else { + captionEngine.start() + this.window?.webContents.send('control.engine.started') + } + }) + // 停止字幕引擎 + ipcMain.on('control.engine.stop', () => { + captionEngine.stop() + this.window?.webContents.send('control.engine.stopped') + }) } } diff --git a/src/main/index.ts b/src/main/index.ts index 9174ab7..fc084a9 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -3,9 +3,6 @@ import { electronApp, optimizer } from '@electron-toolkit/utils' import { controlWindow } from './control' import { captionWindow } from './caption' -import { PythonProcess } from './utils/pythonProcess' -const pySubProcess = new PythonProcess() - app.whenReady().then(() => { electronApp.setAppUserModelId('com.himeditator.autocaption') @@ -18,8 +15,6 @@ app.whenReady().then(() => { controlWindow.createWindow() - // pySubProcess.start() - app.on('activate', function () { if (BrowserWindow.getAllWindows().length === 0){ controlWindow.createWindow() diff --git a/src/main/types/index.ts b/src/main/types/index.ts index e28a7cb..033a285 100644 --- a/src/main/types/index.ts +++ b/src/main/types/index.ts @@ -19,6 +19,7 @@ export interface CaptionItem { } export interface Controls { + engineEnabled: boolean, sourceLang: string, targetLang: string, engine: string, diff --git a/src/main/utils/config.ts b/src/main/utils/config.ts index 328e5a9..86d08e3 100644 --- a/src/main/utils/config.ts +++ b/src/main/utils/config.ts @@ -1,5 +1,8 @@ import { Styles, CaptionItem, Controls } from '../types' import { BrowserWindow } from 'electron' +import { CaptionEngine } from './engine' + +export const captionEngine = new CaptionEngine() export const styles: Styles = { fontFamily: 'sans-serif', @@ -19,12 +22,15 @@ export const controls: Controls = { sourceLang: 'en', targetLang: 'zh', engine: 'gummy', + engineEnabled: false, translation: true, customized: false, customizedApp: '', customizedCommand: '' } +export let engineRunning: boolean = false + export function setStyles(args: any) { styles.fontFamily = args.fontFamily styles.fontSize = args.fontSize @@ -40,23 +46,27 @@ export function setStyles(args: any) { export function sendStyles(window: BrowserWindow) { window.webContents.send('caption.style.set', styles) - console.log('[INFO] Send Styles:', styles) + console.log(`[INFO] Send Styles to #${window.id}:`, styles) } -export function sendCaptionLog(window: BrowserWindow) { - window.webContents.send('both.log.set', captionLog) +export function sendCaptionLog(window: BrowserWindow, command: string) { + if(command === 'add'){ + window.webContents.send(`both.log.add`, captionLog[captionLog.length - 1]) + } + else if(command === 'set'){ + window.webContents.send(`both.log.${command}`, captionLog) + } } export function addCaptionLog(log: CaptionItem) { if(captionLog.length && captionLog[captionLog.length - 1].index === log.index) { - captionLog.splice(captionLog.length - 1, 1) - captionLog.push(log) + captionLog.splice(captionLog.length - 1, 1, log) } else { captionLog.push(log) } for(const window of BrowserWindow.getAllWindows()){ - sendCaptionLog(window) + sendCaptionLog(window, 'add') } } @@ -69,4 +79,9 @@ export function setControls(args: any) { controls.customizedApp = args.customizedApp controls.customizedCommand = args.customizedCommand console.log('[INFO] Set Controls:', controls) +} + +export function sendControls(window: BrowserWindow) { + window.webContents.send('control.control.set', controls) + console.log(`[INFO] Send Controls to #${window.id}:`, controls) } \ No newline at end of file diff --git a/src/main/utils/engine.ts b/src/main/utils/engine.ts new file mode 100644 index 0000000..295a20d --- /dev/null +++ b/src/main/utils/engine.ts @@ -0,0 +1,79 @@ +import { spawn } from 'child_process' +import { app } from 'electron' +import path from 'path' +import { addCaptionLog, controls } from './config' + +export class CaptionEngine { + appPath: string = '' + command: string[] = [] + process: any | undefined + + private getApp() { + if(controls.customized && controls.customizedCommand && controls.customizedApp){ + this.appPath = controls.customizedApp + this.command = [ controls.customizedCommand ] + } + else if(controls.engine === 'gummy'){ + this.appPath = path.join( + app.getAppPath(), + 'python-subprocess', 'subenv', 'Scripts', 'python.exe' + ) + this.command = [] + this.command.push(path.join( + app.getAppPath(), + 'python-subprocess', 'main.py' + )) + this.command.push('-s', controls.sourceLang) + this.command.push('-t', controls.targetLang) + + console.log(this.appPath) + console.log(this.command) + } + } + + public start() { + if (this.process) { + this.stop(); + } + this.getApp() + this.process = spawn(this.appPath, this.command) + controls.engineEnabled = true + + console.log('[INFO] Caption Engine Started: ', { + appPath: this.appPath, + command: this.command + }) + + this.process.stdout.on('data', (data) => { + const lines = data.toString().split('\n'); + lines.forEach( (line: string) => { + if (line.trim()) { + try { + const caption = JSON.parse(line); + addCaptionLog(caption); + } catch (e) { + console.error('Error parsing JSON:', e); + } + } + }); + }); + + this.process.stderr.on('data', (data) => { + console.error(`Python Error: ${data}`); + }); + + this.process.on('close', (code: any) => { + console.log(`Python process exited with code ${code}`); + this.process = undefined; + }); + } + + public stop() { + if (this.process) { + this.process.kill(); + this.process = undefined; + controls.engineEnabled = false; + console.log('[INFO] Caption engine process stopped'); + } + } +} \ No newline at end of file diff --git a/src/main/utils/pythonProcess.ts b/src/main/utils/pythonProcess.ts deleted file mode 100644 index 7cd1034..0000000 --- a/src/main/utils/pythonProcess.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { spawn } from 'child_process' -import { app } from 'electron' -import path from 'path' -import { addCaptionLog } from './config' - -export class PythonProcess { - public start() { - const basePath = app.getAppPath() - const pythonPath = path.join( - basePath, - 'python-subprocess', 'subenv', 'Scripts', 'python.exe' - ) - const targetPath = path.join(basePath, 'python-subprocess', 'main.py') - - console.log(pythonPath) - console.log(targetPath) - - const pythonProcess = spawn(pythonPath, [targetPath]) - - pythonProcess.stdout.on('data', (data) => { - const lines = data.toString().split('\n'); - lines.forEach( (line: string) => { - if (line.trim()) { - try { - const caption = JSON.parse(line); - addCaptionLog(caption); - } catch (e) { - console.error('Error parsing JSON:', e); - } - } - }); - }); - - pythonProcess.stderr.on('data', (data) => { - console.error(`Python Error: ${data}`); - }); - - pythonProcess.on('close', (code) => { - console.log(`Python process exited with code ${code}`); - }); - } -} \ No newline at end of file diff --git a/src/renderer/src/components/CaptionControl.vue b/src/renderer/src/components/CaptionControl.vue index 31af561..cc4e1db 100644 --- a/src/renderer/src/components/CaptionControl.vue +++ b/src/renderer/src/components/CaptionControl.vue @@ -59,12 +59,12 @@