Maveno 11 months ago
parent a4b3db3eba
commit 0a026afba4

1
.gitignore vendored

@ -5,3 +5,4 @@ junk/
ansible/inventory/hawaii.yml
ansible/inventory/peppermint.yml
ffx_test_report.log
bin/conversiontest.py

@ -4,6 +4,8 @@ from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from ffx.model.show import Base, Show
from ffx.model.shifted_season import ShiftedSeason
from ffx.model.pattern import Pattern
from ffx.model.track import Track

@ -0,0 +1,47 @@
import os, sys, importlib, inspect, glob, re
from ffx.configuration_controller import ConfigurationController
from ffx.database import databaseContext
from sqlalchemy import Engine
from sqlalchemy.orm import sessionmaker
class Conversion():
def __init__(self):
self._context = {}
self._context['config'] = ConfigurationController()
self._context['database'] = databaseContext(databasePath=self._context['config'].getDatabaseFilePath())
self.__databaseSession: sessionmaker = self._context['database']['session']
self.__databaseEngine: Engine = self._context['database']['engine']
@staticmethod
def list():
basePath = os.path.dirname(__file__)
filenamePattern = re.compile("conversion_([0-9]+)_([0-9]+)\\.py")
filenameList = [os.path.basename(fp) for fp in glob.glob(f"{ basePath }/*.py") if fp != __file__]
versionTupleList = [(fm.group(1), fm.group(2)) for fn in filenameList if (fm := filenamePattern.search(fn))]
return versionTupleList
@staticmethod
def getClassReference(versionFrom, versionTo):
importlib.import_module(f"ffx.model.conversions.conversion_{ versionFrom }_{ versionTo }")
for name, obj in inspect.getmembers(sys.modules[f"ffx.model.conversions.conversion_{ versionFrom }_{ versionTo }"]):
#HINT: Excluding DispositionCombination as it seems to be included by import (?)
if inspect.isclass(obj) and name != 'Conversion' and name.startswith('Conversion'):
return obj
@staticmethod
def getAllClassReferences():
return [Conversion.getClassReference(verFrom, verTo) for verFrom, verTo in Conversion.list()]

@ -0,0 +1,17 @@
import os, sys, importlib, inspect, glob, re
from .conversion import Conversion
class Conversion_2_3(Conversion):
def __init__(self):
super().__init__()
def applyConversion(self):
s = self.__databaseSession()
e = self.__databaseEngine
with e.connect() as c:
c.execute("ALTER TABLE user ADD COLUMN email VARCHAR(255)")

@ -0,0 +1,7 @@
import os, sys, importlib, inspect, glob, re
from .conversion import Conversion
class Conversion_3_4(Conversion):
pass

@ -1,14 +1,14 @@
import click
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship, sessionmaker, Mapped, backref
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship
from .show import Base, Show
from .track import Track
from ffx.media_descriptor import MediaDescriptor
from ffx.show_descriptor import ShowDescriptor
class Pattern(Base):
__tablename__ = 'patterns'

@ -0,0 +1,52 @@
import click
from sqlalchemy import Column, Integer, ForeignKey
from sqlalchemy.orm import relationship
from .show import Base, Show
class ShiftedSeason(Base):
__tablename__ = 'shifted_seasons'
# v1.x
id = Column(Integer, primary_key=True)
# v2.0
# id: Mapped[int] = mapped_column(Integer, primary_key=True)
# pattern: Mapped[str] = mapped_column(String, nullable=False)
# v1.x
show_id = Column(Integer, ForeignKey('shows.id', ondelete="CASCADE"))
show = relationship(Show, back_populates='shifted_seasons', lazy='joined')
# v2.0
# show_id: Mapped[int] = mapped_column(ForeignKey("shows.id", ondelete="CASCADE"))
# show: Mapped["Show"] = relationship(back_populates="patterns")
src_season = Column(Integer)
first_episode = Column(Integer, default = -1)
last_episode = Column(Integer, default = -1)
season_offset = Column(Integer, default = 0)
episode_offset = Column(Integer, default = 0)
def getSeasonOffset(self):
return self.season_offset
def getEpisodeOffset(self):
return self.episode_offset
def getShifted(self, season, episode):
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

