3 Commits

Author SHA1 Message Date
Maveno
882d021bb6 RFC imports 2024-10-06 16:24:01 +02:00
Maveno
131cca2c53 tracktags UI mwe 2024-10-06 16:07:23 +02:00
Maveno
a03449a32b add/edit tag to ui 2024-10-06 12:21:27 +02:00
19 changed files with 522 additions and 354 deletions

View File

@@ -1,23 +1,7 @@
import os, time, sqlite3, sqlalchemy
from textual.app import App, ComposeResult
from textual.screen import Screen
from textual.widgets import Header, Footer, Placeholder, Label
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
# from ffx.model.show import Base, Show
# from ffx.model.pattern import Pattern
# from ffx.model.track import Track
# from ffx.model.media_tag import MediaTag
# from ffx.model.track_tag import TrackTag
from textual.app import App
from .shows_screen import ShowsScreen
from .warning_screen import WarningScreen
from .dashboard_screen import DashboardScreen
from .settings_screen import SettingsScreen
from .help_screen import HelpScreen
class FfxApp(App):

View File

@@ -3,11 +3,6 @@ import os, re, click, json
from .media_descriptor import MediaDescriptor
from .pattern_controller import PatternController
#from .track_type import TrackType
#from .audio_layout import AudioLayout
#from .track_disposition import TrackDisposition
from .process import executeProcess

View File

@@ -93,15 +93,6 @@ class IsoLanguage(Enum):
return foundLangs[0] if foundLangs else IsoLanguage.UNDEFINED
# def get(lang : str):
#
# selectedLangs = [l for l in IsoLanguage if l.value['iso639_2'] == lang]
#
# if selectedLangs:
# return selectedLangs[0]
# else:
# return None
def label(self):
return str(self.value['name'])

View File

@@ -154,7 +154,10 @@ class Track(Base):
def getType(self):
return TrackType.fromIndex(self.track_type)
# def getIndex(self):
# return int(self.index)
def getSubIndex(self):
return int(self.sub_index)
@@ -171,3 +174,20 @@ class Track(Base):
def getTags(self):
return {str(t.key):str(t.value) for t in self.track_tags}
def getDescriptor(self) -> TrackDescriptor:
kwargs = {}
kwargs[TrackDescriptor.ID_KEY] = self.getId()
kwargs[TrackDescriptor.PATTERN_ID_KEY] = self.getPatternId()
#kwargs[TrackDescriptor.SUB_INDEX_KEY] = self.getIndex()
kwargs[TrackDescriptor.SUB_INDEX_KEY] = self.getSubIndex()
kwargs[TrackDescriptor.TRACK_TYPE_KEY] = self.getType()
kwargs[TrackDescriptor.DISPOSITION_SET_KEY] = self.getDispositionSet()
kwargs[TrackDescriptor.TAGS_KEY] = self.getTags()
return TrackDescriptor(**kwargs)

View File

@@ -2,8 +2,6 @@ import click, re
from ffx.model.pattern import Pattern
from .media_descriptor import MediaDescriptor
class PatternController():
@@ -88,12 +86,7 @@ class PatternController():
s = self.Session()
q = s.query(Pattern).filter(Pattern.id == int(patternId))
if q.count():
# pattern = q.first()
#return self.getPatternDict(pattern)
return q.first()
else:
return None
return q.first() if q.count() else None
except Exception as ex:
raise click.ClickException(f"PatternController.getPattern(): {repr(ex)}")
@@ -125,11 +118,6 @@ class PatternController():
def matchFilename(self, filename):
#SEASON_PATTERN = '[sS]([0-9]+)'
#EPISODE_PATTERN = '[eE]([0-9]+)'
#result = {}
try:
s = self.Session()
q = s.query(Pattern)
@@ -141,26 +129,6 @@ class PatternController():
else:
return None
# for pattern in q.all():
#
# match = re.search(pattern.pattern, filename)
#
# if match:
#
# result['pattern_id'] = pattern.id
# result['show_id'] = pattern.show_id
#
# result['indicator'] = match.group(1)
#
# seasonMatch = re.search(SEASON_PATTERN, result['indicator'])
# if seasonMatch:
# result['season'] = int(seasonMatch.group(1))
#
# episodeMatch = re.search(EPISODE_PATTERN, result['indicator'])
# if episodeMatch:
# result['episode'] = int(episodeMatch.group(1))
except Exception as ex:
raise click.ClickException(f"PatternController.findPattern(): {repr(ex)}")
finally:

