Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
24d0700db2 | ||
|
|
3463c1e371 | ||
|
|
5ca7d6d12c | ||
|
|
77dfb4b1d3 | ||
| 1df2e74566 | |||
| 8f9f77e891 |
229
bin/ffx.py
229
bin/ffx.py
@@ -11,16 +11,22 @@ from ffx.tmdb_controller import TmdbController
|
||||
|
||||
from ffx.database import databaseContext
|
||||
|
||||
from ffx.track_descriptor import TrackDescriptor
|
||||
from ffx.track_type import TrackType
|
||||
from ffx.video_encoder import VideoEncoder
|
||||
from ffx.track_disposition import TrackDisposition
|
||||
|
||||
from ffx.process import executeProcess
|
||||
|
||||
|
||||
VERSION='0.1.2'
|
||||
VERSION='0.1.3'
|
||||
|
||||
# 0.1.1
|
||||
# Bugfixes, TMBD identify shows
|
||||
# 0.1.2
|
||||
# Bugfixes
|
||||
# 0.1.3
|
||||
# Subtitle file imports
|
||||
|
||||
|
||||
@click.group()
|
||||
@@ -59,52 +65,98 @@ def inspect(ctx, filename):
|
||||
app = FfxApp(ctx.obj)
|
||||
app.run()
|
||||
|
||||
#TODO: TrackCodec Klasse
|
||||
CODEC_LOOKUP_TABLE = {
|
||||
'h264': {'format': 'h264', 'extension': 'h264'},
|
||||
'aac': { 'extension': 'aac'},
|
||||
'ac3': {'format': 'ac3', 'extension': 'ac3'},
|
||||
'ass': {'format': 'ass', 'extension': 'ass'},
|
||||
'hdmv_pgs_subtitle': {'format': 'sup', 'extension': 'sup'}
|
||||
}
|
||||
|
||||
def getUnmuxSequence(trackDescriptor: TrackDescriptor, sourcePath, targetPrefix, targetDirectory = ''):
|
||||
|
||||
trackCodec = trackDescriptor.getCodec()
|
||||
|
||||
if not trackCodec in CODEC_LOOKUP_TABLE.keys():
|
||||
return []
|
||||
|
||||
commandTokens = FfxController.COMMAND_TOKENS + ['-i', sourcePath]
|
||||
trackType = trackDescriptor.getType()
|
||||
|
||||
targetPathBase = os.path.join(targetDirectory, targetPrefix) if targetDirectory else targetPrefix
|
||||
|
||||
commandTokens += ['-map',
|
||||
f"0:{trackType.indicator()}:{trackDescriptor.getSubIndex()}",
|
||||
'-c',
|
||||
'copy']
|
||||
|
||||
if 'format' in CODEC_LOOKUP_TABLE[trackCodec].keys():
|
||||
commandTokens += ['-f', CODEC_LOOKUP_TABLE[trackCodec]['format']]
|
||||
|
||||
commandTokens += [f"{targetPathBase}.{CODEC_LOOKUP_TABLE[trackCodec]['extension']}"]
|
||||
|
||||
return commandTokens
|
||||
|
||||
|
||||
# @ffx.command()
|
||||
# @click.pass_context
|
||||
#
|
||||
# @click.argument('paths', nargs=-1)
|
||||
# @click.option('-l', '--label', type=str, default='', help='Label to be used as filename prefix')
|
||||
#
|
||||
# @click.option('-sd', '--subtitle-directory', type=str, default='', help='Load subtitles from here')
|
||||
# @click.option('-sp', '--subtitle-prefix', type=str, default='', help='Subtitle filename prefix')
|
||||
#
|
||||
# @click.option("-o", "--output-directory", type=str, default='')
|
||||
#
|
||||
# @click.option("--dry-run", is_flag=True, default=False)
|
||||
#
|
||||
#
|
||||
# def unmux(ctx,
|
||||
# label,
|
||||
# paths,
|
||||
# subtitle_directory,
|
||||
# subtitle_prefix,
|
||||
# output_directory,
|
||||
# dry_run):
|
||||
#
|
||||
# existingSourcePaths = [p for p in paths if os.path.isfile(p)]
|
||||
# click.echo(f"\nUnmuxing {len(existingSourcePaths)} files")
|
||||
#
|
||||
# for sourcePath in existingSourcePaths:
|
||||
#
|
||||
# sd = getStreamDescriptor(sourcePath)
|
||||
#
|
||||
# print(f"\nFile {sourcePath}\n")
|
||||
#
|
||||
# for v in sd['video']:
|
||||
#
|
||||
# if v['codec_name'] == 'h264':
|
||||
#
|
||||
# commandSequence = ['ffmpeg', '-i', sourcePath, '-map', '0:v:0', '-c', 'copy', '-f', 'h264']
|
||||
# executeProcess()
|
||||
#
|
||||
# for a in sd['audio']:
|
||||
# print(f"A: {a}\n")
|
||||
# for s in sd['subtitle']:
|
||||
# print(f"S: {s}\n")
|
||||
@ffx.command()
|
||||
@click.pass_context
|
||||
|
||||
@click.argument('paths', nargs=-1)
|
||||
@click.option('-l', '--label', type=str, default='', help='Label to be used as filename prefix')
|
||||
@click.option("-o", "--output-directory", type=str, default='')
|
||||
@click.option("-s", "--subtitles-only", is_flag=True, default=False)
|
||||
@click.option("--dry-run", is_flag=True, default=False)
|
||||
def unmux(ctx,
|
||||
paths,
|
||||
label,
|
||||
output_directory,
|
||||
subtitles_only,
|
||||
dry_run):
|
||||
|
||||
existingSourcePaths = [p for p in paths if os.path.isfile(p)]
|
||||
click.echo(f"\nUnmuxing {len(existingSourcePaths)} files")
|
||||
|
||||
for sourcePath in existingSourcePaths:
|
||||
|
||||
fp = FileProperties(ctx.obj, sourcePath)
|
||||
|
||||
|
||||
try:
|
||||
sourceMediaDescriptor = fp.getMediaDescriptor()
|
||||
|
||||
season = fp.getSeason()
|
||||
episode = fp.getEpisode()
|
||||
|
||||
#TODO: Recognition für alle Formate anpassen
|
||||
targetLabel = label if label else fp.getFileBasename()
|
||||
targetIndicator = f"_S{season}E{episode}" if label and season != -1 and episode != -1 else ''
|
||||
|
||||
if label and not targetIndicator:
|
||||
click.echo(f"Skipping file {fp.getFilename()}: Label set but no indicator recognized")
|
||||
continue
|
||||
else:
|
||||
click.echo(f"\nUnmuxing file {fp.getFilename()}\n")
|
||||
|
||||
for trackDescriptor in sourceMediaDescriptor.getAllTrackDescriptors():
|
||||
|
||||
if trackDescriptor.getType() == TrackType.SUBTITLE or not subtitles_only:
|
||||
|
||||
# SEASON_EPISODE_STREAM_LANGUAGE_MATCH = '[sS]([0-9]+)[eE]([0-9]+)_([0-9]+)_([a-z]{3})'
|
||||
targetPrefix = f"{targetLabel}{targetIndicator}_{trackDescriptor.getIndex()}_{trackDescriptor.getLanguage().threeLetter()}"
|
||||
|
||||
unmuxSequence = getUnmuxSequence(trackDescriptor, sourcePath, targetPrefix, targetDirectory = output_directory)
|
||||
|
||||
if unmuxSequence:
|
||||
if not dry_run:
|
||||
click.echo(f"Executing unmuxing sequence: {' '.join(unmuxSequence)}")
|
||||
out, err, rc = executeProcess(unmuxSequence)
|
||||
if rc:
|
||||
click.echo(f"Unmuxing of stream {trackDescriptor.getIndex()} failed with error ({rc}) {err}")
|
||||
else:
|
||||
click.echo(f"Skipping stream with unknown codec {trackDescriptor.getCodec()}")
|
||||
except Exception as ex:
|
||||
click.echo(f"Skipping File {sourcePath} ({ex})")
|
||||
|
||||
|
||||
@ffx.command()
|
||||
@@ -160,6 +212,7 @@ def shows(ctx):
|
||||
|
||||
@click.option("-t", "--no-tmdb", is_flag=True, default=False)
|
||||
@click.option("-j", "--no-jellyfin", is_flag=True, default=False)
|
||||
@click.option("-np", "--no-pattern", is_flag=True, default=False)
|
||||
|
||||
@click.option("--dry-run", is_flag=True, default=False)
|
||||
|
||||
@@ -173,6 +226,7 @@ def convert(ctx,
|
||||
stereo_bitrate,
|
||||
ac3_bitrate,
|
||||
dts_bitrate,
|
||||
|
||||
subtitle_directory,
|
||||
subtitle_prefix,
|
||||
|
||||
@@ -191,6 +245,7 @@ def convert(ctx,
|
||||
denoise,
|
||||
no_tmdb,
|
||||
no_jellyfin,
|
||||
no_pattern,
|
||||
dry_run):
|
||||
"""Batch conversion of audiovideo files in format suitable for web playback, e.g. jellyfin
|
||||
|
||||
@@ -203,12 +258,13 @@ def convert(ctx,
|
||||
|
||||
context = ctx.obj
|
||||
|
||||
context['dry_run'] = True # dry_run
|
||||
context['dry_run'] = dry_run
|
||||
|
||||
context['video_encoder'] = VideoEncoder.fromLabel(video_encoder)
|
||||
|
||||
context['jellyfin'] = not no_jellyfin
|
||||
context['tmdb'] = not no_tmdb
|
||||
context['use_jellyfin'] = not no_jellyfin
|
||||
context['use_tmdb'] = not no_tmdb
|
||||
context['use_pattern'] = not no_pattern
|
||||
|
||||
context['import_subtitles'] = (subtitle_directory and subtitle_prefix)
|
||||
if context['import_subtitles']:
|
||||
@@ -238,11 +294,11 @@ def convert(ctx,
|
||||
cTokens = crop.split(',')
|
||||
if cTokens and len(cTokens) == 2:
|
||||
context['crop_start'] = int(cTokens[0])
|
||||
context['crop_lenght'] = int(cTokens[1])
|
||||
context['crop_length'] = int(cTokens[1])
|
||||
click.echo(f"Crop start={context['crop_start']} length={context['crop_length']}")
|
||||
|
||||
|
||||
tc = TmdbController()
|
||||
tc = TmdbController() if context['use_tmdb'] else None
|
||||
|
||||
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")
|
||||
@@ -265,7 +321,7 @@ def convert(ctx,
|
||||
sourceMediaDescriptor = mediaFileProperties.getMediaDescriptor()
|
||||
|
||||
#HINT: This is None if the filename did not match anything in database
|
||||
currentPattern = mediaFileProperties.getPattern()
|
||||
currentPattern = mediaFileProperties.getPattern() if context['use_pattern'] else None
|
||||
|
||||
click.echo(f"Pattern matching: {'No' if currentPattern is None else 'Yes'}")
|
||||
|
||||
@@ -279,45 +335,39 @@ def convert(ctx,
|
||||
#
|
||||
# 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:
|
||||
if len([v for v in sourceMediaDescriptor.getVideoTracks() if v.getDispositionFlag(TrackDisposition.DEFAULT)]) > 1:
|
||||
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:
|
||||
|
||||
if len([v for v in sourceMediaDescriptor.getVideoTracks() if v.getDispositionFlag(TrackDisposition.FORCED)]) > 1:
|
||||
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:
|
||||
|
||||
if len([a for a in sourceMediaDescriptor.getAudioTracks() if a.getDispositionFlag(TrackDisposition.DEFAULT)]) > 1:
|
||||
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:
|
||||
|
||||
if len([a for a in sourceMediaDescriptor.getAudioTracks() if a.getDispositionFlag(TrackDisposition.FORCED)]) > 1:
|
||||
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:
|
||||
|
||||
if len([s for s in sourceMediaDescriptor.getSubtitleTracks() if s.getDispositionFlag(TrackDisposition.DEFAULT)]) > 1:
|
||||
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:
|
||||
|
||||
if len([s for s in sourceMediaDescriptor.getSubtitleTracks() if s.getDispositionFlag(TrackDisposition.FORCED)]) > 1:
|
||||
forcedSubtitleTrackSubIndex = click.prompt("More than one forced subtitle stream detected! Please select stream", type=int)
|
||||
sourceMediaDescriptor.setForcedSubTrack(TrackType.SUBTITLE, forcedSubtitleTrackSubIndex)
|
||||
|
||||
|
||||
if context['import_subtitles']:
|
||||
sourceMediaDescriptor.importSubtitles(context['subtitle_directory'], context['subtitle_prefix'])
|
||||
sourceMediaDescriptor.importSubtitles(context['subtitle_directory'],
|
||||
context['subtitle_prefix'],
|
||||
mediaFileProperties.getSeason(),
|
||||
mediaFileProperties.getEpisode())
|
||||
|
||||
fc = FfxController(context, sourceMediaDescriptor)
|
||||
|
||||
# mappingTokens = fc.generateMetadataTokens()
|
||||
# click.echo(f"Metadata Tokens: {mappingTokens}")
|
||||
|
||||
|
||||
dispositionTokens = fc.generateDispositionTokens()
|
||||
click.echo(f"Disposition Tokens: {dispositionTokens}")
|
||||
|
||||
@@ -329,15 +379,14 @@ def convert(ctx,
|
||||
# Case pattern matching
|
||||
|
||||
targetMediaDescriptor = currentPattern.getMediaDescriptor()
|
||||
|
||||
currentShowDescriptor = currentPattern.getShowDescriptor()
|
||||
|
||||
|
||||
if context['tmdb']:
|
||||
if context['use_tmdb']:
|
||||
|
||||
tmdbEpisodeResult = tc.queryEpisode(currentShowDescriptor.getId(), mediaFileProperties.getSeason(), mediaFileProperties.getEpisode())
|
||||
|
||||
# click.echo(f"{tmdbEpisodeResult}")
|
||||
|
||||
if tmdbEpisodeResult:
|
||||
fileBasename = tc.getEpisodeFileBasename(currentShowDescriptor.getFilenamePrefix(),
|
||||
tmdbEpisodeResult['name'],
|
||||
@@ -346,20 +395,29 @@ def convert(ctx,
|
||||
currentShowDescriptor.getIndexSeasonDigits(),
|
||||
currentShowDescriptor.getIndexEpisodeDigits(),
|
||||
currentShowDescriptor.getIndicatorSeasonDigits(),
|
||||
currentShowDescriptor.getIndicatorEpisodeDigits())
|
||||
|
||||
|
||||
currentShowDescriptor.getIndicatorEpisodeDigits())
|
||||
else:
|
||||
fileBasename = currentShowDescriptor.getFilenamePrefix()
|
||||
|
||||
click.echo(f"fileBasename={fileBasename}")
|
||||
|
||||
if context['import_subtitles']:
|
||||
targetMediaDescriptor.importSubtitles(context['subtitle_directory'], context['subtitle_prefix'])
|
||||
targetMediaDescriptor.importSubtitles(context['subtitle_directory'],
|
||||
context['subtitle_prefix'],
|
||||
mediaFileProperties.getSeason(),
|
||||
mediaFileProperties.getEpisode())
|
||||
|
||||
targetMediaDescriptor.setJellyfinOrder(context['jellyfin'])
|
||||
# raise click.ClickException(f"tmd subindices: {[t.getSubIndex() for t in targetMediaDescriptor.getAllTrackDescriptors()]}")
|
||||
# click.echo(f"tmd subindices: {[t.getIndex() for t in targetMediaDescriptor.getAllTrackDescriptors()]} {[t.getSubIndex() for t in targetMediaDescriptor.getAllTrackDescriptors()]} {[t.getDispositionFlag(TrackDisposition.DEFAULT) for t in targetMediaDescriptor.getAllTrackDescriptors()]}")
|
||||
|
||||
click.echo(f"Input mapping tokens: {targetMediaDescriptor.getInputMappingTokens()}")
|
||||
if context['use_jellyfin']:
|
||||
# Reorder subtracks in types with default the last, then make subindices flat again
|
||||
targetMediaDescriptor.applyJellyfinOrder()
|
||||
|
||||
# click.echo(f"tmd subindices: {[t.getIndex() for t in targetMediaDescriptor.getAllTrackDescriptors()]} {[t.getSubIndex() for t in targetMediaDescriptor.getAllTrackDescriptors()]} {[t.getDispositionFlag(TrackDisposition.DEFAULT) for t in targetMediaDescriptor.getAllTrackDescriptors()]}")
|
||||
# raise click.Abort
|
||||
|
||||
click.echo(f"Input mapping tokens (2nd pass): {targetMediaDescriptor.getInputMappingTokens()}")
|
||||
|
||||
fc = FfxController(context, targetMediaDescriptor, sourceMediaDescriptor)
|
||||
|
||||
@@ -382,9 +440,10 @@ def convert(ctx,
|
||||
|
||||
extra = ['ffx'] if sourceFilenameExtension == FfxController.DEFAULT_FILE_EXTENSION else []
|
||||
|
||||
targetFilename = fileBasename if context['tmdb'] else mediaFileProperties.assembleTargetFileBasename(label if label else fileBasename,
|
||||
q if len(q_list) > 1 else -1,
|
||||
extraTokens = extra)
|
||||
targetFilename = (fileBasename if context['use_tmdb']
|
||||
else mediaFileProperties.assembleTargetFileBasename(label if label else fileBasename,
|
||||
q if len(q_list) > 1 else -1,
|
||||
extraTokens = extra))
|
||||
|
||||
targetPath = os.path.join(output_directory if output_directory else sourceDirectory, targetFilename)
|
||||
|
||||
@@ -395,7 +454,7 @@ def convert(ctx,
|
||||
preset,
|
||||
denoise)
|
||||
|
||||
# #click.confirm('Warning! This file is not compliant to the defined source schema! Do you want to continue?', abort=True)
|
||||
#TODO: click.confirm('Warning! This file is not compliant to the defined source schema! Do you want to continue?', abort=True)
|
||||
|
||||
endTime = time.perf_counter()
|
||||
click.echo(f"\nDONE\nTime elapsed {endTime - startTime}")
|
||||
|
||||
@@ -107,7 +107,8 @@ class FfxController():
|
||||
|
||||
|
||||
def generateOutputTokens(self, filepath, format, ext):
|
||||
return ['-f', format, f"{filepath}.{ext}"]
|
||||
outputFilePath = f"{filepath}.{ext}"
|
||||
return ['-f', format, outputFilePath]
|
||||
|
||||
|
||||
def generateAudioEncodingTokens(self):
|
||||
@@ -116,7 +117,8 @@ class FfxController():
|
||||
audioTokens = []
|
||||
|
||||
#sourceAudioTrackDescriptors = [smd for smd in self.__sourceMediaDescriptor.getAllTrackDescriptors() if smd.getType() == TrackType.AUDIO]
|
||||
targetAudioTrackDescriptors = [rtd for rtd in self.__targetMediaDescriptor.getReorderedTrackDescriptors() if rtd.getType() == TrackType.AUDIO]
|
||||
# targetAudioTrackDescriptors = [rtd for rtd in self.__targetMediaDescriptor.getReorderedTrackDescriptors() if rtd.getType() == TrackType.AUDIO]
|
||||
targetAudioTrackDescriptors = [td for td in self.__targetMediaDescriptor.getAllTrackDescriptors() if td.getType() == TrackType.AUDIO]
|
||||
|
||||
trackSubIndex = 0
|
||||
for trackDescriptor in targetAudioTrackDescriptors:
|
||||
@@ -165,185 +167,206 @@ class FfxController():
|
||||
return audioTokens
|
||||
|
||||
|
||||
# -disposition:s:0 default -disposition:s:1 0
|
||||
def generateDispositionTokens(self):
|
||||
"""Source media descriptor is optional"""
|
||||
|
||||
sourceTrackDescriptors = [] if self.__sourceMediaDescriptor is None else self.__sourceMediaDescriptor.getAllTrackDescriptors()
|
||||
targetTrackDescriptors = self.__targetMediaDescriptor.getReorderedTrackDescriptors()
|
||||
|
||||
# sourceTrackDescriptors = [] if self.__sourceMediaDescriptor is None else self.__sourceMediaDescriptor.getAllTrackDescriptors()
|
||||
targetTrackDescriptors = self.__targetMediaDescriptor.getAllTrackDescriptors()
|
||||
|
||||
dispositionTokens = []
|
||||
|
||||
# raise click.ClickException(f"ttd subindices: {[t.getSubIndex() for t in targetTrackDescriptors]}")
|
||||
|
||||
# for subStreamIndex in range(len(subDescriptor)):
|
||||
#TODO: Sorting here is for the sole purpose to let the tokens appear with ascending subindices. Why necessary? Jellyfin order?
|
||||
# for trackDescriptor in sorted(targetTrackDescriptors.copy(), key=lambda d: d.getSubIndex()):
|
||||
for trackDescriptor in targetTrackDescriptors:
|
||||
|
||||
# Calculate source sub index. This applies only if a source media descriptor is defined.
|
||||
if sourceTrackDescriptors:
|
||||
changedTargetTrackDescriptor : TrackDescriptor = targetTrackDescriptors[trackDescriptor.getIndex()]
|
||||
changedTargetTrackSourceIndex = changedTargetTrackDescriptor.getSourceIndex()
|
||||
subIndex = sourceTrackDescriptors[changedTargetTrackSourceIndex].getSubIndex()
|
||||
else:
|
||||
#HINT: No dispositions for pgs subtitle tracks that have no external file source
|
||||
if (trackDescriptor.getExternalSourceFilePath()
|
||||
or trackDescriptor.getCodec() != TrackDescriptor.CODEC_PGS):
|
||||
|
||||
subIndex = trackDescriptor.getSubIndex()
|
||||
streamIndicator = trackDescriptor.getType().indicator()
|
||||
dispositionSet = trackDescriptor.getDispositionSet()
|
||||
|
||||
streamIndicator = trackDescriptor.getType().indicator()
|
||||
dispositionSet = trackDescriptor.getDispositionSet()
|
||||
|
||||
if dispositionSet:
|
||||
dispositionTokens += [f"-disposition:{streamIndicator}:{subIndex}", '+'.join([d.label() for d in dispositionSet])]
|
||||
else:
|
||||
dispositionTokens += [f"-disposition:{streamIndicator}:{subIndex}", '0']
|
||||
if dispositionSet:
|
||||
dispositionTokens += [f"-disposition:{streamIndicator}:{subIndex}", '+'.join([d.label() for d in dispositionSet])]
|
||||
else:
|
||||
dispositionTokens += [f"-disposition:{streamIndicator}:{subIndex}", '0']
|
||||
|
||||
return dispositionTokens
|
||||
|
||||
|
||||
# def generateMetadataTokens(self):
|
||||
# """Source media descriptor is mandatory"""
|
||||
#
|
||||
# metadataTokens = []
|
||||
#
|
||||
# # click.echo(f"source media descriptor: track indices={[d.getIndex() for d in sourceMediaDescriptor.getAllTrackDescriptors()]}")
|
||||
# # click.echo(f"target media descriptor: track indices={[d.getIndex() for d in targetMediaDescriptor.getAllTrackDescriptors()]}")
|
||||
#
|
||||
# # +jellyfin -jellyfin
|
||||
# mediaDifferences = self.__targetMediaDescriptor.compare(self.__sourceMediaDescriptor)
|
||||
#
|
||||
# # media diff {'tracks': {'changed': {4: {'tags': {'added': {'Yolo'}}}}}}
|
||||
#
|
||||
# click.echo(f"media diff {mediaDifferences}")
|
||||
#
|
||||
# if MediaDescriptor.TAGS_KEY in mediaDifferences.keys():
|
||||
#
|
||||
# sourceTags = self.__sourceMediaDescriptor.getTags()
|
||||
# targetTags = self.__targetMediaDescriptor.getTags()
|
||||
#
|
||||
# #TODO: Warum erscheint nur -1 im output?
|
||||
# if DIFF_REMOVED_KEY in mediaDifferences[MediaDescriptor.TAGS_KEY].keys():
|
||||
# # for removedTagKey in mediaDifferences[MediaDescriptor.TAGS_KEY][DIFF_REMOVED_KEY]:
|
||||
# # row = (f"removed media tag: key='{removedTagKey}' value='{sourceTags[removedTagKey]}'",)
|
||||
# # self.differencesTable.add_row(*map(str, row))
|
||||
# pass
|
||||
# #metadataTokens += [f"-map_metadata:g", "-1"]
|
||||
#
|
||||
# #for targetMediaTagKey in targetTags:
|
||||
# #metadataTokens += [f"-metadata:g", f"{targetMediaTagKey}={targetTags[targetMediaTagKey]}"]
|
||||
#
|
||||
# else:
|
||||
#
|
||||
# if DIFF_ADDED_KEY in mediaDifferences[MediaDescriptor.TAGS_KEY].keys():
|
||||
# for addedTagKey in mediaDifferences[MediaDescriptor.TAGS_KEY][DIFF_ADDED_KEY]:
|
||||
# # row = (f"added media tag: key='{addedTagKey}' value='{targetTags[addedTagKey]}'",)
|
||||
# click.echo(f"added metadata key='{addedTagKey}' value='{targetTags[addedTagKey]}'->'{targetTags[addedTagKey]}'")
|
||||
# # self.differencesTable.add_row(*map(str, row))
|
||||
# #pass
|
||||
# metadataTokens += [f"-metadata:g", f"{addedTagKey}={targetTags[addedTagKey]}"]
|
||||
#
|
||||
#
|
||||
#
|
||||
# if DIFF_CHANGED_KEY in mediaDifferences[MediaDescriptor.TAGS_KEY].keys():
|
||||
# for changedTagKey in mediaDifferences[MediaDescriptor.TAGS_KEY][DIFF_CHANGED_KEY]:
|
||||
# #row = (f"changed media tag: key='{changedTagKey}' value='{sourceTags[changedTagKey]}'->'{targetTags[changedTagKey]}'",)
|
||||
# click.echo(f"changed metadata key='{changedTagKey}' value='{sourceTags[changedTagKey]}'->'{targetTags[changedTagKey]}'")
|
||||
# # self.differencesTable.add_row(*map(str, row))
|
||||
# #pass
|
||||
# metadataTokens += [f"-metadata:g", f"{changedTagKey}={targetTags[changedTagKey]}"]
|
||||
#
|
||||
# if MediaDescriptor.TRACKS_KEY in mediaDifferences.keys():
|
||||
#
|
||||
# sourceTrackDescriptors = self.__sourceMediaDescriptor.getAllTrackDescriptors()
|
||||
# targetTrackDescriptors = self.__targetMediaDescriptor.getReorderedTrackDescriptors()
|
||||
#
|
||||
# if DIFF_ADDED_KEY in mediaDifferences[MediaDescriptor.TRACKS_KEY].keys():
|
||||
# addedTracksIndices = mediaDifferences[MediaDescriptor.TRACKS_KEY][DIFF_ADDED_KEY]
|
||||
# raise click.ClickException(f"FfxController.generateMetadataTokens(): Adding tracks is not supported. Track indices {addedTracksIndices}")
|
||||
#
|
||||
# #raise click.ClickException(f"add track {mediaDifferences[MediaDescriptor.TRACKS_KEY][DIFF_ADDED_KEY]}")
|
||||
# #for addedTrackIndex in mediaDifferences[MediaDescriptor.TRACKS_KEY][DIFF_ADDED_KEY]:
|
||||
# #addedTrack : Track = targetTrackDescriptors[addedTrackIndex]
|
||||
# # row = (f"added {addedTrack.getType().label()} track: index={addedTrackIndex} lang={addedTrack.getLanguage().threeLetter()}",)
|
||||
# # self.differencesTable.add_row(*map(str, row))
|
||||
#
|
||||
# if DIFF_REMOVED_KEY in mediaDifferences[MediaDescriptor.TRACKS_KEY].keys():
|
||||
# removedTracksIndices = mediaDifferences[MediaDescriptor.TRACKS_KEY][DIFF_ADDED_KEY].keys()
|
||||
# raise click.ClickException(f"FfxController.generateMetadataTokens(): Removing tracks is not supported. Track indices {removedTracksIndices}")
|
||||
# #for removedTrackIndex in mediaDifferences[MediaDescriptor.TRACKS_KEY][DIFF_REMOVED_KEY]:
|
||||
# # row = (f"removed track: index={removedTrackIndex}",)
|
||||
# # self.differencesTable.add_row(*map(str, row))
|
||||
#
|
||||
# # media diff {'tracks': {'changed': {4: {'tags': {'added': {'Yolo'}}}}}}
|
||||
# if DIFF_CHANGED_KEY in mediaDifferences[MediaDescriptor.TRACKS_KEY].keys():
|
||||
# for changedTrackIndex in mediaDifferences[MediaDescriptor.TRACKS_KEY][DIFF_CHANGED_KEY].keys():
|
||||
#
|
||||
# changedTargetTrackDescriptor : TrackDescriptor = targetTrackDescriptors[changedTrackIndex]
|
||||
# changedTargetTrackSourceIndex = changedTargetTrackDescriptor.getSourceIndex()
|
||||
# changedTargetSourceSubIndex = sourceTrackDescriptors[changedTargetTrackSourceIndex].getSubIndex()
|
||||
# # changedSourceTrackDescriptor : TrackDescriptor = sourceTrackDescriptors[changedTargetTrackSourceIndex]
|
||||
# # changedSourceTrackSubIndex = changedSourceTrackDescriptor.getSubIndex()
|
||||
#
|
||||
# changedTrackDiff : dict = mediaDifferences[MediaDescriptor.TRACKS_KEY][DIFF_CHANGED_KEY][changedTrackIndex]
|
||||
#
|
||||
# if MediaDescriptor.TAGS_KEY in changedTrackDiff.keys():
|
||||
#
|
||||
#
|
||||
# if DIFF_REMOVED_KEY in changedTrackDiff[MediaDescriptor.TAGS_KEY]:
|
||||
# #for removedTagKey in changedTrackDiff[MediaDescriptor.TAGS_KEY][DIFF_REMOVED_KEY]:
|
||||
# # row = (f"changed {changedTargetTrackDescriptor.getType().label()} track index={changedTrackIndex} removed key={removedTagKey}",)
|
||||
# # self.differencesTable.add_row(*map(str, row))
|
||||
#
|
||||
# #addedTagValue = targetTrackDescriptors[changedTargetTrackSourceIndex].getTags()[addedTagKey]
|
||||
#
|
||||
# metadataTokens += [f"-map_metadata:s:{changedTargetTrackDescriptor.getType().indicator()}:{changedTargetSourceSubIndex}", "-1"]
|
||||
#
|
||||
# for targetTrackTagKey, targetTrackTagValue in changedTargetTrackDescriptor.getTags():
|
||||
# metadataTokens += [f"-metadata:s:{changedTargetTrackDescriptor.getType().indicator()}:{changedTargetSourceSubIndex}",
|
||||
# f"{targetTrackTagKey}={targetTrackTagValue}"]
|
||||
#
|
||||
# else:
|
||||
#
|
||||
# # media diff {'tracks': {'changed': {4: {'tags': {'added': {'Yolo'}}}}}}
|
||||
# if DIFF_ADDED_KEY in changedTrackDiff[MediaDescriptor.TAGS_KEY]:
|
||||
# for addedTagKey in changedTrackDiff[MediaDescriptor.TAGS_KEY][DIFF_ADDED_KEY]:
|
||||
#
|
||||
# addedTagValue = targetTrackDescriptors[changedTargetTrackSourceIndex].getTags()[addedTagKey]
|
||||
#
|
||||
# # addedTagValue = changedTrackDiff[MediaDescriptor.TAGS_KEY][DIFF_ADDED_KEY][addedTagKey]
|
||||
#
|
||||
# # click.echo(f"addedTagValue={addedTagValue}")
|
||||
# # click.echo(f"sourceTrackDescriptors: subindex={[s.getSubIndex() for s in sourceTrackDescriptors]} sourceindex={[s.getSourceIndex() for s in sourceTrackDescriptors]} tags={[s.getTags() for s in sourceTrackDescriptors]}")
|
||||
# # click.echo(f"targetTrackDescriptors: subindex={[t.getSubIndex() for t in targetTrackDescriptors]} sourceindex={[t.getSourceIndex() for t in targetTrackDescriptors]} tags={[t.getTags() for t in targetTrackDescriptors]}")
|
||||
# # click.echo(f"changed track_index={changedTrackIndex} indicator={changedTargetTrackDescriptor.getType().indicator()} key={addedTagKey} value={addedTagValue} source_index={changedSourceTrackIndex}")
|
||||
#
|
||||
# metadataTokens += [f"-metadata:s:{changedTargetTrackDescriptor.getType().indicator()}:{changedTargetSourceSubIndex}",
|
||||
# f"{addedTagKey}={addedTagValue}"]
|
||||
#
|
||||
# # media diff {'tracks': {'changed': {4: {'tags': {'added': {'Yolo'}}}}}}
|
||||
# if DIFF_CHANGED_KEY in changedTrackDiff[MediaDescriptor.TAGS_KEY]:
|
||||
# for changedTagKey in changedTrackDiff[MediaDescriptor.TAGS_KEY][DIFF_CHANGED_KEY]:
|
||||
#
|
||||
# changedTagValue = targetTrackDescriptors[changedTargetTrackSourceIndex].getTags()[changedTagKey]
|
||||
# # sourceSubIndex = sourceTrackDescriptors[changedTargetTrackSourceIndex].getSubIndex()
|
||||
# # addedTagValue = changedTrackDiff[MediaDescriptor.TAGS_KEY][DIFF_ADDED_KEY][addedTagKey]
|
||||
#
|
||||
# # click.echo(f"addedTagValue={addedTagValue}")
|
||||
# # click.echo(f"sourceTrackDescriptors: subindex={[s.getSubIndex() for s in sourceTrackDescriptors]} sourceindex={[s.getSourceIndex() for s in sourceTrackDescriptors]} tags={[s.getTags() for s in sourceTrackDescriptors]}")
|
||||
# # click.echo(f"targetTrackDescriptors: subindex={[t.getSubIndex() for t in targetTrackDescriptors]} sourceindex={[t.getSourceIndex() for t in targetTrackDescriptors]} tags={[t.getTags() for t in targetTrackDescriptors]}")
|
||||
# # click.echo(f"changed track_index={changedTrackIndex} indicator={changedTargetTrackDescriptor.getType().indicator()} key={addedTagKey} value={addedTagValue} source_index={changedSourceTrackIndex}")
|
||||
#
|
||||
# metadataTokens += [f"-metadata:s:{changedTargetTrackDescriptor.getType().indicator()}:{changedTargetSourceSubIndex}",
|
||||
# f"{changedTagKey}={changedTagValue}"]
|
||||
#
|
||||
# # if TrackDescriptor.DISPOSITION_SET_KEY in changedTrackDiff.keys():
|
||||
#
|
||||
# # if DIFF_ADDED_KEY in changedTrackDiff[TrackDescriptor.DISPOSITION_SET_KEY]:
|
||||
# # for addedDisposition in changedTrackDiff[TrackDescriptor.DISPOSITION_SET_KEY][DIFF_ADDED_KEY]:
|
||||
# # # row = (f"changed {changedTargetTrackDescriptor.getType().label()} track index={changedTrackIndex} added disposition={addedDisposition.label()}",)
|
||||
# # # self.differencesTable.add_row(*map(str, row))
|
||||
# # pass
|
||||
#
|
||||
# # if DIFF_REMOVED_KEY in changedTrackDiff[TrackDescriptor.DISPOSITION_SET_KEY]:
|
||||
# # for removedDisposition in changedTrackDiff[TrackDescriptor.DISPOSITION_SET_KEY][DIFF_REMOVED_KEY]:
|
||||
# # # row = (f"changed {changedTargetTrackDescriptor.getType().label()} track index={changedTrackIndex} removed disposition={removedDisposition.label()}",)
|
||||
# # # self.differencesTable.add_row(*map(str, row))
|
||||
# # pass
|
||||
|
||||
# return metadataTokens
|
||||
|
||||
|
||||
|
||||
def generateMetadataTokens(self):
|
||||
"""Source media descriptor is mandatory"""
|
||||
|
||||
mappingTokens = []
|
||||
|
||||
# click.echo(f"source media descriptor: track indices={[d.getIndex() for d in sourceMediaDescriptor.getAllTrackDescriptors()]}")
|
||||
# click.echo(f"target media descriptor: track indices={[d.getIndex() for d in targetMediaDescriptor.getAllTrackDescriptors()]}")
|
||||
metadataTokens = []
|
||||
|
||||
# +jellyfin -jellyfin
|
||||
mediaDifferences = self.__targetMediaDescriptor.compare(self.__sourceMediaDescriptor)
|
||||
for tagKey, tagValue in self.__targetMediaDescriptor.getTags().items():
|
||||
metadataTokens += [f"-metadata:g",
|
||||
f"{tagKey}={tagValue}"]
|
||||
|
||||
# media diff {'tracks': {'changed': {4: {'tags': {'added': {'Yolo'}}}}}}
|
||||
#HINT: With current ffmpeg version track metadata tags are not passed to the outfile
|
||||
for td in self.__targetMediaDescriptor.getAllTrackDescriptors():
|
||||
|
||||
click.echo(f"media diff {mediaDifferences}")
|
||||
for tagKey, tagValue in td.getTags().items():
|
||||
|
||||
metadataTokens += [f"-metadata:s:{td.getType().indicator()}:{td.getSubIndex()}",
|
||||
f"{tagKey}={tagValue}"]
|
||||
|
||||
if MediaDescriptor.TAGS_KEY in mediaDifferences.keys():
|
||||
|
||||
sourceTags = self.__sourceMediaDescriptor.getTags()
|
||||
targetTags = self.__targetMediaDescriptor.getTags()
|
||||
|
||||
|
||||
if DIFF_REMOVED_KEY in mediaDifferences[MediaDescriptor.TAGS_KEY].keys():
|
||||
# for removedTagKey in mediaDifferences[MediaDescriptor.TAGS_KEY][DIFF_REMOVED_KEY]:
|
||||
# row = (f"removed media tag: key='{removedTagKey}' value='{sourceTags[removedTagKey]}'",)
|
||||
# self.differencesTable.add_row(*map(str, row))
|
||||
mappingTokens += [f"-map_metadata:g", "-1"]
|
||||
|
||||
for targetMediaTagKey in targetTags:
|
||||
mappingTokens += [f"-metadata:g", f"{targetMediaTagKey}={targetTags[targetMediaTagKey]}"]
|
||||
|
||||
else:
|
||||
|
||||
if DIFF_ADDED_KEY in mediaDifferences[MediaDescriptor.TAGS_KEY].keys():
|
||||
for addedTagKey in mediaDifferences[MediaDescriptor.TAGS_KEY][DIFF_ADDED_KEY]:
|
||||
# row = (f"added media tag: key='{addedTagKey}' value='{targetTags[addedTagKey]}'",)
|
||||
click.echo(f"added metadata key='{addedTagKey}' value='{targetTags[addedTagKey]}'->'{targetTags[addedTagKey]}'")
|
||||
# self.differencesTable.add_row(*map(str, row))
|
||||
#pass
|
||||
mappingTokens += [f"-metadata:g", f"{addedTagKey}={targetTags[addedTagKey]}"]
|
||||
|
||||
|
||||
|
||||
if DIFF_CHANGED_KEY in mediaDifferences[MediaDescriptor.TAGS_KEY].keys():
|
||||
for changedTagKey in mediaDifferences[MediaDescriptor.TAGS_KEY][DIFF_CHANGED_KEY]:
|
||||
#row = (f"changed media tag: key='{changedTagKey}' value='{sourceTags[changedTagKey]}'->'{targetTags[changedTagKey]}'",)
|
||||
click.echo(f"changed metadata key='{changedTagKey}' value='{sourceTags[changedTagKey]}'->'{targetTags[changedTagKey]}'")
|
||||
# self.differencesTable.add_row(*map(str, row))
|
||||
#pass
|
||||
mappingTokens += [f"-metadata:g", f"{changedTagKey}={targetTags[changedTagKey]}"]
|
||||
|
||||
if MediaDescriptor.TRACKS_KEY in mediaDifferences.keys():
|
||||
|
||||
sourceTrackDescriptors = self.__sourceMediaDescriptor.getAllTrackDescriptors()
|
||||
targetTrackDescriptors = self.__targetMediaDescriptor.getReorderedTrackDescriptors()
|
||||
|
||||
if DIFF_ADDED_KEY in mediaDifferences[MediaDescriptor.TRACKS_KEY].keys():
|
||||
addedTracksIndices = mediaDifferences[MediaDescriptor.TRACKS_KEY][DIFF_ADDED_KEY]
|
||||
raise click.ClickException(f"FfxController.generateMetadataTokens(): Adding tracks is not supported. Track indices {addedTracksIndices}")
|
||||
|
||||
#raise click.ClickException(f"add track {mediaDifferences[MediaDescriptor.TRACKS_KEY][DIFF_ADDED_KEY]}")
|
||||
#for addedTrackIndex in mediaDifferences[MediaDescriptor.TRACKS_KEY][DIFF_ADDED_KEY]:
|
||||
#addedTrack : Track = targetTrackDescriptors[addedTrackIndex]
|
||||
# row = (f"added {addedTrack.getType().label()} track: index={addedTrackIndex} lang={addedTrack.getLanguage().threeLetter()}",)
|
||||
# self.differencesTable.add_row(*map(str, row))
|
||||
|
||||
if DIFF_REMOVED_KEY in mediaDifferences[MediaDescriptor.TRACKS_KEY].keys():
|
||||
removedTracksIndices = mediaDifferences[MediaDescriptor.TRACKS_KEY][DIFF_ADDED_KEY].keys()
|
||||
raise click.ClickException(f"FfxController.generateMetadataTokens(): Removing tracks is not supported. Track indices {removedTracksIndices}")
|
||||
#for removedTrackIndex in mediaDifferences[MediaDescriptor.TRACKS_KEY][DIFF_REMOVED_KEY]:
|
||||
# row = (f"removed track: index={removedTrackIndex}",)
|
||||
# self.differencesTable.add_row(*map(str, row))
|
||||
|
||||
# media diff {'tracks': {'changed': {4: {'tags': {'added': {'Yolo'}}}}}}
|
||||
if DIFF_CHANGED_KEY in mediaDifferences[MediaDescriptor.TRACKS_KEY].keys():
|
||||
for changedTrackIndex in mediaDifferences[MediaDescriptor.TRACKS_KEY][DIFF_CHANGED_KEY].keys():
|
||||
|
||||
changedTargetTrackDescriptor : TrackDescriptor = targetTrackDescriptors[changedTrackIndex]
|
||||
changedTargetTrackSourceIndex = changedTargetTrackDescriptor.getSourceIndex()
|
||||
changedTargetSourceSubIndex = sourceTrackDescriptors[changedTargetTrackSourceIndex].getSubIndex()
|
||||
# changedSourceTrackDescriptor : TrackDescriptor = sourceTrackDescriptors[changedTargetTrackSourceIndex]
|
||||
# changedSourceTrackSubIndex = changedSourceTrackDescriptor.getSubIndex()
|
||||
|
||||
changedTrackDiff : dict = mediaDifferences[MediaDescriptor.TRACKS_KEY][DIFF_CHANGED_KEY][changedTrackIndex]
|
||||
|
||||
if MediaDescriptor.TAGS_KEY in changedTrackDiff.keys():
|
||||
|
||||
|
||||
if DIFF_REMOVED_KEY in changedTrackDiff[MediaDescriptor.TAGS_KEY]:
|
||||
#for removedTagKey in changedTrackDiff[MediaDescriptor.TAGS_KEY][DIFF_REMOVED_KEY]:
|
||||
# row = (f"changed {changedTargetTrackDescriptor.getType().label()} track index={changedTrackIndex} removed key={removedTagKey}",)
|
||||
# self.differencesTable.add_row(*map(str, row))
|
||||
|
||||
#addedTagValue = targetTrackDescriptors[changedTargetTrackSourceIndex].getTags()[addedTagKey]
|
||||
|
||||
mappingTokens += [f"-map_metadata:s:{changedTargetTrackDescriptor.getType().indicator()}:{changedTargetSourceSubIndex}", "-1"]
|
||||
|
||||
for targetTrackTagKey, targetTrackTagValue in changedTargetTrackDescriptor.getTags():
|
||||
mappingTokens += [f"-metadata:s:{changedTargetTrackDescriptor.getType().indicator()}:{changedTargetSourceSubIndex}",
|
||||
f"{targetTrackTagKey}={targetTrackTagValue}"]
|
||||
|
||||
else:
|
||||
|
||||
# media diff {'tracks': {'changed': {4: {'tags': {'added': {'Yolo'}}}}}}
|
||||
if DIFF_ADDED_KEY in changedTrackDiff[MediaDescriptor.TAGS_KEY]:
|
||||
for addedTagKey in changedTrackDiff[MediaDescriptor.TAGS_KEY][DIFF_ADDED_KEY]:
|
||||
|
||||
addedTagValue = targetTrackDescriptors[changedTargetTrackSourceIndex].getTags()[addedTagKey]
|
||||
|
||||
# addedTagValue = changedTrackDiff[MediaDescriptor.TAGS_KEY][DIFF_ADDED_KEY][addedTagKey]
|
||||
|
||||
# click.echo(f"addedTagValue={addedTagValue}")
|
||||
# click.echo(f"sourceTrackDescriptors: subindex={[s.getSubIndex() for s in sourceTrackDescriptors]} sourceindex={[s.getSourceIndex() for s in sourceTrackDescriptors]} tags={[s.getTags() for s in sourceTrackDescriptors]}")
|
||||
# click.echo(f"targetTrackDescriptors: subindex={[t.getSubIndex() for t in targetTrackDescriptors]} sourceindex={[t.getSourceIndex() for t in targetTrackDescriptors]} tags={[t.getTags() for t in targetTrackDescriptors]}")
|
||||
# click.echo(f"changed track_index={changedTrackIndex} indicator={changedTargetTrackDescriptor.getType().indicator()} key={addedTagKey} value={addedTagValue} source_index={changedSourceTrackIndex}")
|
||||
|
||||
mappingTokens += [f"-metadata:s:{changedTargetTrackDescriptor.getType().indicator()}:{changedTargetSourceSubIndex}",
|
||||
f"{addedTagKey}={addedTagValue}"]
|
||||
|
||||
# media diff {'tracks': {'changed': {4: {'tags': {'added': {'Yolo'}}}}}}
|
||||
if DIFF_CHANGED_KEY in changedTrackDiff[MediaDescriptor.TAGS_KEY]:
|
||||
for changedTagKey in changedTrackDiff[MediaDescriptor.TAGS_KEY][DIFF_CHANGED_KEY]:
|
||||
|
||||
changedTagValue = targetTrackDescriptors[changedTargetTrackSourceIndex].getTags()[changedTagKey]
|
||||
# sourceSubIndex = sourceTrackDescriptors[changedTargetTrackSourceIndex].getSubIndex()
|
||||
# addedTagValue = changedTrackDiff[MediaDescriptor.TAGS_KEY][DIFF_ADDED_KEY][addedTagKey]
|
||||
|
||||
# click.echo(f"addedTagValue={addedTagValue}")
|
||||
# click.echo(f"sourceTrackDescriptors: subindex={[s.getSubIndex() for s in sourceTrackDescriptors]} sourceindex={[s.getSourceIndex() for s in sourceTrackDescriptors]} tags={[s.getTags() for s in sourceTrackDescriptors]}")
|
||||
# click.echo(f"targetTrackDescriptors: subindex={[t.getSubIndex() for t in targetTrackDescriptors]} sourceindex={[t.getSourceIndex() for t in targetTrackDescriptors]} tags={[t.getTags() for t in targetTrackDescriptors]}")
|
||||
# click.echo(f"changed track_index={changedTrackIndex} indicator={changedTargetTrackDescriptor.getType().indicator()} key={addedTagKey} value={addedTagValue} source_index={changedSourceTrackIndex}")
|
||||
|
||||
mappingTokens += [f"-metadata:s:{changedTargetTrackDescriptor.getType().indicator()}:{changedTargetSourceSubIndex}",
|
||||
f"{changedTagKey}={changedTagValue}"]
|
||||
|
||||
# if TrackDescriptor.DISPOSITION_SET_KEY in changedTrackDiff.keys():
|
||||
|
||||
# if DIFF_ADDED_KEY in changedTrackDiff[TrackDescriptor.DISPOSITION_SET_KEY]:
|
||||
# for addedDisposition in changedTrackDiff[TrackDescriptor.DISPOSITION_SET_KEY][DIFF_ADDED_KEY]:
|
||||
# # row = (f"changed {changedTargetTrackDescriptor.getType().label()} track index={changedTrackIndex} added disposition={addedDisposition.label()}",)
|
||||
# # self.differencesTable.add_row(*map(str, row))
|
||||
# pass
|
||||
|
||||
# if DIFF_REMOVED_KEY in changedTrackDiff[TrackDescriptor.DISPOSITION_SET_KEY]:
|
||||
# for removedDisposition in changedTrackDiff[TrackDescriptor.DISPOSITION_SET_KEY][DIFF_REMOVED_KEY]:
|
||||
# # row = (f"changed {changedTargetTrackDescriptor.getType().label()} track index={changedTrackIndex} removed disposition={removedDisposition.label()}",)
|
||||
# # self.differencesTable.add_row(*map(str, row))
|
||||
# pass
|
||||
|
||||
return mappingTokens
|
||||
return metadataTokens
|
||||
|
||||
|
||||
def runJob(self,
|
||||
@@ -390,11 +413,11 @@ class FfxController():
|
||||
if videoEncoder == VideoEncoder.VP9:
|
||||
|
||||
commandSequence1 = (commandTokens
|
||||
+ self.__targetMediaDescriptor.getInputMappingTokens()
|
||||
+ self.__targetMediaDescriptor.getInputMappingTokens(only_video=True)
|
||||
+ self.generateVP9Pass1Tokens(int(quality)))
|
||||
|
||||
if self.__context['perform_crop']:
|
||||
commandSequence1 += FfxController.generateCropTokens()
|
||||
commandSequence1 += self.generateCropTokens()
|
||||
|
||||
commandSequence1 += FfxController.NULL_TOKENS
|
||||
|
||||
@@ -420,7 +443,7 @@ class FfxController():
|
||||
commandSequence2 += self.generateVP9Pass2Tokens(int(quality)) + self.generateAudioEncodingTokens()
|
||||
|
||||
if self.__context['perform_crop']:
|
||||
commandSequence2 += FfxController.generateCropTokens()
|
||||
commandSequence2 += self.generateCropTokens()
|
||||
|
||||
commandSequence2 += self.generateOutputTokens(targetPath,
|
||||
FfxController.DEFAULT_FILE_FORMAT,
|
||||
@@ -429,4 +452,7 @@ class FfxController():
|
||||
click.echo(f"Command 2: {' '.join(commandSequence2)}")
|
||||
|
||||
if not self.__context['dry_run']:
|
||||
executeProcess(commandSequence2)
|
||||
out, err, rc = executeProcess(commandSequence2)
|
||||
if rc:
|
||||
raise click.ClickException(f"Command resulted in error: rc={rc} error={err}")
|
||||
|
||||
|
||||
@@ -186,6 +186,13 @@ class FileProperties():
|
||||
return int(self.__episode)
|
||||
|
||||
|
||||
def getFilename(self):
|
||||
return self.__sourceFilename
|
||||
|
||||
def getFileBasename(self):
|
||||
return self.__sourceFileBasename
|
||||
|
||||
|
||||
def assembleTargetFileBasename(self,
|
||||
label: str = "",
|
||||
quality: int = -1,
|
||||
|
||||
@@ -61,15 +61,6 @@ class MediaDescriptor:
|
||||
else:
|
||||
self.__trackDescriptors = []
|
||||
|
||||
if MediaDescriptor.CLEAR_TAGS_FLAG_KEY in kwargs.keys():
|
||||
if type(kwargs[MediaDescriptor.CLEAR_TAGS_FLAG_KEY]) is not bool:
|
||||
raise TypeError(
|
||||
f"MediaDescriptor.__init__(): Argument {MediaDescriptor.CLEAR_TAGS_FLAG_KEY} is required to be of type bool"
|
||||
)
|
||||
self.__clearTags = kwargs[MediaDescriptor.CLEAR_TAGS_FLAG_KEY]
|
||||
else:
|
||||
self.__clearTags = False
|
||||
|
||||
if MediaDescriptor.JELLYFIN_ORDER_FLAG_KEY in kwargs.keys():
|
||||
if type(kwargs[MediaDescriptor.JELLYFIN_ORDER_FLAG_KEY]) is not bool:
|
||||
raise TypeError(
|
||||
@@ -79,77 +70,6 @@ class MediaDescriptor:
|
||||
else:
|
||||
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():
|
||||
@@ -165,56 +85,66 @@ class MediaDescriptor:
|
||||
TrackDisposition.FORCED, t.getSubIndex() == int(subIndex)
|
||||
)
|
||||
|
||||
def checkDefaultAndForcedDispositions(self):
|
||||
try:
|
||||
self.getDefaultVideoTrack()
|
||||
self.getForcedVideoTrack()
|
||||
self.getDefaultAudioTrack()
|
||||
self.getForcedAudioTrack()
|
||||
self.getDefaultSubtitleTrack()
|
||||
self.getForcedSubtitleTrack()
|
||||
return True
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
def getReorderedTrackDescriptors(self):
|
||||
def checkConfiguration(self):
|
||||
|
||||
videoTracks = self.sortSubIndices(self.getVideoTracks())
|
||||
audioTracks = self.sortSubIndices(self.getAudioTracks())
|
||||
subtitleTracks = self.sortSubIndices(self.getSubtitleTracks())
|
||||
videoTracks = self.getVideoTracks()
|
||||
audioTracks = self.getAudioTracks()
|
||||
subtitleTracks = self.getSubtitleTracks()
|
||||
|
||||
if len([v for v in videoTracks if v.getDispositionFlag(TrackDisposition.DEFAULT)]) > 1:
|
||||
raise ValueError('More than one default video track')
|
||||
if len([a for a in audioTracks if a.getDispositionFlag(TrackDisposition.DEFAULT)]) > 1:
|
||||
raise ValueError('More than one default audio track')
|
||||
if len([s for s in subtitleTracks if s.getDispositionFlag(TrackDisposition.DEFAULT)]) > 1:
|
||||
raise ValueError('More than one default subtitle track')
|
||||
|
||||
videoDefaultTrack = self.getDefaultVideoTrack()
|
||||
self.getForcedVideoTrack()
|
||||
audioDefaultTrack = self.getDefaultAudioTrack()
|
||||
self.getForcedAudioTrack()
|
||||
subtitleDefaultTrack = self.getDefaultSubtitleTrack()
|
||||
self.getForcedSubtitleTrack()
|
||||
if len([v for v in videoTracks if v.getDispositionFlag(TrackDisposition.FORCED)]) > 1:
|
||||
raise ValueError('More than one forced video track')
|
||||
if len([a for a in audioTracks if a.getDispositionFlag(TrackDisposition.FORCED)]) > 1:
|
||||
raise ValueError('More than one forced audio track')
|
||||
if len([s for s in subtitleTracks if s.getDispositionFlag(TrackDisposition.FORCED)]) > 1:
|
||||
raise ValueError('More than one forced subtitle track')
|
||||
|
||||
if self.__jellyfinOrder:
|
||||
if not videoDefaultTrack is None:
|
||||
videoTracks.append(
|
||||
videoTracks.pop(videoTracks.index(videoDefaultTrack))
|
||||
)
|
||||
if not audioDefaultTrack is None:
|
||||
audioTracks.append(
|
||||
audioTracks.pop(audioTracks.index(audioDefaultTrack))
|
||||
)
|
||||
if not subtitleDefaultTrack is None:
|
||||
subtitleTracks.append(
|
||||
subtitleTracks.pop(subtitleTracks.index(subtitleDefaultTrack))
|
||||
)
|
||||
|
||||
reorderedTrackDescriptors = videoTracks + audioTracks + subtitleTracks
|
||||
orderedSourceTrackSequence = [
|
||||
t.getSourceIndex() for t in reorderedTrackDescriptors
|
||||
trackDescriptors = videoTracks + audioTracks + subtitleTracks
|
||||
sourceIndices = [
|
||||
t.getSourceIndex() for t in trackDescriptors
|
||||
]
|
||||
if len(set(sourceIndices)) < len(trackDescriptors):
|
||||
raise ValueError('Multiple streams originating from the same source stream')
|
||||
|
||||
if len(set(orderedSourceTrackSequence)) < len(orderedSourceTrackSequence):
|
||||
raise ValueError(
|
||||
f"Multiple streams originating from the same source stream not supported"
|
||||
)
|
||||
|
||||
return reorderedTrackDescriptors
|
||||
def applyJellyfinOrder(self):
|
||||
"""Reorder subtracks in types with default the last, then make subindices flat again"""
|
||||
|
||||
# videoTracks = self.sortSubIndices(self.getVideoTracks())
|
||||
# audioTracks = self.sortSubIndices(self.getAudioTracks())
|
||||
# subtitleTracks = self.sortSubIndices(self.getSubtitleTracks())
|
||||
|
||||
self.checkConfiguration()
|
||||
|
||||
# from self.__trackDescriptors
|
||||
videoTracks = self.getVideoTracks()
|
||||
audioTracks = self.getAudioTracks()
|
||||
subtitleTracks = self.getSubtitleTracks()
|
||||
|
||||
defaultVideoTracks = [v for v in videoTracks if v.getDispositionFlag(TrackDisposition.DEFAULT)]
|
||||
defaultAudioTracks = [a for a in audioTracks if a.getDispositionFlag(TrackDisposition.DEFAULT)]
|
||||
defaultSubtitleTracks = [s for s in subtitleTracks if s.getDispositionFlag(TrackDisposition.DEFAULT)]
|
||||
|
||||
if defaultVideoTracks:
|
||||
videoTracks.append(videoTracks.pop(videoTracks.index(defaultVideoTracks[0])))
|
||||
self.sortSubIndices(videoTracks)
|
||||
if defaultAudioTracks:
|
||||
audioTracks.append(audioTracks.pop(audioTracks.index(defaultAudioTracks[0])))
|
||||
self.sortSubIndices(audioTracks)
|
||||
if defaultSubtitleTracks:
|
||||
subtitleTracks.append(subtitleTracks.pop(subtitleTracks.index(defaultSubtitleTracks[0])))
|
||||
self.sortSubIndices(subtitleTracks)
|
||||
|
||||
self.__trackDescriptors = videoTracks + audioTracks + subtitleTracks
|
||||
self.sortIndices(self.__trackDescriptors)
|
||||
|
||||
|
||||
@classmethod
|
||||
def fromFfprobe(cls, formatData, streamData):
|
||||
@@ -253,43 +183,46 @@ class MediaDescriptor:
|
||||
def getTags(self):
|
||||
return self.__mediaTags
|
||||
|
||||
|
||||
def sortSubIndices(
|
||||
self, descriptors: List[TrackDescriptor]
|
||||
) -> List[TrackDescriptor]:
|
||||
subIndex = 0
|
||||
for t in descriptors:
|
||||
t.setSubIndex(subIndex)
|
||||
for d in descriptors:
|
||||
d.setSubIndex(subIndex)
|
||||
subIndex += 1
|
||||
return descriptors
|
||||
|
||||
def sortIndices(
|
||||
self, descriptors: List[TrackDescriptor]
|
||||
) -> List[TrackDescriptor]:
|
||||
index = 0
|
||||
for d in descriptors:
|
||||
d.setIndex(index)
|
||||
index += 1
|
||||
return descriptors
|
||||
|
||||
|
||||
def getAllTrackDescriptors(self) -> List[TrackDescriptor]:
|
||||
return self.getVideoTracks() + self.getAudioTracks() + self.getSubtitleTracks()
|
||||
|
||||
def getVideoTracks(self) -> List[TrackDescriptor]:
|
||||
return [
|
||||
v for v in self.__trackDescriptors.copy() if v.getType() == TrackType.VIDEO
|
||||
v for v in self.__trackDescriptors if v.getType() == TrackType.VIDEO
|
||||
]
|
||||
|
||||
def getAudioTracks(self) -> List[TrackDescriptor]:
|
||||
return [
|
||||
a for a in self.__trackDescriptors.copy() if a.getType() == TrackType.AUDIO
|
||||
a for a in self.__trackDescriptors if a.getType() == TrackType.AUDIO
|
||||
]
|
||||
|
||||
def getSubtitleTracks(self) -> List[TrackDescriptor]:
|
||||
return [
|
||||
s
|
||||
for s in self.__trackDescriptors.copy()
|
||||
for s in self.__trackDescriptors
|
||||
if s.getType() == TrackType.SUBTITLE
|
||||
]
|
||||
|
||||
def getJellyfin(self):
|
||||
return self.__jellyfinOrder
|
||||
|
||||
def setJellyfinOrder(self, state):
|
||||
self.__jellyfinOrder = state
|
||||
|
||||
def getClearTags(self):
|
||||
return self.__clearTags
|
||||
|
||||
def compare(self, vsMediaDescriptor: Self):
|
||||
|
||||
@@ -300,6 +233,10 @@ class MediaDescriptor:
|
||||
|
||||
vsTags = vsMediaDescriptor.getTags()
|
||||
tags = self.getTags()
|
||||
|
||||
# tags ist leer
|
||||
# click.echo(f"tags={tags} vsTags={vsTags}")
|
||||
# raise click.Abort
|
||||
|
||||
# HINT: Some tags differ per file, for example creation_time, so these are removed before diff
|
||||
for emt in MediaDescriptor.EXCLUDED_MEDIA_TAGS:
|
||||
@@ -317,7 +254,7 @@ class MediaDescriptor:
|
||||
|
||||
# Target track configuration (from DB)
|
||||
# tracks = self.getAllTrackDescriptors()
|
||||
tracks = self.getReorderedTrackDescriptors()
|
||||
tracks = self.getAllTrackDescriptors() # filtern
|
||||
numTracks = len(tracks)
|
||||
|
||||
# Current track configuration (of file)
|
||||
@@ -371,14 +308,15 @@ class MediaDescriptor:
|
||||
|
||||
def getImportFileTokens(self, use_sub_index: bool = True):
|
||||
|
||||
reorderedTrackDescriptors = self.getReorderedTrackDescriptors()
|
||||
# reorderedTrackDescriptors = self.getReorderedTrackDescriptors()
|
||||
importFileTokens = []
|
||||
|
||||
for rtd in reorderedTrackDescriptors:
|
||||
#for rtd in reorderedTrackDescriptors:
|
||||
for td in self.__trackDescriptors:
|
||||
|
||||
importedFilePath = rtd.getExternalSourceFilePath()
|
||||
importedFilePath = td.getExternalSourceFilePath()
|
||||
|
||||
if not importedFilePath is None:
|
||||
if importedFilePath:
|
||||
importFileTokens += [
|
||||
"-i",
|
||||
importedFilePath,
|
||||
@@ -386,39 +324,51 @@ class MediaDescriptor:
|
||||
|
||||
return importFileTokens
|
||||
|
||||
def getInputMappingTokens(self, use_sub_index: bool = True):
|
||||
|
||||
reorderedTrackDescriptors = self.getReorderedTrackDescriptors()
|
||||
def getInputMappingTokens(self, use_sub_index: bool = True, only_video: bool = False):
|
||||
|
||||
# reorderedTrackDescriptors = self.getReorderedTrackDescriptors()
|
||||
inputMappingTokens = []
|
||||
|
||||
filePointer = 1
|
||||
for rtd in reorderedTrackDescriptors:
|
||||
#for rtd in reorderedTrackDescriptors:
|
||||
for td in self.__trackDescriptors:
|
||||
|
||||
importedFilePath = rtd.getExternalSourceFilePath()
|
||||
trackType = td.getType()
|
||||
|
||||
if (trackType == TrackType.VIDEO or not only_video):
|
||||
|
||||
importedFilePath = td.getExternalSourceFilePath()
|
||||
|
||||
if use_sub_index:
|
||||
|
||||
if importedFilePath:
|
||||
|
||||
inputMappingTokens += [
|
||||
"-map",
|
||||
f"{filePointer}:{trackType.indicator()}:0",
|
||||
]
|
||||
filePointer += 1
|
||||
|
||||
else:
|
||||
|
||||
if td.getCodec() != TrackDescriptor.CODEC_PGS:
|
||||
inputMappingTokens += [
|
||||
"-map",
|
||||
f"0:{trackType.indicator()}:{td.getSubIndex()}",
|
||||
]
|
||||
|
||||
trackType = rtd.getType()
|
||||
if use_sub_index:
|
||||
if importedFilePath is None:
|
||||
inputMappingTokens += [
|
||||
"-map",
|
||||
f"0:{trackType.indicator()}:{rtd.getSubIndex()}",
|
||||
]
|
||||
else:
|
||||
inputMappingTokens += [
|
||||
"-map",
|
||||
f"{filePointer}:{trackType.indicator()}:0",
|
||||
]
|
||||
filePointer += 1
|
||||
else:
|
||||
inputMappingTokens += ["-map", f"0:{rtd.getIndex()}"]
|
||||
if td.getCodec() != TrackDescriptor.CODEC_PGS:
|
||||
inputMappingTokens += ["-map", f"0:{td.getIndex()}"]
|
||||
|
||||
return inputMappingTokens
|
||||
|
||||
def searchSubtitleFiles(searchDirectory, prefix):
|
||||
def searchSubtitleFiles(self, searchDirectory, prefix):
|
||||
|
||||
sesl_match = re.compile(MediaDescriptor.SEASON_EPISODE_STREAM_LANGUAGE_MATCH)
|
||||
|
||||
availableFileSubtitleDescriptors = []
|
||||
subtitleFileDescriptors = []
|
||||
for subtitleFilename in os.listdir(searchDirectory):
|
||||
if subtitleFilename.startswith(prefix) and subtitleFilename.endswith(
|
||||
"." + MediaDescriptor.SUBTITLE_FILE_EXTENSION
|
||||
@@ -432,48 +382,48 @@ class MediaDescriptor:
|
||||
subtitleFileDescriptor["path"] = subtitleFilePath
|
||||
subtitleFileDescriptor["season"] = int(sesl_result.group(1))
|
||||
subtitleFileDescriptor["episode"] = int(sesl_result.group(2))
|
||||
subtitleFileDescriptor["stream"] = int(sesl_result.group(3))
|
||||
subtitleFileDescriptor["index"] = int(sesl_result.group(3))
|
||||
subtitleFileDescriptor["language"] = sesl_result.group(4)
|
||||
|
||||
availableFileSubtitleDescriptors.append(subtitleFileDescriptor)
|
||||
subtitleFileDescriptors.append(subtitleFileDescriptor)
|
||||
|
||||
click.echo(
|
||||
f"Found {len(availableFileSubtitleDescriptors)} subtitles in files\n"
|
||||
)
|
||||
click.echo(f"Available subtitle files {subtitleFileDescriptors}\n")
|
||||
|
||||
return availableFileSubtitleDescriptors
|
||||
return subtitleFileDescriptors
|
||||
|
||||
def importSubtitles(
|
||||
self, searchDirectory, prefix, season: int = -1, episode: int = -1
|
||||
):
|
||||
|
||||
availableFileSubtitleDescriptors = self.searchSubtitleFiles(
|
||||
searchDirectory, prefix
|
||||
)
|
||||
def importSubtitles(self, searchDirectory, prefix, season: int = -1, episode: int = -1):
|
||||
|
||||
click.echo(f"Season: {season} Episode: {episode}")
|
||||
|
||||
availableFileSubtitleDescriptors = self.searchSubtitleFiles(searchDirectory, prefix)
|
||||
|
||||
click.echo(f"availableFileSubtitleDescriptors: {availableFileSubtitleDescriptors}")
|
||||
|
||||
subtitleTracks = self.getSubtitleTracks()
|
||||
|
||||
click.echo(f"subtitleTracks: {[s.getIndex() for s in subtitleTracks]}")
|
||||
|
||||
# if len(availableFileSubtitleDescriptors) != len(subtitleTracks):
|
||||
# raise click.ClickException(f"MediaDescriptor.importSubtitles(): Number if subtitle files not matching number of subtitle tracks")
|
||||
|
||||
matchingFileSubtitleDescriptors = (
|
||||
matchingSubtitleFileDescriptors = (
|
||||
sorted(
|
||||
[
|
||||
d
|
||||
for d in availableFileSubtitleDescriptors
|
||||
if d["season"] == int(season) and d["episode"] == int(episode)
|
||||
],
|
||||
key=lambda d: d["stream"],
|
||||
key=lambda d: d["index"],
|
||||
)
|
||||
if availableFileSubtitleDescriptors
|
||||
else []
|
||||
)
|
||||
|
||||
for mfsd in matchingFileSubtitleDescriptors:
|
||||
matchingSubtitleTrackDescriptor = [
|
||||
s for s in subtitleTracks if s.getIndex() == mfsd["stream"]
|
||||
]
|
||||
click.echo(f"matchingSubtitleFileDescriptors: {matchingSubtitleFileDescriptors}")
|
||||
|
||||
for msfd in matchingSubtitleFileDescriptors:
|
||||
matchingSubtitleTrackDescriptor = [s for s in subtitleTracks if s.getIndex() == msfd["index"]]
|
||||
if matchingSubtitleTrackDescriptor:
|
||||
matchingSubtitleTrackDescriptor[0].setExternalSourceFilePath(
|
||||
mfsd["path"]
|
||||
)
|
||||
click.echo(f"Found matching subtitle file {msfd["path"]}\n")
|
||||
matchingSubtitleTrackDescriptor[0].setExternalSourceFilePath(msfd["path"])
|
||||
|
||||
@@ -156,6 +156,7 @@ class MediaDetailsScreen(Screen):
|
||||
#HINT: This is None if the filename did not match anything in database
|
||||
self.__currentPattern = self.__mediaFileProperties.getPattern()
|
||||
|
||||
# keine tags vorhanden
|
||||
self.__targetMediaDescriptor = self.__currentPattern.getMediaDescriptor() if self.__currentPattern is not None else None
|
||||
|
||||
# Enumerating differences between media descriptors
|
||||
@@ -506,13 +507,18 @@ class MediaDetailsScreen(Screen):
|
||||
|
||||
self.highlightPattern(False)
|
||||
|
||||
for tagKey, tagValue in self.__currentMediaDescriptor.getTags().items():
|
||||
self.__tac.updateMediaTag(patternId, tagKey, tagValue)
|
||||
|
||||
for trackDescriptor in self.__currentMediaDescriptor.getAllTrackDescriptors():
|
||||
self.__tc.addTrack(trackDescriptor, patternId = patternId)
|
||||
|
||||
|
||||
def action_new_pattern(self):
|
||||
|
||||
if not self.__currentMediaDescriptor.checkDefaultAndForcedDispositions():
|
||||
try:
|
||||
self.__currentMediaDescriptor.checkConfiguration()
|
||||
except ValueError:
|
||||
return
|
||||
|
||||
selectedShowDescriptor = self.getSelectedShowDescriptor()
|
||||
|
||||
@@ -62,7 +62,13 @@ class Pattern(Base):
|
||||
|
||||
kwargs[MediaDescriptor.TRACK_DESCRIPTOR_LIST_KEY] = []
|
||||
|
||||
# Set ordered subindices
|
||||
subIndexCounter = {}
|
||||
for track in self.tracks:
|
||||
kwargs[MediaDescriptor.TRACK_DESCRIPTOR_LIST_KEY].append(track.getDescriptor())
|
||||
trackType = track.getType()
|
||||
if not trackType in subIndexCounter.keys():
|
||||
subIndexCounter[trackType] = 0
|
||||
kwargs[MediaDescriptor.TRACK_DESCRIPTOR_LIST_KEY].append(track.getDescriptor(subIndex = subIndexCounter[trackType]))
|
||||
subIndexCounter[trackType] += 1
|
||||
|
||||
return MediaDescriptor(**kwargs)
|
||||
|
||||
@@ -44,6 +44,7 @@ class Track(Base):
|
||||
|
||||
disposition_flags = Column(Integer)
|
||||
|
||||
codec_name = Column(String)
|
||||
audio_layout = Column(Integer)
|
||||
|
||||
|
||||
@@ -133,6 +134,7 @@ class Track(Base):
|
||||
|
||||
return cls(pattern_id = patternId,
|
||||
track_type = trackType,
|
||||
codec_name = streamObj[TrackDescriptor.FFPROBE_CODEC_NAME_KEY],
|
||||
disposition_flags = sum([2**t.index() for (k,v) in streamObj[TrackDescriptor.FFPROBE_DISPOSITION_KEY].items()
|
||||
if v and (t := TrackDisposition.find(k)) is not None]),
|
||||
audio_layout = AudioLayout.identify(streamObj))
|
||||
@@ -149,6 +151,9 @@ class Track(Base):
|
||||
|
||||
def getType(self):
|
||||
return TrackType.fromIndex(self.track_type)
|
||||
|
||||
def getCodec(self):
|
||||
return str(self.codec_name)
|
||||
|
||||
def getIndex(self):
|
||||
return int(self.index) if self.index is not None else -1
|
||||
@@ -198,6 +203,8 @@ class Track(Base):
|
||||
kwargs[TrackDescriptor.SUB_INDEX_KEY] = subIndex
|
||||
|
||||
kwargs[TrackDescriptor.TRACK_TYPE_KEY] = self.getType()
|
||||
kwargs[TrackDescriptor.CODEC_NAME_KEY] = self.getCodec()
|
||||
|
||||
kwargs[TrackDescriptor.DISPOSITION_SET_KEY] = self.getDispositionSet()
|
||||
kwargs[TrackDescriptor.TAGS_KEY] = self.getTags()
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import subprocess
|
||||
from typing import List
|
||||
|
||||
def executeProcess(commandSequence):
|
||||
process = subprocess.Popen(commandSequence, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
def executeProcess(commandSequence: List[str]):
|
||||
# process = subprocess.Popen([t.encode('utf-8') for t in commandSequence], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
process = subprocess.Popen(commandSequence, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf-8')
|
||||
output, error = process.communicate()
|
||||
return output.decode('utf-8'), error.decode('utf-8'), process.returncode
|
||||
# return output.decode('utf-8'), error.decode('utf-8'), process.returncode
|
||||
return output, error, process.returncode
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import os, click, requests
|
||||
import os, click, requests, json
|
||||
|
||||
|
||||
class TmdbController():
|
||||
@@ -57,6 +57,17 @@ class TmdbController():
|
||||
|
||||
#TODO Check for result
|
||||
try:
|
||||
#TODO: Content Type aware processing
|
||||
# response = requests.get(tmdbUrl)
|
||||
# response.encoding = 'utf-8'
|
||||
# return response.json()
|
||||
# response = requests.get(tmdbUrl)
|
||||
|
||||
# contentType = response.headers.get('Content-Type')
|
||||
# print(content_type) # Example: 'application/json; charset=UTF-8'
|
||||
|
||||
# decoded_content = response.content.decode('utf-8')
|
||||
# return json.loads(decoded_content)
|
||||
return requests.get(tmdbUrl).json()
|
||||
except:
|
||||
return {}
|
||||
|
||||
@@ -30,6 +30,7 @@ class TrackController():
|
||||
s = self.Session()
|
||||
track = Track(pattern_id = patId,
|
||||
track_type = int(trackDescriptor.getType().index()),
|
||||
codec_name = str(trackDescriptor.getCodec()),
|
||||
index = int(trackDescriptor.getIndex()),
|
||||
source_index = int(trackDescriptor.getSourceIndex()),
|
||||
disposition_flags = int(TrackDisposition.toFlags(trackDescriptor.getDispositionSet())),
|
||||
@@ -66,6 +67,7 @@ class TrackController():
|
||||
track : Track = q.first()
|
||||
|
||||
track.track_type = int(trackDescriptor.getType().index())
|
||||
track.codec_name = str(trackDescriptor.getCodec())
|
||||
track.audio_layout = int(trackDescriptor.getAudioLayout().index())
|
||||
|
||||
track.disposition_flags = int(TrackDisposition.toFlags(trackDescriptor.getDispositionSet()))
|
||||
|
||||
@@ -21,12 +21,16 @@ class TrackDescriptor:
|
||||
TAGS_KEY = "tags"
|
||||
|
||||
TRACK_TYPE_KEY = "track_type"
|
||||
CODEC_NAME_KEY = "codec_name"
|
||||
AUDIO_LAYOUT_KEY = "audio_layout"
|
||||
|
||||
FFPROBE_INDEX_KEY = "index"
|
||||
FFPROBE_DISPOSITION_KEY = "disposition"
|
||||
FFPROBE_TAGS_KEY = "tags"
|
||||
FFPROBE_CODEC_TYPE_KEY = "codec_type"
|
||||
FFPROBE_CODEC_NAME_KEY = "codec_name"
|
||||
|
||||
CODEC_PGS = 'hdmv_pgs_subtitle'
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
|
||||
@@ -55,7 +59,7 @@ class TrackDescriptor:
|
||||
)
|
||||
self.__externalSourceFilePath = kwargs[TrackDescriptor.EXTERNAL_SOURCE_FILE_PATH_KEY]
|
||||
else:
|
||||
self.__externalSourceFilePath = None
|
||||
self.__externalSourceFilePath = ''
|
||||
|
||||
if TrackDescriptor.INDEX_KEY in kwargs.keys():
|
||||
if type(kwargs[TrackDescriptor.INDEX_KEY]) is not int:
|
||||
@@ -92,6 +96,15 @@ class TrackDescriptor:
|
||||
else:
|
||||
self.__trackType = TrackType.UNKNOWN
|
||||
|
||||
if TrackDescriptor.CODEC_NAME_KEY in kwargs.keys():
|
||||
if type(kwargs[TrackDescriptor.CODEC_NAME_KEY]) is not str:
|
||||
raise TypeError(
|
||||
f"TrackDesciptor.__init__(): Argument {TrackDescriptor.CODEC_NAME_KEY} is required to be of type str"
|
||||
)
|
||||
self.__codecName = kwargs[TrackDescriptor.CODEC_NAME_KEY]
|
||||
else:
|
||||
self.__codecName = ''
|
||||
|
||||
if TrackDescriptor.TAGS_KEY in kwargs.keys():
|
||||
if type(kwargs[TrackDescriptor.TAGS_KEY]) is not dict:
|
||||
raise TypeError(
|
||||
@@ -187,6 +200,8 @@ class TrackDescriptor:
|
||||
kwargs[TrackDescriptor.SUB_INDEX_KEY] = subIndex
|
||||
|
||||
kwargs[TrackDescriptor.TRACK_TYPE_KEY] = trackType
|
||||
kwargs[TrackDescriptor.CODEC_NAME_KEY] = str(streamObj[TrackDescriptor.FFPROBE_CODEC_NAME_KEY])
|
||||
|
||||
kwargs[TrackDescriptor.DISPOSITION_SET_KEY] = (
|
||||
{
|
||||
t
|
||||
@@ -226,6 +241,9 @@ class TrackDescriptor:
|
||||
def getIndex(self):
|
||||
return self.__index
|
||||
|
||||
def setIndex(self, index):
|
||||
self.__index = index
|
||||
|
||||
def getSourceIndex(self):
|
||||
return self.__sourceIndex
|
||||
|
||||
@@ -237,6 +255,9 @@ class TrackDescriptor:
|
||||
|
||||
def getType(self):
|
||||
return self.__trackType
|
||||
|
||||
def getCodec(self):
|
||||
return self.__codecName
|
||||
|
||||
def getLanguage(self):
|
||||
if "language" in self.__trackTags.keys():
|
||||
|
||||
Reference in New Issue
Block a user