Commit Graph
12 Commits
Author SHA1 Message Date
cat-shark 171b088e7c feat: 批量流水线按 GPU 资源调度(非 GPU 阶段并行、GPU 阶段互斥)
此前组内阶段是串行的(先全部提音、再全部转写、再全部翻译),LLM 走线上端点时
翻译阶段不占显存、GPU 全程空转——实测占整轮挂钟约 40%(19.6W / 272MiB)。

- `_run_job` 改为按组启动在途流水线:每个视频独立推进自己的阶段,最多
  `WOV_BATCH_PIPELINE_WORKERS`(默认 4)个阶段在途。
- 派发只看资源:`stage_gpu_need_mb` 为 0 的阶段(提音、线上翻译、ASS)立刻派发,
  可与其它视频的转写并行;需要 GPU 的阶段由 `GpuGate` 互斥准入,并按"阶段索引
  最小者优先"派发,组内仍是先跑完全部转写再进翻译——本机 Ollama 模型每组只
  加载一次,不需要按"是否云端"写分支。
- 同一阶段只在途一份(派发即标记 running),单视频异常不带走整组;暂停沿用
  run 级 paused.flag,暂停后不再派发新阶段。
- 测试:远端翻译与其它视频转写重叠、本机端点下全部转写先于翻译且翻译互斥、
  提音与转写重叠,以及既有分组/暂停/失败隔离用例。
2026-09-18 22:40:53 +08:00
cat-shark 8645e440f4 feat: 日语转写(翻译前归档)也作为产物放到视频旁
翻译前的日语转写只有中间态,重跑字幕或回看译文时无据可查;把它按语言归档到
视频旁,与中文译文、双目字幕并列。

- `learn-translate` 的 `final_outputs` 增加 `ja_srt: asr.srt_uri`(清洗后的转写)。
- `_sidecar_product_name` 改为**按 final_outputs 别名**映射语言后缀
  (`ja_srt`→`.JA.srt`、`cn_srt`→`.CN.srt`、`ass`→`.CN_dual_eye.ass`),未登记别名
  时按扩展名兜底。此前两份 `.srt` 都映射成 `.CN.srt` 会互相覆盖(真实日志:
  "产物已放视频旁: movie.CN.srt, movie.CN.srt")。
- 任务管理的产物列补"日语 SRT"下载链接。
- 测试:按别名区分语言变体 + 端到端验证两份产物都落地且不覆盖。
2026-09-18 22:27:38 +08:00
cat-shark 7a2dee9b64 fix: 批量任务明细写完再排队(CREATING 状态)
`create_job` 先把任务行以 QUEUED 入库(引擎立刻可见),再逐条登记明细(扫描
媒体库时 500+ 条要数秒);引擎轮询到的快照可能还没包含剩余明细,收尾时"无未结束
明细"检查也看不到它们,于是把任务误标 COMPLETED,剩余视频永远不再被处理。

- 任务行改以 `CREATING` 入库,明细全部登记完才置 QUEUED(引擎只取 QUEUED,
  看不到半成品);登记中途异常置 FAILED 并把异常交给路由层。
- 启动时把上一进程遗留的 CREATING 统一置 FAILED(`fail_creating_batch_jobs`),
  避免明细写一半被热重载/强杀后留下看不见的残留任务。
- 回归测试:明细每写一条就问一次引擎队列(写入过程中取不到任务);中途失败记
  FAILED;启动清理遗留 CREATING。
2026-09-18 22:23:39 +08:00
cat-shark dcdc5e8604 feat: 批量分块流水线、本地模型显存让渡与任务列表分工
批量引擎改为「分块流水线」:视频按 WOV_BATCH_STAGE_GROUP_SIZE(默认 8)分组,
组内按 DAG 拓扑序跑完全部视频(全部 extract → 全部 ASR → 全部翻译 → 全部 ASS)
再进入下一组,本地模型每组只加载一次、卸载一次,而不是每个视频来回加载卸载;
产物仍按组增量落到视频旁。调度器新增 execute_run(run_id, stop_after=节点):
该节点完成后任务保持 RUNNING 不收尾,下一次调用从产物表跳过已完成节点继续,
用于实现阶段边界。

