fix(downloader): yt-dlp 未设 retries 时零重试,一次网络抖动就让任务失败#428
Open
pumpkinperson996 wants to merge 1 commit into
Open
fix(downloader): yt-dlp 未设 retries 时零重试,一次网络抖动就让任务失败#428pumpkinperson996 wants to merge 1 commit into
pumpkinperson996 wants to merge 1 commit into
Conversation
## 问题
任意一次瞬时网络故障都会让整个笔记任务失败,即便立刻重试同一个链接就能成功。
实际遇到的报错:
ERROR: [download] Got error: HTTPSConnectionPool(
host='upos-sz-mirrorcosov.bilivideo.com', port=443): Read timed out.
同一个视频一分钟后重新提交,10.18 MiB 四秒下完。
## 原因
yt-dlp 文档里 `retries` 默认 10,但那个默认值是**命令行参数解析器**给的,
Python API 不套用。项目所有 `ydl_opts` 都没设 `retries`,于是:
# yt_dlp/downloader/http.py
for retry in RetryManager(self.params.get('retries'), ...) # -> None
# yt_dlp/utils/_utils.py
self.retries = _retries or 0 # -> 0
也就是**每次下载只尝试一次,零重试**。这不是 B 站独有的问题,
YouTube 以及所有走 yt-dlp 的路径都一样。
## 改动
- `base.py`:新增 `YDL_RETRY_OPTS`(`retries` / `fragment_retries` /
`socket_timeout`),两个 downloader 本来就从 base 导入,不额外引入模块。
- `youtube_downloader.py`(2 处)、`bilibili_downloader.py`(3 处):
所有 `ydl_opts` 都展开该常量。只修报错的那一处会把其余四处继续留在零重试。
- 用的是 yt-dlp 自带的重试机制,没有自写重试循环。
取值偏保守(3 次而非 CLI 的 10):笔记任务是用户在前台等的,
重试太久不如早点失败让用户重来。
## 测试
新增 `tests/test_ydl_retry_opts.py`:
- 行为:常量给出的 RetryManager 预算 > 0;并显式钉住
`RetryManager(None).retries == 0` 这个被规避的坑。
- 结构:用 AST 断言两个 downloader 里**每一个** `ydl_opts` 字面量都展开了
`YDL_RETRY_OPTS`,防止以后新增下载路径时又悄悄回到零重试。
结构用例确认过 red-green:去掉任一处展开即失败,并指出具体文件行号。
无 yt-dlp 的环境下两个行为用例自动 skip,与仓库既有测试风格一致。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
问题
任意一次瞬时网络故障都会让整个笔记任务失败——即便立刻用同一个链接重试就能成功。实际遇到的:
同一个视频(
BV12PD5BCEW2)一分钟后重新提交,10.18 MiB 四秒下完,2.29 MiB/s。CDN 抖了一下,任务就没了。原因
yt-dlp 文档里
retries默认是 10,但那个默认值由命令行参数解析器提供,Python API 不套用。项目里所有ydl_opts都没设retries,于是:每次下载只尝试一次,零重试。 这不是 B 站独有的:YouTube 以及所有走 yt-dlp 的路径都一样。
可以直接验证:
改动
base.pyYDL_RETRY_OPTS(retries/fragment_retries/socket_timeout)youtube_downloader.pyydl_opts展开该常量bilibili_downloader.pyydl_opts展开该常量tests/test_ydl_retry_opts.py用的是 yt-dlp 自带的重试机制,没有自写重试循环。常量放在
base.py是因为两个 downloader 本来就从它导入,不额外引入模块。只改报错的那一处(B 站音频下载)会把其余四处继续留在零重试,所以 5 处全部覆盖。
取值说明:3 次而不是 CLI 的 10。笔记任务是用户在前台等着的,重试太久不如早点失败让用户重来。维护者觉得该更耐受的话,改常量一行即可。
测试
RetryManager(None).retries == 0,把被规避的那个坑写进测试当文档。ydl_opts字面量都展开了YDL_RETRY_OPTS。这条是防止以后新增下载路径时又悄悄回到零重试——失败信息会直接指出文件和行号:结构用例确认过 red-green(去掉任一处展开即失败)。无 yt-dlp 的环境下两个行为用例自动 skip,与仓库既有测试的写法一致。
验证
改动在 Docker 环境里跑通过:
retries确实到达 RetryManager(预算 0 → 3),YouTube 与 B 站均能正常出笔记,其中 B 站用的正是上面那个曾经超时失败的视频。需要说明的是,重试本身只在真实瞬时故障发生时才会触发,我没有人为构造网络故障去观测重试计数——测试钉的是"预算不为 0"这个前置条件。