Files
universal-video-transcode/STATE.md
T
hoelee 99d03ff072 gate policy: frame-share is the verdict for owner-approved items; park after 2 failed rounds
Owner decision 2026-10-10. Eight items were livelocked in the nvenc tier, burning ~3.3
GPU-hours per pass (initial encode + quality+2 retry) and never converting: they missed the
mean bar by 0.2-1.7 thousandths (0.9626-0.9778) while keeping only 0.20-0.93% of frames
below 0.85 - and per this repo's own comment the FRAME-SHARE test is the one that actually
catches a defect, the mean bar splits a continuum. With SIZE_CEIL=1.0 the mean bar is
unreachable for them by construction.

- library_run.py: POLICY (C:\AI\transcode-run\gate_policy.json) with
  * accept - named items are judged by the frame-share test; a mean under the bar is logged,
    not enforced. An item that trips the frame-share test is refused even if listed
    (0912 洞见, 1.33%, is deliberately absent).
  * park  - failed quality ROUNDS per item; at PARK_AFTER=2 the item is skipped with a
    visible SKIP status instead of being re-encoded. Counters persist in the same file.
- scripts/build_gate_policy.py: builds that file from the run log, taking each key verbatim
  from the '--- i/n <relative path>' line (no hand-copying). Result: 7 accepted, 1 refused.
- run/relaunch_nvenc.sh: start the GPU tier ALONE (relaunch_tiers.sh hardcodes both tiers).
- STATE.md: the policy, its rationale and the round counter.
2026-10-10 17:22:45 +08:00

14 KiB
Raw Blame History

STATE — 樊登读书会媒体库转码(2026-10-09 11:40 更新)

给下一个会话的交接。权威运行器:scripts/library_run.py(不要再恢复旧的 hevc_lib.py)。

现在在跑什么

只有 x265 一档在跑(2026-10-09 20:51:41 起,单档启动器 run/relaunch_x265.sh)。 nvenc 档故意不启动 —— GPU 被 TubeSync 的 58 个文件修复批次占着(20:36 起,与铁律一致:同一颗编码器不能两个任务)。

# 只起 x265(E 核,纯 CPU)—— 仓库源 run/relaunch_x265.sh 拷到 /d/tmp/xcode/ 再跑
powershell -NoProfile -Command 'Start-Process -FilePath "C:\Program Files\Git\bin\bash.exe" -ArgumentList "D:/tmp/xcode/relaunch_x265.sh" -WindowStyle Hidden'
# ⚠ 参数必须只有脚本路径;写成 -ArgumentList "-lc","bash <脚本>" 会起一个交互式 bash,什么都不跑
tail -f /d/tmp/xcode/run_x265.log

2026-10-09 16:51:58:两档是整机重启停的,不是 bug

(两档都不在 Hermes 的进程管理里 → process_manage 看不到,也没有退出通知:只能看日志,按 PID 杀。) LastBootUpTime = 2026-10-09 16:51:58;x265 日志停在 16:50:42(404/589)、nvenc 停在 16:51:30 (308/589)。11:48 由 relauncher 重新拉起、跑到重启为止。锁文件里那两个 PID(23792 / 50128)已是死号 —— transcode.Lock 是过期感知的(age > 300 s 且 pid 已死即接管),不需要手工清锁。

重启后内存正常:物理空闲 12.9–13 GB、提交 50.3/55.9 GB(11:19 那次 MemoryError 时是 96.5%)。 ⚠ 但提交上限从 79.6 GB 缩到 55.9 GB:D: 已 92% 满,pagefile 长不上去。

为什么改成"脱管"启动 —— 10:37 那次全停的教训(第 9 个 bug)

上一轮的 x265 / nvenc 是 Hermes 后台进程。用户停会话时它们被一起杀了:两个都停在编码到一半 (stage 里 *_out.mp4 和 .err 停在 10:37,日志里没有 PASS-DONE),所以 persist_on_release 在这个 环境里不可靠。现在两档由 powershell Start-Process 拉起的独立 bash 启动,父进程立刻退出 → 进程树成为孤儿,Hermes 的清理碰不到它们。

代价与规矩:没有退出通知(看日志);停/杀只能按 PID,绝不用进程名批量杀。

进度(2026-10-08 10:50,库内共 589 个文件)

