|
|
|
@ -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)
|
|
|
|
|
|
|
|
|
|