Add asterisk to filename filter, Signature

main
Maveno 11 months ago
parent 1a0a5f4482
commit 0ed85fce4a

@ -19,6 +19,7 @@ from ffx.video_encoder import VideoEncoder
from ffx.track_disposition import TrackDisposition
from ffx.process import executeProcess
from ffx.helper import filterFilename
VERSION='0.2.0'
@ -150,8 +151,7 @@ def unmux(ctx,
subtitles_only):
existingSourcePaths = [p for p in paths if os.path.isfile(p)]
if ctx.obj['verbosity'] > 0:
click.echo(f"\nUnmuxing {len(existingSourcePaths)} files")
ctx.obj['logger'].debug(f"\nUnmuxing {len(existingSourcePaths)} files")
for sourcePath in existingSourcePaths:
@ -169,12 +169,10 @@ def unmux(ctx,
targetIndicator = f"_S{season}E{episode}" if label and season != -1 and episode != -1 else ''
if label and not targetIndicator:
if ctx.obj['verbosity'] > 0:
click.echo(f"Skipping file {fp.getFilename()}: Label set but no indicator recognized")
ctx.obj['logger'].warning(f"Skipping file {fp.getFilename()}: Label set but no indicator recognized")
continue
else:
if ctx.obj['verbosity'] > 0:
click.echo(f"\nUnmuxing file {fp.getFilename()}\n")
ctx.obj['logger'].debug(f"\nUnmuxing file {fp.getFilename()}\n")
for trackDescriptor in sourceMediaDescriptor.getAllTrackDescriptors():
@ -187,18 +185,14 @@ def unmux(ctx,
if unmuxSequence:
if not ctx.obj['dry_run']:
if ctx.obj['verbosity'] > 0:
click.echo(f"Executing unmuxing sequence: {' '.join(unmuxSequence)}")
ctx.obj['logger'].debug(f"Executing unmuxing sequence: {' '.join(unmuxSequence)}")
out, err, rc = executeProcess(unmuxSequence)
if rc:
if ctx.obj['verbosity'] > 0:
click.echo(f"Unmuxing of stream {trackDescriptor.getIndex()} failed with error ({rc}) {err}")
ctx.obj['logger'].error(f"Unmuxing of stream {trackDescriptor.getIndex()} failed with error ({rc}) {err}")
else:
if ctx.obj['verbosity'] > 0:
click.echo(f"Skipping stream with unknown codec {trackDescriptor.getCodec()}")
ctx.obj['logger'].warning(f"Skipping stream with unknown codec {trackDescriptor.getCodec()}")
except Exception as ex:
if ctx.obj['verbosity'] > 0:
click.echo(f"Skipping File {sourcePath} ({ex})")
ctx.obj['logger'].warning(f"Skipping File {sourcePath} ({ex})")
@ffx.command()
@ -267,7 +261,7 @@ def checkUniqueDispositions(context, mediaDescriptor: MediaDescriptor):
@click.option('-q', '--quality', type=str, default=FfxController.DEFAULT_QUALITY, help=f"Quality settings to be used with VP9 encoder (default: {FfxController.DEFAULT_QUALITY})")
@click.option('-p', '--preset', type=str, default=FfxController.DEFAULT_AV1_PRESET, help=f"Quality preset to be used with AV1 encoder (default: {FfxController.DEFAULT_AV1_PRESET})")
@click.option('-s', '--stereo-bitrate', type=int, default=FfxController.DEFAULT_STEREO_BANDWIDTH, help=f"Bitrate in kbit/s to be used to encode stereo audio streams (default: {FfxController.DEFAULT_STEREO_BANDWIDTH})")
@click.option('-a', '--stereo-bitrate', type=int, default=FfxController.DEFAULT_STEREO_BANDWIDTH, help=f"Bitrate in kbit/s to be used to encode stereo audio streams (default: {FfxController.DEFAULT_STEREO_BANDWIDTH})")
@click.option('--ac3', type=int, default=FfxController.DEFAULT_AC3_BANDWIDTH, help=f"Bitrate in kbit/s to be used to encode 5.1 audio streams (default: {FfxController.DEFAULT_AC3_BANDWIDTH})")
@click.option('--dts', type=int, default=FfxController.DEFAULT_DTS_BANDWIDTH, help=f"Bitrate in kbit/s to be used to encode 6.1 audio streams (default: {FfxController.DEFAULT_DTS_BANDWIDTH})")
@ -298,8 +292,11 @@ def checkUniqueDispositions(context, mediaDescriptor: MediaDescriptor):
@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)
@click.option("--no-prompt", is_flag=True, default=False)
@click.option("--no-signature", is_flag=True, default=False)
def convert(ctx,
paths,
@ -331,7 +328,8 @@ def convert(ctx,
no_jellyfin,
no_pattern,
dont_pass_dispositions,
no_prompt):
no_prompt,
no_signature):
"""Batch conversion of audiovideo files in format suitable for web playback, e.g. jellyfin
Files found under PATHS will be converted according to parameters.
@ -349,29 +347,28 @@ def convert(ctx,
context['use_tmdb'] = not no_tmdb
context['use_pattern'] = not no_pattern
context['no_prompt'] = no_prompt
context['no_signature'] = no_signature
context['import_subtitles'] = (subtitle_directory and subtitle_prefix)
if context['import_subtitles']:
context['subtitle_directory'] = subtitle_directory
context['subtitle_prefix'] = subtitle_prefix
# click.echo(f"\nVideo encoder: {video_encoder}")
ctx.obj['logger'].debug(f"\nVideo encoder: {video_encoder}")
qualityTokens = quality.split(',')
q_list = [q for q in qualityTokens if q.isnumeric()]
if ctx.obj['verbosity'] > 0:
click.echo(f"Qualities: {q_list}")
ctx.obj['logger'].debug(f"Qualities: {q_list}")
context['bitrates'] = {}
context['bitrates']['stereo'] = str(stereo_bitrate) if str(stereo_bitrate).endswith('k') else f"{stereo_bitrate}k"
context['bitrates']['ac3'] = str(ac3) if str(ac3).endswith('k') else f"{ac3}k"
context['bitrates']['dts'] = str(dts) if str(dts).endswith('k') else f"{dts}k"
if ctx.obj['verbosity'] > 0:
click.echo(f"Stereo bitrate: {context['bitrates']['stereo']}")
click.echo(f"AC3 bitrate: {context['bitrates']['ac3']}")
click.echo(f"DTS bitrate: {context['bitrates']['dts']}")
ctx.obj['logger'].debug(f"Stereo bitrate: {context['bitrates']['stereo']}")
ctx.obj['logger'].debug(f"AC3 bitrate: {context['bitrates']['ac3']}")
ctx.obj['logger'].debug(f"DTS bitrate: {context['bitrates']['dts']}")
# Process crop parameters
@ -381,15 +378,14 @@ def convert(ctx,
if cTokens and len(cTokens) == 2:
context['crop_start'] = int(cTokens[0])
context['crop_length'] = int(cTokens[1])
if ctx.obj['verbosity'] > 0:
click.echo(f"Crop start={context['crop_start']} length={context['crop_length']}")
ctx.obj['logger'].debug(f"Crop start={context['crop_start']} length={context['crop_length']}")
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]
if ctx.obj['verbosity'] > 0:
click.echo(f"\nRunning {len(existingSourcePaths) * len(q_list)} jobs")
ctx.obj['logger'].info(f"\nRunning {len(existingSourcePaths) * len(q_list)} jobs")
jobIndex = 0
for sourcePath in existingSourcePaths:
@ -402,8 +398,7 @@ def convert(ctx,
sourceFileBasename = '.'.join(sourcePathTokens[:-1])
sourceFilenameExtension = sourcePathTokens[-1]
if ctx.obj['verbosity'] > 0:
click.echo(f"\nProcessing file {sourcePath}")
ctx.obj['logger'].info(f"\nProcessing file {sourcePath}")
mediaFileProperties = FileProperties(context, sourceFilename)
@ -412,8 +407,7 @@ def convert(ctx,
#HINT: This is None if the filename did not match anything in database
currentPattern = mediaFileProperties.getPattern() if context['use_pattern'] else None
if ctx.obj['verbosity'] > 0:
click.echo(f"Pattern matching: {'No' if currentPattern is None else 'Yes'}")
ctx.obj['logger'].debug(f"Pattern matching: {'No' if currentPattern is None else 'Yes'}")
# fileBasename = ''
@ -452,13 +446,14 @@ def convert(ctx,
if context['use_tmdb']:
click.echo(f"Querying TMDB for show_id={currentShowDescriptor.getId()} season={mediaFileProperties.getSeason()} episode{mediaFileProperties.getEpisode()}")
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())
click.echo(f"tmdbEpisodeResult={tmdbEpisodeResult}")
ctx.obj['logger'].debug(f"tmdbEpisodeResult={tmdbEpisodeResult}")
if tmdbEpisodeResult:
filteredEpisodeName = filterFilename(tmdbEpisodeResult['name'])
sourceFileBasename = TmdbController.getEpisodeFileBasename(currentShowDescriptor.getFilenamePrefix(),
tmdbEpisodeResult['name'],
filteredEpisodeName,
mediaFileProperties.getSeason(),
mediaFileProperties.getEpisode(),
currentShowDescriptor.getIndexSeasonDigits(),
@ -474,50 +469,45 @@ def convert(ctx,
mediaFileProperties.getSeason(),
mediaFileProperties.getEpisode())
# 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()]}")
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 context['use_jellyfin']:
# Reorder subtracks in types with default the last, then make subindices flat again
targetMediaDescriptor.applyJellyfinOrder()
# sourceMediaDescriptor.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
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 ctx.obj['verbosity'] > 0:
click.echo(f"Input mapping tokens (2nd pass): {targetMediaDescriptor.getInputMappingTokens()}")
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()}")
if ctx.obj['verbosity'] > 0:
click.echo(f"Season={mediaFileProperties.getSeason()} Episode={mediaFileProperties.getEpisode()}")
if ctx.obj['verbosity'] > 0:
click.echo(f"fileBasename={sourceFileBasename}")
ctx.obj['logger'].debug(f"fileBasename={sourceFileBasename}")
for q in q_list:
if ctx.obj['verbosity'] > 0:
click.echo(f"\nRunning job {jobIndex} file={sourcePath} q={q}")
ctx.obj['logger'].debug(f"\nRunning job {jobIndex} file={sourcePath} q={q}")
jobIndex += 1
extra = ['ffx'] if sourceFilenameExtension == FfxController.DEFAULT_FILE_EXTENSION else []
click.echo(f"label={label if label else 'Falsy'}")
click.echo(f"sourceFileBasename={sourceFileBasename}")
ctx.obj['logger'].debug(f"label={label if label else 'Falsy'}")
ctx.obj['logger'].debug(f"sourceFileBasename={sourceFileBasename}")
targetFilename = (sourceFileBasename if context['use_tmdb']
else mediaFileProperties.assembleTargetFileBasename(label,
q if len(q_list) > 1 else -1,
extraTokens = extra))
targetFileBasename = mediaFileProperties.assembleTargetFileBasename(label,
q if len(q_list) > 1 else -1,
extraTokens = extra)
#TODO #387
targetFilename = ((f"{sourceFileBasename}_q{q}" if len(q_list) > 1 else sourceFileBasename)
if context['use_tmdb'] else targetFileBasename)
targetPath = os.path.join(output_directory if output_directory else sourceDirectory, targetFilename)
# media_S01E02_S01E02
click.echo(f"targetPath={targetPath}")
#TODO: target extension anpassen
ctx.obj['logger'].info(f"Creating file {targetFilename}.webm")
fc.runJob(sourcePath,
targetPath,
@ -529,8 +519,7 @@ def convert(ctx,
#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()
if ctx.obj['verbosity'] > 0:
click.echo(f"\nDONE\nTime elapsed {endTime - startTime}")
ctx.obj['logger'].info(f"\nDONE\nTime elapsed {endTime - startTime}")
if __name__ == '__main__':

@ -28,7 +28,6 @@ class AudioLayout(Enum):
return [a for a in AudioLayout if a.value['label'] == str(label)][0]
except:
raise click.ClickException('fromLabel failed')
return AudioLayout.LAYOUT_UNDEFINED
@staticmethod
@ -36,7 +35,6 @@ class AudioLayout(Enum):
try:
return [a for a in AudioLayout if a.value['index'] == int(index)][0]
except:
raise click.ClickException('fromIndex failed')
return AudioLayout.LAYOUT_UNDEFINED
@staticmethod

@ -43,6 +43,7 @@ class FfxController():
CHANNEL_MAP_5_1 = 'FL-FL|FR-FR|FC-FC|LFE-LFE|SL-BL|SR-BR:5.1'
SIGNATURE_TAGS = {'EDITED_WITH': 'FFX'}
def __init__(self,
context : dict,
@ -214,7 +215,15 @@ class FfxController():
metadataTokens = []
for tagKey, tagValue in self.__targetMediaDescriptor.getTags().items():
mediaTags = self.__targetMediaDescriptor.getTags()
if (not 'no_signature' in self.__context.keys()
or not self.__context['no_signature']):
outputMediaTags = mediaTags | FfxController.SIGNATURE_TAGS
else:
outputMediaTags = mediaTags
for tagKey, tagValue in outputMediaTags.items():
metadataTokens += [f"-metadata:g",
f"{tagKey}={tagValue}"]

@ -229,7 +229,7 @@ class FileProperties():
# targetFilenameExtension = FfxController.DEFAULT_FILE_EXTENSION if extension is None else str(extension)
click.echo(f"assembleTargetFileBasename(): label={label} is {'truthy' if label else 'falsy'}")
self.__logger.debug(f"assembleTargetFileBasename(): label={label} is {'truthy' if label else 'falsy'}")
if label:
@ -256,7 +256,6 @@ class FileProperties():
targetFilename = '_'.join(targetFilenameTokens)
#self.__logger.debug(f"assembleTargetFileBasename(): Target filename: {targetFilename}")
click.echo(f"assembleTargetFileBasename(): Target filename: {targetFilename}")
self.__logger.debug(f"assembleTargetFileBasename(): Target filename: {targetFilename}")
return targetFilename

@ -57,5 +57,11 @@ def filterFilename(fileName: str) -> str:
"""This filter replaces charactes from TMDB responses with characters
less problemating when using in filenames or removes them"""
# This appears in TMDB episode names
fileName = str(fileName).replace(' (*)', '')
fileName = str(fileName).replace('(*)', '')
fileName = str(fileName).replace(':', ';')
return fileName
fileName = str(fileName).replace('*', '')
return fileName.strip()

@ -102,7 +102,8 @@ class Scenario1(Scenario):
commandSequence += ['convert',
mediaFilePath,
'--no-prompt']
'--no-prompt',
'--no-signature']
if variantFilenameLabel:
commandSequence += ['--label', variantFilenameLabel]

@ -94,7 +94,8 @@ class Scenario2(Scenario):
commandSequence += ['convert',
mediaFilePath,
'--no-prompt']
'--no-prompt',
'--no-signature']
if not testContext['use_jellyfin']:
commandSequence += ['--no-jellyfin']

@ -180,7 +180,7 @@ class Scenario4(Scenario):
'convert']
commandSequence += [tfo['filename'] for tfo in testFileList]
commandSequence += ['--no-prompt']
commandSequence += ['--no-prompt', '--no-signature']
if not testContext['use_jellyfin']:
commandSequence += ['--no-jellyfin']

Loading…
Cancel
Save