From f853cf0f85154c1fb3e0e91e93b237ccf8411854 Mon Sep 17 00:00:00 2001 From: Maveno Date: Fri, 8 Nov 2024 18:25:51 +0100 Subject: [PATCH] bugfixes, inc Swap Tracks --- bin/ffx.py | 12 +- bin/ffx/ffx_controller.py | 47 +++++- bin/ffx/helper.py | 11 ++ bin/ffx/media_descriptor.py | 4 +- bin/ffx/media_details_screen.py | 9 +- bin/ffx/model/track.py | 5 +- bin/ffx/pattern_controller.py | 30 ++-- bin/ffx/pattern_delete_screen.py | 20 +-- bin/ffx/pattern_details_screen.py | 118 +++++++++++--- bin/ffx/show_details_screen.py | 2 +- bin/ffx/test/jellyfin_combinator.py | 33 ---- bin/ffx/test/jellyfin_combinator_0.py | 34 ---- bin/ffx/test/jellyfin_combinator_1.py | 34 ---- bin/ffx/test/media_combinator_0.py | 1 - bin/ffx/test/media_combinator_1.py | 1 - bin/ffx/test/media_combinator_2.py | 2 +- bin/ffx/test/media_combinator_3.py | 2 +- bin/ffx/test/media_combinator_4.py | 2 +- bin/ffx/test/media_combinator_5.py | 2 +- bin/ffx/test/media_combinator_6.py | 2 +- bin/ffx/test/media_combinator_7.py | 196 ++++++++++++----------- bin/ffx/test/permutation_combinator_2.py | 36 +++++ bin/ffx/test/permutation_combinator_3.py | 37 +++++ bin/ffx/track_controller.py | 35 +++- 24 files changed, 404 insertions(+), 271 deletions(-) delete mode 100644 bin/ffx/test/jellyfin_combinator.py delete mode 100644 bin/ffx/test/jellyfin_combinator_0.py delete mode 100644 bin/ffx/test/jellyfin_combinator_1.py create mode 100644 bin/ffx/test/permutation_combinator_2.py create mode 100644 bin/ffx/test/permutation_combinator_3.py diff --git a/bin/ffx.py b/bin/ffx.py index 6d9a4f8..df23611 100755 --- a/bin/ffx.py +++ b/bin/ffx.py @@ -437,9 +437,9 @@ def convert(ctx, mediaFileProperties.getSeason(), mediaFileProperties.getEpisode()) - if context['use_jellyfin']: - # Reorder subtracks in types with default the last, then make subindices flat again - sourceMediaDescriptor.applyJellyfinOrder() + # if context['use_jellyfin']: + # # Reorder subtracks in types with default the last, then make subindices flat again + # sourceMediaDescriptor.applyJellyfinOrder() 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()]}") - if context['use_jellyfin']: - # Reorder subtracks in types with default the last, then make subindices flat again - targetMediaDescriptor.applyJellyfinOrder() + # if context['use_jellyfin']: + # # Reorder subtracks in types with default the last, then make subindices flat again + # 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()]}") diff --git a/bin/ffx/ffx_controller.py b/bin/ffx/ffx_controller.py index a2b3de8..9e0a73c 100644 --- a/bin/ffx/ffx_controller.py +++ b/bin/ffx/ffx_controller.py @@ -42,7 +42,8 @@ class FfxController(): '_STATISTICS_TAGS-eng'] IGNORED_METADATA_KEYS = ['VERSION-eng', - 'creation_time'] + 'creation_time', + 'NAME'] @@ -111,9 +112,41 @@ class FfxController(): return ['-ss', str(cropStart), '-t', str(cropLength)] - def generateDenoiseTokens(self, spatial=5, patch=7, research=7, hw=False): - filterName = 'nlmeans_opencl' if hw else 'nlmeans' - return ['-vf', f"{filterName}=s={spatial}:p={patch}:r={research}"] + # strength: float = 3.0 + # patchSize: int = 12 + # 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): @@ -297,7 +330,7 @@ class FfxController(): FfxController.DEFAULT_FILE_FORMAT, 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']: executeProcess(commandSequence) @@ -314,7 +347,7 @@ class FfxController(): 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): os.remove(FfxController.TEMP_FILE_NAME) @@ -342,7 +375,7 @@ class FfxController(): FfxController.DEFAULT_FILE_FORMAT, 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']: out, err, rc = executeProcess(commandSequence2) diff --git a/bin/ffx/helper.py b/bin/ffx/helper.py index 0910cf3..1978dfb 100644 --- a/bin/ffx/helper.py +++ b/bin/ffx/helper.py @@ -53,6 +53,16 @@ def setDiff(a : set, b : set) -> set: 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: """This filter replaces charactes from TMDB responses with characters 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("'", '') return fileName.strip() diff --git a/bin/ffx/media_descriptor.py b/bin/ffx/media_descriptor.py index ac531b9..1324adc 100644 --- a/bin/ffx/media_descriptor.py +++ b/bin/ffx/media_descriptor.py @@ -78,8 +78,8 @@ class MediaDescriptor: # 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 = self.__context['use_jellyfin'] if 'use_jellyfin' in self.__context.keys() else False + self.__jellyfinOrder = False def setDefaultSubTrack(self, trackType: TrackType, subIndex: int): for t in self.getAllTrackDescriptors(): diff --git a/bin/ffx/media_details_screen.py b/bin/ffx/media_details_screen.py index ae86978..514abf7 100644 --- a/bin/ffx/media_details_screen.py +++ b/bin/ffx/media_details_screen.py @@ -9,6 +9,8 @@ from textual.containers import Grid from ffx.model.show import Show from ffx.model.pattern import Pattern +from ffx.audio_layout import AudioLayout + from .pattern_controller import PatternController from .show_controller import ShowController from .track_controller import TrackController @@ -454,6 +456,7 @@ class MediaDetailsScreen(Screen): kwargs[TrackDescriptor.INDEX_KEY] = int(selected_track_data[0]) kwargs[TrackDescriptor.TRACK_TYPE_KEY] = TrackType.fromLabel(selected_track_data[1]) kwargs[TrackDescriptor.SUB_INDEX_KEY] = int(selected_track_data[2]) + kwargs[TrackDescriptor.AUDIO_LAYOUT_KEY] = AudioLayout.fromLabel(selected_track_data[3]) return TrackDescriptor(**kwargs) else: @@ -621,10 +624,8 @@ class MediaDetailsScreen(Screen): def action_edit_pattern(self): - - patternDescriptor = {} - patternDescriptor['show_id'] = self.getSelectedShowDescriptor().getId() - patternDescriptor['pattern'] = self.getPatternFromInput() + + patternDescriptor = self.getPatternDescriptorFromInput() if patternDescriptor['pattern']: diff --git a/bin/ffx/model/track.py b/bin/ffx/model/track.py index be7dc10..5ce2e08 100644 --- a/bin/ffx/model/track.py +++ b/bin/ffx/model/track.py @@ -189,11 +189,12 @@ class Track(Base): 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[TrackDescriptor.CONTEXT_KEY] = context + if not context is None: + kwargs[TrackDescriptor.CONTEXT_KEY] = context kwargs[TrackDescriptor.ID_KEY] = self.getId() kwargs[TrackDescriptor.PATTERN_ID_KEY] = self.getPatternId() diff --git a/bin/ffx/pattern_controller.py b/bin/ffx/pattern_controller.py index be01163..4c31ea1 100644 --- a/bin/ffx/pattern_controller.py +++ b/bin/ffx/pattern_controller.py @@ -139,18 +139,18 @@ class PatternController(): finally: s.close() - def getMediaDescriptor(self, context, patternId): - - try: - s = self.Session() - q = s.query(Pattern).filter(Pattern.id == int(patternId)) - - if q.count(): - return q.first().getMediaDescriptor(context) - else: - return None - - except Exception as ex: - raise click.ClickException(f"PatternController.getPatternDescriptor(): {repr(ex)}") - finally: - s.close() \ No newline at end of file +# def getMediaDescriptor(self, context, patternId): +# +# try: +# s = self.Session() +# q = s.query(Pattern).filter(Pattern.id == int(patternId)) +# +# if q.count(): +# return q.first().getMediaDescriptor(context) +# else: +# return None +# +# except Exception as ex: +# raise click.ClickException(f"PatternController.getMediaDescriptor(): {repr(ex)}") +# finally: +# s.close() \ No newline at end of file diff --git a/bin/ffx/pattern_delete_screen.py b/bin/ffx/pattern_delete_screen.py index 17b8567..25323b0 100644 --- a/bin/ffx/pattern_delete_screen.py +++ b/bin/ffx/pattern_delete_screen.py @@ -7,6 +7,8 @@ from textual.containers import Grid from .show_controller import ShowController from .pattern_controller import PatternController +from ffx.model.pattern import Pattern + # Screen[dict[int, str, int]] class PatternDeleteScreen(Screen): @@ -51,16 +53,16 @@ class PatternDeleteScreen(Screen): self.__pc = PatternController(context = self.context) self.__sc = ShowController(context = self.context) - self.pattern_id = patternId - self.pattern_obj = self.__pc.getPatternDescriptor(patternId) if patternId is not None else {} - self.show_obj = self.__sc.getShowDescriptor(showId) if showId is not None else {} + self.__patternId = patternId + self.__pattern: Pattern = self.__pc.getPattern(patternId) if patternId is not None else {} + self.__showDescriptor = self.__sc.getShowDescriptor(showId) if showId is not None else {} def on_mount(self): - if self.show_obj: - self.query_one("#showlabel", Static).update(f"{self.show_obj['id']} - {self.show_obj['name']} ({self.show_obj['year']})") - if self.pattern_obj: - self.query_one("#patternlabel", Static).update(str(self.pattern_obj['pattern'])) + if self.__showDescriptor: + self.query_one("#showlabel", Static).update(f"{self.__showDescriptor.getId()} - {self.__showDescriptor.getName()} ({self.__showDescriptor.getYear()})") + if not self.__pattern is None: + self.query_one("#patternlabel", Static).update(str(self.__pattern.pattern)) def compose(self): @@ -97,8 +99,8 @@ class PatternDeleteScreen(Screen): if self.pattern_id is None: raise click.ClickException('PatternDeleteScreen.on_button_pressed(): pattern id is undefined') - if self.__pc.deletePattern(self.pattern_id): - self.dismiss(self.pattern_obj) + if self.__pc.deletePattern(self.__patternId): + self.dismiss(self.__pattern) else: #TODO: Meldung diff --git a/bin/ffx/pattern_details_screen.py b/bin/ffx/pattern_details_screen.py index 38b1d9d..a21500d 100644 --- a/bin/ffx/pattern_details_screen.py +++ b/bin/ffx/pattern_details_screen.py @@ -1,4 +1,5 @@ import click, re +from typing import List from textual import events from textual.app import App, ComposeResult @@ -29,6 +30,7 @@ from ffx.track_descriptor import TrackDescriptor from textual.widgets._data_table import CellDoesNotExist from ffx.file_properties import FileProperties +from ffx.iso_language import IsoLanguage # Screen[dict[int, str, int]] @@ -37,9 +39,9 @@ class PatternDetailsScreen(Screen): CSS = """ 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-columns: 25 25 25 25 25; + grid-columns: 25 25 25 25 25 25 25; height: 100%; width: 100%; padding: 1; @@ -70,6 +72,12 @@ class PatternDetailsScreen(Screen): .five { column-span: 5; } + .six { + column-span: 6; + } + .seven { + column-span: 7; + } .box { height: 100%; @@ -126,6 +134,7 @@ class PatternDetailsScreen(Screen): typeCounter = {} + tr: Track for tr in tracks: td : TrackDescriptor = tr.getDescriptor(self.context) @@ -136,19 +145,59 @@ class PatternDetailsScreen(Screen): dispoSet = td.getDispositionSet() + trackLanguage = td.getLanguage() row = (td.getIndex(), trackType.label(), typeCounter[trackType], + td.getCodec(), td.getAudioLayout().label() if trackType == TrackType.AUDIO else ' ', - td.getLanguage().label(), + trackLanguage.label() if trackLanguage != IsoLanguage.UNDEFINED else ' ', td.getTitle(), '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)) 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): self.tagsTable.clear() @@ -181,7 +230,7 @@ class PatternDetailsScreen(Screen): def compose(self): - self.tagsTable = DataTable(classes="five") + self.tagsTable = DataTable(classes="seven") # Define the columns with headers 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.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_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_language = self.tracksTable.add_column("Language", width=15) 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_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' @@ -210,21 +261,21 @@ class PatternDetailsScreen(Screen): # 1 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 yield Static("from show") - yield Static("", id="showlabel", classes="three") + yield Static("", id="showlabel", classes="five") yield Button("Substitute pattern", id="pattern_button") # 3 - yield Static(" ", classes="five") + yield Static(" ", classes="seven") # 4 - yield Static(" ", classes="five") + yield Static(" ", classes="seven") # 5 yield Static("Media Tags") - yield Static(" ") + if self.__pattern is not None: 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(" ") + # 6 yield self.tagsTable # 7 - yield Static(" ", classes="five") + yield Static(" ", classes="seven") # 8 yield Static("Streams") - yield Static(" ") + if self.__pattern is not None: yield Button("Add", id="button_add_track") @@ -252,22 +308,27 @@ class PatternDetailsScreen(Screen): yield Static(" ") yield Static(" ") yield Static(" ") + + yield Static(" ") + yield Button("Up", id="button_track_up") + yield Button("Down", id="button_track_down") + # 9 yield self.tracksTable # 10 - yield Static(" ", classes="five") + yield Static(" ", classes="seven") # 11 - yield Static(" ", classes="five") + yield Static(" ", classes="seven") # 12 yield Button("Save", id="save_button") yield Button("Cancel", id="cancel_button") - yield Static(" ", classes="three") + yield Static(" ", classes="five") # 13 - yield Static(" ", classes="five") + yield Static(" ", classes="seven") yield Footer() @@ -397,6 +458,27 @@ class PatternDetailsScreen(Screen): self.query_one("#pattern_input", Input).value = pattern.replace(patternMatch.group(1), 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): dispoSet = trackDescriptor.getDispositionSet() diff --git a/bin/ffx/show_details_screen.py b/bin/ffx/show_details_screen.py index b0790db..f7e0626 100644 --- a/bin/ffx/show_details_screen.py +++ b/bin/ffx/show_details_screen.py @@ -208,7 +208,7 @@ class ShowDetailsScreen(Screen): 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: row_key, col_key = self.patternTable.coordinate_to_cell_key(self.patternTable.cursor_coordinate) diff --git a/bin/ffx/test/jellyfin_combinator.py b/bin/ffx/test/jellyfin_combinator.py deleted file mode 100644 index 47eb877..0000000 --- a/bin/ffx/test/jellyfin_combinator.py +++ /dev/null @@ -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()] diff --git a/bin/ffx/test/jellyfin_combinator_0.py b/bin/ffx/test/jellyfin_combinator_0.py deleted file mode 100644 index 733b0f8..0000000 --- a/bin/ffx/test/jellyfin_combinator_0.py +++ /dev/null @@ -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 diff --git a/bin/ffx/test/jellyfin_combinator_1.py b/bin/ffx/test/jellyfin_combinator_1.py deleted file mode 100644 index 988754d..0000000 --- a/bin/ffx/test/jellyfin_combinator_1.py +++ /dev/null @@ -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 diff --git a/bin/ffx/test/media_combinator_0.py b/bin/ffx/test/media_combinator_0.py index 41c063b..61cb0b2 100644 --- a/bin/ffx/test/media_combinator_0.py +++ b/bin/ffx/test/media_combinator_0.py @@ -6,7 +6,6 @@ from ffx.track_descriptor import TrackDescriptor from ffx.media_descriptor import MediaDescriptor from .media_combinator import MediaCombinator -from .jellyfin_combinator import JellyfinCombinator from .media_tag_combinator import MediaTagCombinator diff --git a/bin/ffx/test/media_combinator_1.py b/bin/ffx/test/media_combinator_1.py index 4fba8a1..5085254 100644 --- a/bin/ffx/test/media_combinator_1.py +++ b/bin/ffx/test/media_combinator_1.py @@ -6,7 +6,6 @@ from ffx.track_descriptor import TrackDescriptor from ffx.media_descriptor import MediaDescriptor from .media_combinator import MediaCombinator -from .jellyfin_combinator import JellyfinCombinator from .media_tag_combinator import MediaTagCombinator diff --git a/bin/ffx/test/media_combinator_2.py b/bin/ffx/test/media_combinator_2.py index 031f795..96d2731 100644 --- a/bin/ffx/test/media_combinator_2.py +++ b/bin/ffx/test/media_combinator_2.py @@ -9,7 +9,7 @@ from .media_combinator import MediaCombinator from .disposition_combinator_2 import DispositionCombinator2 from .track_tag_combinator_2 import TrackTagCombinator2 -from .jellyfin_combinator import JellyfinCombinator +from .permutation_combinator_2 import PermutationCombinator2 from .media_tag_combinator import MediaTagCombinator diff --git a/bin/ffx/test/media_combinator_3.py b/bin/ffx/test/media_combinator_3.py index 43c6d64..e3f000d 100644 --- a/bin/ffx/test/media_combinator_3.py +++ b/bin/ffx/test/media_combinator_3.py @@ -9,7 +9,7 @@ from .media_combinator import MediaCombinator from .disposition_combinator_3 import DispositionCombinator3 from .track_tag_combinator_3 import TrackTagCombinator3 -from .jellyfin_combinator import JellyfinCombinator +from .permutation_combinator_3 import PermutationCombinator3 from .media_tag_combinator import MediaTagCombinator diff --git a/bin/ffx/test/media_combinator_4.py b/bin/ffx/test/media_combinator_4.py index 2224f63..f33d2ed 100644 --- a/bin/ffx/test/media_combinator_4.py +++ b/bin/ffx/test/media_combinator_4.py @@ -9,7 +9,7 @@ from .media_combinator import MediaCombinator from .disposition_combinator_2 import DispositionCombinator2 from .track_tag_combinator_2 import TrackTagCombinator2 -from .jellyfin_combinator import JellyfinCombinator +from .permutation_combinator_2 import PermutationCombinator2 from .media_tag_combinator import MediaTagCombinator diff --git a/bin/ffx/test/media_combinator_5.py b/bin/ffx/test/media_combinator_5.py index e95f989..cff42d8 100644 --- a/bin/ffx/test/media_combinator_5.py +++ b/bin/ffx/test/media_combinator_5.py @@ -9,7 +9,7 @@ from .media_combinator import MediaCombinator from .disposition_combinator_2 import DispositionCombinator2 from .track_tag_combinator_2 import TrackTagCombinator2 -from .jellyfin_combinator import JellyfinCombinator +from .permutation_combinator_2 import PermutationCombinator2 from .media_tag_combinator import MediaTagCombinator diff --git a/bin/ffx/test/media_combinator_6.py b/bin/ffx/test/media_combinator_6.py index 51d62c1..86e9620 100644 --- a/bin/ffx/test/media_combinator_6.py +++ b/bin/ffx/test/media_combinator_6.py @@ -9,7 +9,7 @@ from .media_combinator import MediaCombinator from .disposition_combinator_2 import DispositionCombinator2 from .track_tag_combinator_2 import TrackTagCombinator2 -from .jellyfin_combinator import JellyfinCombinator +from .permutation_combinator_2 import PermutationCombinator2 from .media_tag_combinator import MediaTagCombinator diff --git a/bin/ffx/test/media_combinator_7.py b/bin/ffx/test/media_combinator_7.py index be81c1c..a910bda 100644 --- a/bin/ffx/test/media_combinator_7.py +++ b/bin/ffx/test/media_combinator_7.py @@ -11,7 +11,8 @@ from .disposition_combinator_2 import DispositionCombinator2 from .disposition_combinator_3 import DispositionCombinator3 from .track_tag_combinator_2 import TrackTagCombinator2 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 class MediaCombinator7(MediaCombinator): @@ -30,6 +31,8 @@ class MediaCombinator7(MediaCombinator): def getPayload(self, + audioPermutation, + subtitlePermutation, audioDispositionTuple = (set(), set()), audioTagTuple = ({}, {}), subtitleDispositionTuple = (set(), set(), set()), @@ -116,100 +119,107 @@ class MediaCombinator7(MediaCombinator): def getYield(self): + + pc2 = PermutationCombinator2(self._context) + pc3 = PermutationCombinator3(self._context) + for MTC in MediaTagCombinator.getAllClassReferences(): for DC2_A in DispositionCombinator2.getAllClassReferences(): for TC2_A in TrackTagCombinator2.getAllClassReferences(): for DC3_S in DispositionCombinator3.getAllClassReferences(): for TC3_S in TrackTagCombinator3.getAllClassReferences(): - for J in JellyfinCombinator.getAllClassReferences(): - - j = J(self._context) - self._context['use_jellyfin'] = j.getPayload() - - dc2a = DC2_A(self._context) - tc2a = TC2_A(self._context) - dc3s = DC3_S(self._context) - tc3s = TC3_S(self._context) - - mtc = MTC(self._context) - - yObj = {} - - yObj['identifier'] = self.getIdentifier() - yObj['variants'] = [self.getVariant(), - f"A:{dc2a.getVariant()}", - f"A:{tc2a.getVariant()}", - f"S:{dc3s.getVariant()}", - f"S:{tc3s.getVariant()}", - mtc.getVariant(), - j.getVariant()] - - yObj['payload'] = self.getPayload(dc2a.getPayload(), - tc2a.getPayload(), - dc3s.getPayload(), - tc3s.getPayload()) - - yObj['assertSelectors'] = ['M', 'AD', 'AT', 'SD', 'ST', 'MT', 'J'] - - yObj['assertFuncs'] = [self.assertFunc, - dc2a.createAssertFunc(), - tc2a.createAssertFunc(), - dc3s.createAssertFunc(), - tc3s.createAssertFunc(), - mtc.createAssertFunc(), - j.assertFunc] - - yObj['shouldFail'] = (self.shouldFail() - | dc2a.shouldFail() - | tc2a.shouldFail() - | dc3s.shouldFail() - | tc3s.shouldFail() - | mtc.shouldFail() - | j.shouldFail()) - yieldObj = {'target': yObj} - - if self.__createPresets: - - dc2a_p = DC2_A(self._context, createPresets = True) - tc2a_p = TC2_A(self._context, createPresets = True) - dc3s_p = DC3_S(self._context, createPresets = True) - tc3s_p = TC3_S(self._context, createPresets = True) - - mtc_p = MTC(self._context, createPresets = True) - - yObj_p = {} - - yObj_p['identifier'] = self.getIdentifier() - yObj_p['variants'] = [self.getVariant(), - f"A:{dc2a_p.getVariant()}", - f"A:{tc2a_p.getVariant()}", - f"S:{dc3s_p.getVariant()}", - f"S:{tc3s_p.getVariant()}", - mtc_p.getVariant(), - j.getVariant()] - - yObj_p['payload'] = self.getPayload(dc2a_p.getPayload(), - tc2a_p.getPayload(), - dc3s_p.getPayload(), - tc3s_p.getPayload()) - - yObj_p['assertSelectors'] = ['M', 'AD', 'AT', 'SD', 'ST', 'MT', 'J'] - - yObj_p['assertFuncs'] = [self.assertFunc, - dc2a_p.createAssertFunc(), - tc2a_p.createAssertFunc(), - dc3s_p.createAssertFunc(), - tc3s_p.createAssertFunc(), - mtc_p.createAssertFunc(), - j.assertFunc] - - yObj_p['shouldFail'] = (self.shouldFail() - | dc2a_p.shouldFail() - | tc2a_p.shouldFail() - | dc3s_p.shouldFail() - | tc3s_p.shouldFail() - | mtc_p.shouldFail() - | j.shouldFail()) - yieldObj['preset'] = yObj_p - - yield yieldObj + for p2y in pc2.getYield(): + for p3y in pc3.getYield(): + + dc2a = DC2_A(self._context) + tc2a = TC2_A(self._context) + dc3s = DC3_S(self._context) + tc3s = TC3_S(self._context) + + mtc = MTC(self._context) + + yObj = {} + + yObj['identifier'] = self.getIdentifier() + yObj['variants'] = [self.getVariant(), + f"A:{p2y['variant']}", + f"S:{p3y['variant']}", + f"A:{dc2a.getVariant()}", + f"A:{tc2a.getVariant()}", + f"S:{dc3s.getVariant()}", + f"S:{tc3s.getVariant()}", + mtc.getVariant()] + + yObj['payload'] = self.getPayload(p2y['permutation'], + p3y['permutation'], + dc2a.getPayload(), + tc2a.getPayload(), + dc3s.getPayload(), + tc3s.getPayload()) + + yObj['assertSelectors'] = ['M', 'AP', 'SP', 'AD', 'AT', 'SD', 'ST', 'MT'] + + yObj['assertFuncs'] = [self.assertFunc, + p2y.createAssertFunc(), + p3y.createAssertFunc(), + dc2a.createAssertFunc(), + tc2a.createAssertFunc(), + dc3s.createAssertFunc(), + tc3s.createAssertFunc(), + mtc.createAssertFunc()] + + yObj['shouldFail'] = (self.shouldFail() + | p2y.shouldFail() + | p3y.shouldFail() + | dc2a.shouldFail() + | tc2a.shouldFail() + | dc3s.shouldFail() + | tc3s.shouldFail() + | mtc.shouldFail()) + yieldObj = {'target': yObj} + + if self.__createPresets: + + dc2a_p = DC2_A(self._context, createPresets = True) + tc2a_p = TC2_A(self._context, createPresets = True) + dc3s_p = DC3_S(self._context, createPresets = True) + tc3s_p = TC3_S(self._context, createPresets = True) + + mtc_p = MTC(self._context, createPresets = True) + + yObj_p = {} + + yObj_p['identifier'] = self.getIdentifier() + yObj_p['variants'] = [self.getVariant(), + f"A:{dc2a_p.getVariant()}", + f"A:{tc2a_p.getVariant()}", + f"S:{dc3s_p.getVariant()}", + f"S:{tc3s_p.getVariant()}", + mtc_p.getVariant(), + j.getVariant()] + + yObj_p['payload'] = self.getPayload(dc2a_p.getPayload(), + tc2a_p.getPayload(), + dc3s_p.getPayload(), + tc3s_p.getPayload()) + + yObj_p['assertSelectors'] = ['M', 'AD', 'AT', 'SD', 'ST', 'MT', 'J'] + + yObj_p['assertFuncs'] = [self.assertFunc, + dc2a_p.createAssertFunc(), + tc2a_p.createAssertFunc(), + dc3s_p.createAssertFunc(), + tc3s_p.createAssertFunc(), + mtc_p.createAssertFunc(), + j.assertFunc] + + yObj_p['shouldFail'] = (self.shouldFail() + | dc2a_p.shouldFail() + | tc2a_p.shouldFail() + | dc3s_p.shouldFail() + | tc3s_p.shouldFail() + | mtc_p.shouldFail() + | j.shouldFail()) + yieldObj['preset'] = yObj_p + + yield yieldObj diff --git a/bin/ffx/test/permutation_combinator_2.py b/bin/ffx/test/permutation_combinator_2.py new file mode 100644 index 0000000..c8b57ca --- /dev/null +++ b/bin/ffx/test/permutation_combinator_2.py @@ -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) diff --git a/bin/ffx/test/permutation_combinator_3.py b/bin/ffx/test/permutation_combinator_3.py new file mode 100644 index 0000000..2f74f1e --- /dev/null +++ b/bin/ffx/test/permutation_combinator_3.py @@ -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) diff --git a/bin/ffx/track_controller.py b/bin/ffx/track_controller.py index 2eae79f..f1d2a01 100644 --- a/bin/ffx/track_controller.py +++ b/bin/ffx/track_controller.py @@ -66,6 +66,8 @@ class TrackController(): track : Track = q.first() + track.index = int(trackDescriptor.getIndex()) + track.track_type = int(trackDescriptor.getType().index()) track.codec_name = str(trackDescriptor.getCodec()) track.audio_layout = int(trackDescriptor.getAudioLayout().index()) @@ -103,13 +105,34 @@ class TrackController(): s = self.Session() 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: raise click.ClickException(f"TrackController.findTracks(): {repr(ex)}") finally: 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 ^ def findVideoTracks(self, patternId): @@ -233,8 +256,8 @@ class TrackController(): s.close() - def setDefaultSubTrack(self, trackType, subIndex): - pass - - def setForcedSubTrack(self, trackType, subIndex): - pass +# def setDefaultSubTrack(self, trackType, subIndex): +# pass +# +# def setForcedSubTrack(self, trackType, subIndex): +# pass