基于 SmallPeaches/DanmakuRender v5 的改造版本, 面向「直播录制 → 弹幕渲染 → 网盘归档」的无人值守流水线,做了 12 项改造与稳定性加固。 当前 v5.1.0,可长期无人值守运行。
监听开播 → 录制 → 叠加弹幕渲染 → rclone 上传网盘 → 清理本地源文件
↑
全程 SQLite 状态持久化,重启自动对账
两个栏目的关联:本项目的第 1 项改造,与本站编码栏目遇到的是同一个缺陷——
ffmpeg 的 -global_quality 缺少 :v 流说明符,参数落到音频编码器,
libopus 不支持质量模式,直接报错并产出 0 字节文件。
详见踩坑记录中"音频报 Quality-based encoding not supported"一条。
两条独立工序先后撞上同一处,这也是把两者放在同一站点的原因。
| # | 改动 | 原因 |
|---|---|---|
| 1 | 编码参数缺失 :v 的自动检查 |
0 字节 -global_quality 32 会被套到音频编码器上,libopus 不支持质量模式,导致渲染失败、输出 0 字节;而清理规则仍会删除源视频,造成录播永久丢失。现于启动时检查 -global_quality / -crf / -cq / -qp 是否带 :v |
| 2 | GOP 按帧率自动计算 | gop_multiplier: 8 → 60fps 得 -g 480、30fps 得 -g 240。帧率由 ffprobe 实时探测。22fps × 8 = 176,实测关键帧位置 [0, 176, 352, 528] 完全吻合 |
| 3 | YouTube 上传改为 rclone | 原版依赖 googleapiclient 但未列入 requirements,启用必然 ImportError。改为 rclone,并将功能重叠的 custom 段合并进 rclone |
| 4 | target 按 engine 自动推断 |
原版一个 key 展开多份上传时,各份均从 bilibili 段继承默认值,导致"B站 + 网盘"场景中网盘那份错误继承 realtime: True |
| 5 | _check_for_upload 空列表保护 |
同组全部分段短于 min_length 时,up_videos[0] 抛 IndexError,中断整个上传流程 |
| 6 | GetStreamURL() 返回 None 时可读报错 |
抖音风控或限流时无 stream_url,原版继续执行并抛 NoneType is not iterable,无法判断是流地址缺失。历史日志中出现 26 次,每次该段录制作废 |
| 7 | rclone 失败可读化 | 原版仅有 return code: 1。现改为子进程输出重定向到临时文件(避免管道阻塞),失败时附尾部 2 KB,命令不存在时明确提示 |
| 8 | 大文件跳过上传规则 | 横屏且(码率 ≥2500 或 帧率 ≥40)则跳过上传,节省带宽与网盘空间。跳过不影响本地清理 |
| 9 | 清理与上传完全解耦 | 详见下节事故复盘,改动最重的一处 |
| 10 | 移除 B站上传段 | 任务中仍写 target: bilibili 时跳过并记录 warning,不中断流程。biliup.exe(26.8 MB)保留——删除反而会触发自动安装弹窗并阻塞 |
| 11 | rclone remote 名修正 | 注释写作 123pan,实际网盘 remote 为 cd2(WebDAV,12 TB 已用 7 TB)。两者为不同网盘 |
| 12 | .gitignore 补充敏感信息 |
忽略 rclone.conf 与 .login_info/,避免网盘密码与 cookie 被推送至 GitHub |
2026-10-05 真实事故:原视频在渲染尚未结束时被删除,渲染失败
(不存在视频文件 xxx.mkv,跳过渲染),原文件不可恢复。
实际损失:1 段录播(掉了颗兔牙-2026年10月05日01点33分),源文件被删且弹幕版未生成。
根因:将 _check_for_clean 放宽为「文件自身 ready 即清理」时,
忽略了一个事实——src_video 一录完即为 ready,但此刻它正被渲染进程读取。
日志显示渲染任务与清理任务在同一毫秒提交(02:33:17.573 / .574),清理先执行,
渲染启动时文件已不存在。
| 防线 | 位置 | 作用 |
|---|---|---|
| 1 | onLiveSegment | 不在渲染期间调用 _check_for_clean。源视频清理仅由 onRenderEnd 触发 |
| 2 | _check_for_clean | 文件仍有未完成的异步任务(wait 非空)时不清理 |
| 3 | _check_for_clean | 源视频的下游仍处于 rendering 状态时不清理 |
防线 3 只拦 rendering,故意不拦 uploading——
上传读取的是 dm_video(另一文件),不影响源视频;且上传任务投递即返回、不等结果。
若拦 uploading,源视频须等上传完成后才能删除,违背"渲染完成即清理"的设计目标。
回归测试:tools\test_clean_order.py 按真实事件顺序断言。修改清理逻辑后必须执行。
弹幕版渲染完成 ─┬─ 源视频 .mkv → 立即按 clean_args 删除(不等上传)
├─ 弹幕版 .mp4 → 立即提交上传(rclone → 网盘)
└─ 弹幕版 .mp4 → 本地保留(作为备份)
上传成功或失败均为独立结果,不影响上述清理行为
另一处细节:upload_skipped 必须被视为可清理状态,否则命中跳过规则的文件永远无法清理,源视频持续堆积。
当前生效规则 = 横屏必选 且(码率 ≥2500 或 帧率 ≥40):
| 情况 | 判定 |
|---|---|
| 横屏 1080p 6000kbps 30fps | 跳过上传 横屏 + 码率 |
| 横屏 1080p 1000kbps 60fps | 跳过上传 横屏 + 帧率 |
| 横屏 1080p 6000kbps 60fps | 跳过上传 三项全部满足 |
| 横屏 1080p 2500kbps 40fps | 跳过上传 恰好达到阈值 |
| 横屏 1080p 1500kbps 30fps | 正常上传 两项均未达到 |
| 横屏 1080p 2499kbps 39fps | 正常上传 略低于阈值 |
| 竖屏 1080×1920 8000kbps 60fps | 正常上传 横屏为必选项,竖屏不跳过 |
| 竖屏 1080×1920 1000kbps 30fps | 正常上传 |
已修正的问题:若横屏参与命中计数,skip_min_matches: 1 会退化为
"只要横屏即跳过",码率与帧率阈值形同虚设。因此在必选模式下,横屏被明确排除在计数之外。
另:数据缺失时 fail-open——任一项探测不到均不触发跳过,宁可多传,不可误跳。
前提:该规则仅在 auto_upload: True 时生效。未开启上传的任务不会触发。
基于原版 12.8 万条记录分析,结论是真正的问题仅三类,其余为噪音。
| 问题 | 次数 | 性质 |
|---|---|---|
| Stream-gears 异常退出 | 1,520 次真实事件 (50 个主播) |
真实问题 主播仍在线时 biliup 子进程退出,该分段作废。根因为 biliup Rust 层缺陷:
抖音流 Non-monotonous DTS 触发 overflow when subtracting durations。
上游未修复,依靠重连退避自动续录 |
抖音 API KeyError('data') |
6,430 条 | 真实问题 风控或限流时接口不返回 data,连锁导致第 6 项所修的 NoneType 崩溃 |
磁盘写满 [Errno 28] |
9 条 | 真实问题 直接中断录制,需增加水位告警 |
| 弹幕链路 error | 814 条 | 噪音 全部自愈(自动取消并重建 task),仅造成弹幕数秒至数十秒空白,视频不受影响 |
download.log WARN |
102 万条 | 噪音 FLV 解析器常规抖动(Non-monotonous DTS 96 万条),零 ERROR。
但文件已达 140 MB / 105 万行,需增加轮转 |
平台分布:全部错误均来自抖音,B站为 0 条——所有任务都是抖音直播间。
dm_filter.dm_type 留空即只收纯文字弹幕,礼物弹幕会在进入模板前被丢弃——本版已填(设为 [danmaku, gift],实测:纯弹幕 ✓ / 礼物 ✓ / 进场 ✗ / 超级弹幕 ✗)。注意原版为 all,两侧有意不同./login_info/bilibili.json).temp/failed_uploads.json 中的失败上传不会自动重试,需在 WebUI 手动触发rendering,看似卡住实为未运行Task xxx not exists.,历史 56 次)。修改 configs/DMR-*.yml 后建议重启程序Failed to resolve(约 1600 条/小时),重试循环无退避与熔断,日均写入 20 MBdownload.log 无限增长,需定期清理或增加轮转| 方面 | 上游 | 本版 |
|---|---|---|
| 上传目标 | B站 / YouTube / 自定义 | rclone → 网盘 |
| 弹幕过滤 | 全部接收 | 仅弹幕与礼物 |
| 跳过上传 | 无 | 横屏 / 码率 / 帧率规则 |
| 清理 | 与上传耦合 | 解耦 |
| 通知 | 无 | 开播通知 + 启动报告 |
| 启动自检 | 无 | 配置完整性 + 关键命名 + 0 字节保险闸 |
| 工具集 | 基础 | 20 余个运维与审计工具 |
| 测试 | 437 个单元测试 | 沿用,未新增运行时依赖 |
即使 ffmpeg 报成功,也会校验产物非空——避免上传空文件并误删源视频。
这是第 1 项改造(:v 问题)之后的第二道保险:参数错误不会静默产出空文件。
渲染失败不触发清理;产物为空判定为失败并删除残留,源视频保留。