From 71943dbabc3ed0b7ed17a519ca341676ae5347d1 Mon Sep 17 00:00:00 2001 From: Maveno Date: Sat, 16 Nov 2024 21:05:01 +0100 Subject: [PATCH] shifted seasons UI part --- bin/ffx/model/shifted_season.py | 33 +- bin/ffx/shifted_season_controller.py | 248 +++++++++---- bin/ffx/shifted_season_delete_screen.py | 65 ++-- bin/ffx/shifted_season_details_screen.py | 440 +++++------------------ bin/ffx/show_details_screen.py | 100 +++++- 5 files changed, 418 insertions(+), 468 deletions(-) diff --git a/bin/ffx/model/shifted_season.py b/bin/ffx/model/shifted_season.py index ffead60..d0ae795 100644 --- a/bin/ffx/model/shifted_season.py +++ b/bin/ffx/model/shifted_season.py @@ -27,7 +27,7 @@ class ShiftedSeason(Base): # show: Mapped["Show"] = relationship(back_populates="patterns") - src_season = Column(Integer) + original_season = Column(Integer) first_episode = Column(Integer, default = -1) last_episode = Column(Integer, default = -1) @@ -36,6 +36,20 @@ class ShiftedSeason(Base): episode_offset = Column(Integer, default = 0) + def getId(self): + return self.id + + + def getOriginalSeason(self): + return self.original_season + + def getFirstEpisode(self): + return self.first_episode + + def getLastEpisode(self): + return self.last_episode + + def getSeasonOffset(self): return self.season_offset @@ -43,10 +57,15 @@ class ShiftedSeason(Base): return self.episode_offset - def getShifted(self, season, episode): + def getObj(self): + + shiftedSeasonObj = {} + + shiftedSeasonObj['original_season'] = self.getOriginalSeason() + shiftedSeasonObj['first_episode'] = self.getFirstEpisode() + shiftedSeasonObj['last_episode'] = self.getLastEpisode() + shiftedSeasonObj['season_offset'] = self.getSeasonOffset() + shiftedSeasonObj['episode_offset'] = self.getEpisodeOffset() + + return shiftedSeasonObj - if season == self.src_season and episode >= self.first_episode and episode <= self.last_episode: - return season + self.season_offset, episode + self.episode_offset - - else: - return season, episode diff --git a/bin/ffx/shifted_season_controller.py b/bin/ffx/shifted_season_controller.py index c4192dc..7102d1d 100644 --- a/bin/ffx/shifted_season_controller.py +++ b/bin/ffx/shifted_season_controller.py @@ -1,7 +1,14 @@ import click, re +from sqlalchemy import not_ from ffx.model.shifted_season import ShiftedSeason +class EpisodeOrderException(Exception): + pass + +class RangeOverlapException(Exception): + pass + class ShiftedSeasonController(): @@ -10,42 +17,91 @@ class ShiftedSeasonController(): self.context = context self.Session = self.context['database']['session'] # convenience + def checkShiftedSeason(self, showId: int, shiftedSeasonObj: dict, shiftedSeasonId: int = 0): + try: + s = self.Session() - def addPattern(self, patternDescriptor): + firstEpisode = int(shiftedSeasonObj['first_episode']) + lastEpisode = int(shiftedSeasonObj['last_episode']) - try: + q = s.query(ShiftedSeason).filter(ShiftedSeason.show_id == int(showId)) + if shiftedSeasonId: + q = q.filter(ShiftedSeason.id != int(shiftedSeasonId)) + + siblingShiftedSeason: ShiftedSeason + for siblingShiftedSeason in q.all(): + + siblingFirstEpisode = siblingShiftedSeason.getFirstEpisode() + siblingLastEpisode = siblingShiftedSeason.getLastEpisode() + + if (lastEpisode >= siblingFirstEpisode + and siblingLastEpisode >= firstEpisode): + return False + return True + + except Exception as ex: + raise click.ClickException(f"ShiftedSeasonController.addShiftedSeason(): {repr(ex)}") + finally: + s.close() + + + def addShiftedSeason(self, showId: int, shiftedSeasonObj: dict): + + if type(showId) is not int: + raise ValueError(f"ShiftedSeasonController.addShiftedSeason(): Argument showId is required to be of type int") + if type(shiftedSeasonObj) is not dict: + raise ValueError(f"ShiftedSeasonController.addShiftedSeason(): Argument shiftedSeasonObj is required to be of type dict") + + try: s = self.Session() - q = s.query(Pattern).filter(Pattern.show_id == int(patternDescriptor['show_id']), - Pattern.pattern == str(patternDescriptor['pattern'])) - if not q.count(): - pattern = Pattern(show_id = int(patternDescriptor['show_id']), - pattern = str(patternDescriptor['pattern'])) - s.add(pattern) - s.commit() - return pattern.getId() - else: - return 0 + firstEpisode = int(shiftedSeasonObj['first_episode']) + lastEpisode = int(shiftedSeasonObj['last_episode']) + + if lastEpisode < firstEpisode: + raise EpisodeOrderException() + + q = s.query(ShiftedSeason).filter(ShiftedSeason.show_id == int(showId)) + + shiftedSeason = ShiftedSeason(show_id = int(showId), + original_season = int(shiftedSeasonObj['original_season']), + first_episode = firstEpisode, + last_episode = lastEpisode, + season_offset = int(shiftedSeasonObj['season_offset']), + episode_offset = int(shiftedSeasonObj['episode_offset'])) + s.add(shiftedSeason) + s.commit() + return shiftedSeason.getId() except Exception as ex: - raise click.ClickException(f"PatternController.addPattern(): {repr(ex)}") + raise click.ClickException(f"ShiftedSeasonController.addShiftedSeason(): {repr(ex)}") finally: s.close() - def updatePattern(self, patternId, patternDescriptor): + def updateShiftedSeason(self, shiftedSeasonId: int, shiftedSeasonObj: dict): + + if type(shiftedSeasonId) is not int: + raise ValueError(f"ShiftedSeasonController.updateShiftedSeason(): Argument shiftedSeasonId is required to be of type int") + + if type(shiftedSeasonObj) is not dict: + raise ValueError(f"ShiftedSeasonController.updateShiftedSeason(): Argument shiftedSeasonObj is required to be of type dict") try: s = self.Session() - q = s.query(Pattern).filter(Pattern.id == int(patternId)) + + q = s.query(ShiftedSeason).filter(ShiftedSeason.id == int(shiftedSeasonId)) if q.count(): - pattern = q.first() + shiftedSeason = q.first() - pattern.show_id = int(patternDescriptor['show_id']) - pattern.pattern = str(patternDescriptor['pattern']) + shiftedSeason.original_season = int(shiftedSeasonObj['original_season']) + shiftedSeason.first_episode = int(shiftedSeasonObj['first_episode']) + shiftedSeason.last_episode = int(shiftedSeasonObj['last_episode']) + shiftedSeason.season_offset = int(shiftedSeasonObj['season_offset']) + shiftedSeason.episode_offset = int(shiftedSeasonObj['episode_offset']) s.commit() return True @@ -54,103 +110,149 @@ class ShiftedSeasonController(): return False except Exception as ex: - raise click.ClickException(f"PatternController.updatePattern(): {repr(ex)}") + raise click.ClickException(f"ShiftedSeasonController.updateShiftedSeason(): {repr(ex)}") finally: s.close() + def findShiftedSeason(self, showId: int, originalSeason: int, firstEpisode: int, lastEpisode: int): + + if type(showId) is not int: + raise ValueError(f"ShiftedSeasonController.findShiftedSeason(): Argument shiftedSeasonId is required to be of type int") + + if type(originalSeason) is not int: + raise ValueError(f"ShiftedSeasonController.findShiftedSeason(): Argument originalSeason is required to be of type int") + + if type(firstEpisode) is not int: + raise ValueError(f"ShiftedSeasonController.findShiftedSeason(): Argument firstEpisode is required to be of type int") + + if type(lastEpisode) is not int: + raise ValueError(f"ShiftedSeasonController.findShiftedSeason(): Argument lastEpisode is required to be of type int") - def findPattern(self, patternDescriptor): - try: s = self.Session() - q = s.query(Pattern).filter(Pattern.show_id == int(patternDescriptor['show_id']), Pattern.pattern == str(patternDescriptor['pattern'])) + q = s.query(ShiftedSeason).filter(ShiftedSeason.show_id == int(showId), + ShiftedSeason.original_season == int(originalSeason), + ShiftedSeason.first_episode == int(firstEpisode), + ShiftedSeason.last_episode == int(lastEpisode)) - if q.count(): - pattern = q.first() - return int(pattern.id) - else: - return None + return q.first().getId() if q.count() else None + + except Exception as ex: + raise click.ClickException(f"PatternController.findShiftedSeason(): {repr(ex)}") + finally: + s.close() + + def getShiftedSeasonSiblings(self, showId: int): + + if type(showId) is not int: + raise ValueError(f"ShiftedSeasonController.getShiftedSeasonSiblings(): Argument shiftedSeasonId is required to be of type int") + + try: + s = self.Session() + q = s.query(ShiftedSeason).filter(ShiftedSeason.show_id == int(showId)) + + return q.all() except Exception as ex: - raise click.ClickException(f"PatternController.findPattern(): {repr(ex)}") + raise click.ClickException(f"PatternController.getShiftedSeasonSiblings(): {repr(ex)}") finally: s.close() - def getPattern(self, patternId : int): + def getShiftedSeason(self, shiftedSeasonId: int): - if type(patternId) is not int: - raise ValueError(f"PatternController.getPattern(): Argument patternId is required to be of type int") + if type(shiftedSeasonId) is not int: + raise ValueError(f"ShiftedSeasonController.getShiftedSeason(): Argument shiftedSeasonId is required to be of type int") try: s = self.Session() - q = s.query(Pattern).filter(Pattern.id == int(patternId)) + q = s.query(ShiftedSeason).filter(ShiftedSeason.id == int(shiftedSeasonId)) return q.first() if q.count() else None except Exception as ex: - raise click.ClickException(f"PatternController.getPattern(): {repr(ex)}") + raise click.ClickException(f"ShiftedSeasonController.getShiftedSeason(): {repr(ex)}") finally: s.close() - def deletePattern(self, patternId): + def deleteShiftedSeason(self, shiftedSeasonId): + + if type(shiftedSeasonId) is not int: + raise ValueError(f"ShiftedSeasonController.deleteShiftedSeason(): Argument shiftedSeasonId is required to be of type int") + try: s = self.Session() - q = s.query(Pattern).filter(Pattern.id == int(patternId)) + q = s.query(ShiftedSeason).filter(ShiftedSeason.id == int(shiftedSeasonId)) if q.count(): #DAFUQ: https://stackoverflow.com/a/19245058 # q.delete() - pattern = q.first() - s.delete(pattern) + shiftedSeason = q.first() + s.delete(shiftedSeason) s.commit() return True return False except Exception as ex: - raise click.ClickException(f"PatternController.deletePattern(): {repr(ex)}") + raise click.ClickException(f"ShiftedSeasonController.deleteShiftedSeason(): {repr(ex)}") finally: s.close() - def matchFilename(self, filename : str) -> dict: - """Returns dict {'match': , 'pattern': } or empty dict of no pattern was found""" +# def matchFilename(self, filename : str) -> dict: +# """Returns dict {'match': , 'pattern': } or empty dict of no pattern was found""" +# +# try: +# s = self.Session() +# q = s.query(Pattern) +# +# matchResult = {} +# +# for pattern in q.all(): +# patternMatch = re.search(str(pattern.pattern), str(filename)) +# if patternMatch is not None: +# matchResult['match'] = patternMatch +# matchResult['pattern'] = pattern +# +# return matchResult +# +# except Exception as ex: +# raise click.ClickException(f"PatternController.matchFilename(): {repr(ex)}") +# 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.getMediaDescriptor(): {repr(ex)}") +# # finally: +# # s.close() + + def shift(self, season, episode): + + if season == self.original_season and episode >= self.first_episode and episode <= self.last_episode: + return season + self.season_offset, episode + self.episode_offset + + else: + return season, episode - try: - s = self.Session() - q = s.query(Pattern) - - matchResult = {} - - for pattern in q.all(): - patternMatch = re.search(str(pattern.pattern), str(filename)) - if patternMatch is not None: - matchResult['match'] = patternMatch - matchResult['pattern'] = pattern - - return matchResult - - except Exception as ex: - raise click.ClickException(f"PatternController.matchFilename(): {repr(ex)}") - finally: - s.close() + def unshift(self, season, episode): -# 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 + if season == self.original_season and episode >= self.first_episode and episode <= self.last_episode: + return season + self.season_offset, episode + self.episode_offset + + else: + return season, episode diff --git a/bin/ffx/shifted_season_delete_screen.py b/bin/ffx/shifted_season_delete_screen.py index 542fbf6..1d993cf 100644 --- a/bin/ffx/shifted_season_delete_screen.py +++ b/bin/ffx/shifted_season_delete_screen.py @@ -9,6 +9,10 @@ from .pattern_controller import PatternController from ffx.model.pattern import Pattern +from .shifted_season_controller import ShiftedSeasonController + +from ffx.model.shifted_season import ShiftedSeason + # Screen[dict[int, str, int]] class ShiftedSeasonDeleteScreen(Screen): @@ -44,25 +48,28 @@ class ShiftedSeasonDeleteScreen(Screen): } """ - def __init__(self, patternId = None, showId = None): + def __init__(self, showId = None, shiftedSeasonId = None): super().__init__() self.context = self.app.getContext() self.Session = self.context['database']['session'] # convenience - self.__pc = PatternController(context = self.context) - self.__sc = ShowController(context = self.context) + self.__ssc = ShiftedSeasonController(context = self.context) - 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 {} + self._showId = showId + self.__shiftedSeasonId = shiftedSeasonId def on_mount(self): - 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)) + + shiftedSeason: ShiftedSeason = self.__ssc.getShiftedSeason(self.__shiftedSeasonId) + + self.query_one("#static_show_id", Static).update(str(self._showId)) + self.query_one("#static_original_season", Static).update(str(shiftedSeason.getOriginalSeason())) + self.query_one("#static_first_episode", Static).update(str(shiftedSeason.getFirstEpisode())) + self.query_one("#static_last_episode", Static).update(str(shiftedSeason.getLastEpisode())) + self.query_one("#static_season_offset", Static).update(str(shiftedSeason.getSeasonOffset())) + self.query_one("#static_episode_offset", Static).update(str(shiftedSeason.getEpisodeOffset())) def compose(self): @@ -71,19 +78,31 @@ class ShiftedSeasonDeleteScreen(Screen): with Grid(): - yield Static("Are you sure to delete the following filename pattern?", id="toplabel", classes="two") - - yield Static("", classes="two") - - yield Static("Pattern") - yield Static("", id="patternlabel") + yield Static("Are you sure to delete the following shifted season?", id="toplabel", classes="two") - yield Static("", classes="two") + yield Static(" ", classes="two") yield Static("from show") - yield Static("", id="showlabel") + yield Static(" ", id="static_show_id") + + yield Static(" ", classes="two") + + yield Static("Original season") + yield Static(" ", id="static_original_season") + + yield Static("First episode") + yield Static(" ", id="static_first_episode") + + yield Static("Last episode") + yield Static(" ", id="static_last_episode") + + yield Static("Season offset") + yield Static(" ", id="static_season_offset") + + yield Static("Episode offset") + yield Static(" ", id="static_episode_offset") - yield Static("", classes="two") + yield Static(" ", classes="two") yield Button("Delete", id="delete_button") yield Button("Cancel", id="cancel_button") @@ -96,11 +115,11 @@ class ShiftedSeasonDeleteScreen(Screen): if event.button.id == "delete_button": - if self.__patternId is None: - raise click.ClickException('PatternDeleteScreen.on_button_pressed(): pattern id is undefined') + if self.__shiftedSeasonId is None: + raise click.ClickException('ShiftedSeasonDeleteScreen.on_button_pressed(): shifted season id is undefined') - if self.__pc.deletePattern(self.__patternId): - self.dismiss(self.__pattern) + if self.__ssc.deleteShiftedSeason(self.__shiftedSeasonId): + self.dismiss(self.__shiftedSeasonId) else: #TODO: Meldung diff --git a/bin/ffx/shifted_season_details_screen.py b/bin/ffx/shifted_season_details_screen.py index c173177..3a6ef95 100644 --- a/bin/ffx/shifted_season_details_screen.py +++ b/bin/ffx/shifted_season_details_screen.py @@ -11,27 +11,10 @@ from ffx.model.show import Show from ffx.model.pattern import Pattern from ffx.model.track import Track -from .pattern_controller import PatternController +from .shifted_season_controller import ShiftedSeasonController from .show_controller import ShowController -from .track_controller import TrackController -from .tag_controller import TagController -from .track_details_screen import TrackDetailsScreen -from .track_delete_screen import TrackDeleteScreen - -from .tag_details_screen import TagDetailsScreen -from .tag_delete_screen import TagDeleteScreen - -from ffx.track_type import TrackType - -from ffx.track_disposition import TrackDisposition -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 -from ffx.audio_layout import AudioLayout +from ffx.model.shifted_season import ShiftedSeason # Screen[dict[int, str, int]] @@ -96,145 +79,37 @@ class ShiftedSeasonDetailsScreen(Screen): } """ - def __init__(self, patternId = None, showId = None): + def __init__(self, showId = None, shiftedSeasonId = None): super().__init__() self.context = self.app.getContext() self.Session = self.context['database']['session'] # convenience - self.__pc = PatternController(context = self.context) - self.__sc = ShowController(context = self.context) - self.__tc = TrackController(context = self.context) - self.__tac = TagController(context = self.context) - - self.__pattern : Pattern = self.__pc.getPattern(patternId) if patternId is not None else None - self.__showDescriptor = self.__sc.getShowDescriptor(showId) if showId is not None else None - - -# #TODO: per controller -# def loadTracks(self, show_id): -# -# try: -# -# tracks = {} -# tracks['audio'] = {} -# tracks['subtitle'] = {} -# -# s = self.Session() -# q = s.query(Pattern).filter(Pattern.show_id == int(show_id)) -# -# return [{'id': int(p.id), 'pattern': p.pattern} for p in q.all()] -# -# except Exception as ex: -# raise click.ClickException(f"loadTracks(): {repr(ex)}") -# finally: -# s.close() - - -# def updateTracks(self): -# -# self.tracksTable.clear() -# -# if self.__pattern is not None: -# -# tracks = self.__tc.findTracks(self.__pattern.getId()) -# -# typeCounter = {} -# -# tr: Track -# for tr in tracks: -# -# td : TrackDescriptor = tr.getDescriptor(self.context) -# -# trackType = td.getType() -# if not trackType in typeCounter.keys(): -# typeCounter[trackType] = 0 -# -# dispoSet = td.getDispositionSet() -# -# trackLanguage = td.getLanguage() -# audioLayout = td.getAudioLayout() -# row = (td.getIndex(), -# trackType.label(), -# typeCounter[trackType], -# td.getCodec(), -# audioLayout.label() if trackType == TrackType.AUDIO -# and audioLayout != AudioLayout.LAYOUT_UNDEFINED else ' ', -# 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', -# 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() -# -# if self.__pattern is not None: -# -# # raise click.ClickException(f"patternid={self.__pattern.getId()}") # 1 -# -# tags = self.__tac.findAllMediaTags(self.__pattern.getId()) -# -# #raise click.ClickException(f"tags={tags}") # encoder:blah -# -# for tagKey, tagValue in tags.items(): -# row = (tagKey, tagValue) -# self.tagsTable.add_row(*map(str, row)) - - -# def on_mount(self): -# -# if not self.__showDescriptor is None: -# self.query_one("#showlabel", Static).update(f"{self.__showDescriptor.getId()} - {self.__showDescriptor.getName()} ({self.__showDescriptor.getYear()})") -# -# if self.__pattern is not None: -# -# self.query_one("#pattern_input", Input).value = str(self.__pattern.getPattern()) -# -# self.updateTags() -# self.updateTracks() + self.__ssc = ShiftedSeasonController(context = self.context) + + self.__showId = showId + self.__shiftedSeasonId = shiftedSeasonId + + def on_mount(self): + + if self.__shiftedSeasonId is not None: + shiftedSeason: ShiftedSeason = self.__ssc.getShiftedSeason(self.__shiftedSeasonId) + + originalSeason = shiftedSeason.getOriginalSeason() + self.query_one("#input_original_season", Input).value = str(originalSeason) + + firstEpisode = shiftedSeason.getFirstEpisode() + self.query_one("#input_first_episode", Input).value = str(firstEpisode) if firstEpisode != -1 else '' + + lastEpisode = shiftedSeason.getLastEpisode() + self.query_one("#input_last_episode", Input).value = str(lastEpisode) if lastEpisode != -1 else '' + + seasonOffset = shiftedSeason.getSeasonOffset() + self.query_one("#input_season_offset", Input).value = str(seasonOffset) if seasonOffset else '' + + episodeOffset = shiftedSeason.getEpisodeOffset() + self.query_one("#input_episode_offset", Input).value = str(episodeOffset) if episodeOffset else '' + def compose(self): @@ -243,7 +118,7 @@ class ShiftedSeasonDetailsScreen(Screen): with Grid(): # 1 - yield Static("Edit shifted season" if self.__pattern is not None else "New shifted season", id="toplabel", classes="three") + yield Static("Edit shifted season" if self.__shiftedSeasonId is not None else "New shifted season", id="toplabel", classes="three") # 2 yield Static(" ", classes="three") @@ -258,7 +133,7 @@ class ShiftedSeasonDetailsScreen(Screen): # 5 yield Static("Last Episode") - yield Input(id="imput_last_episode", classes="two") + yield Input(id="input_last_episode", classes="two") # 6 yield Static("Season offset") @@ -282,209 +157,66 @@ class ShiftedSeasonDetailsScreen(Screen): yield Footer() -# def getPatternFromInput(self): -# return str(self.query_one("#pattern_input", Input).value) - - - -# def getSelectedTrackDescriptor(self): -# -# if not self.__pattern: -# return None -# -# try: -# -# # Fetch the currently selected row when 'Enter' is pressed -# #selected_row_index = self.table.cursor_row -# row_key, col_key = self.tracksTable.coordinate_to_cell_key(self.tracksTable.cursor_coordinate) -# -# if row_key is not None: -# selected_track_data = self.tracksTable.get_row(row_key) -# -# trackIndex = int(selected_track_data[0]) -# trackSubIndex = int(selected_track_data[2]) -# -# return self.__tc.getTrack(self.__pattern.getId(), trackIndex).getDescriptor(self.context, subIndex=trackSubIndex) -# -# else: -# return None -# -# except CellDoesNotExist: -# return None - - - -# def getSelectedTag(self): -# -# try: -# -# # Fetch the currently selected row when 'Enter' is pressed -# #selected_row_index = self.table.cursor_row -# row_key, col_key = self.tagsTable.coordinate_to_cell_key(self.tagsTable.cursor_coordinate) -# -# if row_key is not None: -# selected_tag_data = self.tagsTable.get_row(row_key) -# -# tagKey = str(selected_tag_data[0]) -# tagValue = str(selected_tag_data[1]) -# -# return tagKey, tagValue -# -# else: -# return None -# -# except CellDoesNotExist: -# return None + def getShiftedSeasonObjFromInput(self): + + shiftedSeasonObj = {} + + originalSeason = self.query_one("#input_original_season", Input).value + if not originalSeason: + return None + shiftedSeasonObj['original_season'] = int(originalSeason) + + try: + shiftedSeasonObj['first_episode'] = int(self.query_one("#input_first_episode", Input).value) + except ValueError: + shiftedSeasonObj['first_episode'] = -1 + + try: + shiftedSeasonObj['last_episode'] = int(self.query_one("#input_last_episode", Input).value) + except ValueError: + shiftedSeasonObj['last_episode'] = -1 + + try: + shiftedSeasonObj['season_offset'] = int(self.query_one("#input_season_offset", Input).value) + except ValueError: + shiftedSeasonObj['season_offset'] = 0 + + try: + shiftedSeasonObj['episode_offset'] = int(self.query_one("#input_episode_offset", Input).value) + except ValueError: + shiftedSeasonObj['episode_offset'] = 0 + + + + return shiftedSeasonObj # Event handler for button press def on_button_pressed(self, event: Button.Pressed) -> None: - pass # Check if the button pressed is the one we are interested in -# if event.button.id == "save_button": -# -# patternDescriptor = {} -# patternDescriptor['show_id'] = self.__showDescriptor.getId() -# patternDescriptor['pattern'] = self.getPatternFromInput() -# -# if self.__pattern is not None: -# -# if self.__pc.updatePattern(self.__pattern.getId(), patternDescriptor): -# self.dismiss(patternDescriptor) -# else: -# #TODO: Meldung -# self.app.pop_screen() -# -# else: -# patternId = self.__pc.addPattern(patternDescriptor) -# if patternId: -# self.dismiss(patternDescriptor) -# else: -# #TODO: Meldung -# self.app.pop_screen() -# -# -# if event.button.id == "cancel_button": -# self.app.pop_screen() -# -# -# # Save pattern when just created before adding streams -# if self.__pattern is not None: -# -# numTracks = len(self.tracksTable.rows) -# -# if event.button.id == "button_add_track": -# self.app.push_screen(TrackDetailsScreen(patternId = self.__pattern.getId(), index = numTracks), self.handle_add_track) -# -# selectedTrack = self.getSelectedTrackDescriptor() -# if selectedTrack is not None: -# if event.button.id == "button_edit_track": -# self.app.push_screen(TrackDetailsScreen(trackDescriptor = selectedTrack), self.handle_edit_track) -# if event.button.id == "button_delete_track": -# self.app.push_screen(TrackDeleteScreen(trackDescriptor = selectedTrack), self.handle_delete_track) - - -# if event.button.id == "button_add_tag": -# if self.__pattern is not None: -# self.app.push_screen(TagDetailsScreen(), self.handle_update_tag) -# -# if event.button.id == "button_edit_tag": -# tagKey, tagValue = self.getSelectedTag() -# self.app.push_screen(TagDetailsScreen(key=tagKey, value=tagValue), self.handle_update_tag) -# -# if event.button.id == "button_delete_tag": -# tagKey, tagValue = self.getSelectedTag() -# self.app.push_screen(TagDeleteScreen(key=tagKey, value=tagValue), self.handle_delete_tag) - -# -# if event.button.id == "pattern_button": -# -# pattern = self.query_one("#pattern_input", Input).value -# -# patternMatch = re.search(FileProperties.SE_INDICATOR_PATTERN, pattern) -# -# if patternMatch: -# 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() -# trackType = trackDescriptor.getType() -# index = trackDescriptor.getIndex() -# subIndex = trackDescriptor.getSubIndex() -# language = trackDescriptor.getLanguage() -# title = trackDescriptor.getTitle() -# -# row = (index, -# trackType.label(), -# subIndex, -# " ", -# language.label(), -# title, -# 'Yes' if TrackDisposition.DEFAULT in dispoSet else 'No', -# 'Yes' if TrackDisposition.FORCED in dispoSet else 'No') -# -# self.tracksTable.add_row(*map(str, row)) -# -# -# def handle_edit_track(self, trackDescriptor : TrackDescriptor): -# -# try: -# -# row_key, col_key = self.tracksTable.coordinate_to_cell_key(self.tracksTable.cursor_coordinate) -# -# self.tracksTable.update_cell(row_key, self.column_key_track_audio_layout, trackDescriptor.getAudioLayout().label()) -# self.tracksTable.update_cell(row_key, self.column_key_track_language, trackDescriptor.getLanguage().label()) -# self.tracksTable.update_cell(row_key, self.column_key_track_title, trackDescriptor.getTitle()) -# self.tracksTable.update_cell(row_key, self.column_key_track_default, 'Yes' if TrackDisposition.DEFAULT in trackDescriptor.getDispositionSet() else 'No') -# self.tracksTable.update_cell(row_key, self.column_key_track_forced, 'Yes' if TrackDisposition.FORCED in trackDescriptor.getDispositionSet() else 'No') -# -# except CellDoesNotExist: -# pass -# -# -# def handle_delete_track(self, trackDescriptor : TrackDescriptor): -# self.updateTracks() -# -# -# -# def handle_update_tag(self, tag): -# -# if self.__pattern is None: -# raise click.ClickException(f"PatternDetailsScreen.handle_update_tag: pattern not set") -# -# if self.__tac.updateMediaTag(self.__pattern.getId(), tag[0], tag[1]) is not None: -# self.updateTags() -# -# def handle_delete_tag(self, tag): -# -# if self.__pattern is None: -# raise click.ClickException(f"PatternDetailsScreen.handle_delete_tag: pattern not set") -# -# if self.__tac.deleteMediaTagByKey(self.__pattern.getId(), tag[0]): -# self.updateTags() -# \ No newline at end of file + if event.button.id == "save_button": + + shiftedSeasonObj = self.getShiftedSeasonObjFromInput() + + if shiftedSeasonObj is not None: + + if self.__shiftedSeasonId is not None: + + if self.__ssc.checkShiftedSeason(self.__showId, shiftedSeasonObj, + shiftedSeasonId = self.__shiftedSeasonId): + if self.__ssc.updateShiftedSeason(self.__shiftedSeasonId, shiftedSeasonObj): + self.dismiss((self.__shiftedSeasonId, shiftedSeasonObj)) + else: + #TODO: Meldung + self.app.pop_screen() + + else: + if self.__ssc.checkShiftedSeason(self.__showId, shiftedSeasonObj): + self.__shiftedSeasonId = self.__ssc.addShiftedSeason(self.__showId, shiftedSeasonObj) + self.dismiss((self.__shiftedSeasonId, shiftedSeasonObj)) + + + if event.button.id == "cancel_button": + self.app.pop_screen() diff --git a/bin/ffx/show_details_screen.py b/bin/ffx/show_details_screen.py index ffff715..110ebb1 100644 --- a/bin/ffx/show_details_screen.py +++ b/bin/ffx/show_details_screen.py @@ -16,10 +16,14 @@ from .pattern_delete_screen import PatternDeleteScreen from .show_controller import ShowController from .pattern_controller import PatternController from .tmdb_controller import TmdbController +from .shifted_season_controller import ShiftedSeasonController from .show_descriptor import ShowDescriptor from .shifted_season_details_screen import ShiftedSeasonDetailsScreen +from .shifted_season_delete_screen import ShiftedSeasonDeleteScreen + +from ffx.model.shifted_season import ShiftedSeason from .helper import filterFilename @@ -88,6 +92,7 @@ class ShowDetailsScreen(Screen): self.__sc = ShowController(context = self.context) self.__pc = PatternController(context = self.context) self.__tc = TmdbController() + self.__ssc = ShiftedSeasonController(context = self.context) self.__showDescriptor = self.__sc.getShowDescriptor(showId) if showId is not None else None @@ -106,11 +111,43 @@ class ShowDetailsScreen(Screen): s.close() - def on_mount(self): + + def updateShiftedSeasons(self): + + self.shiftedSeasonsTable.clear() if not self.__showDescriptor is None: - self.query_one("#id_static", Static).update(str(self.__showDescriptor.getId())) + showId = int(self.__showDescriptor.getId()) + + shiftedSeason: ShiftedSeason + for shiftedSeason in self.__ssc.getShiftedSeasonSiblings(showId=showId): + + shiftedSeasonObj = shiftedSeason.getObj() + + firstEpisode = shiftedSeasonObj['first_episode'] + firstEpisodeStr = str(firstEpisode) if firstEpisode != -1 else '' + + lastEpisode = shiftedSeasonObj['last_episode'] + lastEpisodeStr = str(lastEpisode) if lastEpisode != -1 else '' + + row = (shiftedSeasonObj['original_season'], + firstEpisodeStr, + lastEpisodeStr, + shiftedSeasonObj['season_offset'], + shiftedSeasonObj['episode_offset']) + + self.shiftedSeasonsTable.add_row(*map(str, row)) + + + + def on_mount(self): + + if self.__showDescriptor is not None: + + showId = int(self.__showDescriptor.getId()) + + self.query_one("#id_static", Static).update(str(showId)) self.query_one("#name_input", Input).value = str(self.__showDescriptor.getName()) self.query_one("#year_input", Input).value = str(self.__showDescriptor.getYear()) @@ -119,7 +156,7 @@ class ShowDetailsScreen(Screen): self.query_one("#indicator_season_digits_input", Input).value = str(self.__showDescriptor.getIndicatorSeasonDigits()) self.query_one("#indicator_episode_digits_input", Input).value = str(self.__showDescriptor.getIndicatorEpisodeDigits()) - showId = int(self.__showDescriptor.getId()) + #raise click.ClickException(f"show_id {showId}") patternList = self.loadPatterns(showId) # raise click.ClickException(f"patternList {patternList}") @@ -127,6 +164,8 @@ class ShowDetailsScreen(Screen): row = (pattern['pattern'],) self.patternTable.add_row(*map(str, row)) + self.updateShiftedSeasons() + else: self.query_one("#index_season_digits_input", Input).value = "2" @@ -157,7 +196,41 @@ class ShowDetailsScreen(Screen): return selectedPattern + def getSelectedShiftedSeasonObjFromInput(self): + + shiftedSeasonObj = {} + + try: + + # Fetch the currently selected row when 'Enter' is pressed + #selected_row_index = self.table.cursor_row + row_key, col_key = self.shiftedSeasonsTable.coordinate_to_cell_key(self.shiftedSeasonsTable.cursor_coordinate) + + if row_key is not None: + selected_row_data = self.shiftedSeasonsTable.get_row(row_key) + + shiftedSeasonObj['original_season'] = int(selected_row_data[0]) + shiftedSeasonObj['first_episode'] = int(selected_row_data[1]) if selected_row_data[1].isnumeric() else -1 + shiftedSeasonObj['last_episode'] = int(selected_row_data[2]) if selected_row_data[2].isnumeric() else -1 + shiftedSeasonObj['season_offset'] = int(selected_row_data[3]) if selected_row_data[3].isnumeric() else 0 + shiftedSeasonObj['episode_offset'] = int(selected_row_data[4]) if selected_row_data[4].isnumeric() else 0 + + + if self.__showDescriptor is not None: + + showId = int(self.__showDescriptor.getId()) + + shiftedSeasonId = self.__ssc.findShiftedSeason(showId, + originalSeason=shiftedSeasonObj['original_season'], + firstEpisode=shiftedSeasonObj['first_episode'], + lastEpisode=shiftedSeasonObj['last_episode']) + if shiftedSeasonId is not None: + shiftedSeasonObj['id'] = shiftedSeasonId + + except CellDoesNotExist: + pass + return shiftedSeasonObj def action_add_pattern(self): @@ -233,7 +306,7 @@ class ShowDetailsScreen(Screen): self.shiftedSeasonsTable = DataTable(classes="five") - self.column_key_source_season = self.shiftedSeasonsTable.add_column("Src Season", width=30) + self.column_key_original_season = self.shiftedSeasonsTable.add_column("Original Season", width=30) self.column_key_first_episode = self.shiftedSeasonsTable.add_column("First Episode", width=30) self.column_key_last_episode = self.shiftedSeasonsTable.add_column("Last Episode", width=30) self.column_key_season_offset = self.shiftedSeasonsTable.add_column("Season Offset", width=30) @@ -391,16 +464,21 @@ class ShowDetailsScreen(Screen): if event.button.id == "button_add_shifted_season": if not self.__showDescriptor is None: - self.app.push_screen(ShiftedSeasonDetailsScreen(showId = self.__showDescriptor.getId()), self.handle_add_shifted_season) + self.app.push_screen(ShiftedSeasonDetailsScreen(showId = self.__showDescriptor.getId()), self.handle_update_shifted_season) if event.button.id == "button_edit_shifted_season": - pass + selectedShiftedSeasonObj = self.getSelectedShiftedSeasonObjFromInput() + if 'id' in selectedShiftedSeasonObj.keys(): + self.app.push_screen(ShiftedSeasonDetailsScreen(showId = self.__showDescriptor.getId(), shiftedSeasonId=selectedShiftedSeasonObj['id']), self.handle_update_shifted_season) if event.button.id == "button_delete_shifted_season": - pass + selectedShiftedSeasonObj = self.getSelectedShiftedSeasonObjFromInput() + if 'id' in selectedShiftedSeasonObj.keys(): + self.app.push_screen(ShiftedSeasonDeleteScreen(showId = self.__showDescriptor.getId(), shiftedSeasonId=selectedShiftedSeasonObj['id']), self.handle_delete_shifted_season) + + def handle_update_shifted_season(self, screenResult): + self.updateShiftedSeasons() - def handle_add_shifted_season(self, screenResult): - pass - # pattern = (screenResult['pattern'],) - # self.patternTable.add_row(*map(str, pattern)) + def handle_delete_shifted_season(self, screenResult): + self.updateShiftedSeasons() \ No newline at end of file