From 5337639035e76919ac82161a51cd4df62c21402f Mon Sep 17 00:00:00 2001 From: imSp4rky Date: Tue, 9 Jun 2026 22:26:35 -0600 Subject: [PATCH] feat(logging): add debug_requests flag and aggregate subtitle-conversion logs --- unshackle/commands/dl.py | 31 ++++++++++++++++++----- unshackle/core/__main__.py | 4 +++ unshackle/core/config.py | 1 + unshackle/core/tracks/subtitle.py | 27 ++------------------ unshackle/core/tracks/subtitle_convert.py | 1 - unshackle/unshackle-example.yaml | 7 +++++ 6 files changed, 39 insertions(+), 32 deletions(-) diff --git a/unshackle/commands/dl.py b/unshackle/commands/dl.py index 7745469..f670629 100644 --- a/unshackle/commands/dl.py +++ b/unshackle/commands/dl.py @@ -1008,9 +1008,7 @@ class dl: } if cdm_info: - log_event( - "load_cdm", level="INFO", service=self.service, context={"cdm": cdm_info} - ) + log_event("load_cdm", level="INFO", service=self.service, context={"cdm": cdm_info}) self.proxy_providers = [] if no_proxy: @@ -1805,7 +1803,9 @@ class dl: for v in non_hybrid_tracks if any(v.height == res or int(v.width * (9 / 16)) == res for res in quality) ] - title.tracks.videos = Tracks.merge_video_selections(hybrid_selected, non_hybrid_selected) + title.tracks.videos = Tracks.merge_video_selections( + hybrid_selected, non_hybrid_selected + ) else: title.tracks.videos = hybrid_selected else: @@ -2422,15 +2422,30 @@ class dl: sidecar_original_paths[subtitle.id] = original_path with console.status("Converting Subtitles..."): + sub_conversions: dict[tuple[str, str], int] = {} for subtitle in title.tracks.subtitles: if sub_format == "original": continue if sub_format: if subtitle.codec != sub_format: + src = getattr(subtitle.codec, "name", str(subtitle.codec)) + dst = getattr(sub_format, "name", str(sub_format)) subtitle.convert(sub_format, forced=True) + sub_conversions[(src, dst)] = sub_conversions.get((src, dst), 0) + 1 elif subtitle.codec == Subtitle.Codec.TimedTextMarkupLang: # MKV does not support TTML, VTT is the next best option + src = getattr(subtitle.codec, "name", str(subtitle.codec)) subtitle.convert(Subtitle.Codec.WebVTT) + sub_conversions[(src, Subtitle.Codec.WebVTT.name)] = ( + sub_conversions.get((src, Subtitle.Codec.WebVTT.name), 0) + 1 + ) + for (src, dst), count in sub_conversions.items(): + log_event( + "subtitle_convert", + level="INFO", + message=f"Converted {src}->{dst} x{count}", + context={"from": src, "to": dst, "count": count}, + ) with console.status("Checking Subtitles for Fonts..."): font_names: list[str] = [] @@ -2518,7 +2533,9 @@ class dl: # Now repack the decrypted tracks if progress_sink and any(getattr(t, "needs_repack", False) for t in title.tracks): - progress_sink({"phase": "repackaging", "progress": 92.0, "status": "downloading", "active_tracks": []}) + progress_sink( + {"phase": "repackaging", "progress": 92.0, "status": "downloading", "active_tracks": []} + ) with console.status("Repackaging tracks with FFMPEG..."): has_repacked = False for track in title.tracks: @@ -2686,7 +2703,9 @@ class dl: mux_video_standalone(video_track) if progress_sink: - progress_sink({"phase": "muxing", "progress": 96.0, "status": "downloading", "active_tracks": []}) + progress_sink( + {"phase": "muxing", "progress": 96.0, "status": "downloading", "active_tracks": []} + ) try: with Live(Padding(progress, (0, 5, 1, 5)), console=console): mux_index = 0 diff --git a/unshackle/core/__main__.py b/unshackle/core/__main__.py index a475233..74744bd 100644 --- a/unshackle/core/__main__.py +++ b/unshackle/core/__main__.py @@ -44,6 +44,10 @@ def main(version: bool, debug: bool) -> None: if debug_logging_enabled: init_debug_logger(enabled=True) + if debug and not config.debug_requests: + for noisy in ("urllib3", "urllib3.connectionpool", "requests", "rnet", "httpx", "httpcore", "hpack", "h2"): + logging.getLogger(noisy).setLevel(logging.WARNING) + urllib3.disable_warnings(InsecureRequestWarning) traceback.install(console=console, width=80, suppress=[click]) diff --git a/unshackle/core/config.py b/unshackle/core/config.py index 3ffbfdc..4e6d1bb 100644 --- a/unshackle/core/config.py +++ b/unshackle/core/config.py @@ -126,6 +126,7 @@ class Config: self.debug: bool = kwargs.get("debug", False) self.debug_keys: bool = kwargs.get("debug_keys", False) + self.debug_requests: bool = kwargs.get("debug_requests", False) def _validate_output_templates(self) -> None: """Validate output template configurations and warn about potential issues.""" diff --git a/unshackle/core/tracks/subtitle.py b/unshackle/core/tracks/subtitle.py index 8130779..203c271 100644 --- a/unshackle/core/tracks/subtitle.py +++ b/unshackle/core/tracks/subtitle.py @@ -23,7 +23,7 @@ from subtitle_filter import Subtitles from unshackle.core import binaries from unshackle.core.config import config from unshackle.core.tracks.track import Track -from unshackle.core.utilities import get_debug_logger, log_event, try_ensure_utf8 +from unshackle.core.utilities import try_ensure_utf8 from unshackle.core.utils.subprocess import log_tool_run from unshackle.core.utils.webvtt import merge_segmented_webvtt @@ -624,30 +624,7 @@ class Subtitle(Track): config.subtitle.get("conversion_method") or getattr(self, "preferred_conversion_method", None) or "auto" ) pin = None if method == "auto" else method - - dl = get_debug_logger() - if not dl: - return run_conversion(self, codec, pin=pin, forced=forced) - - start = time.monotonic() - try: - result = run_conversion(self, codec, pin=pin, forced=forced) - except Exception as e: - dl.log_error( - "subtitle_convert", - e, - context={"from": str(self.codec), "to": str(codec), "method": method, "forced": forced}, - ) - raise - log_event( - "subtitle_convert", - level="INFO", - message=f"Converted subtitle {self.codec} -> {codec}", - context={"from": str(self.codec), "to": str(codec), "method": method, "forced": forced}, - duration_ms=round((time.monotonic() - start) * 1000, 1), - success=True, - ) - return result + return run_conversion(self, codec, pin=pin, forced=forced) @staticmethod def extract_fonts(text: str) -> set[str]: diff --git a/unshackle/core/tracks/subtitle_convert.py b/unshackle/core/tracks/subtitle_convert.py index 3e9f1b7..986ef18 100644 --- a/unshackle/core/tracks/subtitle_convert.py +++ b/unshackle/core/tracks/subtitle_convert.py @@ -318,7 +318,6 @@ def run_conversion(sub: Subtitle, target: Codec, *, pin: Optional[str] = None, f last_exc = e log.debug(f"Subtitle backend {backend.name} failed ({source.name}->{target.name}): {e}") continue - log.debug(f"Converted subtitle {source.name}->{target.name} via {backend.name}") return finalize(sub, target, out) raise RuntimeError(f"All subtitle backends failed for {source.name}->{target.name}") from last_exc diff --git a/unshackle/unshackle-example.yaml b/unshackle/unshackle-example.yaml index 407eeb5..61b942c 100644 --- a/unshackle/unshackle-example.yaml +++ b/unshackle/unshackle-example.yaml @@ -136,6 +136,13 @@ debug_keys: # Useful for debugging key retrieval and decryption issues # SECURITY NOTE: Passwords, tokens, cookies, and session tokens # are ALWAYS redacted regardless of this setting + +debug_requests: + false # Show per-request HTTP library logs under --debug (default: false) + # When false, --debug mutes the noisy per-segment connection logs from the + # HTTP libraries (urllib3/requests/rnet, e.g. 'GET ...m4s 200'), keeping + # unshackle's own DEBUG output readable. + # Set to true to see every individual request/segment download. # Only affects: content_key, key fields (the actual CEKs) # Never affects: kid, keys_count, key_id (metadata is always logged)