mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2026-11-14 13:05:09 +00:00
feat(tracks): add Tracks.dedupe and match variant tracks by signature
get_tracks_for_variants skips a later manifest's track when a held track has the same signature, so a track whose ID changes per manifest is listed once.
This commit is contained in:
@@ -31,7 +31,7 @@ from unshackle.core.api.handlers import validate_download_parameters
|
||||
from unshackle.core.config import config
|
||||
from unshackle.core.service import Service, TrackRequest
|
||||
from unshackle.core.services import Services
|
||||
from unshackle.core.tracks import Tracks, Video
|
||||
from unshackle.core.tracks import Audio, Chapter, Subtitle, Tracks, Video
|
||||
from unshackle.core.tracks import hybrid as hybrid_mod
|
||||
from unshackle.core.tracks.hybrid import Hybrid, hdr10plus_mean_pq, hdr10plus_shots, nits_to_pq
|
||||
|
||||
@@ -731,3 +731,50 @@ def test_a_native_hybrid_passes_the_gate_and_takes_no_base_at_its_resolution() -
|
||||
|
||||
assert [v.id for v in bases] == ["h1080"]
|
||||
assert dv is not None and dv.id == "dvp2160"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"ranges, codecs, surround_on",
|
||||
[
|
||||
([Video.Range.SDR, Video.Range.HDR10], [], Video.Range.HDR10),
|
||||
([Video.Range.HYBRID], [Video.Codec.AVC, Video.Codec.HEVC], Video.Range.DV),
|
||||
],
|
||||
)
|
||||
def test_variants_merge_every_manifest_but_skip_a_subtitle_copy_and_later_chapters(
|
||||
ranges: list[Video.Range], codecs: list[Video.Codec], surround_on: Video.Range
|
||||
) -> None:
|
||||
"""Audio and a subtitle language that only a later manifest offers are kept. A track whose ID
|
||||
differs per manifest (a signed URL) is not duplicated, and later chapters do not clash."""
|
||||
fetches = 0
|
||||
|
||||
def fetch(title: Any, codec: Any, range_: Video.Range) -> Tracks:
|
||||
nonlocal fetches
|
||||
fetches += 1
|
||||
tracks = Tracks()
|
||||
tracks.add(
|
||||
Video(
|
||||
url="https://example.invalid/v", language="en", id_=f"{range_.name}{codec}", codec=codec, range_=range_
|
||||
)
|
||||
)
|
||||
tracks.add(Audio(url="https://example.invalid/a2", language="en", id_=f"stereo{fetches}", channels=2))
|
||||
if range_ == surround_on:
|
||||
tracks.add(Audio(url="https://example.invalid/a6", language="en", id_="surround", channels=6))
|
||||
tracks.add(Subtitle(url=f"https://example.invalid/s?tok={fetches}", language="en", id_=f"sub{fetches}"))
|
||||
if range_ == surround_on:
|
||||
tracks.add(Subtitle(url=f"https://example.invalid/j?tok={fetches}", language="ja", id_=f"ja{fetches}"))
|
||||
tracks.add(Chapter(0))
|
||||
return tracks
|
||||
|
||||
service = SimpleNamespace(
|
||||
track_request=TrackRequest(codecs=codecs, ranges=ranges),
|
||||
log=logging.getLogger("variants-test"),
|
||||
)
|
||||
|
||||
tracks = Service.get_tracks_for_variants(service, None, fetch) # type: ignore[arg-type]
|
||||
|
||||
assert fetches > 1
|
||||
assert sorted(a.id for a in tracks.audio) == ["stereo1", "surround"]
|
||||
assert [str(s.language) for s in tracks.subtitles] == ["en", "ja"]
|
||||
assert tracks.subtitles[0].id == "sub1"
|
||||
assert len(tracks.chapters) == 1
|
||||
assert len(tracks.videos) == fetches
|
||||
|
||||
@@ -147,3 +147,25 @@ def test_sort_audio_codec_priority_beats_bitrate() -> None:
|
||||
best = Tracks.by_language(tracks.audio, ["en"])[0]
|
||||
# EC3 is prioritized even though the AC3 track has the higher bitrate
|
||||
assert best.id == "ec3-320"
|
||||
|
||||
|
||||
def test_dedupe_keeps_the_first_copy_and_every_track_that_differs() -> None:
|
||||
tracks = Tracks()
|
||||
tracks.audio = [
|
||||
make_audio("ec3-a", codec=Audio.Codec.EC3, bitrate=128_000),
|
||||
make_audio("ec3-b", codec=Audio.Codec.EC3, bitrate=128_000),
|
||||
make_audio("aac", codec=Audio.Codec.AAC, bitrate=128_000),
|
||||
make_audio("ec3-desc", codec=Audio.Codec.EC3, bitrate=128_000, descriptive=True),
|
||||
make_audio("ec3-atmos", codec=Audio.Codec.EC3, bitrate=128_000, joc=16),
|
||||
make_audio("ec3-de", codec=Audio.Codec.EC3, bitrate=128_000, language="de"),
|
||||
]
|
||||
tracks.videos = [
|
||||
make_video("hevc-a", height=1080, bitrate=5_000_000, width=1920),
|
||||
make_video("hevc-b", height=1080, bitrate=5_000_000, width=1920),
|
||||
make_video("hevc-hi", height=1080, bitrate=8_000_000, width=1920),
|
||||
]
|
||||
|
||||
tracks.dedupe()
|
||||
|
||||
assert [a.id for a in tracks.audio] == ["ec3-a", "aac", "ec3-desc", "ec3-atmos", "ec3-de"]
|
||||
assert [v.id for v in tracks.videos] == ["hevc-a", "hevc-hi"]
|
||||
|
||||
Reference in New Issue
Block a user