alpha 0.0.1
This commit is contained in:
93
bin/ffx.py
93
bin/ffx.py
@@ -118,9 +118,6 @@ def shows(ctx):
|
||||
|
||||
@click.argument('paths', nargs=-1)
|
||||
|
||||
@click.option("-t", "--tmdb", is_flag=True, default=False)
|
||||
@click.option("-j", "--jellyfin", is_flag=True, default=False)
|
||||
|
||||
@click.option('-l', '--label', type=str, default='', help='Label to be used as filename prefix')
|
||||
|
||||
@click.option('-v', '--video-encoder', type=str, default=FfxController.DEFAULT_VIDEO_ENCODER, help=f"Target video encoder (vp9 or av1) default: {FfxController.DEFAULT_VIDEO_ENCODER}")
|
||||
@@ -135,11 +132,6 @@ def shows(ctx):
|
||||
@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('-ss', '--subtitle-language', type=str, multiple=True, help='Subtitle stream language(s)')
|
||||
@click.option('-st', '--subtitle-title', type=str, multiple=True, help='Subtitle stream title(s)')
|
||||
|
||||
@click.option('-ds', '--default-subtitle', type=int, default=-1, help='Index of default subtitle stream')
|
||||
@click.option('-fs', '--forced-subtitle', type=int, default=-1, help='Index of forced subtitle stream') # (including default audio stream tag)
|
||||
|
||||
@click.option('-as', '--audio-language', type=str, multiple=True, help='Audio stream language(s)')
|
||||
@click.option('-at', '--audio-title', type=str, multiple=True, help='Audio stream title(s)')
|
||||
@@ -148,23 +140,27 @@ def shows(ctx):
|
||||
@click.option('-da', '--forced-audio', type=int, default=-1, help='Index of forced audio stream')
|
||||
|
||||
|
||||
@click.option('-ss', '--subtitle-language', type=str, multiple=True, help='Subtitle stream language(s)')
|
||||
@click.option('-st', '--subtitle-title', type=str, multiple=True, help='Subtitle stream title(s)')
|
||||
|
||||
@click.option('-ds', '--default-subtitle', type=int, default=-1, help='Index of default subtitle stream')
|
||||
@click.option('-fs', '--forced-subtitle', type=int, default=-1, help='Index of forced subtitle stream') # (including default audio stream tag)
|
||||
|
||||
|
||||
@click.option("--crop", is_flag=False, flag_value="default", default="none")
|
||||
|
||||
@click.option("-o", "--output-directory", type=str, default='')
|
||||
|
||||
|
||||
@click.option("-c", "--clear-metadata", is_flag=True, default=False)
|
||||
@click.option("-d", "--denoise", is_flag=True, default=False)
|
||||
|
||||
|
||||
@click.option("-t", "--no-tmdb", is_flag=True, default=False)
|
||||
@click.option("-j", "--no-jellyfin", is_flag=True, default=False)
|
||||
|
||||
@click.option("--dry-run", is_flag=True, default=False)
|
||||
|
||||
|
||||
def convert(ctx,
|
||||
paths,
|
||||
tmdb,
|
||||
jellyfin,
|
||||
label,
|
||||
video_encoder,
|
||||
quality,
|
||||
@@ -174,18 +170,22 @@ def convert(ctx,
|
||||
dts_bitrate,
|
||||
subtitle_directory,
|
||||
subtitle_prefix,
|
||||
subtitle_language,
|
||||
subtitle_title,
|
||||
default_subtitle,
|
||||
forced_subtitle,
|
||||
|
||||
audio_language,
|
||||
audio_title,
|
||||
default_audio,
|
||||
forced_audio,
|
||||
|
||||
subtitle_language,
|
||||
subtitle_title,
|
||||
default_subtitle,
|
||||
forced_subtitle,
|
||||
|
||||
crop,
|
||||
output_directory,
|
||||
clear_metadata,
|
||||
denoise,
|
||||
no_tmdb,
|
||||
no_jellyfin,
|
||||
dry_run):
|
||||
"""Batch conversion of audiovideo files in format suitable for web playback, e.g. jellyfin
|
||||
|
||||
@@ -202,8 +202,8 @@ def convert(ctx,
|
||||
|
||||
context['video_encoder'] = VideoEncoder.fromLabel(video_encoder)
|
||||
|
||||
context['jellyfin'] = jellyfin
|
||||
context['tmdb'] = tmdb
|
||||
context['jellyfin'] = not no_jellyfin
|
||||
context['tmdb'] = not no_tmdb
|
||||
|
||||
context['import_subtitles'] = (subtitle_directory and subtitle_prefix)
|
||||
if context['import_subtitles']:
|
||||
@@ -237,12 +237,6 @@ def convert(ctx,
|
||||
click.echo(f"Crop start={context['crop_start']} length={context['crop_length']}")
|
||||
|
||||
|
||||
# ## Conversion parameters
|
||||
#
|
||||
# # Parse subtitle files
|
||||
#
|
||||
# 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]
|
||||
@@ -270,6 +264,7 @@ def convert(ctx,
|
||||
|
||||
click.echo(f"Pattern matching: {'No' if currentPattern is None else 'Yes'}")
|
||||
|
||||
fileBasename = ''
|
||||
|
||||
if currentPattern is None:
|
||||
|
||||
@@ -324,8 +319,6 @@ def convert(ctx,
|
||||
audioTokens = fc.generateAudioEncodingTokens()
|
||||
click.echo(f"Audio Tokens: {audioTokens}")
|
||||
|
||||
tmdbFileBasename = ''
|
||||
|
||||
else:
|
||||
|
||||
# Case pattern matching
|
||||
@@ -333,22 +326,28 @@ def convert(ctx,
|
||||
targetMediaDescriptor = currentPattern.getMediaDescriptor()
|
||||
currentShowDescriptor = currentPattern.getShowDescriptor()
|
||||
|
||||
label = currentShowDescriptor.getFilenamePrefix()
|
||||
|
||||
tmdbResult = tc.queryTmdb(currentShowDescriptor.getId(), mediaFileProperties.getSeason(), mediaFileProperties.getEpisode())
|
||||
if context['tmdb']:
|
||||
|
||||
# click.echo(f"{tmdbResult}")
|
||||
tmdbResult = tc.queryTmdb(currentShowDescriptor.getId(), mediaFileProperties.getSeason(), mediaFileProperties.getEpisode())
|
||||
|
||||
tmdbFileBasename = tc.getEpisodeFileBasename(currentShowDescriptor.getFilenamePrefix(),
|
||||
tmdbResult['name'],
|
||||
mediaFileProperties.getSeason(),
|
||||
mediaFileProperties.getEpisode(),
|
||||
currentShowDescriptor.getIndexSeasonDigits(),
|
||||
currentShowDescriptor.getIndexEpisodeDigits(),
|
||||
currentShowDescriptor.getIndicatorSeasonDigits(),
|
||||
currentShowDescriptor.getIndicatorEpisodeDigits())
|
||||
# click.echo(f"{tmdbResult}")
|
||||
|
||||
click.echo(f"tmdbFileBasename={tmdbFileBasename}")
|
||||
if tmdbResult:
|
||||
fileBasename = tc.getEpisodeFileBasename(currentShowDescriptor.getFilenamePrefix(),
|
||||
tmdbResult['name'],
|
||||
mediaFileProperties.getSeason(),
|
||||
mediaFileProperties.getEpisode(),
|
||||
currentShowDescriptor.getIndexSeasonDigits(),
|
||||
currentShowDescriptor.getIndexEpisodeDigits(),
|
||||
currentShowDescriptor.getIndicatorSeasonDigits(),
|
||||
currentShowDescriptor.getIndicatorEpisodeDigits())
|
||||
|
||||
|
||||
else:
|
||||
fileBasename = currentShowDescriptor.getFilenamePrefix()
|
||||
|
||||
click.echo(f"fileBasename={fileBasename}")
|
||||
|
||||
if context['import_subtitles']:
|
||||
targetMediaDescriptor.importSubtitles(context['subtitle_directory'], context['subtitle_prefix'])
|
||||
@@ -376,14 +375,20 @@ def convert(ctx,
|
||||
click.echo(f"\nRunning job {jobIndex} file={sourcePath} q={q}")
|
||||
jobIndex += 1
|
||||
|
||||
targetFilename = tmdbFileBasename if tmdbFileBasename else mediaFileProperties.assembleTargetFilename(label, q if len(q_list) > 1 else -1)
|
||||
targetPath = os.path.join(sourceDirectory, targetFilename)
|
||||
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)
|
||||
|
||||
targetPath = os.path.join(output_directory if output_directory else sourceDirectory, targetFilename)
|
||||
|
||||
fc.runJob(sourcePath,
|
||||
targetPath,
|
||||
context['video_encoder'],
|
||||
q)
|
||||
|
||||
q,
|
||||
preset,
|
||||
denoise)
|
||||
|
||||
# #click.confirm('Warning! This file is not compliant to the defined source schema! Do you want to continue?', abort=True)
|
||||
|
||||
|
||||
@@ -349,10 +349,10 @@ class FfxController():
|
||||
def runJob(self,
|
||||
sourcePath,
|
||||
targetPath,
|
||||
videoEncoder : VideoEncoder = VideoEncoder.VP9,
|
||||
quality : int = DEFAULT_QUALITY,
|
||||
preset : int = DEFAULT_AV1_PRESET,
|
||||
denoise : bool = False):
|
||||
videoEncoder: VideoEncoder = VideoEncoder.VP9,
|
||||
quality: int = DEFAULT_QUALITY,
|
||||
preset: int = DEFAULT_AV1_PRESET,
|
||||
denoise: bool = False):
|
||||
|
||||
|
||||
commandTokens = FfxController.COMMAND_TOKENS + ['-i', sourcePath]
|
||||
@@ -367,8 +367,11 @@ class FfxController():
|
||||
if not self.__sourceMediaDescriptor is None:
|
||||
commandSequence += self.generateMetadataTokens()
|
||||
|
||||
if denoise:
|
||||
commandSequence += self.generateDenoiseTokens()
|
||||
|
||||
commandSequence += (self.generateAudioEncodingTokens()
|
||||
+ self.generateAV1Tokens(quality, preset)
|
||||
+ self.generateAV1Tokens(int(quality), int(preset))
|
||||
+ self.generateAudioEncodingTokens())
|
||||
|
||||
if self.__context['perform_crop']:
|
||||
@@ -388,7 +391,7 @@ class FfxController():
|
||||
|
||||
commandSequence1 = (commandTokens
|
||||
+ self.__targetMediaDescriptor.getInputMappingTokens()
|
||||
+ self.generateVP9Pass1Tokens(quality))
|
||||
+ self.generateVP9Pass1Tokens(int(quality)))
|
||||
|
||||
if self.__context['perform_crop']:
|
||||
commandSequence1 += FfxController.generateCropTokens()
|
||||
@@ -414,7 +417,7 @@ class FfxController():
|
||||
if denoise:
|
||||
commandSequence2 += self.generateDenoiseTokens()
|
||||
|
||||
commandSequence2 += self.generateVP9Pass2Tokens(quality) + self.generateAudioEncodingTokens()
|
||||
commandSequence2 += self.generateVP9Pass2Tokens(int(quality)) + self.generateAudioEncodingTokens()
|
||||
|
||||
if self.__context['perform_crop']:
|
||||
commandSequence2 += FfxController.generateCropTokens()
|
||||
|
||||
@@ -186,12 +186,12 @@ class FileProperties():
|
||||
return int(self.__episode)
|
||||
|
||||
|
||||
def assembleTargetFilename(self,
|
||||
def assembleTargetFileBasename(self,
|
||||
label: str = "",
|
||||
quality: int = -1,
|
||||
fileIndex: int = -1,
|
||||
indexDigits: int = DEFAULT_INDEX_DIGITS,
|
||||
extension: str = None):
|
||||
extraTokens: list = []):
|
||||
|
||||
if 'show_descriptor' in self.context.keys():
|
||||
season_digits = self.context['show_descriptor'][ShowDescriptor.INDICATOR_SEASON_DIGITS_KEY]
|
||||
@@ -202,7 +202,7 @@ class FileProperties():
|
||||
|
||||
targetFilenameTokens = []
|
||||
|
||||
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 not label:
|
||||
targetFilenameTokens = [self.__sourceFileBasename]
|
||||
@@ -220,8 +220,9 @@ class FileProperties():
|
||||
targetFilenameTokens += [f"q{quality}"]
|
||||
|
||||
# In case source and target filenames are the same add an extension to distinct output from input
|
||||
if not label and self.__sourceFilenameExtension == targetFilenameExtension:
|
||||
targetFilenameTokens += ['ffx']
|
||||
#if not label and self.__sourceFilenameExtension == targetFilenameExtension:
|
||||
# targetFilenameTokens += ['ffx']
|
||||
targetFilenameTokens += extraTokens
|
||||
|
||||
targetFilename = '_'.join(targetFilenameTokens)
|
||||
|
||||
|
||||
@@ -37,8 +37,11 @@ class TmdbController():
|
||||
|
||||
tmdbUrl = f"https://api.themoviedb.org/3/tv/{showId}/season/{season}/episode/{episode}{urlParams}"
|
||||
|
||||
return requests.get(tmdbUrl).json()
|
||||
|
||||
#TODO Check for result
|
||||
try:
|
||||
return requests.get(tmdbUrl).json()
|
||||
except:
|
||||
return {}
|
||||
|
||||
def getEpisodeFileBasename(self,
|
||||
showName,
|
||||
|
||||
Reference in New Issue
Block a user