档 已转好 待转
copy 160 4(见下)
x265 13 169
nvenc 25 174
无需处理(已达播发标准) 44 —
合计 242 347
  • 预期:x265 ~3.5 天、nvenc ~3 天跑完各自那档(这一轮 10:43 才重新起来,10:37 被上面那次全停打断)。
  • 台账:C:\AI\transcode-run\ledger.jsonl(每项一行:分类/结果/大小/SSIM)。
  • 进度只认台账与 state,不看 .mp4 扩展名(TubeSync 重命名会把它改回 .mkv)。

2026-10-08 新增的 4 集"每周更新"(copy 档)

1004 反内卷 0912 感官觉醒 0905 如何成为情绪稳定的父母 0822 超赞的一代(都在 樊登读书2026年(更新中)): H.264 + mp3 音轨,其中 0822 超赞的一代 其实是 MPEG-TS 只是挂着 .mp4 名 → iPad 播不了, 需要 copy 档(视频逐位不变 + 音轨转 AAC)。

⚠ 不要跑整轮 --tier copy:copy 档唯一的跳过判据是"已经是 HEVC",而 copy 的产物是 H.264 —— 整轮会把 193 个已转好的 copy 文件全部重做一遍。改用单文件通道:

bash /d/tmp/xcode/convert_new4.sh     # 逐个 --tier copy --path <file> --keep-source,共用 run-copy.lock
  • 1004 反内卷 10:48:53 完成 ✓(535 MB → 535 MB,视频 100% 逐位不变,音频转 AAC,149 s,rc=0)

闸门策略文件(业主决定 2026-10-10):C:\AI\transcode-run\gate_policy.json

8 个项在 nvenc 档里反复失败、每轮白烧 ~3.3 GPU 小时(初始一次 + quality+2 一次),而且永远过不了:它们的 mean 差 0.2–1.7 个千分点(0.9626–0.9778),但帧占比只有 0.20–0.93% —— 按本仓库自己的注释,抓真缺陷的是 帧占比 >1% 那条,mean 那条只是在切一条连续分布。

{
 "accept": [ "7 个项(帧占比 ≤1%)的库内相对路径" ],
 "park":   { "8 个项": 1 }
}
  • accept:名单内的项以帧占比测试为准,mean 低于门槛只记录不拦截。帧占比 >1% 的项即使列在名单里也照样拒 (《2020年\0912 洞见》1.33% 故意不在名单里)。
  • park:每项失败轮数;达到 PARK_AFTER = 2 就 SKIP(不再重编码),要重试就把它的条目删掉。 没有这条,同样的 8 个项每趟都重演一次、永远转不出来。
  • 生成器:scripts/build_gate_policy.py(键逐字取自日志的 --- i/n <相对路径> 行,不手抄)。
  • 停/起的单档启动器:run/relaunch_nvenc.sh(relaunch_tiers.sh 会连带拉起 x265,别用它单独起 GPU 档)。

图片闸门:re-do 用 0.975(业主决定 2026-10-08)

第一轮实测:一趟里 11 次编码只差在 mean(0.9655–0.9795)就被拒,而被接受的那些帧最低分反而更差 (0.33–0.90)—— 阈值在切一条连续的分布,不是在分开缺陷(0718 马斯克原理:min 0.887、0.00% 帧 <0.85,照样被拒)。被拒的文件本来就已能播(HEVC hvc1 + AAC),re-do 的唯一目的是清掉旧设定的膨胀; 而要够到 mean 0.98 得花掉超过源片自身的码率,会被 size ceiling 拦住 —— 结果是文件连同膨胀被永久搁置, 而且每一趟还白烧一次编码。

新规则(library_run.SSIM_MEAN_MIN_REDO = 0.975):只有"重做一个已经在播的文件"才用 0.975,条件为 live = HEVC hvc1 且音轨是 AAC(真能播)且本次是从快照原档重做;首次转换仍用 0.98。 帧占比判据(>1% 帧 <0.85)两档都不变 —— 它才是抓真缺陷的那条:失控(0.9655)仍会被拒, 0808 陌生人效应(0.9739)因为 live 是 H.264、根本不是 re-do,也仍走 0.98。 先在一个文件上验证(0321 随他们去,上一轮 mean 0.9789 被拒)→ 见 /d/tmp/xcode/run_pilot.log。

