diff --git a/bin/ffx/file_properties.py b/bin/ffx/file_properties.py index d480867..73526a3 100644 --- a/bin/ffx/file_properties.py +++ b/bin/ffx/file_properties.py @@ -188,28 +188,6 @@ class FileProperties(): return json.loads(ffprobeOutput)['streams'] -# def getTrackDescriptor(self, streamObj): -# """Convert the stream describing json object into a track descriptor""" -# -# trackType = streamObj['codec_type'] -# -# descriptor = {} -# -# if trackType in [t.label() for t in TrackType]: -# -# descriptor['type'] = trackType -# -# descriptor = {} -# descriptor['disposition_list'] = [t for d in (k for (k,v) in streamObj['disposition'].items() if v) if (t := TrackDisposition.find(d)) if t is not None] -# -# descriptor['tags'] = streamObj['tags'] if 'tags' in streamObj.keys() else {} -# -# if trackType == TrackType.AUDIO.label(): -# descriptor['layout'] = AudioLayout.identify(streamObj) -# -# return descriptor - - def getMediaDescriptor(self): return MediaDescriptor.fromFfprobe(self.getFormatData(), self.getStreamData()) diff --git a/bin/ffx/media_details_screen.py b/bin/ffx/media_details_screen.py index 0bd1ccf..e905dbc 100644 --- a/bin/ffx/media_details_screen.py +++ b/bin/ffx/media_details_screen.py @@ -34,8 +34,8 @@ class MediaDetailsScreen(Screen): CSS = """ Grid { - grid-size: 4 9; - grid-rows: 8 2 2 2 8 2 8 2 8; + grid-size: 4 7; + grid-rows: 8 2 2 2 8 2 8; grid-columns: 25 125 10 75; height: 100%; width: 100%; @@ -149,7 +149,7 @@ class MediaDetailsScreen(Screen): # # def updateAudioTracks(self): # -# self.audioStreamsTable.clear() +# self.tracksTable.clear() # # if self.__pattern is not None: # @@ -166,7 +166,7 @@ class MediaDetailsScreen(Screen): # 'Yes' if TrackDisposition.DEFAULT in dispoSet else 'No', # 'Yes' if TrackDisposition.FORCED in dispoSet else 'No') # -# self.audioStreamsTable.add_row(*map(str, row)) +# self.tracksTable.add_row(*map(str, row)) # # def updateSubtitleTracks(self): # @@ -220,8 +220,9 @@ class MediaDetailsScreen(Screen): row = (mediaTagKey, mediaTagValue) # Convert each element to a string before adding self.mediaTagsTable.add_row(*map(str, row)) - self.updateAudioTracks(self.__mediaDescriptor.getAudioTracks()) - self.updateSubtitleTracks(self.__mediaDescriptor.getSubtitleTracks()) + #self.updateAudioTracks(self.__mediaDescriptor.getAudioTracks()) + #self.updateSubtitleTracks(self.__mediaDescriptor.getSubtitleTracks()) + self.updateTracks() if self.__mediaFilenamePattern is not None: @@ -295,42 +296,34 @@ class MediaDetailsScreen(Screen): - def updateAudioTracks(self, audioTracks): + def updateTracks(self): - self.audioStreamsTable.clear() + self.tracksTable.clear() - for at in audioTracks: + trackDescriptorList = self.__mediaDescriptor.getAllTracks() - dispoSet = at.getDispositionSet() + typeCounter = {} - row = (at.getSubIndex(), - " ", - at.getLanguage().label(), - at.getTitle(), - 'Yes' if TrackDisposition.DEFAULT in dispoSet else 'No', - 'Yes' if TrackDisposition.FORCED in dispoSet else 'No') - - self.audioStreamsTable.add_row(*map(str, row)) - - def updateSubtitleTracks(self, subtitleTracks): - - self.subtitleStreamsTable.clear() + for td in trackDescriptorList: - for st in subtitleTracks: + trackType = td.getType() + if not trackType in typeCounter.keys(): + typeCounter[trackType] = 0 - dispoSet = st.getDispositionSet() + dispoSet = td.getDispositionSet() - row = (st.getSubIndex(), + row = (td.getIndex(), + trackType.label(), + typeCounter[trackType], " ", - st.getLanguage().label(), - st.getTitle(), + td.getLanguage().label(), + td.getTitle(), 'Yes' if TrackDisposition.DEFAULT in dispoSet else 'No', 'Yes' if TrackDisposition.FORCED in dispoSet else 'No') - self.subtitleStreamsTable.add_row(*map(str, row)) - - + self.tracksTable.add_row(*map(str, row)) + typeCounter[trackType] += 1 def compose(self): @@ -352,36 +345,24 @@ class MediaDetailsScreen(Screen): # Define the columns with headers self.column_key_track_tag_key = self.mediaTagsTable.add_column("Key", width=20) - self.column_key_track_tag_value = self.mediaTagsTable.add_column("Value", width=90) + self.column_key_track_tag_value = self.mediaTagsTable.add_column("Value", width=100) self.mediaTagsTable.cursor_type = 'row' - - self.audioStreamsTable = DataTable() - - # Define the columns with headers - self.column_key_audio_subid = self.audioStreamsTable.add_column("Subindex", width=20) - self.column_key_audio_layout = self.audioStreamsTable.add_column("Layout", width=20) - self.column_key_audio_language = self.audioStreamsTable.add_column("Language", width=20) - self.column_key_audio_title = self.audioStreamsTable.add_column("Title", width=30) - self.column_key_audio_default = self.audioStreamsTable.add_column("Default", width=10) - self.column_key_audio_forced = self.audioStreamsTable.add_column("Forced", width=10) - - self.audioStreamsTable.cursor_type = 'row' - - - self.subtitleStreamsTable = DataTable() + self.tracksTable = DataTable() # Define the columns with headers - self.column_key_subtitle_subid = self.subtitleStreamsTable.add_column("Subindex", width=20) - self.column_key_subtitle_spacer = self.subtitleStreamsTable.add_column(" ", width=20) - self.column_key_subtitle_language = self.subtitleStreamsTable.add_column("Language", width=20) - self.column_key_subtitle_title = self.subtitleStreamsTable.add_column("Title", width=30) - self.column_key_subtitle_default = self.subtitleStreamsTable.add_column("Default", width=10) - self.column_key_subtitle_forced = self.subtitleStreamsTable.add_column("Forced", width=10) + self.column_key_track_index = self.tracksTable.add_column("Index", width=5) + self.column_key_track_type = self.tracksTable.add_column("Type", width=10) + self.column_key_track_sub_index = self.tracksTable.add_column("Subindex", width=5) + self.column_key_track_layout = self.tracksTable.add_column("Layout", width=10) + self.column_key_track_language = self.tracksTable.add_column("Language", width=15) + self.column_key_track_title = self.tracksTable.add_column("Title", width=48) + self.column_key_track_default = self.tracksTable.add_column("Default", width=8) + self.column_key_track_forced = self.tracksTable.add_column("Forced", width=8) - self.subtitleStreamsTable.cursor_type = 'row' + self.tracksTable.cursor_type = 'row' # Create the DataTable widget @@ -392,8 +373,6 @@ class MediaDetailsScreen(Screen): self.differencesTable.cursor_type = 'row' - - yield Header() with Grid(): @@ -427,17 +406,17 @@ class MediaDetailsScreen(Screen): yield Static(" ", classes="three") # 7 - yield Static("Audio Streams") - yield self.audioStreamsTable + yield Static("Streams") + yield self.tracksTable yield Static(" ") - # 8 - yield Static(" ", classes="three") - - # 9 - yield Static("Subtitle Streams") - yield self.subtitleStreamsTable - yield Static(" ") +# # 8 +# yield Static(" ", classes="three") +# +# # 9 +# yield Static("Subtitle Streams") +# yield self.subtitleStreamsTable +# yield Static(" ") # 1 @@ -467,7 +446,7 @@ class MediaDetailsScreen(Screen): # yield Static("") # yield Static("") # # 6 -# yield self.audioStreamsTable +# yield self.tracksTable # # # 7 # yield Static(" ", classes="five") @@ -511,10 +490,10 @@ class MediaDetailsScreen(Screen): # # # Fetch the currently selected row when 'Enter' is pressed # #selected_row_index = self.table.cursor_row -# row_key, col_key = self.audioStreamsTable.coordinate_to_cell_key(self.audioStreamsTable.cursor_coordinate) +# row_key, col_key = self.tracksTable.coordinate_to_cell_key(self.tracksTable.cursor_coordinate) # # if row_key is not None: -# selected_track_data = self.audioStreamsTable.get_row(row_key) +# selected_track_data = self.tracksTable.get_row(row_key) # # subIndex = int(selected_track_data[0]) # @@ -589,7 +568,7 @@ class MediaDetailsScreen(Screen): # if self.__pattern is not None: # # if event.button.id == "button_add_audio_stream": -# self.app.push_screen(TrackDetailsScreen(trackType = TrackType.AUDIO, patternId = self.__pattern.getId(), subIndex = len(self.audioStreamsTable.rows)), self.handle_add_track) +# self.app.push_screen(TrackDetailsScreen(trackType = TrackType.AUDIO, patternId = self.__pattern.getId(), subIndex = len(self.tracksTable.rows)), self.handle_add_track) # # selectedAudioTrack = self.getSelectedAudioTrackDescriptor() # if selectedAudioTrack is not None: @@ -638,7 +617,7 @@ class MediaDetailsScreen(Screen): # 'Yes' if TrackDisposition.DEFAULT in dispoSet else 'No', # 'Yes' if TrackDisposition.FORCED in dispoSet else 'No') # -# self.audioStreamsTable.add_row(*map(str, row)) +# self.tracksTable.add_row(*map(str, row)) # # if trackType == TrackType.SUBTITLE: # @@ -657,12 +636,12 @@ class MediaDetailsScreen(Screen): # try: # if trackDescriptor.getType() == TrackType.AUDIO: # -# row_key, col_key = self.audioStreamsTable.coordinate_to_cell_key(self.audioStreamsTable.cursor_coordinate) +# row_key, col_key = self.tracksTable.coordinate_to_cell_key(self.tracksTable.cursor_coordinate) # -# self.audioStreamsTable.update_cell(row_key, self.column_key_audio_language, trackDescriptor.getLanguage().label()) -# self.audioStreamsTable.update_cell(row_key, self.column_key_audio_title, trackDescriptor.getTitle()) -# self.audioStreamsTable.update_cell(row_key, self.column_key_audio_default, 'Yes' if TrackDisposition.DEFAULT in trackDescriptor.getDispositionSet() else 'No') -# self.audioStreamsTable.update_cell(row_key, self.column_key_audio_forced, 'Yes' if TrackDisposition.FORCED in trackDescriptor.getDispositionSet() else 'No') +# 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') # # if trackDescriptor.getType() == TrackType.SUBTITLE: # diff --git a/bin/ffx/model/track.py b/bin/ffx/model/track.py index caa7337..479518e 100644 --- a/bin/ffx/model/track.py +++ b/bin/ffx/model/track.py @@ -32,7 +32,7 @@ class Track(Base): track_type = Column(Integer) # TrackType index = Column(Integer) - sub_index = Column(Integer) + # sub_index = Column(Integer) # v1.x pattern_id = Column(Integer, ForeignKey('patterns.id', ondelete="CASCADE")) @@ -57,7 +57,7 @@ class Track(Base): @classmethod - def fromStreamObj(cls, streamObj, subIndex, patternId): + def fromStreamObj(cls, streamObj, patternId): """{ 'index': 4, 'codec_name': 'hdmv_pgs_subtitle', @@ -128,8 +128,8 @@ class Track(Base): if trackType in [t.label() for t in TrackType]: + # sub_index = int(subIndex), return cls(pattern_id = patternId, - sub_index = int(subIndex), track_type = trackType, disposition_flags = sum([2**t.index() for (k,v) in streamObj['disposition'].items() if v and (t := TrackDisposition.find(k)) is not None])) @@ -149,9 +149,6 @@ class Track(Base): def getIndex(self): return int(self.index) if self.index is not None else -1 - def getSubIndex(self): - return int(self.sub_index) if self.sub_index is not None else -1 - def getLanguage(self): tags = {t.key:t.value for t in self.track_tags} return IsoLanguage.findThreeLetter(tags['language']) if 'language' in tags.keys() else IsoLanguage.UNDEFINED @@ -167,7 +164,7 @@ class Track(Base): return {str(t.key):str(t.value) for t in self.track_tags} - def getDescriptor(self) -> TrackDescriptor: + def getDescriptor(self, subIndex : int = -1) -> TrackDescriptor: kwargs = {} @@ -175,7 +172,9 @@ class Track(Base): kwargs[TrackDescriptor.PATTERN_ID_KEY] = self.getPatternId() kwargs[TrackDescriptor.INDEX_KEY] = self.getIndex() - kwargs[TrackDescriptor.SUB_INDEX_KEY] = self.getSubIndex() + + if subIndex > -1: + kwargs[TrackDescriptor.SUB_INDEX_KEY] = subIndex kwargs[TrackDescriptor.TRACK_TYPE_KEY] = self.getType() kwargs[TrackDescriptor.DISPOSITION_SET_KEY] = self.getDispositionSet() diff --git a/bin/ffx/pattern_details_screen.py b/bin/ffx/pattern_details_screen.py index 03213c6..aa5b4fc 100644 --- a/bin/ffx/pattern_details_screen.py +++ b/bin/ffx/pattern_details_screen.py @@ -8,6 +8,7 @@ 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 @@ -30,8 +31,8 @@ class PatternDetailsScreen(Screen): CSS = """ Grid { - grid-size: 5 12; - grid-rows: 2 2 2 2 2 6 2 2 6 2 2 2; + grid-size: 5 9; + grid-rows: 2 2 2 2 2 6 2 2 2; grid-columns: 25 25 25 25 25; height: 100%; width: 100%; @@ -68,6 +69,10 @@ class PatternDetailsScreen(Screen): height: 100%; border: solid green; } + + #tracks_table { + row-span: 4; + } """ def __init__(self, patternId = None, showId = None): @@ -105,47 +110,38 @@ class PatternDetailsScreen(Screen): s.close() - def updateAudioTracks(self): + def updateTracks(self): - self.audioStreamsTable.clear() + self.tracksTable.clear() if self.__pattern is not None: - audioTracks = self.__tc.findAudioTracks(self.__pattern.getId()) + tracks = self.__tc.findTracks(self.__pattern.getId()) - for at in audioTracks: + typeCounter = {} - dispoSet = at.getDispositionSet() + for tr in tracks: - row = (at.getSubIndex(), - " ", - at.getLanguage().label(), - at.getTitle(), - 'Yes' if TrackDisposition.DEFAULT in dispoSet else 'No', - 'Yes' if TrackDisposition.FORCED in dispoSet else 'No') + td : TrackDescriptor = tr.getDescriptor() - self.audioStreamsTable.add_row(*map(str, row)) - - def updateSubtitleTracks(self): - - self.subtitleStreamsTable.clear() - - if self.__pattern is not None: + trackType = td.getType() + if not trackType in typeCounter.keys(): + typeCounter[trackType] = 0 - subtitleTracks = self.__tc.findSubtitleTracks(self.__pattern.getId()) + dispoSet = td.getDispositionSet() - for st in subtitleTracks: - - dispoSet = st.getDispositionSet() - - row = (st.getSubIndex(), + row = (td.getIndex(), + trackType.label(), + typeCounter[trackType], " ", - st.getLanguage().label(), - st.getTitle(), + td.getLanguage().label(), + td.getTitle(), 'Yes' if TrackDisposition.DEFAULT in dispoSet else 'No', 'Yes' if TrackDisposition.FORCED in dispoSet else 'No') - self.subtitleStreamsTable.add_row(*map(str, row)) + self.tracksTable.add_row(*map(str, row)) + + typeCounter[trackType] += 1 def on_mount(self): @@ -157,38 +153,25 @@ class PatternDetailsScreen(Screen): self.query_one("#pattern_input", Input).value = str(self.__pattern.getPattern()) - self.updateAudioTracks() - self.updateSubtitleTracks() - + self.updateTracks() def compose(self): - self.audioStreamsTable = DataTable(classes="five") + self.tracksTable = DataTable(id="tracks_table", classes="five") - # Define the columns with headers - self.column_key_audio_subid = self.audioStreamsTable.add_column("Subindex", width=20) - self.column_key_audio_layout = self.audioStreamsTable.add_column("Layout", width=20) - self.column_key_audio_language = self.audioStreamsTable.add_column("Language", width=20) - self.column_key_audio_title = self.audioStreamsTable.add_column("Title", width=30) - self.column_key_audio_default = self.audioStreamsTable.add_column("Default", width=10) - self.column_key_audio_forced = self.audioStreamsTable.add_column("Forced", width=10) - - self.audioStreamsTable.cursor_type = 'row' + self.column_key_track_index = self.tracksTable.add_column("Index", width=5) + self.column_key_track_type = self.tracksTable.add_column("Type", width=10) + self.column_key_track_sub_index = self.tracksTable.add_column("Subindex", width=5) + self.column_key_track_layout = self.tracksTable.add_column("Layout", width=10) + self.column_key_track_language = self.tracksTable.add_column("Language", width=15) + self.column_key_track_title = self.tracksTable.add_column("Title", width=48) + self.column_key_track_default = self.tracksTable.add_column("Default", width=8) + self.column_key_track_forced = self.tracksTable.add_column("Forced", width=8) + self.tracksTable.cursor_type = 'row' self.subtitleStreamsTable = DataTable(classes="five") - # Define the columns with headers - self.column_key_subtitle_subid = self.subtitleStreamsTable.add_column("Subindex", width=20) - self.column_key_subtitle_spacer = self.subtitleStreamsTable.add_column(" ", width=20) - self.column_key_subtitle_language = self.subtitleStreamsTable.add_column("Language", width=20) - self.column_key_subtitle_title = self.subtitleStreamsTable.add_column("Title", width=30) - self.column_key_subtitle_default = self.subtitleStreamsTable.add_column("Default", width=10) - self.column_key_subtitle_forced = self.subtitleStreamsTable.add_column("Forced", width=10) - - self.subtitleStreamsTable.cursor_type = 'row' - - yield Header() with Grid(): @@ -200,7 +183,7 @@ class PatternDetailsScreen(Screen): # 2 yield Static("from show") yield Static("", id="showlabel", classes="three") - yield Button("Substitute pattern", id="patternbutton") + yield Button("Substitute pattern", id="pattern_button") # 3 yield Static(" ", classes="five") @@ -208,42 +191,27 @@ class PatternDetailsScreen(Screen): yield Static(" ", classes="five") # 5 - yield Static("Audio streams") + yield Static("Streams") yield Static(" ") if self.__pattern is not None: - yield Button("Add", id="button_add_audio_stream") - yield Button("Edit", id="button_edit_audio_stream") - yield Button("Delete", id="button_delete_audio_stream") + yield Button("Add", id="button_add_track") + yield Button("Edit", id="button_edit_track") + yield Button("Delete", id="button_delete_track") else: yield Static("") yield Static("") yield Static("") # 6 - yield self.audioStreamsTable + yield self.tracksTable # 7 yield Static(" ", classes="five") # 8 - yield Static("Subtitle streams") - yield Static(" ") - - if self.__pattern is not None: - yield Button("Add", id="button_add_subtitle_stream") - yield Button("Edit", id="button_edit_subtitle_stream") - yield Button("Delete", id="button_delete_subtitle_stream") - else: - yield Static("") - yield Static("") - yield Static("") - # 9 - yield self.subtitleStreamsTable - - # 10 yield Static(" ", classes="five") - # 11 + # 9 yield Button("Save", id="save_button") yield Button("Cancel", id="cancel_button") @@ -255,7 +223,7 @@ class PatternDetailsScreen(Screen): - def getSelectedAudioTrackDescriptor(self): + def getSelectedTrackDescriptor(self): if not self.__pattern: return None @@ -264,14 +232,15 @@ class PatternDetailsScreen(Screen): # Fetch the currently selected row when 'Enter' is pressed #selected_row_index = self.table.cursor_row - row_key, col_key = self.audioStreamsTable.coordinate_to_cell_key(self.audioStreamsTable.cursor_coordinate) + row_key, col_key = self.tracksTable.coordinate_to_cell_key(self.tracksTable.cursor_coordinate) if row_key is not None: - selected_track_data = self.audioStreamsTable.get_row(row_key) + selected_track_data = self.tracksTable.get_row(row_key) - subIndex = int(selected_track_data[0]) + trackIndex = int(selected_track_data[0]) + trackSubIndex = int(selected_track_data[2]) - return self.__tc.findTrack(self.__pattern.getId(), TrackType.AUDIO, subIndex).getDescriptor() + return self.__tc.getTrack(self.__pattern.getId(), trackIndex).getDescriptor(subIndex=trackSubIndex) else: return None @@ -280,32 +249,6 @@ class PatternDetailsScreen(Screen): return None - def getSelectedSubtitleTrackDescriptor(self) -> TrackDescriptor: - - if not self.__pattern is None: - return None - - try: - - # Fetch the currently selected row when 'Enter' is pressed - #selected_row_index = self.table.cursor_row - 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) - subIndex = int(selected_track_data[0]) - - return self.__tc.findTrack(self.__pattern.getId(), TrackType.SUBTITLE, subIndex).getDescriptor() - - else: - return None - - except CellDoesNotExist: - return None - - - # 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 @@ -327,14 +270,6 @@ class PatternDetailsScreen(Screen): patternId = self.__pc.addPattern(patternDescriptor) if patternId is not None: - # Add dummy video track - kwargs = {} - kwargs[TrackDescriptor.INDEX_KEY] = 0 - kwargs[TrackDescriptor.SUB_INDEX_KEY] = 0 - kwargs[TrackDescriptor.PATTERN_ID_KEY] = patternId - kwargs[TrackDescriptor.TRACK_TYPE_KEY] = TrackType.VIDEO - self.__tc.addTrack(TrackDescriptor(**kwargs)) - self.dismiss(patternDescriptor) else: #TODO: Meldung @@ -349,36 +284,19 @@ class PatternDetailsScreen(Screen): # Save pattern when just created before adding streams if self.__pattern is not None: - numVideoStreams = len(self.__pattern.getMediaDescriptor().getVideoTracks()) - numAudioStreams = len(self.audioStreamsTable.rows) - numSubtitleStreams = len(self.subtitleStreamsTable.rows) - - addedTrackIndex = numVideoStreams + numAudioStreams + numSubtitleStreams + numTracks = len(self.tracksTable.rows) - #NOTE: Track index + if event.button.id == "button_add_track": + self.app.push_screen(TrackDetailsScreen(patternId = self.__pattern.getId(), index = numTracks), self.handle_add_track) - if event.button.id == "button_add_audio_stream": - self.app.push_screen(TrackDetailsScreen(trackType = TrackType.AUDIO, patternId = self.__pattern.getId(), index = addedTrackIndex, subIndex = numAudioStreams), 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) - selectedAudioTrack = self.getSelectedAudioTrackDescriptor() - if selectedAudioTrack is not None: - if event.button.id == "button_edit_audio_stream": - - self.app.push_screen(TrackDetailsScreen(trackDescriptor = selectedAudioTrack), self.handle_edit_track) - if event.button.id == "button_delete_audio_stream": - self.app.push_screen(TrackDeleteScreen(trackDescriptor = selectedAudioTrack), self.handle_delete_track) - - if event.button.id == "button_add_subtitle_stream": - self.app.push_screen(TrackDetailsScreen(trackType = TrackType.SUBTITLE, patternId = self.__pattern.getId(), index = addedTrackIndex, subIndex = numSubtitleStreams), self.handle_add_track) - - selectedSubtitleTrack = self.getSelectedSubtitleTrackDescriptor() - if selectedSubtitleTrack is not None: - if event.button.id == "button_edit_subtitle_stream": - self.app.push_screen(TrackDetailsScreen(trackDescriptor = selectedSubtitleTrack), self.handle_edit_track) - if event.button.id == "button_delete_subtitle_stream": - self.app.push_screen(TrackDeleteScreen(trackDescriptor = selectedSubtitleTrack), self.handle_delete_track) - - if event.button.id == "patternbutton": + if event.button.id == "pattern_button": INDICATOR_PATTERN = '([sS][0-9]+[eE][0-9]+)' @@ -390,70 +308,41 @@ class PatternDetailsScreen(Screen): self.query_one("#pattern_input", Input).value = pattern.replace(patternMatch.group(1), INDICATOR_PATTERN) - def handle_add_track(self, trackDescriptor): + 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() - if trackType == TrackType.AUDIO: - - row = (subIndex, - " ", - language.label(), - title, - 'Yes' if TrackDisposition.DEFAULT in dispoSet else 'No', - 'Yes' if TrackDisposition.FORCED in dispoSet else 'No') + 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.audioStreamsTable.add_row(*map(str, row)) - - if trackType == TrackType.SUBTITLE: - - row = (subIndex, - " ", - language.label(), - title, - 'Yes' if TrackDisposition.DEFAULT in dispoSet else 'No', - 'Yes' if TrackDisposition.FORCED in dispoSet else 'No') - - self.subtitleStreamsTable.add_row(*map(str, row)) + self.tracksTable.add_row(*map(str, row)) def handle_edit_track(self, trackDescriptor : TrackDescriptor): try: - if trackDescriptor.getType() == TrackType.AUDIO: - - row_key, col_key = self.audioStreamsTable.coordinate_to_cell_key(self.audioStreamsTable.cursor_coordinate) - self.audioStreamsTable.update_cell(row_key, self.column_key_audio_language, trackDescriptor.getLanguage().label()) - self.audioStreamsTable.update_cell(row_key, self.column_key_audio_title, trackDescriptor.getTitle()) - self.audioStreamsTable.update_cell(row_key, self.column_key_audio_default, 'Yes' if TrackDisposition.DEFAULT in trackDescriptor.getDispositionSet() else 'No') - self.audioStreamsTable.update_cell(row_key, self.column_key_audio_forced, 'Yes' if TrackDisposition.FORCED in trackDescriptor.getDispositionSet() else 'No') + row_key, col_key = self.tracksTable.coordinate_to_cell_key(self.tracksTable.cursor_coordinate) - if trackDescriptor.getType() == TrackType.SUBTITLE: - - row_key, col_key = self.subtitleStreamsTable.coordinate_to_cell_key(self.subtitleStreamsTable.cursor_coordinate) - - self.subtitleStreamsTable.update_cell(row_key, self.column_key_subtitle_language, trackDescriptor.getLanguage().label()) - self.subtitleStreamsTable.update_cell(row_key, self.column_key_subtitle_title, trackDescriptor.getTitle()) - self.subtitleStreamsTable.update_cell(row_key, self.column_key_subtitle_default, 'Yes' if TrackDisposition.DEFAULT in trackDescriptor.getDispositionSet() else 'No') - self.subtitleStreamsTable.update_cell(row_key, self.column_key_subtitle_forced, 'Yes' if TrackDisposition.FORCED in trackDescriptor.getDispositionSet() else 'No') + 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): - - try: - if trackDescriptor.getType() == TrackType.AUDIO: - self.updateAudioTracks() - - if trackDescriptor.getType() == TrackType.SUBTITLE: - self.updateSubtitleTracks() - - except CellDoesNotExist: - pass + self.updateTracks() diff --git a/bin/ffx/track_controller.py b/bin/ffx/track_controller.py index 30fbb14..77461d7 100644 --- a/bin/ffx/track_controller.py +++ b/bin/ffx/track_controller.py @@ -21,14 +21,13 @@ class TrackController(): self.Session = self.context['database']['session'] # convenience - def addTrack(self, trackDescriptor): + def addTrack(self, trackDescriptor : TrackDescriptor): try: s = self.Session() track = Track(pattern_id = int(trackDescriptor.getPatternId()), track_type = int(trackDescriptor.getType().index()), index = int(trackDescriptor.getIndex()), - sub_index = int(trackDescriptor.getSubIndex()), disposition_flags = int(TrackDisposition.toFlags(trackDescriptor.getDispositionSet()))) s.add(track) @@ -89,6 +88,32 @@ class TrackController(): finally: s.close() + def findTracks(self, patternId): + + try: + s = self.Session() + + q = s.query(Track).filter(Track.pattern_id == int(patternId)) + return [a for a in q.all()] + + except Exception as ex: + raise click.ClickException(f"TrackController.findTracks(): {repr(ex)}") + finally: + s.close() + + #TODO: mit optionalem Parameter lösen ^ + def findVideoTracks(self, patternId): + + try: + s = self.Session() + + q = s.query(Track).filter(Track.pattern_id == int(patternId), Track.track_type == TrackType.VIDEO.index()) + return [a for a in q.all()] + + except Exception as ex: + raise click.ClickException(f"TrackController.findVideoTracks(): {repr(ex)}") + finally: + s.close() def findAudioTracks(self, patternId): @@ -117,11 +142,11 @@ class TrackController(): s.close() - def findTrack(self, patternId : int, trackType : TrackType, subIndex : int) -> Track: + def getTrack(self, patternId : int, index: 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)) + q = s.query(Track).filter(Track.pattern_id == int(patternId), Track.index == int(index)) if q.count(): return q.first() @@ -129,37 +154,35 @@ class TrackController(): return None except Exception as ex: - raise click.ClickException(f"TrackController.findTrack(): {repr(ex)}") + raise click.ClickException(f"TrackController.getTrack(): {repr(ex)}") finally: s.close() def deleteTrack(self, trackId): try: s = self.Session() + q = s.query(Track).filter(Track.id == int(trackId)) if q.count(): + patternId = int(q.first().pattern_id) - 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) + q_siblings = s.query(Track).filter(Track.pattern_id == patternId).order_by(Track.index) - subIndex = 0 + index = 0 for track in q_siblings.all(): - if track.sub_index == track.getSubIndex(): + if track.id == int(trackId): s.delete(track) else: - track.sub_index = subIndex - subIndex += 1 + track.index = index + index += 1 s.commit() return True - return False - except Exception as ex: raise click.ClickException(f"TrackController.deleteTrack(): {repr(ex)}") finally: diff --git a/bin/ffx/track_delete_screen.py b/bin/ffx/track_delete_screen.py index fd53b71..c600755 100644 --- a/bin/ffx/track_delete_screen.py +++ b/bin/ffx/track_delete_screen.py @@ -7,6 +7,7 @@ from textual.widgets import Header, Footer, Static, Button from textual.containers import Grid from ffx.model.pattern import Pattern +from ffx.model.track import Track from ffx.track_descriptor import TrackDescriptor from .track_controller import TrackController @@ -121,7 +122,7 @@ class TrackDeleteScreen(Screen): if event.button.id == "delete_button": - track = self.__tc.findTrack(self.__trackDescriptor.getPatternId(), self.__trackDescriptor.getType(), self.__trackDescriptor.getSubIndex()) + track = self.__tc.getTrack(self.__trackDescriptor.getPatternId(), self.__trackDescriptor.getIndex()) if track is None: raise click.ClickException(f"Track is none: patternId={self.__trackDescriptor.getPatternId()} type={self.__trackDescriptor.getType()} subIndex={self.__trackDescriptor.getSubIndex()}") diff --git a/bin/ffx/track_descriptor.py b/bin/ffx/track_descriptor.py index 52aeeb6..394d3b1 100644 --- a/bin/ffx/track_descriptor.py +++ b/bin/ffx/track_descriptor.py @@ -47,12 +47,11 @@ class TrackDescriptor(): if TrackDescriptor.SUB_INDEX_KEY in kwargs.keys(): if type(kwargs[TrackDescriptor.SUB_INDEX_KEY]) is not int: - raise TypeError(f"TrackDesciptor.__init__(): Argument {TrackDescriptor.SUB_INDEX_KEY} is required to be of type dict") + raise TypeError(f"TrackDesciptor.__init__(): Argument {TrackDescriptor.SUB_INDEX_KEY} is required to be of type int") self.__subIndex = kwargs[TrackDescriptor.SUB_INDEX_KEY] else: self.__subIndex = -1 - if TrackDescriptor.TRACK_TYPE_KEY in kwargs.keys(): if type(kwargs[TrackDescriptor.TRACK_TYPE_KEY]) is not TrackType: raise TypeError(f"TrackDesciptor.__init__(): Argument {TrackDescriptor.TRACK_TYPE_KEY} is required to be of type TrackType") diff --git a/bin/ffx/track_details_screen.py b/bin/ffx/track_details_screen.py index 6b41f42..a72f5b4 100644 --- a/bin/ffx/track_details_screen.py +++ b/bin/ffx/track_details_screen.py @@ -7,6 +7,7 @@ from textual.widgets import Header, Footer, Static, Button, SelectionList, Selec from textual.containers import Grid from ffx.model.pattern import Pattern +from ffx.model.track import Track from .track_controller import TrackController from .pattern_controller import PatternController @@ -32,9 +33,9 @@ class TrackDetailsScreen(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; + grid-size: 5 21; + grid-rows: 2 2 2 2 2 3 2 2 3 2 2 2 2 2 6 2 2 6 2 2 2; + grid-columns: 25 25 25 25 125; height: 100%; width: 100%; padding: 1; @@ -79,6 +80,10 @@ class TrackDetailsScreen(Screen): height: 100%; border: solid green; } + + .yellow { + tint: yellow 40%; + } """ def __init__(self, trackDescriptor : TrackDescriptor = None, patternId = None, trackType : TrackType = None, index = None, subIndex = None): @@ -126,11 +131,14 @@ class TrackDetailsScreen(Screen): def on_mount(self): + self.query_one("#index_label", Static).update(str(self.__index) if self.__index is not None else '-') + self.query_one("#subindex_label", Static).update(str(self.__subIndex)if self.__subIndex is not None else '-') + if self.__pattern is not None: - self.query_one("#patternlabel", Static).update(self.__pattern.getPattern()) + self.query_one("#pattern_label", Static).update(self.__pattern.getPattern()) - if self.__subIndex is not None: - self.query_one("#subindexlabel", Static).update(str(self.__subIndex)) + if self.__trackType is not None: + self.query_one("#type_select", Select).value = self.__trackType.label() for d in TrackDisposition: @@ -153,7 +161,7 @@ class TrackDetailsScreen(Screen): # Define the columns with headers self.column_key_track_tag_key = self.trackTagsTable.add_column("Key", width=10) - self.column_key_track_tag_value = self.trackTagsTable.add_column("Value", width=30) + self.column_key_track_tag_value = self.trackTagsTable.add_column("Value", width=125) self.trackTagsTable.cursor_type = 'row' @@ -165,71 +173,83 @@ class TrackDetailsScreen(Screen): with Grid(): # 1 - yield Static(f"New {self.__trackType.label()} stream" if self.__isNew else f"Edit {self.__trackType.label()} stream", id="toplabel", classes="five") + yield Static(f"New stream" if self.__isNew else f"Edit stream", id="toplabel", classes="five") # 2 yield Static("for pattern") - yield Static("", id="patternlabel", classes="four") + yield Static("", id="pattern_label", classes="four") # 3 - yield Static("sub index") - yield Static("", id="subindexlabel", classes="four") + yield Static(" ", classes="five") # 4 - yield Static(" ", classes="five") + yield Static("Index / Subindex") + yield Static("", id="index_label", classes="two") + yield Static("", id="subindex_label", classes="two") + # 5 yield Static(" ", classes="five") # 6 + yield Static("Type") + yield Select.from_values([t.label() for t in TrackType], classes="four", id="type_select") + + # 7 + yield Static(" ", classes="five") + + # 9 + yield Static(" ", classes="five") + + # 10 yield Static("Language") yield Select.from_values(languages, classes="four", id="language_select") - # 7 + # 11 yield Static(" ", classes="five") - # 8 + # 12 yield Static("Title") yield Input(id="title_input", classes="four") - # 9 + # 13 yield Static(" ", classes="five") - # 10 + # 14 yield Static(" ", classes="five") - # 11 + # 15 yield Static("Stream tags") 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 + # 16 yield self.trackTagsTable - # 13 + # 17 yield Static(" ", classes="five") - # 14 + # 18 yield Static("Stream dispositions", classes="five") - # 15 + # 19 yield SelectionList[int]( classes="five", id = "dispositions_selection_list" ) - # 16 + # 20 yield Static(" ", classes="five") - # 17 + # 21 yield Static(" ", classes="five") - # 18 + # 22 yield Button("Save", id="save_button") yield Button("Cancel", id="cancel_button") - # 19 + # 23 yield Static(" ", classes="five") - # 20 + # 24 yield Static(" ", classes="five", id="messagestatic") @@ -243,9 +263,9 @@ class TrackDetailsScreen(Screen): kwargs[TrackDescriptor.PATTERN_ID_KEY] = int(self.__pattern.getId()) kwargs[TrackDescriptor.INDEX_KEY] = self.__index - kwargs[TrackDescriptor.SUB_INDEX_KEY] = self.__subIndex + kwargs[TrackDescriptor.SUB_INDEX_KEY] = self.__subIndex #! - kwargs[TrackDescriptor.TRACK_TYPE_KEY] = self.__trackType + kwargs[TrackDescriptor.TRACK_TYPE_KEY] = TrackType.fromLabel(self.query_one("#type_select", Select).value) trackTags = {} language = self.query_one("#language_select", Select).value @@ -298,10 +318,10 @@ class TrackDetailsScreen(Screen): # Check if the button pressed is the one we are interested in if event.button.id == "save_button": - trackDescriptor = self.getTrackDescriptorFromInput() - # Check for multiple default/forced disposition flags + if self.__trackType == TrackType.VIDEO: + trackList = self.__tc.findVideoTracks(self.__pattern.getId()) if self.__trackType == TrackType.AUDIO: trackList = self.__tc.findAudioTracks(self.__pattern.getId()) elif self.__trackType == TrackType.SUBTITLE: @@ -314,6 +334,9 @@ class TrackDetailsScreen(Screen): numDefaultTracks = len([t for t in siblingTrackList if TrackDisposition.DEFAULT in t.getDispositionSet()]) numForcedTracks = len([t for t in siblingTrackList if TrackDisposition.FORCED in t.getDispositionSet()]) + self.__subIndex = len(trackList) + trackDescriptor = self.getTrackDescriptorFromInput() + if ((TrackDisposition.DEFAULT in trackDescriptor.getDispositionSet() and numDefaultTracks) or (TrackDisposition.FORCED in trackDescriptor.getDispositionSet() and numForcedTracks)): @@ -330,7 +353,7 @@ class TrackDetailsScreen(Screen): else: - track = self.__tc.findTrack(self.__pattern.getId(), self.__trackType, self.__subIndex) + track = self.__tc.getTrack(self.__pattern.getId(), self.__index) if self.__tc.updateTrack(track.getId(), trackDescriptor): self.dismiss(trackDescriptor)