refactor(release): move codec mappings from code to YAML knowledge

The three module-level dicts in enrich_from_probe (ffprobe codec name
to scene token, channel count to layout) were exactly the kind of
domain lookup table CLAUDE.md says belongs in YAML, not in Python.
Move them to alfred/knowledge/release/probe_mappings.yaml, load
through a new ReleaseKnowledge.probe_mappings port field, and add a
kb parameter to enrich_from_probe so the consumer reads the maps via
the same injection pattern as everything else.

- New knowledge file: alfred/knowledge/release/probe_mappings.yaml
- New loader: load_probe_mappings() in infrastructure/knowledge/release.py
  (normalizes channel-count keys back to int).
- Port: ReleaseKnowledge gains probe_mappings: dict.
- Adapter: YamlReleaseKnowledge populates it at __init__.
- Consumer: enrich_from_probe(parsed, info, kb) reads the three sub-maps
  from kb.probe_mappings; unknown codecs still fall back to uppercase
  raw value, same behaviour as before.
- Call sites updated: inspect_release passes kb through; the testing
  script gets its kb wiring (it was already broken since the
  ReleaseKnowledge refactor); all 22 enrich_from_probe call sites in
  tests/application/test_enrich_from_probe.py pass _KB.
This commit is contained in:
2026-05-21 07:37:42 +02:00
parent e62dc90bd1
commit 0246f85ef8
9 changed files with 154 additions and 64 deletions
+25 -22
View File
@@ -21,6 +21,9 @@ from __future__ import annotations
from alfred.application.release.enrich_from_probe import enrich_from_probe
from alfred.domain.release.value_objects import ParsedRelease
from alfred.domain.shared.media import AudioTrack, MediaInfo, VideoTrack
from alfred.infrastructure.knowledge.release_kb import YamlReleaseKnowledge
_KB = YamlReleaseKnowledge()
def _info_with_video(*, width=None, height=None, codec=None, **rest) -> MediaInfo:
@@ -59,17 +62,17 @@ def _bare(**overrides) -> ParsedRelease:
class TestQuality:
def test_fills_when_none(self):
p = _bare()
enrich_from_probe(p, _info_with_video(width=1920, height=1080))
enrich_from_probe(p, _info_with_video(width=1920, height=1080), _KB)
assert p.quality == "1080p"
def test_does_not_overwrite_existing(self):
p = _bare(quality="2160p")
enrich_from_probe(p, _info_with_video(width=1920, height=1080))
enrich_from_probe(p, _info_with_video(width=1920, height=1080), _KB)
assert p.quality == "2160p"
def test_no_dims_leaves_none(self):
p = _bare()
enrich_from_probe(p, MediaInfo())
enrich_from_probe(p, MediaInfo(), _KB)
assert p.quality is None
@@ -81,27 +84,27 @@ class TestQuality:
class TestVideoCodec:
def test_hevc_to_x265(self):
p = _bare()
enrich_from_probe(p, _info_with_video(codec="hevc"))
enrich_from_probe(p, _info_with_video(codec="hevc"), _KB)
assert p.codec == "x265"
def test_h264_to_x264(self):
p = _bare()
enrich_from_probe(p, _info_with_video(codec="h264"))
enrich_from_probe(p, _info_with_video(codec="h264"), _KB)
assert p.codec == "x264"
def test_unknown_codec_uppercased(self):
p = _bare()
enrich_from_probe(p, _info_with_video(codec="weird"))
enrich_from_probe(p, _info_with_video(codec="weird"), _KB)
assert p.codec == "WEIRD"
def test_does_not_overwrite_existing(self):
p = _bare(codec="HEVC")
enrich_from_probe(p, _info_with_video(codec="h264"))
enrich_from_probe(p, _info_with_video(codec="h264"), _KB)
assert p.codec == "HEVC"
def test_no_codec_leaves_none(self):
p = _bare()
enrich_from_probe(p, MediaInfo())
enrich_from_probe(p, MediaInfo(), _KB)
assert p.codec is None
@@ -119,7 +122,7 @@ class TestAudio:
]
)
p = _bare()
enrich_from_probe(p, info)
enrich_from_probe(p, info, _KB)
assert p.audio_codec == "EAC3"
assert p.audio_channels == "5.1"
@@ -131,32 +134,32 @@ class TestAudio:
]
)
p = _bare()
enrich_from_probe(p, info)
enrich_from_probe(p, info, _KB)
assert p.audio_codec == "AC3"
assert p.audio_channels == "5.1"
def test_channel_count_unknown_falls_back(self):
info = MediaInfo(audio_tracks=[AudioTrack(0, "aac", 4, "quad", "eng")])
p = _bare()
enrich_from_probe(p, info)
enrich_from_probe(p, info, _KB)
assert p.audio_channels == "4ch"
def test_unknown_audio_codec_uppercased(self):
info = MediaInfo(audio_tracks=[AudioTrack(0, "newcodec", 2, "stereo", "eng")])
p = _bare()
enrich_from_probe(p, info)
enrich_from_probe(p, info, _KB)
assert p.audio_codec == "NEWCODEC"
def test_no_audio_tracks(self):
p = _bare()
enrich_from_probe(p, MediaInfo())
enrich_from_probe(p, MediaInfo(), _KB)
assert p.audio_codec is None
assert p.audio_channels is None
def test_does_not_overwrite_existing_audio_fields(self):
info = MediaInfo(audio_tracks=[AudioTrack(0, "ac3", 6, "5.1", "eng")])
p = _bare(audio_codec="DTS-HD.MA", audio_channels="7.1")
enrich_from_probe(p, info)
enrich_from_probe(p, info, _KB)
assert p.audio_codec == "DTS-HD.MA"
assert p.audio_channels == "7.1"
@@ -175,7 +178,7 @@ class TestLanguages:
]
)
p = _bare()
enrich_from_probe(p, info)
enrich_from_probe(p, info, _KB)
assert p.languages == ["eng", "fre"]
def test_skips_und(self):
@@ -186,7 +189,7 @@ class TestLanguages:
]
)
p = _bare()
enrich_from_probe(p, info)
enrich_from_probe(p, info, _KB)
assert p.languages == ["eng"]
def test_dedup_against_existing_case_insensitive(self):
@@ -201,13 +204,13 @@ class TestLanguages:
)
p = _bare()
p.languages = ["ENG"]
enrich_from_probe(p, info)
enrich_from_probe(p, info, _KB)
# "eng" → upper "ENG" already present → skipped. "fre" → "FRE" new → kept.
assert p.languages == ["ENG", "fre"]
def test_no_audio_tracks_leaves_languages_empty(self):
p = _bare()
enrich_from_probe(p, MediaInfo())
enrich_from_probe(p, MediaInfo(), _KB)
assert p.languages == []
@@ -224,7 +227,7 @@ class TestTechString:
def test_rebuilt_from_filled_quality_and_codec(self):
p = _bare()
enrich_from_probe(
p, _info_with_video(width=1920, height=1080, codec="hevc")
p, _info_with_video(width=1920, height=1080, codec="hevc"), _KB
)
assert p.quality == "1080p"
assert p.codec == "x265"
@@ -234,7 +237,7 @@ class TestTechString:
# Token-level source must stay; probe fills only None fields.
p = _bare(source="BluRay")
enrich_from_probe(
p, _info_with_video(width=1920, height=1080, codec="hevc")
p, _info_with_video(width=1920, height=1080, codec="hevc"), _KB
)
assert p.tech_string == "1080p.BluRay.x265"
@@ -242,10 +245,10 @@ class TestTechString:
# No video info → nothing to fill → derived tech_string stays as it was.
p = _bare(quality="2160p", source="WEB-DL", codec="x265")
assert p.tech_string == "2160p.WEB-DL.x265"
enrich_from_probe(p, MediaInfo())
enrich_from_probe(p, MediaInfo(), _KB)
assert p.tech_string == "2160p.WEB-DL.x265"
def test_empty_when_nothing_known(self):
p = _bare()
enrich_from_probe(p, MediaInfo())
enrich_from_probe(p, MediaInfo(), _KB)
assert p.tech_string == ""