#393 CLI-Overrides
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,4 +1,4 @@
|
|||||||
__pycache__
|
__pycache__
|
||||||
junk/
|
junk/
|
||||||
.vscode/launch.json
|
.vscode
|
||||||
.ipynb_checkpoints/
|
.ipynb_checkpoints/
|
||||||
|
|||||||
131
bin/ffx.py
131
bin/ffx.py
@@ -17,6 +17,7 @@ from ffx.track_descriptor import TrackDescriptor
|
|||||||
from ffx.track_type import TrackType
|
from ffx.track_type import TrackType
|
||||||
from ffx.video_encoder import VideoEncoder
|
from ffx.video_encoder import VideoEncoder
|
||||||
from ffx.track_disposition import TrackDisposition
|
from ffx.track_disposition import TrackDisposition
|
||||||
|
from ffx.nlmeans_controller import NlmeansController
|
||||||
|
|
||||||
from ffx.process import executeProcess
|
from ffx.process import executeProcess
|
||||||
from ffx.helper import filterFilename
|
from ffx.helper import filterFilename
|
||||||
@@ -262,38 +263,42 @@ def checkUniqueDispositions(context, mediaDescriptor: MediaDescriptor):
|
|||||||
|
|
||||||
@click.option('-l', '--label', type=str, default='', help='Label to be used as filename prefix')
|
@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}")
|
@click.option('-v', '--video-encoder', type=str, default=FfxController.DEFAULT_VIDEO_ENCODER, help=f"Target video encoder (vp9 or av1)", show_default=True)
|
||||||
|
|
||||||
@click.option('-q', '--quality', type=str, default=DEFAULT_QUALITY, help=f"Quality settings to be used with VP9 encoder (default: {DEFAULT_QUALITY})")
|
@click.option('-q', '--quality', type=str, default=DEFAULT_QUALITY, help=f"Quality settings to be used with VP9 encoder", show_default=True)
|
||||||
@click.option('-p', '--preset', type=str, default=DEFAULT_AV1_PRESET, help=f"Quality preset to be used with AV1 encoder (default: {DEFAULT_AV1_PRESET})")
|
@click.option('-p', '--preset', type=str, default=DEFAULT_AV1_PRESET, help=f"Quality preset to be used with AV1 encoder", show_default=True)
|
||||||
|
|
||||||
@click.option('-a', '--stereo-bitrate', type=int, default=DEFAULT_STEREO_BANDWIDTH, help=f"Bitrate in kbit/s to be used to encode stereo audio streams (default: {DEFAULT_STEREO_BANDWIDTH})")
|
@click.option('-a', '--stereo-bitrate', type=int, default=DEFAULT_STEREO_BANDWIDTH, help=f"Bitrate in kbit/s to be used to encode stereo audio streams", show_default=True)
|
||||||
@click.option('--ac3', type=int, default=DEFAULT_AC3_BANDWIDTH, help=f"Bitrate in kbit/s to be used to encode 5.1 audio streams (default: {DEFAULT_AC3_BANDWIDTH})")
|
@click.option('--ac3', type=int, default=DEFAULT_AC3_BANDWIDTH, help=f"Bitrate in kbit/s to be used to encode 5.1 audio streams", show_default=True)
|
||||||
@click.option('--dts', type=int, default=DEFAULT_DTS_BANDWIDTH, help=f"Bitrate in kbit/s to be used to encode 6.1 audio streams (default: {DEFAULT_DTS_BANDWIDTH})")
|
@click.option('--dts', type=int, default=DEFAULT_DTS_BANDWIDTH, help=f"Bitrate in kbit/s to be used to encode 6.1 audio streams", show_default=True)
|
||||||
|
|
||||||
@click.option('--subtitle-directory', type=str, default='', help='Load subtitles from here')
|
@click.option('--subtitle-directory', type=str, default='', help='Load subtitles from here')
|
||||||
@click.option('--subtitle-prefix', type=str, default='', help='Subtitle filename prefix')
|
@click.option('--subtitle-prefix', type=str, default='', help='Subtitle filename prefix')
|
||||||
|
|
||||||
|
@click.option('--language', type=str, multiple=True, help='Set stream language. Use format <stream index>:<3 letter iso code>')
|
||||||
|
@click.option('--title', type=str, multiple=True, help='Set stream title. Use format <stream index>:<title>')
|
||||||
|
|
||||||
@click.option('--audio-language', type=str, multiple=True, help='Audio stream language(s)')
|
@click.option('--default-video', type=int, default=-1, help='Index of default video stream')
|
||||||
@click.option('--audio-title', type=str, multiple=True, help='Audio stream title(s)')
|
@click.option('--forced-video', type=int, default=-1, help='Index of forced video stream')
|
||||||
|
|
||||||
@click.option('--default-audio', type=int, default=-1, help='Index of default audio stream')
|
@click.option('--default-audio', type=int, default=-1, help='Index of default audio stream')
|
||||||
@click.option('--forced-audio', type=int, default=-1, help='Index of forced audio stream')
|
@click.option('--forced-audio', type=int, default=-1, help='Index of forced audio stream')
|
||||||
|
|
||||||
|
|
||||||
@click.option('--subtitle-language', type=str, multiple=True, help='Subtitle stream language(s)')
|
|
||||||
@click.option('--subtitle-title', type=str, multiple=True, help='Subtitle stream title(s)')
|
|
||||||
|
|
||||||
@click.option('--default-subtitle', type=int, default=-1, help='Index of default subtitle stream')
|
@click.option('--default-subtitle', type=int, default=-1, help='Index of default subtitle stream')
|
||||||
@click.option('--forced-subtitle', type=int, default=-1, help='Index of forced subtitle stream') # (including default audio stream tag)
|
@click.option('--forced-subtitle', type=int, default=-1, help='Index of forced subtitle stream')
|
||||||
|
|
||||||
|
@click.option('--rearrange-streams', type=str, default="", help='Rearrange output streams order. Use format comma separated integers')
|
||||||
|
|
||||||
@click.option("--crop", is_flag=False, flag_value="default", default="none")
|
@click.option("--crop", is_flag=False, flag_value="default", default="none")
|
||||||
|
|
||||||
@click.option("--output-directory", type=str, default='')
|
@click.option("--output-directory", type=str, default='')
|
||||||
|
|
||||||
@click.option("--denoise", is_flag=True, default=False)
|
@click.option("--denoise", is_flag=False, flag_value="default", default="none")
|
||||||
|
@click.option("--denoise-use-hw", is_flag=True, default=False)
|
||||||
|
@click.option('--denoise-strength', type=str, default='', help='Denoising strength, more blurring vs more details.')
|
||||||
|
@click.option('--denoise-patch-size', type=str, default='', help='Subimage size to apply filtering on luminosity plane. Reduces broader noise patterns but costly.')
|
||||||
|
@click.option('--denoise-chroma-patch-size', type=str, default='', help='Subimage size to apply filtering on chroma planes.')
|
||||||
|
@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("--no-tmdb", is_flag=True, default=False)
|
@click.option("--no-tmdb", is_flag=True, default=False)
|
||||||
# @click.option("--no-jellyfin", is_flag=True, default=False)
|
# @click.option("--no-jellyfin", is_flag=True, default=False)
|
||||||
@@ -318,19 +323,29 @@ def convert(ctx,
|
|||||||
subtitle_directory,
|
subtitle_directory,
|
||||||
subtitle_prefix,
|
subtitle_prefix,
|
||||||
|
|
||||||
audio_language,
|
language,
|
||||||
audio_title,
|
title,
|
||||||
|
|
||||||
|
default_video,
|
||||||
|
forced_video,
|
||||||
default_audio,
|
default_audio,
|
||||||
forced_audio,
|
forced_audio,
|
||||||
|
|
||||||
subtitle_language,
|
|
||||||
subtitle_title,
|
|
||||||
default_subtitle,
|
default_subtitle,
|
||||||
forced_subtitle,
|
forced_subtitle,
|
||||||
|
|
||||||
|
rearrange_streams,
|
||||||
|
|
||||||
crop,
|
crop,
|
||||||
output_directory,
|
output_directory,
|
||||||
|
|
||||||
denoise,
|
denoise,
|
||||||
|
denoise_use_hw,
|
||||||
|
denoise_strength,
|
||||||
|
denoise_patch_size,
|
||||||
|
denoise_chroma_patch_size,
|
||||||
|
denoise_research_window,
|
||||||
|
denoise_chroma_research_window,
|
||||||
|
|
||||||
no_tmdb,
|
no_tmdb,
|
||||||
# no_jellyfin,
|
# no_jellyfin,
|
||||||
no_pattern,
|
no_pattern,
|
||||||
@@ -360,12 +375,70 @@ def convert(ctx,
|
|||||||
context['no_signature'] = no_signature
|
context['no_signature'] = no_signature
|
||||||
context['keep_mkvmerge_metadata'] = keep_mkvmerge_metadata
|
context['keep_mkvmerge_metadata'] = keep_mkvmerge_metadata
|
||||||
|
|
||||||
|
context['denoiser'] = NlmeansController(parameters = denoise,
|
||||||
|
strength = denoise_strength,
|
||||||
|
patchSize = denoise_patch_size,
|
||||||
|
chromaPatchSize = denoise_chroma_patch_size,
|
||||||
|
researchWindow = denoise_research_window,
|
||||||
|
chromaResearchWindow = denoise_chroma_research_window,
|
||||||
|
useHardware = denoise_use_hw)
|
||||||
|
|
||||||
context['import_subtitles'] = (subtitle_directory and subtitle_prefix)
|
context['import_subtitles'] = (subtitle_directory and subtitle_prefix)
|
||||||
if context['import_subtitles']:
|
if context['import_subtitles']:
|
||||||
context['subtitle_directory'] = subtitle_directory
|
context['subtitle_directory'] = subtitle_directory
|
||||||
context['subtitle_prefix'] = subtitle_prefix
|
context['subtitle_prefix'] = subtitle_prefix
|
||||||
|
|
||||||
|
|
||||||
|
cliOverrides = {}
|
||||||
|
|
||||||
|
if language:
|
||||||
|
cliOverrides['languages'] = {}
|
||||||
|
for overLang in language:
|
||||||
|
olTokens = overLang.split(':')
|
||||||
|
if len(olTokens) == 2:
|
||||||
|
try:
|
||||||
|
cliOverrides['languages'][int(olTokens[0])] = olTokens[1]
|
||||||
|
except ValueError:
|
||||||
|
ctx.obj['logger'].warning(f"Ignoring non-integer language index {olTokens[0]}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if title:
|
||||||
|
cliOverrides['titles'] = {}
|
||||||
|
for overTitle in title:
|
||||||
|
otTokens = overTitle.split(':')
|
||||||
|
if len(otTokens) == 2:
|
||||||
|
try:
|
||||||
|
cliOverrides['titles'][int(otTokens[0])] = otTokens[1]
|
||||||
|
except ValueError:
|
||||||
|
ctx.obj['logger'].warning(f"Ignoring non-integer title index {otTokens[0]}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if default_video != -1:
|
||||||
|
cliOverrides['default_video'] = default_video
|
||||||
|
if forced_video != -1:
|
||||||
|
cliOverrides['forced_video'] = forced_video
|
||||||
|
if default_audio != -1:
|
||||||
|
cliOverrides['default_audio'] = default_audio
|
||||||
|
if forced_audio != -1:
|
||||||
|
cliOverrides['forced_audio'] = forced_audio
|
||||||
|
if default_subtitle != -1:
|
||||||
|
cliOverrides['default_subtitle'] = default_subtitle
|
||||||
|
if forced_subtitle != -1:
|
||||||
|
cliOverrides['forced_subtitle'] = forced_subtitle
|
||||||
|
|
||||||
|
if cliOverrides:
|
||||||
|
context['overrides'] = cliOverrides
|
||||||
|
|
||||||
|
|
||||||
|
if rearrange_streams:
|
||||||
|
try:
|
||||||
|
cliOverrides['stream_order'] = [int(si) for si in rearrange_streams.split(",")]
|
||||||
|
except ValueError as ve:
|
||||||
|
errorMessage = "Non-integer in rearrange stream parameter"
|
||||||
|
ctx.obj['logger'].error(errorMessage)
|
||||||
|
raise click.Abort()
|
||||||
|
|
||||||
|
|
||||||
ctx.obj['logger'].debug(f"\nVideo encoder: {video_encoder}")
|
ctx.obj['logger'].debug(f"\nVideo encoder: {video_encoder}")
|
||||||
|
|
||||||
qualityTokens = quality.split(',')
|
qualityTokens = quality.split(',')
|
||||||
@@ -437,10 +510,10 @@ def convert(ctx,
|
|||||||
mediaFileProperties.getSeason(),
|
mediaFileProperties.getSeason(),
|
||||||
mediaFileProperties.getEpisode())
|
mediaFileProperties.getEpisode())
|
||||||
|
|
||||||
# if context['use_jellyfin']:
|
if cliOverrides:
|
||||||
# # Reorder subtracks in types with default the last, then make subindices flat again
|
sourceMediaDescriptor.applyOverrides(cliOverrides)
|
||||||
# sourceMediaDescriptor.applyJellyfinOrder()
|
|
||||||
|
|
||||||
|
#YOLO
|
||||||
fc = FfxController(context, sourceMediaDescriptor)
|
fc = FfxController(context, sourceMediaDescriptor)
|
||||||
|
|
||||||
|
|
||||||
@@ -483,9 +556,10 @@ def convert(ctx,
|
|||||||
|
|
||||||
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"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
|
if cliOverrides:
|
||||||
# targetMediaDescriptor.applyJellyfinOrder()
|
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"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()]}")
|
||||||
|
|
||||||
@@ -525,8 +599,7 @@ def convert(ctx,
|
|||||||
targetPath,
|
targetPath,
|
||||||
context['video_encoder'],
|
context['video_encoder'],
|
||||||
q,
|
q,
|
||||||
preset,
|
preset)
|
||||||
denoise)
|
|
||||||
|
|
||||||
#TODO: 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)
|
||||||
|
|
||||||
|
|||||||
@@ -112,44 +112,6 @@ class FfxController():
|
|||||||
return ['-ss', str(cropStart), '-t', str(cropLength)]
|
return ['-ss', str(cropStart), '-t', str(cropLength)]
|
||||||
|
|
||||||
|
|
||||||
def generateDenoiseTokens(self,
|
|
||||||
strength: float = 2.8,
|
|
||||||
patchSize: int = 12,
|
|
||||||
chromaPatchSize: int = 8,
|
|
||||||
researchWindow: int = 22,
|
|
||||||
chromaResearchWindow: int= 16,
|
|
||||||
useHardware: bool = False):
|
|
||||||
"""
|
|
||||||
s: double
|
|
||||||
|
|
||||||
Denoising strength (from 1 to 30) (default 1)
|
|
||||||
Trade-off between noise removal and detail retention. Comparable to gaussian sigma.
|
|
||||||
|
|
||||||
p: int patch size (from 0 to 99) (default 7)
|
|
||||||
|
|
||||||
Catches larger areas reducing broader noise patterns, but costly
|
|
||||||
|
|
||||||
pc: int patch size for chroma planes (from 0 to 99) (default 0)
|
|
||||||
|
|
||||||
r: int research window (from 0 to 99) (default 15)
|
|
||||||
|
|
||||||
Range to search for comparable patches.
|
|
||||||
Better filtering but costly
|
|
||||||
|
|
||||||
rc: int research window for chroma planes (from 0 to 99) (default 0)
|
|
||||||
|
|
||||||
Good values to denoise film grain that was subobtimally encoded:
|
|
||||||
strength: float = 2.8
|
|
||||||
patchSize: int = 12
|
|
||||||
chromaPatchSize: int = 8
|
|
||||||
researchWindow: int = 22
|
|
||||||
chromaResearchWindow: int= 16
|
|
||||||
"""
|
|
||||||
|
|
||||||
filterName = 'nlmeans_opencl' if useHardware else 'nlmeans'
|
|
||||||
return ['-vf', f"{filterName}=s={strength}:p={patchSize}:pc={chromaPatchSize}:r={researchWindow}:rc={chromaResearchWindow}"]
|
|
||||||
|
|
||||||
|
|
||||||
def generateOutputTokens(self, filepath, format, ext):
|
def generateOutputTokens(self, filepath, format, ext):
|
||||||
outputFilePath = f"{filepath}.{ext}"
|
outputFilePath = f"{filepath}.{ext}"
|
||||||
return ['-f', format, outputFilePath]
|
return ['-f', format, outputFilePath]
|
||||||
@@ -300,10 +262,8 @@ class FfxController():
|
|||||||
targetPath,
|
targetPath,
|
||||||
videoEncoder: VideoEncoder = VideoEncoder.VP9,
|
videoEncoder: VideoEncoder = VideoEncoder.VP9,
|
||||||
quality: int = DEFAULT_QUALITY,
|
quality: int = DEFAULT_QUALITY,
|
||||||
preset: int = DEFAULT_AV1_PRESET,
|
preset: int = DEFAULT_AV1_PRESET):
|
||||||
denoise: bool = False):
|
|
||||||
|
|
||||||
# self.__targetMediaDescriptor order OK
|
|
||||||
|
|
||||||
commandTokens = FfxController.COMMAND_TOKENS + ['-i', sourcePath]
|
commandTokens = FfxController.COMMAND_TOKENS + ['-i', sourcePath]
|
||||||
|
|
||||||
@@ -314,11 +274,12 @@ class FfxController():
|
|||||||
+ self.__targetMediaDescriptor.getInputMappingTokens()
|
+ self.__targetMediaDescriptor.getInputMappingTokens()
|
||||||
+ self.generateDispositionTokens())
|
+ self.generateDispositionTokens())
|
||||||
|
|
||||||
if not self.__sourceMediaDescriptor is None:
|
if not self.__sourceMediaDescriptor is None or 'overrides' in self.__context.keys():
|
||||||
commandSequence += self.generateMetadataTokens()
|
commandSequence += self.generateMetadataTokens()
|
||||||
|
|
||||||
if denoise:
|
# if denoise:
|
||||||
commandSequence += self.generateDenoiseTokens()
|
# commandSequence += self.generateDenoiseTokens()
|
||||||
|
commandSequence1 += self.__context['denoiser'].generateDenoiseTokens()
|
||||||
|
|
||||||
commandSequence += (self.generateAudioEncodingTokens()
|
commandSequence += (self.generateAudioEncodingTokens()
|
||||||
+ self.generateAV1Tokens(int(quality), int(preset))
|
+ self.generateAV1Tokens(int(quality), int(preset))
|
||||||
@@ -361,11 +322,12 @@ class FfxController():
|
|||||||
+ self.__targetMediaDescriptor.getInputMappingTokens()
|
+ self.__targetMediaDescriptor.getInputMappingTokens()
|
||||||
+ self.generateDispositionTokens())
|
+ self.generateDispositionTokens())
|
||||||
|
|
||||||
if not self.__sourceMediaDescriptor is None:
|
if not self.__sourceMediaDescriptor is None or 'overrides' in self.__context.keys():
|
||||||
commandSequence2 += self.generateMetadataTokens()
|
commandSequence2 += self.generateMetadataTokens()
|
||||||
|
|
||||||
if denoise:
|
# if denoise:
|
||||||
commandSequence2 += self.generateDenoiseTokens()
|
# commandSequence2 += self.generateDenoiseTokens()
|
||||||
|
commandSequence2 += self.__context['denoiser'].generateDenoiseTokens()
|
||||||
|
|
||||||
commandSequence2 += self.generateVP9Pass2Tokens(int(quality)) + self.generateAudioEncodingTokens()
|
commandSequence2 += self.generateVP9Pass2Tokens(int(quality)) + self.generateAudioEncodingTokens()
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import os, re, click, logging
|
|||||||
from typing import List, Self
|
from typing import List, Self
|
||||||
|
|
||||||
from ffx.track_type import TrackType
|
from ffx.track_type import TrackType
|
||||||
|
from ffx.iso_language import IsoLanguage
|
||||||
|
|
||||||
from ffx.track_disposition import TrackDisposition
|
from ffx.track_disposition import TrackDisposition
|
||||||
|
|
||||||
from ffx.track_descriptor import TrackDescriptor
|
from ffx.track_descriptor import TrackDescriptor
|
||||||
@@ -70,17 +72,42 @@ class MediaDescriptor:
|
|||||||
else:
|
else:
|
||||||
self.__trackDescriptors = []
|
self.__trackDescriptors = []
|
||||||
|
|
||||||
# if MediaDescriptor.JELLYFIN_ORDER_FLAG_KEY in kwargs.keys():
|
#TODO: to be removed
|
||||||
# if type(kwargs[MediaDescriptor.JELLYFIN_ORDER_FLAG_KEY]) is not bool:
|
|
||||||
# raise TypeError(
|
|
||||||
# f"MediaDescriptor.__init__(): Argument {MediaDescriptor.JELLYFIN_ORDER_FLAG_KEY} is required to be of type bool"
|
|
||||||
# )
|
|
||||||
# self.__jellyfinOrder = kwargs[MediaDescriptor.JELLYFIN_ORDER_FLAG_KEY]
|
|
||||||
# else:
|
|
||||||
# self.__jellyfinOrder = False
|
|
||||||
# self.__jellyfinOrder = self.__context['use_jellyfin'] if 'use_jellyfin' in self.__context.keys() else False
|
|
||||||
self.__jellyfinOrder = False
|
self.__jellyfinOrder = False
|
||||||
|
|
||||||
|
def setTrackLanguage(self, language: str, index: int, trackType: TrackType = None):
|
||||||
|
|
||||||
|
trackLanguage = IsoLanguage.findThreeLetter(language)
|
||||||
|
if trackLanguage == IsoLanguage.UNDEFINED:
|
||||||
|
self.__logger.warning('MediaDescriptor.setTrackLanguage(): Parameter language does not contain a registered '
|
||||||
|
+ f"ISO 639 3-letter language code, skipping to set language for"
|
||||||
|
+ str('' if trackType is None else trackType.label()) + f"track {index}")
|
||||||
|
|
||||||
|
trackList = self.getTrackDescriptors(trackType=trackType)
|
||||||
|
|
||||||
|
if index < 0 or index > len(trackList) - 1:
|
||||||
|
self.__logger.warning(f"MediaDescriptor.setTrackLanguage(): Parameter index ({index}) is "
|
||||||
|
+ f"out of range of {'' if trackType is None else trackType.label()}track list")
|
||||||
|
|
||||||
|
td: TrackDescriptor = trackList[index]
|
||||||
|
td.setLanguage(trackLanguage)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def setTrackTitle(self, title: str, index: int, trackType: TrackType = None):
|
||||||
|
|
||||||
|
trackList = self.getTrackDescriptors(trackType=trackType)
|
||||||
|
|
||||||
|
if index < 0 or index > len(trackList) - 1:
|
||||||
|
self.__logger.error(f"MediaDescriptor.setTrackTitle(): Parameter index ({index}) is "
|
||||||
|
+ f"out of range of {'' if trackType is None else trackType.label()}track list")
|
||||||
|
raise click.Abort()
|
||||||
|
|
||||||
|
td: TrackDescriptor = trackList[index]
|
||||||
|
td.setTitle(title)
|
||||||
|
|
||||||
|
|
||||||
def setDefaultSubTrack(self, trackType: TrackType, subIndex: int):
|
def setDefaultSubTrack(self, trackType: TrackType, subIndex: int):
|
||||||
for t in self.getAllTrackDescriptors():
|
for t in self.getAllTrackDescriptors():
|
||||||
if t.getType() == trackType:
|
if t.getType() == trackType:
|
||||||
@@ -123,6 +150,47 @@ class MediaDescriptor:
|
|||||||
raise ValueError('Multiple streams originating from the same source stream')
|
raise ValueError('Multiple streams originating from the same source stream')
|
||||||
|
|
||||||
|
|
||||||
|
def applyOverrides(self, overrides: dict):
|
||||||
|
|
||||||
|
if 'languages' in overrides.keys():
|
||||||
|
for trackIndex in overrides['languages'].keys():
|
||||||
|
self.setTrackLanguage(overrides['languages'][trackIndex], trackIndex)
|
||||||
|
|
||||||
|
if 'titles' in overrides.keys():
|
||||||
|
for trackIndex in overrides['titles'].keys():
|
||||||
|
self.setTrackTitle(overrides['titles'][trackIndex], trackIndex)
|
||||||
|
|
||||||
|
if 'forced_video' in overrides.keys():
|
||||||
|
sti = int(overrides['forced_video'])
|
||||||
|
self.setForcedSubTrack(TrackType.VIDEO, sti)
|
||||||
|
self.setDefaultSubTrack(TrackType.VIDEO, sti)
|
||||||
|
|
||||||
|
elif 'default_video' in overrides.keys():
|
||||||
|
sti = int(overrides['default_video'])
|
||||||
|
self.setDefaultSubTrack(TrackType.VIDEO, sti)
|
||||||
|
|
||||||
|
if 'forced_audio' in overrides.keys():
|
||||||
|
sti = int(overrides['forced_audio'])
|
||||||
|
self.setForcedSubTrack(TrackType.AUDIO, sti)
|
||||||
|
self.setDefaultSubTrack(TrackType.AUDIO, sti)
|
||||||
|
|
||||||
|
elif 'default_audio' in overrides.keys():
|
||||||
|
sti = int(overrides['default_audio'])
|
||||||
|
self.setDefaultSubTrack(TrackType.AUDIO, sti)
|
||||||
|
|
||||||
|
if 'forced_subtitle' in overrides.keys():
|
||||||
|
sti = int(overrides['forced_subtitle'])
|
||||||
|
self.setForcedSubTrack(TrackType.SUBTITLE, sti)
|
||||||
|
self.setDefaultSubTrack(TrackType.SUBTITLE, sti)
|
||||||
|
|
||||||
|
elif 'default_subtitle' in overrides.keys():
|
||||||
|
sti = int(overrides['default_subtitle'])
|
||||||
|
self.setDefaultSubTrack(TrackType.SUBTITLE, sti)
|
||||||
|
|
||||||
|
if 'stream_order' in overrides.keys():
|
||||||
|
self.rearrangeTrackDescriptors(overrides['stream_order'])
|
||||||
|
|
||||||
|
|
||||||
def applySourceIndices(self, sourceMediaDescriptor: Self):
|
def applySourceIndices(self, sourceMediaDescriptor: Self):
|
||||||
sourceTrackDescriptors = sourceMediaDescriptor.getAllTrackDescriptors()
|
sourceTrackDescriptors = sourceMediaDescriptor.getAllTrackDescriptors()
|
||||||
|
|
||||||
@@ -131,51 +199,16 @@ class MediaDescriptor:
|
|||||||
raise ValueError('MediaDescriptor.applySourceIndices (): Number of track descriptors does not match')
|
raise ValueError('MediaDescriptor.applySourceIndices (): Number of track descriptors does not match')
|
||||||
|
|
||||||
for trackIndex in range(numTrackDescriptors):
|
for trackIndex in range(numTrackDescriptors):
|
||||||
# click.echo(f"{trackIndex} -> {sourceTrackDescriptors[trackIndex].getSourceIndex()}")
|
|
||||||
self.__trackDescriptors[trackIndex].setSourceIndex(sourceTrackDescriptors[trackIndex].getSourceIndex())
|
self.__trackDescriptors[trackIndex].setSourceIndex(sourceTrackDescriptors[trackIndex].getSourceIndex())
|
||||||
|
|
||||||
|
|
||||||
def applyJellyfinOrder(self):
|
def rearrangeTrackDescriptors(self, newOrder: List[int]):
|
||||||
"""Reorder subtracks in types with default the last, then make subindices flat again"""
|
if len(newOrder) != len(self.__trackDescriptors):
|
||||||
|
raise ValueError('Length of list with reordered indices does not match number of track descriptors')
|
||||||
# videoTracks = self.sortSubIndices(self.getVideoTracks())
|
reorderedTrackDescriptors = {}
|
||||||
# audioTracks = self.sortSubIndices(self.getAudioTracks())
|
for oldIndex in newOrder:
|
||||||
# subtitleTracks = self.sortSubIndices(self.getSubtitleTracks())
|
reorderedTrackDescriptors.append(self.__trackDescriptors[oldIndex])
|
||||||
|
self.__trackDescriptors = reorderedTrackDescriptors
|
||||||
self.checkConfiguration()
|
|
||||||
|
|
||||||
# from self.__trackDescriptors
|
|
||||||
videoTracks = self.getVideoTracks()
|
|
||||||
audioTracks = self.getAudioTracks()
|
|
||||||
subtitleTracks = self.getSubtitleTracks()
|
|
||||||
|
|
||||||
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)
|
|
||||||
numVideoTracks = len(videoTracks)
|
|
||||||
for vIndex in range(numVideoTracks):
|
|
||||||
videoTracks[vIndex].setDispositionFlag(TrackDisposition.DEFAULT,
|
|
||||||
vIndex == numVideoTracks - 1)
|
|
||||||
if defaultAudioTracks:
|
|
||||||
audioTracks.append(audioTracks.pop(audioTracks.index(defaultAudioTracks[0])))
|
|
||||||
#self.sortSubIndices(audioTracks)
|
|
||||||
numAudioTracks = len(audioTracks)
|
|
||||||
for aIndex in range(numAudioTracks):
|
|
||||||
audioTracks[aIndex].setDispositionFlag(TrackDisposition.DEFAULT,
|
|
||||||
aIndex == numAudioTracks - 1)
|
|
||||||
if defaultSubtitleTracks:
|
|
||||||
subtitleTracks.append(subtitleTracks.pop(subtitleTracks.index(defaultSubtitleTracks[0])))
|
|
||||||
#self.sortSubIndices(subtitleTracks)
|
|
||||||
numSubtitleTracks = len(subtitleTracks)
|
|
||||||
for sIndex in range(numSubtitleTracks):
|
|
||||||
subtitleTracks[sIndex].setDispositionFlag(TrackDisposition.DEFAULT,
|
|
||||||
sIndex == numSubtitleTracks - 1)
|
|
||||||
self.__trackDescriptors = videoTracks + audioTracks + subtitleTracks
|
|
||||||
#self.sortIndices(self.__trackDescriptors)
|
|
||||||
self.reindexSubIndices()
|
self.reindexSubIndices()
|
||||||
self.reindexIndices()
|
self.reindexIndices()
|
||||||
|
|
||||||
@@ -254,18 +287,30 @@ class MediaDescriptor:
|
|||||||
tdList[trackIndex].setIndex(trackIndex)
|
tdList[trackIndex].setIndex(trackIndex)
|
||||||
|
|
||||||
|
|
||||||
def getAllTrackDescriptors(self) -> List[TrackDescriptor]:
|
def getAllTrackDescriptors(self):
|
||||||
|
"""Returns all track descriptors sorted by type: video, audio then subtitles"""
|
||||||
return self.getVideoTracks() + self.getAudioTracks() + self.getSubtitleTracks()
|
return self.getVideoTracks() + self.getAudioTracks() + self.getSubtitleTracks()
|
||||||
|
|
||||||
|
|
||||||
|
def getTrackDescriptors(self,
|
||||||
|
trackType: TrackType = None) -> List[TrackDescriptor]:
|
||||||
|
|
||||||
|
if trackType is None:
|
||||||
|
return self.__trackDescriptors
|
||||||
|
|
||||||
|
descriptorList = []
|
||||||
|
for td in self.__trackDescriptors:
|
||||||
|
if td.getType() == trackType:
|
||||||
|
descriptorList.append(td)
|
||||||
|
|
||||||
|
return descriptorList
|
||||||
|
|
||||||
|
|
||||||
def getVideoTracks(self) -> List[TrackDescriptor]:
|
def getVideoTracks(self) -> List[TrackDescriptor]:
|
||||||
return [
|
return [v for v in self.__trackDescriptors if v.getType() == TrackType.VIDEO]
|
||||||
v for v in self.__trackDescriptors if v.getType() == TrackType.VIDEO
|
|
||||||
]
|
|
||||||
|
|
||||||
def getAudioTracks(self) -> List[TrackDescriptor]:
|
def getAudioTracks(self) -> List[TrackDescriptor]:
|
||||||
return [
|
return [a for a in self.__trackDescriptors if a.getType() == TrackType.AUDIO]
|
||||||
a for a in self.__trackDescriptors if a.getType() == TrackType.AUDIO
|
|
||||||
]
|
|
||||||
|
|
||||||
def getSubtitleTracks(self) -> List[TrackDescriptor]:
|
def getSubtitleTracks(self) -> List[TrackDescriptor]:
|
||||||
return [
|
return [
|
||||||
@@ -278,10 +323,8 @@ class MediaDescriptor:
|
|||||||
def compare(self, vsMediaDescriptor: Self):
|
def compare(self, vsMediaDescriptor: Self):
|
||||||
|
|
||||||
if not isinstance(vsMediaDescriptor, self.__class__):
|
if not isinstance(vsMediaDescriptor, self.__class__):
|
||||||
errorMessage = f"MediaDescriptor.compare(): Argument is required to be of type {self.__class__}"
|
self.__logger.error(f"MediaDescriptor.compare(): Argument is required to be of type {self.__class__}")
|
||||||
self.__logger.error(errorMessage)
|
raise click.Abort()
|
||||||
# raise click.ClickException(errorMessage)
|
|
||||||
click.Abort()
|
|
||||||
|
|
||||||
vsTags = vsMediaDescriptor.getTags()
|
vsTags = vsMediaDescriptor.getTags()
|
||||||
tags = self.getTags()
|
tags = self.getTags()
|
||||||
@@ -357,10 +400,8 @@ class MediaDescriptor:
|
|||||||
|
|
||||||
def getImportFileTokens(self, use_sub_index: bool = True):
|
def getImportFileTokens(self, use_sub_index: bool = True):
|
||||||
|
|
||||||
# reorderedTrackDescriptors = self.getReorderedTrackDescriptors()
|
|
||||||
importFileTokens = []
|
importFileTokens = []
|
||||||
|
|
||||||
#for rtd in reorderedTrackDescriptors:
|
|
||||||
for td in self.__trackDescriptors:
|
for td in self.__trackDescriptors:
|
||||||
|
|
||||||
importedFilePath = td.getExternalSourceFilePath()
|
importedFilePath = td.getExternalSourceFilePath()
|
||||||
@@ -377,14 +418,6 @@ class MediaDescriptor:
|
|||||||
def getInputMappingTokens(self, use_sub_index: bool = True, only_video: bool = False):
|
def getInputMappingTokens(self, use_sub_index: bool = True, only_video: bool = False):
|
||||||
"""Tracks must be reordered for source index order"""
|
"""Tracks must be reordered for source index order"""
|
||||||
|
|
||||||
# sourceTrackDescriptorSubIndices = [self.__trackDescriptors[std.getSourceIndex()].getSubIndex()
|
|
||||||
# for std in self.__trackDescriptors]
|
|
||||||
|
|
||||||
# self.reindexSubIndices(trackDescriptors = sourceOrderTrackDescriptors)
|
|
||||||
# self.reindexIndices(trackDescriptors = sourceOrderTrackDescriptors)
|
|
||||||
|
|
||||||
# click.echo(sourceTrackDescriptorIndices)
|
|
||||||
|
|
||||||
inputMappingTokens = []
|
inputMappingTokens = []
|
||||||
|
|
||||||
filePointer = 1
|
filePointer = 1
|
||||||
@@ -467,17 +500,12 @@ class MediaDescriptor:
|
|||||||
|
|
||||||
availableFileSubtitleDescriptors = self.searchSubtitleFiles(searchDirectory, prefix)
|
availableFileSubtitleDescriptors = self.searchSubtitleFiles(searchDirectory, prefix)
|
||||||
|
|
||||||
# click.echo(f"availableFileSubtitleDescriptors: {availableFileSubtitleDescriptors}")
|
|
||||||
self.__logger.debug(f"importSubtitles(): availableFileSubtitleDescriptors: {availableFileSubtitleDescriptors}")
|
self.__logger.debug(f"importSubtitles(): availableFileSubtitleDescriptors: {availableFileSubtitleDescriptors}")
|
||||||
|
|
||||||
subtitleTracks = self.getSubtitleTracks()
|
subtitleTracks = self.getSubtitleTracks()
|
||||||
|
|
||||||
# click.echo(f"subtitleTracks: {[s.getIndex() for s in subtitleTracks]}")
|
|
||||||
self.__logger.debug(f"importSubtitles(): subtitleTracks: {[s.getIndex() for s in subtitleTracks]}")
|
self.__logger.debug(f"importSubtitles(): subtitleTracks: {[s.getIndex() for s in subtitleTracks]}")
|
||||||
|
|
||||||
# if len(availableFileSubtitleDescriptors) != len(subtitleTracks):
|
|
||||||
# raise click.ClickException(f"MediaDescriptor.importSubtitles(): Number if subtitle files not matching number of subtitle tracks")
|
|
||||||
|
|
||||||
matchingSubtitleFileDescriptors = (
|
matchingSubtitleFileDescriptors = (
|
||||||
sorted(
|
sorted(
|
||||||
[
|
[
|
||||||
@@ -491,9 +519,7 @@ class MediaDescriptor:
|
|||||||
else []
|
else []
|
||||||
)
|
)
|
||||||
|
|
||||||
# click.echo(f"matchingSubtitleFileDescriptors: {matchingSubtitleFileDescriptors}")
|
|
||||||
self.__logger.debug(f"importSubtitles(): matchingSubtitleFileDescriptors: {matchingSubtitleFileDescriptors}")
|
self.__logger.debug(f"importSubtitles(): matchingSubtitleFileDescriptors: {matchingSubtitleFileDescriptors}")
|
||||||
# click.echo(f"importSubtitles(): matchingSubtitleFileDescriptors: {matchingSubtitleFileDescriptors}")
|
|
||||||
|
|
||||||
for msfd in matchingSubtitleFileDescriptors:
|
for msfd in matchingSubtitleFileDescriptors:
|
||||||
matchingSubtitleTrackDescriptor = [s for s in subtitleTracks if s.getIndex() == msfd["index"]]
|
matchingSubtitleTrackDescriptor = [s for s in subtitleTracks if s.getIndex() == msfd["index"]]
|
||||||
|
|||||||
142
bin/ffx/nlmeans_controller.py
Normal file
142
bin/ffx/nlmeans_controller.py
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
class NlmeansController():
|
||||||
|
"""
|
||||||
|
s: double
|
||||||
|
|
||||||
|
Denoising strength (from 1 to 30) (default 1)
|
||||||
|
Trade-off between noise removal and detail retention. Comparable to gaussian sigma.
|
||||||
|
|
||||||
|
p: int patch size (from 0 to 99) (default 7)
|
||||||
|
|
||||||
|
Catches larger areas reducing broader noise patterns, but costly
|
||||||
|
|
||||||
|
pc: int patch size for chroma planes (from 0 to 99) (default 0)
|
||||||
|
|
||||||
|
r: int research window (from 0 to 99) (default 15)
|
||||||
|
|
||||||
|
Range to search for comparable patches.
|
||||||
|
Better filtering but costly
|
||||||
|
|
||||||
|
rc: int research window for chroma planes (from 0 to 99) (default 0)
|
||||||
|
|
||||||
|
Good values to denoise film grain that was subobtimally encoded:
|
||||||
|
strength: float = 2.8
|
||||||
|
patchSize: int = 12
|
||||||
|
chromaPatchSize: int = 8
|
||||||
|
researchWindow: int = 22
|
||||||
|
chromaResearchWindow: int= 16
|
||||||
|
"""
|
||||||
|
|
||||||
|
DEFAULT_STRENGTH: float = 2.8
|
||||||
|
DEFAULT_PATCH_SIZE: int = 13
|
||||||
|
DEFAULT_CHROMA_PATCH_SIZE: int = 9
|
||||||
|
DEFAULT_RESEARCH_WINDOW: int = 23
|
||||||
|
DEFAULT_CHROMA_RESEARCH_WINDOW: int= 17
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
parameters: str = "none",
|
||||||
|
strength: str = "",
|
||||||
|
patchSize: str = "",
|
||||||
|
chromaPatchSize: str = "",
|
||||||
|
researchWindow: str = "",
|
||||||
|
chromaResearchWindow: str = "",
|
||||||
|
useHardware: bool = False):
|
||||||
|
|
||||||
|
self.__isActive = (parameters != "none"
|
||||||
|
or strength
|
||||||
|
or patchSize
|
||||||
|
or chromaPatchSize
|
||||||
|
or researchWindow
|
||||||
|
or chromaResearchWindow)
|
||||||
|
self.__useHardware = useHardware
|
||||||
|
|
||||||
|
parameterTokens = parameters.split(',')
|
||||||
|
|
||||||
|
self.__strengthList = []
|
||||||
|
if strength:
|
||||||
|
strengthTokens = strength.split(',')
|
||||||
|
for st in strengthTokens:
|
||||||
|
try:
|
||||||
|
strengthValue = float(st)
|
||||||
|
except:
|
||||||
|
raise ValueError('NlmeansController: Strength value has to be of type float')
|
||||||
|
if strengthValue < 1.0 or strengthValue > 30.0:
|
||||||
|
raise ValueError('NlmeansController: Strength value has to be between 1.0 and 30.0')
|
||||||
|
self.__strengthList.append(strengthValue)
|
||||||
|
else:
|
||||||
|
self.__strengthList = [NlmeansController.DEFAULT_STRENGTH]
|
||||||
|
|
||||||
|
self.__patchSizeList = []
|
||||||
|
if patchSize:
|
||||||
|
patchSizeTokens = patchSize.split(',')
|
||||||
|
for pst in patchSizeTokens:
|
||||||
|
try:
|
||||||
|
patchSizeValue = int(pst)
|
||||||
|
except:
|
||||||
|
raise ValueError('NlmeansController: Patch size value has to be of type int')
|
||||||
|
if patchSizeValue < 0 or patchSizeValue > 99:
|
||||||
|
raise ValueError('NlmeansController: Patch size value has to be between 0 and 99')
|
||||||
|
if patchSizeValue % 2 == 0:
|
||||||
|
raise ValueError('NlmeansController: Patch size value has to an odd number')
|
||||||
|
self.__patchSizeList.append(patchSizeValue)
|
||||||
|
else:
|
||||||
|
self.__patchSizeList = [NlmeansController.DEFAULT_PATCH_SIZE]
|
||||||
|
|
||||||
|
self.__chromaPatchSizeList = []
|
||||||
|
if chromaPatchSize:
|
||||||
|
chromaPatchSizeTokens = chromaPatchSize.split(',')
|
||||||
|
for cpst in chromaPatchSizeTokens:
|
||||||
|
try:
|
||||||
|
chromaPatchSizeValue = int(pst)
|
||||||
|
except:
|
||||||
|
raise ValueError('NlmeansController: Chroma patch size value has to be of type int')
|
||||||
|
if chromaPatchSizeValue < 0 or chromaPatchSizeValue > 99:
|
||||||
|
raise ValueError('NlmeansController: Chroma patch value has to be between 0 and 99')
|
||||||
|
if chromaPatchSizeValue % 2 == 0:
|
||||||
|
raise ValueError('NlmeansController: Chroma patch value has to an odd number')
|
||||||
|
self.__chromaPatchSizeList.append(chromaPatchSizeValue)
|
||||||
|
else:
|
||||||
|
self.__chromaPatchSizeList = [NlmeansController.DEFAULT_CHROMA_PATCH_SIZE]
|
||||||
|
|
||||||
|
self.__researchWindowList = []
|
||||||
|
if researchWindow:
|
||||||
|
researchWindowTokens = researchWindow.split(',')
|
||||||
|
for rwt in researchWindowTokens:
|
||||||
|
try:
|
||||||
|
researchWindowValue = int(rwt)
|
||||||
|
except:
|
||||||
|
raise ValueError('NlmeansController: Research window value has to be of type int')
|
||||||
|
if researchWindowValue < 0 or researchWindowValue > 99:
|
||||||
|
raise ValueError('NlmeansController: Research window value has to be between 0 and 99')
|
||||||
|
if researchWindowValue % 2 == 0:
|
||||||
|
raise ValueError('NlmeansController: Research window value has to an odd number')
|
||||||
|
self.__researchWindowList.append(researchWindowValue)
|
||||||
|
else:
|
||||||
|
self.__researchWindowList = [NlmeansController.DEFAULT_RESEARCH_WINDOW]
|
||||||
|
|
||||||
|
self.__chromaResearchWindowList = []
|
||||||
|
if chromaResearchWindow:
|
||||||
|
chromaResearchWindowTokens = chromaResearchWindow.split(',')
|
||||||
|
for crwt in chromaResearchWindowTokens:
|
||||||
|
try:
|
||||||
|
chromaResearchWindowValue = int(crwt)
|
||||||
|
except:
|
||||||
|
raise ValueError('NlmeansController: Chroma research window value has to be of type int')
|
||||||
|
if chromaResearchWindowValue < 0 or chromaResearchWindowValue > 99:
|
||||||
|
raise ValueError('NlmeansController: Chroma research window value has to be between 0 and 99')
|
||||||
|
if chromaResearchWindowValue % 2 == 0:
|
||||||
|
raise ValueError('NlmeansController: Chroma research window value has to an odd number')
|
||||||
|
self.__chromaResearchWindowList.append(chromaResearchWindowValue)
|
||||||
|
else:
|
||||||
|
self.__chromaResearchWindowList = [NlmeansController.DEFAULT_CHROMA_RESEARCH_WINDOW]
|
||||||
|
|
||||||
|
def isActive(self):
|
||||||
|
return self.__isActive
|
||||||
|
|
||||||
|
def generateDenoiseTokens(self):
|
||||||
|
filterName = 'nlmeans_opencl' if self.__useHardware else 'nlmeans'
|
||||||
|
return ['-vf', f"{filterName}=s={self.__strengthList[0]}"
|
||||||
|
+ f":p={self.__patchSizeList[0]}"
|
||||||
|
+ f":pc={self.__chromaPatchSizeList[0]}"
|
||||||
|
+ f":r={self.__researchWindowList[0]}"
|
||||||
|
+ f":rc={self.__chromaResearchWindowList[0]}"] if self.__isActive else []
|
||||||
|
|
||||||
@@ -136,9 +136,9 @@ class Scenario2(Scenario):
|
|||||||
resultFileProperties = FileProperties(testContext, resultFile)
|
resultFileProperties = FileProperties(testContext, resultFile)
|
||||||
resultMediaDescriptor = resultFileProperties.getMediaDescriptor()
|
resultMediaDescriptor = resultFileProperties.getMediaDescriptor()
|
||||||
|
|
||||||
if testContext['use_jellyfin']:
|
# if testContext['use_jellyfin']:
|
||||||
sourceMediaDescriptor.applyJellyfinOrder()
|
# sourceMediaDescriptor.applyJellyfinOrder()
|
||||||
resultMediaDescriptor.applySourceIndices(sourceMediaDescriptor)
|
# resultMediaDescriptor.applySourceIndices(sourceMediaDescriptor)
|
||||||
|
|
||||||
resultMediaTracks = resultMediaDescriptor.getAllTrackDescriptors()
|
resultMediaTracks = resultMediaDescriptor.getAllTrackDescriptors()
|
||||||
|
|
||||||
|
|||||||
@@ -237,8 +237,8 @@ class Scenario4(Scenario):
|
|||||||
for l in rmd.getConfiguration(label = 'resultMediaDescriptor'):
|
for l in rmd.getConfiguration(label = 'resultMediaDescriptor'):
|
||||||
self._logger.debug(l)
|
self._logger.debug(l)
|
||||||
|
|
||||||
if testContext['use_jellyfin']:
|
# if testContext['use_jellyfin']:
|
||||||
sourceMediaDescriptor.applyJellyfinOrder()
|
# sourceMediaDescriptor.applyJellyfinOrder()
|
||||||
|
|
||||||
# num tracks differ
|
# num tracks differ
|
||||||
rmd.applySourceIndices(sourceMediaDescriptor)
|
rmd.applySourceIndices(sourceMediaDescriptor)
|
||||||
|
|||||||
@@ -282,12 +282,21 @@ class TrackDescriptor:
|
|||||||
else:
|
else:
|
||||||
return IsoLanguage.UNDEFINED
|
return IsoLanguage.UNDEFINED
|
||||||
|
|
||||||
|
def setLanguage(self, language: IsoLanguage):
|
||||||
|
if not type(language) is IsoLanguage:
|
||||||
|
raise TypeError('language has to be of type IsoLanguage')
|
||||||
|
self.__trackTags["language"] = language
|
||||||
|
|
||||||
def getTitle(self):
|
def getTitle(self):
|
||||||
if "title" in self.__trackTags.keys():
|
if "title" in self.__trackTags.keys():
|
||||||
return str(self.__trackTags["title"])
|
return str(self.__trackTags["title"])
|
||||||
else:
|
else:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
def setTitle(self, title: str):
|
||||||
|
self.__trackTags["title"] = str(title)
|
||||||
|
|
||||||
|
|
||||||
def getAudioLayout(self):
|
def getAudioLayout(self):
|
||||||
return self.__audioLayout
|
return self.__audioLayout
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user