Skip to content

Commit 5547922

Browse files
committed
激活系统明确区分前后端,打赏中心进度条新增玻璃质感组件;统一文件时间计算函数;实现“媒体信息”面板;小幅降低 AI 图标渲染占用;禁用浏览器下的下载面板;更新协议
1 parent 788adab commit 5547922

File tree

28 files changed

+658
-208
lines changed

28 files changed

+658
-208
lines changed

LICENSE

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ FFBox 源代码和二进制文件均免费向所有个体开放。
88

99
如果使用者存在或曾经存在以下行为致他人受到未明确作出谅解的伤害,则不得使用 FFBox,亦不得通过他人协助或协助他人的方式使用 FFBox(但若您仅开启了 FFBox 服务,而服务使用者与您并不存在交往,则不受此条款限制):
1010

11+
- 无中生有编造他人言论或想法或客观事实
12+
- 拒绝他人理性参与讨论、坚持使用主观想法代表客观世界
1113
- 同时与多个对象维持『任何一方不知情或不同意』的『恋爱关系或模糊的亲密关系』
1214
- 通过隐瞒、欺骗他人等方式满足自身情感需求
1315
- 操纵或滥用他人感情(如过度控制或索取)
1416
- 对他人进行情感上的信任背离(含背叛、空洞承诺等)行为
15-
- 拒绝他人理性参与讨论、坚持使用主观想法代表客观世界
16-
- 无中生有编造他人言论或想法或客观事实
1717

1818
FFBox 作者将保留对本许可与使用条款的解释权及随时修改的权利。
1919

20-
*2025-05-01*
20+
*2025-11-19*

WindowsInstaller/FFBox.iss

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
33

44
#define MyAppName "FFBox"
5-
#define MyAppVersion "5.0"
5+
#define MyAppVersion "5.1"
66
#define MyAppPublisher "滔滔清风"
77
#define MyAppURL "http://ttqf.tech"
88
; #define MyCopyright "版权所有"
@@ -11,7 +11,7 @@
1111

1212
; 安装包输出文件夹
1313
#define MySetupOutDir "./output"
14-
#define MySetupOutBaseFilename "Windows_x86-64_FFBox_5.0"
14+
#define MySetupOutBaseFilename "Windows_x86-64_FFBox_5.1"
1515