@ -4,9 +4,9 @@ from sqlalchemy.orm import relationship, declarative_base, sessionmaker
from ffx.show_descriptor import ShowDescriptor
Base = declarative_base()
class Show(Base):
"""
relationship(argument, opt1, opt2, ...)
@ -38,6 +38,9 @@ class Show(Base):
# v2.0
# patterns: Mapped[List["Pattern"]] = relationship(back_populates="show", cascade="all, delete")
shifted_seasons = relationship('ShiftedSeason', back_populates='show', cascade="all, delete")
index_season_digits = Column(Integer, default=ShowDescriptor.DEFAULT_INDEX_SEASON_DIGITS)
index_episode_digits = Column(Integer, default=ShowDescriptor.DEFAULT_INDEX_EPISODE_DIGITS)
indicator_season_digits = Column(Integer, default=ShowDescriptor.DEFAULT_INDICATOR_SEASON_DIGITS)

@ -0,0 +1,156 @@
import click, re
from ffx.model.shifted_season import ShiftedSeason
class ShiftedSeasonController():
def __init__(self, context):
self.context = context
self.Session = self.context['database']['session'] # convenience
def addPattern(self, patternDescriptor):
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
except Exception as ex:
raise click.ClickException(f"PatternController.addPattern(): {repr(ex)}")
finally:
s.close()
def updatePattern(self, patternId, patternDescriptor):
try:
s = self.Session()
q = s.query(Pattern).filter(Pattern.id == int(patternId))
if q.count():
pattern = q.first()
pattern.show_id = int(patternDescriptor['show_id'])
pattern.pattern = str(patternDescriptor['pattern'])
s.commit()
return True
else:
return False
except Exception as ex:
raise click.ClickException(f"PatternController.updatePattern(): {repr(ex)}")
finally:
s.close()
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']))
if q.count():
pattern = q.first()
return int(pattern.id)
else:
return None
except Exception as ex:
raise click.ClickException(f"PatternController.findPattern(): {repr(ex)}")
finally:
s.close()
def getPattern(self, patternId : int):
if type(patternId) is not int:
raise ValueError(f"PatternController.getPattern(): Argument patternId is required to be of type int")
try:
s = self.Session()
q = s.query(Pattern).filter(Pattern.id == int(patternId))
return q.first() if q.count() else None
except Exception as ex:
raise click.ClickException(f"PatternController.getPattern(): {repr(ex)}")
finally:
s.close()
def deletePattern(self, patternId):
try:
s = self.Session()
q = s.query(Pattern).filter(Pattern.id == int(patternId))
if q.count():
#DAFUQ: https://stackoverflow.com/a/19245058
# q.delete()
pattern = q.first()
s.delete(pattern)
s.commit()
return True
return False
except Exception as ex:
raise click.ClickException(f"PatternController.deletePattern(): {repr(ex)}")
finally:
s.close()
def matchFilename(self, filename : str) -> dict:
"""Returns dict {'match': <a regex match obj>, 'pattern': <ffx pattern obj>} 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()

@ -0,0 +1,111 @@
import click
from textual.screen import Screen
from textual.widgets import Header, Footer, Static, Button
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 ShiftedSeasonDeleteScreen(Screen):
CSS = """
Grid {
grid-size: 2;
grid-rows: 2 auto;
grid-columns: 30 330;
height: 100%;
width: 100%;
padding: 1;
}
Input {
border: none;
}
Button {
border: none;
}
#toplabel {
height: 1;
}
.two {
column-span: 2;
}
.box {
height: 100%;
border: solid green;
}
"""
def __init__(self, patternId = None, showId = 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.__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.__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):
yield Header()
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("", classes="two")
yield Static("from show")
yield Static("", id="showlabel")
yield Static("", classes="two")
yield Button("Delete", id="delete_button")
yield Button("Cancel", id="cancel_button")
yield Footer()
# Event handler for button press
def on_button_pressed(self, event: Button.Pressed) -> None:
if event.button.id == "delete_button":
if self.__patternId is None:
raise click.ClickException('PatternDeleteScreen.on_button_pressed(): pattern id is undefined')
if self.__pc.deletePattern(self.__patternId):
self.dismiss(self.__pattern)
else:
#TODO: Meldung
self.app.pop_screen()
if event.button.id == "cancel_button":
self.app.pop_screen()

