Files
unshackle/tests/core/test_resolution_naming.py
imSp4rky 86f26c31d5 refactor(tracks): name and match resolutions by one standard-height rule
standard_resolution() replaces the aspect-ratio logic in naming and is shared by -q matching, export names and progress/hybrid labels, so 1920x1088, 1916x1076, portrait and DCI sizes get standard names.
2026-10-31 22:05:26 -06:00

120 lines
3.8 KiB
Python

"""Tests for the standard resolution in the filename context, the export name and -q matching."""
from __future__ import annotations
import types
from typing import Optional
import pytest
from unshackle.core.config import config
from unshackle.core.export_name import resolution
from unshackle.core.titles.movie import Movie
from unshackle.core.tracks import Video
from unshackle.core.tracks.tracks import at_resolution
from unshackle.core.utilities import standard_resolution
class DummyService:
pass
@pytest.fixture(autouse=True)
def clean_config(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(config, "tag_rules", [])
monkeypatch.setattr(config, "language_tags", {})
monkeypatch.setattr(config, "codec_names", {})
def quality(width: Optional[int], height: Optional[int], scan_type: str = "Progressive") -> str:
movie = Movie(id_="movie-0001", service=DummyService, name="The Film", year=2024, language="en")
video = types.SimpleNamespace(
width=width,
height=height,
format="AVC",
scan_type=scan_type,
hdr_format_commercial=None,
hdr_format=None,
transfer_characteristics="BT.709",
transfer_characteristics_original=None,
frame_rate="25.000",
)
media_info = types.SimpleNamespace(video_tracks=[video], audio_tracks=[])
return str(movie.build_base_template_context(media_info)["quality"])
@pytest.mark.parametrize(
("width", "height", "expected"),
[
(3840, 2160, 2160),
(1920, 1080, 1080),
(1280, 720, 720),
(2560, 1440, 1440),
(1920, 1088, 1080), # padded to a multiple of 16
(960, 544, 540),
(640, 368, 360),
(1916, 1076, 1080), # a few lines short
(3836, 2156, 2160),
(1920, 800, 1080), # scope crop
(1920, 1036, 1080), # flat crop
(3840, 1600, 2160),
(1280, 544, 720),
(4096, 2160, 2160), # DCI
(4096, 1716, 2160),
(2048, 858, 1080),
(1440, 1080, 1080), # 4:3 pillarbox
(960, 720, 720),
(720, 576, 576), # anamorphic SD
(704, 576, 576),
(720, 480, 480),
(854, 480, 480),
(1080, 1920, 1080), # portrait
(1080, 1350, 1080),
(720, 1280, 720),
(2560, 1080, 1080), # ultrawide
(3440, 1440, 1440),
(1600, 900, 900), # no standard height near it
],
)
def test_standard_resolution(width: int, height: int, expected: int) -> None:
assert standard_resolution(width, height) == expected
def test_a_picture_with_one_side_uses_it() -> None:
assert standard_resolution(None, 1088) == 1080
def test_a_picture_with_no_size_has_no_resolution() -> None:
assert standard_resolution(None, None) is None
def test_the_filename_quality_uses_the_standard_resolution() -> None:
assert quality(1920, 1088) == "1080p"
assert quality(1920, 800) == "1080p"
def test_an_interlaced_picture_keeps_the_i_suffix() -> None:
assert quality(1920, 1088, scan_type="Interlaced") == "1080i"
def test_a_video_track_with_no_size_gives_an_empty_quality() -> None:
assert quality(None, None) == ""
def video(width: int, height: int) -> Video:
return Video(url="https://example.test/v.mpd", language="en", codec=Video.Codec.AVC, width=width, height=height)
def test_the_export_name_uses_the_standard_resolution() -> None:
assert resolution(video(1080, 1920)) == 1080
assert resolution(video(4096, 2160)) == 2160
@pytest.mark.parametrize(("width", "height"), [(1920, 1080), (1920, 800), (1916, 1076), (1920, 1088)])
def test_q_1080_matches_odd_1080p_encodes(width: int, height: int) -> None:
assert at_resolution(video(width, height), 1080)
def test_q_1080_does_not_match_720p() -> None:
assert not at_resolution(video(1280, 720), 1080)