Commit Graph
55 Commits
Author SHA1 Message Date
cat-shark df7a91d97c feat: 新增 GPU 资源门控模块(探测/需求估算/准入租约)
为"按资源而不是按配置调度"提供判定基础:`stage_gpu_need_mb` 给出节点的显存
需求(whisper 按权重 ×1.45;本机 LLM/VLM 端点按预留;线上端点为 0),
`GpuGate` 负责准入(GPU 阶段互斥 + 显存余量检查;探测不到 nvidia-smi 时退化为
互斥,与串行等价)。

- 判定只看资源现状,调用方不需要判断"是否云端"。
- 局域网地址上的本机 Ollama 用 `WOV_LOCAL_MODEL_HOSTS` 声明;预留值可用
  `WOV_LOCAL_LLM_RESERVE_MB` / `WOV_LOCAL_VLM_RESERVE_MB` 调整。
- 测试覆盖互斥、余量边界、无探测退化、真实权重估算与端点判定。
2026-09-18 22:40:37 +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 34f0052d61 test: 对齐用例缺真实素材时跳过而不是判失败
`tests/shared/data/alignment/` 里的媒体素材按规则不入库(.gitignore 掉 mp4/wav),
本机只拷了参考字幕时该用例会判红。按"外部环境状态缺失应跳过"的规则改为:目录在
但一个媒体文件都没有时 skip;有媒体但配对不全仍然失败(那才是真回归)。
2026-09-18 22:23:39 +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 3f4478523e fix: whisper 重复伪影整条删除,避免翻译层空响应失败
whisper 在单个窗口内卡住重复时会把一个单元写满整条 cue(实测 30 秒整、重复
74–446 次,如 `チン`×111)。这类正文会让下游 LLM 跟着循环、把输出预算耗在思考上,
最终 `content` 返回空串并报 `translation alignment failed … Expecting value:
line 1 column 1 (char 0)`;它也可能直接渲染成超长字幕行。

- `nodes/subtitle_cleanup.py` 新增 `remove_repetition_entries`:**纯模式判据、无字符
  词表**——展示时长 ≥15s 且同一 1–6 字单元连续重复 ≥6 次且覆盖正文 ≥70% 的 cue 整条
  删除;真实短促呻吟(`ぇ`×15、`ああああああ`)靠时长区分,零误删。
- `nodes/whisper.py` 在转写产出处应用该清洗(VAD 开关都生效,它与套话幻觉无关)。
- 真实数据验证:本地全部真实转写 3605 条 cue 只删 5 条 30 秒伪影;对照实验同批次
  伪影截短后 5/5 成功、原样 1/5。
2026-09-18 22:23:25 +08:00
cat-shark f656ec98c5 feat: 媒体库字幕重生成统计脚本(数量/时长/旧字幕备份)
模型与管线切换后需要把媒体库里旧管线生成的字幕整批重跑。批量引擎按"视频旁
已有字幕即 SKIPPED"判定,重跑前必须先统计范围、再把旧字幕改名备份,否则建出来
的任务会把所有视频全部跳过。

- `scripts/plan_regenerate_subtitles.py`:扫描媒体库,按 CN 产物的最早 mtime 判定
  生成时间,统计待重生成的数量/时长/体积并输出 JSON 计划;`--backup-old [--apply]
  [--select]` 把旧字幕原地改名为 `<原名>.old-<日期>` 供批量重跑。脚本不依赖仓库,
  可拷到媒体库主机直接跑(CIFS 挂载下逐文件 ffprobe 太慢)。
- 测试覆盖真实 10 秒视频与真实字幕文件,含 ffprobe 时长探测、分类边界、
  备份幂等与选择清单解析。
2026-09-18 22:23:19 +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 ee47556a61 test: 移除根级 testdata/,数据已按模块迁入 tests/**/data/
测试重写时数据已随模块目录入库(见 8a715a8),根级 testdata/ 只剩副本:

- 参考字幕与帧边界素材 → tests/shared/data/、tests/nodes/*/data/;
- translate_eval 评测集 → scripts/data/translate_eval/(评测脚本专用,
  同步更新 build_translate_eval / build_segment_eval / bench_translate_models
  的默认路径);
- extract_reference_srt.py 的默认输出目录改为 tests/shared/data/alignment/。
2026-09-13 15:41:28 +08:00
cat-shark 8a715a8064 test: 按模块重写测试代码,删除旧平铺结构
按"测试规则"重写 tests/:一个模块一个目录、用例按数据→过程→验证三段书写、
不保留全局 conftest.py、测试过程只调用真实生产代码。

结构(73 个文件、30 个模块目录、477 用例):
- tests/nodes/  15 个模块目录(srt/whisper/ass/ffmpeg/frame_extract/vlm/
  subtitle_ocr/llm/llm_filter/subtitle_cleanup/subtitle_correction/
  proper_nouns/adaptive_pool/vad_profiler/echo);
- tests/app/    11 个模块目录(db/scheduler/batch/maintenance/registry/seed/
  storage/config/logging/main/routers 三组 API);
- tests/sdk/test_models、tests/web/test_crop、tests/shared(公共设施)。

测试数据随模块目录入库(tests/**/data/),删除根级 testdata/;.gitignore
的 data/ 改为 /data/,否则会连带忽略 tests/**/data/ 导致测试数据无法入库。

顺带发现并修复三个真实缺陷:
- nodes/srt.py:相邻条目缺少空行时把下一条时间轴吞进正文(静默错位),
  改为正文行遇时间戳行即报错;
- src/wov_app/scheduler.py:_file_size 只捕获 OSError,含 \x00 的产物 URI
  抛 ValueError 导致任务误判失败,改为同时捕获;
- nodes/subtitle_correction.py:生产代码依赖测试包解析 SRT,
  改用生产模块 nodes/srt.py。

真实模型/服务集成测试按外部状态跳过:新增 tests/shared/gpu_memory.py
(运行时探测显存、CUDA OOM 转跳过)与 tests/shared/llm_service.py
(无 Key / 余额 / 限流转跳过)。全量 477 passed。
2026-09-13 15:40:56 +08:00
cat-shark 966f3e6b4b docs: AGENTS.md 只保留代理规则,项目信息迁入 README 与 docs/
把 AGENTS.md(545 行)中的项目知识按性质拆开,只留下对代理的要求:

- AGENTS.md(176 行)只写规则:读文档指引、提交许可、等待规则、TDD、
  测试规则(模块边界/三段结构/功能覆盖优先)、注释规范、目标运行环境、
  PowerShell 规则、文档维护规则;
- 项目信息新建 docs/ 专题:architecture、node-protocol、workflows、
  configuration、operations、testing、decisions;
- README 改为项目索引(定位、快速开始、文档导航、目录、工作流、结论摘要);
- 修正旧文档错误:README 的"详细约定见 AGENTS.md"与"100% 行覆盖率"
  (pytest 已移除该门槛);环境变量表补齐 WOV_AUTO_VAD 等 3 项。

新增 scripts/check_doc_links.py 校验相对链接与锚点,当前 14 个文档全部可达。
2026-09-13 15:40:31 +08:00
cat-shark 669858c1d1 test: 补充纠错节点独立模型环境变量的失败用例(待实现)
新增 test_纠错节点用独立环境变量不受全局模型影响,锁定期望行为:
params.model > SUBTITLE_CORRECTION_MODEL > LLM_MODEL > 兜底默认。

背景:上一个提交想"让纠错节点保留旧模型",但只改了 os.getenv 的兜底常量
——只要 .env 里存在 LLM_MODEL(生产环境必然存在),兜底值永远取不到,
该改动实际无效,真实 LLM 集成测试
test_generic_correction_generalizes_to_unseen_mishearing 失败(新模型
误听泛化实测 0/4,旧模型 4/4)。

