From 697645be2593ceaf650ad600c09c7f58a634db81 Mon Sep 17 00:00:00 2001 From: Javanaut Date: Tue, 7 Jul 2026 17:53:43 +0200 Subject: [PATCH] TF deleted subtracks --- src/ffx/cli.py | 5 +- src/ffx/media_descriptor.py | 66 +++++---- tests/unit/test_media_descriptor_mapping.py | 146 ++++++++++++++++++++ 3 files changed, 186 insertions(+), 31 deletions(-) create mode 100644 tests/unit/test_media_descriptor_mapping.py diff --git a/src/ffx/cli.py b/src/ffx/cli.py index 5749ff1..6d2c8f5 100755 --- a/src/ffx/cli.py +++ b/src/ffx/cli.py @@ -1717,7 +1717,10 @@ def convert(ctx, # ctx.obj['logger'].debug(f"tmd subindices: {[t.getIndex() for t in targetMediaDescriptor.getAllTrackDescriptors()]} {[t.getSubIndex() for t in targetMediaDescriptor.getAllTrackDescriptors()]} {[t.getDispositionFlag(TrackDisposition.DEFAULT) for t in targetMediaDescriptor.getAllTrackDescriptors()]}") ctx.obj['logger'].debug(f"tmd subindices: {[t.getIndex() for t in targetMediaDescriptor.getTrackDescriptors()]} {[t.getSubIndex() for t in targetMediaDescriptor.getTrackDescriptors()]} {[t.getDispositionFlag(TrackDisposition.DEFAULT) for t in targetMediaDescriptor.getTrackDescriptors()]}") - ctx.obj['logger'].debug(f"Input mapping tokens (2nd pass): {targetMediaDescriptor.getInputMappingTokens()}") + ctx.obj['logger'].debug( + "Input mapping tokens (2nd pass): " + + f"{targetMediaDescriptor.getInputMappingTokens(sourceMediaDescriptor=sourceMediaDescriptor)}" + ) fc = FfxController(context, targetMediaDescriptor, sourceMediaDescriptor) diff --git a/src/ffx/media_descriptor.py b/src/ffx/media_descriptor.py index 92eb25c..2768d6f 100644 --- a/src/ffx/media_descriptor.py +++ b/src/ffx/media_descriptor.py @@ -473,45 +473,48 @@ class MediaDescriptor: td: TrackDescriptor = sortedTrackDescriptors[trackIndex] #HINT: Attached thumbnails are not supported by .webm container format - if td.getCodec() != TrackCodec.PNG: + trackType = td.getType() + trackCodec = td.getCodec() + if ( + trackCodec == TrackCodec.PNG + or trackType == TrackType.ATTACHMENT + or (only_video and trackType != TrackType.VIDEO) + ): + continue - sourceTrackDescriptor = sourceTrackDescriptorsByIndex.get(td.getSourceIndex()) - if sourceTrackDescriptor is None: - raise ValueError(f"No source track descriptor found for source index {td.getSourceIndex()}") + sourceTrackDescriptor = sourceTrackDescriptorsByIndex.get(td.getSourceIndex()) + if sourceTrackDescriptor is None: + raise ValueError(f"No source track descriptor found for source index {td.getSourceIndex()}") - stdi = sourceTrackDescriptor.getIndex() - stdsi = sourceTrackDescriptor.getSubIndex() + stdi = sourceTrackDescriptor.getIndex() + stdsi = sourceTrackDescriptor.getSubIndex() - trackType = td.getType() - trackCodec = td.getCodec() + importedFilePath = td.getExternalSourceFilePath() - if (trackType != TrackType.ATTACHMENT - and (trackType == TrackType.VIDEO or not only_video)): + if use_sub_index: + if importedFilePath: - importedFilePath = td.getExternalSourceFilePath() + inputMappingTokens += [ + "-map", + f"{filePointer}:{trackType.indicator()}:0", + ] + filePointer += 1 - if use_sub_index: + else: - if importedFilePath: + if trackCodec not in [TrackCodec.PGS, TrackCodec.VOBSUB]: + inputMappingTokens += [ + "-map", + f"0:{trackType.indicator()}:{stdsi}", + ] - inputMappingTokens += [ - "-map", - f"{filePointer}:{trackType.indicator()}:0", - ] - filePointer += 1 + else: + if trackCodec not in [TrackCodec.PGS, TrackCodec.VOBSUB]: + inputMappingTokens += ["-map", f"0:{stdi}"] - else: - - if not trackCodec in [TrackCodec.PGS, TrackCodec.VOBSUB]: - inputMappingTokens += [ - "-map", - f"0:{trackType.indicator()}:{stdsi}", - ] - - else: - if not trackCodec in [TrackCodec.PGS, TrackCodec.VOBSUB]: - inputMappingTokens += ["-map", f"0:{stdi}"] + if only_video: + return inputMappingTokens if sourceMediaDescriptor: fontDescriptors = [ftd for ftd in sourceMediaDescriptor.getAttachmentTracks() @@ -522,7 +525,10 @@ class MediaDescriptor: and ftd.getAttachmentFormat() == AttachmentFormat.TTF] for ad in sorted(fontDescriptors, key=lambda d: d.getIndex()): - inputMappingTokens += ["-map", f"0:{ad.getIndex()}"] + sourceIndex = ad.getIndex() + if not sourceMediaDescriptor and ad.getSourceIndex() > -1: + sourceIndex = ad.getSourceIndex() + inputMappingTokens += ["-map", f"0:{sourceIndex}"] return inputMappingTokens diff --git a/tests/unit/test_media_descriptor_mapping.py b/tests/unit/test_media_descriptor_mapping.py new file mode 100644 index 0000000..bbba89f --- /dev/null +++ b/tests/unit/test_media_descriptor_mapping.py @@ -0,0 +1,146 @@ +from __future__ import annotations + +from pathlib import Path +import sys +import unittest + + +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.attachment_format import AttachmentFormat # noqa: E402 +from ffx.media_descriptor import MediaDescriptor # noqa: E402 +from ffx.track_codec import TrackCodec # noqa: E402 +from ffx.track_descriptor import TrackDescriptor # noqa: E402 +from ffx.track_type import TrackType # noqa: E402 + + +class MediaDescriptorMappingTests(unittest.TestCase): + def make_target_descriptor(self) -> MediaDescriptor: + return MediaDescriptor( + track_descriptors=[ + TrackDescriptor( + index=0, + source_index=0, + sub_index=0, + track_type=TrackType.VIDEO, + codec_name=TrackCodec.H264, + ), + TrackDescriptor( + index=1, + source_index=7, + sub_index=0, + track_type=TrackType.SUBTITLE, + codec_name=TrackCodec.ASS, + ), + TrackDescriptor( + index=2, + source_index=8, + sub_index=1, + track_type=TrackType.SUBTITLE, + codec_name=TrackCodec.ASS, + ), + TrackDescriptor( + index=3, + source_index=9, + sub_index=0, + track_type=TrackType.ATTACHMENT, + attachment_format=AttachmentFormat.TTF, + ), + ] + ) + + def make_source_descriptor(self) -> MediaDescriptor: + return MediaDescriptor( + track_descriptors=[ + TrackDescriptor( + index=0, + source_index=0, + sub_index=0, + track_type=TrackType.VIDEO, + codec_name=TrackCodec.H264, + ), + TrackDescriptor( + index=3, + source_index=3, + sub_index=0, + track_type=TrackType.SUBTITLE, + codec_name=TrackCodec.VOBSUB, + ), + TrackDescriptor( + index=4, + source_index=4, + sub_index=1, + track_type=TrackType.SUBTITLE, + codec_name=TrackCodec.VOBSUB, + ), + TrackDescriptor( + index=5, + source_index=5, + sub_index=2, + track_type=TrackType.SUBTITLE, + codec_name=TrackCodec.PGS, + ), + TrackDescriptor( + index=6, + source_index=6, + sub_index=3, + track_type=TrackType.SUBTITLE, + codec_name=TrackCodec.PGS, + ), + TrackDescriptor( + index=7, + source_index=7, + sub_index=4, + track_type=TrackType.SUBTITLE, + codec_name=TrackCodec.ASS, + ), + TrackDescriptor( + index=8, + source_index=8, + sub_index=5, + track_type=TrackType.SUBTITLE, + codec_name=TrackCodec.ASS, + ), + TrackDescriptor( + index=9, + source_index=9, + sub_index=0, + track_type=TrackType.ATTACHMENT, + attachment_format=AttachmentFormat.TTF, + ), + ] + ) + + def test_source_aware_mapping_uses_original_subtitle_and_attachment_indices(self): + tokens = self.make_target_descriptor().getInputMappingTokens( + sourceMediaDescriptor=self.make_source_descriptor() + ) + + self.assertEqual( + [ + "-map", + "0:v:0", + "-map", + "0:s:4", + "-map", + "0:s:5", + "-map", + "0:9", + ], + tokens, + ) + + def test_video_only_mapping_does_not_map_font_attachments(self): + tokens = self.make_target_descriptor().getInputMappingTokens( + only_video=True + ) + + self.assertEqual(["-map", "0:v:0"], tokens) + + +if __name__ == "__main__": + unittest.main()