diff --git a/.vscode/settings.json b/.vscode/settings.json index de180aac..dc24bfef 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -160,6 +160,7 @@ "rejecttitle", "remux", "reqs", + "restrictfilenames", "RPAREN", "rtime", "rvfc", diff --git a/app/library/Utils.py b/app/library/Utils.py index ead7069b..37f541f3 100644 --- a/app/library/Utils.py +++ b/app/library/Utils.py @@ -96,29 +96,34 @@ class FileLogFormatter(logging.Formatter): return datetime.fromtimestamp(record.created).astimezone().isoformat(timespec="milliseconds") -def get_static_ytdlp(reload: bool = False) -> YTDLP: +def get_ytdlp(params: dict | None = None) -> YTDLP: """ Get a static YTDLP instance for info extraction. Args: - reload (bool): If True, forces re-creation of the instance. + params (dict|None): YTDLP parameters. Returns: YTDLP: A static YTDLP instance. """ global YTDLP_INFO_CLS # noqa: PLW0603 - if YTDLP_INFO_CLS is None or reload: - YTDLP_INFO_CLS = YTDLP( - params={ - "color": "no_color", - "extract_flat": True, - "skip_download": True, - "ignoreerrors": True, - "ignore_no_formats_error": True, - "quiet": True, - } - ) + + default_params: dict[str, Any] = { + "color": "no_color", + "extract_flat": True, + "skip_download": True, + "ignoreerrors": True, + "ignore_no_formats_error": True, + "quiet": True, + } + + if params: + return YTDLP(params=merge_dict(params, default_params)) + + if YTDLP_INFO_CLS is None: + YTDLP_INFO_CLS = YTDLP(params=default_params) + return YTDLP_INFO_CLS @@ -1438,7 +1443,7 @@ def get_archive_id(url: str) -> dict[str, str | None]: "archive_id": None, } - for key, _ie in get_static_ytdlp()._ies.items(): + for key, _ie in get_ytdlp()._ies.items(): try: if not _ie.suitable(url): continue @@ -1937,16 +1942,17 @@ def get_extras(entry: dict, kind: str = "video") -> dict: return extras -def parse_outtmpl(output_template: str, info_dict: dict) -> str: +def parse_outtmpl(output_template: str, info_dict: dict, params: dict | None = None) -> str: """ Parse yt-dlp output template with given info_dict. Args: output_template (str): The output template string. info_dict (dict): The info dictionary from yt-dlp. + params (dict|None): Additional parameters for yt-dlp. Returns: str: The parsed output string. """ - return get_static_ytdlp().prepare_filename(info_dict=info_dict, outtmpl=output_template) + return get_ytdlp(params=params).prepare_filename(info_dict=info_dict, outtmpl=output_template) diff --git a/app/routes/api/tasks.py b/app/routes/api/tasks.py index 983d4d0c..4ed97dca 100644 --- a/app/routes/api/tasks.py +++ b/app/routes/api/tasks.py @@ -2,6 +2,7 @@ import asyncio import functools import logging import uuid +from pathlib import Path from typing import TYPE_CHECKING, Any from aiohttp import web @@ -266,14 +267,16 @@ async def task_metadata(request: Request, config: Config, encoder: Encoder) -> R if not task.folder: try: - outtmpl = parse_outtmpl( - output_template=task.get_ytdlp_opts().get_all().get("outtmpl", {}).get("default", "{title} [{id}]"), + ytdlp_opts: dict = task.get_ytdlp_opts().get_all() + outtmpl: str = parse_outtmpl( + output_template=ytdlp_opts.get("outtmpl", {}).get("default", "{title} [{id}]"), info_dict=metadata, + params=ytdlp_opts, ) if outtmpl: - _path = save_path / outtmpl + _path: Path = save_path / outtmpl if not _path.is_dir(): - _path = _path.parent + _path: Path = _path.parent (save_path, _) = get_file(config.download_path, _path.relative_to(config.download_path)) if not str(save_path or "").startswith(str(config.download_path)): @@ -302,6 +305,8 @@ async def task_metadata(request: Request, config: Config, encoder: Encoder) -> R data={"error": "Failed to get title from metadata."}, status=web.HTTPBadRequest.status_code ) + LOG.info(f"Generating metadata for task '{task.name}' in '{save_path!s}'") + from yt_dlp.utils import sanitize_filename from app.yt_dlp_plugins.postprocessor.nfo_maker import NFOMakerPP