本用例当前为红(测试代码尚缺 urllib 导入,实现也待补),按用户要求
与实现一起保留待 review 后修正。
2026-09-13 10:21:15 +08:00
cat-shark fc28f22a3f docs: 记录模型切换与过滤层决策,新增等待规则
- AGENTS.md:
  * LLM_MODEL 更新为 Qwen/Qwen3.5-35B-A3B,并说明 subtitle-correction
    节点有意保留旧模型(实测 0/4 vs 4/4);
  * llm-filter 节点表格改写:规则层为默认且唯一启用层级,列出新增的
    水印编号/日期/VLM 提示回显/角色标注规则,并记录 LLM 层关闭的
    数据依据(131 条额外删除中 56% 是真对话);
  * **新增「等待规则(强制,2026-09)」**:单次等待不得超过 10 秒,
    长任务放后台写日志并每 10 秒轮询;明确禁止 sleep 600 /
    timeout 1800 这类阻塞用户的写法(含反例说明)。
- README.md:同步默认模型、纠错节点例外、过滤层默认关闭;
- scripts/probe_llm_rate_limit.py 与 tests/test_integration_prompt_rules.py
  的兜底默认值同步为 Qwen/Qwen3.5-35B-A3B。
2026-09-13 10:15:50 +08:00
cat-shark e98f90e164 fix: llm-filter 默认关闭 LLM 分类层,误删真实对话从 73 条降为 0
逐类人工审查真实任务 run_ac7f480a3ccb(1666 条 OCR 输出)后确认
LLM 五类分类层性价比为负:

- 规则层删除 782 条(47%),几乎全对(---/HTML/___/编号等);
- LLM 层额外删除 131 条,其中 **73 条(56%)是真实对话**,
  如"好好教育她一番吧""腿不要合上""这家医院 为VIP患者提供了特殊服务";
- 它真正抓住而规则层抓不到的仅 58 条且大半可正则化;
- repeat 类别 67 条判定、0 条删除,形同虚设;
- 长文本保护/上下文净化/去重/429 退避/断点存档等机制全是在给
  不稳定的分类器兜底,误删量超过净收益。

改动:
1. 规则层下沉原 LLM 层抓到的确定性模式:水印编号(SPHO-1/PHO一号馆)、
   日期与数值(2011-11-27/4.0)、VLM 提示回显(no text is visible)、
   角色标注((出演))。刻意不删(北冈果林)这类演员名括号——无法与
   (小声)不要啊 可靠区分,且其本身是无害字幕文本;
2. 新增 use_llm 参数并**默认关闭** LLM 分类层,需要旧行为时显式开启;
   ocr-subtitle 工作流显式声明 use_llm=0 并附 _note_use_llm 理由。

真实数据实测:保留 863 条(旧 588)、误删真对话 0 条(旧 73)、
LLM 调用 0 次(旧 680 次/52 秒)。
2026-09-13 10:15:41 +08:00
cat-shark 1007612734 feat: 翻译节点默认模型切换为 Qwen3.5-35B-A3B 并按节点分离兜底
评测结论(同片 2 小时日语 ASR,8 个模型全量对比):
Qwen/Qwen3.5-35B-A3B 与旧默认 Qwen3.6-35B-A3B 质量持平,
速度 0.232 s/行(全评测最快,旧模型档位)。

改动:
- nodes/llm.py 兜底默认值改为 Qwen/Qwen3.5-35B-A3B;
- nodes/subtitle_correction.py **有意保留** Qwen/Qwen3.6-35B-A3B:
  同一误听泛化场景各跑 4 次,旧模型 4/4 正确推断性器官语义,
  新模型 0/4(输出"阴道/曼果/曼戈"字面直译)——该节点不能跟随全局默认;
- tests/test_llm_default_model.py 锁定该分叉不被"顺手统一":
  翻译兜底必须与 .env 一致,纠错兜底必须保留旧模型,
  三处都必须读 LLM_MODEL 环境变量(保留单点覆盖能力)。

模型仍是工作流 DAG 的数据(params.model),切换不需改代码。
2026-09-13 10:15:31 +08:00
cat-shark 8963afa771 feat: 新增翻译模型横向评测工具链与窗口级评测集
为评估"更便宜的 LLM 能否替换 llm-translate 默认模型"新增真实数据驱动的
评测工具(不 mock 模型,直接调用生产节点实现,模型仅作为 params 变量):

- scripts/build_translate_eval.py:把 ocr-subtitle 的中文烧录字幕产物与
  learn-translate 的日语 whisper ASR 按时间配对,产出候选池;
