Skip to content

Commit 045efd8

Browse files
committed
Linux 相关支持;首次连接本地服务器添加重试机制;上传大小添加限制
1 parent 8c5fe0a commit 045efd8

File tree

15 files changed

+203
-58
lines changed

15 files changed

+203
-58
lines changed

config/pkg.linux.config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"scripts": [],
3+
"assets": [],
4+
"targets": [
5+
"node16-linux-x64"
6+
],
7+
"outputPath": "app/backend"
8+
}

electron-builder.json5

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,39 +14,63 @@
1414
"!**/*.map"
1515
],
1616
"extraFiles": [
17-
{
18-
"from": "app/backend/index.exe",
19-
"to": "FFBoxService.exe",
20-
},
21-
{
22-
"from": "app/backend/index",
23-
"to": "Resources/FFBoxService",
24-
},
25-
{
26-
"from": "FFBoxHelper.exe",
27-
"to": "FFBoxHelper.exe",
28-
},
2917
{
3018
"from": "LICENSE",
3119
"to": "LICENSE",
3220
},
3321
],
34-
"mac": {
22+
"win": {
3523
"artifactName": "${productName}_${version}.${ext}",
3624
"target": [
37-
"dmg"
38-
]
25+
{
26+
"target": "dir",
27+
"arch": ["x64"],
28+
}
29+
],
30+
"extraFiles": [
31+
{
32+
"from": "app/backend/index.exe",
33+
"to": "FFBoxService.exe",
34+
},
35+
{
36+
"from": "FFBoxHelper.exe",
37+
"to": "FFBoxHelper.exe",
38+
},
39+
],
3940
},
40-
"win": {
41+
"mac": {
42+
"artifactName": "${productName}_${version}.${ext}",
4143
"target": [
4244
{
43-
"target": "dir",
44-
"arch": [
45-
"x64"
46-
]
45+
"target": "dmg",
46+
'arch': ["arm64"],
4747
}
4848
],
49-
"artifactName": "${productName}_${version}.${ext}"
49+
"extraFiles": [
50+
{
51+
"from": "app/backend/index",
52+
"to": "Resources/FFBoxService",
53+
},
54+
],
55+
},
56+
"linux": {
57+
"target": [
58+
{
59+
"target": "AppImage",
60+
"arch": ["x64"],
61+
},
62+
{
63+
"target": "deb",
64+
"arch": ["x64"],
65+
},
66+
],
67+
"artifactName": "${productName}_${version}.${ext}",
68+
"extraFiles": [
69+
{
70+
"from": "app/backend/index",
71+
"to": "FFBoxService",
72+
},
73+
],
5074
},
5175
"nsis": {
5276
"oneClick": false,

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,14 @@
1515
"build:backend": "node scripts/build-backend.mjs",
1616
"pkg:backend:win": "pkg --config ./config/pkg.win.config.json ./app/backend/index.cjs",
1717
"pkg:backend:mac": "pkg --config ./config/pkg.mac.config.json ./app/backend/index.cjs",
18+
"pkg:backend:linux": "pkg --config ./config/pkg.linux.config.json ./app/backend/index.cjs",
1819
"package": "electron-builder",
1920
"build:everything": "npm run build:frontend && npm run build:backend && npm run package"
2021
},
21-
"author": "",
22+
"author": {
23+
"name": "滔滔清风",
24+
"email": "ttqf.tech@qq.com"
25+
},
2226
"license": "ISC",
2327
"devDependencies": {
2428
"@types/crypto-js": "^4.1.1",

scripts/build-backend.mjs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ const backendConfig = path.resolve('config/vite.backend.ts');
99
// const backendConfig = path.join(__dirname, '../config/vite.backend.ts');
1010
// const backendConfig = require(path.join(__dirname, '../config/vite.backend.ts'));
1111

12-
const isMacOS = process.platform === 'darwin';
13-
const npmExecutablePath = isMacOS ? 'npm' : 'npm.cmd';
12+
const npmExecutablePath = process.platform === 'win32' ? 'npm.cmd' : 'npm';
1413

1514
// 颜色信息可参考 https://misc.flogisoft.com/bash/tip_colors_and_formatting
1615
function wrapColor(color, msg) {
@@ -55,7 +54,8 @@ async function buildBackend() {
5554
configFile: backendConfig,
5655
mode: process.env.NODE_ENV === 'development' ? 'development' : 'production',
5756
});
58-
const buildProcess = spawnSync(npmExecutablePath, ['run', `pkg:backend:${isMacOS ? 'mac' : 'win'}`], { stdio: 'inherit' });
57+
const cmdName = process.platform === 'win32' ? 'win' : (process.platform === 'darwin' ? 'mac' : 'linux');
58+
const buildProcess = spawnSync(npmExecutablePath, ['run', `pkg:backend:${cmdName}`], { stdio: 'inherit' });
5959
// buildProcess.once('exit', process.exit);
6060
}
6161

scripts/dev-frontend.mjs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ const rendererConfig = path.resolve('config/vite.renderer.ts');
1313
const query = new URLSearchParams(import.meta.url.split('?')[1]);
1414
const debug = query.has('debug');
1515

16-
const isMacOS = process.platform === 'darwin';
17-
const npmExecutablePath = isMacOS ? 'npm' : 'npm.cmd';
18-
1916
// 颜色信息可参考 https://misc.flogisoft.com/bash/tip_colors_and_formatting
2017
function wrapColor(color, msg) {
2118
const colorString = [49, 41, 42, 43, 46, 104][['default', 'red', 'green', 'yellow', 'cyan', 'light blue'].indexOf(color) || 0];

src/backend/FFBoxService.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,25 @@ export class FFBoxService extends (EventEmitter as new () => TypedEventEmitter<F
6565
public async initFFmpeg(): Promise<void> {
6666
console.log(getTimeString(new Date()), '检查 FFmpeg 路径和版本。');
6767
if (process.platform === 'darwin') {
68-
await fsPromise.access(path.join(process.execPath, 'ffmpeg'), fs.constants.R_OK).then((result) => {
69-
this.ffmpegPath = path.join(process.execPath, 'ffmpeg');
68+
await fsPromise.access(path.join(process.execPath, '../ffmpeg'), fs.constants.X_OK).then((result) => {
69+
this.ffmpegPath = path.join(process.execPath, '../ffmpeg'); // 【程序目录】沙箱运行模式,service 与 ffmpeg 处在同一层级
7070
}).catch(() => {});
71-
await fsPromise.access('/usr/local/bin/ffmpeg', fs.constants.R_OK).then((result) => {
72-
this.ffmpegPath = '/usr/local/bin/ffmpeg';
71+
await fsPromise.access('/usr/local/bin/ffmpeg', fs.constants.X_OK).then((result) => {
72+
this.ffmpegPath = '/usr/local/bin/ffmpeg'; // 【系统目录】macOS 只允许用户往 /usr/local/bin/ 放东西(而不能是 /usr/bin/),且此种情况下需要完整路径才能引用
7373
}).catch(() => {});
7474
}
75+
if (process.platform === 'linux') {
76+
await fsPromise.access(path.join(process.execPath, '../ffmpeg'), fs.constants.X_OK).then((result) => {
77+
// 【程序目录】deb 沙箱运行模式。service 与 ffmpeg 处在同一目录(/opt/FFBox/)
78+
this.ffmpegPath = path.join(process.execPath, '../ffmpeg');
79+
}).catch(() => {});
80+
await fsPromise.access(path.join(process.cwd(), 'ffmpeg'), fs.constants.X_OK).then((result) => {
81+
this.ffmpegPath = path.join(process.cwd(), 'ffmpeg'); // 【程序目录】AppImage 沙箱运行模式,读取 .AppImage 同级目录
82+
}).catch(() => {});
83+
// 【系统目录】Linux 下 /usr/local/bin/ 和 /usr/bin/ 里的东西均能被直接引用,包括终端执行和沙箱执行,因此此处不需要进行处理
84+
// console.log('路径', process.execPath, process.cwd(), __dirname, this.ffmpegPath);
85+
// this.ffmpegVersion = `路径 ${process.execPath}, ${process.cwd()}, ${__dirname}, ${this.ffmpegPath}`;
86+
}
7587
const ffmpeg = new FFmpeg(this.ffmpegPath, 1);
7688
ffmpeg.on('data', ({ content }) => {
7789
this.setCmdText(-1, content);

src/main/index.ts

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class ElectronApp {
2525
mountAppEvents(): void {
2626
// 本程序是启动的第二个实例时,将因获不到锁而退出
2727
if (!app.requestSingleInstanceLock()) {
28+
console.log('FFBox 已启动,暂不支持启动第二个实例');
2829
app.quit();
2930
process.exit(0);
3031
}
@@ -51,6 +52,13 @@ class ElectronApp {
5152
this.createMainWindow();
5253
}
5354
});
55+
app.on('window-all-closed', () => {
56+
// FFBoxService 进程尽管没有指定 detached
57+
// 但在 macOS 上,主进程退出不会导致 service 退出;在 linux 上,主进程调用了 app.exit() 之后依然会等待 service 退出
58+
// 故保险起见主动关闭
59+
this.service?.sendSig(9);
60+
app.exit();
61+
});
5462

5563
// Set app user model id for windows
5664
// electronApp.setAppUserModelId('com.electron');
@@ -160,11 +168,42 @@ class ElectronApp {
160168
// this.electronStore = new ElectronStore();
161169
}
162170

163-
createService(): Promise<void> {
171+
async createService(): Promise<void> {
164172
this.service = new ProcessInstance();
165173
// 目前做不了进程分离,因为启动的时候会瞬间弹一个黑框,十分不优雅。等后期给选项让用户决定行为再去做:https://github.com/nodejs/node/issues/21825
166174
// return this.service.start('FFBoxService.exe', [], { detached: true, stdio: 'ignore', windowsHide: true, shell: false });
167-
return this.service.start(getOs() === 'Windows' ? 'FFBoxService.exe' : path.join(process.resourcesPath, 'FFBoxService')).then(() => osBridge.sendLoadStatus('service'));
175+
let servicePath = '';
176+
if (getOs() === 'Windows') {
177+
servicePath = 'FFBoxService.exe';
178+
} else if (getOs() === 'MacOS') {
179+
servicePath = path.join(process.resourcesPath, 'FFBoxService');
180+
} else if (getOs() === 'Linux') {
181+
// this.mainWindow.webContents.send('debugMessage', 'service 路径', process.execPath, __dirname, __filename, process.cwd(), path.join(process.execPath, '../FFBoxService'));
182+
await fs.access('./FFBoxService', fs.constants.X_OK).then((result) => {
183+
servicePath = './FFBoxService'; // 通过终端直接执行
184+
}).catch(() => {});
185+
await fs.access(path.join(process.cwd(), 'FFBoxService'), fs.constants.X_OK).then((result) => {
186+
servicePath = path.join(process.cwd(), 'FFBoxService'); // 无沙箱双击执行、通过终端直接执行
187+
}).catch(() => {});
188+
await fs.access(path.join(process.execPath, '../FFBoxService'), fs.constants.X_OK).then((result) => {
189+
servicePath = path.join(process.execPath, '../FFBoxService'); // AppImage 双击执行(/tmp 目录)、deb 安装后双击执行(/opt/FFBox/)
190+
});
191+
}
192+
// this.mainWindow.webContents.send('debugMessage', '选出路径', servicePath);
193+
return new Promise((resolve, reject) => {
194+
this.service.start(servicePath).then(() => {
195+
// 需要加一点延迟才报告成功,主要是因为 service 启动 server 需要一定时间,待 server 启动好之后才让 renderer 去连接
196+
// 在 Windows 中可能不需要加这个延时,但是在 macOS 和 Linux 上似乎都是需要的
197+
// 另外,调试过程中发现,如果尝试使用 debugMessage 把调试消息发送给 renderer,当程序忙的时候 renderer 并不一定会按实际顺序去显示,因此需要适当增加延时以验证 Promise 正常工作
198+
// 150ms 延迟在 Linux 上很可能不够。但目前的设计是在 renderer 那边自动重试,主进程尽快报告完成。
199+
setTimeout(() => {
200+
osBridge.sendLoadStatus('service');
201+
resolve(undefined);
202+
}, 150);
203+
}).catch(() => {
204+
reject();
205+
});
206+
});
168207
}
169208

170209
mountIpcEvents(): void {
@@ -251,9 +290,7 @@ class ElectronApp {
251290
});
252291

253292
// 启动一个 ffboxService,这个 ffboxService 目前钦定监听 localhost:33269,而 serviceBridge 会连接此 service
254-
ipcMain.on('startService', () => {
255-
this.createService();
256-
});
293+
ipcMain.handle('startService', () => this.createService());
257294

258295
// osBridge 系列
259296
ipcMain.on('triggerSystemMenu', () => osBridge.triggerSystemMenu());

src/renderer/src/App.vue

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,19 @@ import nodeBridge from './bridges/nodeBridge';
1414
1515
const appStore = useAppStore();
1616
17-
onMounted(() => {
17+
onMounted(async () => {
1818
// 初始化本地服务器
19-
if (location.href.startsWith('file')) {
20-
nodeBridge.startService();
21-
}
22-
const localServerId = appStore.addServer();
19+
const firstServerId = appStore.addServer();
2320
if (nodeBridge.env === 'electron') {
24-
appStore.initializeServer(localServerId, 'localhost', 33269);
21+
// electron 环境自动连接 localhost
22+
if (location.href.startsWith('file')) {
23+
// 打包后的 electron 环境首先启动 service 再连接
24+
nodeBridge.startService().finally(() => {
25+
appStore.initializeServer(firstServerId, 'localhost', 33269, 3); // 4 次连接机会
26+
});
27+
} else {
28+
appStore.initializeServer(firstServerId, 'localhost', 33269);
29+
}
2530
}
2631
2732
// 挂载退出确认
@@ -72,9 +77,9 @@ onMounted(() => {
7277
gp.audio = await nodeBridge.localStorage.get('audio') || gp.audio;
7378
gp.output = await nodeBridge.localStorage.get('output') || gp.output;
7479
75-
appStore.frontendSettings = await nodeBridge.localStorage.get('frontendSettings') || appStore.frontendSettings;
76-
appStore.applyFrontendSettings(false);
7780
}
81+
appStore.frontendSettings = await nodeBridge.localStorage.get('frontendSettings') || appStore.frontendSettings;
82+
appStore.applyFrontendSettings(false);
7883
})();
7984
8085
setTimeout(() => {

src/renderer/src/bridges/nodeBridge.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,8 @@ export default {
206206
window.jsb?.ipcRenderer?.send('openDevTools');
207207
},
208208

209-
startService(): void {
210-
window.jsb?.ipcRenderer?.send('startService');
209+
startService(): Promise<void> {
210+
return window.jsb?.ipcRenderer?.invoke('startService');
211211
},
212212

213213
setBlurBehindWindow(on = true): void {

src/renderer/src/containers/MainFrame/MainArea/ListArea.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,9 @@ const onDrop = (event: DragEvent) => { // 此函数触发四次 taskList update
147147
<div style="height: 12px" />
148148
<p>1. 在<a @click="handleDownloadFFmpegClicked"> FFmpeg 官网</a>下载适用于 <span>{{ appStore.currentServer.data.os || '对应操作系统' }}</span> 的程序</p>
149149
<p v-if="['Windows', 'unknown'].includes(appStore.currentServer.data.os)">  2.1. 选择一:将 ffmpeg 可执行文件所在路径放至于环境变量中</p>
150-
<p v-if="['Windows', 'unknown'].includes(appStore.currentServer.data.os)">  2.2. 选择二:将 ffmpeg 可执行文件放入 FFBox 可执行程序路径</p>
151-
<p v-if="appStore.currentServer.data.os === 'MacOS'">  2.1. 选择一:将 ffmpeg 可执行文件放入 /usr/local/bin</p>
152-
<p v-if="appStore.currentServer.data.os === 'MacOS'">  2.2. 选择二:将 ffmpeg 可执行文件放入 {{ appStore.currentServer.data.isSandboxed ? 'FFBox.app/Contents/Resources' : 'FFBoxService 可执行程序路径' }}</p>
150+
<p v-if="['MacOS', 'Linux'].includes(appStore.currentServer.data.os)">  2.1. 选择一:将 ffmpeg 可执行文件放入 /usr/local/bin</p>
151+
<p v-if="['Windows', 'Linux', 'unknown'].includes(appStore.currentServer.data.os)">  2.2. 选择二:将 ffmpeg 可执行文件放入 FFBox 可执行程序相同目录</p>
152+
<p v-if="appStore.currentServer.data.os === 'MacOS'">  2.2. 选择二:将 ffmpeg 可执行文件放入 {{ appStore.currentServer.data.isSandboxed ? 'FFBox.app/Contents/Resources' : 'FFBoxService 可执行程序相同目录' }}</p>
153153
<div style="height: 4px" />
154154
<p>完成以上操作后,重启{{ appStore.currentServer.entity.ip === 'localhost' ? '本软件' : ' FFBoxService ' }}即可开始使用</p>
155155
<div style="height: 12px" />

0 commit comments

Comments
 (0)