- nodes/llm.py:翻译节点结束释放本机 Ollama 显存(node 参数 unload_after >
  LLM_UNLOAD_AFTER > 本机 loopback 端点默认卸载,云端端点不卸载;卸载失败只告警),
  新增 keep_model.flag 语义(阶段内保持常驻)与 release_local_model();
  新增节点内暂停(按批 20 行检查 paused.flag,抛 PauseRequested,调度器保持 PAUSED)。
- src/wov_app/batch.py:分组阶段执行与阶段末统一释放显存;失败视频只在它失败
  节点的那个阶段重试(避免 LLM 已常驻时重跑 ASR 抢显存);任务没有明细时保持
  QUEUED 等登记完成、仍有未完成视频时置回 QUEUED 自愈(原先留 RUNNING 会卡死:
  引擎只拾取 QUEUED,任务停在“运行中但没人推进”);无失败视频时删除任务级空目录;
  每个阶段开始前清理 paused.flag / keep_model.flag,避免强杀残留影响后续阶段。
- src/wov_app/config.py:新增 WOV_BATCH_STAGE_GROUP_SIZE(设为 1 即旧的每视频全链路)。
- 任务列表与批量页分工:GET /api/runs 默认排除 source=batch(一个批量任务会产生
  N 条单视频 run,会把 20 条窗口占满;且任务管理页的暂停/重试/删除对批量 run
  语义不成立),需要排查时用 include_batch=1;作为补偿批量页详情新增阶段列
  (阶段 i/N · 中文标签,由该视频 run 的 current_node_id 在 DAG 拓扑序中的位置
  推导,节点类型映射中文标签)。阶段只有节点边界粒度,句级进度不落库、只在日志。
- 顺带纳入此前未提交的批量僵尸状态恢复:recover_interrupted_batch_jobs 除 RUNNING
  外也把「COMPLETED 但仍含未结束视频」的任务置回 QUEUED;fix_zombie_batch_jobs.py
  改为按条件扫描并支持 --apply 预览;批量页明细只列本批真正处理过的视频。

测试新增/更新:分块流水线调用顺序(组内按节点跑完再下一组)、每组只释放一次模型、
阶段内保持常驻标志、翻译按批暂停、失败视频不跨阶段推进、任务无明细/中途登记视频时
置回 QUEUED、任务工作空间与残留信号清理、任务列表默认过滤批量 run、详情阶段字段、
前端阶段列渲染;全量 507 passed(唯一失败为既有素材缺失的 integration 用例)。
2026-09-18 10:31:52 +08:00
cat-shark 7a7212f70c docs: 注释规范要求精简可读,并清理生产代码中的历史叙事
AGENTS.md 的注释规范新增三节可执行约束:

- 只写代码真实逻辑:注释只回答"做什么"与"为什么必须这么做",禁止写决策/
  修改时间、历史版本对比、实测数据与实验结论、事故与缺陷编号(run_xxxx /
  batch_xxxx / R01 等)——这些属 docs/decisions.md 与审查跟踪文件;当前生效
  的约束可以写,但不附带它何时因何变成这样。
- 精简可读:单段连续注释不超过 3 行;docstring 一句话概括职责,不重复函数名
  已表达的信息;不写逐行翻译代码的废话注释,只在非显然处(业务规则、边界、
  易错点、外部约束)加注。
- 覆盖范围:测试注释只说明验证什么行为,回归用例可保留一句溯源;并明确
  参数说明应写在**参数读取处**附近,而不是把多个参数的解释堆在离使用位置
  很远的注释块里。

按此清理生产代码(注释净减 70 行,18 个文件),典型处理:

- nodes/whisper.py:删掉堆在一起、含"用户 2026-08 决定 / 实测 savr-1054"
  等叙事的参数块,把各参数说明移到各自的读取处与 model.transcribe 调用处;
