inc ffx single descriptor 1/
This commit is contained in:
361
bin/ffx.py
361
bin/ffx.py
@@ -8,6 +8,8 @@ from ffx.ffx_app import FfxApp
|
|||||||
from ffx.ffx_controller import FfxController
|
from ffx.ffx_controller import FfxController
|
||||||
from ffx.database import databaseContext
|
from ffx.database import databaseContext
|
||||||
|
|
||||||
|
from ffx.track_type import TrackType
|
||||||
|
|
||||||
|
|
||||||
VERSION='0.1.0'
|
VERSION='0.1.0'
|
||||||
|
|
||||||
@@ -207,53 +209,35 @@ def convert(ctx,
|
|||||||
context['bitrates']['stereo'] = str(stereo_bitrate) if str(stereo_bitrate).endswith('k') else f"{stereo_bitrate}k"
|
context['bitrates']['stereo'] = str(stereo_bitrate) if str(stereo_bitrate).endswith('k') else f"{stereo_bitrate}k"
|
||||||
context['bitrates']['ac3'] = str(ac3_bitrate) if str(ac3_bitrate).endswith('k') else f"{ac3_bitrate}k"
|
context['bitrates']['ac3'] = str(ac3_bitrate) if str(ac3_bitrate).endswith('k') else f"{ac3_bitrate}k"
|
||||||
context['bitrates']['dts'] = str(dts_bitrate) if str(dts_bitrate).endswith('k') else f"{dts_bitrate}k"
|
context['bitrates']['dts'] = str(dts_bitrate) if str(dts_bitrate).endswith('k') else f"{dts_bitrate}k"
|
||||||
#
|
|
||||||
# click.echo(f"Stereo bitrate: {context['bitrates']['stereo']}")
|
click.echo(f"Stereo bitrate: {context['bitrates']['stereo']}")
|
||||||
# click.echo(f"AC3 bitrate: {context['bitrates']['ac3']}")
|
click.echo(f"AC3 bitrate: {context['bitrates']['ac3']}")
|
||||||
# click.echo(f"DTS bitrate: {context['bitrates']['dts']}")
|
click.echo(f"DTS bitrate: {context['bitrates']['dts']}")
|
||||||
#
|
|
||||||
#
|
|
||||||
#
|
# Process crop parameters
|
||||||
|
context['perform_crop'] = (crop != 'none')
|
||||||
|
if context['perform_crop']:
|
||||||
|
cTokens = crop.split(',')
|
||||||
|
if cTokens and len(cTokens) == 2:
|
||||||
|
cropStart = int(cTokens[0])
|
||||||
|
cropLength = int(cTokens[1])
|
||||||
|
cropTokens = FfxController.generateCropTokens(cropStart, cropLength)
|
||||||
|
else:
|
||||||
|
cropTokens = FfxController.generateCropTokens()
|
||||||
|
else:
|
||||||
|
cropTokens = []
|
||||||
|
|
||||||
|
click.echo(f"Crop tokens={cropTokens}")
|
||||||
|
|
||||||
|
|
||||||
# ## Conversion parameters
|
# ## Conversion parameters
|
||||||
#
|
#
|
||||||
# # Parse subtitle files
|
# # Parse subtitle files
|
||||||
# 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
|
|
||||||
# audioLanguages = audio_language
|
|
||||||
# audioTitles = audio_title
|
|
||||||
#
|
|
||||||
# # Overwrite subtitle tags if set
|
|
||||||
# subtitleLanguages = subtitle_language
|
|
||||||
# subtitleTitles = subtitle_title
|
|
||||||
#
|
|
||||||
# defaultAudio = default_audio
|
|
||||||
# defaultSubtitle = default_subtitle
|
|
||||||
# forcedAudio = forced_audio
|
|
||||||
# forcedSubtitle = forced_subtitle
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# # Process crop parameters
|
|
||||||
# context['perform_crop'] = (crop != 'none')
|
|
||||||
# if context['perform_crop']:
|
|
||||||
# cTokens = crop.split(',')
|
|
||||||
# if cTokens and len(cTokens) == 2:
|
|
||||||
# cropStart, cropLength = crop.split(',')
|
|
||||||
# else:
|
|
||||||
# cropStart = FfxController.DEFAULT_CROP_START
|
|
||||||
# cropLength = FfxController.DEFAULT_CROP_LENGTH
|
|
||||||
#
|
|
||||||
# click.echo(f"crop start={cropStart} length={cropLength}")
|
|
||||||
#
|
|
||||||
# cropTokens = generateCropTokens(int(cropStart), int(cropLength))
|
|
||||||
# else:
|
|
||||||
# cropTokens = []
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# job_index = 0
|
|
||||||
#
|
|
||||||
existingSourcePaths = [p for p in paths if os.path.isfile(p) and p.split('.')[-1] in FfxController.INPUT_FILE_EXTENSIONS]
|
existingSourcePaths = [p for p in paths if os.path.isfile(p) and p.split('.')[-1] in FfxController.INPUT_FILE_EXTENSIONS]
|
||||||
click.echo(f"\nRunning {len(existingSourcePaths) * len(q_list)} jobs")
|
click.echo(f"\nRunning {len(existingSourcePaths) * len(q_list)} jobs")
|
||||||
|
|
||||||
@@ -267,27 +251,74 @@ def convert(ctx,
|
|||||||
sourceFileBasename = '.'.join(sourcePathTokens[:-1])
|
sourceFileBasename = '.'.join(sourcePathTokens[:-1])
|
||||||
sourceFilenameExtension = sourcePathTokens[-1]
|
sourceFilenameExtension = sourcePathTokens[-1]
|
||||||
|
|
||||||
|
|
||||||
click.echo(f"\nProcessing file {sourcePath}")
|
click.echo(f"\nProcessing file {sourcePath}")
|
||||||
|
|
||||||
|
|
||||||
mediaFileProperties = FileProperties(context, sourceFilename)
|
mediaFileProperties = FileProperties(context, sourceFilename)
|
||||||
currentMediaDescriptor = mediaFileProperties.getMediaDescriptor()
|
sourceMediaDescriptor = mediaFileProperties.getMediaDescriptor()
|
||||||
|
|
||||||
#HINT: This is None if the filename did not match anything in database
|
#HINT: This is None if the filename did not match anything in database
|
||||||
currentPattern = mediaFileProperties.getPattern()
|
currentPattern = mediaFileProperties.getPattern()
|
||||||
|
|
||||||
targetMediaDescriptor = currentPattern.getMediaDescriptor() if currentPattern is not None else None
|
|
||||||
|
|
||||||
click.echo(f"Pattern matching: {'No' if currentPattern is None else 'Yes'}")
|
click.echo(f"Pattern matching: {'No' if currentPattern is None else 'Yes'}")
|
||||||
|
|
||||||
if not currentPattern is None:
|
|
||||||
|
if currentPattern is None:
|
||||||
|
|
||||||
|
# Case no pattern matching
|
||||||
|
|
||||||
|
# Check for multiple default or forced dispositions if not set by user input or database requirements
|
||||||
|
#
|
||||||
|
# Query user for the correct sub indices, then configure flags in track descriptors associated with media descriptor accordingly.
|
||||||
|
# The correct tokens should then be created by
|
||||||
|
try:
|
||||||
|
sourceMediaDescriptor.getDefaultVideoTrack()
|
||||||
|
except ValueError:
|
||||||
|
defaultVideoTrackSubIndex = click.prompt("More than one default video stream detected! Please select stream", type=int)
|
||||||
|
sourceMediaDescriptor.setDefaultSubTrack(TrackType.VIDEO, defaultVideoTrackSubIndex)
|
||||||
|
try:
|
||||||
|
sourceMediaDescriptor.getForcedVideoTrack()
|
||||||
|
except ValueError:
|
||||||
|
forcedVideoTrackSubIndex = click.prompt("More than one forced video stream detected! Please select stream", type=int)
|
||||||
|
sourceMediaDescriptor.setForcedSubTrack(TrackType.VIDEO, forcedVideoTrackSubIndex)
|
||||||
|
try:
|
||||||
|
sourceMediaDescriptor.getDefaultAudioTrack()
|
||||||
|
except ValueError:
|
||||||
|
defaultAudioTrackSubIndex = click.prompt("More than one default audio stream detected! Please select stream", type=int)
|
||||||
|
sourceMediaDescriptor.setDefaultSubTrack(TrackType.AUDIO, defaultAudioTrackSubIndex)
|
||||||
|
try:
|
||||||
|
sourceMediaDescriptor.getForcedAudioTrack()
|
||||||
|
except ValueError:
|
||||||
|
forcedAudioTrackSubIndex = click.prompt("More than one forced audio stream detected! Please select stream", type=int)
|
||||||
|
sourceMediaDescriptor.setForcedSubTrack(TrackType.AUDIO, forcedAudioTrackSubIndex)
|
||||||
|
try:
|
||||||
|
sourceMediaDescriptor.getDefaultSubtitleTrack()
|
||||||
|
except ValueError:
|
||||||
|
defaultSubtitleTrackSubIndex = click.prompt("More than one default subtitle stream detected! Please select stream", type=int)
|
||||||
|
sourceMediaDescriptor.setDefaultSubTrack(TrackType.SUBTITLE, defaultSubtitleTrackSubIndex)
|
||||||
|
try:
|
||||||
|
sourceMediaDescriptor.getForcedSubtitleTrack()
|
||||||
|
except ValueError:
|
||||||
|
forcedSubtitleTrackSubIndex = click.prompt("More than one forced subtitle stream detected! Please select stream", type=int)
|
||||||
|
sourceMediaDescriptor.setForcedSubTrack(TrackType.SUBTITLE, forcedSubtitleTrackSubIndex)
|
||||||
|
|
||||||
|
fc = FfxController(context, sourceMediaDescriptor)
|
||||||
|
|
||||||
|
dispositionTokens = fc.generateDispositionTokens()
|
||||||
|
click.echo(f"Disposition Tokens: {dispositionTokens}")
|
||||||
|
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
# Case pattern matching
|
||||||
|
|
||||||
|
targetMediaDescriptor = currentPattern.getMediaDescriptor() if currentPattern is not None else None
|
||||||
|
|
||||||
targetMediaDescriptor.setJellyfinOrder(context['jellyfin'])
|
targetMediaDescriptor.setJellyfinOrder(context['jellyfin'])
|
||||||
|
|
||||||
click.echo(f"Input mapping tokens: {targetMediaDescriptor.getInputMappingTokens()}")
|
click.echo(f"Input mapping tokens: {targetMediaDescriptor.getInputMappingTokens()}")
|
||||||
|
|
||||||
fc = FfxController(context, currentMediaDescriptor, targetMediaDescriptor)
|
fc = FfxController(context, targetMediaDescriptor, sourceMediaDescriptor)
|
||||||
|
|
||||||
mappingTokens = fc.generateMetadataTokens()
|
mappingTokens = fc.generateMetadataTokens()
|
||||||
click.echo(f"Metadata Tokens: {mappingTokens}")
|
click.echo(f"Metadata Tokens: {mappingTokens}")
|
||||||
@@ -300,26 +331,7 @@ def convert(ctx,
|
|||||||
|
|
||||||
click.echo(f"Season={mediaFileProperties.getSeason()} Episode={mediaFileProperties.getEpisode()}")
|
click.echo(f"Season={mediaFileProperties.getSeason()} Episode={mediaFileProperties.getEpisode()}")
|
||||||
|
|
||||||
# # Determine season and episode if present in current filename
|
|
||||||
# season_digits = 2
|
|
||||||
# episode_digits = 2
|
|
||||||
# index_digits = 3
|
|
||||||
#
|
|
||||||
# se_result = se_match.search(sourceFilename)
|
|
||||||
# e_result = e_match.search(sourceFilename)
|
|
||||||
#
|
|
||||||
# season = -1
|
|
||||||
# episode = -1
|
|
||||||
# file_index = 0
|
|
||||||
#
|
|
||||||
# if se_result is not None:
|
|
||||||
# season = int(se_result.group(1))
|
|
||||||
# episode = int(se_result.group(2))
|
|
||||||
# elif e_result is not None:
|
|
||||||
# episode = int(e_result.group(1))
|
|
||||||
# else:
|
|
||||||
# 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 []
|
# 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}")
|
||||||
@@ -359,212 +371,9 @@ def convert(ctx,
|
|||||||
# ## ## ##
|
# ## ## ##
|
||||||
# targetStreamDescriptor = sourceStreamDescriptor.copy()
|
# targetStreamDescriptor = sourceStreamDescriptor.copy()
|
||||||
# ## ## ##
|
# ## ## ##
|
||||||
#
|
|
||||||
#
|
|
||||||
# click.echo('\nSource streams:')
|
|
||||||
# for aStream in sourceStreamDescriptor[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 sourceStreamDescriptor[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']}")
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# # 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:
|
|
||||||
# defaultAudio = click.prompt("More than one default audio stream detected! Please select stream", type=int)
|
|
||||||
#
|
|
||||||
# numForcedAudioStreams = len([a for a in sourceStreamDescriptor[STREAM_TYPE_AUDIO] if a['disposition']['forced'] == 1])
|
|
||||||
# if forcedAudio == -1 and numForcedAudioStreams > 1:
|
|
||||||
# forcedAudio = click.prompt("More than one forced audio stream detected! Please select stream", type=int)
|
|
||||||
#
|
|
||||||
# numDefaultSubtitleStreams = len([s for s in sourceStreamDescriptor[STREAM_TYPE_SUBTITLE] if s['disposition']['default'] == 1])
|
|
||||||
# if defaultSubtitle == -1 and numDefaultSubtitleStreams > 1:
|
|
||||||
# defaultSubtitle = click.prompt("More than one default subtitle stream detected! Please select stream", type=int)
|
|
||||||
#
|
|
||||||
# numForcedSubtitleStreams = len([s for s in sourceStreamDescriptor[STREAM_TYPE_SUBTITLE] if s['disposition']['forced'] == 1])
|
|
||||||
# if forcedSubtitle == -1 and numForcedSubtitleStreams > 1:
|
|
||||||
# forcedSubtitle = click.prompt("More than one forced subtitle stream detected! Please select stream", type=int)
|
|
||||||
#
|
|
||||||
# #Define default/forced tags
|
|
||||||
# if defaultAudio != -1:
|
|
||||||
# for substreamIndex in range(len(targetStreamDescriptor[STREAM_TYPE_AUDIO])):
|
|
||||||
# targetStreamDescriptor[STREAM_TYPE_AUDIO][substreamIndex]['disposition']['default'] = 1 if substreamIndex == defaultAudio else 0
|
|
||||||
# if forcedAudio != -1:
|
|
||||||
# for substreamIndex in range(len(targetStreamDescriptor[STREAM_TYPE_AUDIO])):
|
|
||||||
# targetStreamDescriptor[STREAM_TYPE_AUDIO][substreamIndex]['disposition']['forced'] = 1 if substreamIndex == forcedAudio else 0
|
|
||||||
# if defaultSubtitle != -1:
|
|
||||||
# for substreamIndex in range(len(targetStreamDescriptor[STREAM_TYPE_SUBTITLE])):
|
|
||||||
# targetStreamDescriptor[STREAM_TYPE_SUBTITLE][substreamIndex]['disposition']['default'] = 1 if substreamIndex == defaultSubtitle else 0
|
|
||||||
# if forcedSubtitle != -1:
|
|
||||||
# for substreamIndex in range(len(targetStreamDescriptor[STREAM_TYPE_SUBTITLE])):
|
|
||||||
# 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
|
|
||||||
# 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']}")
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# numSourceAudioSubStreams = len(sourceStreamDescriptor[STREAM_TYPE_AUDIO])
|
|
||||||
# numSourceSubtitleSubStreams = 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:
|
|
||||||
#
|
|
||||||
# defaultTargetAudioStreams = [a for a in targetStreamDescriptor[STREAM_TYPE_AUDIO] if a['disposition']['default'] == 1]
|
|
||||||
# if defaultTargetAudioStreams:
|
|
||||||
# audioStreamSourceOrder = getModifiedStreamOrder(len(sourceStreamDescriptor[STREAM_TYPE_AUDIO]), defaultTargetAudioStreams[0]['sub_index'])
|
|
||||||
#
|
|
||||||
# defaultTargetSubtitleStreams = [a for a in targetStreamDescriptor[STREAM_TYPE_SUBTITLE] if a['disposition']['default'] == 1]
|
|
||||||
# if defaultTargetSubtitleStreams:
|
|
||||||
# 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']
|
|
||||||
# mappingTokens = mappingVideoTokens.copy()
|
|
||||||
#
|
|
||||||
# dispositionTokens = []
|
|
||||||
#
|
|
||||||
# audioEncodingTokens = []
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# audioMetadataTokens = []
|
|
||||||
# for audioStreamIndex in range(len(targetStreamDescriptor[STREAM_TYPE_AUDIO])):
|
|
||||||
#
|
|
||||||
# # Modify selected source audio stream for jellyfin if required
|
|
||||||
# sourceAudioStreamIndex = audioStreamSourceOrder[audioStreamIndex]
|
|
||||||
#
|
|
||||||
# # Add audio mapping tokens to list of general mapping tokens
|
|
||||||
# mappingTokens += ['-map', f"0:a:{sourceAudioStreamIndex}"]
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# targetAudioStream = targetStreamDescriptor[STREAM_TYPE_AUDIO][audioStreamIndex]
|
|
||||||
#
|
|
||||||
# # 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)
|
|
||||||
# #
|
|
||||||
#
|
|
||||||
#
|
|
||||||
#
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# click.echo(f"Audio stream source order {audioStreamSourceOrder}")
|
|
||||||
# click.echo(f"Subtitle stream source order {subtitleStreamSourceOrder}")
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# commandTokens = COMMAND_TOKENS + ['-i', sourcePath]
|
# commandTokens = COMMAND_TOKENS + ['-i', sourcePath]
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
@@ -755,9 +564,5 @@ def convert(ctx,
|
|||||||
click.echo(f"Time elapsed {endTime - startTime}")
|
click.echo(f"Time elapsed {endTime - startTime}")
|
||||||
|
|
||||||
|
|
||||||
# click.echo(f"app result: {app.getContext()}")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
ffx()
|
ffx()
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ class FfxController():
|
|||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
context : dict,
|
context : dict,
|
||||||
sourceMediaDescriptor : MediaDescriptor,
|
targetMediaDescriptor : MediaDescriptor,
|
||||||
targetMediaDescriptor : MediaDescriptor):
|
sourceMediaDescriptor : MediaDescriptor = None):
|
||||||
|
|
||||||
self.__context = context
|
self.__context = context
|
||||||
self.__sourceMediaDescriptor = sourceMediaDescriptor
|
self.__sourceMediaDescriptor = sourceMediaDescriptor
|
||||||
@@ -90,7 +90,12 @@ class FfxController():
|
|||||||
'-lag-in-frames', '25']
|
'-lag-in-frames', '25']
|
||||||
|
|
||||||
|
|
||||||
def generateCropTokens(self, start, length):
|
|
||||||
|
@staticmethod
|
||||||
|
def generateCropTokens(cropStart : int = -1, cropLength : int = -1):
|
||||||
|
|
||||||
|
start = int(cropStart if cropStart > -1 else FfxController.DEFAULT_CROP_START)
|
||||||
|
length = int(cropLength if cropLength > -1 else FfxController.DEFAULT_CROP_LENGTH)
|
||||||
|
|
||||||
return ['-ss', str(start), '-t', str(length)]
|
return ['-ss', str(start), '-t', str(length)]
|
||||||
|
|
||||||
@@ -171,27 +176,29 @@ class FfxController():
|
|||||||
def generateDispositionTokens(self):
|
def generateDispositionTokens(self):
|
||||||
"""-disposition:s:X default+forced"""
|
"""-disposition:s:X default+forced"""
|
||||||
|
|
||||||
sourceTrackDescriptors = self.__sourceMediaDescriptor.getAllTrackDescriptors()
|
sourceTrackDescriptors = [] if self.__sourceMediaDescriptor is None else self.__sourceMediaDescriptor.getAllTrackDescriptors()
|
||||||
targetTrackDescriptors = self.__targetMediaDescriptor.getReorderedTrackDescriptors()
|
targetTrackDescriptors = self.__targetMediaDescriptor.getReorderedTrackDescriptors()
|
||||||
|
|
||||||
|
|
||||||
dispositionTokens = []
|
dispositionTokens = []
|
||||||
|
|
||||||
# for subStreamIndex in range(len(subDescriptor)):
|
# for subStreamIndex in range(len(subDescriptor)):
|
||||||
for trackDescriptor in targetTrackDescriptors:
|
for trackDescriptor in targetTrackDescriptors:
|
||||||
|
|
||||||
# Calculate source sub index
|
# Calculate source sub index. This applies only if a source media descriptor is defined.
|
||||||
changedTargetTrackDescriptor : TrackDescriptor = targetTrackDescriptors[trackDescriptor.getIndex()]
|
if sourceTrackDescriptors:
|
||||||
changedTargetTrackSourceIndex = changedTargetTrackDescriptor.getSourceIndex()
|
changedTargetTrackDescriptor : TrackDescriptor = targetTrackDescriptors[trackDescriptor.getIndex()]
|
||||||
sourceSubIndex = sourceTrackDescriptors[changedTargetTrackSourceIndex].getSubIndex()
|
changedTargetTrackSourceIndex = changedTargetTrackDescriptor.getSourceIndex()
|
||||||
|
subIndex = sourceTrackDescriptors[changedTargetTrackSourceIndex].getSubIndex()
|
||||||
|
else:
|
||||||
|
subIndex = trackDescriptor.getSubIndex()
|
||||||
|
|
||||||
streamIndicator = trackDescriptor.getType().indicator()
|
streamIndicator = trackDescriptor.getType().indicator()
|
||||||
dispositionSet = trackDescriptor.getDispositionSet()
|
dispositionSet = trackDescriptor.getDispositionSet()
|
||||||
|
|
||||||
if dispositionSet:
|
if dispositionSet:
|
||||||
dispositionTokens += [f"-disposition:{streamIndicator}:{sourceSubIndex}", '+'.join([d.label() for d in dispositionSet])]
|
dispositionTokens += [f"-disposition:{streamIndicator}:{subIndex}", '+'.join([d.label() for d in dispositionSet])]
|
||||||
else:
|
else:
|
||||||
dispositionTokens += [f"-disposition:{streamIndicator}:{sourceSubIndex}", '0']
|
dispositionTokens += [f"-disposition:{streamIndicator}:{subIndex}", '0']
|
||||||
|
|
||||||
return dispositionTokens
|
return dispositionTokens
|
||||||
|
|
||||||
|
|||||||
@@ -67,24 +67,25 @@ class FileProperties():
|
|||||||
#
|
#
|
||||||
# print(f"season={season} episode={episode} file={file_index}")
|
# print(f"season={season} episode={episode} file={file_index}")
|
||||||
#
|
#
|
||||||
#
|
|
||||||
# # Assemble target filename tokens
|
def assembleTargetFilename(self):
|
||||||
# targetFilenameTokens = []
|
|
||||||
|
targetFilenameTokens = []
|
||||||
# targetFilenameExtension = DEFAULT_FILE_EXTENSION
|
# targetFilenameExtension = DEFAULT_FILE_EXTENSION
|
||||||
#
|
#
|
||||||
# if label:
|
# if label:
|
||||||
# targetFilenameTokens = [label]
|
# targetFilenameTokens = [label]
|
||||||
#
|
#
|
||||||
# if season > -1 and episode > -1:
|
# if self.__season > -1 and self.__episode > -1:
|
||||||
# targetFilenameTokens += [f"S{season:0{season_digits}d}E{episode:0{episode_digits}d}"]
|
# targetFilenameTokens += [f"S{self.__season:0{season_digits}d}E{self.__episode:0{episode_digits}d}"]
|
||||||
# elif episode > -1:
|
# elif self.__episode > -1:
|
||||||
# targetFilenameTokens += [f"E{episode:0{episode_digits}d}"]
|
# targetFilenameTokens += [f"E{self.__episode:0{episode_digits}d}"]
|
||||||
# else:
|
# else:
|
||||||
# targetFilenameTokens += [f"{file_index:0{index_digits}d}"]
|
# targetFilenameTokens += [f"{file_index:0{index_digits}d}"]
|
||||||
#
|
#
|
||||||
# else:
|
# else:
|
||||||
# targetFilenameTokens = [sourceFileBasename]
|
# targetFilenameTokens = [self.__sourceFileBasename]
|
||||||
#
|
|
||||||
|
|
||||||
|
|
||||||
def searchSubtitleFiles(dir, prefix):
|
def searchSubtitleFiles(dir, prefix):
|
||||||
|
|||||||
@@ -65,39 +65,74 @@ class MediaDescriptor():
|
|||||||
self.__jellyfinOrder = False
|
self.__jellyfinOrder = False
|
||||||
|
|
||||||
|
|
||||||
|
def getDefaultVideoTrack(self):
|
||||||
|
videoDefaultTracks = [v for v in self.getVideoTracks() if TrackDisposition.DEFAULT in v.getDispositionSet()]
|
||||||
|
if len(videoDefaultTracks) > 1:
|
||||||
|
raise ValueError('MediaDescriptor.getDefaultVideoTrack(): More than one default video track is not supported')
|
||||||
|
return videoDefaultTracks[0] if videoDefaultTracks else None
|
||||||
|
|
||||||
|
def getForcedVideoTrack(self):
|
||||||
|
videoForcedTracks = [v for v in self.getVideoTracks() if TrackDisposition.FORCED in v.getDispositionSet()]
|
||||||
|
if len(videoForcedTracks) > 1:
|
||||||
|
raise ValueError('MediaDescriptor.getForcedVideoTrack(): More than one forced video track is not supported')
|
||||||
|
return videoForcedTracks[0] if videoForcedTracks else None
|
||||||
|
|
||||||
|
def getDefaultAudioTrack(self):
|
||||||
|
audioDefaultTracks = [a for a in self.getAudioTracks() if TrackDisposition.DEFAULT in a.getDispositionSet()]
|
||||||
|
if len(audioDefaultTracks) > 1:
|
||||||
|
raise ValueError('MediaDescriptor.getDefaultAudioTrack(): More than one default audio track is not supported')
|
||||||
|
return audioDefaultTracks[0] if audioDefaultTracks else None
|
||||||
|
|
||||||
|
def getForcedAudioTrack(self):
|
||||||
|
audioForcedTracks = [a for a in self.getAudioTracks() if TrackDisposition.FORCED in a.getDispositionSet()]
|
||||||
|
if len(audioForcedTracks) > 1:
|
||||||
|
raise ValueError('MediaDescriptor.getForcedAudioTrack(): More than one forced audio track is not supported')
|
||||||
|
return audioForcedTracks[0] if audioForcedTracks else None
|
||||||
|
|
||||||
|
def getDefaultSubtitleTrack(self):
|
||||||
|
subtitleDefaultTracks = [s for s in self.getSubtitleTracks() if TrackDisposition.DEFAULT in s.getDispositionSet()]
|
||||||
|
if len(subtitleDefaultTracks) > 1:
|
||||||
|
raise ValueError('MediaDescriptor.getDefaultSubtitleTrack(): More than one default subtitle track is not supported')
|
||||||
|
return subtitleDefaultTracks[0] if subtitleDefaultTracks else None
|
||||||
|
|
||||||
|
def getForcedSubtitleTrack(self):
|
||||||
|
subtitleForcedTracks = [s for s in self.getSubtitleTracks() if TrackDisposition.FORCED in s.getDispositionSet()]
|
||||||
|
if len(subtitleForcedTracks) > 1:
|
||||||
|
raise ValueError('MediaDescriptor.getForcedSubtitleTrack(): More than one forced subtitle track is not supported')
|
||||||
|
return subtitleForcedTracks[0] if subtitleForcedTracks else None
|
||||||
|
|
||||||
|
|
||||||
|
def setDefaultSubTrack(self, trackType : TrackType, subIndex : int):
|
||||||
|
for t in self.getAllTrackDescriptors():
|
||||||
|
if t.getType() == trackType:
|
||||||
|
t.setDispositionFlag(TrackDisposition.DEFAULT, t.getSubIndex() == int(subIndex))
|
||||||
|
|
||||||
|
def setForcedSubTrack(self, trackType : TrackType, subIndex : int):
|
||||||
|
for t in self.getAllTrackDescriptors():
|
||||||
|
if t.getType() == trackType:
|
||||||
|
t.setDispositionFlag(TrackDisposition.FORCED, t.getSubIndex() == int(subIndex))
|
||||||
|
|
||||||
|
|
||||||
def getReorderedTrackDescriptors(self):
|
def getReorderedTrackDescriptors(self):
|
||||||
|
|
||||||
videoTracks = self.getVideoTracks()
|
videoTracks = self.sortSubIndices(self.getVideoTracks())
|
||||||
audioTracks = self.getAudioTracks()
|
audioTracks = self.sortSubIndices(self.getAudioTracks())
|
||||||
subtitleTracks = self.getSubtitleTracks()
|
subtitleTracks = self.sortSubIndices(self.getSubtitleTracks())
|
||||||
|
|
||||||
videoDefaultTracks = [v for v in videoTracks if TrackDisposition.DEFAULT in v.getDispositionSet()]
|
videoDefaultTrack = self.getDefaultVideoTrack()
|
||||||
videoForcedTracks = [v for v in videoTracks if TrackDisposition.FORCED in v.getDispositionSet()]
|
self.getForcedVideoTrack()
|
||||||
audioDefaultTracks = [a for a in audioTracks if TrackDisposition.DEFAULT in a.getDispositionSet()]
|
audioDefaultTrack = self.getDefaultAudioTrack()
|
||||||
audioForcedTracks = [a for a in audioTracks if TrackDisposition.FORCED in a.getDispositionSet()]
|
self.getForcedAudioTrack()
|
||||||
subtitleDefaultTracks = [s for s in subtitleTracks if TrackDisposition.DEFAULT in s.getDispositionSet()]
|
subtitleDefaultTrack = self.getDefaultSubtitleTrack()
|
||||||
subtitleForcedTracks = [s for s in subtitleTracks if TrackDisposition.FORCED in s.getDispositionSet()]
|
self.getForcedSubtitleTrack()
|
||||||
|
|
||||||
if len(videoDefaultTracks) > 1:
|
|
||||||
raise ValueError('MediaDescriptor.__getSourceIndexOrder(): More than one default video track is not supported')
|
|
||||||
if len(videoForcedTracks) > 1:
|
|
||||||
raise ValueError('MediaDescriptor.__getSourceIndexOrder(): More than one forced video track is not supported')
|
|
||||||
if len(audioDefaultTracks) > 1:
|
|
||||||
raise ValueError('MediaDescriptor.__getSourceIndexOrder(): More than one default audio track is not supported')
|
|
||||||
if len(audioForcedTracks) > 1:
|
|
||||||
raise ValueError('MediaDescriptor.__getSourceIndexOrder(): More than one forced audio track is not supported')
|
|
||||||
if len(subtitleDefaultTracks) > 1:
|
|
||||||
raise ValueError('MediaDescriptor.__getSourceIndexOrder(): More than one default subtitle track is not supported')
|
|
||||||
if len(subtitleForcedTracks) > 1:
|
|
||||||
raise ValueError('MediaDescriptor.__getSourceIndexOrder(): More than one forced subtitle track is not supported')
|
|
||||||
|
|
||||||
if self.__jellyfinOrder:
|
if self.__jellyfinOrder:
|
||||||
if videoDefaultTracks:
|
if not videoDefaultTrack is None:
|
||||||
videoTracks.append(videoTracks.pop(videoTracks.index(videoDefaultTracks[0])))
|
videoTracks.append(videoTracks.pop(videoTracks.index(videoDefaultTrack)))
|
||||||
if audioDefaultTracks:
|
if not audioDefaultTrack is None:
|
||||||
audioTracks.append(audioTracks.pop(audioTracks.index(audioDefaultTracks[0])))
|
audioTracks.append(audioTracks.pop(audioTracks.index(audioDefaultTrack)))
|
||||||
if subtitleDefaultTracks:
|
if not subtitleDefaultTrack is None:
|
||||||
subtitleTracks.append(subtitleTracks.pop(subtitleTracks.index(subtitleDefaultTracks[0])))
|
subtitleTracks.append(subtitleTracks.pop(subtitleTracks.index(subtitleDefaultTrack)))
|
||||||
|
|
||||||
reorderedTrackDescriptors = videoTracks + audioTracks + subtitleTracks
|
reorderedTrackDescriptors = videoTracks + audioTracks + subtitleTracks
|
||||||
orderedSourceTrackSequence = [t.getSourceIndex() for t in reorderedTrackDescriptors]
|
orderedSourceTrackSequence = [t.getSourceIndex() for t in reorderedTrackDescriptors]
|
||||||
@@ -143,32 +178,26 @@ class MediaDescriptor():
|
|||||||
return self.__mediaTags
|
return self.__mediaTags
|
||||||
|
|
||||||
|
|
||||||
|
def sortSubIndices(self, descriptors : List[TrackDescriptor]) -> List[TrackDescriptor]:
|
||||||
|
subIndex = 0
|
||||||
|
for t in descriptors:
|
||||||
|
t.setSubIndex(subIndex)
|
||||||
|
subIndex += 1
|
||||||
|
return descriptors
|
||||||
|
|
||||||
|
|
||||||
def getAllTrackDescriptors(self) -> List[TrackDescriptor]:
|
def getAllTrackDescriptors(self) -> List[TrackDescriptor]:
|
||||||
return self.getVideoTracks() + self.getAudioTracks() + self.getSubtitleTracks()
|
return self.getVideoTracks() + self.getAudioTracks() + self.getSubtitleTracks()
|
||||||
|
|
||||||
def getVideoTracks(self) -> List[TrackDescriptor]:
|
def getVideoTracks(self) -> List[TrackDescriptor]:
|
||||||
videoTracks = [v for v in self.__trackDescriptors.copy() if v.getType() == TrackType.VIDEO]
|
return [v for v in self.__trackDescriptors.copy() if v.getType() == TrackType.VIDEO]
|
||||||
subIndex = 0
|
|
||||||
for v in videoTracks:
|
|
||||||
v.setSubIndex(subIndex)
|
|
||||||
subIndex += 1
|
|
||||||
return videoTracks
|
|
||||||
|
|
||||||
def getAudioTracks(self) -> List[TrackDescriptor]:
|
def getAudioTracks(self) -> List[TrackDescriptor]:
|
||||||
audioTracks = [a for a in self.__trackDescriptors.copy() if a.getType() == TrackType.AUDIO]
|
return [a for a in self.__trackDescriptors.copy() if a.getType() == TrackType.AUDIO]
|
||||||
subIndex = 0
|
|
||||||
for a in audioTracks:
|
|
||||||
a.setSubIndex(subIndex)
|
|
||||||
subIndex += 1
|
|
||||||
return audioTracks
|
|
||||||
|
|
||||||
def getSubtitleTracks(self) -> List[TrackDescriptor]:
|
def getSubtitleTracks(self) -> List[TrackDescriptor]:
|
||||||
subtitleTracks = [s for s in self.__trackDescriptors.copy() if s.getType() == TrackType.SUBTITLE]
|
return [s for s in self.__trackDescriptors.copy() if s.getType() == TrackType.SUBTITLE]
|
||||||
subIndex = 0
|
|
||||||
for s in subtitleTracks:
|
|
||||||
s.setSubIndex(subIndex)
|
|
||||||
subIndex += 1
|
|
||||||
return subtitleTracks
|
|
||||||
|
|
||||||
def getJellyfin(self):
|
def getJellyfin(self):
|
||||||
return self.__jellyfinOrder
|
return self.__jellyfinOrder
|
||||||
|
|||||||
@@ -229,3 +229,10 @@ class TrackController():
|
|||||||
raise click.ClickException(f"TrackController.deleteTrack(): {repr(ex)}")
|
raise click.ClickException(f"TrackController.deleteTrack(): {repr(ex)}")
|
||||||
finally:
|
finally:
|
||||||
s.close()
|
s.close()
|
||||||
|
|
||||||
|
|
||||||
|
def setDefaultSubTrack(self, trackType, subIndex):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def setForcedSubTrack(self, trackType, subIndex):
|
||||||
|
pass
|
||||||
|
|||||||
@@ -204,6 +204,16 @@ class TrackDescriptor():
|
|||||||
return self.__dispositionSet
|
return self.__dispositionSet
|
||||||
|
|
||||||
|
|
||||||
|
def getDispositionFlag(self, disposition : TrackDisposition) -> bool:
|
||||||
|
return bool(disposition in self.__dispositionSet)
|
||||||
|
|
||||||
|
def setDispositionFlag(self, disposition : TrackDisposition, state : bool):
|
||||||
|
if state:
|
||||||
|
self.__dispositionSet.add(disposition)
|
||||||
|
else:
|
||||||
|
self.__dispositionSet.discard(disposition)
|
||||||
|
|
||||||
|
|
||||||
def compare(self, vsTrackDescriptor):
|
def compare(self, vsTrackDescriptor):
|
||||||
|
|
||||||
compareResult = {}
|
compareResult = {}
|
||||||
|
|||||||
Reference in New Issue
Block a user