dfdf
This commit is contained in:
122
tool/ytdlp.py
122
tool/ytdlp.py
@@ -1,10 +1,14 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Sequence
|
||||
|
||||
from SYS.logger import debug
|
||||
from SYS.utils import ensure_directory
|
||||
from models import DownloadOptions
|
||||
|
||||
|
||||
def _get_nested(config: Dict[str, Any], *path: str) -> Any:
|
||||
@@ -140,6 +144,124 @@ class YtDlpTool:
|
||||
return self.defaults.audio_format
|
||||
return self.defaults.video_format
|
||||
|
||||
def build_ytdlp_options(self, opts: DownloadOptions) -> Dict[str, Any]:
|
||||
"""Translate DownloadOptions into yt-dlp API options."""
|
||||
ensure_directory(opts.output_dir)
|
||||
outtmpl = str((opts.output_dir / "%(title)s.%(ext)s").resolve())
|
||||
base_options: Dict[str, Any] = {
|
||||
"outtmpl": outtmpl,
|
||||
"quiet": True,
|
||||
"no_warnings": True,
|
||||
"noprogress": True,
|
||||
"socket_timeout": 30,
|
||||
"retries": 10,
|
||||
"fragment_retries": 10,
|
||||
"http_chunk_size": 10_485_760,
|
||||
"restrictfilenames": True,
|
||||
}
|
||||
|
||||
try:
|
||||
repo_root = Path(__file__).resolve().parents[1]
|
||||
bundled_ffmpeg_dir = repo_root / "MPV" / "ffmpeg" / "bin"
|
||||
if bundled_ffmpeg_dir.exists():
|
||||
base_options.setdefault("ffmpeg_location", str(bundled_ffmpeg_dir))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
if os.name == "nt":
|
||||
base_options.setdefault("file_access_retries", 40)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if opts.cookies_path and opts.cookies_path.is_file():
|
||||
base_options["cookiefile"] = str(opts.cookies_path)
|
||||
else:
|
||||
cookiefile = self.resolve_cookiefile()
|
||||
if cookiefile is not None and cookiefile.is_file():
|
||||
base_options["cookiefile"] = str(cookiefile)
|
||||
|
||||
if opts.no_playlist:
|
||||
base_options["noplaylist"] = True
|
||||
|
||||
fmt = opts.ytdl_format or self.default_format(opts.mode)
|
||||
base_options["format"] = fmt
|
||||
|
||||
if opts.mode == "audio":
|
||||
base_options["postprocessors"] = [{"key": "FFmpegExtractAudio"}]
|
||||
else:
|
||||
format_sort = self.defaults.format_sort or [
|
||||
"res:4320",
|
||||
"res:2880",
|
||||
"res:2160",
|
||||
"res:1440",
|
||||
"res:1080",
|
||||
"res:720",
|
||||
"res",
|
||||
]
|
||||
base_options["format_sort"] = format_sort
|
||||
|
||||
if getattr(opts, "embed_chapters", False):
|
||||
pps = base_options.get("postprocessors")
|
||||
if not isinstance(pps, list):
|
||||
pps = []
|
||||
already_has_metadata = any(
|
||||
isinstance(pp, dict) and str(pp.get("key") or "") == "FFmpegMetadata" for pp in pps
|
||||
)
|
||||
if not already_has_metadata:
|
||||
pps.append(
|
||||
{
|
||||
"key": "FFmpegMetadata",
|
||||
"add_metadata": True,
|
||||
"add_chapters": True,
|
||||
"add_infojson": "if_exists",
|
||||
}
|
||||
)
|
||||
base_options["postprocessors"] = pps
|
||||
|
||||
if opts.mode != "audio":
|
||||
base_options.setdefault("merge_output_format", "mkv")
|
||||
|
||||
if getattr(opts, "write_sub", False):
|
||||
base_options["writesubtitles"] = True
|
||||
base_options["writeautomaticsub"] = True
|
||||
base_options["subtitlesformat"] = "vtt"
|
||||
|
||||
if opts.clip_sections:
|
||||
sections: List[str] = []
|
||||
|
||||
def _secs_to_hms(seconds: float) -> str:
|
||||
total = max(0, int(seconds))
|
||||
minutes, secs = divmod(total, 60)
|
||||
hours, minutes = divmod(minutes, 60)
|
||||
return f"{hours:02d}:{minutes:02d}:{secs:02d}"
|
||||
|
||||
for section_range in str(opts.clip_sections).split(","):
|
||||
section_range = section_range.strip()
|
||||
if not section_range:
|
||||
continue
|
||||
try:
|
||||
start_s_raw, end_s_raw = section_range.split("-", 1)
|
||||
start_s = float(start_s_raw.strip())
|
||||
end_s = float(end_s_raw.strip())
|
||||
if start_s >= end_s:
|
||||
continue
|
||||
sections.append(f"*{_secs_to_hms(start_s)}-{_secs_to_hms(end_s)}")
|
||||
except (ValueError, AttributeError):
|
||||
continue
|
||||
|
||||
if sections:
|
||||
base_options["download_sections"] = sections
|
||||
debug(f"Download sections configured: {', '.join(sections)}")
|
||||
|
||||
if opts.playlist_items:
|
||||
base_options["playlist_items"] = opts.playlist_items
|
||||
|
||||
if not opts.quiet:
|
||||
debug(f"yt-dlp: mode={opts.mode}, format={base_options.get('format')}")
|
||||
|
||||
return base_options
|
||||
|
||||
def build_yt_dlp_cli_args(
|
||||
self,
|
||||
*,
|
||||
|
||||
Reference in New Issue
Block a user