bugfixes, inc Swap Tracks

main
Maveno 11 months ago
parent b492be227a
commit f853cf0f85

@ -437,9 +437,9 @@ def convert(ctx,
mediaFileProperties.getSeason(), mediaFileProperties.getSeason(),
mediaFileProperties.getEpisode()) mediaFileProperties.getEpisode())
if context['use_jellyfin']: # if context['use_jellyfin']:
# Reorder subtracks in types with default the last, then make subindices flat again # # Reorder subtracks in types with default the last, then make subindices flat again
sourceMediaDescriptor.applyJellyfinOrder() # sourceMediaDescriptor.applyJellyfinOrder()
fc = FfxController(context, sourceMediaDescriptor) fc = FfxController(context, sourceMediaDescriptor)
@ -483,9 +483,9 @@ 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']: # if context['use_jellyfin']:
# Reorder subtracks in types with default the last, then make subindices flat again # # Reorder subtracks in types with default the last, then make subindices flat again
targetMediaDescriptor.applyJellyfinOrder() # targetMediaDescriptor.applyJellyfinOrder()
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()]}")

@ -42,7 +42,8 @@ class FfxController():
'_STATISTICS_TAGS-eng'] '_STATISTICS_TAGS-eng']
IGNORED_METADATA_KEYS = ['VERSION-eng', IGNORED_METADATA_KEYS = ['VERSION-eng',
'creation_time'] 'creation_time',
'NAME']
@ -111,9 +112,41 @@ class FfxController():
return ['-ss', str(cropStart), '-t', str(cropLength)] return ['-ss', str(cropStart), '-t', str(cropLength)]
def generateDenoiseTokens(self, spatial=5, patch=7, research=7, hw=False): # strength: float = 3.0
filterName = 'nlmeans_opencl' if hw else 'nlmeans' # patchSize: int = 12
return ['-vf', f"{filterName}=s={spatial}:p={patch}:r={research}"] # chromaPatchSize: int = 8
# researchWindow: int = 20
# chromaResearchWindow: int= 12
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)
"""
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):
@ -297,7 +330,7 @@ class FfxController():
FfxController.DEFAULT_FILE_FORMAT, FfxController.DEFAULT_FILE_FORMAT,
FfxController.DEFAULT_FILE_EXTENSION) FfxController.DEFAULT_FILE_EXTENSION)
self.__logger.debug(f"FfxController.runJon() commandSequence:{' '.join(commandSequence)}") self.__logger.debug(f"FfxController.runJob() commandSequence:{' '.join(commandSequence)}")
if not self.__context['dry_run']: if not self.__context['dry_run']:
executeProcess(commandSequence) executeProcess(commandSequence)
@ -314,7 +347,7 @@ class FfxController():
commandSequence1 += FfxController.NULL_TOKENS commandSequence1 += FfxController.NULL_TOKENS
self.__logger.debug(f"FfxController.runJon() commandSequence1:{' '.join(commandSequence1)}") self.__logger.debug(f"FfxController.runJob() commandSequence1:{' '.join(commandSequence1)}")
if os.path.exists(FfxController.TEMP_FILE_NAME): if os.path.exists(FfxController.TEMP_FILE_NAME):
os.remove(FfxController.TEMP_FILE_NAME) os.remove(FfxController.TEMP_FILE_NAME)
@ -342,7 +375,7 @@ class FfxController():
FfxController.DEFAULT_FILE_FORMAT, FfxController.DEFAULT_FILE_FORMAT,
FfxController.DEFAULT_FILE_EXTENSION) FfxController.DEFAULT_FILE_EXTENSION)
self.__logger.debug(f"FfxController.runJon() commandSequence2:{' '.join(commandSequence2)}") self.__logger.debug(f"FfxController.runJob() commandSequence2:{' '.join(commandSequence2)}")
if not self.__context['dry_run']: if not self.__context['dry_run']:
out, err, rc = executeProcess(commandSequence2) out, err, rc = executeProcess(commandSequence2)

@ -53,6 +53,16 @@ def setDiff(a : set, b : set) -> set:
return diffResult return diffResult
def permutateList(inputList: list, permutation: list):
# 0,1,2: ABC
# 0,2,1: ACB
# 1,2,0: BCA
pass
def filterFilename(fileName: str) -> str: def filterFilename(fileName: str) -> str:
"""This filter replaces charactes from TMDB responses with characters """This filter replaces charactes from TMDB responses with characters
less problemating when using in filenames or removes them""" less problemating when using in filenames or removes them"""
@ -63,5 +73,6 @@ def filterFilename(fileName: str) -> str:
fileName = str(fileName).replace(':', ';') fileName = str(fileName).replace(':', ';')
fileName = str(fileName).replace('*', '') fileName = str(fileName).replace('*', '')
fileName = str(fileName).replace("'", '')
return fileName.strip() return fileName.strip()

@ -78,8 +78,8 @@ class MediaDescriptor:
# self.__jellyfinOrder = kwargs[MediaDescriptor.JELLYFIN_ORDER_FLAG_KEY] # self.__jellyfinOrder = kwargs[MediaDescriptor.JELLYFIN_ORDER_FLAG_KEY]
# else: # else:
# self.__jellyfinOrder = False # self.__jellyfinOrder = False
self.__jellyfinOrder = self.__context['use_jellyfin'] if 'use_jellyfin' in self.__context.keys() else False # self.__jellyfinOrder = self.__context['use_jellyfin'] if 'use_jellyfin' in self.__context.keys() else False
self.__jellyfinOrder = False
def setDefaultSubTrack(self, trackType: TrackType, subIndex: int): def setDefaultSubTrack(self, trackType: TrackType, subIndex: int):
for t in self.getAllTrackDescriptors(): for t in self.getAllTrackDescriptors():

@ -9,6 +9,8 @@ from textual.containers import Grid
from ffx.model.show import Show from ffx.model.show import Show
from ffx.model.pattern import Pattern from ffx.model.pattern import Pattern
from ffx.audio_layout import AudioLayout
from .pattern_controller import PatternController from .pattern_controller import PatternController
from .show_controller import ShowController from .show_controller import ShowController
from .track_controller import TrackController from .track_controller import TrackController
@ -454,6 +456,7 @@ class MediaDetailsScreen(Screen):
kwargs[TrackDescriptor.INDEX_KEY] = int(selected_track_data[0]) kwargs[TrackDescriptor.INDEX_KEY] = int(selected_track_data[0])
kwargs[TrackDescriptor.TRACK_TYPE_KEY] = TrackType.fromLabel(selected_track_data[1]) kwargs[TrackDescriptor.TRACK_TYPE_KEY] = TrackType.fromLabel(selected_track_data[1])
kwargs[TrackDescriptor.SUB_INDEX_KEY] = int(selected_track_data[2]) kwargs[TrackDescriptor.SUB_INDEX_KEY] = int(selected_track_data[2])
kwargs[TrackDescriptor.AUDIO_LAYOUT_KEY] = AudioLayout.fromLabel(selected_track_data[3])
return TrackDescriptor(**kwargs) return TrackDescriptor(**kwargs)
else: else:
@ -622,9 +625,7 @@ class MediaDetailsScreen(Screen):
def action_edit_pattern(self): def action_edit_pattern(self):
patternDescriptor = {} patternDescriptor = self.getPatternDescriptorFromInput()
patternDescriptor['show_id'] = self.getSelectedShowDescriptor().getId()
patternDescriptor['pattern'] = self.getPatternFromInput()
if patternDescriptor['pattern']: if patternDescriptor['pattern']:

@ -189,11 +189,12 @@ class Track(Base):
return bool(self.disposition_flags & 2**disposition.index()) return bool(self.disposition_flags & 2**disposition.index())
def getDescriptor(self, context, subIndex : int = -1) -> TrackDescriptor: def getDescriptor(self, context = None, subIndex : int = -1) -> TrackDescriptor:
kwargs = {} kwargs = {}
kwargs[TrackDescriptor.CONTEXT_KEY] = context if not context is None:
kwargs[TrackDescriptor.CONTEXT_KEY] = context
kwargs[TrackDescriptor.ID_KEY] = self.getId() kwargs[TrackDescriptor.ID_KEY] = self.getId()
kwargs[TrackDescriptor.PATTERN_ID_KEY] = self.getPatternId() kwargs[TrackDescriptor.PATTERN_ID_KEY] = self.getPatternId()

@ -139,18 +139,18 @@ class PatternController():
finally: finally:
s.close() s.close()
def getMediaDescriptor(self, context, patternId): # def getMediaDescriptor(self, context, patternId):
#
try: # try:
s = self.Session() # s = self.Session()
q = s.query(Pattern).filter(Pattern.id == int(patternId)) # q = s.query(Pattern).filter(Pattern.id == int(patternId))
#
if q.count(): # if q.count():
return q.first().getMediaDescriptor(context) # return q.first().getMediaDescriptor(context)
else: # else:
return None # return None
#
except Exception as ex: # except Exception as ex:
raise click.ClickException(f"PatternController.getPatternDescriptor(): {repr(ex)}") # raise click.ClickException(f"PatternController.getMediaDescriptor(): {repr(ex)}")
finally: # finally:
s.close() # s.close()

@ -7,6 +7,8 @@ from textual.containers import Grid
from .show_controller import ShowController from .show_controller import ShowController
from .pattern_controller import PatternController from .pattern_controller import PatternController
from ffx.model.pattern import Pattern
# Screen[dict[int, str, int]] # Screen[dict[int, str, int]]
class PatternDeleteScreen(Screen): class PatternDeleteScreen(Screen):
@ -51,16 +53,16 @@ class PatternDeleteScreen(Screen):
self.__pc = PatternController(context = self.context) self.__pc = PatternController(context = self.context)
self.__sc = ShowController(context = self.context) self.__sc = ShowController(context = self.context)
self.pattern_id = patternId self.__patternId = patternId
self.pattern_obj = self.__pc.getPatternDescriptor(patternId) if patternId is not None else {} self.__pattern: Pattern = self.__pc.getPattern(patternId) if patternId is not None else {}
self.show_obj = self.__sc.getShowDescriptor(showId) if showId is not None else {} self.__showDescriptor = self.__sc.getShowDescriptor(showId) if showId is not None else {}
def on_mount(self): def on_mount(self):
if self.show_obj: if self.__showDescriptor:
self.query_one("#showlabel", Static).update(f"{self.show_obj['id']} - {self.show_obj['name']} ({self.show_obj['year']})") self.query_one("#showlabel", Static).update(f"{self.__showDescriptor.getId()} - {self.__showDescriptor.getName()} ({self.__showDescriptor.getYear()})")
if self.pattern_obj: if not self.__pattern is None:
self.query_one("#patternlabel", Static).update(str(self.pattern_obj['pattern'])) self.query_one("#patternlabel", Static).update(str(self.__pattern.pattern))
def compose(self): def compose(self):
@ -97,8 +99,8 @@ class PatternDeleteScreen(Screen):
if self.pattern_id is None: if self.pattern_id is None:
raise click.ClickException('PatternDeleteScreen.on_button_pressed(): pattern id is undefined') raise click.ClickException('PatternDeleteScreen.on_button_pressed(): pattern id is undefined')
if self.__pc.deletePattern(self.pattern_id): if self.__pc.deletePattern(self.__patternId):
self.dismiss(self.pattern_obj) self.dismiss(self.__pattern)
else: else:
#TODO: Meldung #TODO: Meldung

@ -1,4 +1,5 @@
import click, re import click, re
from typing import List
from textual import events from textual import events
from textual.app import App, ComposeResult from textual.app import App, ComposeResult
@ -29,6 +30,7 @@ from ffx.track_descriptor import TrackDescriptor
from textual.widgets._data_table import CellDoesNotExist from textual.widgets._data_table import CellDoesNotExist
from ffx.file_properties import FileProperties from ffx.file_properties import FileProperties
from ffx.iso_language import IsoLanguage
# Screen[dict[int, str, int]] # Screen[dict[int, str, int]]
@ -37,9 +39,9 @@ class PatternDetailsScreen(Screen):
CSS = """ CSS = """
Grid { Grid {
grid-size: 5 13; grid-size: 7 13;
grid-rows: 2 2 2 2 2 8 2 2 8 2 2 2 2; grid-rows: 2 2 2 2 2 8 2 2 8 2 2 2 2;
grid-columns: 25 25 25 25 25; grid-columns: 25 25 25 25 25 25 25;
height: 100%; height: 100%;
width: 100%; width: 100%;
padding: 1; padding: 1;
@ -70,6 +72,12 @@ class PatternDetailsScreen(Screen):
.five { .five {
column-span: 5; column-span: 5;
} }
.six {
column-span: 6;
}
.seven {
column-span: 7;
}
.box { .box {
height: 100%; height: 100%;
@ -126,6 +134,7 @@ class PatternDetailsScreen(Screen):
typeCounter = {} typeCounter = {}
tr: Track
for tr in tracks: for tr in tracks:
td : TrackDescriptor = tr.getDescriptor(self.context) td : TrackDescriptor = tr.getDescriptor(self.context)
@ -136,19 +145,59 @@ class PatternDetailsScreen(Screen):
dispoSet = td.getDispositionSet() dispoSet = td.getDispositionSet()
trackLanguage = td.getLanguage()
row = (td.getIndex(), row = (td.getIndex(),
trackType.label(), trackType.label(),
typeCounter[trackType], typeCounter[trackType],
td.getCodec(),
td.getAudioLayout().label() if trackType == TrackType.AUDIO else ' ', td.getAudioLayout().label() if trackType == TrackType.AUDIO else ' ',
td.getLanguage().label(), trackLanguage.label() if trackLanguage != IsoLanguage.UNDEFINED else ' ',
td.getTitle(), td.getTitle(),
'Yes' if TrackDisposition.DEFAULT in dispoSet else 'No', 'Yes' if TrackDisposition.DEFAULT in dispoSet else 'No',
'Yes' if TrackDisposition.FORCED in dispoSet else 'No') 'Yes' if TrackDisposition.FORCED in dispoSet else 'No',
td.getSourceIndex())
self.tracksTable.add_row(*map(str, row)) self.tracksTable.add_row(*map(str, row))
typeCounter[trackType] += 1 typeCounter[trackType] += 1
def swapTracks(self, trackIndex1: int, trackIndex2: int):
ti1 = int(trackIndex1)
ti2 = int(trackIndex2)
siblingDescriptors: List[TrackDescriptor] = self.__tc.findSiblingDescriptors(self.__pattern.getId())
numSiblings = len(siblingDescriptors)
if ti1 < 0 or ti1 >= numSiblings:
raise ValueError(f"PatternDetailsScreen.swapTracks(): trackIndex1 ({ti1}) is out of range ({numSiblings})")
if ti2 < 0 or ti2 >= numSiblings:
raise ValueError(f"PatternDetailsScreen.swapTracks(): trackIndex2 ({ti2}) is out of range ({numSiblings})")
sibling1 = siblingDescriptors[trackIndex1]
sibling2 = siblingDescriptors[trackIndex2]
# raise click.ClickException(f"siblings id1={sibling1.getId()} id2={sibling2.getId()}")
subIndex2 = sibling2.getSubIndex()
sibling2.setIndex(sibling1.getIndex())
sibling2.setSubIndex(sibling1.getSubIndex())
sibling1.setIndex(trackIndex2)
sibling1.setSubIndex(subIndex2)
if not self.__tc.updateTrack(sibling1.getId(), sibling1):
raise click.ClickException('Update sibling1 failed')
if not self.__tc.updateTrack(sibling2.getId(), sibling2):
raise click.ClickException('Update sibling2 failed')
self.updateTracks()
def updateTags(self): def updateTags(self):
self.tagsTable.clear() self.tagsTable.clear()
@ -181,7 +230,7 @@ class PatternDetailsScreen(Screen):
def compose(self): def compose(self):
self.tagsTable = DataTable(classes="five") self.tagsTable = DataTable(classes="seven")
# Define the columns with headers # Define the columns with headers
self.column_key_tag_key = self.tagsTable.add_column("Key", width=10) self.column_key_tag_key = self.tagsTable.add_column("Key", width=10)
@ -190,16 +239,18 @@ class PatternDetailsScreen(Screen):
self.tagsTable.cursor_type = 'row' self.tagsTable.cursor_type = 'row'
self.tracksTable = DataTable(id="tracks_table", classes="five") self.tracksTable = DataTable(id="tracks_table", classes="seven")
self.column_key_track_index = self.tracksTable.add_column("Index", width=5) self.column_key_track_index = self.tracksTable.add_column("Index", width=5)
self.column_key_track_type = self.tracksTable.add_column("Type", width=10) self.column_key_track_type = self.tracksTable.add_column("Type", width=10)
self.column_key_track_sub_index = self.tracksTable.add_column("Subindex", width=5) self.column_key_track_sub_index = self.tracksTable.add_column("SubIndex", width=8)
self.column_key_track_codec = self.tracksTable.add_column("Codec", width=10)
self.column_key_track_audio_layout = self.tracksTable.add_column("Layout", width=10) self.column_key_track_audio_layout = self.tracksTable.add_column("Layout", width=10)
self.column_key_track_language = self.tracksTable.add_column("Language", width=15) self.column_key_track_language = self.tracksTable.add_column("Language", width=15)
self.column_key_track_title = self.tracksTable.add_column("Title", width=48) self.column_key_track_title = self.tracksTable.add_column("Title", width=48)
self.column_key_track_default = self.tracksTable.add_column("Default", width=8) self.column_key_track_default = self.tracksTable.add_column("Default", width=8)
self.column_key_track_forced = self.tracksTable.add_column("Forced", width=8) self.column_key_track_forced = self.tracksTable.add_column("Forced", width=8)
self.column_key_track_source_index = self.tracksTable.add_column("SrcIndex", width=8)
self.tracksTable.cursor_type = 'row' self.tracksTable.cursor_type = 'row'
@ -210,21 +261,21 @@ class PatternDetailsScreen(Screen):
# 1 # 1
yield Static("Edit filename pattern" if self.__pattern is not None else "New filename pattern", id="toplabel") yield Static("Edit filename pattern" if self.__pattern is not None else "New filename pattern", id="toplabel")
yield Input(type="text", id="pattern_input", classes="four") yield Input(type="text", id="pattern_input", classes="six")
# 2 # 2
yield Static("from show") yield Static("from show")
yield Static("", id="showlabel", classes="three") yield Static("", id="showlabel", classes="five")
yield Button("Substitute pattern", id="pattern_button") yield Button("Substitute pattern", id="pattern_button")
# 3 # 3
yield Static(" ", classes="five") yield Static(" ", classes="seven")
# 4 # 4
yield Static(" ", classes="five") yield Static(" ", classes="seven")
# 5 # 5
yield Static("Media Tags") yield Static("Media Tags")
yield Static(" ")
if self.__pattern is not None: if self.__pattern is not None:
yield Button("Add", id="button_add_tag") yield Button("Add", id="button_add_tag")
@ -234,15 +285,20 @@ class PatternDetailsScreen(Screen):
yield Static(" ") yield Static(" ")
yield Static(" ") yield Static(" ")
yield Static(" ") yield Static(" ")
yield Static(" ")
yield Static(" ")
yield Static(" ")
# 6 # 6
yield self.tagsTable yield self.tagsTable
# 7 # 7
yield Static(" ", classes="five") yield Static(" ", classes="seven")
# 8 # 8
yield Static("Streams") yield Static("Streams")
yield Static(" ")
if self.__pattern is not None: if self.__pattern is not None:
yield Button("Add", id="button_add_track") yield Button("Add", id="button_add_track")
@ -252,22 +308,27 @@ class PatternDetailsScreen(Screen):
yield Static(" ") yield Static(" ")
yield Static(" ") yield Static(" ")
yield Static(" ") yield Static(" ")
yield Static(" ")
yield Button("Up", id="button_track_up")
yield Button("Down", id="button_track_down")
# 9 # 9
yield self.tracksTable yield self.tracksTable
# 10 # 10
yield Static(" ", classes="five") yield Static(" ", classes="seven")
# 11 # 11
yield Static(" ", classes="five") yield Static(" ", classes="seven")
# 12 # 12
yield Button("Save", id="save_button") yield Button("Save", id="save_button")
yield Button("Cancel", id="cancel_button") yield Button("Cancel", id="cancel_button")
yield Static(" ", classes="three") yield Static(" ", classes="five")
# 13 # 13
yield Static(" ", classes="five") yield Static(" ", classes="seven")
yield Footer() yield Footer()
@ -397,6 +458,27 @@ class PatternDetailsScreen(Screen):
self.query_one("#pattern_input", Input).value = pattern.replace(patternMatch.group(1), self.query_one("#pattern_input", Input).value = pattern.replace(patternMatch.group(1),
FileProperties.SE_INDICATOR_PATTERN) FileProperties.SE_INDICATOR_PATTERN)
if event.button.id == "button_track_up":
selectedTrackDescriptor = self.getSelectedTrackDescriptor()
selectedTrackIndex = selectedTrackDescriptor.getIndex()
if selectedTrackIndex > 0 and selectedTrackIndex < self.tracksTable.row_count:
correspondingTrackIndex = selectedTrackIndex - 1
self.swapTracks(selectedTrackIndex, correspondingTrackIndex)
if event.button.id == "button_track_down":
selectedTrackDescriptor = self.getSelectedTrackDescriptor()
selectedTrackIndex = selectedTrackDescriptor.getIndex()
if selectedTrackIndex >= 0 and selectedTrackIndex < (self.tracksTable.row_count - 1):
correspondingTrackIndex = selectedTrackIndex + 1
self.swapTracks(selectedTrackIndex, correspondingTrackIndex)
def handle_add_track(self, trackDescriptor : TrackDescriptor): def handle_add_track(self, trackDescriptor : TrackDescriptor):
dispoSet = trackDescriptor.getDispositionSet() dispoSet = trackDescriptor.getDispositionSet()

@ -208,7 +208,7 @@ class ShowDetailsScreen(Screen):
self.app.push_screen(PatternDeleteScreen(patternId = selectedPatternId, showId = self.__showDescriptor.getId()), self.handle_remove_pattern) self.app.push_screen(PatternDeleteScreen(patternId = selectedPatternId, showId = self.__showDescriptor.getId()), self.handle_remove_pattern)
def handle_remove_pattern(self, screenResult): def handle_remove_pattern(self, pattern):
try: try:
row_key, col_key = self.patternTable.coordinate_to_cell_key(self.patternTable.cursor_coordinate) row_key, col_key = self.patternTable.coordinate_to_cell_key(self.patternTable.cursor_coordinate)

@ -1,33 +0,0 @@
import os, sys, importlib, glob, inspect, itertools
class JellyfinCombinator():
IDENTIFIER = 'jellyfin'
def __init__(self, context = None):
self._context = context
self._logger = context['logger']
self._reportLogger = context['report_logger']
def getIdentifier(self):
return JellyfinCombinator.IDENTIFIER
@staticmethod
def list():
basePath = os.path.dirname(__file__)
return [os.path.basename(p)[20:-3]
for p
in glob.glob(f"{ basePath }/jellyfin_combinator_*.py", recursive = True)
if p != __file__]
@staticmethod
def getClassReference(identifier):
importlib.import_module(f"ffx.test.jellyfin_combinator_{ identifier }")
for name, obj in inspect.getmembers(sys.modules[f"ffx.test.jellyfin_combinator_{ identifier }"]):
#HINT: Excluding MediaCombinator as it seems to be included by import (?)
if inspect.isclass(obj) and name != 'JellyfinCombinator' and name.startswith('JellyfinCombinator'):
return obj
@staticmethod
def getAllClassReferences():
return [JellyfinCombinator.getClassReference(i) for i in JellyfinCombinator.list()]

@ -1,34 +0,0 @@
import os, sys, importlib, glob, inspect, itertools
from ffx.track_type import TrackType
from ffx.track_descriptor import TrackDescriptor
from ffx.media_descriptor import MediaDescriptor
from .jellyfin_combinator import JellyfinCombinator
class JellyfinCombinator0(JellyfinCombinator):
VARIANT = 'J0'
# def __init__(self, SubCombinators: dict = {}, context = None):
def __init__(self, context = None):
self._context = context
self._logger = context['logger']
self._reportLogger = context['report_logger']
# self._SubCombinators = SubCombinators
def getVariant(self):
return JellyfinCombinator0.VARIANT
def getPayload(self):
return False
def assertFunc(self, testObj = {}):
pass
def shouldFail(self):
return False

@ -1,34 +0,0 @@
import os, sys, importlib, glob, inspect, itertools
from ffx.track_type import TrackType
from ffx.track_descriptor import TrackDescriptor
from ffx.media_descriptor import MediaDescriptor
from .jellyfin_combinator import JellyfinCombinator
class JellyfinCombinator1(JellyfinCombinator):
VARIANT = 'J1'
# def __init__(self, SubCombinators: dict = {}, context = None):
def __init__(self, context = None):
self._context = context
self._logger = context['logger']
self._reportLogger = context['report_logger']
# self._SubCombinators = SubCombinations
def getVariant(self):
return JellyfinCombinator1.VARIANT
def getPayload(self):
return True
def assertFunc(self, testObj = {}):
pass
def shouldFail(self):
return False

@ -6,7 +6,6 @@ from ffx.track_descriptor import TrackDescriptor
from ffx.media_descriptor import MediaDescriptor from ffx.media_descriptor import MediaDescriptor
from .media_combinator import MediaCombinator from .media_combinator import MediaCombinator
from .jellyfin_combinator import JellyfinCombinator
from .media_tag_combinator import MediaTagCombinator from .media_tag_combinator import MediaTagCombinator

@ -6,7 +6,6 @@ from ffx.track_descriptor import TrackDescriptor
from ffx.media_descriptor import MediaDescriptor from ffx.media_descriptor import MediaDescriptor
from .media_combinator import MediaCombinator from .media_combinator import MediaCombinator
from .jellyfin_combinator import JellyfinCombinator
from .media_tag_combinator import MediaTagCombinator from .media_tag_combinator import MediaTagCombinator

@ -9,7 +9,7 @@ from .media_combinator import MediaCombinator
from .disposition_combinator_2 import DispositionCombinator2 from .disposition_combinator_2 import DispositionCombinator2
from .track_tag_combinator_2 import TrackTagCombinator2 from .track_tag_combinator_2 import TrackTagCombinator2
from .jellyfin_combinator import JellyfinCombinator from .permutation_combinator_2 import PermutationCombinator2
from .media_tag_combinator import MediaTagCombinator from .media_tag_combinator import MediaTagCombinator

@ -9,7 +9,7 @@ from .media_combinator import MediaCombinator
from .disposition_combinator_3 import DispositionCombinator3 from .disposition_combinator_3 import DispositionCombinator3
from .track_tag_combinator_3 import TrackTagCombinator3 from .track_tag_combinator_3 import TrackTagCombinator3
from .jellyfin_combinator import JellyfinCombinator from .permutation_combinator_3 import PermutationCombinator3
from .media_tag_combinator import MediaTagCombinator from .media_tag_combinator import MediaTagCombinator

@ -9,7 +9,7 @@ from .media_combinator import MediaCombinator
from .disposition_combinator_2 import DispositionCombinator2 from .disposition_combinator_2 import DispositionCombinator2
from .track_tag_combinator_2 import TrackTagCombinator2 from .track_tag_combinator_2 import TrackTagCombinator2
from .jellyfin_combinator import JellyfinCombinator from .permutation_combinator_2 import PermutationCombinator2
from .media_tag_combinator import MediaTagCombinator from .media_tag_combinator import MediaTagCombinator

@ -9,7 +9,7 @@ from .media_combinator import MediaCombinator
from .disposition_combinator_2 import DispositionCombinator2 from .disposition_combinator_2 import DispositionCombinator2
from .track_tag_combinator_2 import TrackTagCombinator2 from .track_tag_combinator_2 import TrackTagCombinator2
from .jellyfin_combinator import JellyfinCombinator from .permutation_combinator_2 import PermutationCombinator2
from .media_tag_combinator import MediaTagCombinator from .media_tag_combinator import MediaTagCombinator

@ -9,7 +9,7 @@ from .media_combinator import MediaCombinator
from .disposition_combinator_2 import DispositionCombinator2 from .disposition_combinator_2 import DispositionCombinator2
from .track_tag_combinator_2 import TrackTagCombinator2 from .track_tag_combinator_2 import TrackTagCombinator2
from .jellyfin_combinator import JellyfinCombinator from .permutation_combinator_2 import PermutationCombinator2
from .media_tag_combinator import MediaTagCombinator from .media_tag_combinator import MediaTagCombinator

@ -11,7 +11,8 @@ from .disposition_combinator_2 import DispositionCombinator2
from .disposition_combinator_3 import DispositionCombinator3 from .disposition_combinator_3 import DispositionCombinator3
from .track_tag_combinator_2 import TrackTagCombinator2 from .track_tag_combinator_2 import TrackTagCombinator2
from .track_tag_combinator_3 import TrackTagCombinator3 from .track_tag_combinator_3 import TrackTagCombinator3
from .jellyfin_combinator import JellyfinCombinator from .permutation_combinator_2 import PermutationCombinator2
from .permutation_combinator_3 import PermutationCombinator3
from .media_tag_combinator import MediaTagCombinator from .media_tag_combinator import MediaTagCombinator
class MediaCombinator7(MediaCombinator): class MediaCombinator7(MediaCombinator):
@ -30,6 +31,8 @@ class MediaCombinator7(MediaCombinator):
def getPayload(self, def getPayload(self,
audioPermutation,
subtitlePermutation,
audioDispositionTuple = (set(), set()), audioDispositionTuple = (set(), set()),
audioTagTuple = ({}, {}), audioTagTuple = ({}, {}),
subtitleDispositionTuple = (set(), set(), set()), subtitleDispositionTuple = (set(), set(), set()),
@ -116,100 +119,107 @@ class MediaCombinator7(MediaCombinator):
def getYield(self): def getYield(self):
pc2 = PermutationCombinator2(self._context)
pc3 = PermutationCombinator3(self._context)
for MTC in MediaTagCombinator.getAllClassReferences(): for MTC in MediaTagCombinator.getAllClassReferences():
for DC2_A in DispositionCombinator2.getAllClassReferences(): for DC2_A in DispositionCombinator2.getAllClassReferences():
for TC2_A in TrackTagCombinator2.getAllClassReferences(): for TC2_A in TrackTagCombinator2.getAllClassReferences():
for DC3_S in DispositionCombinator3.getAllClassReferences(): for DC3_S in DispositionCombinator3.getAllClassReferences():
for TC3_S in TrackTagCombinator3.getAllClassReferences(): for TC3_S in TrackTagCombinator3.getAllClassReferences():
for J in JellyfinCombinator.getAllClassReferences(): for p2y in pc2.getYield():
for p3y in pc3.getYield():
j = J(self._context)
self._context['use_jellyfin'] = j.getPayload() dc2a = DC2_A(self._context)
tc2a = TC2_A(self._context)
dc2a = DC2_A(self._context) dc3s = DC3_S(self._context)
tc2a = TC2_A(self._context) tc3s = TC3_S(self._context)
dc3s = DC3_S(self._context)
tc3s = TC3_S(self._context) mtc = MTC(self._context)
mtc = MTC(self._context) yObj = {}
yObj = {} yObj['identifier'] = self.getIdentifier()
yObj['variants'] = [self.getVariant(),
yObj['identifier'] = self.getIdentifier() f"A:{p2y['variant']}",
yObj['variants'] = [self.getVariant(), f"S:{p3y['variant']}",
f"A:{dc2a.getVariant()}", f"A:{dc2a.getVariant()}",
f"A:{tc2a.getVariant()}", f"A:{tc2a.getVariant()}",
f"S:{dc3s.getVariant()}", f"S:{dc3s.getVariant()}",
f"S:{tc3s.getVariant()}", f"S:{tc3s.getVariant()}",
mtc.getVariant(), mtc.getVariant()]
j.getVariant()]
yObj['payload'] = self.getPayload(p2y['permutation'],
yObj['payload'] = self.getPayload(dc2a.getPayload(), p3y['permutation'],
tc2a.getPayload(), dc2a.getPayload(),
dc3s.getPayload(), tc2a.getPayload(),
tc3s.getPayload()) dc3s.getPayload(),
tc3s.getPayload())
yObj['assertSelectors'] = ['M', 'AD', 'AT', 'SD', 'ST', 'MT', 'J']
yObj['assertSelectors'] = ['M', 'AP', 'SP', 'AD', 'AT', 'SD', 'ST', 'MT']
yObj['assertFuncs'] = [self.assertFunc,
dc2a.createAssertFunc(), yObj['assertFuncs'] = [self.assertFunc,
tc2a.createAssertFunc(), p2y.createAssertFunc(),
dc3s.createAssertFunc(), p3y.createAssertFunc(),
tc3s.createAssertFunc(), dc2a.createAssertFunc(),
mtc.createAssertFunc(), tc2a.createAssertFunc(),
j.assertFunc] dc3s.createAssertFunc(),
tc3s.createAssertFunc(),
yObj['shouldFail'] = (self.shouldFail() mtc.createAssertFunc()]
| dc2a.shouldFail()
| tc2a.shouldFail() yObj['shouldFail'] = (self.shouldFail()
| dc3s.shouldFail() | p2y.shouldFail()
| tc3s.shouldFail() | p3y.shouldFail()
| mtc.shouldFail() | dc2a.shouldFail()
| j.shouldFail()) | tc2a.shouldFail()
yieldObj = {'target': yObj} | dc3s.shouldFail()
| tc3s.shouldFail()
if self.__createPresets: | mtc.shouldFail())
yieldObj = {'target': yObj}
dc2a_p = DC2_A(self._context, createPresets = True)
tc2a_p = TC2_A(self._context, createPresets = True) if self.__createPresets:
dc3s_p = DC3_S(self._context, createPresets = True)
tc3s_p = TC3_S(self._context, createPresets = True) dc2a_p = DC2_A(self._context, createPresets = True)
tc2a_p = TC2_A(self._context, createPresets = True)
mtc_p = MTC(self._context, createPresets = True) dc3s_p = DC3_S(self._context, createPresets = True)
tc3s_p = TC3_S(self._context, createPresets = True)
yObj_p = {}
mtc_p = MTC(self._context, createPresets = True)
yObj_p['identifier'] = self.getIdentifier()
yObj_p['variants'] = [self.getVariant(), yObj_p = {}
f"A:{dc2a_p.getVariant()}",
f"A:{tc2a_p.getVariant()}", yObj_p['identifier'] = self.getIdentifier()
f"S:{dc3s_p.getVariant()}", yObj_p['variants'] = [self.getVariant(),
f"S:{tc3s_p.getVariant()}", f"A:{dc2a_p.getVariant()}",
mtc_p.getVariant(), f"A:{tc2a_p.getVariant()}",
j.getVariant()] f"S:{dc3s_p.getVariant()}",
f"S:{tc3s_p.getVariant()}",
yObj_p['payload'] = self.getPayload(dc2a_p.getPayload(), mtc_p.getVariant(),
tc2a_p.getPayload(), j.getVariant()]
dc3s_p.getPayload(),
tc3s_p.getPayload()) yObj_p['payload'] = self.getPayload(dc2a_p.getPayload(),
tc2a_p.getPayload(),
yObj_p['assertSelectors'] = ['M', 'AD', 'AT', 'SD', 'ST', 'MT', 'J'] dc3s_p.getPayload(),
tc3s_p.getPayload())
yObj_p['assertFuncs'] = [self.assertFunc,
dc2a_p.createAssertFunc(), yObj_p['assertSelectors'] = ['M', 'AD', 'AT', 'SD', 'ST', 'MT', 'J']
tc2a_p.createAssertFunc(),
dc3s_p.createAssertFunc(), yObj_p['assertFuncs'] = [self.assertFunc,
tc3s_p.createAssertFunc(), dc2a_p.createAssertFunc(),
mtc_p.createAssertFunc(), tc2a_p.createAssertFunc(),
j.assertFunc] dc3s_p.createAssertFunc(),
tc3s_p.createAssertFunc(),
yObj_p['shouldFail'] = (self.shouldFail() mtc_p.createAssertFunc(),
| dc2a_p.shouldFail() j.assertFunc]
| tc2a_p.shouldFail()
| dc3s_p.shouldFail() yObj_p['shouldFail'] = (self.shouldFail()
| tc3s_p.shouldFail() | dc2a_p.shouldFail()
| mtc_p.shouldFail() | tc2a_p.shouldFail()
| j.shouldFail()) | dc3s_p.shouldFail()
yieldObj['preset'] = yObj_p | tc3s_p.shouldFail()
| mtc_p.shouldFail()
yield yieldObj | j.shouldFail())
yieldObj['preset'] = yObj_p
yield yieldObj

@ -0,0 +1,36 @@
class PermutationCombinator2():
IDENTIFIER = 'permutation2'
PERMUTATION_LIST = [
[0,1],
[1,0]
]
def __init__(self, context = None):
self._context = context
self._logger = context['logger']
self._reportLogger = context['report_logger']
def getIdentifier(self):
return PermutationCombinator2.IDENTIFIER
def getPayload(self, permutationIndex):
return {
'variant': f"P{permutationIndex}",
'permutation': PermutationCombinator2.PERMUTATION_LIST[permutationIndex]
}
def createAssertFunc(self):
def f(testObj = {}):
pass
return f
def shouldFail(self):
return False
def getYield(self):
for permutationIndex in range(len(PermutationCombinator2.PERMUTATION_LIST)):
yield self.getPayload(permutationIndex)

@ -0,0 +1,37 @@
class PermutationCombinator3():
IDENTIFIER = 'permutation3'
PERMUTATION_LIST = [
[0,1,2],
[0,2,1],
[1,2,0]
]
def __init__(self, context = None):
self._context = context
self._logger = context['logger']
self._reportLogger = context['report_logger']
def getIdentifier(self):
return PermutationCombinator3.IDENTIFIER
def getPayload(self, permutationIndex):
return {
'variant': f"P{permutationIndex}",
'permutation': PermutationCombinator3.PERMUTATION_LIST[permutationIndex]
}
def createAssertFunc(self):
def f(testObj = {}):
pass
return f
def shouldFail(self):
return False
def getYield(self):
for permutationIndex in range(len(PermutationCombinator3.PERMUTATION_LIST)):
yield self.getPayload(permutationIndex)

@ -66,6 +66,8 @@ class TrackController():
track : Track = q.first() track : Track = q.first()
track.index = int(trackDescriptor.getIndex())
track.track_type = int(trackDescriptor.getType().index()) track.track_type = int(trackDescriptor.getType().index())
track.codec_name = str(trackDescriptor.getCodec()) track.codec_name = str(trackDescriptor.getCodec())
track.audio_layout = int(trackDescriptor.getAudioLayout().index()) track.audio_layout = int(trackDescriptor.getAudioLayout().index())
@ -103,13 +105,34 @@ class TrackController():
s = self.Session() s = self.Session()
q = s.query(Track).filter(Track.pattern_id == int(patternId)) q = s.query(Track).filter(Track.pattern_id == int(patternId))
return [a for a in q.all()] return sorted([t for t in q.all()], key=lambda d: d.getIndex())
except Exception as ex: except Exception as ex:
raise click.ClickException(f"TrackController.findTracks(): {repr(ex)}") raise click.ClickException(f"TrackController.findTracks(): {repr(ex)}")
finally: finally:
s.close() s.close()
def findSiblingDescriptors(self, patternId):
"""Finds all stored tracks related to a pattern, packs them in descriptors
and also setting sub indices and returns list of descriptors"""
siblingTracks = self.findTracks(patternId)
siblingDescriptors = []
subIndexCounter = {}
st: Track
for st in siblingTracks:
trackType = st.getType()
if not trackType in subIndexCounter.keys():
subIndexCounter[trackType] = 0
siblingDescriptors.append(st.getDescriptor(subIndex=subIndexCounter[trackType]))
subIndexCounter[trackType] += 1
return siblingDescriptors
#TODO: mit optionalem Parameter lösen ^ #TODO: mit optionalem Parameter lösen ^
def findVideoTracks(self, patternId): def findVideoTracks(self, patternId):
@ -233,8 +256,8 @@ class TrackController():
s.close() s.close()
def setDefaultSubTrack(self, trackType, subIndex): # def setDefaultSubTrack(self, trackType, subIndex):
pass # pass
#
def setForcedSubTrack(self, trackType, subIndex): # def setForcedSubTrack(self, trackType, subIndex):
pass # pass

Loading…
Cancel
Save