feat(mux): add muxing.default_subtitle_type for the default subtitle

Name which subtitle type (forced, normal, sdh) gets the default flag. The forced default now follows the default audio track, not the first.
This commit is contained in:
imSp4rky
2026-11-10 17:53:14 -07:00
parent d12bf31b6b
commit 2873cf4c53
6 changed files with 166 additions and 19 deletions
+87 -2
View File
@@ -1,10 +1,14 @@
from __future__ import annotations
from typing import Optional
import logging
from pathlib import Path
from typing import Callable, Optional
import pytest
from langcodes import Language
from unshackle.core.tracks import Audio, Tracks
from unshackle.core.config import config
from unshackle.core.tracks import Audio, Subtitle, Tracks
@pytest.mark.parametrize(
@@ -24,3 +28,84 @@ def test_first_match_index(languages: list[str], preferred: Optional[str], expec
for i, lang in enumerate(languages)
]
assert Tracks.first_match_index(tracks, preferred) == expected
def subtitle(index: int, language: str, kind: str) -> Subtitle:
return Subtitle(
id_=f"s{index}",
url=f"https://x.example/{index}",
language=language,
codec=Subtitle.Codec.WebVTT,
forced=kind == "forced",
sdh=kind == "sdh",
)
@pytest.mark.parametrize(
("language", "types", "expected"),
[
("pl", [], 0),
("pl", ["normal", "sdh", "forced"], 1),
("pl", ["forced"], 0),
("en", ["sdh", "normal"], 3),
("pl", ["sdh"], 0),
("fr", ["normal"], None),
(None, ["normal"], 1),
(None, ["sdh", "normal"], 3),
(None, [], None),
],
)
def test_default_subtitle_index(language: Optional[str], types: list[str], expected: Optional[int]) -> None:
kinds = [("pl", "forced"), ("pl", "normal"), ("en", "normal"), ("en", "sdh")]
subtitles = [subtitle(i, lang, kind) for i, (lang, kind) in enumerate(kinds)]
assert Tracks.default_subtitle_index(subtitles, language, types) == expected
@pytest.mark.parametrize(
("audio", "expected"),
[("en", 1), ("ar", 0), ("fr", 2), (None, 2)],
)
def test_a_forced_default_with_no_language_follows_the_audio(audio: Optional[str], expected: int) -> None:
kinds = [("ar", "forced"), ("en", "forced"), ("en", "normal")]
subtitles = [subtitle(i, lang, kind) for i, (lang, kind) in enumerate(kinds)]
audio_language = Language.get(audio) if audio else None
assert Tracks.default_subtitle_index(subtitles, None, ["forced", "normal"], audio_language) == expected
def downloaded_tracks(tmp_path: Path, audio: list[tuple[str, bool]], subtitles: list[tuple[str, str]]) -> Tracks:
tracks = Tracks()
for i, (lang, original) in enumerate(audio):
track = Audio(id_=f"a{i}", url=f"https://x.example/a{i}", language=lang, is_original_lang=original)
tracks.add(track)
for i, (lang, kind) in enumerate(subtitles):
tracks.add(subtitle(i, lang, kind))
for track in tracks:
track.path = tmp_path / f"{track.id}.bin"
track.path.write_bytes(b"\x00")
return tracks
def subtitle_defaults(args: list[str], count: int) -> list[str]:
return [args[i + 1] for i, arg in enumerate(args) if arg == "--default-track"][-count:]
def test_mux_reads_default_subtitle_type(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
caplog: pytest.LogCaptureFixture,
mux_args: Callable[..., list[str]],
) -> None:
monkeypatch.setattr(config, "muxing", {"default_subtitle_type": ["full", "CC", "Normal"]})
tracks = downloaded_tracks(tmp_path, [("en", True)], [("pl", "forced"), ("pl", "normal"), ("en", "sdh")])
with caplog.at_level(logging.WARNING):
args = mux_args(tracks)
assert subtitle_defaults(args, 3) == ["0:False", "0:False", "0:True"]
assert "'full' is not a subtitle type" in caplog.text
def test_the_forced_default_follows_the_default_audio(tmp_path: Path, mux_args: Callable[..., list[str]]) -> None:
tracks = downloaded_tracks(tmp_path, [("en", False), ("pl", True)], [("en", "forced"), ("pl", "forced")])
assert subtitle_defaults(mux_args(tracks), 2) == ["0:False", "0:True"]