Skip to content

fix(youtube): 修复 YouTube 笔记生成失败 "Requested format is not available"#427

Open
pumpkinperson996 wants to merge 1 commit into
JefferyHcool:masterfrom
pumpkinperson996:fix/youtube-format-not-available
Open

fix(youtube): 修复 YouTube 笔记生成失败 "Requested format is not available"#427
pumpkinperson996 wants to merge 1 commit into
JefferyHcool:masterfrom
pumpkinperson996:fix/youtube-format-not-available

Conversation

@pumpkinperson996

Copy link
Copy Markdown

问题

输入 YouTube 链接生成笔记时任务必定失败:

ERROR: [youtube] <id>: Requested format is not available. Use --list-formats for a list of available formats

即使字幕已经抓取成功(日志里能看到 成功获取 YouTube 字幕,共 1021 段),任务仍然在下载阶段崩掉,最终 TaskStatus.FAILED

原因

两个独立的问题叠在一起:

1. yt-dlp 被钉在 2025.3.31backend/requirements.txt

YouTube 之后轮换过 player,该版本解不出 nsig 签名,所有音视频格式被丢弃,只剩 storyboard 图片:

WARNING: [youtube] nsig extraction failed: Some formats may be missing
WARNING: Only images are available for download. use --list-formats to see them
ERROR: [youtube] Requested format is not available

2. "只取元信息"的路径却要求选出音频格式

有字幕时 NoteGenerator 不下载音视频,只调 download(skip_download=True) 拿 title/duration/cover。但 YoutubeDownloader.download 无条件设置了 format='bestaudio[ext=m4a]/bestaudio/best',导致一个根本不需要媒体流的调用也会因选不出格式而抛错——把一个字幕已经到手的任务整个带崩,随后回退到"完整下载"再挂一次。

第 2 点是放大器:只修 yt-dlp 版本的话,下次 YouTube 再变、yt-dlp 再落后时,同样的全量失败会重演;修掉之后,有字幕的视频会降级为"没有音频"而不是任务失败。

改动

文件 改动
youtube_downloader.py skip_download 时设 ignore_no_formats_error=True
youtube_downloader.py ext = info.get("ext") or "m4a":跳过下载时 yt-dlp 返回 ext=Nonedict.get 的默认值对显式 None 不生效,会拼出 xxx.None
requirements.txt yt-dlp==2025.3.31>=2026.7.4
bilibili_dm_patch.py wrapper 透传 **kwargs(见下)
tests/ 新增 YouTube 元信息路径测试;补充 B 站 kwargs 透传测试

关于 yt-dlp>= 而不是 ==

yt-dlp 是对抗 YouTube 持续变化的滚动依赖,精确钉版本本身就是这个 bug 的成因——钉到 ==2026.7.4 只是把同一个定时器重置一遍。同文件的 youtube-transcript-api>=1.0.0 已有先例。如果维护者倾向保持 ==,我可以改。

⚠️ 升级 yt-dlp 会连带打断 B 站(本 PR 一并修了)

这点值得单独说明。升级后 yt-dlp 的 _real_extract 会以 fatal=False 调用 _download_playinfo,而 bilibili_dm_patch.py 的 wrapper 钉死了签名,于是所有 B 站下载都会抛:

TypeError: _patched_download_playinfo() got an unexpected keyword argument 'fatal'

wrapper 已改为透传 **kwargs,对新旧 yt-dlp 都成立。顺带一提:yt-dlp 2026.x 已经在 _download_playinfo 里原生注入 self._dm_params,这个 monkey-patch 现在是冗余的——但本 PR 不动它,那是另一件事,需要单独验证。

验证

真实环境跑通(Docker,fast-whisper + DeepSeek):

  • YouTube(有字幕 → 元信息路径):原本必失败的链接现在正常出笔记
  • B 站(无字幕 → 完整下载 + 转写):正常出笔记
  • 真实音频下载单独验证过(format 140 m4a),确认无字幕的 YouTube 视频也能走通
pytest tests/  →  46 passed, 3 skipped, 1 failed

唯一失败的 test_task_serial_executor::test_executor_runs_tasks_one_by_one 在升级前后表现完全一致(我在 yt-dlp==2025.3.31 的干净容器里复现过),与本次改动无关,因此未作改动。

新增测试均确认过 red-green:在未修复的代码上会失败,且 B 站那条复现的正是上面production 里的 TypeError

## 问题

输入 YouTube 链接生成笔记时任务必定失败:

    ERROR: [youtube] <id>: Requested format is not available

即使字幕已经抓取成功(日志里能看到"成功获取 YouTube 字幕,共 N 段"),
任务仍然在下载阶段崩掉。

## 原因

两个独立的问题叠加:

1. `requirements.txt` 把 yt-dlp 钉在 `2025.3.31`。YouTube 之后轮换过
   player,旧版 yt-dlp 解不出 nsig 签名,所有音视频格式都被丢弃,只剩下
   storyboard 图片(日志:`Only images are available for download`),
   格式选择随即抛错。

2. 有字幕时 `NoteGenerator` 走的是"只取元信息"的路径
   (`download(skip_download=True)`,只要 title/duration/cover),但
   `YoutubeDownloader.download` 无条件设置了
   `format='bestaudio[ext=m4a]/bestaudio/best'`。于是一个根本不需要媒体流的
   调用,也会因为选不出格式而失败——把一个字幕已经到手的任务整个带崩。

## 改动

- `youtube_downloader.py`:`skip_download` 时设置
  `ignore_no_formats_error=True`。yt-dlp 落后于 YouTube 时,只取元信息的路径
  降级为"没有音频",而不是让整个笔记失败。
- `youtube_downloader.py`:`ext = info.get("ext") or "m4a"`。跳过下载时
  yt-dlp 返回 `ext=None`,`dict.get` 的默认值对显式 None 不生效,
  会拼出 `xxx.None` 这样的路径。
- `requirements.txt`:`yt-dlp==2025.3.31` → `>=2026.7.4`。yt-dlp 是对抗
  YouTube 变化的滚动依赖,精确钉版本本身就是这个 bug 的成因;用 `>=` 与同文件
  的 `youtube-transcript-api>=1.0.0` 保持一致。
- `bilibili_dm_patch.py`:wrapper 改为透传 `**kwargs`。升级 yt-dlp 后
  `_real_extract` 会以 `fatal=False` 调用 `_download_playinfo`,而 wrapper
  钉死了签名,导致 **所有 B 站下载** 抛
  `TypeError: ... got an unexpected keyword argument 'fatal'`。
- 测试:新增 `test_youtube_metadata_only.py` 覆盖上面两条 YouTube 保证;
  `test_bilibili_dm_patch.py` 新增未知 kwargs 透传用例,并让 fake 响应带上
  `code` 字段(yt-dlp 2026.x 会先校验信封再返回 data)。

## 验证

- 真实跑通:YouTube(有字幕,走元信息路径)与 B 站(无字幕,走完整下载 +
  转写)均能生成笔记。
- `pytest tests/` → 46 passed。唯一失败的
  `test_task_serial_executor` 在升级前后表现一致,与本次改动无关,未作改动。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant