diff --git a/bin/ffx.py b/bin/ffx.py index f5a26f3..3cbc36d 100755 --- a/bin/ffx.py +++ b/bin/ffx.py @@ -203,7 +203,7 @@ def convert(ctx, context = ctx.obj - context['dry_run'] = True # dry_run + context['dry_run'] = dry_run context['video_encoder'] = VideoEncoder.fromLabel(video_encoder) diff --git a/bin/ffx/ffx_controller.py b/bin/ffx/ffx_controller.py index b0376fe..68926c3 100644 --- a/bin/ffx/ffx_controller.py +++ b/bin/ffx/ffx_controller.py @@ -176,21 +176,25 @@ class FfxController(): # for subStreamIndex in range(len(subDescriptor)): for trackDescriptor in targetTrackDescriptors: - # Calculate source sub index. This applies only if a source media descriptor is defined. - if sourceTrackDescriptors: - changedTargetTrackDescriptor : TrackDescriptor = targetTrackDescriptors[trackDescriptor.getIndex()] - changedTargetTrackSourceIndex = changedTargetTrackDescriptor.getSourceIndex() - subIndex = sourceTrackDescriptors[changedTargetTrackSourceIndex].getSubIndex() - else: - subIndex = trackDescriptor.getSubIndex() + #HINT: No dispositions for pgs subtitle tracks that have no external file source + if (trackDescriptor.getExternalSourceFilePath() + or trackDescriptor.getCodec() != TrackDescriptor.CODEC_PGS): - streamIndicator = trackDescriptor.getType().indicator() - dispositionSet = trackDescriptor.getDispositionSet() + # Calculate source sub index. This applies only if a source media descriptor is defined. + if sourceTrackDescriptors: + changedTargetTrackDescriptor : TrackDescriptor = targetTrackDescriptors[trackDescriptor.getIndex()] + changedTargetTrackSourceIndex = changedTargetTrackDescriptor.getSourceIndex() + subIndex = sourceTrackDescriptors[changedTargetTrackSourceIndex].getSubIndex() + else: + subIndex = trackDescriptor.getSubIndex() - if dispositionSet: - dispositionTokens += [f"-disposition:{streamIndicator}:{subIndex}", '+'.join([d.label() for d in dispositionSet])] - else: - dispositionTokens += [f"-disposition:{streamIndicator}:{subIndex}", '0'] + streamIndicator = trackDescriptor.getType().indicator() + dispositionSet = trackDescriptor.getDispositionSet() + + if dispositionSet: + dispositionTokens += [f"-disposition:{streamIndicator}:{subIndex}", '+'.join([d.label() for d in dispositionSet])] + else: + dispositionTokens += [f"-disposition:{streamIndicator}:{subIndex}", '0'] return dispositionTokens @@ -198,7 +202,7 @@ class FfxController(): def generateMetadataTokens(self): """Source media descriptor is mandatory""" - mappingTokens = [] + metadataTokens = [] # click.echo(f"source media descriptor: track indices={[d.getIndex() for d in sourceMediaDescriptor.getAllTrackDescriptors()]}") # click.echo(f"target media descriptor: track indices={[d.getIndex() for d in targetMediaDescriptor.getAllTrackDescriptors()]}") @@ -215,15 +219,15 @@ class FfxController(): sourceTags = self.__sourceMediaDescriptor.getTags() targetTags = self.__targetMediaDescriptor.getTags() - + #TODO: Warum erscheint nur -1 im output? if DIFF_REMOVED_KEY in mediaDifferences[MediaDescriptor.TAGS_KEY].keys(): # for removedTagKey in mediaDifferences[MediaDescriptor.TAGS_KEY][DIFF_REMOVED_KEY]: # row = (f"removed media tag: key='{removedTagKey}' value='{sourceTags[removedTagKey]}'",) - # self.differencesTable.add_row(*map(str, row)) - mappingTokens += [f"-map_metadata:g", "-1"] + # self.differencesTable.add_row(*map(str, row)) + metadataTokens += [f"-map_metadata:g", "-1"] for targetMediaTagKey in targetTags: - mappingTokens += [f"-metadata:g", f"{targetMediaTagKey}={targetTags[targetMediaTagKey]}"] + metadataTokens += [f"-metadata:g", f"{targetMediaTagKey}={targetTags[targetMediaTagKey]}"] else: @@ -233,7 +237,7 @@ class FfxController(): click.echo(f"added metadata key='{addedTagKey}' value='{targetTags[addedTagKey]}'->'{targetTags[addedTagKey]}'") # self.differencesTable.add_row(*map(str, row)) #pass - mappingTokens += [f"-metadata:g", f"{addedTagKey}={targetTags[addedTagKey]}"] + metadataTokens += [f"-metadata:g", f"{addedTagKey}={targetTags[addedTagKey]}"] @@ -243,7 +247,7 @@ class FfxController(): click.echo(f"changed metadata key='{changedTagKey}' value='{sourceTags[changedTagKey]}'->'{targetTags[changedTagKey]}'") # self.differencesTable.add_row(*map(str, row)) #pass - mappingTokens += [f"-metadata:g", f"{changedTagKey}={targetTags[changedTagKey]}"] + metadataTokens += [f"-metadata:g", f"{changedTagKey}={targetTags[changedTagKey]}"] if MediaDescriptor.TRACKS_KEY in mediaDifferences.keys(): @@ -289,10 +293,10 @@ class FfxController(): #addedTagValue = targetTrackDescriptors[changedTargetTrackSourceIndex].getTags()[addedTagKey] - mappingTokens += [f"-map_metadata:s:{changedTargetTrackDescriptor.getType().indicator()}:{changedTargetSourceSubIndex}", "-1"] + metadataTokens += [f"-map_metadata:s:{changedTargetTrackDescriptor.getType().indicator()}:{changedTargetSourceSubIndex}", "-1"] for targetTrackTagKey, targetTrackTagValue in changedTargetTrackDescriptor.getTags(): - mappingTokens += [f"-metadata:s:{changedTargetTrackDescriptor.getType().indicator()}:{changedTargetSourceSubIndex}", + metadataTokens += [f"-metadata:s:{changedTargetTrackDescriptor.getType().indicator()}:{changedTargetSourceSubIndex}", f"{targetTrackTagKey}={targetTrackTagValue}"] else: @@ -310,7 +314,7 @@ class FfxController(): # click.echo(f"targetTrackDescriptors: subindex={[t.getSubIndex() for t in targetTrackDescriptors]} sourceindex={[t.getSourceIndex() for t in targetTrackDescriptors]} tags={[t.getTags() for t in targetTrackDescriptors]}") # click.echo(f"changed track_index={changedTrackIndex} indicator={changedTargetTrackDescriptor.getType().indicator()} key={addedTagKey} value={addedTagValue} source_index={changedSourceTrackIndex}") - mappingTokens += [f"-metadata:s:{changedTargetTrackDescriptor.getType().indicator()}:{changedTargetSourceSubIndex}", + metadataTokens += [f"-metadata:s:{changedTargetTrackDescriptor.getType().indicator()}:{changedTargetSourceSubIndex}", f"{addedTagKey}={addedTagValue}"] # media diff {'tracks': {'changed': {4: {'tags': {'added': {'Yolo'}}}}}} @@ -326,7 +330,7 @@ class FfxController(): # click.echo(f"targetTrackDescriptors: subindex={[t.getSubIndex() for t in targetTrackDescriptors]} sourceindex={[t.getSourceIndex() for t in targetTrackDescriptors]} tags={[t.getTags() for t in targetTrackDescriptors]}") # click.echo(f"changed track_index={changedTrackIndex} indicator={changedTargetTrackDescriptor.getType().indicator()} key={addedTagKey} value={addedTagValue} source_index={changedSourceTrackIndex}") - mappingTokens += [f"-metadata:s:{changedTargetTrackDescriptor.getType().indicator()}:{changedTargetSourceSubIndex}", + metadataTokens += [f"-metadata:s:{changedTargetTrackDescriptor.getType().indicator()}:{changedTargetSourceSubIndex}", f"{changedTagKey}={changedTagValue}"] # if TrackDescriptor.DISPOSITION_SET_KEY in changedTrackDiff.keys(): @@ -343,7 +347,7 @@ class FfxController(): # # self.differencesTable.add_row(*map(str, row)) # pass - return mappingTokens + return metadataTokens def runJob(self, @@ -390,11 +394,11 @@ class FfxController(): if videoEncoder == VideoEncoder.VP9: commandSequence1 = (commandTokens - + self.__targetMediaDescriptor.getInputMappingTokens() + + self.__targetMediaDescriptor.getInputMappingTokens(only_video=True) + self.generateVP9Pass1Tokens(int(quality))) if self.__context['perform_crop']: - commandSequence1 += FfxController.generateCropTokens() + commandSequence1 += self.generateCropTokens() commandSequence1 += FfxController.NULL_TOKENS @@ -420,7 +424,7 @@ class FfxController(): commandSequence2 += self.generateVP9Pass2Tokens(int(quality)) + self.generateAudioEncodingTokens() if self.__context['perform_crop']: - commandSequence2 += FfxController.generateCropTokens() + commandSequence2 += self.generateCropTokens() commandSequence2 += self.generateOutputTokens(targetPath, FfxController.DEFAULT_FILE_FORMAT, @@ -429,4 +433,6 @@ class FfxController(): click.echo(f"Command 2: {' '.join(commandSequence2)}") if not self.__context['dry_run']: - executeProcess(commandSequence2) + out, err, rc = executeProcess(commandSequence2) + if rc: + raise click.ClickException(f"Command resulted in error: rc={rc} error={err}") diff --git a/bin/ffx/media_descriptor.py b/bin/ffx/media_descriptor.py index 7a31135..44809f0 100644 --- a/bin/ffx/media_descriptor.py +++ b/bin/ffx/media_descriptor.py @@ -378,7 +378,7 @@ class MediaDescriptor: importedFilePath = rtd.getExternalSourceFilePath() - if not importedFilePath is None: + if importedFilePath: importFileTokens += [ "-i", importedFilePath, @@ -386,7 +386,7 @@ class MediaDescriptor: return importFileTokens - def getInputMappingTokens(self, use_sub_index: bool = True): + def getInputMappingTokens(self, use_sub_index: bool = True, only_video: bool = False): reorderedTrackDescriptors = self.getReorderedTrackDescriptors() inputMappingTokens = [] @@ -394,23 +394,33 @@ class MediaDescriptor: filePointer = 1 for rtd in reorderedTrackDescriptors: - importedFilePath = rtd.getExternalSourceFilePath() - trackType = rtd.getType() - if use_sub_index: - if importedFilePath is None: - inputMappingTokens += [ - "-map", - f"0:{trackType.indicator()}:{rtd.getSubIndex()}", - ] + + if (trackType == TrackType.VIDEO or not only_video): + + importedFilePath = rtd.getExternalSourceFilePath() + + if use_sub_index: + + if importedFilePath: + + inputMappingTokens += [ + "-map", + f"{filePointer}:{trackType.indicator()}:0", + ] + filePointer += 1 + + else: + + if rtd.getCodec() != TrackDescriptor.CODEC_PGS: + inputMappingTokens += [ + "-map", + f"0:{trackType.indicator()}:{rtd.getSubIndex()}", + ] + else: - inputMappingTokens += [ - "-map", - f"{filePointer}:{trackType.indicator()}:0", - ] - filePointer += 1 - else: - inputMappingTokens += ["-map", f"0:{rtd.getIndex()}"] + if rtd.getCodec() != TrackDescriptor.CODEC_PGS: + inputMappingTokens += ["-map", f"0:{rtd.getIndex()}"] return inputMappingTokens diff --git a/bin/ffx/model/track.py b/bin/ffx/model/track.py index 5872fff..6af0481 100644 --- a/bin/ffx/model/track.py +++ b/bin/ffx/model/track.py @@ -44,6 +44,7 @@ class Track(Base): disposition_flags = Column(Integer) + codec_name = Column(String) audio_layout = Column(Integer) @@ -133,6 +134,7 @@ class Track(Base): return cls(pattern_id = patternId, track_type = trackType, + codec_name = streamObj[TrackDescriptor.FFPROBE_CODEC_NAME_KEY], disposition_flags = sum([2**t.index() for (k,v) in streamObj[TrackDescriptor.FFPROBE_DISPOSITION_KEY].items() if v and (t := TrackDisposition.find(k)) is not None]), audio_layout = AudioLayout.identify(streamObj)) @@ -149,6 +151,9 @@ class Track(Base): def getType(self): return TrackType.fromIndex(self.track_type) + + def getCodec(self): + return str(self.codec_name) def getIndex(self): return int(self.index) if self.index is not None else -1 @@ -198,6 +203,8 @@ class Track(Base): kwargs[TrackDescriptor.SUB_INDEX_KEY] = subIndex kwargs[TrackDescriptor.TRACK_TYPE_KEY] = self.getType() + kwargs[TrackDescriptor.CODEC_NAME_KEY] = self.getCodec() + kwargs[TrackDescriptor.DISPOSITION_SET_KEY] = self.getDispositionSet() kwargs[TrackDescriptor.TAGS_KEY] = self.getTags() diff --git a/bin/ffx/track_controller.py b/bin/ffx/track_controller.py index f4558e3..2eae79f 100644 --- a/bin/ffx/track_controller.py +++ b/bin/ffx/track_controller.py @@ -30,6 +30,7 @@ class TrackController(): s = self.Session() track = Track(pattern_id = patId, track_type = int(trackDescriptor.getType().index()), + codec_name = str(trackDescriptor.getCodec()), index = int(trackDescriptor.getIndex()), source_index = int(trackDescriptor.getSourceIndex()), disposition_flags = int(TrackDisposition.toFlags(trackDescriptor.getDispositionSet())), @@ -66,6 +67,7 @@ class TrackController(): track : Track = q.first() track.track_type = int(trackDescriptor.getType().index()) + track.codec_name = str(trackDescriptor.getCodec()) track.audio_layout = int(trackDescriptor.getAudioLayout().index()) track.disposition_flags = int(TrackDisposition.toFlags(trackDescriptor.getDispositionSet())) diff --git a/bin/ffx/track_descriptor.py b/bin/ffx/track_descriptor.py index 84307f3..fda5c4a 100644 --- a/bin/ffx/track_descriptor.py +++ b/bin/ffx/track_descriptor.py @@ -21,12 +21,16 @@ class TrackDescriptor: TAGS_KEY = "tags" TRACK_TYPE_KEY = "track_type" + CODEC_NAME_KEY = "codec_name" AUDIO_LAYOUT_KEY = "audio_layout" FFPROBE_INDEX_KEY = "index" FFPROBE_DISPOSITION_KEY = "disposition" FFPROBE_TAGS_KEY = "tags" FFPROBE_CODEC_TYPE_KEY = "codec_type" + FFPROBE_CODEC_NAME_KEY = "codec_name" + + CODEC_PGS = 'hdmv_pgs_subtitle' def __init__(self, **kwargs): @@ -55,7 +59,7 @@ class TrackDescriptor: ) self.__externalSourceFilePath = kwargs[TrackDescriptor.EXTERNAL_SOURCE_FILE_PATH_KEY] else: - self.__externalSourceFilePath = None + self.__externalSourceFilePath = '' if TrackDescriptor.INDEX_KEY in kwargs.keys(): if type(kwargs[TrackDescriptor.INDEX_KEY]) is not int: @@ -92,6 +96,15 @@ class TrackDescriptor: else: self.__trackType = TrackType.UNKNOWN + if TrackDescriptor.CODEC_NAME_KEY in kwargs.keys(): + if type(kwargs[TrackDescriptor.CODEC_NAME_KEY]) is not str: + raise TypeError( + f"TrackDesciptor.__init__(): Argument {TrackDescriptor.CODEC_NAME_KEY} is required to be of type str" + ) + self.__codecName = kwargs[TrackDescriptor.CODEC_NAME_KEY] + else: + self.__codecName = '' + if TrackDescriptor.TAGS_KEY in kwargs.keys(): if type(kwargs[TrackDescriptor.TAGS_KEY]) is not dict: raise TypeError( @@ -187,6 +200,8 @@ class TrackDescriptor: kwargs[TrackDescriptor.SUB_INDEX_KEY] = subIndex kwargs[TrackDescriptor.TRACK_TYPE_KEY] = trackType + kwargs[TrackDescriptor.CODEC_NAME_KEY] = str(streamObj[TrackDescriptor.FFPROBE_CODEC_NAME_KEY]) + kwargs[TrackDescriptor.DISPOSITION_SET_KEY] = ( { t @@ -237,6 +252,9 @@ class TrackDescriptor: def getType(self): return self.__trackType + + def getCodec(self): + return self.__codecName def getLanguage(self): if "language" in self.__trackTags.keys():