inc stream handling
This commit is contained in:
206
bin/ffx.py
206
bin/ffx.py
@@ -299,7 +299,8 @@ def generateOutputTokens(filepath, format, ext):
|
|||||||
return ['-f', format, f"{filepath}.{ext}"]
|
return ['-f', format, f"{filepath}.{ext}"]
|
||||||
|
|
||||||
|
|
||||||
def generateAudioTokens(context, index, layout):
|
def generateAudioEncodingTokens(context, index, layout):
|
||||||
|
"""Generates ffmpeg options for one output audio stream including channel remapping, codec and bitrate"""
|
||||||
|
|
||||||
if layout == STREAM_LAYOUT_6_1:
|
if layout == STREAM_LAYOUT_6_1:
|
||||||
return [f"-c:a:{index}",
|
return [f"-c:a:{index}",
|
||||||
@@ -342,16 +343,25 @@ def generateClearTokens(streams):
|
|||||||
return clearTokens
|
return clearTokens
|
||||||
|
|
||||||
|
|
||||||
def generateDispositionTokens(subDescriptor):
|
def getDispositionFlags(subStreamDescriptor):
|
||||||
|
return {k for (k,v) in subStreamDescriptor['disposition'].items() if v == 1} if 'disposition' in subStreamDescriptor.keys() else set()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# def generateDispositionTokens(subDescriptor):
|
||||||
|
def generateDispositionTokens(subDescriptor, modifyOrder = []):
|
||||||
"""-disposition:s:X default+forced"""
|
"""-disposition:s:X default+forced"""
|
||||||
|
|
||||||
dispositionTokens = []
|
dispositionTokens = []
|
||||||
|
|
||||||
for subStreamIndex in range(len(subDescriptor)):
|
for subStreamIndex in range(len(subDescriptor)):
|
||||||
|
|
||||||
subStream = subDescriptor[subStreamIndex]
|
sourceSubStreamIndex = modifyOrder[subStreamIndex] if modifyOrder else subStreamIndex
|
||||||
|
|
||||||
|
subStream = subDescriptor[sourceSubStreamIndex]
|
||||||
|
|
||||||
streamType = subStream['codec_type'][0] # v|a|s
|
streamType = subStream['codec_type'][0] # v|a|s
|
||||||
dispositionFlags = {k for (k,v) in subStream['disposition'].items() if v == 1} if 'disposition' in subStream.keys() else set()
|
dispositionFlags = getDispositionFlags(subStream)
|
||||||
|
|
||||||
if dispositionFlags:
|
if dispositionFlags:
|
||||||
dispositionTokens += [f"-disposition:{streamType}:{subStreamIndex}", '+'.join(dispositionFlags)]
|
dispositionTokens += [f"-disposition:{streamType}:{subStreamIndex}", '+'.join(dispositionFlags)]
|
||||||
@@ -360,6 +370,8 @@ def generateDispositionTokens(subDescriptor):
|
|||||||
|
|
||||||
return dispositionTokens
|
return dispositionTokens
|
||||||
|
|
||||||
|
# def countStreamDispositions(subStreamDescriptor):
|
||||||
|
# return len([l for (k,v) in subStreamDescriptor['disposition'].items()])
|
||||||
|
|
||||||
def searchSubtitleFiles(dir, prefix):
|
def searchSubtitleFiles(dir, prefix):
|
||||||
|
|
||||||
@@ -529,6 +541,7 @@ def convert(ctx,
|
|||||||
context['import_subtitles'] = (subtitle_directory and subtitle_prefix)
|
context['import_subtitles'] = (subtitle_directory and subtitle_prefix)
|
||||||
availableFileSubtitleDescriptors = searchSubtitleFiles(subtitle_directory, subtitle_prefix) if context['import_subtitles'] else []
|
availableFileSubtitleDescriptors = searchSubtitleFiles(subtitle_directory, subtitle_prefix) if context['import_subtitles'] else []
|
||||||
|
|
||||||
|
|
||||||
# Overwrite audio tags if set
|
# Overwrite audio tags if set
|
||||||
audioLanguages = audio_language
|
audioLanguages = audio_language
|
||||||
audioTitles = audio_title
|
audioTitles = audio_title
|
||||||
@@ -546,13 +559,18 @@ def convert(ctx,
|
|||||||
# Process crop parameters
|
# Process crop parameters
|
||||||
context['perform_crop'] = (crop != 'none')
|
context['perform_crop'] = (crop != 'none')
|
||||||
if context['perform_crop']:
|
if context['perform_crop']:
|
||||||
cropTokens = crop.split(',')
|
cTokens = crop.split(',')
|
||||||
if cropTokens and len(cropTokens) == 2:
|
if cTokens and len(cTokens) == 2:
|
||||||
context['crop_start'], context['crop_length'] = crop.split(',')
|
cropStart, cropLength = crop.split(',')
|
||||||
else:
|
else:
|
||||||
context['crop_start'] = DEFAULT_CROP_START
|
cropStart = DEFAULT_CROP_START
|
||||||
context['crop_length'] = DEFAULT_CROP_LENGTH
|
cropLength = DEFAULT_CROP_LENGTH
|
||||||
click.echo(f"crop start={context['crop_start']} length={context['crop_length']}")
|
|
||||||
|
click.echo(f"crop start={cropStart} length={cropLength}")
|
||||||
|
|
||||||
|
cropTokens = generateCropTokens(int(cropStart), int(cropLength))
|
||||||
|
else:
|
||||||
|
cropTokens = []
|
||||||
|
|
||||||
|
|
||||||
job_index = 0
|
job_index = 0
|
||||||
@@ -597,6 +615,8 @@ def convert(ctx,
|
|||||||
else:
|
else:
|
||||||
file_index += 1
|
file_index += 1
|
||||||
|
|
||||||
|
matchingFileSubtitleDescriptors = sorted([d for d in availableFileSubtitleDescriptors if d['season'] == season and d['episode'] == episode], key=lambda d: d['stream']) if availableFileSubtitleDescriptors else []
|
||||||
|
|
||||||
print(f"season={season} episode={episode} file={file_index}")
|
print(f"season={season} episode={episode} file={file_index}")
|
||||||
|
|
||||||
|
|
||||||
@@ -628,9 +648,10 @@ def convert(ctx,
|
|||||||
click.echo(f"File with path {sourcePath} does not contain any audiovisual data, skipping ...")
|
click.echo(f"File with path {sourcePath} does not contain any audiovisual data, skipping ...")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
###
|
|
||||||
|
## ## ##
|
||||||
targetStreamDescriptor = sourceStreamDescriptor.copy()
|
targetStreamDescriptor = sourceStreamDescriptor.copy()
|
||||||
###
|
## ## ##
|
||||||
|
|
||||||
|
|
||||||
click.echo('\nSource streams:')
|
click.echo('\nSource streams:')
|
||||||
@@ -658,7 +679,7 @@ def convert(ctx,
|
|||||||
if forcedSubtitle == -1 and numForcedSubtitleStreams > 1:
|
if forcedSubtitle == -1 and numForcedSubtitleStreams > 1:
|
||||||
forcedSubtitle = click.prompt("More than one forced subtitle stream detected! Please select stream", type=int)
|
forcedSubtitle = click.prompt("More than one forced subtitle stream detected! Please select stream", type=int)
|
||||||
|
|
||||||
#Fix multiple default/forced tags
|
#Define default/forced tags
|
||||||
if defaultAudio != -1:
|
if defaultAudio != -1:
|
||||||
for substreamIndex in range(len(targetStreamDescriptor[STREAM_TYPE_AUDIO])):
|
for substreamIndex in range(len(targetStreamDescriptor[STREAM_TYPE_AUDIO])):
|
||||||
targetStreamDescriptor[STREAM_TYPE_AUDIO][substreamIndex]['disposition']['default'] = 1 if substreamIndex == defaultAudio else 0
|
targetStreamDescriptor[STREAM_TYPE_AUDIO][substreamIndex]['disposition']['default'] = 1 if substreamIndex == defaultAudio else 0
|
||||||
@@ -670,7 +691,7 @@ def convert(ctx,
|
|||||||
targetStreamDescriptor[STREAM_TYPE_SUBTITLE][substreamIndex]['disposition']['default'] = 1 if substreamIndex == defaultSubtitle else 0
|
targetStreamDescriptor[STREAM_TYPE_SUBTITLE][substreamIndex]['disposition']['default'] = 1 if substreamIndex == defaultSubtitle else 0
|
||||||
if forcedSubtitle != -1:
|
if forcedSubtitle != -1:
|
||||||
for substreamIndex in range(len(targetStreamDescriptor[STREAM_TYPE_SUBTITLE])):
|
for substreamIndex in range(len(targetStreamDescriptor[STREAM_TYPE_SUBTITLE])):
|
||||||
targetStreamDescriptor[STREAM_TYPE_SUBTITLE][substreamIndex]['disposition']['forded'] = 1 if substreamIndex == forcedSubtitle else 0
|
targetStreamDescriptor[STREAM_TYPE_SUBTITLE][substreamIndex]['disposition']['forced'] = 1 if substreamIndex == forcedSubtitle else 0
|
||||||
|
|
||||||
|
|
||||||
# Set language and title in source stream descriptors if given per command line option
|
# Set language and title in source stream descriptors if given per command line option
|
||||||
@@ -687,7 +708,6 @@ def convert(ctx,
|
|||||||
targetStreamDescriptor[STREAM_TYPE_SUBTITLE][streamIndex]['tags']['title'] = subtitleTitles[streamIndex]
|
targetStreamDescriptor[STREAM_TYPE_SUBTITLE][streamIndex]['tags']['title'] = subtitleTitles[streamIndex]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
click.echo('\nTarget streams:')
|
click.echo('\nTarget streams:')
|
||||||
for aStream in targetStreamDescriptor[STREAM_TYPE_AUDIO]:
|
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']}")
|
click.echo(f"audio stream {aStream['sub_index']} lang={aStream['tags']['language']} title={aStream['tags']['title']} default={aStream['disposition']['default']} forced={aStream['disposition']['forced']}")
|
||||||
@@ -695,10 +715,15 @@ def convert(ctx,
|
|||||||
click.echo(f"subtitle stream {sStream['sub_index']} lang={sStream['tags']['language']} title={sStream['tags']['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']}")
|
||||||
|
|
||||||
|
|
||||||
# Find source stream order
|
numSourceAudioSubStreams = len(sourceStreamDescriptor[STREAM_TYPE_AUDIO])
|
||||||
audioStreamSourceOrder = list(range(len(sourceStreamDescriptor[STREAM_TYPE_AUDIO])))
|
numSourceSubtitleSubStreams = len(sourceStreamDescriptor[STREAM_TYPE_SUBTITLE])
|
||||||
subtitleStreamSourceOrder = list(range(len(sourceStreamDescriptor[STREAM_TYPE_SUBTITLE])))
|
|
||||||
|
|
||||||
|
# Stream order is just a list of integer
|
||||||
|
audioStreamSourceOrder = list(range(numSourceAudioSubStreams))
|
||||||
|
subtitleStreamSourceOrder = list(range(numSourceSubtitleSubStreams))
|
||||||
|
|
||||||
|
|
||||||
|
# In order for the jellyfin media web UI to work properly the default/forced stream has to be the last in the sequence
|
||||||
if jellyfin:
|
if jellyfin:
|
||||||
|
|
||||||
defaultTargetAudioStreams = [a for a in targetStreamDescriptor[STREAM_TYPE_AUDIO] if a['disposition']['default'] == 1]
|
defaultTargetAudioStreams = [a for a in targetStreamDescriptor[STREAM_TYPE_AUDIO] if a['disposition']['default'] == 1]
|
||||||
@@ -710,33 +735,121 @@ def convert(ctx,
|
|||||||
subtitleStreamSourceOrder = getModifiedStreamOrder(len(sourceStreamDescriptor[STREAM_TYPE_SUBTITLE]), defaultTargetSubtitleStreams[0]['sub_index'])
|
subtitleStreamSourceOrder = getModifiedStreamOrder(len(sourceStreamDescriptor[STREAM_TYPE_SUBTITLE]), defaultTargetSubtitleStreams[0]['sub_index'])
|
||||||
|
|
||||||
|
|
||||||
|
# audioDispositionTokens = generateDispositionTokens(targetStreamDescriptor[STREAM_TYPE_AUDIO])
|
||||||
|
# subtitleDispositionTokens = generateDispositionTokens(targetStreamDescriptor[STREAM_TYPE_SUBTITLE])
|
||||||
|
|
||||||
|
audioDispositionTokens = generateDispositionTokens(targetStreamDescriptor[STREAM_TYPE_AUDIO], modifyOrder = audioStreamSourceOrder)
|
||||||
|
subtitleDispositionTokens = generateDispositionTokens(targetStreamDescriptor[STREAM_TYPE_SUBTITLE], modifyOrder = subtitleStreamSourceOrder)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
mappingVideoTokens = ['-map', '0:v:0']
|
mappingVideoTokens = ['-map', '0:v:0']
|
||||||
|
|
||||||
mappingTokens = mappingVideoTokens.copy()
|
mappingTokens = mappingVideoTokens.copy()
|
||||||
|
|
||||||
dispositionTokens = []
|
dispositionTokens = []
|
||||||
|
|
||||||
audioTokens = []
|
audioEncodingTokens = []
|
||||||
|
|
||||||
|
|
||||||
audioMetadataTokens = []
|
audioMetadataTokens = []
|
||||||
for audioStreamIndex in range(len(sourceStreamDescriptor[STREAM_TYPE_AUDIO])):
|
for audioStreamIndex in range(len(targetStreamDescriptor[STREAM_TYPE_AUDIO])):
|
||||||
|
|
||||||
|
# Modify selected source audio stream for jellyfin if required
|
||||||
sourceAudioStreamIndex = audioStreamSourceOrder[audioStreamIndex]
|
sourceAudioStreamIndex = audioStreamSourceOrder[audioStreamIndex]
|
||||||
sourceAudioStream = sourceStreamDescriptor[STREAM_TYPE_AUDIO][sourceAudioStreamIndex]
|
|
||||||
|
|
||||||
# Create mapping and ffmpeg options for audio streams
|
# Add audio mapping tokens to list of general mapping tokens
|
||||||
# mappingTokens += ['-map', f"0:a:{sourceAudioStreamIndex['src_sub_index']}"]
|
|
||||||
mappingTokens += ['-map', f"0:a:{sourceAudioStreamIndex}"]
|
mappingTokens += ['-map', f"0:a:{sourceAudioStreamIndex}"]
|
||||||
|
|
||||||
# audioTokens += generateAudioTokens(context, sourceAudioStream['src_sub_index'], sourceAudioStream['layout'])
|
|
||||||
audioTokens += generateAudioTokens(context, sourceAudioStreamIndex, sourceAudioStream['audio_layout'])
|
|
||||||
|
|
||||||
if sourceStreamDescriptor[STREAM_TYPE_AUDIO][audioStreamIndex]['tags']['language'] != targetStreamDescriptor[STREAM_TYPE_AUDIO][audioStreamIndex]['tags']['language']:
|
targetAudioStream = targetStreamDescriptor[STREAM_TYPE_AUDIO][audioStreamIndex]
|
||||||
audioMetadataTokens += [f"-metadata:s:a:{audioStreamIndex}", f"language={targetStreamDescriptor[STREAM_TYPE_AUDIO][audioStreamIndex]['tags']['language']}"]
|
|
||||||
|
# audioEncodingTokens += generateAudioEncodingTokens(context, sourceAudioStream['src_sub_index'], sourceAudioStream['layout'])
|
||||||
|
audioEncodingTokens += generateAudioEncodingTokens(context, audioStreamIndex, targetAudioStream['audio_layout'])
|
||||||
|
|
||||||
|
if sourceStreamDescriptor[STREAM_TYPE_AUDIO][sourceAudioStreamIndex]['tags']['language'] != targetStreamDescriptor[STREAM_TYPE_AUDIO][audioStreamIndex]['tags']['language']:
|
||||||
|
audioMetadataTokens += [f"-metadata:s:a:{audioStreamIndex}", f"language={targetStreamDescriptor[STREAM_TYPE_AUDIO][sourceAudioStreamIndex]['tags']['language']}"]
|
||||||
|
|
||||||
|
if sourceStreamDescriptor[STREAM_TYPE_AUDIO][sourceAudioStreamIndex]['tags']['title'] != targetStreamDescriptor[STREAM_TYPE_AUDIO][audioStreamIndex]['tags']['title']:
|
||||||
|
audioMetadataTokens += [f"-metadata:s:a:{audioStreamIndex}", f"title={targetStreamDescriptor[STREAM_TYPE_AUDIO][sourceAudioStreamIndex]['tags']['title']}"]
|
||||||
|
|
||||||
|
# targetStreamDescriptor[STREAM_TYPE_AUDIO][audioStreamIndex]['disposition']['default'] = 1 if streamIndex == defaultAudio else 0
|
||||||
|
|
||||||
|
|
||||||
|
subtitleImportFileTokens = []
|
||||||
|
subtitleMetadataTokens = []
|
||||||
|
|
||||||
|
if context['import_subtitles'] and numSourceSubtitleSubStreams != len(matchingFileSubtitleDescriptors):
|
||||||
|
click.echo(f"The number of subtitle streams found in file with path {sourcePath} is different from the number of subtitle streams provided by matching imported files, skipping ...")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 0: Quelle f1 = forced
|
||||||
|
# 1: QUelle f2 = full
|
||||||
|
|
||||||
|
for subtitleStreamIndex in range(len(targetStreamDescriptor[STREAM_TYPE_SUBTITLE])):
|
||||||
|
|
||||||
|
# Modify selected source subtitle stream for jellyfin if required
|
||||||
|
sourceSubtitleStreamIndex = subtitleStreamSourceOrder[subtitleStreamIndex]
|
||||||
|
|
||||||
|
|
||||||
|
if context['import_subtitles']:
|
||||||
|
|
||||||
|
fileSubtitleDescriptor = matchingFileSubtitleDescriptors[subtitleStreamIndex] # original order
|
||||||
|
|
||||||
|
subtitleImportFileTokens += ['-i', fileSubtitleDescriptor['path']] # original order
|
||||||
|
|
||||||
|
# Create mapping for subtitle streams when imported from files
|
||||||
|
mappingTokens += ['-map', f"{sourceSubtitleStreamIndex+1}:s:0"] # modified order
|
||||||
|
|
||||||
|
|
||||||
|
if fileSubtitleDescriptor['language'] != targetStreamDescriptor[STREAM_TYPE_SUBTITLE][subtitleStreamIndex]['tags']['language']:
|
||||||
|
subtitleMetadataTokens += [f"-metadata:s:s:{sourceSubtitleStreamIndex}", f"language={targetStreamDescriptor[STREAM_TYPE_SUBTITLE][subtitleStreamIndex]['tags']['language']}"]
|
||||||
|
|
||||||
|
subtitleMetadataTokens += [f"-metadata:s:s:{sourceSubtitleStreamIndex}", f"title={targetStreamDescriptor[STREAM_TYPE_SUBTITLE][subtitleStreamIndex]['tags']['title']}"]
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
# Add subtitle mapping tokens to list of general mapping tokens
|
||||||
|
mappingTokens += ['-map', f"0:s:{sourceSubtitleStreamIndex}"]
|
||||||
|
|
||||||
|
if sourceStreamDescriptor[STREAM_TYPE_SUBTITLE][sourceSubtitleStreamIndex]['tags']['language'] != targetStreamDescriptor[STREAM_TYPE_SUBTITLE][subtitleStreamIndex]['tags']['language']:
|
||||||
|
subtitleMetadataTokens += [f"-metadata:s:s:{subtitleStreamIndex}", f"language={targetStreamDescriptor[STREAM_TYPE_SUBTITLE][subtitleStreamIndex]['tags']['language']}"]
|
||||||
|
|
||||||
|
if sourceStreamDescriptor[STREAM_TYPE_SUBTITLE][sourceSubtitleStreamIndex]['tags']['title'] != targetStreamDescriptor[STREAM_TYPE_SUBTITLE][subtitleStreamIndex]['tags']['title']:
|
||||||
|
subtitleMetadataTokens += [f"-metadata:s:s:{subtitleStreamIndex}", f"title={targetStreamDescriptor[STREAM_TYPE_SUBTITLE][subtitleStreamIndex]['tags']['title']}"]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# # Reorder audio stream descriptors and create disposition options if default is given per command line option
|
||||||
|
# if defaultAudio == -1:
|
||||||
|
# sourceAudioStreams = audioStreams
|
||||||
|
# else:
|
||||||
|
# for streamIndex in range(len(audioStreams)):
|
||||||
|
# audioStreams[streamIndex]['disposition']['default'] = 1 if streamIndex == defaultAudio else 0
|
||||||
|
#
|
||||||
|
# sourceAudioStreams = getReorderedSubstreams(audioStreams, defaultAudio) if jellyfin else audioStreams
|
||||||
|
#
|
||||||
|
# dispositionTokens += generateDispositionTokens(sourceAudioStreams)
|
||||||
|
#
|
||||||
|
# # Set forced tag in subtitle descriptor if given per command line option
|
||||||
|
# if forcedSubtitle != -1:
|
||||||
|
# for streamIndex in range(len(subtitleStreams)):
|
||||||
|
# subtitleStreams[streamIndex]['disposition']['forced'] = 1 if streamIndex == forcedSubtitle else 0
|
||||||
|
#
|
||||||
|
# # Reorder subtitle stream descriptors and create disposition options if default is given per command line option
|
||||||
|
# if defaultSubtitle == -1:
|
||||||
|
# sourceSubtitleStreams = subtitleStreams
|
||||||
|
# else:
|
||||||
|
# for streamIndex in range(len(subtitleStreams)):
|
||||||
|
# subtitleStreams[streamIndex]['disposition']['default'] = 1 if streamIndex == defaultSubtitle else 0
|
||||||
|
#
|
||||||
|
# sourceSubtitleStreams = getReorderedSubstreams(subtitleStreams, defaultSubtitle) if jellyfin else subtitleStreams
|
||||||
|
#
|
||||||
|
# dispositionTokens += generateDispositionTokens(sourceSubtitleStreams)
|
||||||
|
#
|
||||||
|
|
||||||
if sourceStreamDescriptor[STREAM_TYPE_AUDIO][audioStreamIndex]['tags']['title'] != targetStreamDescriptor[STREAM_TYPE_AUDIO][audioStreamIndex]['tags']['title']:
|
|
||||||
audioMetadataTokens += [f"-metadata:s:a:{audioStreamIndex}", f"title={targetStreamDescriptor[STREAM_TYPE_AUDIO][audioStreamIndex]['tags']['title']}"]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -747,17 +860,12 @@ def convert(ctx,
|
|||||||
|
|
||||||
commandTokens = COMMAND_TOKENS + ['-i', sourcePath]
|
commandTokens = COMMAND_TOKENS + ['-i', sourcePath]
|
||||||
|
|
||||||
subtitleFileTokens = []
|
|
||||||
subtitleMetadataTokens = []
|
|
||||||
|
|
||||||
# matchingSubtitles = []
|
# matchingSubtitles = []
|
||||||
# if context['import_subtitles']:
|
# if context['import_subtitles']:
|
||||||
#
|
#
|
||||||
# subtitles = [a for a in availableFileSubtitleDescriptors if a['season'] == season and a['episode'] == episode]
|
|
||||||
# mSubtitles = sorted(subtitles, key=lambda d: d['stream'])
|
|
||||||
#
|
|
||||||
# for sfd in mSubtitles:
|
|
||||||
# subtitleFileTokens += ['-i', sfd['path']]
|
|
||||||
#
|
#
|
||||||
# for streamIndex in range(len(mSubtitles)):
|
# for streamIndex in range(len(mSubtitles)):
|
||||||
# mSubtitles[streamIndex]['forced'] = 1 if forcedSubtitle != -1 and streamIndex == forcedSubtitle else 0
|
# mSubtitles[streamIndex]['forced'] = 1 if forcedSubtitle != -1 and streamIndex == forcedSubtitle else 0
|
||||||
@@ -863,19 +971,19 @@ def convert(ctx,
|
|||||||
if video_encoder == 'av1':
|
if video_encoder == 'av1':
|
||||||
|
|
||||||
commandSequence = (commandTokens
|
commandSequence = (commandTokens
|
||||||
+ subtitleFileTokens
|
+ subtitleImportFileTokens
|
||||||
+ mappingTokens
|
+ mappingTokens
|
||||||
+ dispositionTokens
|
|
||||||
+ audioMetadataTokens
|
+ audioMetadataTokens
|
||||||
+ subtitleMetadataTokens
|
+ subtitleMetadataTokens
|
||||||
+ audioTokens
|
+ audioDispositionTokens
|
||||||
+ generateAV1Tokens(q, preset) + audioTokens)
|
+ subtitleDispositionTokens
|
||||||
|
+ audioEncodingTokens
|
||||||
|
+ generateAV1Tokens(q, preset) + audioEncodingTokens)
|
||||||
|
|
||||||
if clear_metadata:
|
if clear_metadata:
|
||||||
commandSequence += generateClearTokens(sourceStreamDescriptor)
|
commandSequence += generateClearTokens(sourceStreamDescriptor)
|
||||||
|
|
||||||
if context['perform_crop']:
|
commandSequence += cropTokens
|
||||||
commandSequence += generateCropTokens(context['crop_start'], context['crop_length'])
|
|
||||||
|
|
||||||
commandSequence += generateOutputTokens(targetFilename, DEFAULT_FILE_FORMAT, DEFAULT_FILE_EXTENSION)
|
commandSequence += generateOutputTokens(targetFilename, DEFAULT_FILE_FORMAT, DEFAULT_FILE_EXTENSION)
|
||||||
|
|
||||||
@@ -889,8 +997,7 @@ def convert(ctx,
|
|||||||
|
|
||||||
commandSequence1 = commandTokens + mappingVideoTokens + generateVP9Pass1Tokens(q)
|
commandSequence1 = commandTokens + mappingVideoTokens + generateVP9Pass1Tokens(q)
|
||||||
|
|
||||||
if context['perform_crop']:
|
commandSequence1 += cropTokens
|
||||||
commandSequence1 += generateCropTokens(context['crop_start'], context['crop_length'])
|
|
||||||
|
|
||||||
commandSequence1 += NULL_TOKENS
|
commandSequence1 += NULL_TOKENS
|
||||||
|
|
||||||
@@ -904,22 +1011,23 @@ def convert(ctx,
|
|||||||
|
|
||||||
|
|
||||||
commandSequence2 = (commandTokens
|
commandSequence2 = (commandTokens
|
||||||
+ subtitleFileTokens
|
+ subtitleImportFileTokens
|
||||||
+ mappingTokens
|
+ mappingTokens
|
||||||
+ audioMetadataTokens
|
+ audioMetadataTokens
|
||||||
+ subtitleMetadataTokens
|
+ subtitleMetadataTokens
|
||||||
|
+ audioDispositionTokens
|
||||||
|
+ subtitleDispositionTokens
|
||||||
+ dispositionTokens)
|
+ dispositionTokens)
|
||||||
|
|
||||||
if denoise:
|
if denoise:
|
||||||
commandSequence2 += generateDenoiseTokens()
|
commandSequence2 += generateDenoiseTokens()
|
||||||
|
|
||||||
commandSequence2 += generateVP9Pass2Tokens(q) + audioTokens
|
commandSequence2 += generateVP9Pass2Tokens(q) + audioEncodingTokens
|
||||||
|
|
||||||
if clear_metadata:
|
if clear_metadata:
|
||||||
commandSequence2 += generateClearTokens(sourceStreamDescriptor)
|
commandSequence2 += generateClearTokens(sourceStreamDescriptor)
|
||||||
|
|
||||||
if context['perform_crop']:
|
commandSequence2 += cropTokens
|
||||||
commandSequence2 += generateCropTokens(context['crop_start'], context['crop_length'])
|
|
||||||
|
|
||||||
commandSequence2 += generateOutputTokens(targetFilename, DEFAULT_FILE_FORMAT, DEFAULT_FILE_EXTENSION)
|
commandSequence2 += generateOutputTokens(targetFilename, DEFAULT_FILE_FORMAT, DEFAULT_FILE_EXTENSION)
|
||||||
|
|
||||||
|
|||||||
2
bin/ffx/ffx_controller.py
Normal file
2
bin/ffx/ffx_controller.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
class FfxController():
|
||||||
|
pass
|
||||||
2
bin/ffx/file_pattern.py
Normal file
2
bin/ffx/file_pattern.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
class FilePattern():
|
||||||
|
pass
|
||||||
2
bin/ffx/show.py
Normal file
2
bin/ffx/show.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
class Show():
|
||||||
|
pass
|
||||||
2
bin/ffx/show_controller.py
Normal file
2
bin/ffx/show_controller.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
class ShowController():
|
||||||
|
pass
|
||||||
@@ -1,11 +1,56 @@
|
|||||||
from enum import Enum
|
|
||||||
from language_data import LanguageData
|
from language_data import LanguageData
|
||||||
from stream_type import StreamType
|
from stream_type import StreamType
|
||||||
|
|
||||||
class StreamDescriptor():
|
class StreamDescriptor():
|
||||||
|
|
||||||
def __init__(self, streamType : StreamType, language : LanguageData, title : str):
|
def __init__(self,
|
||||||
|
streamType : StreamType,
|
||||||
|
language : LanguageData,
|
||||||
|
title : str,
|
||||||
|
codec : str,
|
||||||
|
subIndex : int = -1):
|
||||||
|
|
||||||
self.__streamType = streamType
|
self.__streamType = streamType
|
||||||
|
self.__subIndex = subIndex
|
||||||
|
|
||||||
self.__streamLanguage = language
|
self.__streamLanguage = language
|
||||||
self.__streamTitle = title
|
self.__streamTitle = title
|
||||||
|
|
||||||
|
self.__codecName = codec
|
||||||
|
|
||||||
|
# "index": 4,
|
||||||
|
# "codec_name": "hdmv_pgs_subtitle",
|
||||||
|
# "codec_long_name": "HDMV Presentation Graphic Stream subtitles",
|
||||||
|
# "codec_type": "subtitle",
|
||||||
|
# "codec_tag_string": "[0][0][0][0]",
|
||||||
|
# "codec_tag": "0x0000",
|
||||||
|
# "r_frame_rate": "0/0",
|
||||||
|
# "avg_frame_rate": "0/0",
|
||||||
|
# "time_base": "1/1000",
|
||||||
|
# "start_pts": 0,
|
||||||
|
# "start_time": "0.000000",
|
||||||
|
# "duration_ts": 1421035,
|
||||||
|
# "duration": "1421.035000",
|
||||||
|
# "disposition": {
|
||||||
|
# "default": 1,
|
||||||
|
# "dub": 0,
|
||||||
|
# "original": 0,
|
||||||
|
# "comment": 0,
|
||||||
|
# "lyrics": 0,
|
||||||
|
# "karaoke": 0,
|
||||||
|
# "forced": 0,
|
||||||
|
# "hearing_impaired": 0,
|
||||||
|
# "visual_impaired": 0,
|
||||||
|
# "clean_effects": 0,
|
||||||
|
# "attached_pic": 0,
|
||||||
|
# "timed_thumbnails": 0,
|
||||||
|
# "non_diegetic": 0,
|
||||||
|
# "captions": 0,
|
||||||
|
# "descriptions": 0,
|
||||||
|
# "metadata": 0,
|
||||||
|
# "dependent": 0,
|
||||||
|
# "still_image": 0
|
||||||
|
# },
|
||||||
|
# "tags": {
|
||||||
|
# "language": "ger",
|
||||||
|
# "title": "German Full"
|
||||||
|
|||||||
Reference in New Issue
Block a user