tmdb / filename / loop MWE

click-textual
Javanaut 12 months ago
parent e3115e7557
commit cf49ff06d1

@ -6,9 +6,13 @@ from ffx.file_properties import FileProperties
from ffx.ffx_app import FfxApp from ffx.ffx_app import FfxApp
from ffx.ffx_controller import FfxController from ffx.ffx_controller import FfxController
from ffx.show_controller import ShowController
from ffx.tmdb_controller import TmdbController
from ffx.database import databaseContext from ffx.database import databaseContext
from ffx.track_type import TrackType from ffx.track_type import TrackType
from ffx.video_encoder import VideoEncoder
VERSION='0.1.0' VERSION='0.1.0'
@ -194,7 +198,9 @@ def convert(ctx,
context = ctx.obj context = ctx.obj
context['dry_run'] = dry_run context['dry_run'] = True # dry_run
context['video_encoder'] = VideoEncoder.fromLabel(video_encoder)
context['jellyfin'] = jellyfin context['jellyfin'] = jellyfin
context['tmdb'] = tmdb context['tmdb'] = tmdb
@ -209,8 +215,8 @@ def convert(ctx,
qualityTokens = quality.split(',') qualityTokens = quality.split(',')
q_list = [q for q in qualityTokens if q.isnumeric()] q_list = [q for q in qualityTokens if q.isnumeric()]
# click.echo(f"Qualities: {q_list}") click.echo(f"Qualities: {q_list}")
#
context['bitrates'] = {} context['bitrates'] = {}
context['bitrates']['stereo'] = str(stereo_bitrate) if str(stereo_bitrate).endswith('k') else f"{stereo_bitrate}k" context['bitrates']['stereo'] = str(stereo_bitrate) if str(stereo_bitrate).endswith('k') else f"{stereo_bitrate}k"
context['bitrates']['ac3'] = str(ac3_bitrate) if str(ac3_bitrate).endswith('k') else f"{ac3_bitrate}k" context['bitrates']['ac3'] = str(ac3_bitrate) if str(ac3_bitrate).endswith('k') else f"{ac3_bitrate}k"
@ -236,7 +242,8 @@ def convert(ctx,
# # Parse subtitle files # # Parse subtitle files
# #
# availableFileSubtitleDescriptors = searchSubtitleFiles(subtitle_directory, subtitle_prefix) if context['import_subtitles'] else [] # availableFileSubtitleDescriptors = searchSubtitleFiles(subtitle_directory, subtitle_prefix) if context['import_subtitles'] else []
# sc = ShowController(context)
tc = TmdbController()
existingSourcePaths = [p for p in paths if os.path.isfile(p) and p.split('.')[-1] in FfxController.INPUT_FILE_EXTENSIONS] existingSourcePaths = [p for p in paths if os.path.isfile(p) and p.split('.')[-1] in FfxController.INPUT_FILE_EXTENSIONS]
click.echo(f"\nRunning {len(existingSourcePaths) * len(q_list)} jobs") click.echo(f"\nRunning {len(existingSourcePaths) * len(q_list)} jobs")
@ -317,11 +324,31 @@ def convert(ctx,
audioTokens = fc.generateAudioEncodingTokens() audioTokens = fc.generateAudioEncodingTokens()
click.echo(f"Audio Tokens: {audioTokens}") click.echo(f"Audio Tokens: {audioTokens}")
tmdbFileBasename = ''
else: else:
# Case pattern matching # Case pattern matching
targetMediaDescriptor = currentPattern.getMediaDescriptor() if currentPattern is not None else None targetMediaDescriptor = currentPattern.getMediaDescriptor()
currentShowDescriptor = currentPattern.getShowDescriptor()
label = currentShowDescriptor.getFilenamePrefix()
tmdbResult = tc.queryTmdb(currentShowDescriptor.getId(), mediaFileProperties.getSeason(), mediaFileProperties.getEpisode())
# click.echo(f"{tmdbResult}")
tmdbFileBasename = tc.getEpisodeFileBasename(currentShowDescriptor.getFilenamePrefix(),
tmdbResult['name'],
mediaFileProperties.getSeason(),
mediaFileProperties.getEpisode(),
currentShowDescriptor.getIndexSeasonDigits(),
currentShowDescriptor.getIndexEpisodeDigits(),
currentShowDescriptor.getIndicatorSeasonDigits(),
currentShowDescriptor.getIndicatorEpisodeDigits())
click.echo(f"tmdbFileBasename={tmdbFileBasename}")
if context['import_subtitles']: if context['import_subtitles']:
targetMediaDescriptor.importSubtitles(context['subtitle_directory'], context['subtitle_prefix']) targetMediaDescriptor.importSubtitles(context['subtitle_directory'], context['subtitle_prefix'])
@ -349,6 +376,14 @@ def convert(ctx,
click.echo(f"\nRunning job {jobIndex} file={sourcePath} q={q}") click.echo(f"\nRunning job {jobIndex} file={sourcePath} q={q}")
jobIndex += 1 jobIndex += 1
targetFilename = tmdbFileBasename if tmdbFileBasename else mediaFileProperties.assembleTargetFilename(label, q if len(q_list) > 1 else -1)
targetPath = os.path.join(sourceDirectory, targetFilename)
fc.runJob(sourcePath,
targetPath,
context['video_encoder'],
q)
# #click.confirm('Warning! This file is not compliant to the defined source schema! Do you want to continue?', abort=True) # #click.confirm('Warning! This file is not compliant to the defined source schema! Do you want to continue?', abort=True)

@ -17,7 +17,7 @@ class FfxController():
TEMP_FILE_NAME = "ffmpeg2pass-0.log" TEMP_FILE_NAME = "ffmpeg2pass-0.log"
DEFAULT_VIDEO_ENCODER = 'vp9' DEFAULT_VIDEO_ENCODER = VideoEncoder.VP9.label()
DEFAULT_QUALITY = 23 DEFAULT_QUALITY = 23
DEFAULT_AV1_PRESET = 5 DEFAULT_AV1_PRESET = 5
@ -409,7 +409,7 @@ class FfxController():
+ self.generateDispositionTokens()) + self.generateDispositionTokens())
if not self.__sourceMediaDescriptor is None: if not self.__sourceMediaDescriptor is None:
commandSequence += self.generateMetadataTokens() commandSequence2 += self.generateMetadataTokens()
if denoise: if denoise:
commandSequence2 += self.generateDenoiseTokens() commandSequence2 += self.generateDenoiseTokens()

@ -43,7 +43,7 @@ class FileProperties():
matchResult = self.__pc.matchFilename(self.__sourceFilename) matchResult = self.__pc.matchFilename(self.__sourceFilename)
self.__pattern = matchResult['pattern'] if matchResult else None self.__pattern: Pattern = matchResult['pattern'] if matchResult else None
matchedGroups = matchResult['match'].groups() if matchResult else {} matchedGroups = matchResult['match'].groups() if matchResult else {}
seIndicator = matchedGroups[0] if matchedGroups else self.__sourceFilename seIndicator = matchedGroups[0] if matchedGroups else self.__sourceFilename
@ -187,13 +187,13 @@ class FileProperties():
def assembleTargetFilename(self, def assembleTargetFilename(self,
label = None, label: str = "",
quality: int = -1, quality: int = -1,
fileIndex: int = -1, fileIndex: int = -1,
indexDigits: int = DEFAULT_INDEX_DIGITS, indexDigits: int = DEFAULT_INDEX_DIGITS,
extension: str = None): extension: str = None):
if 'show_descriptor' in self.context['show_descriptor'].keys(): if 'show_descriptor' in self.context.keys():
season_digits = self.context['show_descriptor'][ShowDescriptor.INDICATOR_SEASON_DIGITS_KEY] season_digits = self.context['show_descriptor'][ShowDescriptor.INDICATOR_SEASON_DIGITS_KEY]
episode_digits = self.context['show_descriptor'][ShowDescriptor.INDICATOR_EPISODE_DIGITS_KEY] episode_digits = self.context['show_descriptor'][ShowDescriptor.INDICATOR_EPISODE_DIGITS_KEY]
else: else:
@ -204,25 +204,23 @@ class FileProperties():
targetFilenameExtension = FfxController.DEFAULT_FILE_EXTENSION if extension is None else str(extension) targetFilenameExtension = FfxController.DEFAULT_FILE_EXTENSION if extension is None else str(extension)
if label is None: if not label:
targetFilenameTokens = [self.__sourceFileBasename] targetFilenameTokens = [self.__sourceFileBasename]
else: else:
targetFilenameTokens = [label] targetFilenameTokens = [label]
if fileIndex > -1: if fileIndex > -1:
targetFilenameTokens += [f"{fileIndex:0{indexDigits}d}"] targetFilenameTokens += [f"{fileIndex:0{indexDigits}d}"]
elif self.__season > -1 and self.__episode > -1: elif self.__season > -1 and self.__episode > -1:
targetFilenameTokens += [f"S{self.__season:0{season_digits}d}E{self.__episode:0{episode_digits}d}"] targetFilenameTokens += [f"S{self.__season:0{season_digits}d}E{self.__episode:0{episode_digits}d}"]
elif self.__episode > -1: elif self.__episode > -1:
targetFilenameTokens += [f"E{self.__episode:0{episode_digits}d}"] targetFilenameTokens += [f"E{self.__episode:0{episode_digits}d}"]
if len(quality) != 1: if quality != -1:
targetFilenameTokens += [f"q{quality}"] targetFilenameTokens += [f"q{quality}"]
# In case source and target filenames are the same add an extension to distinct output from input # In case source and target filenames are the same add an extension to distinct output from input
if label is None and self.__sourceFilenameExtension == targetFilenameExtension: if not label and self.__sourceFilenameExtension == targetFilenameExtension:
targetFilenameTokens += ['ffx'] targetFilenameTokens += ['ffx']
targetFilename = '_'.join(targetFilenameTokens) targetFilename = '_'.join(targetFilenameTokens)

@ -1,10 +1,13 @@
import click
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship, sessionmaker, Mapped, backref from sqlalchemy.orm import relationship, sessionmaker, Mapped, backref
from .show import Base from .show import Base, Show
from .track import Track from .track import Track
from ffx.media_descriptor import MediaDescriptor from ffx.media_descriptor import MediaDescriptor
from ffx.show_descriptor import ShowDescriptor
class Pattern(Base): class Pattern(Base):
@ -20,7 +23,7 @@ class Pattern(Base):
# v1.x # v1.x
show_id = Column(Integer, ForeignKey('shows.id', ondelete="CASCADE")) show_id = Column(Integer, ForeignKey('shows.id', ondelete="CASCADE"))
show = relationship('Show', back_populates='patterns', lazy='joined') show = relationship(Show, back_populates='patterns', lazy='joined')
# v2.0 # v2.0
# show_id: Mapped[int] = mapped_column(ForeignKey("shows.id", ondelete="CASCADE")) # show_id: Mapped[int] = mapped_column(ForeignKey("shows.id", ondelete="CASCADE"))
@ -38,6 +41,10 @@ class Pattern(Base):
def getShowId(self): def getShowId(self):
return int(self.show_id) return int(self.show_id)
def getShowDescriptor(self) -> ShowDescriptor:
click.echo(f"self.show {self.show} id={self.show_id}")
return self.show.getDescriptor()
def getId(self): def getId(self):
return int(self.id) return int(self.id)

@ -44,30 +44,16 @@ class Show(Base):
indicator_episode_digits = Column(Integer, default=ShowDescriptor.DEFAULT_INDICATOR_EPISODE_DIGITS) indicator_episode_digits = Column(Integer, default=ShowDescriptor.DEFAULT_INDICATOR_EPISODE_DIGITS)
def getDesciptor(self): def getDescriptor(self):
descriptor = {} kwargs = {}
descriptor['id'] = int(self.id) kwargs[ShowDescriptor.ID_KEY] = int(self.id)
descriptor['name'] = str(self.name) kwargs[ShowDescriptor.NAME_KEY] = str(self.name)
descriptor['year'] = int(self.year) kwargs[ShowDescriptor.YEAR_KEY] = int(self.year)
kwargs[ShowDescriptor.INDEX_SEASON_DIGITS_KEY] = int(self.index_season_digits)
descriptor['index_season_digits'] = int(self.index_season_digits) kwargs[ShowDescriptor.INDEX_EPISODE_DIGITS_KEY] = int(self.index_season_digits)
descriptor['index_episode_digits'] = int(self.index_episode_digits) kwargs[ShowDescriptor.INDICATOR_SEASON_DIGITS_KEY] = int(self.indicator_season_digits)
descriptor['indicator_season_digits'] = int(self.indicator_season_digits) kwargs[ShowDescriptor.INDICATOR_EPISODE_DIGITS_KEY] = int(self.indicator_episode_digits)
descriptor['indicator_episode_digits'] = int(self.indicator_episode_digits)
return ShowDescriptor(**kwargs)
return descriptor
# def getDescriptor(self):
#
# kwargs = {}
#
# kwargs[]
# kwargs[]
# kwargs[]
#
# kwargs[]
# kwargs[]
# kwargs[]
# kwargs[]

@ -26,6 +26,18 @@ class ShowController():
finally: finally:
s.close() s.close()
def getShow(self, showId):
try:
s = self.Session()
q = s.query(Show).filter(Show.id == showId)
return q.first() if q.count() else None
except Exception as ex:
raise click.ClickException(f"ShowController.getShow(): {repr(ex)}")
finally:
s.close()
def getAllShows(self): def getAllShows(self):

@ -13,7 +13,7 @@ from ffx.helper import dictDiff, DIFF_ADDED_KEY, DIFF_CHANGED_KEY, DIFF_REMOVED_
class ShowDescriptor(): class ShowDescriptor():
"""This class represents the structural content of a media file including streams and metadata""" """This class represents the structural content of a media file including streams and metadata"""
CONTEXT_KEY = 'context' # CONTEXT_KEY = 'context'
ID_KEY = 'id' ID_KEY = 'id'
NAME_KEY = 'name' NAME_KEY = 'name'
@ -29,306 +29,74 @@ class ShowDescriptor():
DEFAULT_INDICATOR_SEASON_DIGITS = 2 DEFAULT_INDICATOR_SEASON_DIGITS = 2
DEFAULT_INDICATOR_EPISODE_DIGITS = 2 DEFAULT_INDICATOR_EPISODE_DIGITS = 2
def getDesciptor(self):
descriptor = {}
descriptor['id'] = int(self.id)
descriptor['name'] = str(self.name)
descriptor['year'] = int(self.year)
descriptor['index_season_digits'] = int(self.index_season_digits)
descriptor['index_episode_digits'] = int(self.index_episode_digits)
descriptor['indicator_season_digits'] = int(self.indicator_season_digits)
descriptor['indicator_episode_digits'] = int(self.indicator_episode_digits)
return descriptor
def __init__(self, **kwargs): def __init__(self, **kwargs):
if ShowDescriptor.ID_KEY in kwargs.keys(): if ShowDescriptor.ID_KEY in kwargs.keys():
if type(kwargs[ShowDescriptor.ID_KEY]) is not dict: if type(kwargs[ShowDescriptor.ID_KEY]) is not int:
raise TypeError(f"ShowDescriptor.__init__(): Argument {ShowDescriptor.ID_KEY} is required to be of type dict") raise TypeError(f"ShowDescriptor.__init__(): Argument {ShowDescriptor.ID_KEY} is required to be of type int")
self.__showId = kwargs[ShowDescriptor.ID_KEY] self.__showId = kwargs[ShowDescriptor.ID_KEY]
else: else:
self.__showId = {} self.__showId = {}
if ShowDescriptor.NAME_KEY in kwargs.keys(): if ShowDescriptor.NAME_KEY in kwargs.keys():
if type(kwargs[ShowDescriptor.NAME_KEY]) is not dict: if type(kwargs[ShowDescriptor.NAME_KEY]) is not str:
raise TypeError(f"ShowDescriptor.__init__(): Argument {ShowDescriptor.NAME_KEY} is required to be of type dict") raise TypeError(f"ShowDescriptor.__init__(): Argument {ShowDescriptor.NAME_KEY} is required to be of type str")
self.__showName = kwargs[ShowDescriptor.NAME_KEY] self.__showName = kwargs[ShowDescriptor.NAME_KEY]
else: else:
self.__showName = {} self.__showName = {}
if ShowDescriptor.YEAR_KEY in kwargs.keys(): if ShowDescriptor.YEAR_KEY in kwargs.keys():
if type(kwargs[ShowDescriptor.YEAR_KEY]) is not dict: if type(kwargs[ShowDescriptor.YEAR_KEY]) is not int:
raise TypeError(f"ShowDescriptor.__init__(): Argument {ShowDescriptor.YEAR_KEY} is required to be of type dict") raise TypeError(f"ShowDescriptor.__init__(): Argument {ShowDescriptor.YEAR_KEY} is required to be of type int")
self.__showYear = kwargs[ShowDescriptor.YEAR_KEY] self.__showYear = kwargs[ShowDescriptor.YEAR_KEY]
else: else:
self.__showYear = {} self.__showYear = {}
if ShowDescriptor.INDEX_SEASON_DIGITS_KEY in kwargs.keys(): if ShowDescriptor.INDEX_SEASON_DIGITS_KEY in kwargs.keys():
if type(kwargs[ShowDescriptor.INDEX_SEASON_DIGITS_KEY]) is not dict: if type(kwargs[ShowDescriptor.INDEX_SEASON_DIGITS_KEY]) is not int:
raise TypeError(f"ShowDescriptor.__init__(): Argument {ShowDescriptor.INDEX_SEASON_DIGITS_KEY} is required to be of type dict") raise TypeError(f"ShowDescriptor.__init__(): Argument {ShowDescriptor.INDEX_SEASON_DIGITS_KEY} is required to be of type int")
self.__indexSeasonDigits = kwargs[ShowDescriptor.INDEX_SEASON_DIGITS_KEY] self.__indexSeasonDigits = kwargs[ShowDescriptor.INDEX_SEASON_DIGITS_KEY]
else: else:
self.__indexSeasonDigits = {} self.__indexSeasonDigits = {}
if ShowDescriptor.INDEX_EPISODE_DIGITS_KEY in kwargs.keys(): if ShowDescriptor.INDEX_EPISODE_DIGITS_KEY in kwargs.keys():
if type(kwargs[ShowDescriptor.INDEX_EPISODE_DIGITS_KEY]) is not dict: if type(kwargs[ShowDescriptor.INDEX_EPISODE_DIGITS_KEY]) is not int:
raise TypeError(f"ShowDescriptor.__init__(): Argument {ShowDescriptor.INDEX_EPISODE_DIGITS_KEY} is required to be of type dict") raise TypeError(f"ShowDescriptor.__init__(): Argument {ShowDescriptor.INDEX_EPISODE_DIGITS_KEY} is required to be of type int")
self.__indexEpisodeDigits = kwargs[ShowDescriptor.INDEX_EPISODE_DIGITS_KEY] self.__indexEpisodeDigits = kwargs[ShowDescriptor.INDEX_EPISODE_DIGITS_KEY]
else: else:
self.__indexEpisodeDigits = {} self.__indexEpisodeDigits = {}
if ShowDescriptor.INDICATOR_SEASON_DIGITS_KEY in kwargs.keys(): if ShowDescriptor.INDICATOR_SEASON_DIGITS_KEY in kwargs.keys():
if type(kwargs[ShowDescriptor.INDICATOR_SEASON_DIGITS_KEY]) is not dict: if type(kwargs[ShowDescriptor.INDICATOR_SEASON_DIGITS_KEY]) is not int:
raise TypeError(f"ShowDescriptor.__init__(): Argument {ShowDescriptor.INDICATOR_SEASON_DIGITS_KEY} is required to be of type dict") raise TypeError(f"ShowDescriptor.__init__(): Argument {ShowDescriptor.INDICATOR_SEASON_DIGITS_KEY} is required to be of type int")
self.__indicatorSeasonDigits = kwargs[ShowDescriptor.INDICATOR_SEASON_DIGITS_KEY] self.__indicatorSeasonDigits = kwargs[ShowDescriptor.INDICATOR_SEASON_DIGITS_KEY]
else: else:
self.__indicatorSeasonDigits = {} self.__indicatorSeasonDigits = {}
if ShowDescriptor.INDICATOR_EPISODE_DIGITS_KEY in kwargs.keys(): if ShowDescriptor.INDICATOR_EPISODE_DIGITS_KEY in kwargs.keys():
if type(kwargs[ShowDescriptor.INDICATOR_EPISODE_DIGITS_KEY]) is not dict: if type(kwargs[ShowDescriptor.INDICATOR_EPISODE_DIGITS_KEY]) is not int:
raise TypeError(f"ShowDescriptor.__init__(): Argument {ShowDescriptor.INDICATOR_EPISODE_DIGITS_KEY} is required to be of type dict") raise TypeError(f"ShowDescriptor.__init__(): Argument {ShowDescriptor.INDICATOR_EPISODE_DIGITS_KEY} is required to be of type int")
self.__indicatorEpisodeDigits = kwargs[ShowDescriptor.INDICATOR_EPISODE_DIGITS_KEY] self.__indicatorEpisodeDigits = kwargs[ShowDescriptor.INDICATOR_EPISODE_DIGITS_KEY]
else: else:
self.__indicatorEpisodeDigits = {} self.__indicatorEpisodeDigits = {}
def getId(self):
def getDefaultVideoTrack(self): return self.__showId
videoDefaultTracks = [v for v in self.getVideoTracks() if TrackDisposition.DEFAULT in v.getDispositionSet()] def getName(self):
if len(videoDefaultTracks) > 1: return self.__showName
raise ValueError('ShowDescriptor.getDefaultVideoTrack(): More than one default video track is not supported') def getYear(self):
return videoDefaultTracks[0] if videoDefaultTracks else None return self.__showYear
def getForcedVideoTrack(self): def getIndexSeasonDigits(self):
videoForcedTracks = [v for v in self.getVideoTracks() if TrackDisposition.FORCED in v.getDispositionSet()] return self.__indexSeasonDigits
if len(videoForcedTracks) > 1: def getIndexEpisodeDigits(self):
raise ValueError('ShowDescriptor.getForcedVideoTrack(): More than one forced video track is not supported') return self.__indexEpisodeDigits
return videoForcedTracks[0] if videoForcedTracks else None def getIndicatorSeasonDigits(self):
return self.__indicatorSeasonDigits
def getDefaultAudioTrack(self): def getIndicatorEpisodeDigits(self):
audioDefaultTracks = [a for a in self.getAudioTracks() if TrackDisposition.DEFAULT in a.getDispositionSet()] return self.__indicatorEpisodeDigits
if len(audioDefaultTracks) > 1:
raise ValueError('ShowDescriptor.getDefaultAudioTrack(): More than one default audio track is not supported') def getFilenamePrefix(self):
return audioDefaultTracks[0] if audioDefaultTracks else None return f"{self.__showName} ({str(self.__showYear)})"
def getForcedAudioTrack(self):
audioForcedTracks = [a for a in self.getAudioTracks() if TrackDisposition.FORCED in a.getDispositionSet()]
if len(audioForcedTracks) > 1:
raise ValueError('ShowDescriptor.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('ShowDescriptor.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('ShowDescriptor.getForcedSubtitleTrack(): More than one forced subtitle track is not supported')
return subtitleForcedTracks[0] if subtitleForcedTracks else None
def setDefaultSubTrack(self, trackType : TrackType, subIndex : int):
for t in self.getAllTrackDescriptors():
if t.getType() == trackType:
t.setDispositionFlag(TrackDisposition.DEFAULT, t.getSubIndex() == int(subIndex))
def setForcedSubTrack(self, trackType : TrackType, subIndex : int):
for t in self.getAllTrackDescriptors():
if t.getType() == trackType:
t.setDispositionFlag(TrackDisposition.FORCED, t.getSubIndex() == int(subIndex))
def getReorderedTrackDescriptors(self):
videoTracks = self.sortSubIndices(self.getVideoTracks())
audioTracks = self.sortSubIndices(self.getAudioTracks())
subtitleTracks = self.sortSubIndices(self.getSubtitleTracks())
videoDefaultTrack = self.getDefaultVideoTrack()
self.getForcedVideoTrack()
audioDefaultTrack = self.getDefaultAudioTrack()
self.getForcedAudioTrack()
subtitleDefaultTrack = self.getDefaultSubtitleTrack()
self.getForcedSubtitleTrack()
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]
if len(set(orderedSourceTrackSequence)) < len(orderedSourceTrackSequence):
raise ValueError(f"Multiple streams originating from the same source stream not supported")
return reorderedTrackDescriptors
@classmethod
def fromFfprobe(cls, formatData, streamData):
kwargs = {}
if ShowDescriptor.FFPROBE_TAGS_KEY in formatData.keys():
kwargs[ShowDescriptor.TAGS_KEY] = formatData[ShowDescriptor.FFPROBE_TAGS_KEY]
kwargs[ShowDescriptor.TRACK_DESCRIPTOR_LIST_KEY] = []
#TODO: Evtl obsolet
subIndexCounters = {}
for streamObj in streamData:
ffprobeCodecType = streamObj[ShowDescriptor.FFPROBE_CODEC_TYPE_KEY]
trackType = TrackType.fromLabel(ffprobeCodecType)
if trackType != TrackType.UNKNOWN:
if trackType not in subIndexCounters.keys():
subIndexCounters[trackType] = 0
kwargs[ShowDescriptor.TRACK_DESCRIPTOR_LIST_KEY].append(TrackDescriptor.fromFfprobe(streamObj,
subIndex=subIndexCounters[trackType]))
subIndexCounters[trackType] += 1
return cls(**kwargs)
def getTags(self):
return self.__mediaTags
def sortSubIndices(self, descriptors : List[TrackDescriptor]) -> List[TrackDescriptor]:
subIndex = 0
for t in descriptors:
t.setSubIndex(subIndex)
subIndex += 1
return descriptors
def getAllTrackDescriptors(self) -> List[TrackDescriptor]:
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]
def getAudioTracks(self) -> List[TrackDescriptor]:
return [a for a in self.__trackDescriptors.copy() if a.getType() == TrackType.AUDIO]
def getSubtitleTracks(self) -> List[TrackDescriptor]:
return [s for s in self.__trackDescriptors.copy() 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, vsShowDescriptor : Self):
if not isinstance(vsShowDescriptor, self.__class__):
raise click.ClickException(f"ShowDescriptor.compare(): Argument is required to be of type {self.__class__}")
vsTags = vsShowDescriptor.getTags()
tags = self.getTags()
#HINT: Some tags differ per file, for example creation_time, so these are removed before diff
for emt in ShowDescriptor.EXCLUDED_MEDIA_TAGS:
if emt in tags.keys():
del tags[emt]
if emt in vsTags.keys():
del vsTags[emt]
tagsDiff = dictDiff(vsTags, tags)
compareResult = {}
if tagsDiff:
compareResult[ShowDescriptor.TAGS_KEY] = tagsDiff
# Target track configuration (from DB)
#tracks = self.getAllTrackDescriptors()
tracks = self.getReorderedTrackDescriptors()
numTracks = len(tracks)
# Current track configuration (of file)
vsTracks = vsShowDescriptor.getAllTrackDescriptors()
numVsTracks = len(vsTracks)
maxNumOfTracks = max(numVsTracks, numTracks)
trackCompareResult = {}
for tp in range(maxNumOfTracks):
# inspect/update funktionier nur so
if self.__jellyfinOrder:
vsTrackIndex = tracks[tp].getSourceIndex()
else:
vsTrackIndex = tp
# vsTrackIndex = tracks[tp].getSourceIndex()
# Will trigger if tracks are missing in file
if tp > (numVsTracks - 1):
if DIFF_ADDED_KEY not in trackCompareResult.keys():
trackCompareResult[DIFF_ADDED_KEY] = set()
trackCompareResult[DIFF_ADDED_KEY].add(tracks[tp].getIndex())
continue
# Will trigger if tracks are missing in DB definition
# New tracks will be added per update via this way
if tp > (numTracks - 1):
if DIFF_REMOVED_KEY not in trackCompareResult.keys():
trackCompareResult[DIFF_REMOVED_KEY] = {}
trackCompareResult[DIFF_REMOVED_KEY][vsTracks[vsTrackIndex].getIndex()] = vsTracks[vsTrackIndex]
continue
# assumption is made here that the track order will not change for all files of a sequence
trackDiff = tracks[tp].compare(vsTracks[vsTrackIndex])
if trackDiff:
if DIFF_CHANGED_KEY not in trackCompareResult.keys():
trackCompareResult[DIFF_CHANGED_KEY] = {}
trackCompareResult[DIFF_CHANGED_KEY][vsTracks[vsTrackIndex].getIndex()] = trackDiff
if trackCompareResult:
compareResult[ShowDescriptor.TRACKS_KEY] = trackCompareResult
return compareResult
def getInputMappingTokens(self, use_sub_index : bool = True):
reorderedTrackDescriptors = self.getReorderedTrackDescriptors()
inputMappingTokens = []
for rtd in reorderedTrackDescriptors:
trackType = rtd.getType()
if use_sub_index:
inputMappingTokens += ['-map', f"0:{trackType.indicator()}:{rtd.getSubIndex()}"]
else:
inputMappingTokens += ['-map', f"0:{rtd.getIndex()}"]
return inputMappingTokens

@ -40,12 +40,11 @@ class TmdbController():
return requests.get(tmdbUrl).json() return requests.get(tmdbUrl).json()
def getEpisodeFilename(self, def getEpisodeFileBasename(self,
showName, showName,
episodeName, episodeName,
season, season,
episode, episode,
extension,
indexSeasonDigits = 2, indexSeasonDigits = 2,
indexEpisodeDigits = 2, indexEpisodeDigits = 2,
indicatorSeasonDigits = 2, indicatorSeasonDigits = 2,
@ -93,6 +92,4 @@ class TmdbController():
if indicatorEpisodeDigits: if indicatorEpisodeDigits:
filenameTokens += ['E{num:{fill}{width}}'.format(num=episode, fill='0', width=indicatorEpisodeDigits)] filenameTokens += ['E{num:{fill}{width}}'.format(num=episode, fill='0', width=indicatorEpisodeDigits)]
filenameTokens += ['.', extension]
return ''.join(filenameTokens) return ''.join(filenameTokens)

Loading…
Cancel
Save