Adapt unmux command to changes in convert command
This commit is contained in:
@@ -47,6 +47,10 @@ SUBTITLE_PREFIX_OPTION_HELP = (
|
|||||||
"Subtitle filename prefix. Requires --subtitle-directory, or a configured "
|
"Subtitle filename prefix. Requires --subtitle-directory, or a configured "
|
||||||
+ "subtitlesDirectory base path that contains a matching <prefix>/ subdirectory."
|
+ "subtitlesDirectory base path that contains a matching <prefix>/ subdirectory."
|
||||||
)
|
)
|
||||||
|
UNMUX_OUTPUT_DIRECTORY_OPTION_HELP = (
|
||||||
|
"Write extracted streams here. When omitted together with --subtitles-only and "
|
||||||
|
+ "--label, FFX uses the configured subtitlesDirectory base path plus the label."
|
||||||
|
)
|
||||||
CROPDETECT_SEEK_OPTION_HELP = (
|
CROPDETECT_SEEK_OPTION_HELP = (
|
||||||
"Start crop detection this many seconds into the input. "
|
"Start crop detection this many seconds into the input. "
|
||||||
+ "Useful for skipping logos, intros, or black frames."
|
+ "Useful for skipping logos, intros, or black frames."
|
||||||
@@ -160,6 +164,27 @@ def resolveSubtitleImportOptions(context, subtitleDirectory, subtitlePrefix):
|
|||||||
return True, resolvedSubtitleDirectory, resolvedSubtitlePrefix
|
return True, resolvedSubtitleDirectory, resolvedSubtitlePrefix
|
||||||
|
|
||||||
|
|
||||||
|
def resolveUnmuxOutputDirectory(context, outputDirectory, subtitlesOnly, label):
|
||||||
|
resolvedOutputDirectory = (
|
||||||
|
os.path.expanduser(str(outputDirectory).strip())
|
||||||
|
if outputDirectory
|
||||||
|
else ''
|
||||||
|
)
|
||||||
|
resolvedLabel = str(label).strip()
|
||||||
|
|
||||||
|
if resolvedOutputDirectory or not subtitlesOnly or not resolvedLabel:
|
||||||
|
return resolvedOutputDirectory, False
|
||||||
|
|
||||||
|
configuredSubtitlesBaseDirectory = context['config'].getSubtitlesDirectoryPath()
|
||||||
|
if not configuredSubtitlesBaseDirectory:
|
||||||
|
raise click.ClickException(
|
||||||
|
"Subtitles-only unmux with --label requires --output-directory or a configured "
|
||||||
|
+ "subtitlesDirectory default in ffx.json."
|
||||||
|
)
|
||||||
|
|
||||||
|
return os.path.join(configuredSubtitlesBaseDirectory, resolvedLabel), True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@click.group()
|
@click.group()
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
@@ -416,7 +441,7 @@ def getUnmuxSequence(trackDescriptor: TrackDescriptor, sourcePath, targetPrefix,
|
|||||||
|
|
||||||
@click.argument('paths', nargs=-1)
|
@click.argument('paths', nargs=-1)
|
||||||
@click.option('-l', '--label', type=str, default='', help='Label to be used as filename prefix')
|
@click.option('-l', '--label', type=str, default='', help='Label to be used as filename prefix')
|
||||||
@click.option("-o", "--output-directory", type=str, default='')
|
@click.option("-o", "--output-directory", type=str, default='', help=UNMUX_OUTPUT_DIRECTORY_OPTION_HELP)
|
||||||
@click.option("-s", "--subtitles-only", is_flag=True, default=False)
|
@click.option("-s", "--subtitles-only", is_flag=True, default=False)
|
||||||
@click.option(
|
@click.option(
|
||||||
'--nice',
|
'--nice',
|
||||||
@@ -454,6 +479,15 @@ def unmux(ctx,
|
|||||||
ctx.obj['resource_limits']['cpu_limit'] = cpu
|
ctx.obj['resource_limits']['cpu_limit'] = cpu
|
||||||
ctx.obj['resource_limits']['cpu_percent'] = cpu
|
ctx.obj['resource_limits']['cpu_percent'] = cpu
|
||||||
|
|
||||||
|
output_directory, create_output_directory = resolveUnmuxOutputDirectory(
|
||||||
|
ctx.obj,
|
||||||
|
output_directory,
|
||||||
|
subtitles_only,
|
||||||
|
label,
|
||||||
|
)
|
||||||
|
if create_output_directory and existingSourcePaths and not ctx.obj.get('dry_run', False):
|
||||||
|
os.makedirs(output_directory, exist_ok=True)
|
||||||
|
|
||||||
for sourcePath in existingSourcePaths:
|
for sourcePath in existingSourcePaths:
|
||||||
|
|
||||||
fp = FileProperties(ctx.obj, sourcePath)
|
fp = FileProperties(ctx.obj, sourcePath)
|
||||||
|
|||||||
106
tests/integration/test_cli_unmux.py
Normal file
106
tests/integration/test_cli_unmux.py
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
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, create_source_fixture
|
||||||
|
|
||||||
|
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 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)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
94
tests/unit/test_cli_unmux_output_directory.py
Normal file
94
tests/unit/test_cli_unmux_output_directory.py
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import click
|
||||||
|
|
||||||
|
|
||||||
|
SRC_ROOT = Path(__file__).resolve().parents[2] / "src"
|
||||||
|
|
||||||
|
if str(SRC_ROOT) not in sys.path:
|
||||||
|
sys.path.insert(0, str(SRC_ROOT))
|
||||||
|
|
||||||
|
|
||||||
|
from ffx import cli # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
class StaticConfig:
|
||||||
|
def __init__(self, subtitles_directory: str = ""):
|
||||||
|
self._subtitles_directory = subtitles_directory
|
||||||
|
|
||||||
|
def getSubtitlesDirectoryPath(self):
|
||||||
|
return self._subtitles_directory
|
||||||
|
|
||||||
|
|
||||||
|
class UnmuxOutputDirectoryTests(unittest.TestCase):
|
||||||
|
def test_subtitles_only_with_label_uses_configured_subtitles_base_directory(self):
|
||||||
|
with tempfile.TemporaryDirectory() as tempdir:
|
||||||
|
context = {
|
||||||
|
"config": StaticConfig(str(Path(tempdir) / "subtitles")),
|
||||||
|
}
|
||||||
|
|
||||||
|
resolved_output_directory, should_create = cli.resolveUnmuxOutputDirectory(
|
||||||
|
context,
|
||||||
|
"",
|
||||||
|
True,
|
||||||
|
"dball",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(str(Path(tempdir) / "subtitles" / "dball"), resolved_output_directory)
|
||||||
|
self.assertTrue(should_create)
|
||||||
|
|
||||||
|
def test_explicit_output_directory_keeps_existing_behavior(self):
|
||||||
|
with tempfile.TemporaryDirectory() as tempdir:
|
||||||
|
context = {
|
||||||
|
"config": StaticConfig(str(Path(tempdir) / "subtitles")),
|
||||||
|
}
|
||||||
|
explicit_output_directory = str(Path(tempdir) / "manual")
|
||||||
|
|
||||||
|
resolved_output_directory, should_create = cli.resolveUnmuxOutputDirectory(
|
||||||
|
context,
|
||||||
|
explicit_output_directory,
|
||||||
|
True,
|
||||||
|
"dball",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(explicit_output_directory, resolved_output_directory)
|
||||||
|
self.assertFalse(should_create)
|
||||||
|
|
||||||
|
def test_subtitles_only_without_label_keeps_existing_behavior(self):
|
||||||
|
context = {
|
||||||
|
"config": StaticConfig("/tmp/subtitles"),
|
||||||
|
}
|
||||||
|
|
||||||
|
resolved_output_directory, should_create = cli.resolveUnmuxOutputDirectory(
|
||||||
|
context,
|
||||||
|
"",
|
||||||
|
True,
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual("", resolved_output_directory)
|
||||||
|
self.assertFalse(should_create)
|
||||||
|
|
||||||
|
def test_subtitles_only_with_label_requires_configured_default_when_output_directory_is_missing(self):
|
||||||
|
context = {
|
||||||
|
"config": StaticConfig(""),
|
||||||
|
}
|
||||||
|
|
||||||
|
with self.assertRaises(click.ClickException) as caught:
|
||||||
|
cli.resolveUnmuxOutputDirectory(
|
||||||
|
context,
|
||||||
|
"",
|
||||||
|
True,
|
||||||
|
"dball",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertIn("subtitlesDirectory default", str(caught.exception))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user