Merge pull request #428 from pumpkinperson996/fix/ytdlp-zero-retries

fix(downloader): yt-dlp 未设 retries 时零重试,一次网络抖动就让任务失败
This commit is contained in:
Jianwu Huang
2026-08-25 13:57:33 +08:00
committed by GitHub
4 changed files with 145 additions and 2 deletions
+13
View File
@@ -13,6 +13,19 @@ QUALITY_MAP = {
"slow": "128"
}
# yt-dlp 的 `retries` 默认值(10)是命令行参数解析器给的,Python API 不套用它:
# 不显式设置时 HttpFD 拿到的是 `self.params.get('retries')` == None,而
# `RetryManager.__init__` 做的是 `self.retries = _retries or 0`——也就是
# 一次都不重试。任何一次网络抖动(例如 B 站 CDN
# upos-sz-mirror*.bilivideo.com 读超时)都会让整个笔记任务直接失败。
#
# 这里的值偏保守:笔记任务是用户在前台等的,重试太多不如早点失败让用户重来。
YDL_RETRY_OPTS = {
"retries": 3,
"fragment_retries": 3,
"socket_timeout": 30,
}
class Downloader(ABC):
def __init__(self):
@@ -7,7 +7,7 @@ from typing import Union, Optional, List
import yt_dlp
from app.downloaders.base import Downloader, DownloadQuality, QUALITY_MAP
from app.downloaders.base import Downloader, DownloadQuality, QUALITY_MAP, YDL_RETRY_OPTS
from app.downloaders.bilibili_dm_patch import apply_bilibili_dm_img_patch
from app.downloaders.bilibili_subtitle import BilibiliSubtitleFetcher
from app.models.notes_model import AudioDownloadResult
@@ -63,6 +63,7 @@ class BilibiliDownloader(Downloader, ABC):
output_path = os.path.join(output_dir, "%(id)s.%(ext)s")
ydl_opts = {
**YDL_RETRY_OPTS,
'format': 'bestaudio[ext=m4a]/bestaudio/best',
'outtmpl': output_path,
'http_headers': {'Referer': 'https://www.bilibili.com'},
@@ -122,6 +123,7 @@ class BilibiliDownloader(Downloader, ABC):
output_path = os.path.join(output_dir, "%(id)s.%(ext)s")
ydl_opts = {
**YDL_RETRY_OPTS,
'format': 'bv*[ext=mp4]/bestvideo+bestaudio/best',
'outtmpl': output_path,
'http_headers': {'Referer': 'https://www.bilibili.com'},
@@ -183,6 +185,7 @@ class BilibiliDownloader(Downloader, ABC):
video_id = extract_video_id(video_url, "bilibili")
ydl_opts = {
**YDL_RETRY_OPTS,
'writesubtitles': True,
'writeautomaticsub': True,
'subtitleslangs': langs,
@@ -5,7 +5,7 @@ from typing import Union, Optional, List
import yt_dlp
from app.downloaders.base import Downloader, DownloadQuality
from app.downloaders.base import Downloader, DownloadQuality, YDL_RETRY_OPTS
from app.downloaders.youtube_subtitle import YouTubeSubtitleFetcher
from app.models.notes_model import AudioDownloadResult
from app.models.transcriber_model import TranscriptResult
@@ -47,6 +47,7 @@ class YoutubeDownloader(Downloader, ABC):
output_path = os.path.join(output_dir, "%(id)s.%(ext)s")
ydl_opts = {
**YDL_RETRY_OPTS,
'format': 'bestaudio[ext=m4a]/bestaudio/best',
'outtmpl': output_path,
'noplaylist': True,
@@ -100,6 +101,7 @@ class YoutubeDownloader(Downloader, ABC):
output_path = os.path.join(output_dir, "%(id)s.%(ext)s")
ydl_opts = {
**YDL_RETRY_OPTS,
'format': 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]',
'outtmpl': output_path,
'noplaylist': True,