- scripts/build_segment_eval.py:改为**窗口级**配对(一条中文基准字幕 +
  其时间窗内 1-3 条日语 cue)。逐条配对不可用——烧录字幕是按屏幕合并的
  整行,与 whisper 的 cue 切分不同,直接逐条对照会被基准错位污染;
- scripts/bench_translate_models.py:对每个模型跑完整片、记录单次调用耗时
  与 token、输出窗口级多模型对照表供人工 review;VL 模型需剔除
  enable_thinking(Qwen3-VL 不接受该参数,生产代码固定携带);
- scripts/run_translate_bench_queue.py:批量评测队列;
- tests/test_translate_model_bench.py:评测数据契约与对照输出一致性测试
  (含先红后绿修复:stdout 与 markdown 两套输出格式漂移);
- testdata/translate_eval/:候选池、窗口池与 124 窗口人工评测集资产。

评测结论见 data/experiments/translate_models/REPORT.md(gitignored)。
2026-09-13 10:15:23 +08:00
cat-shark f99f8de171 fix: 拒绝环形 DAG 并避免无效工作流堵塞任务队列 2026-09-11 17:19:40 +08:00
cat-shark 6eb65e4356 fix: SRT 按 cue 解析并按 ID 回填译文,OCR 空帧分段与失败重试 2026-09-11 17:19:25 +08:00
cat-shark 3a612919f7 fix: 保持产物收尾幂等并保护批量成品完整性 2026-09-11 16:12:32 +08:00
cat-shark f3faad0391 fix: 修复自适应线程池限流扩容及缩容滞后 2026-09-11 15:59:46 +08:00
cat-shark 13f72178e9 fix: 防止任务删除误删源视频目录并跟踪审查问题 2026-09-11 15:40:09 +08:00
cat-shark eba9163246 feat: 优化学习视频转写与字幕清洗 2026-09-11 14:26:02 +08:00
cat-shark a8fe133aa4 feat: whisper 新增 decode_full 无VAD整段解码参数并改为整条删除式幻觉清洗
解决转写漏句(有人说话但没识别出来)问题:silero VAD 对呻吟/轻语/BGM
混叠声学切段能力天然不足,把真话当非语音剔除(实测 savr-1054 全片仅
召回 115 条)。新增 decode_full 参数(默认 false 保持 VAD 现状):

- decode_full=true 时强制无 VAD 整段解码 + 跳过自动 VAD 分析,救回被
  剔除的弱语音(savr-1054 全片 115 条 → 340 条)
- 副作用是长时寒暄套话幻觉(おやすみなさい/ご視聴ありがとうございま
  した 等),whisper 转录后连带时间戳整条删除(clean_japanese_ha
  lllucinations),不留下 '-' 占位污染下游(占位会渲染进 ASS 成减号)
- llm-translate 翻译后同样整条删除中文长时寒暄幻觉(clean_srt_text)
- 短时(≤15s)相同词可能是剧情真实道晚安,保留(15s 阈值实测校准)
- subtitle_cleanup 由 '-' 占位式改为整条删除式 + 剩余重编号,新增
  JAPANESE_HALLUCINATION_TOKENS 词表

新增工作流 learn-translate(学习资料转译+翻译字幕)示范 decode_full
用法,并确立参数标注约定:params._note_<参数名> 存放设定理由与正反例、
_node_help 放节点参数手册(_ 前缀说明键,节点执行时忽略,零运行影响)。

调研记录见 docs/调研-whisper漏句与decode_full验证.md(A/B 实验、结论
修正与 5 个待决问题)。
2026-09-07 17:21:04 +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 4d2a1912da feat: VR字幕默认MarginV改为700并统一历史字幕样式
- srt-to-dual-eye-ass: 默认顶部安全边距 margin_top 120→700(2026-09),
  120落在画面最顶需抬头看,700为实测合适值、视线自然可读
- nodes/ass.py: 样式定义抽为单一事实来源(DEFAULT_MARGIN_TOP/颜色常量
  +ass_header/style_row/dialogue_line),新生成字幕与历史统一脚本共用同一
  出口,以后调样式只改一处、两边永不漂移