1616
; 安装包所用的资源文件夹
1717
#define MyResDir "./res"
@@ -21,7 +21,7 @@
2121
; 点击 license 打开的网页连接
2222
#define MyAppLkLicenseURL 'http://ffbox.ttqf.tech/LICENSE'
2323
; 安装目录至少需要的空间
24-
#define MyAppNeedSpaceByte 300000000
24+
#define MyAppNeedSpaceByte 322000000
2525
; 外部程序调用本安装程序时,会向外部传安装进度的 window api Message ID
2626
#define WM_MY_INSTALL_PROGRESS 6364
2727

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "FFBox",
3-
"version": "5.1.0-alpha",
3+
"version": "5.1.0",
44
"description": "",
55
"main": "app/main/index.cjs",
66
"scripts": {

src/backend/FFBoxService.ts

Lines changed: 9 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { genTaskOutputFiles, getFFmpegParaArray } from '@common/getFFmpegParaArr
1010
import { defaultParams } from '@common/defaultParams';
1111
import localConfig from '@common/localConfig';
1212
import { parseFFmpegCodecsToCodecsList, parseFFmpegMuDeMuxersToList } from '@common/params/parser';
13-
import { getInitialServiceTask, convertAnyTaskToTask, TypedEventEmitter, replaceOutputParams, randomString, getOutputDuration, parseTimeString } from '@common/utils';
13+
import { getInitialServiceTask, convertAnyTaskToTask, TypedEventEmitter, replaceOutputParams, randomString, getOutputDuration, parseTimeString, getOutputFileTime } from '@common/utils';
1414
import { getMachineId, log } from './utils';
1515
import { FFmpeg } from './FFmpegInvoke';
1616
import UIBridge from './uiBridge';
@@ -33,7 +33,7 @@ export class FFBoxService extends (EventEmitter as new () => TypedEventEmitter<F
3333
private globalTask: ServiceTask;
3434
public notifications: Notification[] = [];
3535
private latestNotificationId = 0;
36-
private functionLevel = 20;
36+
public functionLevel = 20;
3737
public machineId: string;
3838
// 设置部分
3939
private maxThreads = 1;
@@ -690,59 +690,22 @@ export class FFBoxService extends (EventEmitter as new () => TypedEventEmitter<F
690690
if (mux.keepFileTime) {
691691
try {
692692
// 如果输入文件不可读取,或者 utimes 失败,或者 FFBox 无法正确计算文件时间,都会产生 hasTimeError
693-
const originalFilePath = task.after.input.files[0]?.filePath;
694-
// await fsPromise.access(originalFilePath, fs.constants.R_OK);
695-
const { accessTime, createTime, modifyTime } = task.before;
696-
// const { atime, birthtime, mtime } = fs.statSync(originalFilePath);
697-
log.info(`[任务 ${id}] 将按照首个输入文件的时间修改任务时间。原创建时间 ${new Date(createTime).toISOString()};原修改时间 ${new Date(modifyTime).toISOString()};原访问时间 ${new Date(accessTime).toISOString()}。`);
698-
if (mux.keepFileTime === 'original') {
699-
// 原样复制文件时间。输出文件的创建时间、修改时间、访问时间将从输入文件的时间原样复制
693+
const { accessTime, createTime, modifyTime, ok } = getOutputFileTime(task, i);
694+
if (ok) {
695+
log.info(`[任务 ${id}] 将按照首个输入文件的时间修改任务时间。新创建时间 ${new Date(createTime).toISOString()};新修改时间 ${new Date(modifyTime).toISOString()};新访问时间 ${new Date(accessTime).toISOString()}。`);
700696
await utimes(outputFilePath, { btime: createTime, mtime: modifyTime, atime: accessTime });
701697
} else {
702-
const startTime1 = parseTimeString(task.after.input.files[0].begin);
703-
const startTime2 = parseTimeString(mux.begin);
704-
const startTime = ((startTime1 === -1 ? 0 : startTime1) + (startTime2 === -1 ? 0 : startTime2)) * 1000;
705-
const duration = (getOutputDuration(task) || 0) * 1000; // 假设 getOutputDuration 可接收 index
706-
if (mux.keepFileTime === 'autoShift') {
707-
// 复制修正后的文件时间(依创建时间)。输出文件的创建时间、修改时间将以创建时间为基准,按照剪裁位置自动调整后进行修改
708-
const newCreateTime = createTime + startTime;
709-
const newModifyTime = createTime + startTime + duration;
710-
await utimes(outputFilePath, { btime: newCreateTime, mtime: newModifyTime, atime: accessTime });
711-
} else if (mux.keepFileTime === 'fixCTbyMTandShift' && task.before.duration > 0) {
712-
// 复制修正后的文件时间(依修改时间)。输出文件的创建时间、修改时间将以修改时间为基准,按照剪裁位置自动调整后进行修改,用于修复拷贝后创建时间丢失的问题
713-
const newCreateTime = modifyTime - task.before.duration * 1000 + startTime;
714-
const newModifyTime = modifyTime - task.before.duration * 1000 + startTime + duration;
715-
await utimes(outputFilePath, { btime: newCreateTime, mtime: newModifyTime, atime: accessTime });
716-
} else if (mux.keepFileTime === 'fixByFilenameAndShift') {
717-
// 根据文件名修正新文件时间。用于修复文件时间丢失的问题,将通过文件名作为创建时间,根据剪裁位置自动调整后进行修改
718-
const regExp1 = /(\d\d\d\d).?([01]\d).?([0123]\d).?([012]\d).?([0-5]\d).?([0-5]\d)?/;
719-
const regExp2 = /(\d\d\d\d) ?? ?([01]?\d) ?? ?([0123]?\d) ?? ?([012]?\d) ?? ?([0-5]?\d) ?? ?([0-5]?\d)? ?? ?/;
720-
const r = originalFilePath.match(regExp1) || originalFilePath.match(regExp2);
721-
if (r) {
722-
const oldCreateTime = new Date(`${r[1]}-${r[2]}-${r[3]} ${r[4]}:${r[5]}:${r[6] || 0}`);
723-
if (!isNaN(oldCreateTime.getTime())) {
724-
const newCreateTime = oldCreateTime.getTime() + startTime;
725-
const newModifyTime = oldCreateTime.getTime() + startTime + duration;
726-
await utimes(outputFilePath, { btime: newCreateTime, mtime: newModifyTime, atime: accessTime });
727-
} else {
728-
hasTimeError.push(outputFilePath);
729-
}
730-
} else {
731-
hasTimeError.push(outputFilePath);
732-
}
733-
} else {
734-
hasTimeError.push(outputFilePath);
735-
}
698+
hasTimeError.push(i + 1 + '');
736699
}
737700
} catch (error) {
738-
hasTimeError.push(task.outputFiles[i]);
701+
hasTimeError.push(i + 1 + '');
739702
}
740703
}
741704
}
742705
task.status = TaskStatus.finished;
743706
task.progressLog.elapsed = new Date().getTime() / 1000 - task.progressLog.lastStarted;
744707
if (hasTimeError.length) {
745-
this.setNotification(id, '任务「' + task.taskName + '」已转码完成,但修改文件时间失败。请检查文件权限。', NotificationLevel.warning);
708+
this.setNotification(id, `任务「${task.taskName}」已转码完成,但修改第 ${hasTimeError.join(' ')} 个文件时间失败。`, NotificationLevel.warning);
746709
} else {
747710
this.setNotification(id, `任务「${task.taskName}」已转码完成`, NotificationLevel.ok);
748711
}
@@ -1120,7 +1083,7 @@ export class FFBoxService extends (EventEmitter as new () => TypedEventEmitter<F
11201083
this.notifications[notificationId] = notification;
11211084
}
11221085

1123-
public activate(activationCode: string): boolean {
1086+
private activate(activationCode: string): boolean {
11241087
const fixedCode = 'd324c697ebfc42b7';
11251088
const key = this.machineId + fixedCode;
11261089
const decrypted = CryptoJS.AES.decrypt(activationCode, key);

src/backend/uiBridge.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Http from 'http';
22
import WebSocket, { WebSocketServer } from 'ws';
3+
import CryptoJS from 'crypto-js';
34
import Koa from 'koa';
45
import Router from 'koa-router';
56
import { koaBody } from 'koa-body';
@@ -273,7 +274,8 @@ function getRouter(): Router {
273274
const result = {
274275
os: getOs(),
275276
isSandboxed: process.cwd() === '/', // macOS 中,直接双击运行服务(无论是否在 app 内)会得到用户目录,在终端运行会得到终端当前目录,通过 FFBox 调用会得到 '/'
276-
machineId: ffboxService.machineId,
277+
machineId: ffboxService.machineId,
278+
functionLevel: ffboxService.functionLevel,
277279
};
278280
ctx.response.status = 200;
279281
ctx.response.body = result;
@@ -290,7 +292,7 @@ function getRouter(): Router {
290292
const body = ctx.request.body;
291293
if (body.sessionId) {
292294
const users: { username: string; passkey: string; maxFunctionLevel: number }[]
293-
= (await localConfig.get('service.users') as any) || [{ username : "", passkey: "", maxFunctionLevel: 100 }];
295+
= (await localConfig.get('userInfo.users') as any) || [{ username : "", passkey: "", maxFunctionLevel: 100 }];
294296
const client = clients.get(body.sessionId);
295297
const user = users.find((user) => user.username === body.username);
296298
if (client && user) {
@@ -407,6 +409,29 @@ function getRouter(): Router {
407409
ctx.response.body = result;
408410
});
409411

412+
// 激活
413+
router.post('/activation', async function (ctx) {
414+
if (!ctx.request.body?.userInput) {
415+
// 非法请求
416+
ctx.response.status = 400;
417+
return;
418+
}
419+
const userInput = ctx.request.body.userInput;
420+
const fixedCode = 'd324c697ebfc42b7';
421+
const key = ffboxService.machineId + fixedCode;
422+
const decrypted = CryptoJS.AES.decrypt(userInput, key);
423+
const activationResult = CryptoJS.enc.Utf8.stringify(decrypted);
424+
if (parseInt(activationResult).toString() === activationResult) {
425+
ffboxService.functionLevel = parseInt(activationResult);
426+
localConfig.set('userInfo.activationCode', userInput);
427+
const returnEncrypted = CryptoJS.AES.encrypt(activationResult, fixedCode).toString();
428+
ctx.response.status = 200;
429+
ctx.response.body = returnEncrypted;
430+
} else {
431+
ctx.response.status = 200;
432+
}
433+
});
434+
410435
// 获取缓存
411436
router.get('/cache', async function (ctx) {
412437
const result = await ffboxService!.getCacheInfo(false);

src/backend/utils.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ export function getOs() {
1717

1818
export function getMachineId() {
1919
const execPath = {
20-
darwin: 'ioreg -rd1 -c IOPlatformExpertDevice',
21-
win32: `%windir%/System32/REG.exe ` +
22-
'QUERY HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography ' +
23-
'/v MachineGuid',
24-
linux: '( cat /var/lib/dbus/machine-id /etc/machine-id 2> /dev/null || hostname ) | head -n 1 || :',
25-
freebsd: 'kenv -q smbios.system.uuid || sysctl -n kern.hostuuid',
26-
} as any;
20+
darwin: 'ioreg -rd1 -c IOPlatformExpertDevice',
21+
win32: `%windir%/System32/REG.exe ` +
22+
'QUERY HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography ' +
23+
'/v MachineGuid',
24+
linux: '( cat /var/lib/dbus/machine-id /etc/machine-id 2> /dev/null || hostname ) | head -n 1 || :',
25+
freebsd: 'kenv -q smbios.system.uuid || sysctl -n kern.hostuuid',
26+
} as any;
2727
try {
2828
const execResult = execSync(execPath[process.platform]).toString();
2929
function extract (result: string) {

src/common/constants.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
export const version = (() => {
2-
let ret = '5.1-alpha';
2+
let ret = '5.1';
33
if (!buildInfo) {
44
ret += ' *'
55
} else if (buildInfo.isDev) {
66
ret += ` ${buildInfo.gitCommit}`
77
}
88
return ret;
99
})();
10-
export const buildNumber = 17;
11-
// 1.0 1.1 2.0 2.1 2.2 2.3 2.4 2.5 2.6 3.0 4.0 4.1 4.2 4.3 4.4 4.5 5.0
10+
export const buildNumber = 18;
11+
// 1.0 1.1 2.0 2.1 2.2 2.3 2.4 2.5 2.6 3.0 4.0 4.1 4.2 4.3 4.4 4.5 5.0 5.1

src/common/types.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ export interface FFBoxServiceInterface {
5252
queuePause(): void;
5353
deleteNotification(taskId: number, index: number): void;
5454
setParameters(ids: number[], params: OutputParams[]): void;
55-
activate(activationCode: string): boolean | void;
5655
trailLimit_stopTranscoding(id: number, reason: 'media' | 'working', byFrontend?: boolean): void;
5756
}
5857

src/common/utils.ts

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
/* eslint-disable no-fallthrough */
2-
// #region 格式转换区
3-
42
import { OutputParams, ServiceTask, Task, TaskStatus } from '@common/types';
53
import { UITask } from '@renderer/types';
64
import { deleteNode } from './params/filter';
75

6+
// #region 格式转换区
7+
88
/**
99
* 传入秒数,返回 --:--:--.--
1010
*/
@@ -422,6 +422,56 @@ export function getOutputDuration(task: Task): number {
422422
return duration;
423423
}
424424

425+
/**
426+
* 根据任务配置返回新文件时间(仅计算,不进行文件操作)
427+
*/
428+
export function getOutputFileTime(task: Task, index: number) {
429+
let ok = true; // 仅代表计算是否成功,不代表是否已修改时间
430+
const output = task.after.outputs[index];
431+
const mux = output.mux;
432+
let { accessTime, createTime, modifyTime } = task.before;
433+
434+
if (mux.keepFileTime === 'original') {
435+
} else {
436+
const startTime1 = parseTimeString(task.after.input.files[0].begin);
437+
const startTime2 = parseTimeString(mux.begin);
438+
const startTime = ((startTime1 === -1 ? 0 : startTime1) + (startTime2 === -1 ? 0 : startTime2)) * 1000;
439+
const duration = (getOutputDuration(task) || 0) * 1000; // 假设 getOutputDuration 可接收 index
440+
if (mux.keepFileTime === 'autoShift') {
441+
// 复制修正后的文件时间(依创建时间)。输出文件的创建时间、修改时间将以创建时间为基准,按照剪裁位置自动调整后进行修改
442+
const newCreateTime = createTime + startTime;
443+
const newModifyTime = createTime + startTime + duration;
444+
[createTime, modifyTime] = [newCreateTime, newModifyTime];
445+
} else if (mux.keepFileTime === 'fixCTbyMTandShift' && task.before.duration > 0) {
446+
// 复制修正后的文件时间(依修改时间)。输出文件的创建时间、修改时间将以修改时间为基准,按照剪裁位置自动调整后进行修改,用于修复拷贝后创建时间丢失的问题
447+
const newCreateTime = modifyTime - task.before.duration * 1000 + startTime;
448+
const newModifyTime = modifyTime - task.before.duration * 1000 + startTime + duration;
449+
[createTime, modifyTime] = [newCreateTime, newModifyTime];
450+
} else if (mux.keepFileTime === 'fixByFilenameAndShift') {
451+
const originalFilePath = task.after.input.files[0]?.filePath;
452+
// 根据文件名修正新文件时间。用于修复文件时间丢失的问题,将通过文件名作为创建时间,根据剪裁位置自动调整后进行修改
453+
const regExp1 = /(\d\d\d\d).?([01]\d).?([0123]\d).?([012]\d).?([0-5]\d).?([0-5]\d)?/;
454+
const regExp2 = /(\d\d\d\d) ?? ?([01]?\d) ?? ?([0123]?\d) ?? ?([012]?\d) ?? ?([0-5]?\d) ?? ?([0-5]?\d)? ?? ?/;
455+
const r = originalFilePath.match(regExp1) || originalFilePath.match(regExp2);
456+
if (r) {
457+
const oldCreateTime = new Date(`${r[1]}-${r[2]}-${r[3]} ${r[4]}:${r[5]}:${r[6] || 0}`);
458+
if (!isNaN(oldCreateTime.getTime())) {
459+
const newCreateTime = oldCreateTime.getTime() + startTime;
460+
const newModifyTime = oldCreateTime.getTime() + startTime + duration;
461+
[createTime, modifyTime] = [newCreateTime, newModifyTime];
462+
} else {
463+
ok = false;
464+
}
465+
} else {
466+
ok = false;
467+
}
468+
} else {
469+
ok = false;
470+
}
471+
}
472+
return { accessTime, createTime, modifyTime, ok };
473+
}
474+
425475
/**
426476
* 任务信息在进行网络传送前调用此函数,过滤掉仅存在于 ServiceTask | UITask 的属性
427477
*/

src/main/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import parsePath from 'parse-path';
66
import CryptoJS from 'crypto-js';
77
import { utimes } from 'utimes';
88
import fs from 'fs/promises';
9+
import { getMachineId } from './utils';
910
import ProcessInstance from '@common/processInstance';
1011
import localConfig from '@common/localConfig';
1112
import { convertFFBoxMenuToElectronMenuTemplate, getOs } from './utils';
@@ -475,7 +476,7 @@ class ElectronApp {
475476
}
476477
fs.readFile(licensePath, { encoding: 'utf-8' }).then((data) => {
477478
const cipherText = CryptoJS.SHA1(data);
478-
if (['a4252c34196e5b0c934402cb7a94bb7588625e22', 'a8a902aca93241689c6df8b6e7f92bdb2ae05c66'].includes(cipherText.toString())) {
479+
if (['03a87d14cad233d7f57d7e3642bc8f9665df48ed', 'ae08d78587d0e2e1584981291938abdf936ca3a6'].includes(cipherText.toString())) {
479480
// 两个校验码,适配 LF 换行符和 CRLF 换行符
480481
resolve(data);
481482
} else {
@@ -487,6 +488,11 @@ class ElectronApp {
487488
});
488489
});
489490

491+
// 获取机器码
492+
ipcMain.handle('getMachineId', async (event) => {
493+
return getMachineId();
494+
});
495+
490496
// 代为请求
491497
ipcMain.handle('request', async (event, url: string, options?: { method?: string; body?: any; headers?: Record<string, string> }) => {
492498
const requestOptions: RequestInit = {

0 commit comments

Comments
 (0)