video-transcribe — 视频全文转录成结构化 Markdown
把一段视频(本地文件或在线链接)转录成结构化 Markdown 文档:关键帧截图 + 音频全文转写(ASR)+ 画面文字识别(OCR)+ 精炼渲染 + 增强代码块 + 术语速查表,按语义单元分段并生成摘要。
概述
视频是"最难检索"的内容形态——必须完整看一遍才知道讲了什么。本 skill 把它变成可读、可检索、可二次加工的 Markdown,保留三大要素:
- 画面:按语义单元抽取关键帧截图(四层选帧,图片多而精),嵌入正文对应位置;
- 声音:本地 faster-whisper 全文转写,带时间戳,忠实还原口播内容;
- 文字:OCR 提取 PPT / 代码 / 界面上的文字。
特别针对受限环境做了降级设计:没有系统 ffmpeg、没有云端 ASR 密钥、没有视觉模型时,仍可用纯本地方案(imageio-ffmpeg + faster-whisper + rapidocr-onnxruntime)完成全流程。
> 本 skill 的原始场景是「上位机 / 工业自动化教程视频」(如 C# WPF / WinForms / PLC 实战),方法论对任意教程 / 课程 / 演讲视频通用。
适用场景
- 教程 / 课程视频:编程、工业自动化(上位机 / PLC)、办公软件(WPF / WinForms / Office)等教学视频,转录成带代码块的教程文档;
- 知识沉淀:长视频 / 系列课程,把"看过就忘"的内容沉淀为可检索的文档,便于后续 RAG / 二次蒸馏;
- 视频归档:把口播 + 画面内容整理成结构化笔记;
- 上位机 / 工业领域:设备调试、开发流程、控件使用等经验分享视频。
典型调用示例
不同说法都能触发本 skill,agent 据此决定调用哪个脚本、走几步:
| 用户说法 | agent 执行 |
|---|---|
| "把 XX 视频转录成文档" / "转录这个 B 站链接" | download_url.py → 全流程 |
| "转录这个本地文件 D:/xxx.mp4" | extract.py → 全流程(跳过下载) |
| "只给我 ASR 转写草稿" | 仅 asr.py 单步 |
| "我转录到一半断了" | 直接重跑 asr.py(断点续跑自动续) |
| "查看转写进度" | asr.py --status |
| "OCR 那步崩了怎么办" | 指向 troubleshooting「资源/内存类」 |
| "这是充电专属视频" | download_url.py --cookies cookies.txt |
| "把这个文件夹的视频全部转录" | batch_transcribe.py --dir <目录> --out <输出根>(+ watchdog.py 守护) |
| "视频是 AV1 格式抽帧崩了" | extract.py 已内置 ignore_err,仍崩则分段续抽 |
> 新用户只需自然描述需求,agent 会判断是"下载+全流程"还是"某一步重跑/查进度",并自动处理断点续跑。
核心能力
| 能力 | 实现 | 说明 |
|---|---|---|
| 在线视频下载 | yt-dlp |
支持 B 站 / YouTube 等;充电专属视频支持 Cookie 登录态 |
| 关键帧抽取 | ffmpeg + Pillow 平均哈希 | 场景检测去重 + 语义覆盖校验(保证不遗漏知识点) |
| 四层选帧 | 骨架 / 演进 / 最终 / 运行帧 | 图片多而精,避免只有"最终态"单帧 |
| 音频转写 | faster-whisper(本地) | 中文 ASR,输出带时间戳段落,无需云端密钥 |
| 断点续跑 + 进度查询 | asr.py 增量落盘 + --status |
转写中断重跑自动续、不从头;随时查进度 |
| 画面文字识别 | rapidocr-onnxruntime | 提取 PPT / 代码 / 界面文字 |
| 图文匹配复核 | OCR 关键词初筛 + 读图复核 + 白名单覆盖 | 逐段挑选最匹配帧,避免图文不符 |
| 精炼渲染 | 术语清洗 + 去口语化 + 跨段切句 + 自动补标点 | 把"一坨口语稿"变成"章节概述 + 精炼正文" |
| 增强代码块 | 标题 / 说明 / 口播时间戳 / 文件 / 阶段 / 🔑 关键点 | 代码↔口播双向锚点,重构场景用 diff |
| 术语速查表 | 内置 TERMS 词典 | 附注末尾自动生成术语对照表 |
| 友好错误提示 | errors.py 统一「❌原因 + ✅怎么办」 |
出错直接告诉用户下一步做什么,不必翻文档 |
| Markdown 输出 | 模板渲染 + 契约自检 | frontmatter + 章节 + 图片 + 代码块 + 摘要 + 附注 |
标准项目目录结构
流水线在一个项目目录内运行,目录结构如下(脚本输入输出均遵循此约定):
<项目目录>/
├── data/work/
│ ├── transcript_raw.json # 阶段3 ASR 输出:{"language","segments":[{"start","end","text"}]}
│ ├── frames/ # 阶段2 抽帧:frame_0001.png ...(每 N 秒 1 帧)
│ ├── keyframes/ # 阶段4 去重后关键帧
│ └── frames_ocr.json # 阶段5 OCR 输出:[{"frame","ts_sec","ts","ocr_lines"}]
└── output/
├── meta.json # 视频元数据(驱动 frontmatter,必填)
├── chapters.json # 章节边界 [{"标题","起始秒","结束秒"}]
├── chapters_overview.json # 章节概述 {"标题":"概述 Markdown"}
├── frame_map.json # [{"img","ts","frame","ts_sec"}]
├── code_blocks.json # 增强代码块 {"img名":{lang,file,stage,title,summary,speak_at,code,highlights}}
├── frame_match.json # 图文匹配白名单 {"章节标题":[img_XX,...]}
├── frame_match_report.json # 阶段7 match_frames.py 输出
├── resource/ # 最终图片 img_01.png ...
└── <标题>.md # 最终文档
工作流程(严格按序,共 10 阶段)
> 各阶段脚本均为 argparse 参数化,python <脚本> --help 可看用法。长耗时阶段(下载 / ASR / OCR)用后台进程跑,配合进度汇报(见「进度汇报规范」)。
阶段 0 · 环境准备(一次性)
无 ffmpeg 时用 imageio-ffmpeg 内置二进制;ASR 用 faster-whisper;OCR 用 rapidocr-onnxruntime。
python -m pip install imageio-ffmpeg faster-whisper rapidocr-onnxruntime yt-dlp -i https://pypi.tuna.tsinghua.edu.cn/simple
> 关键坑:pypi.org 下载 onnxruntime(13.8MB)经常超时 → 必须加 -i https://pypi.tuna.tsinghua.edu.cn/simple。
阶段 1 · 获取视频
- 本地文件:复制到 ASCII 短路径(WinRT / ffmpeg 对中文路径兼容性差,如
D:\vid.mp4)。 - 在线 URL:
scripts/download_url.py下载。B 站视频先调 API 拿元信息判断是否充电专属:
# ① 查元信息(标题/UP主/时长/cid/是否充电专属)
curl -s "https://api.bilibili.com/x/web-interface/view?bvid=BVxxxx" -H "User-Agent: Mozilla/5.0"
# ② 免费视频直接下载;充电专属(is_upower_exclusive:true)需 --cookies
python scripts/download_url.py "https://www.bilibili.com/video/BVxxxx" "<项目目录>" [--cookies cookies.txt]
> 下载后必须用 ffprobe 校验时长 ≈ API 返回时长,避免只下到 20 秒试看片段。
阶段 2 · 抽音 + 抽帧
python scripts/extract.py <视频路径> [帧间隔秒=3] <项目目录>/data/work
产出 audio.wav(16kHz 单声道,供 ASR)+ frames/frame_%04d.png(供 OCR / 关键帧)。
阶段 3 · ASR 转写(最耗时,后台跑 + 断点续跑)
# 模型未下载时先跑:python scripts/download_model.py <模型目录> small
python scripts/asr.py --audio <项目目录>/data/work/audio.wav --model <模型目录> --out <项目目录>/data/work/transcript_raw.json
# 查看进度(随时可查,不转写):
python scripts/asr.py --audio <项目目录>/data/work/audio.wav --out <项目目录>/data/work/transcript_raw.json --status
> 断点续跑(v3.1.3 起内置,中断不从头来):asr.py 每转写 20 段就增量落盘;若转写被中断(网络/超时/手动停),直接重跑同一条命令,脚本会自动读已保存结果、从最后一段时间戳截取剩余音频继续转写,只处理剩余部分。已转写到 40 分钟就不会再重跑前 40 分钟。
> 模型下载坑:HuggingFace 与 hf-mirror.com 在国内常被墙 → 用 ModelScope 下载(download_model.py 一键下载 Systran/faster-whisper-small,约 483MB)。
> 长耗时任务脱离会话:ASR / OCR 这类 20 分钟以上的任务,用独立脚本文件 + 系统后台启动(nohup python asr.py ... > asr.log 2>&1 & 或 PowerShell Start-Process)跑,不要用内联 python -c,这样即使 agent 会话中断,脚本也在后台继续跑完并落盘。
阶段 4 · 关键帧去重 + 语义覆盖校验
python scripts/dedup.py <帧目录> <关键帧目录> [哈希阈值=2]
去重后必须做语义覆盖校验(详见「关键方法论 → 语义覆盖校验」):用 ASR 分段数反查关键帧时间分布,对空白段补帧,保证每个语义单元 ≥ 1 张截图。
阶段 5 · 画面 OCR(ASR 完成后跑,避免内存竞争)
python scripts/ocr.py <关键帧目录> <项目目录>/data/work/frames_ocr.json
> 内存坑:faster-whisper 与 rapidocr 同时跑会内存竞争导致 OCR 崩溃 → 先跑完 ASR 再做 OCR;帧多时用「每帧独立进程 + sleep 2~4 秒」。
阶段 6 · 关键帧选取(四层选帧,图片多而精)
用 scripts/select_frames.py 从原始帧按「均匀抽样 + 关键时间点」选帧,复制到 output/resource/ 并生成 frame_map.json。每个语义单元按四层分类选帧(详见「关键方法论 → 四层选帧」):
python scripts/select_frames.py <帧目录> <项目目录>/output/resource <项目目录>/output/frame_map.json --step 20 --key-times 60,131,291,...
阶段 7 · 图文匹配复核(渲染前必做)
关键帧是"画面",正文是"口播",两者按时间戳挂载会出现图文不符(画面滞后/超前于口播,或讲师中途切换画面)。渲染前必须复核:
python scripts/match_frames.py <项目目录> # 产出 frame_match_report.json + 终端打印评分
流程:跑脚本拿候选帧评分 → 读图复核高分帧与零命中帧 → 逐章挑出画面与功能说明一一对应的帧(允许多帧展示细节 / 动态过程)→ 写 output/frame_match.json 白名单覆盖时间戳归属 → 清理白名单外的帧。
> 图片与小标题精确呼应(核心要求):每张截图必须与它所在的步骤子节小标题(### 步骤 X.Y 标题)内容准确对应——小标题讲「串口号 ComboBox」,配图就必须是串口号那一行的 ComboBox,不能是其他行的画面。选图时以"小标题说了什么 → 画面显示了什么"为准一一核对,而非只看时间戳。
> 选图完成后必须复核一次:全部图片选完、写入文档后,逐张回查「图片 ↔ 小标题」匹配度——画面内容是否与小标题描述的控件 / 步骤一致,发现不匹配立即换图。这一轮复核是交付前的硬性检查,不可跳过。
> 判定原则:宁可少而准,不可多而杂。一张画面与文字对应的帧,胜过三张对不上的帧。
阶段 8 · 精炼渲染
python scripts/render_refined.py <项目目录> <输出标题>
脚本自动完成:术语清洗 → 去口语化 → 重复词压缩 → 跨 segment 切句 + 自动补标点 → 实体 inline code 化 → 章节概述注入 → 关键帧按时间穿插(段落化输出)→ 增强代码块渲染 → 术语速查表。
> ⚠️ render_refined.py 输出的是「精炼素材稿」,不是最终稿:它把 ASR 口语切成通顺句子并清洗了术语,但仍是"逐句排列",未达到可交付的笔记质量。交付前必须按「关键方法论 ⑥ 正文记录规范」人工精炼——把逐句正文改写为「章节概述 + 步骤化精炼叙述」(不逐句记录口播)。渲染前需准备好以下输入文件:
| 文件 | 必填 | 说明 |
|---|---|---|
output/meta.json |
✅ | 视频元数据,驱动 frontmatter 与文首 |
output/chapters.json |
✅ | 章节边界 [{标题, 起始秒, 结束秒}] |
output/chapters_overview.json |
强烈建议 | 每章书面语概述(含 inline code),自动注入章首 |
output/code_blocks.json |
✅ | 增强代码块(OCR 对代码不可靠,需读图转写录入) |
output/frame_match.json |
可选 | 图文匹配白名单,覆盖时间戳归属 |
meta.json 字段:title / author / source_url / duration / platform / tags(数组)/ intro(一句话简介)/ lead_image(首图文件名,默认 img_01.png)/ subject(主题)。
增强代码块 schema(code_blocks.json 的值):{lang, file, stage, title, summary, speak_at, code, highlights?}
- lang — fenced 语言标签(xml / csharp / diff);重构对比写 diff(+ 新增 / - 删除)
- file — 代码所属文件名
- stage — init 骨架 / mid 填充中 / final 最终态 / refactor 重构后
- title — 代码块粗体标题(必填)
- summary — 一句话说明"做了什么、为什么"
- speak_at — 口播时间戳区间(如 01:14~02:06),代码↔口播锚点
- code — 作者原始代码,照抄不改(完整可运行片段,结合多帧拼全;不要规范化/美化,优化建议用 // 💡 或 <!-- 💡 --> 单独附注,不改原文)
- highlights — 关键点列表(每条用 🔑 开头)
读图转写 prompt 模板(约束代码转写质量):
你是 WPF/C# 教程视频的代码转写员。请阅读下列连续帧(同文件前后快照),按以下规则转写:
1. 【完整保留原始代码】忠实还原作者在视频里敲的原始代码——变量名、命名、结构、写法一律**照抄不改**,含 xmlns、属性、方法体、花括号配对。不要"规范化"、不要"美化"、不要替换作者的命名风格。
2. 【优化建议单独附注】若发现可优化之处(可读性、性能、更地道的写法),**不要改动原始代码**,用单独注释附注在代码旁,如 `// 💡 优化建议:此处可用 xxx 替代`(XML 用 `<!-- 💡 优化建议:... -->`),与原始代码清晰区分。
3. 【标注演进阶段】每段代码注明 init / mid / final / refactor。
4. 【补关键行注释】核心 API、魔法值、易错点用 `// ⭐` 或 `<!-- ⭐ -->` 行内标注一句中文说明。
5. 【还原口播解释】讲师口头解释的"为什么",浓缩成该行上方注释,不要丢。
6. 【诚实标注】看不清的行用 `// TODO: 转写推测` 标记,宁可留标记也不编造。
token 预算(硬约束):代码帧读图 ≤ 20 张/视频;每段代码读 2–3 帧(首帧定结构、尾帧补滚动内容);同文件连续帧只读首尾。
阶段 9 · 术语校对与三等校验
- 术语校对(agent 自动完成,无需用户参与判定):whisper 会把专业术语听错(如"上位机→商务机、ComboBox→Combogs、StaticResource→staticalresults")。agent 依据上下文 / 技术规范 / 代码截图,自动把这些误识修正为正确写法,全程无需用户本人判断或提供任何输入;
- 图片按正文顺序命名
img_01.png起; - 三等校验:图片引用数 = resource 文件数 = 实际嵌入数,缺一即报错。
关键方法论(保证质量的进阶要点)
① 语义覆盖校验(保证内容完整性)
哈希去重会过度合并「画面变化小但语义不同」的代码 / 属性帧。关键帧数量取决于语义单元数,不取决于时长:
- ASR 完成后识别语义单元(章节 / 控件 / 代码步骤),记录时间戳;
- 对照关键帧时间分布,找出「时间跨度 > 60 秒」的空白段;
- 对每个缺失语义单元,从原始帧按时间点补 1 帧(
帧序号 = 秒数/抽帧间隔 + 1); - 最终目标:每个语义单元 ≥ 1 张截图。
② 四层选帧(图片多而精)
每个语义单元的帧按四层分类选取,避免只有「最终态」单帧(50 分钟视频若只配 15 张图,平均 3.4 分钟才 1 张,严重不足):
| 层级 | 内容 | 选取时机 |
|---|---|---|
| 骨架帧 | 起始状态(空结构 / 空布局) | 章节开头 |
| 演进帧 | 代码填充的中间态 | 每个子步骤(如串口号→波特率→数据位…各 1 帧) |
| 最终帧 | 完成态 | 章节结尾 |
| 运行帧 | 界面运行效果(最高价值) | 验证 / 演示小节 |
- 运行效果帧优先:运行界面截图(下拉展开、勾选生效、断点命中)价值远高于满屏代码,选帧时给运行帧加权;
- 代码帧裁剪去噪:裁掉 IDE 顶部菜单栏 + 左侧解决方案资源管理器噪声,只留代码区;
- before/after 对比:报错 / 重构场景做对比图(报错截图 + 修复后截图并排),配合
diff代码块。
③ 图文匹配复核
见「阶段 7」。核心:OCR 关键词初筛(match_frames.py)→ 读图复核 → 白名单覆盖,解决"时间戳对齐 ≠ 图文对齐"的问题。
④ 关键词收窄(选帧时防过宽)
select_frames.py 若按关键词选"有意义"帧,关键词必须收窄为非代码术语(封面 / 需求文档 / 运行界面),否则会命中几乎所有代码屏导致选 100% 关键帧:
- ✅ 收窄(推荐):
["白话上位机", "智能灌装", "需求分析", "用例场景", "主页监控", "运行日志", "系统设置", "报警", "三色灯", "Typora"] - ❌ 禁止(过宽):
["导航", "MainWindow", "ViewModel", "Command", "ContentControl", "Style", "DataTemplate"]
⑤ 重新转录的必做项(同一视频再次转录 / 跨会话复用时极易漏)
每次"重新转录"同一视频,不要盲目复用上一轮的 chapters.json / chapters_overview.json / code_blocks.json:
- 章节边界必须按新 ASR 重新推导:旧时间边界可能与新 ASR 错位。拿到
transcript_raw.json后逐段扫[MM:SS] 文本找清晰转折点,重新划边界; code_blocks.json的 img 键名必须重映射:每次选帧按时间顺序重新编号img_NN.png,旧键名全部失效。按时间戳/帧号匹配,或重新读图转写;- 本次 Cookie 用完即删:不写入日志/记忆/回复,上次会话遗留的也要清。
⑥ 正文记录规范(第09课风格,不逐句记录)★
核心原则(优先级从高到低):
- 总结全面,不得缺失任何重要内容:完整性第一。宁可多写几笔,也不遗漏任何知识点 / 步骤 / 易错点;
- 可信度:不添加原文没有的信息(不脑补、不编造讲师没讲的内容),不遗漏原文已有的信息;
- 精炼性:不逐句复制 ASR 口播。whisper 输出的是口语碎片("好了,那我们…""对不对"),逐句拼接既啰嗦又难读。正文应按「章节概述 + 步骤化精炼叙述」组织,风格参考已交付的第09课文档(
第09课_Settings视图模型双向绑定与命令处理.md)。
正文组织格式(自上而下):
| 层级 | 格式 | 内容要点 |
|---|---|---|
| 章节标题 | ## 一、标题 ⏱ MM:SS |
按语义单元划分,时间戳取章节起始 |
| 章节概述 | 1 段书面语(2-3 句) | 本章做什么、为什么、涉及哪些技术 |
| 步骤子节 | ### 步骤 X.Y 标题 ⏱ MM:SS |
每个关键步骤独立成节 |
| 精炼正文 | 建议每步 4-6 句(参考,可灵活) | 做了什么 → 怎么做的 → 为什么,不逐句记口播 |
| 代码块 | fenced + <!-- stage: ... --> |
完整可运行片段,代码是"主角" |
| 截图 | 每步 1-2 张 | 紧跟在对应正文 / 代码后,按顺序嵌入 |
| 关键点 | - 列表 / 表格 |
核心属性、绑定、易错点归纳 |
| 章节分隔 | --- |
章节之间加分隔线 |
信息扩充点(解决"正文偏少")——每步正文在精炼的基础上补足:
- 补「为什么」:讲师没明说但隐含的设计理由(如"用
RadioButton而非Button,是因为导航同一时刻只能选中一个"); - 补「背景知识」:框架概念一句话解释(如
ObservableCollectionvsList、x:Key写与不写的区别、*星号的含义); - 批量信息用表格归纳:如五行参数绑定对照表、样式属性说明表,比逐条罗列更清晰;
- 关键代码补注释:核心 API、魔法值用
// ⭐或<!-- ⭐ -->标注。
句数参考与完整性:
- "每步 4-6 句"仅为参考性建议,非硬性规定,可灵活调整——内容多时多写、内容少时少写;判断标准是"这步的重要信息是否都覆盖了",而非句数是否达标;
- 一句话能讲清的不硬凑两段;每章 3-8 个步骤子节为宜;
- 技术术语一律 inline code(ItemsSource、SelectedItem),ASR 误识必须修正;
- 截图不是越多越好,每步 1-2 张「图与文对应」的帧即可(见 ③ 图文匹配)。
精炼流程(先全部转录、再分析、后精炼):
- 先全部转录:ASR 完整转写 →
render_refined.py生成素材稿(此时不做取舍,保证信息全量在案); - 再分析:通读素材稿,识别全部语义单元与重要信息点,列出「信息清单」(每章讲了什么、几个步骤、哪些代码/属性/易错点);
- 后精炼:按信息清单逐项精炼改写,写完后对照清单逐项核查——每一项重要信息都有对应正文 / 代码块 / 截图,无遗漏、无编造。
> 截图挑选同样适配上述原则:先保留全部候选帧(先全部转录)→ 按语义单元分析哪些帧承载了独立信息(再分析)→ 精选「图与文对应」的帧嵌入(后精炼)。完整性优先(每个重要知识点至少 1 帧),不添加与内容无关的帧。同时要求:每张图与对应步骤子节小标题精确呼应,选图完成后必须逐张复核一次匹配度(详见「阶段 7」)。
正文示例(第09课风格 + 扩充):
### 步骤 2.2 串口号(第一行) ⏱ 06:52
通信参数区的每一行都是一个 `TextBlock`(参数名)+ 一个 `ComboBox`(下拉)。先写第一行「串口号」:`TextBlock` 用 `VerticalAlignment="Center"` 垂直居中,`ComboBox` 同时绑定数据源和选中项。为什么用 `ComboBox` 而不是直接输入?因为串口列表来自系统枚举,用户从合法端口里选,比手输更不易出错。`Grid.Row="0"` 指定它在第一行,`Grid.Column="1"` 放在参数名的右侧一列。
```xml
<TextBlock Text="串口号" Grid.Row="0" Grid.Column="0"
VerticalAlignment="Center" Foreground="White"/>
<ComboBox Grid.Row="0" Grid.Column="1"
ItemsSource="{Binding PortNames}"
SelectedItem="{Binding SelectedPortName}"/>
两个关键绑定:
- ItemsSource="{Binding PortNames}":下拉的数据源来自 ViewModel 的 PortNames 集合;
- SelectedItem="{Binding SelectedPortName}":选中的项双向绑定到 SelectedPortName——界面选择会写回 ViewModel,反之 ViewModel 修改也会刷新界面。这也是"双向绑定"在本课的核心体现。
## 防断开机制(长任务稳定性保障:看门狗 + 心跳 + 检查点)
> ⚠️ **强制要求(防断开,不可跳过)**:凡涉及长耗时阶段(下载 / ASR / OCR / 批量转录)的任务,**必须启用看门狗守护**——
> - 批量 / 多视频 / 跨会话任务:启动命令必须带 `--guard`(`batch_transcribe.py` 自动拉起看门狗),或手动先启动 `watchdog.py` 再跑任务;
> - 单视频长 ASR / OCR:必须用后台进程跑(`nohup` / `Start-Process`)并带 `--progress` 心跳(`asr.py`);
> - 未启用守护即运行长任务,视为未遵守本规范——进程被杀/会话中断导致的进度丢失由执行方负责。
长耗时阶段(下载 / ASR / OCR)在后台跑,可能因会话中断、系统回收、内存不足被杀死。防断开机制让任务**可中断、可续跑、可自动恢复**(经验来自批量转录实战复盘),由三个脚本能力支撑:
| 机制 | 脚本 | 作用 |
|---|---|---|
| 阶段检查点 | `progress.py` | 每阶段完成写 `progress.json`,重跑自动 skip 已完成阶段,不从头再来 |
| 心跳 | `progress.py`(配合各脚本) | `last_heartbeat` 定期刷新;ASR 每转一段刷一次,供看门狗判断进程是否卡死 |
| 看门狗 | `watchdog.py` | 定期巡检目标进程:死亡自动重启;心跳陈旧(默认 1800s)强杀防卡死;`--single-instance` 防多守护叠加 |
| 批量编排器 | `batch_transcribe.py` | 多视频依次转录:阶段检查点 + 3 次退避重试 + 子进程超时 + 进度看板 |
### 用法
```bash
# ① 后台启动批量编排(脱离会话,防止连带被杀)
python scripts/batch_transcribe.py --dir <视频目录> --out <输出根> > batch.log 2>&1 &
# ② 起看门狗守护(进程死亡自动重启续跑;重复启动会自动单例退出)
python scripts/watchdog.py --cmd "python scripts/batch_transcribe.py --dir <视频目录> --out <输出根>" \
--progress <输出根> --single-instance --logfile watchdog.log
# ③ 随时查进度(单视频项目或全局看板)
python scripts/progress.py <某视频项目目录> --status
python scripts/batch_transcribe.py --dir <视频目录> --out <输出根> --dashboard
关键经验(实测沉淀)
- 心跳阈值必须覆盖最长合法静默阶段:OCR 是单进程阶段(期间不刷心跳),
--stale默认 1800s 足够;若某阶段单步可能超过阈值,应在该阶段循环内逐段刷心跳(batch_transcribe.py的 ASR 即如此); - 守护进程必须单例:多个 watchdog 叠加会导致多个转录进程同时写同一音频段、损坏状态文件——务必
--single-instance; - 只写不删:受限环境下删除操作可能被拦截(safe-delete shim),编排器只落盘不清理,靠检查点 skip;
- 先小样验证再放量:批量前先用 1 个短视频跑通全流程,再放全部视频;
- 定时汇报:agent 无法在无消息时自主发言——用 automation 定时任务(系统最小粒度 HOURLY,可用
BYMINUTE=0,20,40变通到每 20 分钟),或遵守「进度汇报规范」; - 暂停 / 恢复:随时停掉进程,重跑同一条命令即从检查点续跑,无损。
防短机制(输出完整性保护,防止"生成内容过短 / 输出不完整")
流水线各产出环节(ASR / 选帧 / 渲染)都可能因输入质量、环境或中断导致"产出过短"——转写只覆盖半段、截图只有一两张、正文只有寥寥几句。防短机制在关键产出落盘后做内容量校验,低于阈值默认告警(不阻塞、不中断断点续跑),必要时 --strict 硬失败,并生成校验报告供人工复核。
三层防线
| 层 | 环节 | 校验点 | 失败处理 |
|---|---|---|---|
| 脚本层 | render_refined.py(阶段8) |
每章正文 ≥ 50 字、全文 ≥ 300 字、正文图片 ≥ 1 张(均可参数覆盖) | 默认 warn 告警;--strict 时 fail |
| 脚本层 | asr.py(阶段3) |
转写覆盖率(末段 end / 音频总时长)≥ 70% | warn 告警(不中断断点续跑) |
| 脚本层 | select_frames.py(阶段6) |
选帧总数 ≥ 下限(默认 3,--min-frames 覆盖) |
warn 告警 |
| 兜底层 | render_refined.py |
空章节渲染占位提示(已有);校验报告 completeness_check.json |
不编造正文,只标注待人工核对 |
| 指令层 | agent 精炼(阶段9) | 交付前按「信息清单」逐项核查 + 字数/截图自检 | 见下方「交付前自检清单」 |
参数(均有默认值,兼容原调用方式)
python scripts/render_refined.py <项目目录> <标题> [--strict] [--min-chars-per-chapter=50] [--min-total-chars=300] [--min-frames-total=1]
python scripts/select_frames.py <帧目录> <resource输出> <frame_map> --step 20 --key-times 60,... [--min-frames 3]
- 阈值默认值适用于 5~60 分钟教程视频;短视频 / 静音片段触发告警属正常,人工核对
output/completeness_check.json后放行即可; - 校验报告:
output/completeness_check.json(每章字数、占位标记、图片数、问题清单、passed)。
交付前自检清单(agent 精炼环节强制)
- 信息清单逐项核查:通读素材稿先列「信息清单」(每章讲什么、几个步骤、哪些代码/属性/易错点),精炼后逐项对照——每一项都有对应正文 / 代码块 / 截图;
- 字数自检:每章正文 ≥ 50 字(纯占位章节除外)、全文 ≥ 300 字,低于阈值先复核"是否真遗漏"再决定是否放行;
- 截图自检:每个重要知识点 ≥ 1 张对应截图,正文图片数 ≥ 1;
- 空章节复核:
_(本节无语音内容)_占位章节必须确认是"视频本身无语音"而非"ASR / 去噪误删"; - 三等校验:图片引用数 = resource 文件数 = 实际嵌入数(已有,保留)。
进度汇报规范(强制,贯穿全程)
任务开始后,每 10 分钟主动向用户汇报一次进度,直到任务全部结束。不得等用户追问。
每次汇报包含四项:① 当前阶段 ② 已完成(完成度)③ 下一步 ④ 预计剩余。
实现要点:长耗时阶段用后台进程跑 + 非阻塞轮询,每 10 分钟醒来汇报一次;阶段切换时即时汇报;优先用后台任务完成通知驱动。
注意事项(关键坑,均已实测)
网络类
- pypi 超时:onnxruntime 等大包从 pypi.org 反复超时 → 清华镜像
-i https://pypi.tuna.tsinghua.edu.cn/simple。 - HuggingFace 被墙:模型下载改用 ModelScope(
Systran/faster-whisper-small)。
视频类
- AV1 流抽帧崩溃:B 站 AV1 视频(
av1/libaom-av1)ffmpeg 可能在某帧解码崩溃 →-err_detect ignore_err+ 从崩溃点后-ss <秒> -start_number <N>分段续抽。 - 充电专属视频:
is_upower_exclusive: true未登录只有 20 秒试看 → 需用户导出 Cookie(yt-dlp --cookies)。凭证用完必须删除,不写入日志/记忆/回复。
资源类
- ASR 与 OCR 内存竞争:两者同时跑会导致 OCR 每 ~5 帧崩溃 → 先跑完 ASR 再做 OCR,OCR 帧多时用「每帧独立进程 + sleep」。
- 下载后必须校验时长:ffprobe 确认时长 ≈ API 返回时长。
文件系统类
- 中文 / 特殊字符路径:ffmpeg / WinRT 对中文路径报错 → 复制到 ASCII 短路径。
- img 文件"补一个丢一个":批量复制图片时偶发文件系统延迟丢失 → 一次性
copy2全部 + 立即校验编号完整性,缺号立即补。 - 后台任务
/tmp不可见:后台子进程里写/tmp临时文件可能读取不到 → 用内联列表(ls ... | while read)替代临时文件。
目录结构(skill 自身)
video-transcribe/
├── SKILL.md # 本文档(核心)
├── README.md # 上架展示页(门面)
├── _skillhub_meta.json # 上架元数据
├── references/
│ └── troubleshooting.md # 疑难排查(按类别详列现象+解法)
└── scripts/
├── errors.py # 统一友好错误提示(❌原因 + ✅怎么办)
├── download_url.py # 阶段1 下载在线视频(yt-dlp,含 --cookies)
├── download_model.py # 阶段0 从 ModelScope 下载 whisper 模型
├── extract.py # 阶段2 抽音 + 抽帧(ffmpeg)
├── asr.py # 阶段3 ASR 转写(faster-whisper,增量落盘+断点续跑+--status)
├── dedup.py # 阶段4 关键帧去重(平均哈希)
├── ocr.py # 阶段5 画面 OCR(rapidocr,增量保存)
├── select_frames.py # 阶段6 四层选帧 → resource + frame_map
├── match_frames.py # 阶段7 图文匹配复核(OCR 关键词评分)
└── render_refined.py # 阶段8 精炼渲染(术语清洗+切句+嵌帧+增强代码块)
Markdown 输出模板
---
title: <视频标题>
author: <作者>
source_url: <链接>(或 source_file)
duration: <时长>
generated_at: <YYYY-MM-DD>
platform: <Bilibili / YouTube>
tags: [..]
---
# <总标题>
> 一句话简介

## <章节1标题> ⏱ MM:SS
章节概述段(1 段书面语,2-3 句)
### 步骤 1.1 标题 ⏱ MM:SS
精炼正文(建议每步 4-6 句,参考可灵活;保证总结全面)……

```xml
<!-- stage: mid —— 说明 -->
<...完整代码.../>
- 🔑 关键点 1
- 🔑 关键点 2
<章节2标题> ⏱ MM:SS
……(结构同上)
全文摘要
- 章节1:……
- 章节2:……
附注
- 作者 / 主题 / 转录方式 / 图片来源 / 整理日期 / 标签
术语速查
| 术语 | 说明 |
|---|---|
StaticResource |
静态资源引用,语法 {StaticResource Key} |
| ... | ... |
| ``` |
> ⚠️ 模板是构建契约,不是可选说明:render_refined.py 内置「模板契约自检」,输出缺失 frontmatter / 一句话简介 / 首图 / 章节时间戳 / 全文摘要 / 附注 任一即告警。