- 新增scripts/unify_ass_style.py: 解析旧文件分辨率与全部Dialogue后经
  ass_header/dialogue_line重建,把媒体库历史样式(底部an2实心白1200/
  半透明1020/顶部120)原地统一为an8顶部对齐+70%透明+MarginV=700;
  默认dry-run预览,--apply写盘;非VR字幕自动跳过
- /mnt/fnOS/123 库401个.CN_dual_eye.ass已全部改写(0失败),269764条
  字幕事件无an2残留,二次运行幂等
- 测试: ass默认值断言120→700,新增invoke默认700用例;新增
  tests/test_unify_ass_style.py(历史三世代收敛/与write_ass逐字节一致/
  幂等/CLI dry-run与apply)
2026-09-06 18:02:31 +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 fbcb5115d8 feat: llm 翻译节点打印分批进度与 token 处理速度日志
翻译节点 translate_lines 分批调用 LLM 时补充可观测日志:
- 任务开始打印总行数与总批数;每完成一批打印总进度(已完成/总行数、
  第几批/共几批、批耗时、累计耗时与行/s),结束打印汇总(总耗时、
  累计 tokens、tok/s 与行/s),便于评估 LLM 处理速度;
- _call_llm 解析响应 usage(total_tokens),每次请求打印单批耗时与
  token/s(接口不返回 usage 时按 0 处理);
- _translate_batch 改为返回(译文, 本批 tokens),供累计汇总;
- 补充空输入直接返回的单测,保持 100% 行覆盖率。
2026-09-06 13:57:44 +08:00
cat-shark 6a265a1bc8 feat: 注册 subtitle-correction 节点到进程内注册表
在 register_all 中导入 subtitle_correction 并注册其 invoke,
使调度器可按 node_type 调用领域纠错节点。已通过 registry 冒烟验证
(list_nodes 含 subtitle-correction)。
2026-09-06 10:44:21 +08:00
cat-shark f2895ee103 feat: 字幕领域纠错模块(通用领域词表 + 有效上下文,不过拟合)
背景:成人视频 ASR 常把性器官(チンポ/マンコ)听错成近音词(手先/
チェーンバー 等),翻译逐字直译导致与画面严重不符。

实验结论:
- 硬编码 ASR 误听例子(手先→肉棒)过拟合——换视频的误听词就失效;
- 通用领域词表(只列性器官的常见日文词+中文)+ 有效上下文 + 通用引导
  可泛化——对从没见过的误听(バナナ/マンゴー→性器官)也能按语境推断。

实现 nodes/subtitle_correction.py:
- PROPER_SESSION_WORDS:通用性器官领域词表(不含 ASR 误听噪声词)
- _is_fragment:纯语气词/碎片过滤
- _build_context:目标 ±60s 有效上下文(过滤碎片,保留动作链)
- _system_prompt:通用引导(无具体误听例子)
- _extract_target_line:从 LLM 输出解析目标行译文
- invoke:逐条领域纠错,产出 corrected.srt

测试 tests/test_subtitle_correction.py(12 个):
- 碎片判定/上下文过滤/目标解析/提示词无噪声例子(9 快速单测)
- 真实 LLM 泛化回归(对未出现误听词也能推断性器官)+ 端到端 invoke
2026-09-06 10:44:09 +08:00
cat-shark 2c3c356348 feat: whisper 节点自动接入自适应 VAD + 方案文档
- nodes/whisper.py:invoke 检测到 vad_filter=true 且未显式传 vad_parameters
  且 WOV_AUTO_VAD=1 时,自动调用 vad_profiler.vad_parameters_for_audio 按
  本音频信号动态确定 VAD 参数并传给 transcribe;分析失败回退默认不中断转写
  (防御性,不影响整段流程)。
- tests/test_nodes.py:新增自动 VAD 异常回退测试(patch profiler 抛错,
  验证 whisper 正常转写 + transcribe 收到 vad_parameters=None)。
- docs/adaptive_vad.md:完整方案文档(背景/可行性/三层架构/参数建议规则/
  评分器/实现落点/取舍/实现状态)。
