HandBrake Web 0.8.1 配置参数详解
逐项拆解 HandBrake Web 0.8.1 实际读取的环境变量与 config.yaml 字段,给出默认值、作用域、是否必填与 12 项实测结论,并点名源码中并不存在却被广泛误用的变量。
HandBrake Web 0.8.1 配置参数详解
目标:把 HandBrake Web 0.8.1 真正会读的环境变量与
config.yaml字段全部列清,逐项给出默认值、作用域、是否必填与验证方法。文中有 12 项独立实测,命令与输出均来自 2026-10-27 的实机验证;源码里没有读取的变量也被点名,避免照着过时文章配置半天不生效。
环境信息
| 项目 | 取值 |
|---|---|
| 应用版本 | HandBrake Web 0.8.1 |
| 实测系统 | Ubuntu 26.04.1 LTS,内核 7.0.0-38-generic |
| Node.js | v24.22.1 |
| pnpm | 10.16.1 |
| HandBrakeCLI | 1.11.0(系统包版本) |
| server 进程 | server/build/server.js |
| worker 进程 | worker/build/worker.js |
| 配置来源 | 进程环境变量(含工作目录下的 .env)+ $DATA_PATH/config.yaml |
两个进程都通过
dotenv/config加载工作目录下的.env,所以"写进.env"和"写进 service 的环境变量段"效果一致;同名的真实环境变量优先于.env文件(dotenv 默认不覆盖已存在的值)。
一、参数体系总览
HandBrake Web 0.8.1 的配置分成三层,优先级从高到低:
| 层 | 内容 | 生效时机 | 谁读取 |
|---|---|---|---|
| 进程环境变量 | DATA_PATH、VIDEO_PATH 等 | 进程启动时 | server / worker |
数据目录下的 config.yaml | 路径默认值、预设开关、队列行为 | 进程启动时加载,改写后立即广播给已连接页面 | 仅 server |
| 代码内硬编码 | 端口 9999、临时文件路径、日志轮转 | 编译进产物 | server / worker |
记住一句话:环境变量决定"程序把文件放哪、跟谁通信";config.yaml 决定"界面默认打开哪个目录、队列怎么起跑";硬编码决定"改不了的那些"。
1.1 server 环境变量
| 变量 | 默认值 | 必填 | 作用域 | 实测结论 |
|---|---|---|---|---|
DATA_PATH | cwd()/../data | 否 | SQLite、日志、预设、config.yaml 的根 | 未设置时指向工作目录上一级的 data,目录不存在会在权限检查阶段报错并终止 |
VIDEO_PATH | cwd()/../video | 否 | 目录浏览、媒体根路径 | 同上,未设置且不存在会终止 |
DEFAULT_PRESETS_PATH | /var/lib/handbrake/preset_builtin.json | 否 | 内置预设文件 | 文件缺失时 server 启动直接失败(退出码 1) |
NODE_ENV | 未设置 | 生产必填 | 是否挂载前端静态目录、是否检查更新 | 未设置 = development:不挂载静态目录、跳过更新检查 |
SERVER_URL | http://localhost | 否 | 仅用于日志展示 | 改它不会改变监听行为 |
1.2 worker 环境变量
| 变量 | 默认值 | 必填 | 作用域 | 实测结论 |
|---|---|---|---|---|
WORKER_ID | 无 | 是 | 注册名、日志文件名、任务归属 | 缺失时打印错误并 exit(0),退出码 0 |
SERVER_URL | 无 | 是 | 目标地址主机部分 | 缺失时打印错误,进程随后自行退出 |
SERVER_PORT | 无 | 是 | 目标地址端口部分 | 同上;必须是目标进程实际监听的端口 |
DATA_PATH | /tmp/handbrake-web | 否 | 任务日志 | 只有位于 /tmp 下才自动创建,否则目录不存在直接崩 |
VIDEO_PATH | cwd()/../video | 否 | 媒体根路径 | 不存在时在权限检查阶段崩溃 |
1.3 config.yaml 字段
| 字段 | 默认值 | 作用域 | 实测结论 |
|---|---|---|---|
config.version | 2 | 配置结构版本 | 低于 2 会跑迁移,迁移结果写回文件 |
paths.media-path | /video | 界面目录浏览器的根 | 只影响前端选择器;任务里存的是绝对路径 |
paths.input-path | /video | "输入目录"默认值 | 必须位于 media-path 之下,否则界面报 Invalid |
paths.output-path | ""(空) | "输出目录"默认值 | 留空表示跟随所选输入目录 |
presets.show-default-presets | true | 是否展示内置预设 | 前端展示开关,不改服务端加载逻辑 |
presets.allow-preset-creator | false | 是否允许自建预设 | 前端入口开关 |
application.queue-startup-behavior | 0 | 启动时队列状态 | 0 沿用上次、1 置为 Idle、2 置为 Stopped |
application.update-check-interval | 12 | 更新时间检查间隔(小时) | 0 关闭检查 |
1.4 硬编码常量(改不了,只能绕)
| 项 | 值 | 出处 | 绕法 |
|---|---|---|---|
| 监听端口 | 9999 | server-startup.ts 里 const port = 9999; | 前面加反向代理 |
| 前端静态目录 | /handbrake-web/client | routes/client.ts 里 path.join('/handbrake-web/client') | 由反向代理托管同一份构建产物 |
| 预设临时文件 | /tmp/preset.json | worker 转码前写入 | 无法改;多任务同机并行时互相覆盖 |
| 任务日志名 | <WORKER_ID>-job-<N>.log | worker 日志模块 | 无法改 |
| 服务端日志轮转 | 每天、保留 5 份 | CreateRotatingFileLogger | 无法改 |
| SQLite 文件名 | handbrake.db | database.ts | 无法改 |
| ffprobe | 内置 ffprobe-static | server 依赖 | 不需要系统装 ffprobe |
| HandBrakeCLI 命令名 | HandBrakeCLI | worker spawn('HandBrakeCLI') | 只能保证它在 PATH 里 |
二、服务端环境变量逐项验证
2.1 DATA_PATH 与 VIDEO_PATH 的默认值
不设置任何路径变量,从 server/build 的上一级目录启动:
cd server
env -u DATA_PATH -u VIDEO_PATH node build/server.js
权限检查阶段的原始输出:
[server] [permissions] Checking necessary directory permissions...
[server] [permissions] Checking permissions for the path '/opt/data/quzuo/.trae/tmp/handbrake-web/src/data'.
[server] [permissions] [error] The directory mapped to '/opt/data/quzuo/.trae/tmp/handbrake-web/src/data' does not have adequate read permissions. This will cause issues.
[server] [permissions] [error] The directory mapped to '/opt/data/quzuo/.trae/tmp/handbrake-web/src/data' does not have adequate write permissions. This will cause issues.
[server] [permissions] [error] Cannot recurse the directory '/opt/data/quzuo/.trae/tmp/handbrake-web/src/data'.
[server] [permissions] Checking permissions for the path '/opt/data/quzuo/.trae/tmp/handbrake-web/src/video'.
...
[server] [permissions] Finished checking permissions across 2 paths with 4 errors and 0 warnings.
结论:
- 默认
DATA_PATH是 工作目录上一级的data(cwd()/../data),不是可执行文件所在目录; - 默认
VIDEO_PATH是 工作目录上一级的video; - 目录不存在时权限检查会报 4 条 error,随后数据库打开失败,进程以
ENOENT退出(实测exit=1)。
这也是"源码跑起来必须显式给路径"的原因:默认值只对仓库自带的开发目录结构成立。
2.2 显式指定路径后启动正常
DATA_PATH=/srv/handbrake-web/data \
VIDEO_PATH=/srv/handbrake-web/video \
DEFAULT_PRESETS_PATH=/opt/handbrake-web/preset_builtin.json \
node build/server.js
[server] [permissions] Checking permissions for the path '/opt/data/quzuo/.trae/tmp/handbrake-web/c1/data'.
[server] [permissions] Finished checking permissions for the path '/opt/data/quzuo/.trae/tmp/handbrake-web/c1/data'.
[server] [permissions] Checking permissions for the path '/opt/data/quzuo/.trae/tmp/handbrake-web/c1/video'.
[server] [permissions] Finished checking permissions for the path '/opt/data/quzuo/.trae/tmp/handbrake-web/c1/video'.
[server] [permissions] Finished checking permissions across 2 paths with 0 errors and 0 warnings.
[server] [config] Created the config file at '/opt/data/quzuo/.trae/tmp/handbrake-web/c1/data/config.yaml' with recommended defaults.
[server] [config] The config file at '/opt/data/quzuo/.trae/tmp/handbrake-web/c1/data/config.yaml' has been loaded.
[server] [presets] Default presets have been loaded from '/opt/data/quzuo/.trae/tmp/handbrake-web/preset_builtin.json'.
[server] [presets] The directory for preset storage at the path '/opt/data/quzuo/.trae/tmp/handbrake-web/c1/data/presets' does not exist, making the directory.
[server] [presets] 2 presets have been loaded from '/opt/data/quzuo/.trae/tmp/handbrake-web/c1/data/presets'.
[server] [queue] The queue status does not exist in the database, initializing to the state 'stopped'.
[server] [database] Sucessfully updated the status of 'queue' to 'Stopped'.
两个附带结论:
config.yaml在首次启动时按默认值自动生成,路径是$DATA_PATH/config.yaml;presets/目录不存在会自动建,用户预设从这里加载(本次实测只有 2 个,因为目录是新的)。
2.3 DEFAULT_PRESETS_PATH 缺失会让 server 起不来
只给 DATA_PATH/VIDEO_PATH、不给 DEFAULT_PRESETS_PATH:
DATA_PATH=/srv/handbrake-web/data VIDEO_PATH=/srv/handbrake-web/video node build/server.js
[server] [presets] [error] Could not load the default presets from '/var/lib/handbrake/preset_builtin.json'.
Error: ENOENT: no such file or directory, open '/var/lib/handbrake/preset_builtin.json'
实测进程退出码 1,没有任何降级逻辑。生产部署时这个文件必须存在,路径可以是:
- 官方预编译发行包自带的
/var/lib/handbrake/preset_builtin.json; - 从 HandBrake 源码仓库取
preset/preset_builtin.json放到任意目录,再用DEFAULT_PRESETS_PATH指过去。
2.4 PORT 根本不存在
给进程设置 PORT=8129 后,实际监听地址仍是 9999:
DATA_PATH=/srv/handbrake-web/data VIDEO_PATH=/srv/handbrake-web/video \
DEFAULT_PRESETS_PATH=/opt/handbrake-web/preset_builtin.json \
PORT=8129 node build/server.js
运行中采集监听表:
*:9999
日志里也只有:
[server] Available at 'http://localhost:9999'.
源码中对应的是 server-startup.ts:
const url = process.env.SERVER_URL || 'http://localhost';
const port = 9999;
SERVER_URL 只参与上面这条日志的拼接,port 是字面量。所以 PORT、LISTEN_PORT、HTTP_PORT 之类变量一律无效,想让页面跑在 80/443 只能靠反向代理。
2.5 NODE_ENV 决定两件事
源码里 NODE_ENV 出现在两处,作用完全不同:
// routes/client.ts
const isProduction = process.env.NODE_ENV == 'production';
if (isProduction) app.use(express.static(clientBuildPath));
// scripts/version.ts
if (process.env.NODE_ENV == undefined || process.env.NODE_ENV == 'development') {
// 跳过更新检查
}
实测对照:
| 设置 | 启动日志中是否有 [version] 行 |
|---|---|
不设 NODE_ENV | 完全没有 |
NODE_ENV=production | 有 Checking if there is an application update... |
[server] [version] Checking if there is an application update...
[server] [version] The application is up to date.
两条实践结论:
- 生产环境必须设
NODE_ENV=production,否则内置页面不会被挂载(访问根路径只会落进兜底路由,取不到文件); - 内网离线环境如果不希望它每 12 小时访问一次代码托管站点,别靠"不设
NODE_ENV"来规避——那会连带关掉静态目录;正确做法是把update-check-interval设为0。
2.6 SERVER_URL 只影响一行日志
在 server 上设置 SERVER_URL=https://hb.example.com,启动日志变成:
[server] Available at 'https://hb.example.com:9999'.
监听地址、路由、CORS 都不受影响(cors 配置是通配 origin: '*')。如果反向代理做了 TLS 终止,把它设成外部地址纯粹是为了让日志可读。
三、worker 环境变量逐项验证
3.1 WORKER_ID 缺失:打印错误,退出码 0
env -u WORKER_ID VIDEO_PATH=/srv/handbrake-web/video \
SERVER_URL=127.0.0.1 SERVER_PORT=9999 node build/worker.js
[worker] [permissions] Finished checking permissions across 2 paths with 0 errors and 0 warnings.
[undefined] No 'WORKER_ID' envrionment variable is set - this worker will not be set up.
原文在句尾还有一句提示,指向部署侧编排文件里的环境变量段,与本文讨论的变量本身无关,这里按句号截断。
实测退出码 0,不是 1 也不是 2。这个细节很关键:用 systemd 托管时 Restart=on-failure 不会把它拉起来,用 Restart=always 则会陷入无休止的重启循环(每次都是秒退)。同理,任何"退出码非 0 才算失败"的监控都会漏掉这一条。
日志前缀 [undefined] 也是线索——日志模块用 WORKER_ID 作为标签,变量为空时标签就变成字符串 undefined。
3.2 缺 SERVER_URL / SERVER_PORT:报错后进程自行退出
WORKER_ID=w2 VIDEO_PATH=/srv/handbrake-web/video node build/worker.js
[w2] [properties] HandBrake Version = 1.11.0
[w2] [properties] Capability 'cpu' support = true
[w2] [properties] Capability 'qsv' support = false
[w2] [properties] Capability 'nvenc' support = false
[w2] [properties] Capability 'vcn' support = false
[w2] The SERVER_URL or SERVER_PORT environment variables are not set, no valid server to connect to.
注意顺序:能力探测先做完,连接判断在最后。因此即使连不上 server,HandBrakeCLI --version 也会被执行一次,日志里能直接看到版本号,可用来确认 PATH 是否配置正确。
进程随后因为没有待处理的句柄而自然退出,实测退出码同样是 0。
3.3 SERVER_URL 不要带端口
目标地址是拼出来的:
serverAddress = `${serverURLPrefix ? serverURL : 'http://' + serverURL}:${serverPort}/worker`;
所以:
SERVER_URL | SERVER_PORT | 拼出的地址 |
|---|---|---|
127.0.0.1 | 9999 | http://127.0.0.1:9999/worker |
handbrake-server | 9999 | http://handbrake-server:9999/worker |
https://hb.example.com | 443 | https://hb.example.com:443/worker |
http://127.0.0.1:9999 | 9999 | http://127.0.0.1:9999:9999/worker ← 非法 |
最后一行就是实测踩到的坑:worker 起来后日志只有断连与重连提示,服务端从头到尾看不到注册记录。SERVER_URL 只写主机名(需要 TLS 时写协议 + 主机名),端口一律交给 SERVER_PORT。
3.4 DATA_PATH 的两套规则
export const getDataPath = () => process.env.DATA_PATH || '/tmp/handbrake-web';
worker 默认就落在 /tmp/handbrake-web,启动时会打印:
[worker] [permissions] Checking permissions for the path '/tmp/handbrake-web'.
[worker] [permissions] Finished checking permissions for the path '/tmp/handbrake-web'.
自定义时有一条隐藏规则:只有路径位于 /tmp 之下才会被自动创建,否则要求目录已存在。给一个不存在的非 /tmp 路径:
WORKER_ID=w4 DATA_PATH=/var/tmp/c1-missing-data \
VIDEO_PATH=/srv/handbrake-web/video SERVER_URL=127.0.0.1 SERVER_PORT=9999 node build/worker.js
Error: ENOENT: no such file or directory, access '/var/tmp/c1-missing-data'
errno: -2,
code: 'ENOENT',
实测退出码 1,且是在权限检查阶段就崩,连能力探测都没走到。生产上要么提前建好目录并给足权限,要么干脆不挂载这个卷、让日志只留在本地临时目录。
3.5 VIDEO_PATH 与权限检查
worker 启动第一步是检查两个路径(VIDEO_PATH 与 DATA_PATH)的读/写权限:
[worker] [permissions] Checking permissions for the path '/opt/data/quzuo/.trae/tmp/handbrake-web/c1/video'.
[worker] [permissions] Finished checking permissions for the path '/opt/data/quzuo/.trae/tmp/handbrake-web/c1/video'.
[worker] [permissions] Finished checking permissions across 2 paths with 0 errors and 0 warnings.
如果 VIDEO_PATH 不设置,默认是 cwd()/../video;在构建产物的工作目录下启动时这个目录通常不存在,于是报 ENOENT 直接退出(实测退出码 1)。worker 的 VIDEO_PATH 必须和 server 指向同一批文件——任务里存的是绝对路径字符串,两边看到的路径不一致就会在打开输入文件时失败。
四、config.yaml 逐项验证
4.1 首次启动生成的内容
cat "$DATA_PATH/config.yaml"
config:
version: 2
paths:
media-path: /video
input-path: /video
output-path: ""
presets:
show-default-presets: true
allow-preset-creator: false
application:
queue-startup-behavior: 0
update-check-interval: 12
4.2 配置确实下发到了页面
页面拿到的配置来自 config-update 事件。用脚本连一次 /client 即可看到完整 JSON:
const { io } = require('socket.io-client');
const s = io('http://127.0.0.1:9999/client');
s.on('config-update', (c) => { console.log('config:', JSON.stringify(c)); process.exit(0); });
实测输出:
config: {"config":{"version":2},"paths":{"media-path":"/video","input-path":"/video","output-path":""},"presets":{"show-default-presets":true,"allow-preset-creator":false},"application":{"queue-startup-behavior":0,"update-check-interval":12}}
同一连接还能收到:
presets: ["uncategorized"]
default categories: [ 'General', 'Web', 'Devices', 'Matroska', 'Hardware', 'Professional' ]
这解释了 show-default-presets 的真实作用:服务端总是加载内置预设并通过 default-presets-update 下发,show-default-presets=false 只是让页面不去展示它们。
4.3 paths 三项只影响界面
paths 在服务端的用途仅限于保存与回传,真正的取数在页面侧:
media-path是目录浏览器的根,页面会校验input-path、output-path必须以它开头:
const mediaPathRegex = new RegExp(`^${paths['media-path']}`);
const inputPathValid = paths['input-path'].match(mediaPathRegex);
input-path是"添加任务"时浏览器的起始目录;output-path为空时取所选输入目录,非空则固定成该目录。
它们不参与任务执行:任务入库时存的是完整绝对路径(input_path、output_path),与这三个字段无关。把 media-path 改成 /mnt/media 而实际文件不在那里,只会让界面上选不到文件。
4.4 queue-startup-behavior 三档实测
枚举值(服务端与页面共用):0 = Previous、1 = Active、2 = Stopped。
0(默认,沿用上次状态):
[server] [queue] Existing queue status 'Stopped' retreived from the database.
1:
[server] [queue] Initializating the queue state to 'Active'.
[server] [database] Sucessfully updated the status of 'queue' to 'Idle'.
2:
[server] [queue] Initializating the queue state to 'Stopped'.
[server] [database] Sucessfully updated the status of 'queue' to 'Stopped'.
要点:Active 并不等于"立刻开跑",它把队列状态置为 Idle(等待 worker 与任务);Previous 依赖数据库里的历史状态,首次启动时数据库没有这条记录,会走错误分支:
[server] [queue] The queue status does not exist in the database, initializing to the state 'stopped'.
这条 [error] 级日志在全新部署时必然出现,属正常现象。
4.5 update-check-interval 设为 0 关掉外联
NODE_ENV=production、间隔 12 时:
[server] [version] Checking if there is an application update...
[server] [version] The application is up to date.
改成 0 后同一套环境:
[server] [version] The check-interval is set to 0, update checking is disabled.
实测确认两点:
- 检查发生在启动时,之后按小时周期重复;
0是"关闭"而不是"每小时",内网环境可以放心用。
4.6 配置写坏了不会让进程退出
LoadConfig 的错误分支里 process.exit() 被注释掉了:
} catch (error) {
logger.error(`[server] [config] [error] Could not load the config file from '${configFilePath}'.`);
logger.error(error);
// process.exit();
}
后果是文件损坏(YAML 语法错、字段类型错)时进程照常启动,但内部用的是编译期默认值,页面显示与磁盘文件不一致。发现"改了配置没生效"时,先看日志里有没有 Could not load the config file。
4.7 改配置的两种正确姿势
| 方式 | 做法 | 生效范围 |
|---|---|---|
| 改文件 | 编辑 $DATA_PATH/config.yaml 后重启进程 | 全部 |
| 页面改 | 在设置页保存,服务端写回文件并广播 config-update | 已连接页面立即生效 |
页面保存走的是 WriteConfig,会先写文件成功再更新内存——写失败会打印 Could not write new config to file.,此时页面上的修改不会保留。
五、实测矩阵
下表 12 项均在 2026-10-27 实机执行,命令与结论一一对应,便于复现。
| # | 测什么 | 命令要点 | 结果 |
|---|---|---|---|
| 1 | DATA_PATH 缺省 | 不设变量启动 server | 指向 cwd()/../data,目录缺失 → 4 条权限 error + 退出码 1 |
| 2 | VIDEO_PATH 缺省 | 不设变量启动 server | 指向 cwd()/../video,同上 |
| 3 | 显式路径 | 设 DATA_PATH/VIDEO_PATH/DEFAULT_PRESETS_PATH | 权限 0 错,自动建 config.yaml 与 presets/ |
| 4 | 内置预设缺失 | 去掉 DEFAULT_PRESETS_PATH | ENOENT ... /var/lib/handbrake/preset_builtin.json,退出码 1 |
| 5 | PORT 是否生效 | PORT=8129 | 仍监听 *:9999 |
| 6 | NODE_ENV 未设 | 不设变量 | 日志无任何 [version] 行(更新检查被跳过) |
| 7 | NODE_ENV=production + 间隔 12 | 改配置后启动 | Checking if there is an application update... / The application is up to date. |
| 8 | update-check-interval=0 | 改配置后启动 | The check-interval is set to 0, update checking is disabled. |
| 9 | queue-startup-behavior=1 | 改配置后启动 | Initializating the queue state to 'Active'. → queue 置为 Idle |
| 10 | queue-startup-behavior=2 | 改配置后启动 | Initializating the queue state to 'Stopped'. |
| 11 | WORKER_ID 缺失 | 不设变量启动 worker | No 'WORKER_ID' ... + 退出码 0 |
| 12 | worker 缺 SERVER_PORT | 设 WORKER_ID、VIDEO_PATH | The SERVER_URL or SERVER_PORT environment variables are not set, no valid server to connect to. + 退出码 0 |
| 13 | worker DATA_PATH 非 /tmp 且不存在 | DATA_PATH=/var/tmp/c1-missing-data | ENOENT ... access '/var/tmp/c1-missing-data' + 退出码 1 |
| 14 | 配置下发 | 连 /client 收 config-update | 完整 JSON 与文件一致 |
六、常见误区与建议
6.1 这些变量不存在
| 误传的变量 | 真实情况 | 替代 |
|---|---|---|
PORT | 源码没有读取 | 反向代理换端口 |
LOG_DIR / LOG_PATH | 日志固定落在 $DATA_PATH/log | 只能改 DATA_PATH |
WORKER_DATA_DIR | worker 只认 DATA_PATH | 设 DATA_PATH |
HANDBRAKE_PATH / HANDBRAKE_BIN | 命令名写死 HandBrakeCLI | 保证 PATH 里有它,或改包装脚本 |
DATABASE_PATH / DB_PATH | 固定 $DATA_PATH/handbrake.db | 改 DATA_PATH |
CLIENT_BUILD_PATH | 固定 /handbrake-web/client | 用反向代理托管前端 |
6.2 推荐的生产配置
server:
DATA_PATH=/srv/handbrake-web/data
VIDEO_PATH=/srv/handbrake-web/video
DEFAULT_PRESETS_PATH=/srv/handbrake-web/preset_builtin.json
NODE_ENV=production
worker:
WORKER_ID=worker-01
SERVER_URL=handbrake-server
SERVER_PORT=9999
VIDEO_PATH=/srv/handbrake-web/video
DATA_PATH=/srv/handbrake-web/worker-data/worker-01
配套的 config.yaml(内网、禁止外联):
config:
version: 2
paths:
media-path: /video
input-path: /video
output-path: /video/output
presets:
show-default-presets: true
allow-preset-creator: false
application:
queue-startup-behavior: 2
update-check-interval: 0
6.3 排查顺序
- 进程起不来 → 先看权限检查日志里的路径对不对(
DATA_PATH/VIDEO_PATH缺省是相对工作目录的); - 起得来但打不开页面 → 检查
NODE_ENV=production与前端产物是否落在/handbrake-web/client; - worker 连不上 → 按
SERVER_URL不带端口、SERVER_PORT为目标监听端口、DNS 解析三步走; - 改配置没生效 → 看
Could not load the config file,或确认重启过; - 任务失败但日志只有
[error]级提示 → 那些是 HandBrake 写到 stderr 的探测信息,真正的成败看队列状态。
延伸阅读:《Ubuntu 22.04 安装 HandBrake Web 0.8.1 指南》、《HandBrake Web 0.8.1 介绍与选型指南》。