diff --git a/src/main/control.ts b/src/main/control.ts index a1f9faa..98ec486 100644 --- a/src/main/control.ts +++ b/src/main/control.ts @@ -90,8 +90,17 @@ class ControlWindow { this.window?.webContents.send('control.engine.already') } else { - captionEngine.start() - this.window?.webContents.send('control.engine.started') + if( + process.env.DASHSCOPE_API_KEY || + (controls.customized && controls.customizedApp) + ) { + if(this.window){ + captionEngine.start(this.window) + } + } + else { + this.sendErrorMessage('没有检测到 DASHSCOPE_API_KEY 环境变量,如果要使用 gummy 引擎,需要在阿里云百炼平台获取 API Key 并添加到本机环境变量') + } } }) // 停止字幕引擎 @@ -104,6 +113,10 @@ class ControlWindow { captionLog.splice(0) }) } + + public sendErrorMessage(message: string) { + this.window?.webContents.send('control.error.send', message) + } } export const controlWindow = new ControlWindow() \ No newline at end of file diff --git a/src/main/utils/engine.ts b/src/main/utils/engine.ts index e2fe350..6c14041 100644 --- a/src/main/utils/engine.ts +++ b/src/main/utils/engine.ts @@ -1,103 +1,119 @@ import { spawn, exec } from 'child_process' -import { app } from 'electron' +import { app, BrowserWindow } from 'electron' import { is } from '@electron-toolkit/utils' import path from 'path' -import { addCaptionLog, controls } from './config' +import { addCaptionLog, controls, sendControls } from './config' +import { controlWindow } from '../control' -export class CaptionEngine { - appPath: string = '' - command: string[] = [] - process: any | undefined +export class CaptionEngine { + appPath: string = '' + command: string[] = [] + process: any | undefined - private getApp() { - if(controls.customized && controls.customizedApp){ - this.appPath = controls.customizedApp - this.command = [ controls.customizedCommand ] - } - else if(controls.engine === 'gummy'){ - let gummyName = '' - if(process.platform === 'win32'){ - gummyName = 'main-gummy.exe' - } - else if(process.platform === 'linux'){ - gummyName = 'main-gummy' - } - else{ - throw new Error('Unsupported platform') - } - if(is.dev){ - this.appPath = path.join( - app.getAppPath(), - 'python-subprocess', 'dist', gummyName - ) - } - else{ - this.appPath = path.join( - process.resourcesPath, - 'python-subprocess', 'dist', gummyName - ) - } - this.command = [] - this.command.push('-s', controls.sourceLang) - this.command.push('-t', controls.translation ? controls.targetLang : 'none') - this.command.push('-a', controls.audio ? '1' : '0') + private getApp() { + if (controls.customized && controls.customizedApp) { + this.appPath = controls.customizedApp + this.command = [controls.customizedCommand] + } + else if (controls.engine === 'gummy') { + controls.customized = false + let gummyName = '' + if (process.platform === 'win32') { + gummyName = 'main-gummy.exe' + } + else if (process.platform === 'linux') { + gummyName = 'main-gummy' + } + else { + controlWindow.sendErrorMessage('不支持的操作系统平台:' + process.platform) + throw new Error('Unsupported platform') + } + if (is.dev) { + this.appPath = path.join( + app.getAppPath(), + 'python-subprocess', 'dist', gummyName + ) + } + else { + this.appPath = path.join( + process.resourcesPath, + 'python-subprocess', 'dist', gummyName + ) + } + this.command = [] + this.command.push('-s', controls.sourceLang) + this.command.push('-t', controls.translation ? controls.targetLang : 'none') + this.command.push('-a', controls.audio ? '1' : '0') - console.log('[INFO] engine', this.appPath) - console.log('[INFO] engine command',this.command) - } + console.log('[INFO] Engine Path:', this.appPath) + console.log('[INFO] Engine Command:', this.command) + } + } + + public start(window: BrowserWindow) { + if (this.process) { + this.stop(); + } + this.getApp() + try { + this.process = spawn(this.appPath, this.command) + } + catch (e) { + controlWindow.sendErrorMessage('字幕引擎启动失败:' + e) + console.error('[ERROR] Error starting subprocess:', e) + return } - public start() { - if (this.process) { - this.stop(); + console.log('[INFO] Caption Engine Started: ', { + appPath: this.appPath, + command: this.command + }) + + controls.engineEnabled = true + sendControls(window) + window.webContents.send('control.engine.started') + + 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) { + controlWindow.sendErrorMessage('字幕引擎输出内容无法解析为 JSON 对象:' + e) + console.error('[ERROR] Error parsing JSON:', e); + } } - 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.stderr.on('data', (data) => { + controlWindow.sendErrorMessage('字幕引擎错误:' + data) + console.error(`[ERROR] Subprocess Error: ${data}`); + }); - 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; + this.process.on('close', (code: any) => { + console.log(`[INFO] Subprocess exited with code ${code}`); + this.process = undefined; + }); + } + + public stop() { + if (this.process) { + if (process.platform === "win32" && this.process.pid) { + exec(`taskkill /pid ${this.process.pid} /t /f`, (error) => { + if (error) { + controlWindow.sendErrorMessage('字幕引擎进程关闭失败:' + error) + console.error(`[ERROR] Failed to kill process: ${error}`); + } }); + } else { + this.process.kill('SIGKILL'); + } + this.process = undefined; + controls.engineEnabled = false; + console.log('[INFO] Caption engine process stopped'); } - - public stop() { - if (this.process) { - if (process.platform === "win32" && this.process.pid) { - exec(`taskkill /pid ${this.process.pid} /t /f`, (error) => { - if (error) { - console.error(`Failed to kill process: ${error}`); - } - }); - } else { - this.process.kill('SIGKILL'); - } - this.process = undefined; - controls.engineEnabled = false; - console.log('[INFO] Caption engine process stopped'); - } - } + } } \ No newline at end of file diff --git a/src/renderer/src/stores/captionControl.ts b/src/renderer/src/stores/captionControl.ts index dffbafb..5576913 100644 --- a/src/renderer/src/stores/captionControl.ts +++ b/src/renderer/src/stores/captionControl.ts @@ -1,6 +1,9 @@ import { ref } from 'vue' import { defineStore } from 'pinia' + import { notification } from 'ant-design-vue' +import { ExclamationCircleOutlined } from '@ant-design/icons-vue' +import { h } from 'vue' export const useCaptionControlStore = defineStore('captionControl', () => { const captionEngine = ref([ @@ -109,6 +112,16 @@ export const useCaptionControlStore = defineStore('captionControl', () => { }); }) + window.electron.ipcRenderer.on('control.error.send', (_, message) => { + notification.open({ + message: '发生错误', + description: message, + duration: null, + placement: 'topLeft', + icon: () => h(ExclamationCircleOutlined, { style: 'color: #ff4d4f' }) + }); + }) + return { captionEngine, // 字幕引擎 audioType, // 音频类型