From 9e4fdcdcd89f2bfcdad39df3df957648a09158bb Mon Sep 17 00:00:00 2001 From: imSp4rky Date: Sun, 10 May 2026 11:04:29 -0600 Subject: [PATCH] fix(dl): re-pick DV/HDR10 when HYBRID falls back under best_available When HYBRID is requested alongside other ranges with best_available and no HDR10 base layer exists, the pre-validation hybrid selection had already locked in the lowest-resolution DV track. Snapshot the pre-hybrid pool and redo Cartesian range/quality/codec/lang selection over surviving ranges so DV (or HDR10-only) honors --worst and default best-pick semantics. --- unshackle/commands/dl.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/unshackle/commands/dl.py b/unshackle/commands/dl.py index b62f004..e034655 100644 --- a/unshackle/commands/dl.py +++ b/unshackle/commands/dl.py @@ -1702,6 +1702,7 @@ class dl: sys.exit(1) # choose best track by range and quality + pre_hybrid_videos: list[Video] = list(title.tracks.videos) if has_hybrid else [] if has_hybrid: # Apply hybrid selection for HYBRID tracks hybrid_candidate_tracks = [ @@ -1812,6 +1813,31 @@ class dl: f"Continuing with remaining range(s): {', '.join(r.name for r in other_ranges)}" ) range_ = other_ranges + fallback_pool = pre_hybrid_videos + if video_multi_lang: + fallback_langs = list(dict.fromkeys(str(v.language) for v in fallback_pool)) + else: + fallback_langs = [None] + fallback_selected: list[Video] = [] + for resolution, color_range, codec, vlang in product( + quality or [None], other_ranges, vcodec or [None], fallback_langs + ): + candidates = [ + t + for t in fallback_pool + if ( + not resolution + or t.height == resolution + or int(t.width * (9 / 16)) == resolution + ) + and (not color_range or t.range == color_range) + and (not codec or t.codec == codec) + and (vlang is None or str(t.language) == vlang) + ] + match = candidates[-1] if worst and candidates else next(iter(candidates), None) + if match and match not in fallback_selected: + fallback_selected.append(match) + title.tracks.videos = fallback_selected else: self.log.error(msg) self.log.error(msg_detail)