feat: 媒体库字幕重生成统计脚本(数量/时长/旧字幕备份)

模型与管线切换后需要把媒体库里旧管线生成的字幕整批重跑。批量引擎按"视频旁
已有字幕即 SKIPPED"判定,重跑前必须先统计范围、再把旧字幕改名备份,否则建出来
的任务会把所有视频全部跳过。

- `scripts/plan_regenerate_subtitles.py`:扫描媒体库,按 CN 产物的最早 mtime 判定
  生成时间,统计待重生成的数量/时长/体积并输出 JSON 计划;`--backup-old [--apply]
  [--select]` 把旧字幕原地改名为 `<原名>.old-<日期>` 供批量重跑。脚本不依赖仓库,
  可拷到媒体库主机直接跑(CIFS 挂载下逐文件 ffprobe 太慢)。
- 测试覆盖真实 10 秒视频与真实字幕文件,含 ffprobe 时长探测、分类边界、
  备份幂等与选择清单解析。
This commit is contained in:
2026-09-18 22:23:19 +08:00
parent dcdc5e8604
commit f656ec98c5
6 changed files with 673 additions and 0 deletions
+34
View File
@@ -160,6 +160,40 @@
- **环境变量**`WOV_BATCH_ENABLED`(默认 1)、`WOV_BATCH_INTERVAL_SECONDS`
(默认 1.0),完整列表见 [configuration.md](./configuration.md)。
## 媒体库历史字幕重建(统计 → 备份 → 批量重跑)
模型或管线切换后,媒体库里由旧管线生成的字幕需要整批重跑。批量引擎按
“视频旁已有字幕即 SKIPPED”判定,所以重跑前必须先统计范围、再把旧字幕改名
备份,否则建出来的任务会把所有视频全部跳过。工具是 `scripts/plan_regenerate_subtitles.py`
(**不依赖仓库**,可拷到媒体库主机直接跑:CIFS 挂载下逐文件 ffprobe 与
目录扫描都慢得多,在 NAS 本地跑 524 个视频只要几秒)。
生成时间的判定口径:只看本流水线产出的 CN 产物(`<视频名>.CN.srt`、
`<视频名>.CN_dual_eye.ass`),取它们**最早**的 mtime——双目 `.ass` 可能被样式
统一脚本原地改写而“变新”,中文字幕的 mtime 才是真实生成时间。
```bash
# 1) 统计:数量 / 时长 / 体积 / 按月分布,并写出逐条明细 JSON
python3 scripts/plan_regenerate_subtitles.py /vol1/1000/123 \
--before 2026-09-01 --out data/experiments/regen_plan/plan_2026-09-01.json
# 2) 备份旧字幕(默认预览,--apply 才落盘):改名 <原名>.old-<当天日期>
python3 scripts/plan_regenerate_subtitles.py /vol1/1000/123 --backup-old
python3 scripts/plan_regenerate_subtitles.py /vol1/1000/123 --backup-old --apply \
--select sample10.txt # 只处理清单里的视频,支持 # 注释
# 3) 建批量任务(批量页或 POST /api/batch/jobs
```
- 备份文件名带 `.old-<日期>` 后缀,扩展名不再是字幕后缀,批量引擎不会再把它当
旁挂字幕;确认新字幕无误后删除备份,需要回退则去掉后缀改回原名。
- 未备份的视频仍有旁挂字幕 → 创建任务时记 SKIPPED,因此“整库建一个任务”即可,
实际只会处理被备份的那批;已重跑的视频产出新 CN 字幕(mtime 变新),全量重建
时自动归入“已是最新”,不会重复处理。
- 实测数据(380 个待重生成 / 207.88 小时;10 个样本 6.54 小时素材跑 77.7 分钟、
GPU 平均 289.6 W、约 0.375 kWh,全量推算约 41 小时 / 整机 15 kWh)见
`data/experiments/regen_plan/REPORT.md`。
## 相关文档
- 环境变量全表:[configuration.md](./configuration.md)
+2
View File
@@ -51,6 +51,7 @@ tests/
├── web/test_crop/ # 对应 web/assets/(框选几何换算)
├── web/test_batch/ # 对应 web/assets/(批量页渲染)
├── scripts/test_fix_zombie_batch_jobs/ # 对应 scripts/(僵尸批量任务修复)
├── scripts/test_plan_regenerate_subtitles/ # 对应 scripts/(字幕重生成计划统计)
└── shared/ # 跨模块公共设施
├── realdata_contract.py # 真实数据契约与对齐量化
├── srt_entries.py # 按秒解析 SRT
@@ -161,6 +162,7 @@ tests/
| `web/assets/crop.js` | 框选几何换算 | `tests/web/test_crop/`(真实 node 执行) | 已覆盖 |
| `web/assets/batch.js` | 批量页明细/进度渲染 | `tests/web/test_batch/`(真实 node 执行) | 已覆盖 |
| `scripts/fix_zombie_batch_jobs.py` | 僵尸批量任务诊断与修复 | `tests/scripts/test_fix_zombie_batch_jobs/` | 已覆盖 |
| `scripts/plan_regenerate_subtitles.py` | 媒体库字幕重生成计划统计(数量/时长/分类) | `tests/scripts/test_plan_regenerate_subtitles/` | 已覆盖 |
| `tests/shared/srt_entries.py` | 按秒解析 SRT(测试公共设施) | `tests/shared/test_srt_entries/` | 已覆盖 |
| `tests/shared/realdata_contract.py` | 真实数据契约与对齐量化 | `tests/shared/test_alignment/` | 已覆盖 |
| `tests/shared/env_isolation.py` | 环境/临时目录隔离 | 被 `tests/app/test_config` 等间接覆盖 | 已覆盖(间接) |