From 3b3c8120b89a09fdce951c233b1703840622a395 Mon Sep 17 00:00:00 2001 From: arabcoders Date: Mon, 18 May 2026 22:12:13 +0300 Subject: [PATCH] fix: recent change made fetch_info unpicklable --- app/features/ytdlp/extractor.py | 33 +++++++++++++------ .../ytdlp/tests/test_ytdlp_extractor.py | 18 ++++++++++ ui/app/pages/logs.vue | 18 +++++++--- 3 files changed, 55 insertions(+), 14 deletions(-) diff --git a/app/features/ytdlp/extractor.py b/app/features/ytdlp/extractor.py index b8071879..a9e9e674 100644 --- a/app/features/ytdlp/extractor.py +++ b/app/features/ytdlp/extractor.py @@ -19,19 +19,32 @@ LOG: logging.Logger = logging.getLogger("downloads.extractor") def _ytdlp_logger(target: logging.Logger): - def _log(level: int, msg: str, *args: Any, **kwargs: Any) -> None: + return _YTDLPLogger(target) + + +class _YTDLPLogger: + def __init__(self, target: logging.Logger) -> None: + self.target = target + + def __call__(self, level: int, msg: str, *args: Any, **kwargs: Any) -> None: kwargs.setdefault("stacklevel", 4) if level <= logging.DEBUG and isinstance(msg, str) and msg.startswith("[debug] "): - target.debug(msg.removeprefix("[debug] "), *args, **kwargs) + self.target.debug(msg.removeprefix("[debug] "), *args, **kwargs) return if level <= logging.DEBUG: - target.info(msg, *args, **kwargs) + self.target.info(msg, *args, **kwargs) return - target.log(level, msg, *args, **kwargs) + self.target.log(level, msg, *args, **kwargs) - return _log + +class _LogCapture: + def __init__(self, logs: list[str]) -> None: + self.logs = logs + + def __call__(self, _: int, msg: str, *args: Any, **__: Any) -> None: + self.logs.append(msg % args if args else msg) def _get_process_pool_kwargs() -> dict[str, Any]: @@ -243,7 +256,7 @@ def extract_info_sync( sanitize_info: bool = False, capture_logs: int | None = None, **kwargs, -) -> tuple[dict[str, Any] | None, list[dict[str, Any]]]: +) -> tuple[dict[str, Any] | None, list[str]]: """ Extract video information from a URL. @@ -258,7 +271,7 @@ def extract_info_sync( **kwargs: Additional arguments Returns: - tuple[dict | None, list[dict]]: Extracted information and captured logs. + tuple[dict | None, list[str]]: Extracted information and captured logs. """ params: dict[str, Any] = {**config, **_DATA.YTDLP_PARAMS, "simulate": True} @@ -287,7 +300,7 @@ def extract_info_sync( captured_logs: list[str] = kwargs.get("captured_logs", []) if capture_logs is not None: log_wrapper.add_target( - target=lambda _, msg: captured_logs.append(msg), + target=_LogCapture(captured_logs), level=capture_logs, name="log-capture", ) @@ -351,7 +364,7 @@ async def fetch_info( extractor_config: ExtractorConfig | None = None, budget_sleep: bool = False, **kwargs, -) -> tuple[dict[str, Any] | None, list[dict[str, Any]]]: +) -> tuple[dict[str, Any] | None, list[str]]: """ Extract video information from a URL. @@ -371,7 +384,7 @@ async def fetch_info( **kwargs: Additional arguments Returns: - tuple[dict | None, list[dict]]: Extracted information and captured logs. + tuple[dict | None, list[str]]: Extracted information and captured logs. """ if extractor_config is None: diff --git a/app/features/ytdlp/tests/test_ytdlp_extractor.py b/app/features/ytdlp/tests/test_ytdlp_extractor.py index a2a7d955..33972198 100644 --- a/app/features/ytdlp/tests/test_ytdlp_extractor.py +++ b/app/features/ytdlp/tests/test_ytdlp_extractor.py @@ -1,13 +1,16 @@ import logging +import pickle from unittest.mock import MagicMock, patch from app.features.ytdlp.extractor import ( ExtractorConfig, ExtractorPool, + _LogCapture, _get_process_pool_kwargs, _ytdlp_logger, extract_info_sync, ) +from app.features.ytdlp.utils import LogWrapper class TestProcessPoolConfiguration: @@ -170,3 +173,18 @@ class TestYtdlpLogger: _ytdlp_logger(logger)(logging.DEBUG, "screen line") logger.info.assert_called_once_with("screen line", stacklevel=4) + + def test_targets_are_picklable(self) -> None: + logs: list[str] = [] + wrapper = LogWrapper() + wrapper.add_target(_ytdlp_logger(logging.getLogger("yt-dlp.test")), level=logging.DEBUG, name="yt-dlp.test") + wrapper.add_target(_LogCapture(logs), level=logging.WARNING, name="log-capture") + + pickle.dumps(wrapper) + + def test_capture_formats_args(self) -> None: + logs: list[str] = [] + + _LogCapture(logs)(logging.WARNING, "hello %s", "world") + + assert logs == ["hello world"] diff --git a/ui/app/pages/logs.vue b/ui/app/pages/logs.vue index 3702f4e9..5e9fed60 100644 --- a/ui/app/pages/logs.vue +++ b/ui/app/pages/logs.vue @@ -142,7 +142,9 @@ ]" >

- + {{ logTimeLabel(entry.log.datetime) }} @@ -169,7 +171,8 @@ [{{ entry.log.logger }}] @@ -223,8 +226,15 @@ /> {{ getLogLevel(selectedLog.level) }} - - {{ selectedLog.logger }} + + {{ selectedLog.logger }} {{ logTimeTitle(selectedLog.datetime) }}