---
name: bilibili-douyin-notes
description: 把B站/抖音/YouTube/小红书视频整理成Markdown课堂笔记。用户发来 bilibili.com、b23.tv、douyin.com、youtube.com、youtu.be、xiaohongshu.com、xhslink.com 等视频链接（或分享口令）并要求做笔记、总结、转文字时使用。产出符合模板规范、经质量门禁校验的课堂笔记 .md 文件。
---

# B站/抖音/YouTube/小红书视频 → Markdown 课堂笔记

## 触发条件

用户消息中包含 bilibili.com / b23.tv / BV号 / douyin.com / v.douyin.com / iesdouyin.com / youtube.com / youtu.be（含 /shorts/、/live/）/ xiaohongshu.com / xhslink.com 链接（或抖音、小红书分享口令文本），且要求总结、做笔记、整理复习资料、提取视频内容等。

## 平台分流（第一步先看链接属于哪个平台）

- **B站** → `bili_fetch.py`：走官方API，优先字幕，按需下载音频/视频流
- **抖音** → `douyin_fetch.py`：游客身份（无需登录/SESSDATA），直接下载无水印视频到本地，一般无字幕 → 必走第二步ASR（抖音视频短，耗时可忽略）。需要 `pip install gmssl`；`abogus.py` 为 GPLv3 代码（源自 Evil0ctal/Douyin_TikTok_Download_API 经 BiliNote 移植），分发需保留其许可头
- **YouTube** → `yt_fetch.py`：字幕三级回退（youtube-transcript-api 人工→自动字幕 → yt-dlp json3 字幕 → 无字幕交 ASR），有字幕时不下载任何媒体；需要 `pip install yt-dlp youtube-transcript-api`，并建议安装 [deno](https://deno.com)（yt-dlp 解 YouTube JS 挑战所需）
- **小红书** → `xhs_fetch.py`：免登录、无签名，手机 UA 请求分享页并解析内嵌 `__INITIAL_STATE__`，下载视频到本地（默认 h264 转码流，`--origin` 下载体积大得多的原片）；**只支持视频笔记**，图文笔记脚本直接提示退出；链接必须带 `xsec_token`（让用户从 App「分享 → 复制链接」整段发来）；一般无字幕 → 必走第二步 ASR
- 四个平台的 metadata.json 字段一致，第二/三步脚本自动兼容：B站分P按 `bvid/cid` 按需下载，抖音分P带 `media_path` 直接用本地视频文件

## 工具位置

- 脚本目录：本 skill 安装目录下的 `scripts/`（命令中的 `<skill安装目录>` 占位符请替换为本 skill 实际安装路径）
- 配置文件：本 skill 安装目录下的 `config.json`（存放 SESSDATA；截图上传语雀用的 OSS 配置写在 `oss` 字段）
- 参考资产：
  - `references/note-template.md` —— 课堂笔记模板 v4（语雀直导版式：骨架、要点表达、语法契约、时间戳规范）
  - `references/asr-glossary.json` —— ASR 术语纠错词表（真实错法→正确写法）
  - `references/anchors.json` —— 截图关键词锚点表（按视频 ID 隔离；仅在不用 `--shots` 清单时兜底）

## 工作流程

### 第一步：抓取元数据 + 字幕（B站 / YouTube）或下载视频（抖音 / 小红书）

```bash
# B站（也支持 b23.tv 短链）
python "<skill安装目录>/scripts/bili_fetch.py" "<视频链接>" "<工作目录>/bili-notes-<BV号>"

# B站只探元数据：不抓字幕、不建目录、不写任何文件（先确认视频可访问/时长/分P数时用）
python "<skill安装目录>/scripts/bili_fetch.py" --probe "<视频链接>"

# 抖音（也支持 v.douyin.com 短链和整段分享口令文本）
python "<skill安装目录>/scripts/douyin_fetch.py" "<链接或口令>" "<工作目录>/douyin-notes-<视频ID>"

# YouTube（watch / youtu.be / shorts / live / embed 链接或 11 位 ID 均可）
python "<skill安装目录>/scripts/yt_fetch.py" "<视频链接>" "<工作目录>/yt-notes-<视频ID>"
# 可选：--lang zh-Hans,en（字幕语言优先级）--download（无字幕时下载音频供 ASR）
#       --cookies <cookies.txt> / --cookies-from-browser chrome（风控/年龄限制）--proxy http://127.0.0.1:7890

# 小红书（xhslink.com 短链 / explore / discovery/item 链接或整段分享文本，必须带 xsec_token）
python "<skill安装目录>/scripts/xhs_fetch.py" "<分享链接或分享文本>" "<工作目录>/xhs-notes-<笔记ID>"
```

- 两个脚本都自动生成 `<输出目录>/metadata.json`，stdout 最后一行是 JSON 摘要。
- **B站**：每个分P的字幕 `.txt`（带 `[mm:ss]` 时间戳），按摘要里每个分P的 **`subtitle_detail` 字段六值分流（cc / ai / ai_mismatch / none / api_empty / error，其中 cc 与 ai 处置相同）**：
  - `cc` / `ai` → 已拿到官方/AI字幕，直接进入第三步生成笔记（AI 字幕已由脚本自动做"内容↔标题 ASCII 词"交叉校验，错位时会判为 `ai_mismatch` 而不是 `ai`）；
  - `ai_mismatch` → AI 字幕整体错位（返回了 ai 状态但内容是别的音频，实测有配成无关电影对白的案例）：**不可信，直接进入第二步 ASR 兜底**，不要用该字幕生成笔记；存疑字幕已另存 `*.ai字幕存疑.txt` 备查，摘要带 `hint_mismatch` 时可向用户提一句；
  - `none` → 该分P确实没有字幕，直接进入第二步 ASR，不用再折腾；**但若摘要带 `hint_optional`（未配置登录态时可能出现），转述给用户**：「若该视频本应有 AI 字幕，可扫码登录（`--login`）后重跑，通常能跳过语音识别」——由用户判断要不要扫，不强制；
  - `api_empty` → 接口没返回可用字幕URL（`sessdata_loaded:false` 时常见，AI字幕需要登录）：**主动询问用户「要现在扫码登录B站吗？」，确认后代跑 `python "<skill安装目录>/scripts/bili_fetch.py" --login`（脚本生成二维码 PNG 图片并打印路径，把路径给用户打开扫码即登录，SESSDATA 自动写入 config.json）后重跑一次 fetch**；用户拒绝或重跑后仍是 `api_empty` 才走第二步 ASR 兜底；
  - `error` → 查看摘要里的 error 信息（可能是视频失效/风控），重试一次或直接告知用户。
- **抖音**：视频已下载到 `<输出目录>/media_p01.mp4`（无水印，本地已存在则跳过下载），无字幕，直接进入第二步 ASR。
- **YouTube**：按摘要里分P的 `subtitle_detail` 分流：
  - `manual` / `auto` / `ytdlp_manual` / `ytdlp_auto` → 已写出 `01_<标题>.txt`（`[mm:ss]` 格式，自动字幕已去滚动重复）+ `.done` 标记，直接进入第三步；`subtitle_lang` 为英文等非中文时，笔记用中文撰写、术语保留原文；
  - `none` → 无可用字幕：加 `--download` 重跑（下载音频到 `media_p01.m4a`），再进入第二步 ASR；
  - `blocked` → YouTube 拒绝了当前 IP：先建议用户配 `--proxy` 或 `--cookies-from-browser` 重跑；仍不行再 `--download` 走 ASR；
  - `error` → 看 stderr 日志，重试一次或告知用户。
  - 没有 `media_path` 的 YouTube 分P不能直接跑第二步（脚本不会去 B站接口下载），务必先 `--download`。
- **小红书**：视频已下载到 `<输出目录>/media_p01.mp4`（本地已存在则跳过下载），无字幕，直接进入第二步 ASR；时间索引与抖音一样无深链（保持纯文本）。

### 第二步（兜底）：语音识别转写

仅对缺字幕的分P转写（脚本自动跳过已有文本的分P）：

```bash
# 只转写第N个分P（推荐先转1个试听质量/估算耗时）
python "<skill安装目录>/scripts/bili_transcribe.py" "<工作目录>/bili-notes-<BV号>/metadata.json" --page 1

# 转写所有缺字幕的分P
python "<skill安装目录>/scripts/bili_transcribe.py" "<工作目录>/bili-notes-<BV号>/metadata.json"
```

注意事项：
- **设备与降级链**：自动检测 CUDA（有 NVIDIA GPU 用 float16，否则 CPU int8 + 批量推理）；cuda 加载失败自动回退 cpu，批量推理不可用自动回退逐段，高阶模型（medium/large）加载失败自动降一档直到 small——**每次落级都打印原因（stderr 警告 + JSON `fallback_reasons`），不静默**。检测异常时可用 `--device cpu` / `--device cuda` 强制指定；`--model` 未指定时读 config.json 的 `asr_model`（再默认 small），`asr_cpu_threads` 可调 CPU 线程数。
- **术语偏好注入**：脚本自动读词表（仓库层 + 用户层），把高频术语的正确词注入 ASR 的 initial_prompt——转写时就写对，比事后纠错更准。
- **耗时**：CPU int8 下 small 模型约为视频时长的 1~3 倍（批量推理可缓解；有 NVIDIA GPU 时快于实时），长视频（>1小时）耗时明显，转写前告知用户预计时间；长视频或对质量不满意时用 `--model medium` 重转（**先删对应的 txt**，否则脚本会跳过）。
- 首次运行会下载模型（small≈480MB，自动走 hf-mirror.com 镜像）。
- ASR 文稿没有标点分段质量保证，技术术语常被转错——生成笔记前用 `references/asr-glossary.json` 词表对照校正（见第三步第2条）。

### 第二步半（可选，只在用户要“带截图”时做）：抽取重点截图

用户没提截图就跳过本步。要截图时，**先完成第三步的正文，再回来按小节定截图**——
截什么由理解了内容的 Agent 决定，脚本只负责按清单抽帧：

1. 正文写完后，为需要画面佐证的小节（PPT 关键页、架构图、代码演示、对比表）写截图清单
   `<工作目录>/shots.json`，每小节 0~2 张、宁缺毋滥：
   ```json
   {"shots": [
     {"page": 1, "section": 2, "t": "05:12", "caption": "开闭原则两层含义", "name": "two-meanings"}
   ]}
   ```
   `page` 分P号、`section` 笔记 `## N.` 小节号、`t` 该分P**转写稿里真实存在的时刻**
   （先 grep 确认；不在转写稿 ±5s 内的条目会被脚本拒绝，进 `failed`，reason=`t_not_in_transcript`）、
   `caption` 图注、`name` 文件名（可选，缺省用 caption）。
2. 按清单抽帧：
   ```bash
   python "<skill安装目录>/scripts/bili_screenshot.py" "<工作目录>/metadata.json" "<工作目录>" "<工作目录>/images" --shots "<工作目录>/shots.json"
   ```
   输出 `images/p<页>_s<小节>_<name>.png` + `images/manifest.json`（记录 section）。
3. **逐张看图**（视觉复核）：画面与 caption 不符、是讲师脸或过渡帧 → 调整 `t` 重跑或删掉该条；
   再把截图以 `![<caption>](images/xxx.png)` 嵌入**清单里写的那个小节**、它佐证的要点旁边。

脚本行为：
- **多帧择优**：每个时刻向后取 3 张候选（间隔 4s），优先画质合格帧，再挑与相邻候选差异最小
  （画面已静止，避开 PPT 翻页过渡）的一张；
- **去重**：同一分P里与已选截图是同一画面（感知哈希接近且像素差小）的不落盘，记入进度 JSON 的 `duplicates`；
- **画质 QC**：亮度用像素占比法（近黑>97% 判 dark、近白>98.5% 判 bright——白底 PPT 不会误判），
  清晰度用拉普拉斯方差（<10 判 blurry）；每帧 `qc` 字段非 `ok` 时由 Agent 视觉复核决定是否采用；
- B站下载 480p DASH 视频抽帧后即删（填了 SESSDATA 自动取更高画质）；抖音 / 小红书复用本地视频不联网；
- **不给 `--shots` 时**退回关键词锚点：查 `references/anchors.json` 中**本视频 ID** 的条目
  （按视频隔离，别的课程的锚点不会串用），没有就用 `auto_anchors()` 从转写稿提取高频词——
  这种方式与小节无关、定位粗糙，仅作兜底；
- 截图文件名默认可中文；需要 ASCII 文件名时加 `--ascii-names`。

### 第三步：生成课堂笔记

> **使用场景定位（2026-10-21，v4）**：本 skill 解决的是“看完课不用自己做笔记”——
> 产出的是**课后复习资料**，正文按技术知识体系组织、阅读连贯：**正文零时间戳、
> 零考试向标签**（要点用 `+` 列表 + 关键术语加粗）；视频时间戳只存在于文末的
> “视频时间索引”，供回看原片时跳转。版式为**可直接导入语雀**的纯 Markdown：
> 无 frontmatter、无一级标题、答案与时间索引不折叠。

1. 读取 metadata.json（分P结构、时长）+ 各分P转写/字幕文本。
2. **ASR 术语校正（工具化，不是凭感觉）**：先跑
   `python "<skill安装目录>/scripts/apply_glossary.py" "<转写稿.txt>"`（默认 dry-run，按类别分组只出报告不改文件），按报告核对命中项，确认后再加 `--apply` 落盘（自动留 `.bak` 备份，命中词条 hits 自动累加）。词表分两层：仓库层 `references/asr-glossary.json`（编程/美食/游戏/通用大类）+ 用户层（输出目录 `glossary.user.json` 或 `~/.zcode/bdn-glossary.user.json`，同键用户层优先）；候选词命中只报告（标 `[候选]`）永不替换。**维护闭环**：发现词表未覆盖的新错法 → `--add "错法=正确词" --category 编程`（或 `--candidate` 先入候选、`--promote "错法"` 转正）；`--list` 查看全部；`--categories`/`--topic` 控制启用范围。注意：转写阶段已自动把高频术语注入 initial_prompt（见第二步），词表越丰富转写越准。
3. **长视频分块（写死流程，禁止跳过）**：单分P转写稿超过约 2 万字时——用 `scripts/transcript_chunk.py` 分块（默认 15000 字/块、末尾重叠 2 段，输出 `chunks/` 目录）→ **map**：逐块提炼该块要点清单 → **reduce**：把各块要点按小节层级合并汇总成最终笔记。**reduce 去重**：重叠区会在相邻块产生重复内容，合并时按 `[mm:ss]` 时间戳唯一化——同一时刻的要点/锚点只保留一次，跨块重复的锚点去重后再排进小节（该去重仅作用于分块合并的中间产物；正文按 v4 规则仍然禁时刻）。禁止跳过分块直接全文阅读长转写稿。
4. 按 `references/note-template.md`（**v4 模板**）生成一份 MD 文件，保存到用户工作目录，命名 `<序号>-<主题>.md`（如 `2.13-业务流程建模.md`；序号取课程选集号，或按用户已有笔记递增如 `01`；主题取视频标题关键词——文件名即语雀文档标题）。生成前必读该模板文件。v4 硬性要求：
   - **无 frontmatter、无 `#` 一级标题**：文件第一行就是引语块 `> 核心主线：<一句话知识主线>`（行尾两个空格换行），下一行 `> 课程来源：<视频标题>（B站 BVxxx，UP主：xxx，约 N 分钟）`，有代码仓库再加 `> 课程代码：`；笔记类型取 metadata.json 顶层 `suggested_note_type`（技术密度高的短片可改为 lecture，此时第 6、7 步加 `--note-type lecture`）；
   - **目录**：`+ [N. 小节名](#锚点)` 不带时刻，末尾固定列出 本讲知识地图 / 概念卡片 / 自测题（复习用）/ 视频时间索引；
   - **小节**：`## N. 小节名`（禁时刻后缀），**下一行紧接** `**核心思想：** <一句话>`（不空行），每小节 ≥3 行实质内容；要点用 `+ 要点句`、关键术语 `**加粗**`，**禁止三词标签前缀（核心必考/重点掌握/了解即可及任何变体）、禁止行内 `[mm:ss]`**；对比性内容优先用表格；小节之间可用 `---` 分隔；
   - **代码块**：真实语言 fence；来源标注可选——来自克隆仓库写 `# 来源：仓库 <子项目>/<文件路径>`（注释符随语言），按视频讲解组织的可写 `# 按视频讲解还原` 或不写；任何代码块内禁时刻；
   - **概念卡片**：`### 概念名`（禁标签后缀）+ `+ 定义：` / `+ 出现：` / `+ 依赖：` / `+ 易混：` 四行；`出现` 写作 `第 N 节「小节名」、第 M 节「小节名」`（≥2 个小节，节名与正文小节标题一致），禁时刻；
   - **自测题（不折叠）**：`N. <题干>？（对应：概念名）`（概念必须在概念卡片集合内：lecture 必须、short 可选），空一行后直接写 `**答案：** <至多一行>`，**不用 `<details>`**，禁时刻；lecture 4~7 道（short 2~3 道）；选题主打高频原理与面试易错点，禁 yes/no 题干；
   - **mermaid 知识地图 = 概念关系图，不是目录树**：` ```mermaid ` 代码块，推荐 `graph TD`；`R((主题))` 为根，**节点用概念卡片里的概念**（每张概念卡都要出现），小节名放在 `subgraph S1[分组名]` 标题上而不是当节点；**边带关系标签**表达概念之间怎么关联——`A -->|依赖| B`、`A -->|推出| B`、`A -->|组成| B`、`A -.->|易混| B`，lecture 带标签边 ≥ 概念卡数 − 1，关系尽量与概念卡「依赖 / 易混」一致；节点标签禁半角 ( ) [ ] { } 与引号、≤12 字（需要括号用全角）；导入语雀后自动渲染为图；
   - **视频时间索引**：文末最后一节，前接 `---`，**不折叠、不写 NAV 标记**；模型只写条目 `+ [mm:ss] <与正文一级小节同名>`（一级章节一条），**不写 URL**；时刻必须取自转写稿真实时间行（允许 ±5s 内小幅前移，禁止虚构）；lecture 必须有本节，short 可整体省略。
5. 笔记中引用的代码/命令必须以视频文稿内容为准；文稿未讲到的细节不要编造，可标注"（视频中未展开，建议补充）"。若视频简介里有代码仓库，克隆下来读取源码，把笔记中的代码示例换成仓库中的真实代码（首选做法）。
6. **填充时间索引深链（v4 笔记默认执行）**：
   `python "<skill安装目录>/scripts/note_nav.py" "<笔记.md>" --template v4 --meta "<metadata.json>" --txt-dir "<转写稿目录>"`
   （每个选集单独一份笔记时加 `--page <分P号>`）脚本幂等地把「## 视频时间索引」节内的 `+ [mm:ss] 标题` 条目原位填充为 `+ [mm:ss](URL) 标题`（B站生成 `?p=N&t=S` 深链，YouTube 生成 `watch?v=<id>&t=<秒>s` 深链，抖音 / 小红书无深链保持纯文本）；`note_type=short` 跳过本步；找不到时间索引节时 warning 跳过。v3 老笔记（frontmatter 写 v3）按 NAV 标记填链；v1 / v2 已不再支持。模型输出不含 URL，脚本输出不含自由文本，职责不重叠。
7. **质量门禁**：生成后运行
   `python "<skill安装目录>/scripts/validate_note.py" "<笔记.md>" --template v4 --meta "<metadata.json>" --txt-dir "<转写稿目录>"`
   （每个选集单独一份笔记时加 `--page <分P号>`）errors 必须为 0 才交付；warnings 酌情处理（如精简篇幅）。没有 frontmatter 的笔记默认按 v4 检查（`--template v4` 可省略）；v4 下出现 frontmatter / 一级标题、正文时刻、标签残留、`<details>` 折叠或 NAV 标记、时间索引缺失（lecture）、自测数量越界、概念卡「出现」格式非法等均为 error。frontmatter 写 `template_version: v3` 的老笔记按 v3 规则检查；写 v1 / v2 或缺版本号会直接报 `VERSION` 错误（v1 / v2 规则已移除）。报告 JSON 的 `errors` / `warnings` 为**对象数组**（每项含 `check` 检查编号与 `line` 行号）。
8. **截图上传 OSS（仅带截图的笔记）**：语雀导入 Markdown 不会上传本地图片，交付前先预演
   `python "<skill安装目录>/scripts/upload_images.py" "<笔记.md>"`（列出将上传的图片与目标 OSS 链接，不联网不改文件），
   确认后加 `--apply`：按内容哈希命名上传（远端已有则跳过，可重复执行），再把笔记里的 `images/...` 原位改写为 OSS 链接（留 `.bak` 备份）。
   OSS 配置读 `config.json` 的 `oss` 字段或环境变量 `OSS_ENDPOINT / OSS_BUCKET / OSS_ACCESS_KEY_ID / OSS_ACCESS_KEY_SECRET / OSS_PREFIX / OSS_PUBLIC_BASE_URL`
   （也可 `--env-file <xxx.env>`）；Bucket 需公共读或经 CDN 可公开访问。**门禁要在上传前跑**（上传后图片是网络链接，位置与图注校验会跳过）；
   缺配置时脚本只列出缺哪些键，不要向用户索要密钥明文，让用户自己填进 config.json 或环境变量。
9. **媒体清理（交付后必问用户）**：若输出目录存在缓存媒体（抖音的 `media_p01.mp4` 等本地视频，或残留的 `.part`/`.audio_*`/`.video_*` 临时文件），**询问用户「是否清理缓存的视频/音频文件？重新做笔记会重新下载」**：确认 → 先跑
   `python "<skill安装目录>/scripts/clean_media.py" "<输出目录>"`
   把将删除的清单给用户过目，再执行 `--apply` 实删；拒绝 → 跳过并说明缓存保留的意义（重跑截图/转写不再联网）。B站的临时音频/视频在流程中已自动删除，通常无残留。

## 笔记模板

**完整骨架与语法契约以 `references/note-template.md` 为准（生成前必读）**——全文含
lecture 完整骨架、时间戳规范、语法契约、short 差异表、截图嵌入规范、生成流程。
结构层次速览（节点内容以 `<…>` 占位）：

```markdown
> 核心主线：<一句话知识主线>       # 文件第一行；无 frontmatter、无 # 标题（文件名 <序号>-<主题>.md 即标题）
> 课程来源：<视频标题等>          # 有仓库再加 > 课程代码：<仓库地址>
## 目录              # + [N. 小节名](#锚点) 不带时刻；末尾固定列出 本讲知识地图 / 概念卡片 / 自测题（复习用）/ 视频时间索引
## N. 小节名 ×N      # 下一行紧接 **核心思想：** <一句话> + `+` 要点列表（术语加粗）+ 代码块 / 对比表格 +（带截图时）本节截图
## 本讲知识地图      # mermaid graph TD 概念关系图：节点=概念卡，subgraph 分组，边带关系标签
## 概念卡片          # ### <概念名>：+ 定义 / + 出现：第 N 节「小节名」 / + 依赖 / + 易混
## 自测题（复习用）  # 不折叠：题干后空一行写 **答案：**，lecture 4~7 道 / short 2~3 道
---
## 视频时间索引      # 不折叠、无 NAV 标记；条目 + [mm:ss] <章节名>，URL 由 note_nav.py 填充
```

## 常见问题

| 现象 | 处理 |
|---|---|
| 分P `subtitle_detail` 为 `api_empty` | 主动询问用户「要现在扫码登录B站吗？」，确认后代跑 `bili_fetch.py --login`（生成二维码 PNG 图片并打印路径）后重跑；用户拒绝或重跑后仍 `api_empty` 才走 ASR |
| 分P `subtitle_detail` 为 `ai_mismatch` | AI 字幕整体错位（内容是别的音频，看着通顺但与视频无关），fetch 已自动判出并按无字幕处理；直接走 ASR，不要试图沿用存疑字幕 |
| playurl 下载失败/音频为空 | 可能是风控，重试一次；仍失败则改用 `yt-dlp` 下载音频后手动喂给 transcribe 的 transcribe 函数思路 |
| faster-whisper 首次运行卡在下载 | 检查 HF_ENDPOINT 是否为 https://hf-mirror.com，或手动 `pip install -U huggingface_hub` |
| 转写文本术语错乱 | 用 `references/asr-glossary.json` 对照校正；词表未覆盖的新错法按同样格式补进词表 `map`，下次自动生效 |
| `subtitle_detail` 为 `error` | 看摘要里的 error 信息：视频可能已失效/被风控，重试一次或告知用户换视频 |
| 转写质量差（长视频尤甚） | 先删对应 txt，再用 `--model medium` 重转；有 NVIDIA GPU 时脚本会自动用 CUDA float16，也可 `--device cuda` 强制 |
| 抖音 detail 接口 403 / "无 aweme_detail" | 签名算法可能被抖音升级，参考 Evil0ctal/Douyin_TikTok_Download_API 或 JefferyHcool/BiliNote 更新 `abogus.py` / `WEB_SIGN_SALT`；另确认视频可公开访问（非私密/仅粉丝可见） |
| 抖音视频下载中断/文件过小 | 下载地址有时效，重跑 douyin_fetch.py 重新解析即可（本地已有完整 mp4 会自动跳过下载） |
| YouTube 报 "Sign in to confirm you're not a bot" / `blocked` | 云服务器 IP 常被 YouTube 拒绝：加 `--cookies-from-browser chrome`（或导出 cookies.txt 用 `--cookies`），必要时 `--proxy` 或在 config.json 配 `youtube_proxy` |
| YouTube 缺格式 / 下载失败 | 升级 `pip install -U yt-dlp` 并安装 deno（2025 年起 YouTube 需要 JS 运行时解挑战） |
| YouTube 年龄限制视频 | youtube-transcript-api 拿不到，需 `--cookies`/`--cookies-from-browser` 让 yt-dlp 回退取字幕 |
| 小红书提示缺少 xsec_token / 被重定向 / 无笔记数据 | 让用户在 App「分享 → 复制链接」把整段文本发来（token 有时效，过期重新复制）；笔记需公开可见 |
| 小红书提示风控验证（HTTP 461/471） | 请求过频触发验证码：稍后重试，或在 config.json 配 `xhs_cookie`（浏览器登录后复制 Cookie） |
| 小红书提示「这是图文笔记」 | 本工具只处理视频笔记，告知用户换视频笔记链接 |
| YouTube 正在直播 / 预告 | 脚本直接提示退出，等直播结束、回放生成后再试 |
| 环境缺依赖 | B站流程需 faster-whisper + av；抖音流程另需 `pip install gmssl`；YouTube 流程另需 `pip install yt-dlp youtube-transcript-api`；小红书流程无额外依赖 |