- nodes/llm_filter.py、nodes/subtitle_cleanup.py:模块 docstring 去掉英文
  背景叙事与条数统计,保留"默认只跑规则层""整条删除而非 '-' 占位"等当前
  行为;
- src/wov_app/{batch,db,scheduler}.py 与 routers:去掉 batch_xxx/run_xxx 事故
  编号与"修复前……"对比,改为一句"否则会出现什么问题";
- nodes/ass.py、frame_extract.py:去掉废弃值对比与日期,保留判据本身。

安全验证:用 AST 对比(剥离 docstring 后比较语法树)确认 18 个文件**零逻辑
变更**;`nodes/proper_nouns.py` 的规则表 reason 字段会注入 LLM 提示词,属于
数据而非注释,已恢复原值。全量测试 476 passed。
2026-09-13 16:37:49 +08:00
cat-shark 8f6083f8cf feat: 全系统统一 Whisper V2 权重并移除 demo 工作流
确认所有运行时引用均使用 V2(V3 已停用),并修复一处真实不一致:

- 工作流数据文件:learn-translate 用 faster-whisper-large-v2、
  zh-direct 用 whisper-large-v2-translate-zh-v0.2-st-ct2(原本即 V2);
- 本地库的 demo 最新版本仍指向 large-v3:workflows/demo.json 早已改为 V2,
  但 seed 对已存在工作流刻意跳过,导致旧库停留在历史上用 V3 保存的定义,
  即本机跑 demo 实际加载 V3 权重。按用户决定移除 demo 工作流及其关联的
  6 个 run、1 个批量任务与 431 条明细(媒体库中已放置的 6 个字幕成品保留);
- nodes/whisper.py 候选与远端兜底本就是 large-v2;
- V3 权重目录保留在盘上仅作对照实验,文档标注为废弃;
  scripts/compare_whisper_v2_vs_v3.py 保留用于对照。

顺带修复与清理:
- src/wov_app/scheduler.py:_file_size 补捕 ValueError(见上一条提交说明
  的真实缺陷,此处为同一批改动);
