本文へ移動
cccskills
無料GitHub で公開

lecture-to-md

把课堂视频(本地或 B 站/YouTube)、文字稿、课件三者(任意组合)整理成一份详细的中文 Markdown 课堂笔记,输出按课程标题命名的 `{title_name}.md`(首行为 `# 文档标题`)+ 相对路径图片。

Markdown 工作流,与上游 `lecture-to-notes` 的 LaTeX/PDF 输出并行存在;上游 skill 完全不动。触发词:markdown 笔记、md 笔记、视频转 markdown、Markdown 讲义、不要 LaTeX、不要 PDF、纯文本笔记。

インストール方法を見る

含まれるファイル(7)

  • SKILL.md23.4 KB
  • agents/openai.yaml360 B
  • assets/notes-prompt.md6.6 KB
  • references/splitting.md18.5 KB
  • references/structure-reorder.md16.8 KB
  • references/subagent-workflow.md8.5 KB
  • scripts/clean_subs.py4.0 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Lecture to Notes (Markdown)

把课堂视频(本地或 B 站/YouTube)、文字稿、课件三者(任意组合)整理成一份详细的中文 Markdown 课堂笔记,输出按课程标题命名的 {title_name}.md(首行为 # 文档标题)+ 相对路径图片。作为上游 lecture-to-notes LaTeX/PDF 流的 Markdown 平行方案;本 skill 与上游 skill 完全独立,不修改也不依赖上游 skill 的脚本。

子代理分工:素材获取(下载/ASR/抽帧/课件转 PNG)委派 S0 子代理并行做;主代理通读文字稿定死一级标题结构后,切素材(文字稿切片 / 帧按时段分目录 / 课件标页码)委派 S1 子代理做;然后每个撰写子代理领 1–3 个 H1 并行撰写 → 主代理汇总 → 对照文字稿重排目录结构(只改标题、不动正文)→ 交付。 切分与素材委派见 references/splitting.md,派发与汇总见 references/subagent-workflow.md,结构重排见 references/structure-reorder.md。

与 LaTeX 版的区别

lecture-to-notes (LaTeX)本 skill (Markdown)
输出.tex + PDF{title_name}.md + 图片
抽帧每 15s(按章节)严格每 5s
配图全帧 + contact sheet 人工选LLM 多模态按 ### 标题自动选图
配图优先级视频帧课件截图 > 视频帧
编译xelatex无需编译

子技能(asr 与云端转写)

本 skill 包内自带两个 ASR 子 skill,默认走本地 ASR(见下「ASR 选型」一节):

子 skill 路径何时用
local-asr/默认。无字幕时全文转写 / 短片段核验。已在 macOS Apple Silicon、Linux ARM64(CPU)与 Windows(PowerShell 5.1,CPU)端到端跑通。
volcengine-asr/备用。火山引擎豆包 BigASR / 豆包 2.0,需要 APP ID + Token;长音频首选异步模式

调用约定见各子 skill 的 SKILL.md,本 SKILL.md 不重复列参数。

ASR 选型

  • 首选 local-asr/(sherpa-onnx X-ASR,跨平台路径齐备,int8 量化 + Apple Silicon AMX 矩阵加速 ~100× 实时);调用时默认带 --timestamps,以保留字幕时间轴。
  • 若用户已自带文字稿(火山引擎豆包、飞书妙记等),跳过 ASR,直接走 Phase 1 的文字稿核验。
  • 火山引擎走 volcengine-asr/,把产出的 transcript.txt 当作 S0-a 的产物继续。

Dependencies

跨平台检查命令是否就位,缺则按平台提示用户安装(具体安装命令见各子 skill 的 setup 脚本):

平台检查命令备注
macOS / Linux shellwhich <cmd> 或 command -v <cmd>沙箱 PATH 可能不含 /opt/homebrew/bin(macOS)/ /usr/local/bin(Linux),见「已知坑」#1
Windows PowerShell 5.1 / cmdwhere.exe <cmd>which 不是 Windows 内建命令,PowerShell 5.1 也没有该 alias;只有装了 Git for Windows / msys2 时才会有 which.exe(实测)。Windows 沙箱 PATH 可能不含 winget links / scoop shims / Chocolatey bin,见「已知坑」#1

最简做法:跑 local-asr/scripts/common.py::require("<cmd>"),它内部已经做了 shutil.which 兜底 + 按平台补齐常见 bin 目录(macOS /opt/homebrew/bin、Windows winget links / %ProgramFiles%\ffmpeg\bin / ~\scoop\shims / %ProgramData%\chocolatey\bin 等),并返回绝对路径,避免子进程再吃 PATH 的亏。

工具必需用途
yt-dlp✓视频/字幕/元数据下载(YouTube + Bilibili)
ffmpeg✓5s 抽帧、音频提取(ffprobe 随 ffmpeg 一起装,Phase 4 判时长要用)
python3✓clean_subs.py
pdftoppm△课件 PDF → PNG
soffice (LibreOffice)△课件 PPT/PPTX → PDF
local-asr/△无字幕时全文转写 / 前 1min 核验;默认加 --timestamps 出 srt/vtt 时间戳
volcengine-asr/△调用火山引擎豆包 ASR(需要凭据)
sherpa-onnx (pip 包)△local-asr/ 后端依赖,首次跑 bash ../local-asr/scripts/setup.sh 自动装

已知坑(务必遵守)

  1. PATH 兜底:沙箱 PATH 可能丢以下常用 bin 目录——macOS /opt/homebrew/bin、/usr/local/bin、/opt/local/bin;Linux /usr/local/bin、~/.local/bin、/snap/bin;Windows %LOCALAPPDATA%\Microsoft\WinGet\Links、%ProgramFiles%\ffmpeg\bin、~\scoop\shims、~\scoop\apps\<app>\current\bin、%ProgramData%\chocolatey\bin。yt-dlp/ffmpeg/pdftoppm/soffice 一律用绝对路径,或先 require() 兜底(参考 ../local-asr/scripts/common.py::require,Python 脚本首选;它内部已按平台列好候选目录)。
  2. 帧序号 → 时间戳:verify_figures.py(LaTeX 版脚本)硬编码 ×15,本 skill 不复用该脚本。不要硬编码 帧时间 = (帧序号-1)×5——fps=1/5 的首帧可能不落在 0s、末尾帧可能被被丢、起始 PTS/掉帧都会让算术失真(实测 12s 片段只出 2 帧而非 3 帧)。帧时间一律由 ffmpeg 的 showinfo 记录真实 pts_time(见 Phase 1 的 S0-b)。
  3. ASR 时间戳选型:local-asr/ 默认 sherpa-onnx X-ASR 模型直接产出 token 级时间戳(transducer 模型自带)。S0-a 调用默认带 --timestamps(transcribe.py --timestamps,会写 srt/vtt);只有用户明确只要纯文本时才省略。若是已有稿子想补时间戳,跑一次 transcribe.py 音频 --timestamps,把生成的 srt 时间轴对齐到原文即可(sherpa-onnx 不需要独立的 ForcedAligner 模型)。

平台识别

匹配平台
youtube.com, youtu.beYouTube
bilibili.com/video/BV, b23.tvBilibili

B 站分 P(多 part)视频:先用 yt-dlp --flat-playlist --dump-json "<URL>" 列出所有 part,先问用户处理哪几 P 再下载。

输入矩阵(视频 × 文字稿 × 课件)

视频文字稿课件处理动作
✓✗✗取视频 → 先试官方字幕(CC→自动字幕),兜底 local-asr/ → 5s 抽帧配图
✓✓✗核验文字稿 → 5s 抽帧配图
✓✗✓取视频 → 先试官方字幕,兜底 local-asr/ → 课件截图优先 + 5s 抽帧(都要做)
✓✓✓核验文字稿 → 课件截图优先 + 5s 抽帧(都要做)
✗✓✗直接总结文字稿,无图
✗✓✓总结文字稿 + 课件截图
✗✗✓仅课件:暂时禁止 → 提示用户补视频或文字稿
✗✗✗询问用户至少提供一样
  • 视频来源:本地路径 → 直接用;B 站/YouTube URL → yt-dlp 下载。
  • 文字稿来源:路径/粘贴文本 → 直接用;否则下载官方字幕(CC → 自动字幕去重);再否则 local-asr/ 全文转写(如果用户指定了火山引擎走 volcengine-asr/)。
  • 课件来源:PDF / PPT / PPTX 路径。

工作流

工作目录约定

默认输出到当前仓库根目录下,沿用 <course_id>_<lecture>_<title>/ 子目录约定,例如 nju_os_01_intro/、cs336_01_tokenization/。

CRITICAL:后台/长命令一律用绝对路径(沙箱 shell 会重置 cwd,相对路径会写错地方)。

目录内布局(>30min 时 outline.md / parts/ 才会出现):

<outdir>/
  {video_name}.mp4
  frames/                 ← 全课程 5s 帧 + times.txt(唯一时间依据)
  slides/                 ← 课件每页 PNG,共享不复制
  outline.md              ┐
  parts/                  │ 子代理模式(>30min)才出现
    transcript_part_XX.txt│
    frames_part_XX/       │ 本时段帧 + 局部 times.txt
    part_XX.md            ┘
  {title_name}.md
  assets/{title_name}/    ← 最终配图(pXX_ 前缀隔离)

Phase 0 输入解析

按输入矩阵确定路径;缺输入先补齐,不空跑。三样都没有 → 用 AskUserQuestion 让用户至少提供一样。

Phase 1 素材获取(委派 S0 子代理,主代理不亲自下载)

素材获取是机械操作,且过程嘈杂(yt-dlp 试 cookie / 换格式 / 应对 bot 验证、ASR 长转写重试、ffmpeg 抽帧日志),一律委派给子代理,主代理只拿结果——既隔离主代理上下文,又能与读稿并行。

按输入矩阵决定派哪些 S0(缺什么派什么,本地已提供的直接跳过)。skills/lecture-to-md/ 是本 skill 的根目录,子代理用它定位脚本。

子代理干什么何时派产物
S0-a文字稿获取:官方字幕 → 自动字幕 → 去重清洗 → 兜底 ASR无文字稿输入transcript.txt(尽量带时间戳)
S0-b视频下载 + 严格 5s 抽帧 + times.txt有视频输入video.mp4 + frames/ + frames/times.txt
S0-c课件 PDF/PPT → 每页 PNG有课件输入slides/slide-*.png

三个 S0 彼此独立,在同一条消息里并行派发(subagent_type: general-purpose),下载 / 转写 / 抽帧同时跑。主代理趁这个空档等文字稿产出、为读稿做准备。

S0-a:文字稿获取

你是素材准备子代理,负责给一门课的视频/音频准备文字稿。

工作目录(绝对路径):{outdir}
仓库根目录:{repo_root}

## 任务
按下面优先级拿到文字稿,写到 {outdir}/transcript.txt(能带时间戳就带,保留时间戳):

1. 官方 CC 字幕:
   yt-dlp --write-subs --sub-langs "zh.*,en.*" --convert-subs srt --skip-download "{URL}"
2. 无 CC 时自动字幕(YouTube):
   yt-dlp --write-auto-subs --sub-langs "en" --convert-subs srt --skip-download "{URL}"
3. 自动字幕逐行重复 2-3 次,务必去重:
   python3 {repo_root}/skills/lecture-to-md/lecture-to-md/scripts/clean_subs.py subs.en.srt --stats
4. 无任何字幕 → 本地 ASR 全文转写(sherpa-onnx X-ASR,默认中文):
   python3 {repo_root}/skills/lecture-to-md/local-asr/scripts/transcribe.py "{abs_video_path}" --lang zh --timestamps
   首次跑会提示执行 bash setup.sh 装 sherpa-onnx + 下载 X-ASR 模型(约 200MB)。

## 输出(返回给主代理,200 字内)
- 文字稿路径 + 行数/字数
- 来源:官方字幕 / 自动字幕 / local-asr
- 是否带时间戳
- ASR 转写时长(若走 ASR)
- 任何异常(下载失败、bot 验证、字幕语言缺失、ASR 模型未下载等)

S0-b:视频下载 + 抽帧

你是素材准备子代理,负责下载视频并抽帧。

工作目录(绝对路径):{outdir}
仓库根目录:{repo_root}

## 任务
1. 下载视频(本地路径则跳过):
   yt-dlp -f "bestvideo+bestaudio/best" --merge-output-format mp4 -o "video.mp4" "{URL}"
   # YouTube 遇 bot 验证("Sign in to confirm you're not a bot")时追加:
   #   --cookies-from-browser chrome   (或 safari / firefox / edge)
2. 严格 5s 抽帧 + 记录真实时间(禁止按帧号推算,见 SKILL.md「已知坑」#2):
   mkdir -p frames
   ffmpeg -i video.mp4 -vf "fps=1/5,showinfo" frames/f_%05d.png 2> frames/showinfo.log
3. 生成「帧文件 → 真实秒数」映射表 times.txt:
   python3 - <<'PY'
   import re, glob
   pts = re.findall(r'pts_time:(\d+(?:\.\d+)?)',
                    open('frames/showinfo.log', encoding='utf-8', errors='ignore').read())
   n_png = len(glob.glob('frames/f_*.png'))
   assert len(pts) == n_png, f"帧数不匹配: showinfo {len(pts)} vs png {n_png}"
   open('frames/times.txt', 'w', encoding='utf-8').write(
       "".join(f"f_{i:05d}.png\t{t}\n" for i, t in enumerate(pts, 1)))
   PY
4. 报时长:
   ffprobe -v error -show_entries format=duration -of default=nw=1:nk=1 video.mp4

## 输出(返回给主代理,200 字内)
- video.mp4 路径 + 时长
- 抽帧张数 + times.txt 首末帧时间(首帧应≈0,末帧应≈时长)
- 任何异常(下载失败、bot 验证、抽帧中断等)

S0-c:课件转 PNG

你是素材准备子代理,负责把课件转成每页 PNG。

工作目录(绝对路径):{outdir}
仓库根目录:{repo_root}

## 任务
- PDF 直接转:pdftoppm -png -r 120 deck.pdf slides/slide
- PPT/PPTX 先转 PDF 再转 PNG:
  soffice --headless --convert-to pdf deck.pptx --outdir .
  pdftoppm -png -r 120 deck.pdf slides/slide

## 输出(返回给主代理,200 字内)
- slides/ 路径 + 总页数
- 文件名命名规则(slide-01.png … slide-NNN.png,数字即 PDF 页码)
- 任何异常(转换失败、缺页、字体乱码等)

工具绝对路径:沙箱 PATH 可能不含 /opt/homebrew/bin,子代理执行 yt-dlp/ffmpeg/pdftoppm/soffice 时同样要先 which 或用绝对路径(见「已知坑」#1)。

Phase 2 文字稿核验(主代理,仅当「视频 + 用户另给的文字稿」同时提供)

S0-a 已经拿到官方字幕(或 ASR 转写),主代理用它核验用户额外提供的文字稿是否与视频对得上:

  1. 有官方字幕 → 直接拿官方字幕比对用户文字稿;
  2. 无官方字幕 → 用 S0-a 的 ASR 结果(或 local-asr/ 只转写前 1 分钟)比对;
  3. 比对前 1min + 随机抽几段:主题/关键词明显对不上才算不一致(措辞不同属正常),明显不一致 → 停下用 AskUserQuestion 问用户是否继续;
  4. 都无法核验(网站访问不了 / 无官方字幕且 ASR 不可用)→ 记录「文字稿未核验」并继续,不阻塞。

Phase 3 验收素材(主代理,S0 产物必须亲自验)

委派不等于闭眼收结果——子代理可能抽帧失败却报「完成」、字幕下错语言、课件页码随手给个范围。主代理亲自跑几条命令验收,成本极低:

# ① times.txt 行数 == frames/*.png 数量(抓抽帧中途失败)
[ "$(wc -l < frames/times.txt)" = "$(ls frames/f_*.png | wc -l)" ] && echo "✓ 帧数一致" || echo "✗ 帧数不一致"

# ② times.txt 首行 ≈ 0、末行 ≈ 视频时长(抓 pts 偏移)
head -1 frames/times.txt; tail -1 frames/times.txt
ffprobe -v error -show_entries format=duration -of default=nw=1:nk=1 video.mp4

# ③ 文字稿非空、首尾合理
wc -l transcript.txt; head -5 transcript.txt; tail -5 transcript.txt

# ④ 课件 PNG 张数 == 页数(有课件时)
ls slides/slide-*.png | wc -l

frames/times.txt 是后续所有时间溯源的唯一依据:配图标注「画面时间」、子代理按时间区间取帧、大纲锚点换算,全部查这张表。

课件:直接用 S0-c 转出的每页 PNG(slides/slide-01.png …)。

Phase 4 结构与分片:决定单线程还是子代理

4.1 判定课程时长

ffprobe -v error -show_entries format=duration -of default=nw=1:nk=1 video.mp4

无视频(纯文字稿)时按字数估算:中文约 250 字/分钟、英文约 150 词/分钟。

  • ≤ 30 分钟 → 走 Phase 5-A:主代理单线程直出,不必拆分子代理。
  • > 30 分钟 → 必须走 Phase 5-B:先出大纲 → 按一级标题切分 → 派子代理并行撰写 → 主代理汇总。
    • 切分侧(大纲格式、切分规则、切素材命令)见 references/splitting.md
    • 派发侧(prompt 模板、汇总、校验)见 references/subagent-workflow.md

4.2 配图跟随撰写者:单线程时主代理自己挑图;子代理模式下选图下沉给各子代理——它们手上有本片段的完整上下文,选图比主代理隔空猜更准。

Phase 5 生成 md

读取 assets/notes-prompt.md 作为写作规范,产出 <outdir>/{title_name}.md。

  • 文件名与文档标题:{title_name} 取自课程标题(如 cs336_01_tokenization);文件第一行是文档标题 # <课程标题>,与文件名对应,不计入正文 H1 ≤ 10 的限额;
  • 图片用相对路径 assets/{title_name}/xxx.png;
  • 视频帧图下用 > 画面时间 00:12:31 标注来源(时间查 frames/times.txt);
  • 课件图标注页码(如 > 课件第 12 页)。

5-A 单线程(≤ 30 分钟)

用多模态模型按每个三级标题(###) 挑 1~多张最相关图:

  • 优先课件截图,课件缺失时用视频帧;无合适图则该 H3 不配图(不硬凑);
  • 选出的图复制到 assets/{title_name}/ 并统一命名(如 h3_01.png、h3_02a.png);
  • 然后一气写完全文。

5-B 子代理并行(> 30 分钟)

步骤谁做产物
① 通读文字稿,定结构主代理(不可跳过、不可委派)outline.md(H1 + 稿子锚点 + 帧区间 + 课件页码区间)
② 按一级标题切分主代理切分方案(写进 outline.md)
③ 切素材S1 子代理parts/transcript_part_XX.txt、parts/frames_part_XX/(课件不搬,只标页码)
④ 并行撰写 + 各自配图再启动N 个子代理parts/part_XX.md + assets/{title_name}/pXX_*.png
⑤ 按序汇总、统一风格主代理{title_name}.md → 接着做 Phase 6 结构重排

四条硬规则:

  1. 主代理必须先读完整个文字稿再定大纲——只有通读才能按课程真实结构划分,不能让子代理边读边自由发挥。
  2. 切分单位是一级标题,且大纲里的 H1 就是最终 {title_name}.md 里的 H1——子代理不得改名、增删、调序。每个子代理最多分 1–3 个一级标题。
  3. 素材按片段隔离:文字稿切进 parts/(相邻段重叠 30–60 秒保证衔接),视频帧按时段切进 parts/frames_part_XX/(帧不重叠,避免重复配图)。课件不复制——只在 prompt 里给路径 + 页码区间;课件常是整门课合订 PDF,不标页码子代理就会用到别的课时的页。
  4. 并行不共享文件:每个子代理只写 parts/part_XX.md 和自己前缀为 pXX_ 的图片,互不覆盖。

切素材是机械操作(sed 按行号切、srt 按时间切、帧按时段 awk+cp、课件页码定位),委派给 S1 子代理执行、主代理验收——见 references/splitting.md 第 4 节;派发 prompt 模板与汇总校验见 references/subagent-workflow.md。

Phase 6 目录结构重排(主代理,只改标题不动正文)

汇总出来的 H1 反映的是「切分块」,不一定等于「课程结构」——定大纲时可能会为了让各子代理工作量均衡,容易把大主题拆散、把小主题合并。这一环把失真修回来。

必做:5-B 子代理模式。建议做:5-A 单线程模式(同样可能切偏,跑一遍诊断代价很低)。

谁做主代理,且必须重新对照文字稿,不能只看 {title_name}.md 自己改自己
改什么只有标题行:调层级、改文字、删重复、在段落边界插新标题、同步序号
不改什么正文段落、公式代码、图片引用与图注、「画面时间」「课件第 N 页」标注
判据去掉所有标题行之后,重排前后的正文应逐字相同(diff 可验)

诊断信号(命中任一条就该重排):H1 带子层级编号(「2.3 …」「B2 …」)、H1 带续接词(「…(续)」「再谈…」)、相邻 H1 同属一个母题、各 H1 字数悬殊、H1 是具体技术点而非主题块。

完整诊断表、从文字稿识别真实母题的 5 类信号、执行步骤与自检命令见 references/structure-reorder.md。

Phase 7 交付

报告 {title_name}.md 与 assets/{title_name}/ 的绝对路径。(我们称这个 {title_name}.md 和 assets/{title_name}/)为成果,用户可能后续会要你移动这个到指定地方,你按这个粒度来理解成果或者产品)

复用资产

  • references/splitting.md:切分规范——何时启用、outline.md 格式、切分规则、S0/S1 素材子代理委派(prompt 模板 + 主代理验收)
  • references/subagent-workflow.md:子代理规范(派发与汇总)——prompt 模板、汇总校验、常见错误
  • references/structure-reorder.md:结构重排(Phase 6)——诊断切分失真、对照文字稿定位真实母题、只改标题行不改正文的两级自检
  • scripts/clean_subs.py:YouTube 自动字幕去重(✓ 复用,已复制到本 skill scripts/)
  • local-asr/:本地 ASR 子 skill(sherpa-onnx X-ASR,跨平台)——见 ../local-asr/SKILL.md
  • volcengine-asr/:火山引擎 ASR 子 skill(BigASR / 豆包 2.0,云端)——见 ../volcengine-asr/SKILL.md

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Use when users provide YouTube, Bilibili, or X/Twitter lecture URLs and want reader-first Chinese LaTeX/PDF notes with source-faithful claims, fluent authored prose, and verified teaching figures, especially requests phrased as lecture notes, 视频转PDF, 课程笔记, 讲义, YouTube笔记, B站笔记, X/Twitter lecture notes, or BV号; do not produce transcript dumps, quota-padded prose, or screenshot galleries.

日本語の概要は準備中です。原文の説明を表示しています。

ysyecust/lecture-to-notes2802026年10月3日 更新

local-asr

無料

把本地长视频/音频转写成文字稿 + 可选字幕,纯本地(不上传云端),用 sherpa-onnx X-ASR Zipformer transducer 模型(int8 量化、中英双语、自动标点)。已在 macOS Apple Silicon(int8 + AMX,~100× 实时)、Linux ARM64(CPU,~32× 实时)与 Windows(PowerShell 5.1,CPU)端到端验证。使用 `$lecture-to-md` 默认 ASR 后端;默认不要换成 Qwen。

日本語の概要は準備中です。原文の説明を表示しています。

ysyecust/lecture-to-notes2802026年10月3日 更新

Generate a self-contained, beautifully styled HTML analysis of an academic paper. Use when the user provides an arXiv link, paper URL, PDF, or asks to analyze a research paper. Produces a standalone HTML file with structured sections (Problem, Translation/Analogy, Architecture, Key Results, Verdict), embedded figures, and responsive design. Trigger words include 读论文, 分析论文, paper analysis, paper review.

日本語の概要は準備中です。原文の説明を表示しています。

ysyecust/lecture-to-notes2802026年10月3日 更新

Transcribe local audio or video with Volcengine Doubao file ASR, including BigASR 1.0 Turbo direct upload and asynchronous 1.0 standard, 1.0 idle, or 2.0 standard jobs through TOS. Use when the user asks for 火山引擎、豆包语音、录音文件识别、音视频转文字、录音稿或字幕。作为 `lecture-to-md` 的可选云端 ASR 子 skill;默认走本地 sherpa-onnx X-ASR,仅在用户明确要求云端 API 或需更高准确率时启用。

日本語の概要は準備中です。原文の説明を表示しています。

ysyecust/lecture-to-notes2802026年10月3日 更新

ysyecust のスキルをすべて見る

このスキルの問題を報告する