mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2026-06-22 08:57:25 +00:00
Group selected videos by (resolution, range, codec) and mux each group into one MKV; only language is collapsed, so ranges/codecs/resolutions stay in separate files. Adds --merge-video flag + muxing.merge_video config (global and per-service), docs, and tests.
130 lines
4.6 KiB
Python
130 lines
4.6 KiB
Python
"""Tests for ``--merge-video`` track grouping.
|
|
|
|
``group_videos_by_variant`` (``unshackle/commands/dl.py``) decides which selected video
|
|
tracks share one MKV when merge mode is on. The rule: group by ``(resolution, range,
|
|
codec)`` so only language varies within a file; resolutions, ranges and codecs stay
|
|
separate. With ``merge=False`` every track is its own group (one file per track).
|
|
|
|
These lock the pure grouping unit; the surrounding mux loop is Click-command orchestration.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from unshackle.commands.dl import group_videos_by_variant
|
|
from unshackle.core.tracks import Video
|
|
|
|
|
|
def make_video(
|
|
track_id: str,
|
|
*,
|
|
range_: Video.Range,
|
|
height: int,
|
|
codec: Video.Codec,
|
|
language: str = "en",
|
|
) -> Video:
|
|
return Video(
|
|
id_=track_id,
|
|
url=f"https://example.test/{track_id}.m3u8",
|
|
language=language,
|
|
codec=codec,
|
|
range_=range_,
|
|
width=int(height * 16 / 9),
|
|
height=height,
|
|
bitrate=1_000_000,
|
|
)
|
|
|
|
|
|
HEVC = Video.Codec.HEVC
|
|
AVC = Video.Codec.AVC
|
|
SDR = Video.Range.SDR
|
|
HDR10 = Video.Range.HDR10
|
|
DV = Video.Range.DV
|
|
|
|
|
|
def test_merge_collapses_language_only() -> None:
|
|
"""Same (height, range, codec), different language → one group."""
|
|
videos = [
|
|
make_video("en", range_=SDR, height=1080, codec=HEVC, language="en"),
|
|
make_video("fr", range_=SDR, height=1080, codec=HEVC, language="fr"),
|
|
]
|
|
groups = group_videos_by_variant(videos, merge=True)
|
|
assert len(groups) == 1
|
|
assert [v.id for v in groups[0]] == ["en", "fr"]
|
|
|
|
|
|
def test_merge_splits_on_codec() -> None:
|
|
"""H264 vs H265 of the same resolution+range → separate groups."""
|
|
videos = [
|
|
make_video("hevc", range_=SDR, height=1080, codec=HEVC),
|
|
make_video("avc", range_=SDR, height=1080, codec=AVC),
|
|
]
|
|
groups = group_videos_by_variant(videos, merge=True)
|
|
assert len(groups) == 2
|
|
assert all(len(g) == 1 for g in groups)
|
|
|
|
|
|
def test_merge_splits_on_range() -> None:
|
|
"""SDR vs HDR10 of the same resolution+codec → separate groups."""
|
|
videos = [
|
|
make_video("sdr", range_=SDR, height=1080, codec=HEVC),
|
|
make_video("hdr10", range_=HDR10, height=1080, codec=HEVC),
|
|
]
|
|
groups = group_videos_by_variant(videos, merge=True)
|
|
assert len(groups) == 2
|
|
|
|
|
|
def test_merge_splits_on_resolution() -> None:
|
|
"""1080p vs 2160p of the same range+codec → separate groups."""
|
|
videos = [
|
|
make_video("1080", range_=SDR, height=1080, codec=HEVC),
|
|
make_video("2160", range_=SDR, height=2160, codec=HEVC),
|
|
]
|
|
groups = group_videos_by_variant(videos, merge=True)
|
|
assert len(groups) == 2
|
|
|
|
|
|
def test_merge_multi_range_yields_one_group_per_range() -> None:
|
|
"""Regression guard: -r HYBRID,DV,HDR10,SDR must never collapse into one file.
|
|
|
|
HYBRID is resolved upstream into a DV deliverable plus the requested standalone
|
|
ranges; here the four selected single-range tracks must stay in four groups.
|
|
"""
|
|
videos = [
|
|
make_video("sdr", range_=SDR, height=2160, codec=HEVC),
|
|
make_video("hdr10", range_=HDR10, height=2160, codec=HEVC),
|
|
make_video("dv", range_=DV, height=2160, codec=HEVC),
|
|
make_video("dv-hybrid", range_=DV, height=1080, codec=HEVC), # different height
|
|
]
|
|
groups = group_videos_by_variant(videos, merge=True)
|
|
assert len(groups) == 4
|
|
|
|
|
|
def test_no_merge_yields_one_group_per_track() -> None:
|
|
"""merge=False reproduces today's per-track behaviour exactly."""
|
|
videos = [
|
|
make_video("en", range_=SDR, height=1080, codec=HEVC, language="en"),
|
|
make_video("fr", range_=SDR, height=1080, codec=HEVC, language="fr"),
|
|
make_video("avc", range_=SDR, height=1080, codec=AVC),
|
|
]
|
|
groups = group_videos_by_variant(videos, merge=False)
|
|
assert len(groups) == 3
|
|
assert all(len(g) == 1 for g in groups)
|
|
|
|
|
|
def test_merge_preserves_first_seen_order() -> None:
|
|
"""Group order follows first-seen track order, for stable output filenames."""
|
|
videos = [
|
|
make_video("hevc-en", range_=SDR, height=1080, codec=HEVC, language="en"),
|
|
make_video("avc-en", range_=SDR, height=1080, codec=AVC, language="en"),
|
|
make_video("hevc-fr", range_=SDR, height=1080, codec=HEVC, language="fr"),
|
|
]
|
|
groups = group_videos_by_variant(videos, merge=True)
|
|
# HEVC group seen first (and gathers both languages), AVC group second.
|
|
assert [v.id for v in groups[0]] == ["hevc-en", "hevc-fr"]
|
|
assert [v.id for v in groups[1]] == ["avc-en"]
|
|
|
|
|
|
def test_empty_input_returns_empty() -> None:
|
|
assert group_videos_by_variant([], merge=True) == []
|
|
assert group_videos_by_variant([], merge=False) == []
|