View File

@@ -1,12 +1,8 @@
import click
from textual import events
from textual.app import App, ComposeResult
from textual.screen import Screen
from textual.widgets import Header, Footer, Placeholder, Label, ListView, ListItem, Static, DataTable, Button, Input
from textual.containers import Grid, Horizontal
from ffx.model.pattern import Pattern
from textual.widgets import Header, Footer, Static, Button
from textual.containers import Grid
from .show_controller import ShowController
from .pattern_controller import PatternController

View File

@@ -3,8 +3,8 @@ import click, re
from textual import events
from textual.app import App, ComposeResult
from textual.screen import Screen
from textual.widgets import Header, Footer, Placeholder, Label, ListView, ListItem, Static, DataTable, Button, Input
from textual.containers import Grid, Horizontal
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
@@ -80,7 +80,7 @@ class PatternDetailsScreen(Screen):
self.__sc = ShowController(context = self.context)
self.__tc = TrackController(context = self.context)
self.__pattern = self.__pc.getPattern(patternId) if patternId is not None else None
self.__pattern : Pattern = self.__pc.getPattern(patternId) if patternId is not None else None
self.show_obj = self.__sc.getShowDesciptor(showId) if showId is not None else {}
@@ -270,18 +270,7 @@ class PatternDetailsScreen(Screen):
subIndex = int(selected_track_data[0])
audioTrack = self.__tc.findTrack(self.__pattern.getId(), TrackType.AUDIO, subIndex)
kwargs = {}
kwargs[TrackDescriptor.PATTERN_ID_KEY] = self.__pattern.getId()
kwargs[TrackDescriptor.SUB_INDEX_KEY] = subIndex
kwargs[TrackDescriptor.TRACK_TYPE_KEY] = TrackType.AUDIO
kwargs[TrackDescriptor.DISPOSITION_SET_KEY] = audioTrack.getDispositionSet()
kwargs[TrackDescriptor.TAGS_KEY] = audioTrack.getTags()
return TrackDescriptor(**kwargs)
return self.__tc.findTrack(self.__pattern.getId(), TrackType.AUDIO, subIndex).getDescriptor()
else:
return None
@@ -290,7 +279,7 @@ class PatternDetailsScreen(Screen):
return None
def getSelectedSubtitleTrackDescriptor(self):
def getSelectedSubtitleTrackDescriptor(self) -> TrackDescriptor:
if not self.__pattern is None:
return None
@@ -302,22 +291,11 @@ class PatternDetailsScreen(Screen):
row_key, col_key = self.subtitleStreamsTable.coordinate_to_cell_key(self.subtitleStreamsTable.cursor_coordinate)
if row_key is not None:
selected_track_data = self.subtitleStreamsTable.get_row(row_key)
selected_track_data = self.subtitleStreamsTable.get_row(row_key)
subIndex = int(selected_track_data[0])
subtitleTrack = self.__tc.findTrack(self.__pattern.getId(), TrackType.SUBTITLE, subIndex)
kwargs = {}
kwargs[TrackDescriptor.PATTERN_ID_KEY] = self.__pattern.getId()
kwargs[TrackDescriptor.SUB_INDEX_KEY] = subIndex
kwargs[TrackDescriptor.TRACK_TYPE_KEY] = TrackType.SUBTITLE
kwargs[TrackDescriptor.DISPOSITION_SET_KEY] = subtitleTrack.getDispositionSet()
kwargs[TrackDescriptor.TAGS_KEY] = subtitleTrack.getTags()
return TrackDescriptor(**kwargs)
return self.__tc.findTrack(self.__pattern.getId(), TrackType.SUBTITLE, subIndex).getDescriptor()
else:
return None

View File

