From f2747076a6fcfa64ea660f3301076a3c1e10b59f Mon Sep 17 00:00:00 2001 From: imSp4rky Date: Sat, 31 Oct 2026 12:21:19 -0600 Subject: [PATCH] fix(tracks): retry ccextractor on the repackaged file --- tests/tracks/test_ccextractor_retry.py | 41 ++++++++++++++++++++++++++ unshackle/core/tracks/video.py | 9 +++--- 2 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 tests/tracks/test_ccextractor_retry.py diff --git a/tests/tracks/test_ccextractor_retry.py b/tests/tracks/test_ccextractor_retry.py new file mode 100644 index 00000000..6e5b1b7f --- /dev/null +++ b/tests/tracks/test_ccextractor_retry.py @@ -0,0 +1,41 @@ +"""Tests for the repackaged-copy retry in ``Video.ccextractor``.""" + +from __future__ import annotations + +import subprocess +from pathlib import Path +from typing import Any + +import pytest +from langcodes import Language + +from unshackle.core.tracks import Video +from unshackle.core.tracks import video as video_module + + +def test_retry_runs_on_the_repackaged_file(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: + original = tmp_path / "v.mp4" + original.write_bytes(b"x") + repacked = tmp_path / "v_repack.mp4" + track = Video(id_="v", url="https://example.test/v.m3u8", language="en", codec=Video.Codec.AVC) + track.path = original + + def fake_repackage() -> None: + original.unlink() + repacked.write_bytes(b"x") + track.path = repacked + + inputs: list[Path] = [] + + def fake_run(args: list[Any], **_: Any) -> None: + inputs.append(Path(args[-1])) + if not Path(args[-1]).exists(): + raise subprocess.CalledProcessError(2, args) # ccextractor: no input file + raise subprocess.CalledProcessError(10, args) # ccextractor: no captions found + + monkeypatch.setattr(video_module.binaries, "CCExtractor", Path("/usr/bin/ccextractor")) + monkeypatch.setattr(video_module.subprocess, "run", fake_run) + monkeypatch.setattr(track, "repackage", fake_repackage) + + assert track.ccextractor("cc", tmp_path / "cc.srt", Language.get("en")) is None + assert inputs == [original, repacked] diff --git a/unshackle/core/tracks/video.py b/unshackle/core/tracks/video.py index 9b828006..dcc76b9d 100644 --- a/unshackle/core/tracks/video.py +++ b/unshackle/core/tracks/video.py @@ -655,9 +655,9 @@ class Video(Track): raise EnvironmentError("ccextractor executable was not found.") out_path = Path(out_path) - video_path, ccextractor = self.path, binaries.CCExtractor + ccextractor = binaries.CCExtractor - def run_ccextractor(fallback: str) -> bool: + def run_ccextractor(video_path: Path, fallback: str) -> bool: cc_start = time.monotonic() try: subprocess.run( @@ -692,9 +692,10 @@ class Video(Track): # Try on the original file first (preserves container-level CC data like c608 boxes), # then fall back to repacked file (ccextractor can fail on some container formats). - if not run_ccextractor("retrying on a repackaged copy"): + if not run_ccextractor(self.path, "retrying on a repackaged copy"): + # repackage() deletes the original file and moves self.path to the repacked copy self.repackage() - run_ccextractor("skipping CC extraction") + run_ccextractor(self.path, "skipping CC extraction") if out_path.exists(): cc_track = Subtitle(