soulsync/tests/test_video_quality_profile.py
BoulderBadgeDad 23cefd3c15 video quality profile: rebuild as rich-curated Radarr-class model (logic + tests)
Replaces the simplified resolutions+4-sources+codec model with a real quality
ladder to actually stand in for Radarr/Sonarr:

  - tiers: source×resolution ladder (Remux-2160p … SDTV) as one ranked, toggleable
    list, best→worst, with a cutoff (stop upgrading once the library holds a tier
    at/above it — no endless re-grabbing).
  - rejects: hard blocks (cam/screener/workprint/3d, optional x264).
  - soft preferences (score/tie-break, never reject alone): prefer_codec (any/hevc/av1),
    prefer_hdr (off/prefer/require), prefer_audio (any/surround/lossless/atmos),
    prefer_repack.
  - size guard: min/max GB per item (0 = no limit), min pinned to a real cap.

Pure normalize/load/save (no DB/network), isolated from music. API endpoint is an
unchanged passthrough. 12 tests green, ruff clean. UI next.
2026-06-19 08:38:16 -07:00

130 lines
5 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,
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"] == "bluray-1080p"
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["min_size_gb"] == 0 and d["max_size_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_must_be_a_known_tier():
assert normalize({"cutoff": "web-720p"})["cutoff"] == "web-720p"
assert normalize({"cutoff": "nonsense"})["cutoff"] == "bluray-1080p" # falls back
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_clamps_and_pins_min_to_cap():
assert normalize({"max_size_gb": -5})["max_size_gb"] == 0
assert normalize({"max_size_gb": 99999})["max_size_gb"] == MAX_SIZE_CAP_GB
out = normalize({"min_size_gb": 50, "max_size_gb": 20}) # min above the cap
assert out["min_size_gb"] == 20 and out["max_size_gb"] == 20
keep = normalize({"min_size_gb": 5, "max_size_gb": 0}) # 0 cap = no limit, min stays
assert keep["min_size_gb"] == 5 and keep["max_size_gb"] == 0
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_size_gb": 40, "cutoff": "web-720p",
"tiers": [{"key": "remux-2160p", "enabled": True}]})
assert saved["prefer_codec"] == "av1" and saved["max_size_gb"] == 40
assert saved["cutoff"] == "web-720p"
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()