@ -0,0 +1,490 @@
import click, re
from typing import List
from textual import events
from textual.app import App, ComposeResult
from textual.screen import Screen
from textual.widgets import Header, Footer, Static, Button, Input, DataTable
from textual.containers import Grid
from ffx.model.show import Show
from ffx.model.pattern import Pattern
from ffx.model.track import Track
from .pattern_controller import PatternController
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
# Screen[dict[int, str, int]]
class ShiftedSeasonDetailsScreen(Screen):
CSS = """
Grid {
grid-size: 3 10;
grid-rows: 2 2 2 2 2 2 2 2 2 2;
grid-columns: 40 40 40;
height: 100%;
width: 100%;
padding: 1;
}
Input {
border: none;
}
Button {
border: none;
}
DataTable {
min-height: 6;
}
#toplabel {
height: 1;
}
.two {
column-span: 3;
}
.three {
column-span: 3;
}
.four {
column-span: 4;
}
.five {
column-span: 5;
}
.six {
column-span: 6;
}
.seven {
column-span: 7;
}
.box {
height: 100%;
border: solid green;
}
.yellow {
tint: yellow 40%;
}
"""
def __init__(self, patternId = None, showId = 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()
def compose(self):
yield Header()
with Grid():
# 1
yield Static("Edit shifted season" if self.__pattern is not None else "New shifted season", id="toplabel", classes="three")
# 2
yield Static(" ", classes="three")
# 3
yield Static("Original season")
yield Input(id="input_original_season", classes="two")
# 4
yield Static("First Episode")
yield Input(id="input_first_episode", classes="two")
# 5
yield Static("Last Episode")
yield Input(id="imput_last_episode", classes="two")
# 6
yield Static("Season offset")
yield Input(id="input_season_offset", classes="two")
# 7
yield Static("Episode offset")
yield Input(id="input_episode_offset", classes="two")
# 8
yield Static(" ", classes="three")
# 9
yield Button("Save", id="save_button")
yield Button("Cancel", id="cancel_button")
yield Static(" ")
# 10
yield Static(" ", classes="three")
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
# 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()
#

@ -19,6 +19,8 @@ from .tmdb_controller import TmdbController
from .show_descriptor import ShowDescriptor
from .shifted_season_details_screen import ShiftedSeasonDetailsScreen
from .helper import filterFilename
@ -28,8 +30,8 @@ class ShowDetailsScreen(Screen):
CSS = """
Grid {
grid-size: 5 14;
grid-rows: 2 2 2 2 2 2 2 2 2 2 2 6 2 2;
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%;
@ -44,7 +46,7 @@ class ShowDetailsScreen(Screen):
}
DataTable {
column-span: 2;
min-height: 5;
min-height: 8;
}
#toplabel {
@ -160,7 +162,7 @@ class ShowDetailsScreen(Screen):
def action_add_pattern(self):
if not self.__showDescriptor is None:
self.app.push_screen(PatternDetailsScreen(showId = self.__showDescriptor.getId()), self.handle_add_pattern) # <-
self.app.push_screen(PatternDetailsScreen(showId = self.__showDescriptor.getId()), self.handle_add_pattern)
def handle_add_pattern(self, screenResult):
@ -228,6 +230,18 @@ class ShowDetailsScreen(Screen):
self.patternTable.cursor_type = 'row'
self.shiftedSeasonsTable = DataTable(classes="five")
self.column_key_source_season = self.shiftedSeasonsTable.add_column("Src 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():
@ -275,14 +289,29 @@ class ShowDetailsScreen(Screen):
yield Static(" ", classes="five")
# 11
yield Static("File patterns", classes="five")
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.patternTable
yield self.shiftedSeasonsTable
# 13
yield Static("File patterns", classes="five")
# 14
yield self.patternTable
# 15
yield Static(" ", classes="five")
# 14
# 16
yield Button("Save", id="save_button")
yield Button("Cancel", id="cancel_button")
@ -358,3 +387,20 @@ class ShowDetailsScreen(Screen):
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_add_shifted_season)
if event.button.id == "button_edit_shifted_season":
pass
if event.button.id == "button_delete_shifted_season":
pass
def handle_add_shifted_season(self, screenResult):
pass
# pattern = (screenResult['pattern'],)
# self.patternTable.add_row(*map(str, pattern))

Loading…
Cancel
Save