soulsync/tests/test_video_quality_profile.py
BoulderBadgeDad 9f38c34900 video quality profile: loose resolution cutoff + movie/episode size split
Addressing UX feedback (Boulder): the cutoff and size guard were confusing for a
library that holds BOTH movies and TV.

  - 'Upgrade until' is now a LOOSE resolution target (4K / 1080p / 720p / SD /
    'best — never stop'), not a specific source×resolution tier. 4K is always in
    the list regardless of which tiers are toggled on (the old dropdown only
    listed enabled tiers, so 4K vanished when off). Stored as cutoff_resolution.
  - Size guard split into Max movie size + Max episode size (runtime-aware, like
    Radarr's MB/min but human-readable) — a flat GB cap was meaningless across a
    2-hour movie and a 25-min episode. Dropped the confusing min slider.

Pure-logic + API + UI in lockstep; removed the now-dead per-row cutoff marker.
12 tests green, ruff clean, JS/HTML balance clean.
2026-06-19 10:40:53 -07:00

131 lines
5.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Video quality profile — the pure default/normalize/load/save seam for the
rich-curated (Radarr-class) model: a ranked source×resolution tier ladder +
cutoff + hard rejects + soft codec/HDR/audio/repack preferences + size guard,
isolated from the music side."""
from __future__ import annotations
import json
from core.video.quality_profile import (
AUDIO_MODES,
CODECS,
HDR_MODES,
MAX_SIZE_CAP_GB,
REJECTS,
RESOLUTIONS,
TIERS,
default_profile,
load,
normalize,
normalize_tiers,
save,
)
def _keys(tiers):
return [t["key"] for t in tiers]
def test_default_shape():
d = default_profile()
assert _keys(d["tiers"]) == list(TIERS) # complete ladder, canonical order
on = {t["key"] for t in d["tiers"] if t["enabled"]}
assert "bluray-1080p" in on and "web-720p" in on # 1080p/720p on
assert "remux-2160p" not in on and "sdtv" not in on # 4K + SD off by default
assert d["cutoff_resolution"] == "1080p" # loose resolution target
assert "cam" in d["rejects"] and "x264" not in d["rejects"] # junk blocked, x264 allowed
assert d["prefer_codec"] == "hevc" and d["prefer_hdr"] == "prefer"
assert d["prefer_audio"] == "any" and d["prefer_repack"] is True
assert d["max_movie_gb"] == 0 and d["max_episode_gb"] == 0
def test_normalize_garbage_returns_default():
assert normalize(None) == default_profile()
assert normalize("nope") == default_profile()
assert normalize(123) == default_profile()
def test_normalize_tiers_preserves_order_completes_and_coerces():
# caller sends a re-ranked subset with a couple toggled; rest must be appended
out = normalize_tiers([
{"key": "web-1080p", "enabled": False},
{"key": "bogus", "enabled": True}, # junk dropped
{"key": "remux-2160p", "enabled": True},
"web-1080p", # dupe ignored
])
keys = _keys(out)
assert keys[0] == "web-1080p" and keys[1] == "remux-2160p" # caller order kept
assert set(keys) == set(TIERS) and len(keys) == len(TIERS) # ladder completed
by = {t["key"]: t["enabled"] for t in out}
assert by["web-1080p"] is False and by["remux-2160p"] is True
assert by["bluray-1080p"] is True # untouched default stays on
def test_normalize_cutoff_is_a_loose_resolution_target():
assert normalize({"cutoff_resolution": "2160p"})["cutoff_resolution"] == "2160p"
assert normalize({"cutoff_resolution": ""})["cutoff_resolution"] == "" # best/always-upgrade
assert normalize({"cutoff_resolution": "nonsense"})["cutoff_resolution"] == "1080p" # falls back
assert "2160p" in RESOLUTIONS # 4K always offered
def test_normalize_rejects_keep_canonical_order_and_drop_junk():
out = normalize({"rejects": ["x264", "bogus", "cam", "x264"]})
assert out["rejects"] == ["cam", "x264"] # canonical order, valid only, deduped
def test_normalize_soft_prefs_validate():
out = normalize({"prefer_codec": "av1", "prefer_hdr": "require",
"prefer_audio": "atmos", "prefer_repack": 0})
assert out["prefer_codec"] == "av1" and out["prefer_hdr"] == "require"
assert out["prefer_audio"] == "atmos" and out["prefer_repack"] is False
# unknown enum values fall back to the default
bad = normalize({"prefer_codec": "vp9", "prefer_hdr": "maybe", "prefer_audio": "8ch"})
assert bad["prefer_codec"] == "hevc" and bad["prefer_hdr"] == "prefer"
assert bad["prefer_audio"] == "any"
def test_normalize_size_splits_movie_and_episode_and_clamps():
assert normalize({"max_movie_gb": -5})["max_movie_gb"] == 0 # negative clamped
assert normalize({"max_movie_gb": 99999})["max_movie_gb"] == MAX_SIZE_CAP_GB # capped
out = normalize({"max_movie_gb": 40, "max_episode_gb": 5}) # independent caps
assert out["max_movie_gb"] == 40 and out["max_episode_gb"] == 5
def test_constants():
assert CODECS == ("any", "hevc", "av1")
assert HDR_MODES == ("off", "prefer", "require")
assert AUDIO_MODES == ("any", "surround", "lossless", "atmos")
assert "cam" in REJECTS and "screener" in REJECTS
# ── DB round-trip via an injected fake ────────────────────────────────────────
class _FakeDB:
def __init__(self):
self._kv = {}
def get_setting(self, key, default=None):
return self._kv.get(key, default)
def set_setting(self, key, value):
self._kv[key] = value
def test_load_default_when_unset():
assert load(_FakeDB()) == default_profile()
def test_save_then_load_roundtrips_normalized():
db = _FakeDB()
saved = save(db, {"prefer_codec": "av1", "max_movie_gb": 40, "cutoff_resolution": "2160p",
"tiers": [{"key": "remux-2160p", "enabled": True}]})
assert saved["prefer_codec"] == "av1" and saved["max_movie_gb"] == 40
assert saved["cutoff_resolution"] == "2160p"
assert json.loads(db.get_setting("quality_profile"))["prefer_codec"] == "av1"
assert load(db) == saved
def test_load_recovers_from_corrupt_json():
db = _FakeDB()
db.set_setting("quality_profile", "{not json")
assert load(db) == default_profile()