feat(engine): 添加启动超时功能和强制终止引擎的支持

- 在 ControlWindow 中添加了 'control.engine.forceKill' 事件处理,允许强制终止引擎。
- 在 CaptionEngine 中实现了启动超时机制,若引擎启动超时,将自动强制停止并发送错误消息。
- 更新了国际化文件,添加了与启动超时相关的提示信息。
- 在 EngineControl 组件中添加了启动超时的输入选项,允许用户设置超时时间。
- 更新了相关类型定义以支持新的启动超时配置。
This commit is contained in:
xuemian
2025-08-28 10:24:08 +10:00
parent 34362fea3d
commit 051a497f3a
14 changed files with 150 additions and 16 deletions

View File

@@ -14,8 +14,9 @@ export class CaptionEngine {
process: any | undefined
client: net.Socket | undefined
port: number = 8080
status: 'running' | 'starting' | 'stopping' | 'stopped' = 'stopped'
status: 'running' | 'starting' | 'stopping' | 'stopped' | 'starting-timeout' = 'stopped'
timerID: NodeJS.Timeout | undefined
startTimeoutID: NodeJS.Timeout | undefined
private getApp(): boolean {
if (allConfig.controls.customized) {
@@ -96,6 +97,11 @@ export class CaptionEngine {
public connect() {
if(this.client) { Log.warn('Client already exists, ignoring...') }
// 清除启动超时计时器
if (this.startTimeoutID) {
clearTimeout(this.startTimeoutID)
this.startTimeoutID = undefined
}
this.client = net.createConnection({ port: this.port }, () => {
Log.info('Connected to caption engine server');
});
@@ -130,6 +136,17 @@ export class CaptionEngine {
this.process = spawn(this.appPath, this.command)
this.status = 'starting'
Log.info('Caption Engine Starting, PID:', this.process.pid)
// 设置启动超时机制
const timeoutMs = allConfig.controls.startTimeoutSeconds * 1000
this.startTimeoutID = setTimeout(() => {
if (this.status === 'starting') {
Log.warn(`Engine start timeout after ${allConfig.controls.startTimeoutSeconds} seconds, forcing kill...`)
this.status = 'starting-timeout'
controlWindow.sendErrorMessage(i18n('engine.start.timeout'))
this.forceKill()
}
}, timeoutMs)
this.process.stdout.on('data', (data: any) => {
const lines = data.toString().split('\n')
@@ -165,6 +182,11 @@ export class CaptionEngine {
}
this.status = 'stopped'
clearInterval(this.timerID)
// 清理启动超时计时器
if (this.startTimeoutID) {
clearTimeout(this.startTimeoutID)
this.startTimeoutID = undefined
}
Log.info(`Engine exited with code ${code}`)
});
}
@@ -188,21 +210,47 @@ export class CaptionEngine {
}
public kill(){
if(!this.process || !this.process.pid) return
if(this.status !== 'running'){
Log.warn('Trying to kill engine which is not running, current status:', this.status)
return
}
this.sendCommand('stop')
if(this.client){
this.client.destroy()
this.client = undefined
}
this.status = 'stopping'
this.timerID = setTimeout(() => {
if(this.status !== 'stopping') return
Log.warn('Engine process still not stopped, trying to kill...')
this.forceKill()
}, 4000);
}
public forceKill(){
if(!this.process || !this.process.pid) return
Log.warn('Force killing engine process, PID:', this.process.pid)
// 清理启动超时计时器
if (this.startTimeoutID) {
clearTimeout(this.startTimeoutID)
this.startTimeoutID = undefined
}
Log.warn('Trying to kill engine process, PID:', this.process.pid)
if(this.client){
this.client.destroy()
this.client = undefined
}
if (this.process.pid) {
let cmd = `kill ${this.process.pid}`;
let cmd = `kill -9 ${this.process.pid}`;
if (process.platform === "win32") {
cmd = `taskkill /pid ${this.process.pid} /t /f`
}
exec(cmd)
exec(cmd, (error) => {
if (error) {
Log.error('Failed to force kill process:', error)
} else {
Log.info('Process force killed successfully')
}
})
}
this.status = 'stopping'
}