2026-09-06 08:15:13 +08:00
cat-shark d0d96a8f89 feat: 每视频自适应 VAD 调参模块(信号分析 + 幻觉词扣分评分)
背景:实测 CJOD-255 全程 BGM 覆盖(56% 低能量、几乎无静音),固定 VAD
参数把音乐当语音 → whisper 全段解码 → 80% 碎片化 + 32% 漏句 + 敏感段丢失。

实现 nodes/vad_profiler.py:
- profile_audio:1s 能量网格分析 → 静音比例/BGM 覆盖/长停顿识别
- suggest_vad_parameters:按信号特征推荐 threshold/min_silence/speech_pad
  (BGM 覆盖 -> 降 threshold 增人声敏感;长停顿 -> 降 speech_pad 防时间漂移;
   静音占比高 -> 升 threshold 剔虚警)
- score_transcript:启发式评分(不用参考字幕,符合部署实际)——
  碎片率 + 幻觉词(感谢观看/晚安/音乐)扣分 + 平均字数适中
- vad_parameters_for_audio:信号分析 + 可选片段网格验证,选最优参数
- _grid_search_vad / _candidate_params:候选网格搜索与异常回退

测试 tests/test_vad_profiler.py:19 个用例覆盖信号分析、四个建议分支、
幻觉词/碎片评分、网格选优、异常回退、空音频/低采样率、候选展开。
2026-09-06 08:14:59 +08:00
cat-shark a032855210 feat: 字幕质量提升——长时寒暄幻觉清洗 + 专名/成人隐语不直译
两部分同属字幕质量优化,共用 llm.py 翻译链路:

1) 长时寒暄幻觉词清洗(nodes/subtitle_cleanup.py)
   对展示时长超过阈值(默认 15s,实测 30s 幻觉占位 vs 2s 真实词的分界)
   且文本含收尾/开场寒暄(晚安、感谢观看等)的字幕,文本替换为 '-',
   由后续过滤流程移除;短时寒暄(如剧情中真实互道晚安)保留不误删。
    写文件前调用 。

2) 专名/隐语不直译(nodes/proper_nouns.py)
   片假名专名(人名/品牌/角色)与成人语境隐语是 LLM 误译重灾区:
   - ジンゴ 被误译成'芒果'、カンタくん 被保留日文而非音译;
   - マンゴー/バナナ/リンゴ/金玉/おまんこ/ちんちん 等在色情语境中
     是生殖器官代称,字面直译严重错译。
   整理三张规则表(专名/拟声词/成人隐语,用户提供隐语表),
    检测原文命中后动态注入翻译提示词,
   让 LLM 按正确语义处理。文档见 docs/proper_nouns.md。

测试(红→绿):
- tests/test_hallucination_mask.py:长时掩码/短时保留/非寒暄保留/阈值边界
- tests/test_proper_nouns.py:专名命中/拟声词/成人隐语/真实数据集成
- 真实 LLM 集成测试(完整 1440 行翻译 + 清洗 + 专名注入)通过
2026-09-05 22:32:40 +08:00
cat-shark fcdcfe020b fix: SRT 解析器正确处理空文本字幕条目
旧正则把空文本条目与下一行合并,导致真实集成测试偶发
"译文条数 1439 != 原文 1440"(翻译补空/空字幕使条目被吞并)。
改为按时间轴行分块解析:序号缺省、空文本也独立计数,时间轴不丢失。
2026-09-05 22:32:15 +08:00
cat-shark 2e8bbb15a4 feat: ASS字幕移到顶部安全区并加透明度(零视差不变)
- srt-to-dual-eye-ass: an2底部→an8顶部对齐,MarginV改为可配margin_top(默认120),
  字幕避开画面中央人脸高发区(B-1顶部安全区)