@@ -1,16 +1,6 @@
import subprocess
#class ProcessController():
#def __init__(self, commandSequence):
# self.__commandSequence = commandSequence
def executeProcess(commandSequence):
process = subprocess.Popen(commandSequence, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, error = process.communicate()
return output.decode('utf-8'), error.decode('utf-8'), process.returncode

View File

@@ -17,25 +17,10 @@ class ShowController():
s = self.Session()
q = s.query(Show).filter(Show.id == showId)
showDescriptor = {}
if q.count():
show = q.first()
# showDescriptor['id'] = int(show.id)
# showDescriptor['name'] = str(show.name)
# showDescriptor['year'] = int(show.year)
#
# showDescriptor['index_season_digits'] = int(show.index_season_digits)
# showDescriptor['index_episode_digits'] = int(show.index_episode_digits)
# showDescriptor['indicator_season_digits'] = int(show.indicator_season_digits)
# showDescriptor['indicator_episode_digits'] = int(show.indicator_episode_digits)
#
# return showDescriptor
show = q.first()
return show.getDesciptor()
except Exception as ex:
raise click.ClickException(f"ShowController.getShowDesciptor(): {repr(ex)}")
finally:

View File

@@ -1,12 +1,6 @@
import click
from textual import events
from textual.app import App, ComposeResult
from textual.screen import Screen
from textual.widgets import Header, Footer, Placeholder, Label, ListView, ListItem, Static, DataTable, Button, Input
from textual.containers import Grid, Horizontal
from ffx.model.show import Show
from textual.widgets import Header, Footer, Static, Button
from textual.containers import Grid
from .show_controller import ShowController

View File

@@ -1,10 +1,8 @@
import click
from textual import events
from textual.app import App, ComposeResult
from textual.screen import Screen
from textual.widgets import Header, Footer, Placeholder, Label, ListView, ListItem, Static, DataTable, Button, Input
from textual.containers import Grid, Horizontal
from textual.widgets import Header, Footer, Static, Button, DataTable, Input
from textual.containers import Grid
from textual.widgets._data_table import CellDoesNotExist
@@ -215,12 +213,9 @@ class ShowDetailsScreen(Screen):
# Define the columns with headers
self.column_key_pattern = self.patternTable.add_column("Pattern", width=150)
#self.column_key_name = self.patternTable.add_column("Name", width=50)
#self.column_key_year = self.patternTable.add_column("Year", width=10)
self.patternTable.cursor_type = 'row'
yield Header()
with Grid():

View File

@@ -3,7 +3,7 @@ import click
from textual.app import App, ComposeResult
from textual.screen import Screen
from textual.widgets import Header, Footer, Placeholder, Label, ListView, ListItem, Static, DataTable, Button
from textual.containers import Grid, Horizontal
from textual.containers import Grid
from ffx.model.show import Show

View File

@@ -2,11 +2,6 @@ import click
from ffx.model.track import Track
from .track_type import TrackType
from .track_disposition import TrackDisposition
from .iso_language import IsoLanguage
from ffx.model.media_tag import MediaTag
from ffx.model.track_tag import TrackTag
@@ -19,175 +14,170 @@ class TagController():
self.Session = self.context['database']['session'] # convenience
def addMediaTag(self, trackDescriptor):
def updateMediaTag(self, trackId, tagKey, tagValue):
try:
s = self.Session()
track = Track(pattern_id = int(trackDescriptor['pattern_id']),
track_type = int(trackDescriptor['type'].value),
sub_index = int(trackDescriptor['sub_index']),
# language = str(trackDescriptor['language'].threeLetter()),
# title = str(trackDescriptor['title']),
disposition_flags = int(TrackDisposition.toFlags(trackDescriptor['disposition_list'])))
s.add(track)
s.commit()
except Exception as ex:
raise click.ClickException(f"TrackController.addTrack(): {repr(ex)}")
finally:
s.close()
def addTrackTag(self, trackDescriptor):
try:
s = self.Session()
track = Track(pattern_id = int(trackDescriptor['pattern_id']),
track_type = int(trackDescriptor['type'].value),
sub_index = int(trackDescriptor['sub_index']),
# language = str(trackDescriptor['language'].threeLetter()),
# title = str(trackDescriptor['title']),
disposition_flags = int(TrackDisposition.toFlags(trackDescriptor['disposition_list'])))
s.add(track)
s.commit()
except Exception as ex:
raise click.ClickException(f"TrackController.addTrack(): {repr(ex)}")
finally:
s.close()
def updateTrack(self, trackId, trackDescriptor):
try:
s = self.Session()
q = s.query(Track).filter(Track.id == int(trackId))
if q.count():
track = q.first()
track.sub_index = int(trackDescriptor['sub_index'])
# track.language = str(trackDescriptor['language'].threeLetter())
# track.title = str(trackDescriptor['title'])
track.disposition_flags = int(TrackDisposition.toFlags(trackDescriptor['disposition_list']))
s.commit()
return True
q = s.query(MediaTag).filter(MediaTag.track_id == int(trackId),
MediaTag.key == str(tagKey),
MediaTag.value == str(tagValue))
tag = q.first()
if tag:
tag.value = str(tagValue)
else:
return False
tag = MediaTag(track_id = int(trackId),
key = str(tagKey),
value = str(tagValue))
s.add(tag)
s.commit()
return int(tag.id)
except Exception as ex:
raise click.ClickException(f"TrackController.addTrack(): {repr(ex)}")
raise click.ClickException(f"TagController.updateTrackTag(): {repr(ex)}")
finally:
s.close()
def findAllTracks(self, patternId):
def updateTrackTag(self, trackId, tagKey, tagValue):
try:
s = self.Session()
trackDescriptors = {}
trackDescriptors[TrackType.AUDIO.label()] = []
trackDescriptors[TrackType.SUBTITLE.label()] = []
q = s.query(TrackTag).filter(TrackTag.track_id == int(trackId),
TrackTag.key == str(tagKey),
TrackTag.value == str(tagValue))
tag = q.first()
if tag:
tag.value = str(tagValue)
else:
tag = TrackTag(track_id = int(trackId),
key = str(tagKey),
value = str(tagValue))
s.add(tag)
s.commit()
q_audio = s.query(Track).filter(Track.pattern_id == int(patternId), Track.track_type == TrackType.AUDIO.index())
for audioTrack in q_audio.all():
trackDescriptors[TrackType.AUDIO.label()].append(audioTrack.id)
return int(tag.id)
except Exception as ex:
raise click.ClickException(f"TagController.updateTrackTag(): {repr(ex)}")
finally:
s.close()
def findAllMediaTags(self, trackId) -> dict:
try:
s = self.Session()
q_subtitle = s.query(Track).filter(Track.pattern_id == int(patternId), Track.track_type == TrackType.SUBTITLE.index())
for subtitleTrack in q_subtitle.all():
trackDescriptors[TrackType.SUBTITLE.label()].append(subtitleTrack.id)
return trackDescriptors
except Exception as ex:
raise click.ClickException(f"TrackController.findAllTracks(): {repr(ex)}")
finally:
s.close()
def findTrack(self, patternId, trackType : TrackType, subIndex):
try:
s = self.Session()
q = s.query(Track).filter(Track.pattern_id == int(patternId), Track.track_type == trackType.value, Track.sub_index == int(subIndex))
q = s.query(MediaTag).filter(MediaTag.track_id == int(trackId))
if q.count():
track = q.first()
return int(track.id)
else:
return None
except Exception as ex:
raise click.ClickException(f"TrackController.findTrack(): {repr(ex)}")
finally:
s.close()
def getTrackDescriptor(self, trackId):
try:
s = self.Session()
q = s.query(Track).filter(Track.id == int(trackId))
if q.count():
track = q.first()
#return self.getTrackDict(track)
return track.getDescriptor()
return {t.key:t.value for t in q.all()}
else:
return {}
except Exception as ex:
raise click.ClickException(f"TrackController.getTrackDescriptor(): {repr(ex)}")
raise click.ClickException(f"TagController.findAllMediaTags(): {repr(ex)}")
finally:
s.close()
def deleteTrack(self, trackId):
def findAllTrackTags(self, trackId) -> dict:
try:
s = self.Session()
q = s.query(Track).filter(Track.id == int(trackId))
q = s.query(TrackTag).filter(TrackTag.track_id == int(trackId))
if q.count():
return {t.key:t.value for t in q.all()}
else:
return {}
except Exception as ex:
raise click.ClickException(f"TagController.findAllTracks(): {repr(ex)}")
finally:
s.close()
def findMediaTag(self, trackId : int, trackKey : str) -> MediaTag:
try:
s = self.Session()
q = s.query(Track).filter(MediaTag.track_id == int(trackId), MediaTag.key == str(trackKey))
if q.count():
return q.first()
else:
return None
except Exception as ex:
raise click.ClickException(f"TagController.findMediaTag(): {repr(ex)}")
finally:
s.close()
def findTrackTag(self, trackId : int, tagKey : str) -> TrackTag:
try:
s = self.Session()
q = s.query(TrackTag).filter(TrackTag.track_id == int(trackId), TrackTag.key == str(tagKey))
if q.count():
return q.first()
else:
return None
except Exception as ex:
raise click.ClickException(f"TagController.findTrackTag(): {repr(ex)}")
finally:
s.close()
def deleteMediaTag(self, tagId) -> bool:
try:
s = self.Session()
q = s.query(MediaTag).filter(MediaTag.id == int(tagId))
if q.count():
trackDescriptor = self.getTrackDict(q.first())
tag = q.first()
q_siblings = s.query(Track).filter(Track.pattern_id == int(trackDescriptor['pattern_id']), Track.track_type == trackDescriptor['type'].value).order_by(Track.sub_index)
subIndex = 0
for track in q_siblings.all():
if track.sub_index == trackDescriptor['sub_index']:
s.delete(track)
else:
track.sub_index = subIndex
subIndex += 1
s.delete(tag)
s.commit()
return True
return False
except Exception as ex:
raise click.ClickException(f"TagController.deleteMediaTag(): {repr(ex)}")
finally:
s.close()
def deleteTrackTag(self, tagId : int) -> bool:
if type(tagId) is not int:
raise TypeError('TagController.deleteTrackTag(): Argument tagId is required to be of type int')
try:
s = self.Session()
q = s.query(TrackTag).filter(TrackTag.id == int(tagId))
if q.count():
tag = q.first()
s.delete(tag)
s.commit()
return True
return False
except Exception as ex:
raise click.ClickException(f"TrackController.deleteTrack(): {repr(ex)}")
raise click.ClickException(f"TagController.deleteTrackTag(): {repr(ex)}")
finally:
s.close()

View File

@@ -0,0 +1,98 @@
from textual.screen import Screen
from textual.widgets import Header, Footer, Static, Button
from textual.containers import Grid
# Screen[dict[int, str, int]]
class TagDeleteScreen(Screen):
CSS = """
Grid {
grid-size: 4 9;
grid-rows: 2 2 2 2 2 2 2 2 2;
grid-columns: 30 30 30 30;
height: 100%;
width: 100%;
padding: 1;
}
Input {
border: none;
}
Button {
border: none;
}
#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;
}
"""
def __init__(self, key=None, value=None):
super().__init__()
self.__key = key
self.__value = value
def on_mount(self):
self.query_one("#keylabel", Static).update(str(self.__key))
self.query_one("#valuelabel", Static).update(str(self.__value))
def compose(self):
yield Header()
with Grid():
#1
yield Static(f"Are you sure to delete this tag ?", id="toplabel", classes="five")
#2
yield Static("Key")
yield Static(" ", id="keylabel", classes="four")
#3
yield Static("Value")
yield Static(" ", id="valuelabel", classes="four")
#4
yield Static(" ", classes="five")
#9
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":
tag = (self.__key, self.__value)
self.dismiss(tag)
if event.button.id == "cancel_button":
self.app.pop_screen()

View File

@@ -0,0 +1,121 @@
from textual.screen import Screen
from textual.widgets import Header, Footer, Static, Button, Input
from textual.containers import Grid
# Screen[dict[int, str, int]]
class TagDetailsScreen(Screen):
CSS = """
Grid {
grid-size: 5 20;
grid-rows: 2 2 2 2 2 3 2 2 2 2 2 6 2 2 6 2 2 2 2 6;
grid-columns: 25 25 25 25 225;
height: 100%;
width: 100%;
padding: 1;
}
Input {
border: none;
}
Button {
border: none;
}
SelectionList {
border: none;
min-height: 6;
}
Select {
border: none;
}
DataTable {
min-height: 6;
}
#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;
}
"""
def __init__(self, key=None, value=None):
super().__init__()
self.__key = key
self.__value = value
def on_mount(self):
if self.__key is not None:
self.query_one("#key_input", Input).value = str(self.__key)
if self.__value is not None:
self.query_one("#value_input", Input).value = str(self.__value)
def compose(self):
yield Header()
with Grid():
# 8
yield Static("Key")
yield Input(id="key_input", classes="four")
yield Static("Value")
yield Input(id="value_input", classes="four")
# 17
yield Static(" ", classes="five")
# 18
yield Button("Save", id="save_button")
yield Button("Cancel", id="cancel_button")
# 19
yield Static(" ", classes="five")
# 20
yield Static(" ", classes="five", id="messagestatic")
yield Footer(id="footer")
def getTagFromInput(self):
tagKey = self.query_one("#key_input", Input).value
tagValue = self.query_one("#value_input", Input).value
return (tagKey, tagValue)
# Event handler for button press
def on_button_pressed(self, event: Button.Pressed) -> None:
# Check if the button pressed is the one we are interested in
if event.button.id == "save_button":
self.dismiss(self.getTagFromInput())
if event.button.id == "cancel_button":
self.app.pop_screen()

View File

@@ -10,6 +10,7 @@ from .iso_language import IsoLanguage
from .track_type import TrackType
from ffx.model.track_tag import TrackTag
from ffx.track_descriptor import TrackDescriptor
class TrackController():
@@ -24,7 +25,6 @@ class TrackController():
try:
s = self.Session()
track = Track(pattern_id = int(trackDescriptor.getPatternId()),
track_type = int(trackDescriptor.getType().index()),
sub_index = int(trackDescriptor.getSubIndex()),
@@ -47,7 +47,10 @@ class TrackController():
s.close()
def updateTrack(self, trackId, trackDescriptor):
def updateTrack(self, trackId, trackDescriptor : TrackDescriptor):
if type(trackDescriptor) is not TrackDescriptor:
raise TypeError('TrackController.updateTrack(): Argument trackDescriptor is required to be of type TrackDescriptor')
try:
s = self.Session()
@@ -55,50 +58,37 @@ class TrackController():
if q.count():
track = q.first()
track : Track = q.first()
track.sub_index = int(trackDescriptor['sub_index'])
# track.language = str(trackDescriptor['language'].threeLetter())
# track.title = str(trackDescriptor['title'])
track.sub_index = int(trackDescriptor.getSubIndex())
track.disposition_flags = int(TrackDisposition.toFlags(trackDescriptor.getDispositionSet()))
s.commit()
descriptorTagKeys = trackDescriptor.getTags()
tagKeysInDescriptor = set(descriptorTagKeys.keys())
tagKeysInDb = {t.key for t in track.track_tags}
for k in tagKeysInDescriptor & tagKeysInDb: # to update
tags = [t for t in track.track_tags if t.key == k]
tags[0].value = descriptorTagKeys[k]
for k in tagKeysInDescriptor - tagKeysInDb: # to add
tag = TrackTag(track_id=track.id, key=k, value=descriptorTagKeys[k])
s.add(tag)
for k in tagKeysInDb - tagKeysInDescriptor: # to remove
tags = [t for t in track.track_tags if t.key == k]
s.delete(tags[0])
s.commit()
return True
else:
return False
except Exception as ex:
raise click.ClickException(f"TrackController.addTrack(): {repr(ex)}")
raise click.ClickException(f"TrackController.updateTrack(): {repr(ex)}")
finally:
s.close()
#
# def findAllTracks(self, patternId):
#
# try:
# s = self.Session()
#
# trackDescriptors = {}
# trackDescriptors[TrackType.AUDIO.label()] = []
# trackDescriptors[TrackType.SUBTITLE.label()] = []
#
# q_audio = s.query(Track).filter(Track.pattern_id == int(patternId), Track.track_type == TrackType.AUDIO.index())
# for audioTrack in q_audio.all():
# trackDescriptors[TrackType.AUDIO.label()].append(audioTrack.id)
#
# q_subtitle = s.query(Track).filter(Track.pattern_id == int(patternId), Track.track_type == TrackType.SUBTITLE.index())
# for subtitleTrack in q_subtitle.all():
# trackDescriptors[TrackType.SUBTITLE.label()].append(subtitleTrack.id)
#
# return trackDescriptors
#
# except Exception as ex:
# raise click.ClickException(f"TrackController.findAllTracks(): {repr(ex)}")
# finally:
# s.close()
#
def findAudioTracks(self, patternId):
try:
@@ -126,15 +116,13 @@ class TrackController():
s.close()
def findTrack(self, patternId : int, trackType : TrackType, subIndex : int):
def findTrack(self, patternId : int, trackType : TrackType, subIndex : int) -> Track:
try:
s = self.Session()
q = s.query(Track).filter(Track.pattern_id == int(patternId), Track.track_type == trackType.index(), Track.sub_index == int(subIndex))
if q.count():
#track = q.first()
#return int(track.id)
return q.first()
else:
return None
@@ -151,7 +139,6 @@ class TrackController():
if q.count():
#trackDescriptor = self.getTrackDict(q.first())
track = q.first()
q_siblings = s.query(Track).filter(Track.pattern_id == track.getPatternId(), Track.track_type == track.getType().index()).order_by(Track.sub_index)

View File

@@ -3,14 +3,12 @@ import click
from textual import events
from textual.app import App, ComposeResult
from textual.screen import Screen
from textual.widgets import Header, Footer, Placeholder, Label, ListView, ListItem, Static, DataTable, Button, Input
from textual.containers import Grid, Horizontal
from textual.widgets import Header, Footer, Static, Button
from textual.containers import Grid
from ffx.model.pattern import Pattern
from ffx.track_descriptor import TrackDescriptor
# from .show_controller import ShowController
# from .pattern_controller import PatternController
from .track_controller import TrackController
from .track_type import TrackType

View File

@@ -6,6 +6,7 @@ from .track_disposition import TrackDisposition
class TrackDescriptor():
ID_KEY = 'id'
INDEX_KEY = 'index'
SUB_INDEX_KEY = 'sub_index'
PATTERN_ID_KEY = 'pattern_id'
@@ -20,6 +21,13 @@ class TrackDescriptor():
def __init__(self, **kwargs):
if TrackDescriptor.ID_KEY in kwargs.keys():
if type(kwargs[TrackDescriptor.ID_KEY]) is not int:
raise TypeError(f"TrackDesciptor.__init__(): Argument {TrackDescriptor.ID_KEY} is required to be of type int")
self.__trackId = kwargs[TrackDescriptor.ID_KEY]
else:
self.__trackId = -1
if TrackDescriptor.PATTERN_ID_KEY in kwargs.keys():
if type(kwargs[TrackDescriptor.PATTERN_ID_KEY]) is not int:
raise TypeError(f"TrackDesciptor.__init__(): Argument {TrackDescriptor.PATTERN_ID_KEY} is required to be of type int")
@@ -134,6 +142,9 @@ class TrackDescriptor():
return None
def getId(self):
return self.__trackId
def getPatternId(self):
return self.__patternId

View File

@@ -3,16 +3,14 @@ import click, time
from textual import events
from textual.app import App, ComposeResult
from textual.screen import Screen
from textual.widgets import Header, Footer, Placeholder, Label, ListView, ListItem, Static, DataTable, Button, Input, Checkbox, SelectionList, Select
from textual.containers import Grid, Horizontal
from textual.widgets import Header, Footer, Static, Button, SelectionList, Select, DataTable, Input
from textual.containers import Grid
from ffx.model.show import Show
from ffx.model.pattern import Pattern
from .track_controller import TrackController
from .pattern_controller import PatternController
# from .show_controller import ShowController
from .tag_controller import TagController
from .track_type import TrackType
@@ -22,6 +20,11 @@ from .audio_layout import AudioLayout
from .track_descriptor import TrackDescriptor
from .tag_details_screen import TagDetailsScreen
from .tag_delete_screen import TagDeleteScreen
from textual.widgets._data_table import CellDoesNotExist
# Screen[dict[int, str, int]]
class TrackDetailsScreen(Screen):
@@ -86,35 +89,37 @@ class TrackDetailsScreen(Screen):
self.__tc = TrackController(context = self.context)
self.__pc = PatternController(context = self.context)
INDEX_KEY = 'index'
SUB_INDEX_KEY = 'sub_index'
PATTERN_ID_KEY = 'pattern_id'
TRACK_TYPE_KEY = 'track_type'
DISPOSITION_SET_KEY = 'disposition_set'
TAGS_KEY = 'tags'
AUDIO_LAYOUT_KEY = 'audio_layout'
# if trackDescriptor is None:
# self.__trackDescriptor = TrackDescriptor(index=,
# sub_index=
# pattern_id=patternId,
# track_type=trackType)
# else:
self.__tac = TagController(context = self.context)
self.__isNew = trackDescriptor is None
if self.__isNew:
self.__trackType = trackType
self.__subIndex = subIndex
self.__trackDescriptor = None
self.__pattern = self.__pc.getPattern(patternId) if patternId is not None else {}
self.__trackDescriptor : TrackDescriptor = None
self.__pattern : Pattern = self.__pc.getPattern(patternId) if patternId is not None else {}
else:
self.__trackType = trackDescriptor.getType()
self.__subIndex = trackDescriptor.getSubIndex()
self.__trackDescriptor = trackDescriptor
self.__pattern = self.__pc.getPattern(self.__trackDescriptor.getPatternId())
self.__trackDescriptor : TrackDescriptor = trackDescriptor
self.__pattern : Pattern = self.__pc.getPattern(self.__trackDescriptor.getPatternId())
def updateTags(self):
self.trackTagsTable.clear()
trackId = self.__trackDescriptor.getId()
if trackId != -1:
trackTags = self.__tac.findAllTrackTags(trackId)
for k,v in trackTags.items():
if k != 'language' and k != 'title':
row = (k,v)
self.trackTagsTable.add_row(*map(str, row))
def on_mount(self):
@@ -122,12 +127,9 @@ class TrackDetailsScreen(Screen):
if self.__pattern is not None:
self.query_one("#patternlabel", Static).update(self.__pattern.getPattern())
if self.__subIndex is not None:
self.query_one("#subindexlabel", Static).update(str(self.__subIndex))
for d in TrackDisposition:
dispositionIsSet = (self.__trackDescriptor is not None
@@ -140,6 +142,7 @@ class TrackDetailsScreen(Screen):
self.query_one("#language_select", Select).value = self.__trackDescriptor.getLanguage().label()
self.query_one("#title_input", Input).value = self.__trackDescriptor.getTitle()
self.updateTags()
def compose(self):
@@ -193,8 +196,9 @@ class TrackDetailsScreen(Screen):
# 11
yield Static("Stream tags")
yield Static(" ", classes="two")
yield Static(" ")
yield Button("Add", id="button_add_stream_tag")
yield Button("Edit", id="button_edit_stream_tag")
yield Button("Delete", id="button_delete_stream_tag")
# 12
yield self.trackTagsTable
@@ -243,13 +247,15 @@ class TrackDetailsScreen(Screen):
trackTags = {}
language = self.query_one("#language_select", Select).value
# raise click.ClickException(f"language={language}")
if language:
trackTags['language'] = IsoLanguage.find(language).threeLetter()
title = self.query_one("#title_input", Input).value
if title:
trackTags['title'] = title
kwargs[TrackDescriptor.TAGS_KEY] = trackTags
tableTags = {row[0]:row[1] for r in self.trackTagsTable.rows if (row := self.trackTagsTable.get_row(r)) and row[0] != 'language' and row[0] != 'title'}
kwargs[TrackDescriptor.TAGS_KEY] = trackTags | tableTags
dispositionFlags = sum([2**f for f in self.query_one("#dispositions_selection_list", SelectionList).selected])
kwargs[TrackDescriptor.DISPOSITION_SET_KEY] = TrackDisposition.toSet(dispositionFlags)
@@ -259,6 +265,31 @@ class TrackDetailsScreen(Screen):
return TrackDescriptor(**kwargs)
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.trackTagsTable.coordinate_to_cell_key(self.trackTagsTable.cursor_coordinate)
if row_key is not None:
selected_tag_data = self.trackTagsTable.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:
@@ -297,14 +328,50 @@ class TrackDetailsScreen(Screen):
else:
trackId = self.__tc.findTrack(self.__pattern.getId(), self.__trackType, self.__subIndex)
track = self.__tc.findTrack(self.__pattern.getId(), self.__trackType, self.__subIndex)
if self.__tc.updateTrack(trackId, trackDescriptor):
if self.__tc.updateTrack(track.getId(), trackDescriptor):
self.dismiss(trackDescriptor)
else:
self.app.pop_screen()
if event.button.id == "cancel_button":
self.app.pop_screen()
if event.button.id == "button_add_stream_tag":
if not self.__isNew:
self.app.push_screen(TagDetailsScreen(), self.handle_update_tag)
if event.button.id == "button_edit_stream_tag":
tagKey, tagValue = self.getSelectedTag()
self.app.push_screen(TagDetailsScreen(key=tagKey, value=tagValue), self.handle_update_tag)
if event.button.id == "button_delete_stream_tag":
tagKey, tagValue = self.getSelectedTag()
self.app.push_screen(TagDeleteScreen(key=tagKey, value=tagValue), self.handle_delete_tag)
def handle_update_tag(self, tag):
trackId = self.__trackDescriptor.getId()
if trackId == -1:
raise click.ClickException(f"TrackDetailsScreen.handle_add_tag: trackId not set (-1) trackDescriptor={self.__trackDescriptor}")
if self.__tac.updateTrackTag(trackId, tag[0], tag[1]) is not None:
self.updateTags()
def handle_delete_tag(self, trackTag):
trackId = self.__trackDescriptor.getId()
if trackId == -1:
raise click.ClickException(f"TrackDetailsScreen.handle_delete_tag: trackId not set (-1) trackDescriptor={self.__trackDescriptor}")
tag = self.__tac.findTrackTag(trackId, trackTag[0])
if tag is not None:
if self.__tac.deleteTrackTag(tag.id):
self.updateTags()