From 8ad50fd8814b5458cb9db9885201e6241cbea465 Mon Sep 17 00:00:00 2001 From: Maveno Date: Wed, 18 Sep 2024 07:39:42 +0200 Subject: [PATCH] decon/inc stream parsing --- bin/ffx.py | 193 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 110 insertions(+), 83 deletions(-) diff --git a/bin/ffx.py b/bin/ffx.py index 55a2f65..8afb256 100755 --- a/bin/ffx.py +++ b/bin/ffx.py @@ -209,30 +209,29 @@ def getStreamDescriptor(filename): for subStream in streamData: - s = subStream.copy() - - #Defaulting to undefined if tag not defined for stream - if 'tags' in subStream.keys() and 'language' in subStream['tags'].keys(): - s['language'] = subStream['tags']['language'] - else: - s['language'] = 'undefined' - - #Defaulting to undefined if tag not defined for stream - if 'tags' in subStream.keys() and 'title' in subStream['tags'].keys(): - s['title'] = subStream['tags']['title'] - else: - s['title'] = 'undefined' + if not 'disposition' in subStream.keys(): + subStream['disposition'] = {} + if not 'default' in subStream['disposition'].keys(): + subStream['disposition']['default'] = 0 + if not 'forced' in subStream['disposition'].keys(): + subStream['disposition']['forced'] = 0 + if not 'tags' in subStream.keys(): + subStream['tags'] = {} + if not 'language' in subStream['tags'].keys(): + subStream['tags']['language'] = 'undefined' + if not 'title' in subStream['tags'].keys(): + subStream['tags']['title'] = 'undefined' if subStream['codec_type'] == STREAM_TYPE_AUDIO: if 'channel_layout' in subStream.keys(): - s['layout'] = subStream['channel_layout'] + subStream['audio_layout'] = subStream['channel_layout'] elif subStream['channels'] == 6: - s['layout'] = STREAM_LAYOUT_6CH + subStream['audio_layout'] = STREAM_LAYOUT_6CH else: - s['layout'] = 'undefined' + subStream['audio_layout'] = 'undefined' - descriptor[s['codec_type']].append(s) - descriptor[s['codec_type']][-1]['src_sub_index'] = len(descriptor[s['codec_type']]) - 1 + descriptor[subStream['codec_type']].append(subStream) + descriptor[subStream['codec_type']][-1]['sub_index'] = len(descriptor[subStream['codec_type']]) - 1 return descriptor @@ -247,13 +246,13 @@ def getModifiedStreamOrder(length, last): return seq -def getReorderedSubstreams(subDescriptor, last): - numSubStreams = len(subDescriptor) - modifiedOrder = getModifiedStreamOrder(numSubStreams, last) - reorderedDescriptor = [] - for streamIndex in range(numSubStreams): - reorderedDescriptor.append(subDescriptor[modifiedOrder[streamIndex]]) - return reorderedDescriptor +# def getReorderedSubstreams(subDescriptor, last): +# numSubStreams = len(subDescriptor) +# modifiedOrder = getModifiedStreamOrder(numSubStreams, last) +# reorderedDescriptor = [] +# for streamIndex in range(numSubStreams): +# reorderedDescriptor.append(subDescriptor[modifiedOrder[streamIndex]]) +# return reorderedDescriptor def generateAV1Tokens(q, p): @@ -619,46 +618,61 @@ def convert(ctx, targetFilenameTokens = [sourceFileBasename] + # SSD -> TSD -> MSO + # + # src file [vtt files] -> SSD + # + # in opts, checks -> TSD + # + # jellyfin -> MSO + # + # TSD - SSD, MSO -> out opts + + # Load source stream descriptor try: + ### sourceStreamDescriptor = getStreamDescriptor(sourcePath) - - # Ensure disposition and tags keys for every stream - for substreamIndex in range(len(sourceStreamDescriptor[STREAM_TYPE_AUDIO])): - if not 'disposition' in sourceStreamDescriptor[STREAM_TYPE_AUDIO][substreamIndex].keys(): - sourceStreamDescriptor[STREAM_TYPE_AUDIO][substreamIndex]['disposition'] = {} - if not 'default' in sourceStreamDescriptor[STREAM_TYPE_AUDIO][substreamIndex]['disposition'].keys(): - sourceStreamDescriptor[STREAM_TYPE_AUDIO][substreamIndex]['disposition']['default'] = 0 - if not 'forced' in sourceStreamDescriptor[STREAM_TYPE_AUDIO][substreamIndex]['disposition'].keys(): - sourceStreamDescriptor[STREAM_TYPE_AUDIO][substreamIndex]['disposition']['forced'] = 0 - for substreamIndex in range(len(sourceStreamDescriptor[STREAM_TYPE_AUDIO])): - if not 'tags' in sourceStreamDescriptor[STREAM_TYPE_AUDIO][substreamIndex].keys(): - sourceStreamDescriptor[STREAM_TYPE_AUDIO][substreamIndex]['tags'] = {} - for substreamIndex in range(len(sourceStreamDescriptor[STREAM_TYPE_SUBTITLE])): - if not 'disposition' in sourceStreamDescriptor[STREAM_TYPE_SUBTITLE][substreamIndex].keys(): - sourceStreamDescriptor[STREAM_TYPE_SUBTITLE][substreamIndex]['disposition'] = {} - if not 'default' in sourceStreamDescriptor[STREAM_TYPE_SUBTITLE][substreamIndex]['disposition'].keys(): - sourceStreamDescriptor[STREAM_TYPE_SUBTITLE][substreamIndex]['disposition']['default'] = 0 - if not 'forced' in sourceStreamDescriptor[STREAM_TYPE_SUBTITLE][substreamIndex]['disposition'].keys(): - sourceStreamDescriptor[STREAM_TYPE_SUBTITLE][substreamIndex]['disposition']['forced'] = 0 - for substreamIndex in range(len(sourceStreamDescriptor[STREAM_TYPE_SUBTITLE])): - if not 'tags' in sourceStreamDescriptor[STREAM_TYPE_SUBTITLE][substreamIndex].keys(): - sourceStreamDescriptor[STREAM_TYPE_SUBTITLE][substreamIndex]['tags'] = {} - - targetStreamDescriptor = sourceStreamDescriptor.copy() + ### except Exception: click.echo(f"File with path {sourcePath} does not contain any audiovisual data, skipping ...") continue - - click.echo('Source streans:') +# # Ensure disposition and tags keys for every stream +# for substreamIndex in range(len(sourceStreamDescriptor[STREAM_TYPE_AUDIO])): +# if not 'disposition' in sourceStreamDescriptor[STREAM_TYPE_AUDIO][substreamIndex].keys(): +# sourceStreamDescriptor[STREAM_TYPE_AUDIO][substreamIndex]['disposition'] = {} +# if not 'default' in sourceStreamDescriptor[STREAM_TYPE_AUDIO][substreamIndex]['disposition'].keys(): +# sourceStreamDescriptor[STREAM_TYPE_AUDIO][substreamIndex]['disposition']['default'] = 0 +# if not 'forced' in sourceStreamDescriptor[STREAM_TYPE_AUDIO][substreamIndex]['disposition'].keys(): +# sourceStreamDescriptor[STREAM_TYPE_AUDIO][substreamIndex]['disposition']['forced'] = 0 +# for substreamIndex in range(len(sourceStreamDescriptor[STREAM_TYPE_AUDIO])): +# if not 'tags' in sourceStreamDescriptor[STREAM_TYPE_AUDIO][substreamIndex].keys(): +# sourceStreamDescriptor[STREAM_TYPE_AUDIO][substreamIndex]['tags'] = {} +# for substreamIndex in range(len(sourceStreamDescriptor[STREAM_TYPE_SUBTITLE])): +# if not 'disposition' in sourceStreamDescriptor[STREAM_TYPE_SUBTITLE][substreamIndex].keys(): +# sourceStreamDescriptor[STREAM_TYPE_SUBTITLE][substreamIndex]['disposition'] = {} +# if not 'default' in sourceStreamDescriptor[STREAM_TYPE_SUBTITLE][substreamIndex]['disposition'].keys(): +# sourceStreamDescriptor[STREAM_TYPE_SUBTITLE][substreamIndex]['disposition']['default'] = 0 +# if not 'forced' in sourceStreamDescriptor[STREAM_TYPE_SUBTITLE][substreamIndex]['disposition'].keys(): +# sourceStreamDescriptor[STREAM_TYPE_SUBTITLE][substreamIndex]['disposition']['forced'] = 0 +# for substreamIndex in range(len(sourceStreamDescriptor[STREAM_TYPE_SUBTITLE])): +# if not 'tags' in sourceStreamDescriptor[STREAM_TYPE_SUBTITLE][substreamIndex].keys(): +# sourceStreamDescriptor[STREAM_TYPE_SUBTITLE][substreamIndex]['tags'] = {} + + ### + targetStreamDescriptor = sourceStreamDescriptor.copy() + ### + + + click.echo('\nSource streams:') for aStream in sourceStreamDescriptor[STREAM_TYPE_AUDIO]: - click.echo(f"audio stream {aStream['src_sub_index']} lang={aStream['language']} title={aStream['title']} default={aStream['disposition']['default']} forced={aStream['disposition']['forced']}") - + click.echo(f"audio stream {aStream['sub_index']} lang={aStream['tags']['language']} title={aStream['tags']['title']} default={aStream['disposition']['default']} forced={aStream['disposition']['forced']}") for sStream in sourceStreamDescriptor[STREAM_TYPE_SUBTITLE]: - click.echo(f"subtitle stream {sStream['src_sub_index']} lang={sStream['language']} title={sStream['title']} default={sStream['disposition']['default']} forced={sStream['disposition']['forced']}") + click.echo(f"subtitle stream {sStream['sub_index']} lang={sStream['tags']['language']} title={sStream['tags']['title']} default={sStream['disposition']['default']} forced={sStream['disposition']['forced']}") + # Check for multiple default or forced dispositions if not set by user input or database requirements #NOTE: It is currently expected that all source file have the same substream pattern, e.g. coming from the same encoder numDefaultAudioStreams = len([a for a in sourceStreamDescriptor[STREAM_TYPE_AUDIO] if a['disposition']['default'] == 1]) if defaultAudio == -1 and numDefaultAudioStreams > 1: @@ -676,7 +690,6 @@ def convert(ctx, if forcedSubtitle == -1 and numForcedSubtitleStreams > 1: forcedSubtitle = click.prompt("More than one forced subtitle stream detected! Please select stream", type=int) - #Fix multiple default/forced tags if defaultAudio != -1: for substreamIndex in range(len(targetStreamDescriptor[STREAM_TYPE_AUDIO])): @@ -692,14 +705,50 @@ def convert(ctx, targetStreamDescriptor[STREAM_TYPE_SUBTITLE][substreamIndex]['disposition']['forded'] = 1 if substreamIndex == forcedSubtitle else 0 - click.echo('Target streans:') - for aStream in targetStreamDescriptor[STREAM_TYPE_AUDIO]: - click.echo(f"audio stream {aStream['src_sub_index']} lang={aStream['language']} title={aStream['title']} default={aStream['disposition']['default']} forced={aStream['disposition']['forced']}") - for sStream in targetStreamDescriptor[STREAM_TYPE_SUBTITLE]: - click.echo(f"subtitle stream {sStream['src_sub_index']} lang={sStream['language']} title={sStream['title']} default={sStream['disposition']['default']} forced={sStream['disposition']['forced']}") + ## Process input opts + +# # Source stream descriptors +# audioStreams = targetStreamDescriptor[STREAM_TYPE_AUDIO] +# subtitleStreams = targetStreamDescriptor[STREAM_TYPE_SUBTITLE] +# +# # Set language and title in source stream descriptors if given per command line option +# for streamIndex in range(len(audioStreams)): +# if streamIndex <= len(audioLanguages) - 1: +# audioStreams[streamIndex]['tags']['language'] = audioLanguages[streamIndex] +# if streamIndex <= len(audioTitles) - 1: +# audioStreams[streamIndex]['tags']['title'] = audioTitles[streamIndex] +# +# for streamIndex in range(len(subtitleStreams)): +# if streamIndex <= len(subtitleLanguages) - 1: +# subtitleStreams[streamIndex]['tags']['language'] = subtitleLanguages[streamIndex] +# if streamIndex <= len(subtitleTitles) - 1: +# subtitleStreams[streamIndex]['tags']['title'] = subtitleTitles[streamIndex] + # Set language and title in source stream descriptors if given per command line option + for streamIndex in range(len(targetStreamDescriptor[STREAM_TYPE_AUDIO])): + if streamIndex <= len(audioLanguages) - 1: + targetStreamDescriptor[STREAM_TYPE_AUDIO][streamIndex]['tags']['language'] = audioLanguages[streamIndex] + if streamIndex <= len(audioTitles) - 1: + targetStreamDescriptor[STREAM_TYPE_AUDIO][streamIndex]['tags']['title'] = audioTitles[streamIndex] + + for streamIndex in range(len(targetStreamDescriptor[STREAM_TYPE_SUBTITLE])): + if streamIndex <= len(subtitleLanguages) - 1: + targetStreamDescriptor[STREAM_TYPE_SUBTITLE][streamIndex]['tags']['language'] = subtitleLanguages[streamIndex] + if streamIndex <= len(subtitleTitles) - 1: + targetStreamDescriptor[STREAM_TYPE_SUBTITLE][streamIndex]['tags']['title'] = subtitleTitles[streamIndex] + + + + + click.echo('\nTarget streams:') + for aStream in targetStreamDescriptor[STREAM_TYPE_AUDIO]: + click.echo(f"audio stream {aStream['sub_index']} lang={aStream['tags']['language']} title={aStream['tags']['title']} default={aStream['disposition']['default']} forced={aStream['disposition']['forced']}") + for sStream in targetStreamDescriptor[STREAM_TYPE_SUBTITLE]: + click.echo(f"subtitle stream {sStream['sub_index']} lang={sStream['tags']['language']} title={sStream['tags']['title']} default={sStream['disposition']['default']} forced={sStream['disposition']['forced']}") + + continue commandTokens = COMMAND_TOKENS + ['-i', sourcePath] @@ -739,28 +788,6 @@ def convert(ctx, audioTokens = [] - # Source stream descriptors - audioStreams = sourceStreamDescriptor[STREAM_TYPE_AUDIO] - subtitleStreams = sourceStreamDescriptor[STREAM_TYPE_SUBTITLE] - - # Set language and title in source stream descriptors if given per command line option - for streamIndex in range(len(audioStreams)): - if 'tags' not in audioStreams[streamIndex].keys(): - audioStreams[streamIndex]['tags'] = {} - - if streamIndex <= len(audioLanguages) - 1: - audioStreams[streamIndex]['tags']['language'] = audioLanguages[streamIndex] - if streamIndex <= len(audioTitles) - 1: - audioStreams[streamIndex]['tags']['title'] = audioTitles[streamIndex] - - for streamIndex in range(len(subtitleStreams)): - if 'tags' not in subtitleStreams[streamIndex].keys(): - subtitleStreams[streamIndex]['tags'] = {} - - if streamIndex <= len(subtitleLanguages) - 1: - subtitleStreams[streamIndex]['tags']['language'] = subtitleLanguages[streamIndex] - if streamIndex <= len(subtitleTitles) - 1: - subtitleStreams[streamIndex]['tags']['title'] = subtitleTitles[streamIndex] # Reorder audio stream descriptors and create disposition options if default is given per command line option if defaultAudio == -1: @@ -813,7 +840,7 @@ def convert(ctx, numMatchingSubtitles = len(matchingSubtitles) if jellyfin and defaultSubtitle != -1: - subtitleSequence = getModifiedStreamOrder(numMatchingSubtitles, default_subtitle) + subtitleSequence = getModifiedStreamOrder(numMatchingSubtitles, default_subtitle) #! else: subtitleSequence = range(numMatchingSubtitles)