目录与日志

用途 路径
运行日志 /d/tmp/xcode/run_x265.log、run_nvenc.log、run_copy.log、run_new4.log
脱管启动器 / 新集转换 /d/tmp/xcode/relaunch_tiers.sh、convert_new4.sh(+ relauncher.out);源在仓库 run/(改仓库那份再拷出来)
单文件日志 /d/tmp/xcode/run_single.log
暂存目录(编码/闸门 raw) C:\AI\transcode-run\stage(C: 盘,要留 ≥8 GB)
台账(每项一行 JSON) C:\AI\transcode-run\ledger.jsonl
锁(每档一个 + 心跳) C:\AI\transcode-run\run-<tier>.lock

本轮修掉的 bug(按时间顺序,全部已提交推送)

  1. 闸门判据太粗糙 → 改"缺陷形状"判:mean < 0.98 或 >1% 帧 <0.85 才判错,并剔除近平坦帧(SSIM 在黑场/淡入淡出上无意义;实测:好编码 0/1500、误判文件 0.33%、真缺陷 2.9%)。
  2. 闸门用输入 seek 解码 → 改两侧顺序解码(open GOP 源从中间 seek 会解出错误画面:同一文件与自身比对 min 0.25)。
  3. 闸门 raw 文件固定名 → 三个档并行时互踩(实测报出"100% 帧不符"的假缺陷)→ 改 PID + tag 命名。
  4. 安装只用 ffprobe 元数据复核 → RaiDrive 返回缓存属性,rename 没落地也说成功(实测"ok 1085→478 MB"而线上仍是原档)→ 改安装后回读哈希。
  5. 源拷贝只校验字节数 → 加 SHA-256 比对共享盘副本(长度对内容错是真风险)。
  6. probe 单次失败即判文件失败 → 加重试 3 次。
  7. 看门狗用 stderr 管道(本轮最贵的 bug)→ ffmpeg -stats 每 0.5 s 一行,64 KB 管道几分钟塞满 → ffmpeg 阻塞在 write() → 输出不增长 → 看门狗误杀好编码(整夜 ~270 杀、只转成 24 个、GPU 0%)。修:stderr 写文件,阈值 5→15 分钟。自检脚本 D:\dev\hoelee-tubesync-management\cache\watchdog_selfcheck.py。
  8. 看门狗保留(它要防的是真实场景:用户更新 NVIDIA 驱动会把在跑的 NVENC 冻死 1.5 小时且不报错)。
  9. Hermes 后台进程会随会话一起被杀 —— 两个档在 10:37 同时死在编码中,日志里没有 PASS-DONE;改脱管启动(见上)。

未完成 / 待办

  • 两档跑完 → 全库落地审计:D:\dev\hoelee-tubesync-management\cache\install_audit.py(核对线上文件真实大小 + 编码器;曾一次抓到"报告成功没落地")+ copy_land_check.py(copy 档用音轨 aac/mp3 判定)。
  • 失败项:无需清单,新一轮自动重做(失败时文件原样)。
  • 2022年/0723 《走出强迫症》/走出强迫症.mp4 用新下载(D:\Download\0723走出强迫症.mp4,618,696,954 B)已转好装好(450,502,893 B,HEVC+AAC,SSIM min 0.997,时长 3399.81 s)✓。快照里那份仍是坏的(618,697,011 B,moov 缺失)→ 待定:是否用干净副本替换快照里的坏档,以免将来"从原档重转"踩坑。
  • 目标文件夹里曾出现一个 0 字节的 0723走出强迫症.mp4(现已不在)—— 若是用户自建请告知命名约定。
  • 全库唯一真损坏:走出强迫症.mp4 的原档(上面已处理);其余 573 个可读文件截断审计 0 可疑。

用法速查

# 单文件(任意位置)转好并装回库,走同一套分档/闸门/回读
python scripts/library_run.py --path "D:/Download/x.mp4" --dest "Z:/.../x.mp4" --keep-source

# 只审计不落地(本地编码+闸门,输出丢弃)
python scripts/library_run.py --tier x265 --limit 3 --keep-local

# 分档预览
python scripts/library_run.py --dry-run --limit 20

第 10 个 bug:MemoryError 会在共享盘上留孤儿(2026-10-09)