- .gitignore:data/ 改为 /data/,避免连带忽略 tests/**/data/;
- scripts/*:评测集路径改到 scripts/data/translate_eval/;
- 代码注释与文档同步移除 demo 引用(历史调研文档保留说明性引用)。

验证:全量 477 passed;新库 seed 只创建 3 个 V2 工作流。
2026-09-13 15:41:58 +08:00
cat-shark 3a612919f7 fix: 保持产物收尾幂等并保护批量成品完整性 2026-09-11 16:12:32 +08:00
cat-shark c063aed1f1 fix: 批量任务残留PENDING仍误标COMPLETED——置完成前校验无残留+崩溃恢复批量任务
事故(batch_969fabe74b83 等 3 个任务实测):批量引擎串行处理到 9.9GB
大视频时中断,_run_job 无条件收尾把任务置 COMPLETED,留下"N 个 PENDING
待处理却已完成"的僵尸状态——3 个任务遗留的 PENDING 完全相同(kiwvr-887/
mdvr-413/savr-1077 共 10 个大视频),且均卡在大文件前从未被真正处理。

- batch.py: _run_job 置 COMPLETED 前校验全部非 SKIPPED 视频已结束
  (无 PENDING/PAUSED 残留),否则保持 RUNNING 交引擎下一轮续跑
- db.py/main.py: 新增 recover_interrupted_batch_jobs,重启时把 RUNNING
  批量任务恢复 QUEUED(否则停在 RUNNING 永远不会被再次拾起)
- tests: 新增 2 条 TDD 回归(残留 PENDING 不置 COMPLETED、批量崩溃恢复)
- scripts/fix_zombie_batch_jobs.py: 历史僵尸数据修复脚本(置回 QUEUED 续跑)
- AGENTS.md: 补充完成任务判定与崩溃恢复约定
2026-09-07 11:25:33 +08:00
cat-shark 44a607b636 fix: 批量进度只统计无字幕视频,SKIPPED 不计入 total/done
用户反馈数量展示错误:整批 431 个视频中 383 个已有字幕被 SKIPPED,
此前 total=全部视频、done 也含 SKIPPED,把已处理过的视频混进计数。

新口径(2026-09):
- total = 创建时扫描出的无字幕(需处理)视频数,创建即固定;SKIPPED 不计
- done = 实际处理完成(COMPLETED)数,SKIPPED 不计;failed 单独计
- 全部视频都有字幕(total=0)→ 创建即视为 COMPLETED,不排队空跑
- sync_batch_job_progress 同步修正 total(老任务读取时自动收敛到新口径)
- 前端 batchProgress 相应更新:total=0 显示"无待处理视频"

tests: 更新 4 条旧口径断言 + 新增 1 条全跳过 API 测试,62 项相关测试通过。
2026-09-06 17:25:01 +08:00
cat-shark 52512c26e5 fix: 批量任务进度实时汇总,暂停/中断时前端不再显示 0/总数 0%
batch_jobs.done 此前只在任务整体完成时一次性汇总,处理中途(尤其被暂停)
恒为 0,导致前端显示 "0/431 完成 0%" 而实际已处理多个视频
(回归 batch_fee668175444:已处理 15 个仍显示 0/431)。

- db: 新增 sync_batch_job_progress(按明细实时重算 done=COMPLETED+SKIPPED、
  failed=FAILED 并落库)与 refresh_batch_job(对齐后返回最新任务)
- batch 引擎: 暂停返回、SKIPPED/COMPLETED continue、视频缺失、单视频异常后、
  任务收尾等边界统一调用实时对齐,替代收尾一次性 sum
- routers/batch: 列表/详情读取前实时对齐,即使引擎不在运行也返回真实进度
- tests: 新增 6 条 TDD 回归测试覆盖 db 助手、引擎暂停、SKIPPED、API 读取侧

同时按用户要求:移除 100% 行覆盖率强制门槛(pytest 不再 --cov-fail-under),
约定小改动只跑相关测试、保证功能可用即可(AGENTS.md 与 pyproject.toml)。
2026-09-06 16:57:05 +08:00
cat-shark d1557eca46 feat: 批量处理重做——视频旁已有字幕即跳过、产物对齐 CN 命名并清理过程文件
- 创建批量任务时一次性定位视频:视频所在目录存在文件名含视频名的字幕文件
  (.srt/.ass/.ssa/.vtt)直接记 SKIPPED,不触发流水线;运行时只消费已定位
  的明细,不再重新扫描文件夹。
- 视频完成后把最终产物放到视频旁,命名对齐媒体库约定:中文字幕存为
  <视频名>.CN.srt、双目字幕存为 <视频名>.CN_dual_eye.ass;其余扩展名产物
  保留原文件名。
- 收尾删除 run 记录与过程工作空间;工作空间改到应用私有目录
  storage/batch/<job_id>/<bv_id>/,与用户媒体库隔离,防止媒体库把切片数据
  当视频入库。
- 批量 API 产物清单/下载改为解析视频旁字幕文件,旧版 batch.done.json 语义
  别名保持兼容;删除任务时清理私有工作空间。
- 前端说明与创建提示同步;测试按新语义重写并补覆盖(278 passed,100% 行覆盖率)。
2026-09-03 08:22:42 +08:00
cat-shark 711867e79f feat: 文件夹批量处理引擎(后端)
- BatchWorker 单线程轮询 batch_jobs 表,处理 source=batch 的运行,
  与主调度器互不抢占(next_queued_run 排除 batch 来源)
- 直接读取用户所选文件夹下的视频逐个执行流水线,不上传到工作目录;
  中间态与产物落在视频旁同名文件夹,batch.done.json 完成标记去重
- 支持暂停/继续、失败容错(单视频失败不阻塞后续)、删除任务只清库
- 孤儿清理跳过 source=batch 运行,防止误删用户视频文件夹
- workflow_runs 新增 source 列(upload/batch),旧库自动迁移
2026-08-23 16:25:09 +08:00