You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
492 lines
17 KiB
Python
492 lines
17 KiB
Python
import click
|
|
|
|
from textual.screen import Screen
|
|
from textual.widgets import Header, Footer, Static, Button, DataTable, Input
|
|
from textual.containers import Grid
|
|
from textual.widgets._data_table import CellDoesNotExist
|
|
|
|
from ffx.model.pattern import Pattern
|
|
|
|
from .pattern_details_screen import PatternDetailsScreen
|
|
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
|
|
|
|
|
|
# Screen[dict[int, str, int]]
|
|
class ShowDetailsScreen(Screen):
|
|
|
|
CSS = """
|
|
|
|
Grid {
|
|
grid-size: 5 16;
|
|
grid-rows: 2 2 2 2 2 2 2 2 2 2 2 9 2 9 2 2;
|
|
grid-columns: 30 30 30 30 30;
|
|
height: 100%;
|
|
width: 100%;
|
|
padding: 1;
|
|
}
|
|
|
|
Input {
|
|
border: none;
|
|
}
|
|
Button {
|
|
border: none;
|
|
}
|
|
|
|
DataTable {
|
|
column-span: 2;
|
|
min-height: 8;
|
|
}
|
|
|
|
DataTable .datatable--cursor {
|
|
background: darkorange;
|
|
color: black;
|
|
}
|
|
|
|
DataTable .datatable--header {
|
|
background: steelblue;
|
|
color: white;
|
|
}
|
|
|
|
#toplabel {
|
|
height: 1;
|
|
}
|
|
|
|
|
|
.two {
|
|
column-span: 2;
|
|
}
|
|
.three {
|
|
column-span: 3;
|
|
}
|
|
.four {
|
|
column-span: 4;
|
|
}
|
|
.five {
|
|
column-span: 5;
|
|
}
|
|
|
|
.box {
|
|
height: 100%;
|
|
border: solid green;
|
|
}
|
|
"""
|
|
|
|
BINDINGS = [
|
|
("a", "add_pattern", "Add Pattern"),
|
|
("e", "edit_pattern", "Edit Pattern"),
|
|
("r", "remove_pattern", "Remove Pattern"),
|
|
]
|
|
|
|
def __init__(self, showId = None):
|
|
super().__init__()
|
|
|
|
self.context = self.app.getContext()
|
|
self.Session = self.context['database']['session'] # convenience
|
|
|
|
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
|
|
|
|
|
|
def loadPatterns(self, show_id : int):
|
|
|
|
try:
|
|
s = self.Session()
|
|
q = s.query(Pattern).filter(Pattern.show_id == int(show_id))
|
|
|
|
return [{'id': int(p.id), 'pattern': str(p.pattern)} for p in q.all()]
|
|
|
|
except Exception as ex:
|
|
raise click.ClickException(f"ShowDetailsScreen.loadPatterns(): {repr(ex)}")
|
|
finally:
|
|
s.close()
|
|
|
|
|
|
|
|
def updateShiftedSeasons(self):
|
|
|
|
self.shiftedSeasonsTable.clear()
|
|
|
|
if not self.__showDescriptor is None:
|
|
|
|
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())
|
|
|
|
self.query_one("#index_season_digits_input", Input).value = str(self.__showDescriptor.getIndexSeasonDigits())
|
|
self.query_one("#index_episode_digits_input", Input).value = str(self.__showDescriptor.getIndexEpisodeDigits())
|
|
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())
|
|
|
|
|
|
#raise click.ClickException(f"show_id {showId}")
|
|
patternList = self.loadPatterns(showId)
|
|
# raise click.ClickException(f"patternList {patternList}")
|
|
for pattern in patternList:
|
|
row = (pattern['pattern'],)
|
|
self.patternTable.add_row(*map(str, row))
|
|
|
|
self.updateShiftedSeasons()
|
|
|
|
else:
|
|
|
|
self.query_one("#index_season_digits_input", Input).value = "2"
|
|
self.query_one("#index_episode_digits_input", Input).value = "2"
|
|
self.query_one("#indicator_season_digits_input", Input).value = "2"
|
|
self.query_one("#indicator_episode_digits_input", Input).value = "2"
|
|
|
|
|
|
def getSelectedPatternDescriptor(self):
|
|
|
|
selectedPattern = {}
|
|
|
|
try:
|
|
|
|
# Fetch the currently selected row when 'Enter' is pressed
|
|
#selected_row_index = self.table.cursor_row
|
|
row_key, col_key = self.patternTable.coordinate_to_cell_key(self.patternTable.cursor_coordinate)
|
|
|
|
if row_key is not None:
|
|
selected_row_data = self.patternTable.get_row(row_key)
|
|
|
|
selectedPattern['show_id'] = self.__showDescriptor.getId()
|
|
selectedPattern['pattern'] = str(selected_row_data[0])
|
|
|
|
except CellDoesNotExist:
|
|
pass
|
|
|
|
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):
|
|
if not self.__showDescriptor is None:
|
|
self.app.push_screen(PatternDetailsScreen(showId = self.__showDescriptor.getId()), self.handle_add_pattern)
|
|
|
|
|
|
def handle_add_pattern(self, screenResult):
|
|
|
|
pattern = (screenResult['pattern'],)
|
|
self.patternTable.add_row(*map(str, pattern))
|
|
|
|
|
|
def action_edit_pattern(self):
|
|
|
|
selectedPatternDescriptor = self.getSelectedPatternDescriptor()
|
|
|
|
if selectedPatternDescriptor:
|
|
|
|
selectedPatternId = self.__pc.findPattern(selectedPatternDescriptor)
|
|
|
|
if selectedPatternId is None:
|
|
raise click.ClickException(f"ShowDetailsScreen.action_edit_pattern(): Pattern to edit has no id")
|
|
|
|
self.app.push_screen(PatternDetailsScreen(patternId = selectedPatternId, showId = self.__showDescriptor.getId()), self.handle_edit_pattern) # <-
|
|
|
|
|
|
def handle_edit_pattern(self, screenResult):
|
|
|
|
try:
|
|
|
|
row_key, col_key = self.patternTable.coordinate_to_cell_key(self.patternTable.cursor_coordinate)
|
|
self.patternTable.update_cell(row_key, self.column_key_pattern, screenResult['pattern'])
|
|
|
|
except CellDoesNotExist:
|
|
pass
|
|
|
|
|
|
def action_remove_pattern(self):
|
|
|
|
selectedPatternDescriptor = self.getSelectedPatternDescriptor()
|
|
|
|
if selectedPatternDescriptor:
|
|
|
|
selectedPatternId = self.__pc.findPattern(selectedPatternDescriptor)
|
|
|
|
if selectedPatternId is None:
|
|
raise click.ClickException(f"ShowDetailsScreen.action_remove_pattern(): Pattern to remove has no id")
|
|
|
|
self.app.push_screen(PatternDeleteScreen(patternId = selectedPatternId, showId = self.__showDescriptor.getId()), self.handle_remove_pattern)
|
|
|
|
|
|
def handle_remove_pattern(self, pattern):
|
|
|
|
try:
|
|
row_key, col_key = self.patternTable.coordinate_to_cell_key(self.patternTable.cursor_coordinate)
|
|
self.patternTable.remove_row(row_key)
|
|
|
|
except CellDoesNotExist:
|
|
pass
|
|
|
|
|
|
def compose(self):
|
|
|
|
# Create the DataTable widget
|
|
self.patternTable = DataTable(classes="five")
|
|
|
|
# Define the columns with headers
|
|
self.column_key_pattern = self.patternTable.add_column("Pattern", width=150)
|
|
|
|
self.patternTable.cursor_type = 'row'
|
|
|
|
|
|
self.shiftedSeasonsTable = DataTable(classes="five")
|
|
|
|
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)
|
|
self.column_key_episode_offset = self.shiftedSeasonsTable.add_column("Episode Offset", width=30)
|
|
|
|
self.shiftedSeasonsTable.cursor_type = 'row'
|
|
|
|
|
|
yield Header()
|
|
|
|
with Grid():
|
|
|
|
# 1
|
|
yield Static("Show" if not self.__showDescriptor is None else "New Show", id="toplabel")
|
|
yield Button("Identify", id="identify_button")
|
|
yield Static(" ", classes="three")
|
|
|
|
# 2
|
|
yield Static("ID")
|
|
if not self.__showDescriptor is None:
|
|
yield Static("", id="id_static", classes="four")
|
|
else:
|
|
yield Input(type="integer", id="id_input", classes="four")
|
|
|
|
# 3
|
|
yield Static("Name")
|
|
yield Input(type="text", id="name_input", classes="four")
|
|
|
|
# 4
|
|
yield Static("Year")
|
|
yield Input(type="integer", id="year_input", classes="four")
|
|
|
|
#5
|
|
yield Static(" ", classes="five")
|
|
|
|
#6
|
|
yield Static("Index Season Digits")
|
|
yield Input(type="integer", id="index_season_digits_input", classes="four")
|
|
|
|
#7
|
|
yield Static("Index Episode Digits")
|
|
yield Input(type="integer", id="index_episode_digits_input", classes="four")
|
|
|
|
#8
|
|
yield Static("Indicator Season Digits")
|
|
yield Input(type="integer", id="indicator_season_digits_input", classes="four")
|
|
|
|
#9
|
|
yield Static("Indicator Edisode Digits")
|
|
yield Input(type="integer", id="indicator_episode_digits_input", classes="four")
|
|
|
|
# 10
|
|
yield Static(" ", classes="five")
|
|
|
|
# 11
|
|
yield Static("Shifted seasons", classes="two")
|
|
|
|
if self.__showDescriptor is not None:
|
|
yield Button("Add", id="button_add_shifted_season")
|
|
yield Button("Edit", id="button_edit_shifted_season")
|
|
yield Button("Delete", id="button_delete_shifted_season")
|
|
else:
|
|
yield Static(" ")
|
|
yield Static(" ")
|
|
yield Static(" ")
|
|
|
|
# 12
|
|
yield self.shiftedSeasonsTable
|
|
|
|
# 13
|
|
yield Static("File patterns", classes="five")
|
|
# 14
|
|
yield self.patternTable
|
|
|
|
# 15
|
|
yield Static(" ", classes="five")
|
|
|
|
# 16
|
|
yield Button("Save", id="save_button")
|
|
yield Button("Cancel", id="cancel_button")
|
|
|
|
|
|
yield Footer()
|
|
|
|
|
|
def getShowDescriptorFromInput(self) -> ShowDescriptor:
|
|
|
|
kwargs = {}
|
|
|
|
try:
|
|
if self.__showDescriptor:
|
|
kwargs[ShowDescriptor.ID_KEY] = int(self.__showDescriptor.getId())
|
|
else:
|
|
kwargs[ShowDescriptor.ID_KEY] = int(self.query_one("#id_input", Input).value)
|
|
except ValueError:
|
|
return None
|
|
|
|
try:
|
|
kwargs[ShowDescriptor.NAME_KEY] = str(self.query_one("#name_input", Input).value)
|
|
except ValueError:
|
|
pass
|
|
try:
|
|
kwargs[ShowDescriptor.YEAR_KEY] = int(self.query_one("#year_input", Input).value)
|
|
except ValueError:
|
|
pass
|
|
|
|
try:
|
|
kwargs[ShowDescriptor.INDEX_SEASON_DIGITS_KEY] = int(self.query_one("#index_season_digits_input", Input).value)
|
|
except ValueError:
|
|
pass
|
|
|
|
try:
|
|
kwargs[ShowDescriptor.INDEX_EPISODE_DIGITS_KEY] = int(self.query_one("#index_episode_digits_input", Input).value)
|
|
except ValueError:
|
|
pass
|
|
try:
|
|
kwargs[ShowDescriptor.INDICATOR_SEASON_DIGITS_KEY] = int(self.query_one("#indicator_season_digits_input", Input).value)
|
|
except ValueError:
|
|
pass
|
|
try:
|
|
kwargs[ShowDescriptor.INDICATOR_EPISODE_DIGITS_KEY] = int(self.query_one("#indicator_episode_digits_input", Input).value)
|
|
except ValueError:
|
|
pass
|
|
|
|
return ShowDescriptor(**kwargs)
|
|
|
|
|
|
# Event handler for button press
|
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
|
|
|
if event.button.id == "save_button":
|
|
|
|
showDescriptor = self.getShowDescriptorFromInput()
|
|
|
|
if not showDescriptor is None:
|
|
if self.__sc.updateShow(showDescriptor):
|
|
self.dismiss(showDescriptor)
|
|
else:
|
|
#TODO: Meldung
|
|
self.app.pop_screen()
|
|
|
|
if event.button.id == "cancel_button":
|
|
self.app.pop_screen()
|
|
|
|
|
|
if event.button.id == "identify_button":
|
|
|
|
showDescriptor = self.getShowDescriptorFromInput()
|
|
if not showDescriptor is None:
|
|
showName, showYear = self.__tc.getShowNameAndYear(showDescriptor.getId())
|
|
|
|
self.query_one("#name_input", Input).value = filterFilename(showName)
|
|
self.query_one("#year_input", Input).value = str(showYear)
|
|
|
|
|
|
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_update_shifted_season)
|
|
|
|
if event.button.id == "button_edit_shifted_season":
|
|
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":
|
|
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_delete_shifted_season(self, screenResult):
|
|
self.updateShiftedSeasons() |