fix(api): show filename codec and range labels in codec_display and range_display

This commit is contained in:
imSp4rky
2026-10-28 15:34:52 -06:00
parent b0c006d7f6
commit d12a943d96
5 changed files with 37 additions and 7 deletions
+3 -3
View File
@@ -333,10 +333,10 @@ When you give both `season` and `episode`, the server combines them into a `"{se
"title": { "type": "movie", "name": "Example Movie", "id": "12345", "year": 2021 },
"video": [
{
"id": "v-1", "codec": "H265", "codec_display": "HEVC",
"id": "v-1", "codec": "HEVC", "codec_display": "H.265",
"bitrate": 8000, "bitrate_bps": 8000000, "width": 3840, "height": 2160,
"resolution": "2160p", "fps": "24", "range": "HDR10",
"range_display": "HDR10", "scan_type": null, "closed_captions": [],
"range_display": "HDR", "scan_type": null, "closed_captions": [],
"dv_compatible_bitstream": false, "hdr10plus": false, "hdr10_base": false, "language": "en",
"is_original_lang": true,
"name": null, "track_name": "English", "needs_repack": false, "edition": [], "data": null,
@@ -345,7 +345,7 @@ When you give both `season` and `episode`, the server combines them into a `"{se
],
"audio": [
{
"id": "a-1", "codec": "EC3", "codec_display": "DD+",
"id": "a-1", "codec": "EC3", "codec_display": "DDP",
"bitrate": 640, "bitrate_bps": 640000, "channels": "5.1", "language": "en",
"is_original": true, "is_original_lang": true, "atmos": false, "joc": 0,
"descriptive": false, "name": null, "track_name": "English", "needs_repack": false, "edition": [],
+3
View File
@@ -91,6 +91,9 @@ For `hdr`, unshackle renames each part of a Dolby Vision label separately, so `H
changes `DV.HDR10P` to `DV.HDR10+`. When an audio name ends in a digit, `audio_full` puts a
separator before the channels: `EAC3.5.1`, not `EAC35.1`.
The REST API fields `codec_display` and `range_display` use the same names and the same map.
The `dl --export` file name does not use `codec_names`.
!!! note "`tag_rules` see the new names"
unshackle evaluates [`tag_rules`](#tag_rules) after the rename. With the example above, a
rule must match `audio: EAC3`, not `audio: DDP`.
@@ -22,6 +22,7 @@ from unshackle.core.api.handlers import (
validate_download_parameters,
validate_service,
)
from unshackle.core.config import config
from unshackle.core.titles.episode import Episode
from unshackle.core.titles.movie import Movie
from unshackle.core.tracks import Audio, Subtitle, Video
@@ -172,6 +173,22 @@ def test_serialize_video_track_basic() -> None:
assert "url" not in d
def test_display_labels_match_the_filename_labels(monkeypatch: pytest.MonkeyPatch) -> None:
"""codec_display and range_display use the filename names, after any codec_names override."""
hevc = video(codec=Video.Codec.HEVC, range_=Video.Range.DV)
hevc.hdr10_base = True
assert serialize_audio_track(audio(codec=Audio.Codec.EC3))["codec_display"] == "DDP"
assert (serialize_video_track(hevc)["codec_display"], serialize_video_track(hevc)["range_display"]) == (
"H.265",
"DV+HDR",
)
assert serialize_video_track(video(codec=Video.Codec.VC1))["codec_display"] == "VC-1"
monkeypatch.setattr(config, "codec_names", {"DDP": "EAC3", "H.265": "HEVC"})
assert serialize_audio_track(audio(codec=Audio.Codec.EC3))["codec_display"] == "EAC3"
assert serialize_video_track(hevc)["codec_display"] == "HEVC"
def test_serialize_video_track_include_url() -> None:
d = serialize_video_track(video(), include_url=True)
assert d["url"] == "https://example.com/v.mpd"
+6 -3
View File
@@ -37,6 +37,7 @@ from unshackle.core.services import Services
from unshackle.core.session import RnetSession, utf8_text
from unshackle.core.titles import Episode, Movie, Song, Title_T
from unshackle.core.titles.music import SONG_FIELDS
from unshackle.core.titles.title import rename
from unshackle.core.tracks import Audio, Subtitle, Tracks, Video
from unshackle.core.tunnel import HEARTBEAT, MAX_MESSAGE, MAX_TUNNELS, TUNNELS, Tunnel, tunnel_proxy
from unshackle.core.utilities import declared_kwargs, kind_only_conflict, original_tag
@@ -1734,12 +1735,13 @@ def serialize_track_common(track: Any) -> Dict[str, Any]:
def serialize_video_track(track: Video, include_url: bool = False) -> Dict[str, Any]:
"""Convert video track to JSON-serializable dict."""
codec_name = enum_name(track.codec)
codec_value = track.codec.value if track.codec else codec_name
range_name = enum_name(track.range)
result = {
"id": str(track.id),
"codec": codec_name,
"codec_display": VIDEO_CODEC_MAP.get(codec_name, codec_name),
"codec_display": rename(VIDEO_CODEC_MAP.get(codec_name, codec_value)),
"bitrate": int(track.bitrate / 1000) if track.bitrate else None,
"bitrate_bps": track.bitrate or None,
"width": track.width,
@@ -1747,7 +1749,7 @@ def serialize_video_track(track: Video, include_url: bool = False) -> Dict[str,
"resolution": f"{track.width}x{track.height}" if track.width and track.height else None,
"fps": track.fps if track.fps else None,
"range": range_name,
"range_display": DYNAMIC_RANGE_MAP.get(track.range_label, track.range_label),
"range_display": "+".join(rename(DYNAMIC_RANGE_MAP.get(p, p)) for p in track.range_label.split("+")),
"scan_type": enum_name(track.scan_type) if track.scan_type else None,
"closed_captions": list(getattr(track, "closed_captions", None) or []),
"dv_compatible_bitstream": bool(getattr(track, "dv_compatible_bitstream", False)),
@@ -1779,11 +1781,12 @@ def serialize_audio_track(track: Audio, include_url: bool = False, is_original:
track 'orig' would download.
"""
codec_name = enum_name(track.codec)
codec_value = track.codec.value if track.codec else codec_name
result = {
"id": str(track.id),
"codec": codec_name,
"codec_display": AUDIO_CODEC_MAP.get(codec_name, codec_name),
"codec_display": rename(AUDIO_CODEC_MAP.get(codec_value, codec_value)),
"bitrate": int(track.bitrate / 1000) if track.bitrate else None,
"bitrate_bps": track.bitrate or None,
"channels": track.channels if track.channels else None,
+8 -1
View File
@@ -28,7 +28,14 @@ DYNAMIC_RANGE_MAP = {
"HDR10 / HDR10+": "HDR10P",
"HDR10 / HDR10": "HDR",
}
AUDIO_CODEC_MAP = {"E-AC-3": "DDP", "AC-3": "DD", "DTS-UHD": "DTS-X", "DTS LBR": "DTS-LBR", "MPEG-H 3D Audio": "MPEG-H"}
AUDIO_CODEC_MAP = {
"E-AC-3": "DDP",
"DD+": "DDP", # Audio.Codec.EC3's value, which the REST API names a track by
"AC-3": "DD",
"DTS-UHD": "DTS-X",
"DTS LBR": "DTS-LBR",
"MPEG-H 3D Audio": "MPEG-H",
}
SPACED_AUDIO_CODECS = {"DTS-X"}