- 文字填充&H80FFFFFF→&HB3FFFFFF(约70%透明),描边纯黑→&H80000000半透明黑
- A-1零视差保持不变:左右眼水平相对位置一致,字幕固定屏幕平面
- 新增3条测试(顶部对齐/自定义margin_top/invoke读参数),ass节点覆盖率100%
- 新增docs/调查报告:双目视觉景深原理、字幕深度配置公式与遮挡解决方案
2026-09-05 21:03:55 +08:00
cat-shark 5c12bbcb74 fix: 翻译批内行数错位(内容-时间错位)+ system_prompt 元组 bug
根因(真实任务 run_51242078d76e):
1. LLM 按 CHUNK_SIZE=20 分批翻译时,对语义碎片句(如单独的助词/名词/
   语气词)偶发多拆/少拆一行,translate_lines 无条件 extend 导致:
   - 多行 -> 后续所有字幕文本整体错位,时间戳从原文复制、文本却错贴时间;
   - 少行 -> invoke 末尾补空导致该条内容缺失。
   程序按时戳看不出问题,实际"内容对错时间"(如第756条"好像喜欢害羞
   的样子"错贴 3805s,实为"恥ずかしいのが好きみたいなので"的译文)。
2. system_prompt 圆括号内出现 f-string 赋值导致隐式字符串拼接失效,
   整体变成 tuple,json 序列化后 content 是数组 -> LLM API 400。

修复:
- 提示词强化:逐行独立翻译 + 碎片句按语境独立成行 + 禁止合并/拆分;
- _repair_batch:多行末尾合并到前一行、少行重试该批(最多3次)仍不足
  补空串占位(宁缺勿错位),保证译文与原文逐条时间对齐;
- system_prompt 显式 + 拼接为单个字符串。

测试(先红后绿):
- test_translate_lines_aligns_extra_line:多行合并对齐
- test_translate_lines_aligns_missing_line:少行重试补齐
- test_translate_lines_pads_after_retries_exhausted:重试耗尽补空
- test_pipeline_zh_cn_timetext_alignment:真实 LLM 完整 1440 行逐条对齐
pyproject.toml: 注册 integration marker
2026-09-05 19:36:47 +08:00
cat-shark 078170c12a feat: 真实数据时间对齐集成测试框架
- tests/realdata_contract.py:数据契约底座(SRT 解析/清洗、时间对齐量化
  指标 align_report、幻觉词/专名判定、提示词规则拼接)
- tests/test_integration_alignment.py:流水线产物 vs 硬字幕参考的时间对齐
  (真实数据复现"字幕过早/过晚",红→绿闭环)
- tests/test_integration_prompt_rules.py:寒暄幻觉/专名提示词规则测试
- scripts/extract_reference_srt.py:从烧录字幕视频自动提取参考时间轴
- testdata/REALDATA_README.md + alignment/*.reference.srt:真实参考字幕
  (视频素材较大,gitignore 不入库)
2026-09-05 19:36:23 +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 ec8c8d7dcd docs: 更新 AGENTS.md(批量处理/断点存档/环境变量) 2026-08-23 16:25:22 +08:00
cat-shark 39f309094d fix: ASS 双眼字幕样式修正
- 右眼 MarginR 误用 width-50 改为 50(左右眼对称内边距)
- 字幕颜色改为半透明白(&H80FFFFFF),避免遮挡 VR 画面
2026-08-23 16:25:20 +08:00
cat-shark 87abde33d4 feat: subtitle-ocr 进度日志增加预计剩余时间 ETA
- 进度日志追加"预计剩余 X分Y秒"(剩余帧/当前速度,速度未知时不显示)
2026-08-23 16:25:18 +08:00
cat-shark 5688f38d62 fix: frame-extract 默认裁切区域改为画面底部 1/4
- 默认 crop 由 [0,0.82,1,0.18] 调整为 [0,0.75,1,0.25]:字幕很少出现在
  画面上半部分,扩大裁切高度提升 OCR 召回
- 同步更新 ocr-subtitle 工作流 DAG 与参数覆盖测试
2026-08-23 16:25:17 +08:00
cat-shark 3aa05bbf76 feat: whisper 分块间暂停检查与默认 float16 计算类型
- 分块转写在每个分块前检查 run 根目录 paused.flag,批量/任务暂停时
  分块粒度内中止(默认 60s 一块),当前块执行完才停
- 默认 compute_type 由 auto 改为 float16,测试断言同步更新
2026-08-23 16:25:15 +08:00
cat-shark 3b5bd42b60 feat: llm-filter 节点级断点存档
- 每条 LLM 判定成功立即追加 filter_partial.jsonl(多线程加锁串行化)
- 失败/中断后重跑只重判未判定条目,已判定结果复用,与 OCR 存档同机制
- 附带上下文净化回归重跑脚本(run_011d01f19999)
2026-08-23 16:25:13 +08:00
cat-shark cc707dda75 feat: 批量处理页面与导航
- 新增 web/batch.html + batch.js:目录树选择器(懒加载,本地后端
  提供 roots/dirs 接口)、工作流下拉、进度与产物下载
- 四个页面导航栏增加"批量处理"入口;styles.css 补齐批量页样式
2026-08-23 16:25:11 +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
cat-shark b16e0e9f3c feat: 任务断点续跑/暂停中断/LLM 过滤优化与调度容错
调度与状态机:
- 修复 PAUSED 任务被拾起后复活执行(点击暂停反而开始任务):next_queued_run
  只取 QUEUED,execute_run 以 PAUSED 进入直接返回,暂停必须显式 resume
- 重启恢复:启动时 recover_interrupted_runs 把遗留 RUNNING 置 QUEUED(保留产物)
- 暂停信号 paused.flag:暂停接口写、继续/重试清除,OCR 逐帧检查秒级中断,
  节点内被暂停保持 PAUSED 不误报 FAILED
- 调度轮询容错:_loop 异常不杀死线程(曾致任务永久停留 QUEUED)

subtitle-ocr 节点级断点:
- ocr_partial.jsonl 逐帧存档,重启/暂停后只处理未处理帧,产物与一次跑完一致
- 进度日志携带窗口平均耗时与线程数;取消后抑制进度日志井喷

llm-filter 过滤质量与限流自适应:
- 上下文净化:喂给 LLM 的是过滤后的字幕(规则层垃圾从上下文剔除)
- 正则确定性过滤:裸网址域名、HTML/水印模式直接删除
- 429/5xx 指数退避重试;worker 限流错误 report_failure 内存临时降最大线程数
  并缩容(无错误窗口回升),失败条目降并发后重试一轮
- 保留长文本保护(noise 不删 ≥min_keep_len 文本,LLM 判定不稳的必要兜底)

前端:
- 工作流编排页支持选择工作流编辑(加载最新/历史版本)、版本历史面板、
  新建/编辑双模式;管理后台编辑跳转 workflow.html?edit=<id>

工作流:ocr-subtitle v7(filter pool_max_workers=20、pool_fast_threshold=1)
2026-08-19 01:27:41 +08:00
cat-shark 4ebbfc5198 test: 多线程字幕顺序验证(全量真实数据夹具)+ 文档更新
- 新增 TestSubtitleOcrOrderThreading:模拟真实 vlm-ocr API 返回(不走真实
  OCR),全量 14236 帧真实数据 + 种子化可变延迟(约 8% 慢帧,模拟真实 OCR
  延迟波动),验证 4/16 线程产物与单线程确认结果逐字节一致、并发真实发生、
  完成顺序乱序但输出保序、时间轴严格递增、每条字幕与起始时刻帧文本对齐
- 全量夹具:frames_manifest_full.json + ocr_frames_full.json(真实任务
  run_ac7f480a3ccb 全部 14236 帧),常驻测试不跳过
- AGENTS.md:更新 llm-filter 参数说明与测试资产清单
2026-08-17 23:20:20 +08:00
cat-shark 5ffa2ac39e feat: llm-filter 两级过滤(规则层+五类分类+去重+长文本保护),工作流单线程 v4
- 规则层(不调 LLM):横线装饰/HTML 水印 token/URL/邮箱/单双 ASCII 字符直接删
- LLM 五类分类:garbage/overlay/noise 删,repeat/dialogue 留,未识别回退保留
- 按文本去重:相同文本只调一次 LLM(忽略空白/大小写),判定一致并省调用
- 长文本保护:≥min_keep_len 时 noise 不构成删除依据
- 真实任务 run_ac7f480a3ccb 验证:非规则误删 350→193(-45%),呻吟/对话保留
- ocr-subtitle 工作流 v4:pool 钉死单线程(1/1)
- 回归夹具 testdata/ocr_srt_run_ac7f480a3ccb.srt(真实 1666 条 OCR 输出)
2026-08-17 23:20:16 +08:00