fix(mux): address embedded audio by the file's real mkvmerge track IDs

A TS file can hold its audio first, which put the audio name on the video.
This commit is contained in:
imSp4rky
2026-10-24 12:45:25 -06:00
parent ecf17baffe
commit 1036528fa3
6 changed files with 127 additions and 62 deletions
@@ -1,7 +1,11 @@
import json
from types import SimpleNamespace
import pytest
from unshackle.core import binaries
from unshackle.core.tracks import Tracks, Video
from unshackle.core.tracks.tracks import file_track_ids
def make_video(track_id: str, **data) -> Video:
@@ -19,6 +23,12 @@ def make_video(track_id: str, **data) -> Video:
return video
def downloaded(video: Video, tmp_path) -> Video:
video.path = tmp_path / "v.ts"
video.path.write_bytes(b"\x00" * 16)
return video
def mux_until_mkvmerge_check(tracks: Tracks) -> None:
# the embedded-audio metadata pass runs before the mkvmerge lookup, so a missing
# binary stops the mux right after the state under test has been written
@@ -35,7 +45,7 @@ def test_names_undeclared_video(monkeypatch):
assert video.needs_repack
assert video.data["audio_language"] == "zh"
assert video.data["audio_language_name"] == "Chinese"
assert video.embedded_audio_name() == "Chinese"
def test_service_declaration_is_preserved(monkeypatch):
@@ -50,3 +60,36 @@ def test_service_declaration_is_preserved(monkeypatch):
assert declared.data["audio_language_name"] == "Thai [Original]"
assert not declared.needs_repack
assert undeclared.data["audio_language"] == "zh"
def test_embedded_audio_without_a_name_gets_the_language_name(tmp_path, mux_args):
args = mux_args(Tracks(downloaded(make_video("v1", audio_language="pt-BR"), tmp_path)), {"video": 0, "audio": 1})
assert args[args.index("--track-name") + 1] == "1:Portuguese"
def test_embedded_audio_options_use_the_file_track_ids(tmp_path, mux_args):
video = make_video("v1", audio_language="ko", audio_language_name="Korean")
args = mux_args(Tracks(downloaded(video, tmp_path)), {"audio": 0, "video": 1})
assert args[args.index("--track-name") + 1] == "0:Korean"
original = f"{video.is_original_lang}"
assert [args[i + 1] for i, arg in enumerate(args) if arg == "--original-flag"] == [f"1:{original}", f"0:{original}"]
assert "0:ko" in args and "1:zh" in args
def test_embedded_audio_options_are_left_out_when_the_file_has_no_audio(tmp_path, mux_args):
args = mux_args(Tracks(downloaded(make_video("v1", audio_language="ko"), tmp_path)), {"video": 0})
assert "--track-name" not in args
def test_repack_and_mux_share_the_embedded_audio_name():
assert make_video("v1", audio_language="zh").embedded_audio_name() == "Chinese"
assert make_video("v1", audio_language="zh", audio_language_name="Mandarin").embedded_audio_name() == "Mandarin"
def test_file_track_ids_reads_the_first_id_of_each_type(monkeypatch, tmp_path):
tracks = [{"id": 0, "type": "audio"}, {"id": 1, "type": "video"}, {"id": 2, "type": "audio"}]
reply = SimpleNamespace(stdout=json.dumps({"tracks": tracks}))
monkeypatch.setattr("unshackle.core.tracks.tracks.subprocess.run", lambda *a, **k: reply)
assert file_track_ids(tmp_path / "v.ts") == {"audio": 0, "video": 1}
monkeypatch.setattr("unshackle.core.tracks.tracks.subprocess.run", lambda *a, **k: SimpleNamespace(stdout=""))
assert file_track_ids(tmp_path / "v.ts") == {"video": 0, "audio": 1}
+4 -17
View File
@@ -1,10 +1,9 @@
from pathlib import Path
from typing import Optional
from typing import Callable, Optional
import pytest
from langcodes import Language
from unshackle.core import binaries
from unshackle.core.config import config
from unshackle.core.tracks import Audio, Subtitle, Tracks, Video
from unshackle.core.utilities import language_name, multi_script_languages, with_script
@@ -130,18 +129,7 @@ def test_a_service_name_wins() -> None:
assert sub("zh", name="简体中文").get_track_name() == "简体中文"
def test_mkvmerge_gets_the_subtitle_script_and_name(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
class Stop(Exception):
pass
captured: list[list[str]] = []
def fake_popen(args: list[str], **kwargs: object) -> None:
captured.append(args)
raise Stop
monkeypatch.setattr("unshackle.core.tracks.tracks.subprocess.Popen", fake_popen)
monkeypatch.setattr(binaries, "MKVToolNix", Path("mkvmerge"))
def test_mkvmerge_gets_the_subtitle_script_and_name(tmp_path: Path, mux_args: Callable[..., list[str]]) -> None:
video = Video(id_="v", url="https://x/v", codec=Video.Codec.AVC, language="zh-TW", width=1920, height=1080)
audio = Audio(id_="a", url="https://x/a", codec=Audio.Codec.AAC, language="zh-TW", bitrate=1)
subtitle = sub("zh-TW")
@@ -149,12 +137,11 @@ def test_mkvmerge_gets_the_subtitle_script_and_name(tmp_path: Path, monkeypatch:
track.path = tmp_path / file
track.path.write_bytes(b"\x00" * 16)
with pytest.raises(Stop):
Tracks([video, audio, subtitle]).mux("T", delete=False, output_path=tmp_path / "o.mkv")
args = mux_args(Tracks([video, audio, subtitle]), output_path=tmp_path / "o.mkv")
def last_option(file: str, flag: str) -> str:
"""Return the value of the last flag given before the file, which is the one mkvmerge applies to it."""
before = captured[0][: captured[0].index(str(tmp_path / file))]
before = args[: args.index(str(tmp_path / file))]
return before[len(before) - before[::-1].index(flag)]
assert last_option("s.vtt", "--language") == "0:zh-Hant-TW"