Anpassung --cut flag
This commit is contained in:
@@ -958,7 +958,6 @@ def checkUniqueDispositions(context, mediaDescriptor: MediaDescriptor):
|
|||||||
metavar="DURATION|START,DURATION",
|
metavar="DURATION|START,DURATION",
|
||||||
is_flag=False,
|
is_flag=False,
|
||||||
flag_value=DEFAULT_CUT_OPTION_VALUE,
|
flag_value=DEFAULT_CUT_OPTION_VALUE,
|
||||||
default=None,
|
|
||||||
callback=normalizeCutOption,
|
callback=normalizeCutOption,
|
||||||
help=CUT_OPTION_HELP,
|
help=CUT_OPTION_HELP,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -95,7 +95,25 @@ def write_vtt(path: Path, lines: tuple[str, ...]) -> Path:
|
|||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
def create_source_fixture(workdir: Path, filename: str, tracks: list[SourceTrackSpec], duration_seconds: int = 1) -> Path:
|
def create_source_fixture(
|
||||||
|
workdir: Path,
|
||||||
|
filename: str,
|
||||||
|
tracks: list[SourceTrackSpec],
|
||||||
|
duration_seconds: int = 1,
|
||||||
|
*,
|
||||||
|
video_encoder: str = "libx264",
|
||||||
|
video_encoder_options: tuple[str, ...] = (
|
||||||
|
"-preset",
|
||||||
|
"ultrafast",
|
||||||
|
"-crf",
|
||||||
|
"35",
|
||||||
|
"-pix_fmt",
|
||||||
|
"yuv420p",
|
||||||
|
),
|
||||||
|
audio_encoder: str = "aac",
|
||||||
|
audio_encoder_options: tuple[str, ...] = ("-b:a", "48k"),
|
||||||
|
subtitle_encoder: str = "webvtt",
|
||||||
|
) -> Path:
|
||||||
output_path = workdir / filename
|
output_path = workdir / filename
|
||||||
|
|
||||||
has_video = any(track.track_type == TrackType.VIDEO for track in tracks)
|
has_video = any(track.track_type == TrackType.VIDEO for track in tracks)
|
||||||
@@ -189,21 +207,16 @@ def create_source_fixture(workdir: Path, filename: str, tracks: list[SourceTrack
|
|||||||
command += map_tokens
|
command += map_tokens
|
||||||
command += metadata_tokens
|
command += metadata_tokens
|
||||||
command += disposition_tokens
|
command += disposition_tokens
|
||||||
|
if has_video:
|
||||||
|
command += ["-c:v", video_encoder] + list(video_encoder_options)
|
||||||
|
|
||||||
|
if has_audio:
|
||||||
|
command += ["-c:a", audio_encoder] + list(audio_encoder_options)
|
||||||
|
|
||||||
|
if subtitle_input_indices:
|
||||||
|
command += ["-c:s", subtitle_encoder]
|
||||||
|
|
||||||
command += [
|
command += [
|
||||||
"-c:v",
|
|
||||||
"libx264",
|
|
||||||
"-preset",
|
|
||||||
"ultrafast",
|
|
||||||
"-crf",
|
|
||||||
"35",
|
|
||||||
"-pix_fmt",
|
|
||||||
"yuv420p",
|
|
||||||
"-c:a",
|
|
||||||
"aac",
|
|
||||||
"-b:a",
|
|
||||||
"48k",
|
|
||||||
"-c:s",
|
|
||||||
"webvtt",
|
|
||||||
"-t",
|
"-t",
|
||||||
str(duration_seconds),
|
str(duration_seconds),
|
||||||
"-shortest",
|
"-shortest",
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ from ffx.track_type import TrackType # noqa: E402
|
|||||||
|
|
||||||
|
|
||||||
class UnmuxSequenceTests(unittest.TestCase):
|
class UnmuxSequenceTests(unittest.TestCase):
|
||||||
def test_h265_video_unmux_uses_annex_b_bitstream_filter(self):
|
def test_h265_video_unmux_uses_annex_b_bitstream_filter_without_forced_format(self):
|
||||||
track_descriptor = TrackDescriptor(
|
track_descriptor = TrackDescriptor(
|
||||||
index=0,
|
index=0,
|
||||||
sub_index=0,
|
sub_index=0,
|
||||||
@@ -46,8 +46,6 @@ class UnmuxSequenceTests(unittest.TestCase):
|
|||||||
"copy",
|
"copy",
|
||||||
"-bsf:v",
|
"-bsf:v",
|
||||||
"hevc_mp4toannexb",
|
"hevc_mp4toannexb",
|
||||||
"-f",
|
|
||||||
"h265",
|
|
||||||
"episode_0_eng.h265",
|
"episode_0_eng.h265",
|
||||||
],
|
],
|
||||||
sequence,
|
sequence,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import sys
|
import sys
|
||||||
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
@@ -16,6 +17,7 @@ from ffx.i18n import set_current_language # noqa: E402
|
|||||||
from ffx.logging_utils import get_ffx_logger # noqa: E402
|
from ffx.logging_utils import get_ffx_logger # noqa: E402
|
||||||
from ffx.track_codec import TrackCodec # noqa: E402
|
from ffx.track_codec import TrackCodec # noqa: E402
|
||||||
from ffx.track_type import TrackType # noqa: E402
|
from ffx.track_type import TrackType # noqa: E402
|
||||||
|
from tests.support.ffx_bundle import SourceTrackSpec, create_source_fixture # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
class StaticConfig:
|
class StaticConfig:
|
||||||
@@ -39,25 +41,41 @@ class FilePropertiesAssetProbeTests(unittest.TestCase):
|
|||||||
}
|
}
|
||||||
set_current_language("de")
|
set_current_language("de")
|
||||||
|
|
||||||
media_path = (
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
Path(__file__).resolve().parents[1]
|
media_path = create_source_fixture(
|
||||||
/ "assets"
|
Path(tmpdir),
|
||||||
/ "Boruto; Naruto Next Generations (2017) - 0069 Super-Chochos Liebestaumel - S01E0069.webm"
|
"fixture.webm",
|
||||||
)
|
[
|
||||||
|
SourceTrackSpec(TrackType.VIDEO, identity="video-0"),
|
||||||
|
SourceTrackSpec(TrackType.AUDIO, identity="audio-1", language="eng"),
|
||||||
|
SourceTrackSpec(
|
||||||
|
TrackType.SUBTITLE,
|
||||||
|
identity="subtitle-2",
|
||||||
|
language="eng",
|
||||||
|
subtitle_lines=("Lorem ipsum dolor sit amet.",),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
duration_seconds=3,
|
||||||
|
video_encoder="libvpx-vp9",
|
||||||
|
video_encoder_options=("-b:v", "0", "-crf", "45"),
|
||||||
|
audio_encoder="libopus",
|
||||||
|
audio_encoder_options=("-b:a", "48k"),
|
||||||
|
subtitle_encoder="webvtt",
|
||||||
|
)
|
||||||
|
|
||||||
file_properties = FileProperties(context, str(media_path))
|
file_properties = FileProperties(context, str(media_path))
|
||||||
tracks = file_properties.getMediaDescriptor().getTrackDescriptors()
|
tracks = file_properties.getMediaDescriptor().getTrackDescriptors()
|
||||||
|
|
||||||
subtitle_codecs = [
|
subtitle_codecs = [
|
||||||
track.getCodec()
|
track.getCodec()
|
||||||
for track in tracks
|
for track in tracks
|
||||||
if track.getType() == TrackType.SUBTITLE
|
if track.getType() == TrackType.SUBTITLE
|
||||||
]
|
]
|
||||||
|
|
||||||
self.assertIn(TrackCodec.VP9, [track.getCodec() for track in tracks])
|
self.assertIn(TrackCodec.VP9, [track.getCodec() for track in tracks])
|
||||||
self.assertIn(TrackCodec.OPUS, [track.getCodec() for track in tracks])
|
self.assertIn(TrackCodec.OPUS, [track.getCodec() for track in tracks])
|
||||||
self.assertTrue(subtitle_codecs)
|
self.assertTrue(subtitle_codecs)
|
||||||
self.assertTrue(all(codec == TrackCodec.WEBVTT for codec in subtitle_codecs))
|
self.assertTrue(all(codec == TrackCodec.WEBVTT for codec in subtitle_codecs))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ if str(SRC_ROOT) not in sys.path:
|
|||||||
|
|
||||||
|
|
||||||
from ffx.logging_utils import get_ffx_logger # noqa: E402
|
from ffx.logging_utils import get_ffx_logger # noqa: E402
|
||||||
|
from ffx.helper import LogLevel # noqa: E402
|
||||||
from ffx.media_descriptor import MediaDescriptor # noqa: E402
|
from ffx.media_descriptor import MediaDescriptor # noqa: E402
|
||||||
from ffx.metadata_editor import ( # noqa: E402
|
from ffx.metadata_editor import ( # noqa: E402
|
||||||
apply_metadata_edits,
|
apply_metadata_edits,
|
||||||
@@ -33,6 +34,16 @@ class StaticConfig:
|
|||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
class NotificationCollector:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.messages: list[str] = []
|
||||||
|
self.levels: list[LogLevel | None] = []
|
||||||
|
|
||||||
|
def __call__(self, message: str, level: LogLevel | None = None) -> None:
|
||||||
|
self.messages.append(message)
|
||||||
|
self.levels.append(level)
|
||||||
|
|
||||||
|
|
||||||
def make_context(*, dry_run: bool = False) -> dict:
|
def make_context(*, dry_run: bool = False) -> dict:
|
||||||
return {
|
return {
|
||||||
"logger": get_ffx_logger(),
|
"logger": get_ffx_logger(),
|
||||||
@@ -151,7 +162,7 @@ class MetadataEditorTests(unittest.TestCase):
|
|||||||
context = make_context(dry_run=True)
|
context = make_context(dry_run=True)
|
||||||
baseline_descriptor = make_descriptor()
|
baseline_descriptor = make_descriptor()
|
||||||
draft_descriptor = baseline_descriptor.clone(context=context)
|
draft_descriptor = baseline_descriptor.clone(context=context)
|
||||||
notifications = []
|
notifications = NotificationCollector()
|
||||||
expected_command = build_metadata_edit_command(
|
expected_command = build_metadata_edit_command(
|
||||||
build_metadata_edit_context(context),
|
build_metadata_edit_context(context),
|
||||||
"/tmp/example.mkv",
|
"/tmp/example.mkv",
|
||||||
@@ -170,12 +181,13 @@ class MetadataEditorTests(unittest.TestCase):
|
|||||||
"/tmp/example.mkv",
|
"/tmp/example.mkv",
|
||||||
baseline_descriptor,
|
baseline_descriptor,
|
||||||
draft_descriptor,
|
draft_descriptor,
|
||||||
loggingHandler = notifications.append,
|
loggingHandler = notifications,
|
||||||
)
|
)
|
||||||
|
|
||||||
mocked_execute.assert_not_called()
|
mocked_execute.assert_not_called()
|
||||||
mocked_replace.assert_not_called()
|
mocked_replace.assert_not_called()
|
||||||
self.assertEqual(["ffmpeg dry-run prepared."], notifications)
|
self.assertEqual(["ffmpeg dry-run prepared."], notifications.messages)
|
||||||
|
self.assertEqual([None], notifications.levels)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
{
|
{
|
||||||
"applied": False,
|
"applied": False,
|
||||||
@@ -204,7 +216,7 @@ class MetadataEditorTests(unittest.TestCase):
|
|||||||
context["verbosity"] = 1
|
context["verbosity"] = 1
|
||||||
baseline_descriptor = make_descriptor()
|
baseline_descriptor = make_descriptor()
|
||||||
draft_descriptor = baseline_descriptor.clone(context=context)
|
draft_descriptor = baseline_descriptor.clone(context=context)
|
||||||
notifications = []
|
notifications = NotificationCollector()
|
||||||
|
|
||||||
with (
|
with (
|
||||||
patch("ffx.metadata_editor.create_temporary_output_path", return_value="/tmp/.edit.mkv"),
|
patch("ffx.metadata_editor.create_temporary_output_path", return_value="/tmp/.edit.mkv"),
|
||||||
@@ -216,11 +228,12 @@ class MetadataEditorTests(unittest.TestCase):
|
|||||||
"/tmp/example.mkv",
|
"/tmp/example.mkv",
|
||||||
baseline_descriptor,
|
baseline_descriptor,
|
||||||
draft_descriptor,
|
draft_descriptor,
|
||||||
loggingHandler = notifications.append,
|
loggingHandler = notifications,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(1, len(notifications))
|
self.assertEqual(1, len(notifications.messages))
|
||||||
self.assertTrue(notifications[0].startswith("ffmpeg: ffmpeg "))
|
self.assertTrue(notifications.messages[0].startswith("ffmpeg: ffmpeg "))
|
||||||
|
self.assertEqual([LogLevel.DEBUG], notifications.levels)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user