reworking target media decriptor track handling
This commit is contained in:
78
bin/ffx.py
78
bin/ffx.py
@@ -13,6 +13,7 @@ from ffx.database import databaseContext
|
||||
|
||||
from ffx.track_type import TrackType
|
||||
from ffx.video_encoder import VideoEncoder
|
||||
from ffx.track_disposition import TrackDisposition
|
||||
|
||||
|
||||
VERSION='0.1.2'
|
||||
@@ -160,6 +161,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)
|
||||
|
||||
@@ -191,6 +193,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
|
||||
|
||||
@@ -207,8 +210,9 @@ def convert(ctx,
|
||||
|
||||
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']:
|
||||
@@ -242,7 +246,7 @@ def convert(ctx,
|
||||
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 +269,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 +283,36 @@ 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'])
|
||||
|
||||
fc = FfxController(context, sourceMediaDescriptor)
|
||||
|
||||
# mappingTokens = fc.generateMetadataTokens()
|
||||
# click.echo(f"Metadata Tokens: {mappingTokens}")
|
||||
|
||||
|
||||
dispositionTokens = fc.generateDispositionTokens()
|
||||
click.echo(f"Disposition Tokens: {dispositionTokens}")
|
||||
|
||||
@@ -329,18 +324,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}")
|
||||
|
||||
#print(type(tmdbEpisodeResult['name']))
|
||||
#quit()
|
||||
|
||||
if tmdbEpisodeResult:
|
||||
fileBasename = tc.getEpisodeFileBasename(currentShowDescriptor.getFilenamePrefix(),
|
||||
tmdbEpisodeResult['name'],
|
||||
@@ -349,9 +340,7 @@ def convert(ctx,
|
||||
currentShowDescriptor.getIndexSeasonDigits(),
|
||||
currentShowDescriptor.getIndexEpisodeDigits(),
|
||||
currentShowDescriptor.getIndicatorSeasonDigits(),
|
||||
currentShowDescriptor.getIndicatorEpisodeDigits())
|
||||
|
||||
|
||||
currentShowDescriptor.getIndicatorEpisodeDigits())
|
||||
else:
|
||||
fileBasename = currentShowDescriptor.getFilenamePrefix()
|
||||
|
||||
@@ -359,8 +348,16 @@ def convert(ctx,
|
||||
|
||||
if context['import_subtitles']:
|
||||
targetMediaDescriptor.importSubtitles(context['subtitle_directory'], context['subtitle_prefix'])
|
||||
|
||||
# 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()]}")
|
||||
|
||||
targetMediaDescriptor.setJellyfinOrder(context['jellyfin'])
|
||||
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()}")
|
||||
|
||||
@@ -385,9 +382,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)
|
||||
|
||||
@@ -398,7 +396,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}")
|
||||
|
||||
@@ -117,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:
|
||||
@@ -166,29 +167,25 @@ 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:
|
||||
|
||||
#HINT: No dispositions for pgs subtitle tracks that have no external file source
|
||||
if (trackDescriptor.getExternalSourceFilePath()
|
||||
or trackDescriptor.getCodec() != TrackDescriptor.CODEC_PGS):
|
||||
|
||||
# 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:
|
||||
subIndex = trackDescriptor.getSubIndex()
|
||||
|
||||
subIndex = trackDescriptor.getSubIndex()
|
||||
streamIndicator = trackDescriptor.getType().indicator()
|
||||
dispositionSet = trackDescriptor.getDispositionSet()
|
||||
|
||||
@@ -358,37 +355,20 @@ class FfxController():
|
||||
metadataTokens = []
|
||||
|
||||
for tagKey, tagValue in self.__targetMediaDescriptor.getTags().items():
|
||||
|
||||
metadataTokens += [f"-metadata:g",
|
||||
f"{tagKey}={tagValue}"]
|
||||
|
||||
#HINT: With current ffmpeg version track metadata tags are not passed to the outfile
|
||||
for td in self.__targetMediaDescriptor.getAllTrackDescriptors():
|
||||
|
||||
#NOTE: With current version track metadata tags are not passed to the outfile
|
||||
targetTrackDescriptors = self.__targetMediaDescriptor.getReorderedTrackDescriptors()
|
||||
|
||||
|
||||
for d in targetTrackDescriptors:
|
||||
|
||||
# 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}")
|
||||
for tagKey, tagValue in d.getTags().items():
|
||||
for tagKey, tagValue in td.getTags().items():
|
||||
|
||||
metadataTokens += [f"-metadata:s:{d.getType().indicator()}:{d.getSubIndex()}",
|
||||
metadataTokens += [f"-metadata:s:{td.getType().indicator()}:{td.getSubIndex()}",
|
||||
f"{tagKey}={tagValue}"]
|
||||
|
||||
return metadataTokens
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def runJob(self,
|
||||
sourcePath,
|
||||
targetPath,
|
||||
|
||||
@@ -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():
|
||||
@@ -166,7 +86,7 @@ class MediaDescriptor:
|
||||
)
|
||||
|
||||
|
||||
def checkDefaultAndForcedDispositions(self):
|
||||
def checkConfiguration(self):
|
||||
|
||||
videoTracks = self.getVideoTracks()
|
||||
audioTracks = self.getAudioTracks()
|
||||
@@ -193,42 +113,37 @@ class MediaDescriptor:
|
||||
if len(set(sourceIndices)) < len(trackDescriptors):
|
||||
raise ValueError('Multiple streams originating from the same source stream')
|
||||
|
||||
def getReorderedTrackDescriptors(self):
|
||||
|
||||
|
||||
|
||||
reorderedTrackDescriptors = videoTracks + audioTracks + subtitleTracks
|
||||
orderedSourceTrackSequence = [
|
||||
t.getSourceIndex() for t in reorderedTrackDescriptors
|
||||
]
|
||||
|
||||
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()
|
||||
|
||||
if len([v for v in videoTracks if v.getDispositionFlag(TrackDisposition.DEFAULT)]) > 1:
|
||||
pass
|
||||
if len([a for a in audioTracks if a.getDispositionFlag(TrackDisposition.DEFAULT)]) > 1:
|
||||
pass
|
||||
if len([s for s in subtitleTracks if s.getDispositionFlag(TrackDisposition.DEFAULT)]):
|
||||
pass
|
||||
|
||||
videoTracks.append(videoTracks.pop(videoTracks.index(videoTracks)))
|
||||
audioTracks.append(audioTracks.pop(audioTracks.index(audioTracks)))
|
||||
subtitleTracks.append(subtitleTracks.pop(subtitleTracks.index(subtitleTracks)))
|
||||
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
|
||||
@@ -268,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):
|
||||
|
||||
@@ -315,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:
|
||||
@@ -332,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)
|
||||
@@ -386,12 +308,13 @@ 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 importedFilePath:
|
||||
importFileTokens += [
|
||||
@@ -403,17 +326,18 @@ class MediaDescriptor:
|
||||
|
||||
def getInputMappingTokens(self, use_sub_index: bool = True, only_video: bool = False):
|
||||
|
||||
reorderedTrackDescriptors = self.getReorderedTrackDescriptors()
|
||||
# reorderedTrackDescriptors = self.getReorderedTrackDescriptors()
|
||||
inputMappingTokens = []
|
||||
|
||||
filePointer = 1
|
||||
for rtd in reorderedTrackDescriptors:
|
||||
#for rtd in reorderedTrackDescriptors:
|
||||
for td in self.__trackDescriptors:
|
||||
|
||||
trackType = rtd.getType()
|
||||
trackType = td.getType()
|
||||
|
||||
if (trackType == TrackType.VIDEO or not only_video):
|
||||
|
||||
importedFilePath = rtd.getExternalSourceFilePath()
|
||||
importedFilePath = td.getExternalSourceFilePath()
|
||||
|
||||
if use_sub_index:
|
||||
|
||||
@@ -427,15 +351,15 @@ class MediaDescriptor:
|
||||
|
||||
else:
|
||||
|
||||
if rtd.getCodec() != TrackDescriptor.CODEC_PGS:
|
||||
if td.getCodec() != TrackDescriptor.CODEC_PGS:
|
||||
inputMappingTokens += [
|
||||
"-map",
|
||||
f"0:{trackType.indicator()}:{rtd.getSubIndex()}",
|
||||
f"0:{trackType.indicator()}:{td.getSubIndex()}",
|
||||
]
|
||||
|
||||
else:
|
||||
if rtd.getCodec() != TrackDescriptor.CODEC_PGS:
|
||||
inputMappingTokens += ["-map", f"0:{rtd.getIndex()}"]
|
||||
if td.getCodec() != TrackDescriptor.CODEC_PGS:
|
||||
inputMappingTokens += ["-map", f"0:{td.getIndex()}"]
|
||||
|
||||
return inputMappingTokens
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -241,6 +241,9 @@ class TrackDescriptor:
|
||||
def getIndex(self):
|
||||
return self.__index
|
||||
|
||||
def setIndex(self, index):
|
||||
self.__index = index
|
||||
|
||||
def getSourceIndex(self):
|
||||
return self.__sourceIndex
|
||||
|
||||
|
||||
Reference in New Issue
Block a user