229 lines
7.1 KiB
Python
229 lines
7.1 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
|
|
from tests.support.ffx_bundle import (
|
|
SourceTrackSpec,
|
|
build_controller_context,
|
|
create_source_fixture,
|
|
dispose_controller_context,
|
|
)
|
|
|
|
from ffx.pattern_controller import PatternController
|
|
from ffx.show_controller import ShowController
|
|
from ffx.show_descriptor import ShowDescriptor
|
|
from ffx.track_codec import TrackCodec
|
|
from ffx.track_descriptor import TrackDescriptor
|
|
from ffx.track_type import TrackType
|
|
|
|
try:
|
|
import pytest
|
|
except ImportError: # pragma: no cover - unittest-only environments
|
|
pytest = None
|
|
|
|
if pytest is not None:
|
|
pytestmark = [pytest.mark.integration]
|
|
|
|
|
|
SRC_ROOT = Path(__file__).resolve().parents[2] / "src"
|
|
|
|
|
|
def run_ffx_unmux(workdir: Path, home_dir: Path, database_path: Path, *args: str) -> subprocess.CompletedProcess[str]:
|
|
env = os.environ.copy()
|
|
env["HOME"] = str(home_dir)
|
|
existing_pythonpath = env.get("PYTHONPATH", "")
|
|
env["PYTHONPATH"] = str(SRC_ROOT) if not existing_pythonpath else f"{SRC_ROOT}{os.pathsep}{existing_pythonpath}"
|
|
|
|
command = [
|
|
sys.executable,
|
|
"-m",
|
|
"ffx",
|
|
"--database-file",
|
|
str(database_path),
|
|
"unmux",
|
|
*args,
|
|
]
|
|
return subprocess.run(command, cwd=workdir, env=env, capture_output=True, text=True)
|
|
|
|
|
|
class UnmuxCliTests(unittest.TestCase):
|
|
def setUp(self):
|
|
self.tempdir = tempfile.TemporaryDirectory()
|
|
self.workdir = Path(self.tempdir.name)
|
|
self.home_dir = self.workdir / "home"
|
|
self.home_dir.mkdir()
|
|
self.database_path = self.workdir / "test.db"
|
|
|
|
def tearDown(self):
|
|
self.tempdir.cleanup()
|
|
|
|
def write_config(self, data: dict) -> None:
|
|
config_dir = self.home_dir / ".local" / "etc"
|
|
config_dir.mkdir(parents=True, exist_ok=True)
|
|
(config_dir / "ffx.json").write_text(json.dumps(data), encoding="utf-8")
|
|
|
|
def assertCompleted(self, completed):
|
|
if completed.returncode != 0:
|
|
self.fail(
|
|
"FFX unmux failed\n"
|
|
f"STDOUT:\n{completed.stdout}\n"
|
|
f"STDERR:\n{completed.stderr}"
|
|
)
|
|
|
|
def seed_matching_show(self, pattern_expression: str, *, indicator_season_digits: int, indicator_episode_digits: int) -> None:
|
|
context = build_controller_context(self.database_path)
|
|
try:
|
|
ShowController(context).updateShow(
|
|
ShowDescriptor(
|
|
id=1,
|
|
name="Unmux Test Show",
|
|
year=2000,
|
|
indicator_season_digits=indicator_season_digits,
|
|
indicator_episode_digits=indicator_episode_digits,
|
|
)
|
|
)
|
|
PatternController(context).savePatternSchema(
|
|
{
|
|
"show_id": 1,
|
|
"pattern": pattern_expression,
|
|
"quality": 0,
|
|
"notes": "",
|
|
},
|
|
trackDescriptors=[
|
|
TrackDescriptor(
|
|
index=0,
|
|
source_index=0,
|
|
track_type=TrackType.VIDEO,
|
|
codec_name=TrackCodec.H264,
|
|
tags={},
|
|
disposition_set=set(),
|
|
)
|
|
],
|
|
)
|
|
finally:
|
|
dispose_controller_context(context)
|
|
|
|
def test_subtitles_only_without_output_directory_uses_configured_base_plus_label(self):
|
|
self.write_config(
|
|
{
|
|
"subtitlesDirectory": "~/.local/var/sync/subtitles",
|
|
}
|
|
)
|
|
source_filename = "unmux_s01e01.mkv"
|
|
source_path = create_source_fixture(
|
|
self.workdir,
|
|
source_filename,
|
|
[
|
|
SourceTrackSpec(TrackType.VIDEO, identity="video-0"),
|
|
SourceTrackSpec(
|
|
TrackType.SUBTITLE,
|
|
identity="subtitle-1",
|
|
language="eng",
|
|
subtitle_lines=("subtitle payload",),
|
|
),
|
|
],
|
|
)
|
|
|
|
completed = run_ffx_unmux(
|
|
self.workdir,
|
|
self.home_dir,
|
|
self.database_path,
|
|
"--subtitles-only",
|
|
"--label",
|
|
"dball",
|
|
str(source_path),
|
|
)
|
|
self.assertCompleted(completed)
|
|
|
|
expected_directory = self.home_dir / ".local" / "var" / "sync" / "subtitles" / "dball"
|
|
self.assertTrue(expected_directory.is_dir(), expected_directory)
|
|
|
|
def test_unmux_uses_configured_indicator_digits_in_output_filenames(self):
|
|
self.write_config(
|
|
{
|
|
"defaultIndicatorSeasonDigits": 3,
|
|
"defaultIndicatorEpisodeDigits": 4,
|
|
}
|
|
)
|
|
source_filename = "unmux_s01e01.mkv"
|
|
output_directory = self.workdir / "unmux-output"
|
|
output_directory.mkdir()
|
|
source_path = create_source_fixture(
|
|
self.workdir,
|
|
source_filename,
|
|
[
|
|
SourceTrackSpec(TrackType.VIDEO, identity="video-0"),
|
|
],
|
|
)
|
|
|
|
completed = run_ffx_unmux(
|
|
self.workdir,
|
|
self.home_dir,
|
|
self.database_path,
|
|
"--label",
|
|
"dball",
|
|
"--output-directory",
|
|
str(output_directory),
|
|
str(source_path),
|
|
)
|
|
self.assertCompleted(completed)
|
|
|
|
output_filenames = sorted(path.name for path in output_directory.iterdir())
|
|
self.assertEqual(1, len(output_filenames), output_filenames)
|
|
self.assertTrue(
|
|
output_filenames[0].startswith("dball_S001E0001_"),
|
|
output_filenames,
|
|
)
|
|
|
|
def test_unmux_prefers_matched_show_indicator_digits_over_config_defaults(self):
|
|
self.write_config(
|
|
{
|
|
"defaultIndicatorSeasonDigits": 4,
|
|
"defaultIndicatorEpisodeDigits": 4,
|
|
}
|
|
)
|
|
self.seed_matching_show(
|
|
r"^unmux_([sS][0-9]+[eE][0-9]+)\.mkv$",
|
|
indicator_season_digits=1,
|
|
indicator_episode_digits=3,
|
|
)
|
|
source_filename = "unmux_s01e01.mkv"
|
|
output_directory = self.workdir / "unmux-output"
|
|
output_directory.mkdir()
|
|
source_path = create_source_fixture(
|
|
self.workdir,
|
|
source_filename,
|
|
[
|
|
SourceTrackSpec(TrackType.VIDEO, identity="video-0"),
|
|
],
|
|
)
|
|
|
|
completed = run_ffx_unmux(
|
|
self.workdir,
|
|
self.home_dir,
|
|
self.database_path,
|
|
"--label",
|
|
"dball",
|
|
"--output-directory",
|
|
str(output_directory),
|
|
str(source_path),
|
|
)
|
|
self.assertCompleted(completed)
|
|
|
|
output_filenames = sorted(path.name for path in output_directory.iterdir())
|
|
self.assertEqual(1, len(output_filenames), output_filenames)
|
|
self.assertTrue(
|
|
output_filenames[0].startswith("dball_S1E001_"),
|
|
output_filenames,
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|