f
This commit is contained in:
@@ -1954,7 +1954,7 @@ class Download_File(Cmdlet):
|
||||
height_selector = None
|
||||
if query_wants_audio:
|
||||
# Explicit audio request should map to best-audio-only selector
|
||||
ytdl_format = "ba"
|
||||
ytdl_format = "bestaudio"
|
||||
elif height_selector:
|
||||
ytdl_format = height_selector
|
||||
elif query_format:
|
||||
|
||||
@@ -907,8 +907,18 @@ class YtDlpTool:
|
||||
"fragment_retries": 10,
|
||||
"http_chunk_size": 10_485_760,
|
||||
"restrictfilenames": True,
|
||||
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36",
|
||||
"referer": "https://www.youtube.com/",
|
||||
}
|
||||
|
||||
base_options.setdefault(
|
||||
"http_headers",
|
||||
{
|
||||
"User-Agent": base_options.get("user_agent"),
|
||||
"Referer": base_options.get("referer"),
|
||||
},
|
||||
)
|
||||
|
||||
try:
|
||||
repo_root = Path(__file__).resolve().parents[1]
|
||||
bundled_ffmpeg_dir = repo_root / "MPV" / "ffmpeg" / "bin"
|
||||
@@ -1095,7 +1105,7 @@ class YtDlpTool:
|
||||
base_options["playlist_items"] = opts.playlist_items
|
||||
|
||||
if not opts.quiet:
|
||||
debug(f"yt-dlp: mode={opts.mode}, format={base_options.get('format')}")
|
||||
debug(f"yt-dlp: mode={opts.mode}, format={base_options.get('format')}, cookiefile={base_options.get('cookiefile')}")
|
||||
|
||||
return base_options
|
||||
|
||||
@@ -1729,6 +1739,31 @@ except ImportError:
|
||||
extract_ytdlp_tags = None # type: ignore
|
||||
|
||||
|
||||
def _is_http_403(exc: Exception) -> bool:
|
||||
msg_parts: list[str] = []
|
||||
try:
|
||||
msg_parts.append(str(exc))
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
cause = getattr(exc, "__cause__", None)
|
||||
if cause is not None:
|
||||
msg_parts.append(str(cause))
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
context = getattr(exc, "__context__", None)
|
||||
if context is not None:
|
||||
msg_parts.append(str(context))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
for msg in msg_parts:
|
||||
if "HTTP Error 403" in msg or "403: Forbidden" in msg or "403 Forbidden" in msg:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def download_media(opts: DownloadOptions, *, config: Optional[Dict[str, Any]] = None, debug_logger: Optional[DebugLogger] = None) -> Any:
|
||||
"""Download streaming media exclusively via yt-dlp.
|
||||
|
||||
@@ -1798,14 +1833,15 @@ def download_media(opts: DownloadOptions, *, config: Optional[Dict[str, Any]] =
|
||||
debug_logger.write_record("ytdlp-start", {"url": opts.url})
|
||||
|
||||
assert yt_dlp is not None
|
||||
info: Optional[Dict[str, Any]] = None
|
||||
session_id = None
|
||||
first_section_info: Dict[str, Any] = {}
|
||||
try:
|
||||
if not opts.quiet:
|
||||
if ytdl_options.get("download_sections"):
|
||||
debug(f"[yt-dlp] download_sections: {ytdl_options['download_sections']}")
|
||||
debug(f"[yt-dlp] force_keyframes_at_cuts: {ytdl_options.get('force_keyframes_at_cuts', False)}")
|
||||
|
||||
session_id = None
|
||||
first_section_info: Dict[str, Any] = {}
|
||||
if ytdl_options.get("download_sections"):
|
||||
live_ui, _ = PipelineProgress(pipeline_context).ui_and_pipe_index()
|
||||
quiet_sections = bool(opts.quiet) or (live_ui is not None)
|
||||
@@ -1820,6 +1856,40 @@ def download_media(opts: DownloadOptions, *, config: Optional[Dict[str, Any]] =
|
||||
with yt_dlp.YoutubeDL(ytdl_options) as ydl: # type: ignore[arg-type]
|
||||
info = ydl.extract_info(opts.url, download=True)
|
||||
except Exception as exc:
|
||||
retry_attempted = False
|
||||
if _is_http_403(exc) and not ytdl_options.get("download_sections"):
|
||||
retry_attempted = True
|
||||
try:
|
||||
if not opts.quiet:
|
||||
debug("yt-dlp hit HTTP 403; retrying with browser cookies + android/web player client")
|
||||
|
||||
fallback_options = dict(ytdl_options)
|
||||
fallback_options.pop("cookiefile", None)
|
||||
_add_browser_cookies_if_available(fallback_options)
|
||||
|
||||
extractor_args = fallback_options.get("extractor_args")
|
||||
if not isinstance(extractor_args, dict):
|
||||
extractor_args = {}
|
||||
youtube_args = extractor_args.get("youtube")
|
||||
if not isinstance(youtube_args, dict):
|
||||
youtube_args = {}
|
||||
if "player_client" not in youtube_args:
|
||||
youtube_args["player_client"] = ["android", "web"]
|
||||
extractor_args["youtube"] = youtube_args
|
||||
fallback_options["extractor_args"] = extractor_args
|
||||
|
||||
with yt_dlp.YoutubeDL(fallback_options) as ydl: # type: ignore[arg-type]
|
||||
info = ydl.extract_info(opts.url, download=True)
|
||||
except Exception as exc2:
|
||||
log(f"yt-dlp failed: {exc2}", file=sys.stderr)
|
||||
if debug_logger is not None:
|
||||
debug_logger.write_record(
|
||||
"exception",
|
||||
{"phase": "yt-dlp", "error": str(exc2), "traceback": traceback.format_exc()},
|
||||
)
|
||||
raise DownloadError("yt-dlp download failed") from exc2
|
||||
|
||||
if not retry_attempted:
|
||||
log(f"yt-dlp failed: {exc}", file=sys.stderr)
|
||||
if debug_logger is not None:
|
||||
debug_logger.write_record(
|
||||
|
||||
Reference in New Issue
Block a user