Maveno 11 months ago
parent b16e76370b
commit 5febb96916

@ -14,6 +14,8 @@ from ffx.database import databaseContext
from ffx.media_descriptor import MediaDescriptor
from ffx.track_descriptor import TrackDescriptor
from ffx.show_descriptor import ShowDescriptor
from ffx.track_type import TrackType
from ffx.video_encoder import VideoEncoder
from ffx.track_disposition import TrackDisposition
@ -299,9 +301,11 @@ def checkUniqueDispositions(context, mediaDescriptor: MediaDescriptor):
@click.option('--denoise-research-window', type=str, default='', help='Range to search for comparable patches on luminosity plane. Better filtering but costly.')
@click.option('--denoise-chroma-research-window', type=str, default='', help='Range to search for comparable patches on chroma planes.')
@click.option('--show', type=int, default=-1, help='Set TMDB show identifier')
@click.option('--season', type=int, default=-1, help='Set season of show')
@click.option('--episode', type=int, default=-1, help='Set episode of show')
@click.option("--no-tmdb", is_flag=True, default=False)
# @click.option("--no-jellyfin", is_flag=True, default=False)
@click.option("--no-pattern", is_flag=True, default=False)
@click.option("--dont-pass-dispositions", is_flag=True, default=False)
@ -346,6 +350,10 @@ def convert(ctx,
denoise_research_window,
denoise_chroma_research_window,
show,
season,
episode,
no_tmdb,
# no_jellyfin,
no_pattern,
@ -389,6 +397,11 @@ def convert(ctx,
context['subtitle_prefix'] = subtitle_prefix
existingSourcePaths = [p for p in paths if os.path.isfile(p) and p.split('.')[-1] in FfxController.INPUT_FILE_EXTENSIONS]
# CLI Overrides
cliOverrides = {}
if language:
@ -426,6 +439,18 @@ def convert(ctx,
if forced_subtitle != -1:
cliOverrides['forced_subtitle'] = forced_subtitle
if show != -1 or season != -1 or episode != -1:
if len(existingSourcePaths) > 1:
context['logger'].warning(f"Ignoring TMDB show, season, episode overrides, not supported for multiple source files")
else:
cliOverrides['tmdb'] = {}
if show != -1:
cliOverrides['tmdb']['show'] = show
if season != -1:
cliOverrides['tmdb']['season'] = season
if episode != -1:
cliOverrides['tmdb']['episode'] = episode
if cliOverrides:
context['overrides'] = cliOverrides
@ -468,7 +493,7 @@ def convert(ctx,
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]
ctx.obj['logger'].info(f"\nRunning {len(existingSourcePaths) * len(q_list)} jobs")
jobIndex = 0
@ -487,6 +512,15 @@ def convert(ctx,
mediaFileProperties = FileProperties(context, sourceFilename)
#HINT: -1 if not set
showSeason = (cliOverrides['tmdb']['season'] if 'tmdb' in cliOverrides.keys()
and 'season' in cliOverrides['tmdb'] else mediaFileProperties.getSeason())
showEpisode = (cliOverrides['tmdb']['episode'] if 'tmdb' in cliOverrides.keys()
and 'episode' in cliOverrides['tmdb'] else mediaFileProperties.getEpisode())
ctx.obj['logger'].debug(f"Season={showSeason} Episode={showEpisode}")
sourceMediaDescriptor = mediaFileProperties.getMediaDescriptor()
#HINT: This is None if the filename did not match anything in database
@ -494,80 +528,84 @@ def convert(ctx,
ctx.obj['logger'].debug(f"Pattern matching: {'No' if currentPattern is None else 'Yes'}")
# fileBasename = ''
# Setup FfxController accordingly depending on pattern matching is enabled and a pattern was matched
if currentPattern is None:
# Case no pattern matching
# fileBasename = currentShowDescriptor.getFilenamePrefix()
checkUniqueDispositions(context, sourceMediaDescriptor)
currentShowDescriptor = None
if context['import_subtitles']:
sourceMediaDescriptor.importSubtitles(context['subtitle_directory'],
context['subtitle_prefix'],
mediaFileProperties.getSeason(),
mediaFileProperties.getEpisode())
showSeason,
showEpisode)
if cliOverrides:
sourceMediaDescriptor.applyOverrides(cliOverrides)
#YOLO
fc = FfxController(context, sourceMediaDescriptor)
else:
# Case pattern matching
targetMediaDescriptor = currentPattern.getMediaDescriptor(ctx.obj)
checkUniqueDispositions(context, targetMediaDescriptor)
currentShowDescriptor = currentPattern.getShowDescriptor(ctx.obj)
if context['use_tmdb']:
ctx.obj['logger'].debug(f"Querying TMDB for show_id={currentShowDescriptor.getId()} season={mediaFileProperties.getSeason()} episode{mediaFileProperties.getEpisode()}")
tmdbEpisodeResult = tc.queryEpisode(currentShowDescriptor.getId(), mediaFileProperties.getSeason(), mediaFileProperties.getEpisode())
ctx.obj['logger'].debug(f"tmdbEpisodeResult={tmdbEpisodeResult}")
if tmdbEpisodeResult:
filteredEpisodeName = filterFilename(tmdbEpisodeResult['name'])
sourceFileBasename = TmdbController.getEpisodeFileBasename(currentShowDescriptor.getFilenamePrefix(),
filteredEpisodeName,
mediaFileProperties.getSeason(),
mediaFileProperties.getEpisode(),
currentShowDescriptor.getIndexSeasonDigits(),
currentShowDescriptor.getIndexEpisodeDigits(),
currentShowDescriptor.getIndicatorSeasonDigits(),
currentShowDescriptor.getIndicatorEpisodeDigits())
else:
sourceFileBasename = currentShowDescriptor.getFilenamePrefix()
if context['import_subtitles']:
targetMediaDescriptor.importSubtitles(context['subtitle_directory'],
context['subtitle_prefix'],
mediaFileProperties.getSeason(),
mediaFileProperties.getEpisode())
showSeason,
showEpisode)
ctx.obj['logger'].debug(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()]}")
if cliOverrides:
targetMediaDescriptor.applyOverrides(cliOverrides)
ctx.obj['logger'].debug(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()]}")
ctx.obj['logger'].debug(f"Input mapping tokens (2nd pass): {targetMediaDescriptor.getInputMappingTokens()}")
fc = FfxController(context, targetMediaDescriptor, sourceMediaDescriptor)
ctx.obj['logger'].debug(f"Season={mediaFileProperties.getSeason()} Episode={mediaFileProperties.getEpisode()}")
# Assemble target filename accordingly depending on TMDB lookup is enabled
#HINT: -1 if not set
showId = cliOverrides['tmdb']['show'] if 'tmdb' in cliOverrides.keys() and 'show' in cliOverrides['tmdb'] else (-1 if currentShowDescriptor is None else currentShowDescriptor.getId())
if context['use_tmdb'] and showId != -1 and showSeason != -1 and showEpisode != -1:
ctx.obj['logger'].debug(f"Querying TMDB for show_id={showId} season={showSeason} episode{showEpisode}")
if currentPattern is None:
sName, showYear = tc.getShowNameAndYear(showId)
showName = filterFilename(sName)
showFilenamePrefix = f"{showName} ({str(showYear)})"
indexSeasonDigits = ShowDescriptor.DEFAULT_INDEX_SEASON_DIGITS
indexEpisodeDigits = ShowDescriptor.DEFAULT_INDEX_EPISODE_DIGITS
indicatorSeasonDigits = ShowDescriptor.DEFAULT_INDICATOR_SEASON_DIGITS
indicatorEpisodeDigits = ShowDescriptor.DEFAULT_INDICATOR_EPISODE_DIGITS
else:
showFilenamePrefix = currentShowDescriptor.getFilenamePrefix()
indexSeasonDigits = currentShowDescriptor.getIndexSeasonDigits()
indexEpisodeDigits = currentShowDescriptor.getIndexEpisodeDigits()
indicatorSeasonDigits = currentShowDescriptor.getIndicatorSeasonDigits()
indicatorEpisodeDigits = currentShowDescriptor.getIndicatorEpisodeDigits()
tmdbEpisodeResult = tc.queryEpisode(showId, showSeason, showEpisode)
ctx.obj['logger'].debug(f"tmdbEpisodeResult={tmdbEpisodeResult}")
if tmdbEpisodeResult:
filteredEpisodeName = filterFilename(tmdbEpisodeResult['name'])
sourceFileBasename = TmdbController.getEpisodeFileBasename(showFilenamePrefix,
filteredEpisodeName,
showSeason,
showEpisode,
indexSeasonDigits,
indexEpisodeDigits,
indicatorSeasonDigits,
indicatorEpisodeDigits)
ctx.obj['logger'].debug(f"fileBasename={sourceFileBasename}")

@ -122,23 +122,12 @@ 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 = [td for td in self.__targetMediaDescriptor.getAllTrackDescriptors() if td.getType() == TrackType.AUDIO]
trackSubIndex = 0
for trackDescriptor in targetAudioTrackDescriptors:
# Calculate source sub index
#changedTargetTrackDescriptor : TrackDescriptor = targetAudioTrackDescriptors[trackDescriptor.getIndex()]
#changedTargetTrackSourceIndex = changedTargetTrackDescriptor.getSourceIndex()
#sourceSubIndex = sourceAudioTrackDescriptors[changedTargetTrackSourceIndex].getSubIndex()
trackAudioLayout = trackDescriptor.getAudioLayout()
#TODO: Sollte nicht die sub index unverändert bleiben wenn jellyfin reordering angewendet wurde?
# siehe auch: MediaDescriptor.getInputMappingTokens()
#trackSubIndex = trackDescriptor.getSubIndex()
if trackAudioLayout == AudioLayout.LAYOUT_6_1:
audioTokens += [f"-c:a:{trackSubIndex}",
@ -180,10 +169,6 @@ class FfxController():
sourceTrackDescriptors = ([] if self.__sourceMediaDescriptor is None
else self.__sourceMediaDescriptor.getAllTrackDescriptors())
# if not self.__sourceMediaDescriptor is None:
# sourceTrackDescriptors = self.__sourceMediaDescriptor.getAllTrackDescriptors()
# else:
# sourceTrackDescriptors = []
dispositionTokens = []
@ -274,12 +259,9 @@ class FfxController():
+ self.__targetMediaDescriptor.getInputMappingTokens()
+ self.generateDispositionTokens())
if not self.__sourceMediaDescriptor is None or 'overrides' in self.__context.keys():
commandSequence += self.generateMetadataTokens()
# if denoise:
# commandSequence += self.generateDenoiseTokens()
commandSequence1 += self.__context['denoiser'].generateDenoiseTokens()
# Optional tokens
commandSequence += self.generateMetadataTokens()
commandSequence += self.__context['denoiser'].generateDenoiseTokens()
commandSequence += (self.generateAudioEncodingTokens()
+ self.generateAV1Tokens(int(quality), int(preset))
@ -301,8 +283,16 @@ class FfxController():
if videoEncoder == VideoEncoder.VP9:
commandSequence1 = (commandTokens
+ self.__targetMediaDescriptor.getInputMappingTokens(only_video=True)
+ self.generateVP9Pass1Tokens(int(quality)))
+ self.__targetMediaDescriptor.getInputMappingTokens(only_video=True))
# Optional tokens
#NOTE: Filters and so needs to run on the first pass as well, as here
# the required bitrate for the second run is determined and recorded
# TODO: Results seems to be slightly better with first pass omitted,
# Confirm or find better filter settings for 2-pass
# commandSequence1 += self.__context['denoiser'].generateDenoiseTokens()
commandSequence1 += self.generateVP9Pass1Tokens(int(quality))
if self.__context['perform_crop']:
commandSequence1 += self.generateCropTokens()
@ -322,11 +312,8 @@ class FfxController():
+ self.__targetMediaDescriptor.getInputMappingTokens()
+ self.generateDispositionTokens())
if not self.__sourceMediaDescriptor is None or 'overrides' in self.__context.keys():
commandSequence2 += self.generateMetadataTokens()
# if denoise:
# commandSequence2 += self.generateDenoiseTokens()
# Optional tokens
commandSequence2 += self.generateMetadataTokens()
commandSequence2 += self.__context['denoiser'].generateDenoiseTokens()
commandSequence2 += self.generateVP9Pass2Tokens(int(quality)) + self.generateAudioEncodingTokens()
@ -348,7 +335,7 @@ class FfxController():
def createEmptyFile(self,
path: str = 'output.mp4',
path: str = 'empty.mkv',
sizeX: int = 1280,
sizeY: int = 720,
rate: int = 25,

@ -354,8 +354,7 @@ class ShowDetailsScreen(Screen):
showDescriptor = self.getShowDescriptorFromInput()
if not showDescriptor is None:
showResult = self.__tc.queryShow(showDescriptor.getId())
firstAirDate = datetime.strptime(showResult['first_air_date'], '%Y-%m-%d')
showName, showYear = self.__tc.getShowNameAndYear(showDescriptor.getId())
self.query_one("#name_input", Input).value = filterFilename(showResult['name'])
self.query_one("#year_input", Input).value = str(firstAirDate.year)
self.query_one("#name_input", Input).value = filterFilename(showName)
self.query_one("#year_input", Input).value = str(showYear)

@ -1,4 +1,6 @@
import os, click, requests, json, time, logging
from datetime import datetime
class TMDB_REQUEST_EXCEPTION(Exception):
def __init__(self, statusCode, statusMessage):
@ -95,6 +97,14 @@ class TmdbController():
return self.getTmdbRequest(tmdbUrl)
def getShowNameAndYear(self, showId: int):
showResult = self.queryShow(int(showId))
firstAirDate = datetime.strptime(showResult['first_air_date'], '%Y-%m-%d')
return str(showResult['name']), int(firstAirDate.year)
def queryEpisode(self, showId, season, episode):
"""
First level keys in the response object:

Loading…
Cancel
Save