Skip to content

Commit 5c48c2b

Browse files
refactor: add enum type for ipc channel name (#47)
* refactor: add enum type for ipc channel name * fix: type * refactor: 3 different IPC channels * refactor: 3 different IPC channels * fix: MagicStr in Renderer part --------- Co-authored-by: Tohrusky <65994850+Tohrusky@users.noreply.github.com>
1 parent 3c3b6df commit 5c48c2b

23 files changed

Lines changed: 703 additions & 625 deletions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# VSET --Video SuperResolution Encode Tool
1+
# VSET
22
基于*Vapoursynth*的图形化视频批量压制处理工具。
33

44
## [💬 感谢发电名单](https://github.com/NangInShell/VSET/blob/main/Thanks.md)

electron.vite.config.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
55
export default defineConfig({
66
main: {
77
plugins: [externalizeDepsPlugin()],
8+
resolve: {
9+
alias: {
10+
'@main': resolve('src/main'),
11+
'@shared': resolve('src/shared'),
12+
},
13+
},
814
},
915
preload: {
1016
plugins: [externalizeDepsPlugin()],
@@ -13,6 +19,7 @@ export default defineConfig({
1319
resolve: {
1420
alias: {
1521
'@renderer': resolve('src/renderer/src'),
22+
'@shared': resolve('src/shared'),
1623
},
1724
},
1825
plugins: [vue()],

eslint.config.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@ export default antfu(
4242
{
4343
files: ['**/*.vue'],
4444
rules: {
45+
'@typescript-eslint/ban-ts-comment': ['error', { 'ts-ignore': 'allow-with-description' }],
46+
'@typescript-eslint/explicit-function-return-type': 'error',
47+
'@typescript-eslint/explicit-module-boundary-types': 'off',
48+
'@typescript-eslint/no-empty-function': ['error', { allow: ['arrowFunctions'] }],
49+
'@typescript-eslint/no-explicit-any': ['off'],
50+
'@typescript-eslint/no-non-null-assertion': 'off',
51+
'@typescript-eslint/no-var-requires': 'off',
52+
'@typescript-eslint/no-inferrable-types': 'off',
4553
'vue/require-default-prop': 'off',
4654
'vue/multi-word-component-names': 'off',
4755
},

package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,16 @@
2323
"dependencies": {
2424
"@element-plus/icons-vue": "^2.3.2",
2525
"electron-updater": "^6.6.2",
26-
"element-plus": "^2.10.7",
26+
"element-plus": "^2.11.1",
2727
"iconv-lite": "^0.6.3",
2828
"naive-ui": "^2.42.0",
2929
"pinia": "^3.0.3",
3030
"pinia-plugin-persistedstate": "^4.5.0",
3131
"ps-tree": "^1.2.0",
32-
"systeminformation": "^5.27.7",
32+
"systeminformation": "^5.27.8",
3333
"tree-kill": "^1.2.2",
3434
"vicons": "^0.0.1",
35-
"vue": "^3.5.18",
35+
"vue": "^3.5.20",
3636
"vue-router": "^4.5.1"
3737
},
3838
"devDependencies": {
@@ -48,10 +48,10 @@
4848
"electron": "^27.3.11",
4949
"electron-builder": "^26.0.12",
5050
"electron-vite": "^4.0.0",
51-
"eslint": "^9.33.0",
51+
"eslint": "^9.34.0",
5252
"eslint-plugin-vue": "^10.4.0",
5353
"typescript": "^5.9.2",
54-
"vite": "^7.1.2",
54+
"vite": "^7.1.3",
5555
"vue-tsc": "^2.2.12"
5656
},
5757
"pnpm": {

pnpm-lock.yaml

Lines changed: 472 additions & 471 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/main/childProcessManager.ts

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,56 @@ import type { ChildProcess } from 'node:child_process'
22
import kill from 'tree-kill'
33
import { requestStop } from './runCommand'
44

5-
const childProcesses: ChildProcess[] = []
5+
interface ManagedProc {
6+
name: string
7+
proc: ChildProcess
8+
}
9+
10+
const childProcesses: ManagedProc[] = []
611

7-
export function addProcess(proc: ChildProcess): void {
8-
childProcesses.push(proc)
12+
export function addProcess(name: string, proc: ChildProcess): void {
13+
childProcesses.push({ name, proc })
914
}
1015

1116
export function removeProcess(proc: ChildProcess): void {
12-
const index = childProcesses.indexOf(proc)
17+
const index = childProcesses.findIndex(p => p.proc === proc)
1318
if (index !== -1) {
1419
childProcesses.splice(index, 1)
1520
}
1621
}
1722

23+
function safeUnpipe(): void {
24+
const vspipe = childProcesses.find(p => p.name === 'vspipe')
25+
const ffmpeg = childProcesses.find(p => p.name === 'ffmpeg')
26+
27+
if (vspipe?.proc?.stdout && ffmpeg?.proc?.stdin) {
28+
try {
29+
vspipe.proc.stdout.unpipe(ffmpeg.proc.stdin)
30+
}
31+
catch {}
32+
try {
33+
ffmpeg.proc.stdin.end()
34+
}
35+
catch {}
36+
}
37+
}
38+
1839
// ✅ 使用 Promise 确保等待 kill 完成
1940
export async function killAllProcesses(): Promise<void> {
2041
requestStop()
21-
const promises = childProcesses.map((proc) => {
42+
43+
// 先安全断开管道
44+
safeUnpipe()
45+
const promises = childProcesses.map(({ name, proc }) => {
2246
return new Promise<void>((resolve) => {
2347
if (!proc.killed && typeof proc.pid === 'number') {
24-
console.log(`🔪 正在终止子进程 PID=${proc.pid}`)
48+
console.log(`stop [${name}] PID=${proc.pid}`)
2549
kill(proc.pid, 'SIGKILL', (err) => {
2650
if (err) {
27-
console.error(`❌ 无法终止 PID=${proc.pid}:`, err)
51+
console.error(`can not stop [${name}] PID=${proc.pid}:`, err)
2852
}
2953
else {
30-
console.log(`✅ 成功终止 PID=${proc.pid}`)
54+
console.log(`success stop [${name}] PID=${proc.pid}`)
3155
}
3256
resolve()
3357
})

src/main/index.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import path from 'node:path'
22
import { electronApp, is, optimizer } from '@electron-toolkit/utils'
3+
import { IpcChannelInvoke, IpcChannelSend } from '@shared/constant/ipc'
34
import { app, BrowserWindow, ipcMain, nativeImage, shell } from 'electron'
45
import appIcon from '../../resources/icon.png?asset'
56
import { killAllProcesses } from './childProcessManager'
@@ -26,23 +27,23 @@ function createWindow(): BrowserWindow {
2627
})
2728

2829
// ipcMain
29-
ipcMain.on('execute-command', runCommand)
30+
ipcMain.on(IpcChannelSend.EXECUTE_COMMAND, runCommand)
3031

31-
ipcMain.on('pause', PauseCommand)
32+
ipcMain.on(IpcChannelSend.PAUSE, PauseCommand)
3233

33-
ipcMain.on('preview', preview)
34+
ipcMain.on(IpcChannelSend.PREVIEW, preview)
3435

35-
ipcMain.on('preview-frame', previewFrame)
36+
ipcMain.on(IpcChannelSend.PREVIEW_FRAME, previewFrame)
3637

37-
ipcMain.on('stop-all-processes', killAllProcesses)
38+
ipcMain.on(IpcChannelSend.STOP_ALL_PROCESSES, killAllProcesses)
3839

39-
ipcMain.on('generate-json', writeSettingsJson)
40+
ipcMain.on(IpcChannelSend.GENERATE_JSON, writeSettingsJson)
4041

41-
ipcMain.handle('open-folder-dialog', openDirectory)
42+
ipcMain.handle(IpcChannelInvoke.OPEN_DIRECTORY_DIALOG, openDirectory)
4243

43-
ipcMain.handle('get-gpu-info', getGpuInfo)
44+
ipcMain.handle(IpcChannelInvoke.GET_GPU_INFO, getGpuInfo)
4445

45-
ipcMain.handle('get-cpu-info', getCpuInfo)
46+
ipcMain.handle(IpcChannelInvoke.GET_CPU_INFO, getCpuInfo)
4647

4748
// mainWindow
4849
mainWindow.on('ready-to-show', () => {

src/main/previewOutput.ts

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { IpcMainEvent } from 'electron'
33
import { Buffer } from 'node:buffer'
44
import { spawn } from 'node:child_process'
55
import path from 'node:path'
6+
import { IpcChannelOn } from '@shared/constant/ipc'
67
import iconv from 'iconv-lite'
78
import { addProcess, removeProcess } from './childProcessManager'
89
import { getExecPath, getGenVpyPath } from './getCorePath'
@@ -12,8 +13,8 @@ export async function preview(event: IpcMainEvent, taskConfig: TaskConfig): Prom
1213
const vspipePath = getExecPath().vspipe
1314

1415
if (!taskConfig.fileList || taskConfig.fileList.length === 0) {
15-
event.sender.send('ffmpeg-output', '错误: 没有提供用于预览的文件。\n')
16-
event.sender.send('ffmpeg-finish')
16+
event.sender.send(IpcChannelOn.FFMPEG_OUTPUT, '错误: 没有提供用于预览的文件。\n')
17+
event.sender.send(IpcChannelOn.FFMPEG_FINISHED)
1718
return
1819
}
1920

@@ -38,7 +39,7 @@ export async function preview(event: IpcMainEvent, taskConfig: TaskConfig): Prom
3839
}
3940
await new Promise<void>((resolve, reject) => {
4041
const vspipeInfoProcess = spawn(vspipePath, ['--info', vpyPath])
41-
addProcess(vspipeInfoProcess)
42+
addProcess('vspipe', vspipeInfoProcess)
4243

4344
let vspipeOut = '' // 用于保存 stdout 内容
4445
// eslint-disable-next-line unused-imports/no-unused-vars
@@ -47,36 +48,36 @@ export async function preview(event: IpcMainEvent, taskConfig: TaskConfig): Prom
4748
vspipeInfoProcess.stdout.on('data', (data: Buffer) => {
4849
const str = iconv.decode(data, 'gbk')
4950
vspipeOut += str
50-
event.sender.send('ffmpeg-output', `${str}`)
51+
event.sender.send(IpcChannelOn.FFMPEG_OUTPUT, `${str}`)
5152
})
5253

5354
vspipeInfoProcess.stderr.on('data', (data: Buffer) => {
5455
const str = iconv.decode(data, 'gbk')
5556
stderrOut += str
56-
event.sender.send('ffmpeg-output', `${str}`)
57+
event.sender.send(IpcChannelOn.FFMPEG_OUTPUT, `${str}`)
5758
})
5859

5960
vspipeInfoProcess.on('close', (code) => {
6061
removeProcess(vspipeInfoProcess)
61-
event.sender.send('ffmpeg-output', `vspipe info 执行完毕,退出码: ${code}\n`)///////
62+
event.sender.send(IpcChannelOn.FFMPEG_OUTPUT, `vspipe info 执行完毕,退出码: ${code}\n`)///////
6263
info = {
6364
width: vspipeOut.match(/Width:\s*(\d+)/)?.[1] || '未知',
6465
height: vspipeOut.match(/Height:\s*(\d+)/)?.[1] || '未知',
6566
frames: vspipeOut.match(/Frames:\s*(\d+)/)?.[1] || '0',
6667
fps: vspipeOut.match(/FPS:\s*([\d/]+)\s*\(([\d.]+) fps\)/)?.[2] || '0',
6768
}
6869

69-
event.sender.send('preview-info', info)
70+
event.sender.send(IpcChannelOn.PREVIEW_INFO, info)
7071
resolve()
7172
})
7273

7374
vspipeInfoProcess.on('error', (err) => {
74-
event.sender.send('ffmpeg-output', `vspipe 执行出错: ${err.message}\n`)
75+
event.sender.send(IpcChannelOn.FFMPEG_OUTPUT, `vspipe 执行出错: ${err.message}\n`)
7576
reject(err)
7677
})
7778
})
78-
event.sender.send('preview-vpyPath', vpyPath)
79-
event.sender.send('ffmpeg-finish')
79+
event.sender.send(IpcChannelOn.PREVIEW_VPY_PATH, vpyPath)
80+
event.sender.send(IpcChannelOn.FFMPEG_FINISHED)
8081
}
8182

8283
export async function previewFrame(event: IpcMainEvent, vpyPath: string, currentFrame: number): Promise<void> {
@@ -87,7 +88,7 @@ export async function previewFrame(event: IpcMainEvent, vpyPath: string, current
8788
const cmd = `"${vspipePath}" -c y4m --start ${currentFrame} --end ${currentFrame} "${vpyPath}" - | "${ffmpegPath}" -y -f yuv4mpegpipe -i - -frames:v 1 -vcodec png -f image2pipe -`
8889

8990
const vspipePreviewProcess = spawn(cmd, { shell: true })
90-
addProcess(vspipePreviewProcess)
91+
addProcess('vspipe', vspipePreviewProcess)
9192

9293
const chunks: Buffer[] = []
9394

@@ -97,24 +98,24 @@ export async function previewFrame(event: IpcMainEvent, vpyPath: string, current
9798

9899
vspipePreviewProcess.stderr.on('data', (data: Buffer) => {
99100
const str = iconv.decode(data, 'gbk')
100-
event.sender.send('ffmpeg-output', str)
101+
event.sender.send(IpcChannelOn.FFMPEG_OUTPUT, str)
101102
})
102103

103104
vspipePreviewProcess.on('close', (code) => {
104105
removeProcess(vspipePreviewProcess)
105106
if (code === 0) {
106107
const buffer = Buffer.concat(chunks)
107108
const base64 = `data:image/png;base64,${buffer.toString('base64')}`
108-
event.sender.send('preview-image', base64)
109+
event.sender.send(IpcChannelOn.PREVIEW_IMAGE, base64)
109110
}
110111
else {
111-
event.sender.send('ffmpeg-output', `预览失败,退出码: ${code}`)
112-
event.sender.send('preview-image', null)
112+
event.sender.send(IpcChannelOn.FFMPEG_OUTPUT, `预览失败,退出码: ${code}`)
113+
event.sender.send(IpcChannelOn.PREVIEW_IMAGE, null)
113114
}
114-
event.sender.send('ffmpeg-finish')
115+
event.sender.send(IpcChannelOn.FFMPEG_FINISHED)
115116
})
116117

117118
vspipePreviewProcess.on('error', (err) => {
118-
event.sender.send('ffmpeg-output', `命令执行出错: ${err.message}`)
119+
event.sender.send(IpcChannelOn.FFMPEG_OUTPUT, `命令执行出错: ${err.message}`)
119120
})
120121
}

0 commit comments

Comments
 (0)