现象:11:19:31 与 11:19:37 连续两次 !! FAIL MemoryError:(消息空白),两次相隔 6 秒 —— 一次是《崇祯传》装盘(写 493 MB 到 RaiDrive),一次是下一项读源。

根因是整机内存/提交额度耗尽,不是文件问题:

物理可用 0.0 GB / 48 GB         已提交 76.8 GB / 上限 79.6 GB(96.5 %)
pagefile d: 初始 8 GB / 上限 32 GB   —— 但 D: 只剩 19 GB(97 % 满),长不上去
大户:Chrome ≈4 GB、Hermes ≈3.7 GB(3 个进程)、upscayl 1.17 GB、whisper-server 0.83 GB、Discord 0.76 GB

copy_* / _sha_remote 当时只 except OSError,而 MemoryError 不是 OSError → 直接冒到 per-item 兜底;删半截 remote_tmp 的清理写在 except OSError 里 → 半截/完整的 .new.mp4 留在共享盘上。

已修(本轮,scripts/library_run.py):

  1. _sha_remote / copy_remote_to_local / copy_local_to_remote 的 except OSError → except (OSError, MemoryError)(瞬时抖动变重试,不是 FAIL);
  2. copy_local_to_remote 内的清理、读回不符时的清理同样放宽;
  3. per-item 兜底加了无条件删除 <dest>.new.mp4(用 os.remove + 忽略 FileNotFoundError,不用 os.path.exists,因为该共享盘的元数据可能是陈旧的)。

实测(注入 MemoryError):上传路径不再抛异常、半截文件被删;读源路径重试后抛干净的 IOError; _sha_remote 重试 2 次后抛出。

内存仍是硬前提:物理可用 0 GB / 提交 96.5 % 时重启,第一项照样会 FAIL(现在至少不留残留)。 重启前建议:关掉 upscayl / 多余 Chrome 标签 / Discord 腾出 5-6 GB;pagefile 上限别放在 97 % 满的 D:。

8 个 .new.mp4 残留(2026-10-09 11:35 逐个探过)

原档都在,没丢东西。7 个是半截前缀(内容早停 → 没用,删掉让下一轮重做); 只有一个是完整的、可以救回来:

文件 暂存 判定
2022年/0115《崇祯传》 493 MB ✅ 完整(3616.8 s 与源一致、内容到 3609 s)→ 校验后可改名安装

已处理(2026-10-09 12:10)

  • 《崇祯传》已安装:先过全部门才改名 —— 暂存档拷回本地并回读比对哈希(493,366,137 B,与共享盘副本一致) → 结构闸门 True → 全解码 True(唯一能证明没截断的检查)→ 图片闸门 min 0.9980 / mean 0.9988(1500 帧, 0 帧 < 0.85,远高于 0.98 的杆) → os.replace 改名 → 安装回读 sha256 5a903ab8a3d1(493,366,137 B == 本地) → 替换后闸门 hevc/aac、tag=hvc1、dur 3616.8 ✓。省下一次 3616 秒的重编码(822,288,651 B h264 → 493,366,137 B HEVC)。
  • 7 个半截残留已删(8 MB–165 MB,都是内容早停的前缀):0511原生家庭、20190601被管教的勇气、0622人生海海、 0807东京传、0527 洪业、» 0819 李白、1230 不安的哲学。它们对应的 live 原档一个没动,下一轮自动重做。
  • 共享盘上 *.new.mp4 现在 0 个。 | 0622人生海海 / » 0819 李白 / 0527 洪业 / 0807东京传 / 20190601被管教的勇气 / 1230 不安的哲学 / 0511原生家庭 | 8 MB–165 MB | ❌ 半截,删 |

红线(用户明确要求过)

  • 绝不删除/覆盖快照原档(#snapshot 是唯一的干净档案)。
  • 失败时不动 live 文件;只有"结构 + 全解码 + 图片闸门 + 上传回读 + 安装回读"全过才替换。
  • 杀进程只按 PID(绝不用进程名批量杀,taskkill /IM ffmpeg.exe 会连带杀掉别人的编码);一档一个锁;同一档不要跑两个实例。
  • 动 GPU(更新驱动/跑 ComfyUI/加载大模型)前先暂停 nvenc 档。