From 1c9f67e47af5eacdcd1f7d3c276bea971d9b3e6d Mon Sep 17 00:00:00 2001 From: Maveno Date: Sat, 5 Oct 2024 22:41:57 +0200 Subject: [PATCH] nightl --- bin/ffx.py | 23 +++- bin/ffx/database.py | 41 +++++++ bin/ffx/ffx_app.py | 33 +---- bin/ffx/file_properties.py | 8 +- bin/ffx/model/pattern.py | 31 +++-- bin/ffx/model/track.py | 18 +-- bin/ffx/pattern_controller.py | 59 +++++---- bin/ffx/pattern_delete_screen.py | 2 +- bin/ffx/pattern_details_screen.py | 124 +++++++++---------- bin/ffx/show_controller.py | 2 +- bin/ffx/show_delete_screen.py | 2 +- bin/ffx/show_details_screen.py | 2 +- bin/ffx/shows_screen.py | 2 +- bin/ffx/tag_controller.py | 193 ++++++++++++++++++++++++++++++ bin/ffx/track_controller.py | 125 +++++++++++-------- bin/ffx/track_delete_screen.py | 2 +- bin/ffx/track_descriptor.py | 94 +++++++++++++-- bin/ffx/track_details_screen.py | 159 +++++++++++++++--------- bin/ffx/track_disposition.py | 24 ++-- 19 files changed, 670 insertions(+), 274 deletions(-) create mode 100644 bin/ffx/database.py create mode 100644 bin/ffx/tag_controller.py diff --git a/bin/ffx.py b/bin/ffx.py index c85094b..408c0c4 100755 --- a/bin/ffx.py +++ b/bin/ffx.py @@ -4,6 +4,8 @@ import os, sys, subprocess, json, click, time, re from ffx.ffx_app import FfxApp +from ffx.database import databaseContext + from ffx.media_descriptor import MediaDescriptor from ffx.file_properties import FileProperties @@ -221,8 +223,9 @@ def searchSubtitleFiles(dir, prefix): @click.pass_context def ffx(ctx): """FFX""" + ctx.obj = {} - pass + ctx.obj['database'] = databaseContext() # Define a subcommand @@ -239,13 +242,18 @@ def help(): -@click.argument('filename', nargs=1) + @ffx.command() -def inspect(filename): +@click.pass_context +@click.argument('filename', nargs=1) +def inspect(ctx, filename): + +# if 'database' not in ctx.obj.keys(): +# ctx.obj['database'] = databaseContext() try: - fp = FileProperties(filename) + fp = FileProperties(ctx.obj, filename) md = fp.getMediaDescriptor() print(md.getTags()) @@ -315,6 +323,9 @@ def inspect(filename): def shows(ctx): +# if 'database' not in ctx.obj.keys(): +# ctx.obj['database'] = databaseContext() + app = FfxApp(ctx.obj) app.run() @@ -402,6 +413,10 @@ def convert(ctx, context = ctx.obj + if 'database' not in context.keys(): + context['database'] = databaseContext() + + click.echo(f"\nVideo encoder: {video_encoder}") qualityTokens = quality.split(',') diff --git a/bin/ffx/database.py b/bin/ffx/database.py new file mode 100644 index 0000000..545074f --- /dev/null +++ b/bin/ffx/database.py @@ -0,0 +1,41 @@ +import os + +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 + + +def databaseContext(): + + databaseContext = {} + + # Initialize DB + homeDir = os.path.expanduser("~") + ffxVarDir = os.path.join(homeDir, '.local', 'var', 'ffx') + if not os.path.exists(ffxVarDir): + os.makedirs(ffxVarDir) + + databaseContext['url'] = f"sqlite:///{os.path.join(ffxVarDir, 'ffx.db')}" + databaseContext['engine'] = create_engine(databaseContext['url']) + databaseContext['session'] = sessionmaker(bind=databaseContext['engine']) + + Base.metadata.create_all(databaseContext['engine']) + + # isSyncronuous = False + # while not isSyncronuous: + # while True: + # try: + # with databaseContext['database_engine'].connect() as connection: + # connection.execute(sqlalchemy.text('PRAGMA foreign_keys=ON;')) + # #isSyncronuous = True + # break + # except sqlite3.OperationalError: + # time.sleep(0.1) + + return databaseContext \ No newline at end of file diff --git a/bin/ffx/ffx_app.py b/bin/ffx/ffx_app.py index 7012801..c2f8cd5 100644 --- a/bin/ffx/ffx_app.py +++ b/bin/ffx/ffx_app.py @@ -6,12 +6,12 @@ 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.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 ffx.model.media_tag import MediaTag +# from ffx.model.track_tag import TrackTag from .shows_screen import ShowsScreen from .warning_screen import WarningScreen @@ -35,29 +35,6 @@ class FfxApp(App): # Data 'input' variable self.context = context - # Initialize DB - homeDir = os.path.expanduser("~") - ffxVarDir = os.path.join(homeDir, '.local', 'var', 'ffx') - if not os.path.exists(ffxVarDir): - os.makedirs(ffxVarDir) - - self.context['database_url'] = f"sqlite:///{os.path.join(ffxVarDir, 'ffx.db')}" - self.context['database_engine'] = create_engine(self.context['database_url']) - self.context['database_session'] = sessionmaker(bind=self.context['database_engine']) - - Base.metadata.create_all(self.context['database_engine']) - - # isSyncronuous = False - # while not isSyncronuous: - # while True: - # try: - # with self.context['database_engine'].connect() as connection: - # connection.execute(sqlalchemy.text('PRAGMA foreign_keys=ON;')) - # #isSyncronuous = True - # break - # except sqlite3.OperationalError: - # time.sleep(0.1) - def on_mount(self) -> None: self.push_screen(ShowsScreen()) diff --git a/bin/ffx/file_properties.py b/bin/ffx/file_properties.py index 65e6676..afeee20 100644 --- a/bin/ffx/file_properties.py +++ b/bin/ffx/file_properties.py @@ -1,6 +1,7 @@ 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 @@ -18,7 +19,7 @@ class FileProperties(): EPISODE_INDICATOR_MATCH = '[eE]([0-9]+)' - def __init__(self, sourcePath): + def __init__(self, context, sourcePath): # Separate basedir, basename and extension for current source file self.__sourcePath = sourcePath @@ -55,7 +56,10 @@ class FileProperties(): file_index += 1 -# + pc = PatternController(context) + pattern = pc.matchFilename(self.__sourceFilename) + + click.echo(pattern) # matchingFileSubtitleDescriptors = sorted([d for d in availableFileSubtitleDescriptors if d['season'] == season and d['episode'] == episode], key=lambda d: d['stream']) if availableFileSubtitleDescriptors else [] # # print(f"season={season} episode={episode} file={file_index}") diff --git a/bin/ffx/model/pattern.py b/bin/ffx/model/pattern.py index dc1deb1..88087d9 100644 --- a/bin/ffx/model/pattern.py +++ b/bin/ffx/model/pattern.py @@ -32,18 +32,25 @@ class Pattern(Base): media_tags = relationship('MediaTag', back_populates='pattern', cascade="all, delete") - def getDescriptor(self): - - descriptor = {} - descriptor['id'] = int(self.id) - descriptor['pattern'] = str(self.pattern) - descriptor['show_id'] = int(self.show_id) - - descriptor['tags'] = {} - for t in self.media_tags: - descriptor['tags'][str(t.key)] = str(t.value) - - return descriptor + # def getDescriptor(self): +# + # descriptor = {} + # descriptor['id'] = int(self.id) + # descriptor['pattern'] = str(self.pattern) + # descriptor['show_id'] = int(self.show_id) +# + # descriptor['tags'] = {} + # for t in self.media_tags: + # descriptor['tags'][str(t.key)] = str(t.value) +# + # return descriptor + + + def getShow(self): + pass + + def getTracks(self): + pass def getMediaDescriptor(self): diff --git a/bin/ffx/model/track.py b/bin/ffx/model/track.py index 9be45eb..7542f0e 100644 --- a/bin/ffx/model/track.py +++ b/bin/ffx/model/track.py @@ -9,6 +9,7 @@ from ffx.track_type import TrackType from ffx.iso_language import IsoLanguage from ffx.track_disposition import TrackDisposition +from ffx.track_descriptor import TrackDescriptor class Track(Base): @@ -28,6 +29,8 @@ class Track(Base): # P=pattern_id+sub_index+track_type track_type = Column(Integer) # TrackType + + index = Column(Integer) sub_index = Column(Integer) # v1.x @@ -39,7 +42,7 @@ class Track(Base): # title = Column(String) - track_tags = relationship('TrackTag', back_populates='track', cascade="all, delete") + track_tags = relationship('TrackTag', back_populates='track', cascade="all, delete", lazy="joined") disposition_flags = Column(Integer) @@ -49,15 +52,14 @@ class Track(Base): trackType = kwargs.pop('track_type', None) if trackType is not None: - self.track_type = int(trackType.value) + self.track_type = int(trackType) # language = kwargs.pop('language', None) # if language is not None: # self.language = str(language.threeLetter()) - dispositionList = kwargs.pop('disposition_flags', None) - if dispositionList is not None: - self.disposition_flags = int(TrackDisposition.toFlags(dispositionList)) + dispositionSet = kwargs.pop(TrackDescriptor.DISPOSITION_SET_KEY, set()) + self.disposition_flags = int(TrackDisposition.toFlags(dispositionSet)) super().__init__(**kwargs) @@ -177,14 +179,14 @@ class Track(Base): 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.UNKNOWN + return IsoLanguage.findThreeLetter(tags['language']) if 'language' in tags.keys() else IsoLanguage.UNDEFINED def getTitle(self): tags = {t.key:t.value for t in self.track_tags} return tags['title'] if 'title' in tags.keys() else '' - def getDispositionList(self): - return TrackDisposition.toList(self.disposition_flags) + def getDispositionSet(self): + return TrackDisposition.toSet(self.disposition_flags) def getTags(self): return {str(k.value):str(v.value) for (k,v) in self.track_tags} diff --git a/bin/ffx/pattern_controller.py b/bin/ffx/pattern_controller.py index 6b48380..77cbe0f 100644 --- a/bin/ffx/pattern_controller.py +++ b/bin/ffx/pattern_controller.py @@ -10,7 +10,7 @@ class PatternController(): def __init__(self, context): self.context = context - self.Session = self.context['database_session'] # convenience + self.Session = self.context['database']['session'] # convenience def addPattern(self, patternDescriptor): @@ -79,19 +79,21 @@ class PatternController(): s.close() - def getPatternDescriptor(self, patternId): + def getPattern(self, patternId): try: s = self.Session() q = s.query(Pattern).filter(Pattern.id == int(patternId)) if q.count(): - pattern = q.first() + # pattern = q.first() #return self.getPatternDict(pattern) - return pattern.getDescriptor() + return q.first() + else: + return None except Exception as ex: - raise click.ClickException(f"PatternController.getPatternDescriptor(): {repr(ex)}") + raise click.ClickException(f"PatternController.getPattern(): {repr(ex)}") finally: s.close() @@ -120,37 +122,44 @@ class PatternController(): def matchFilename(self, filename): - SEASON_PATTERN = '[sS]([0-9]+)' - EPISODE_PATTERN = '[eE]([0-9]+)' + #SEASON_PATTERN = '[sS]([0-9]+)' + #EPISODE_PATTERN = '[eE]([0-9]+)' - result = {} + #result = {} try: s = self.Session() q = s.query(Pattern) - for pattern in q.all(): - - match = re.search(pattern.pattern, filename) - - if match: + matchedPatterns = [p for p in q.all() if re.search(p.pattern, filename)] - 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)) + if matchedPatterns: + return matchedPatterns[0] + else: + return None - episodeMatch = re.search(EPISODE_PATTERN, result['indicator']) - if episodeMatch: - result['episode'] = int(episodeMatch.group(1)) +# 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.matchFilename(): {repr(ex)}") + raise click.ClickException(f"PatternController.findPattern(): {repr(ex)}") finally: s.close() diff --git a/bin/ffx/pattern_delete_screen.py b/bin/ffx/pattern_delete_screen.py index 8b47aa1..1508d5a 100644 --- a/bin/ffx/pattern_delete_screen.py +++ b/bin/ffx/pattern_delete_screen.py @@ -50,7 +50,7 @@ class PatternDeleteScreen(Screen): super().__init__() self.context = self.app.getContext() - self.Session = self.context['database_session'] # convenience + self.Session = self.context['database']['session'] # convenience self.__pc = PatternController(context = self.context) self.__sc = ShowController(context = self.context) diff --git a/bin/ffx/pattern_details_screen.py b/bin/ffx/pattern_details_screen.py index 14beba9..018fc38 100644 --- a/bin/ffx/pattern_details_screen.py +++ b/bin/ffx/pattern_details_screen.py @@ -73,13 +73,13 @@ class PatternDetailsScreen(Screen): super().__init__() self.context = self.app.getContext() - self.Session = self.context['database_session'] # convenience + 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.pattern_obj = self.__pc.getPatternDescriptor(patternId) if patternId is not None else {} + self.__pattern = self.__pc.getPattern(patternId) self.show_obj = self.__sc.getShowDesciptor(showId) if showId is not None else {} @@ -98,7 +98,7 @@ class PatternDetailsScreen(Screen): return [{'id': int(p.id), 'pattern': p.pattern} for p in q.all()] except Exception as ex: - click.ClickException(f"loadPatterns(): {repr(ex)}") + raise click.ClickException(f"loadTracks(): {repr(ex)}") finally: s.close() @@ -107,19 +107,18 @@ class PatternDetailsScreen(Screen): self.audioStreamsTable.clear() - trackIds = self.__tc.findAllTracks(self.pattern_obj['id']) + audioTracks = self.__tc.findAudioTracks(self.__pattern.getId()) - for audioTrackId in trackIds[TrackType.AUDIO.label()]: + for at in audioTracks: - ad = self.__tc.getTrackDescriptor(audioTrackId) - dispoList = ad['disposition_list'] + dispoSet = at.getDispositionSet() - row = (ad['sub_index'], + row = (at.getSubIndex(), " ", - ad['language'].label(), - ad['title'], - 'Yes' if TrackDisposition.DEFAULT in dispoList else 'No', - 'Yes' if TrackDisposition.FORCED in dispoList else 'No') + 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)) @@ -127,33 +126,30 @@ class PatternDetailsScreen(Screen): self.subtitleStreamsTable.clear() - trackIds = self.__tc.findAllTracks(self.pattern_obj['id']) + subtitleTracks = self.__tc.findSubtitleTracks(self.__pattern.getId()) - for subtitleTrackId in trackIds[TrackType.SUBTITLE.label()]: + for st in subtitleTracks: - sd = self.__tc.getTrackDescriptor(subtitleTrackId) - dispoList = sd['disposition_list'] + dispoSet = st.getDispositionSet() - row = (sd['sub_index'], + row = (st.getSubIndex(), " ", - sd['language'].label(), - sd['title'], - 'Yes' if TrackDisposition.DEFAULT in dispoList else 'No', - 'Yes' if TrackDisposition.FORCED in dispoList else 'No') + st.getLanguage().label(), + st.getTitle(), + 'Yes' if TrackDisposition.DEFAULT in dispoSet else 'No', + 'Yes' if TrackDisposition.FORCED in dispoSet else 'No') self.subtitleStreamsTable.add_row(*map(str, row)) def on_mount(self): - if self.pattern_obj: - self.query_one("#pattern_input", Input).value = str(self.pattern_obj['pattern']) - if self.show_obj: self.query_one("#showlabel", Static).update(f"{self.show_obj['id']} - {self.show_obj['name']} ({self.show_obj['year']})") - - if self.pattern_obj: + if self.__pattern is not None: + + self.query_one("#pattern_input", Input).value = str(self.__pattern.getPattern()) self.updateAudioTracks() self.updateSubtitleTracks() @@ -192,7 +188,7 @@ class PatternDetailsScreen(Screen): with Grid(): # 1 - yield Static("Edit filename pattern" if self.pattern_obj else "New filename pattern", id="toplabel") + yield Static("Edit filename pattern" if self.__pattern is not None else "New filename pattern", id="toplabel") yield Input(type="text", id="pattern_input", classes="four") # 2 @@ -209,7 +205,7 @@ class PatternDetailsScreen(Screen): yield Static("Audio streams") yield Static(" ") - if self.pattern_obj: + 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") @@ -227,7 +223,7 @@ class PatternDetailsScreen(Screen): yield Static("Subtitle streams") yield Static(" ") - if self.pattern_obj: + 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") @@ -253,9 +249,9 @@ class PatternDetailsScreen(Screen): - def getSelectedAudioTrackId(self): + def getSelectedAudioTrack(self): - if not self.pattern_obj: + if not self.__pattern: return None try: @@ -269,7 +265,7 @@ class PatternDetailsScreen(Screen): subIndex = int(selected_track_data[0]) - return self.__tc.findTrack(self.pattern_obj['id'], TrackType.AUDIO, subIndex) + return self.__tc.findTrack(self.__pattern.getId(), TrackType.AUDIO, subIndex) else: return None @@ -278,9 +274,9 @@ class PatternDetailsScreen(Screen): return None - def getSelectedSubtitleTrackId(self): + def getSelectedSubtitleTrack(self): - if not self.pattern_obj: + if not self.__pattern is None: return None try: @@ -294,7 +290,7 @@ class PatternDetailsScreen(Screen): subIndex = int(selected_track_data[0]) - return self.__tc.findTrack(self.pattern_obj['id'], TrackType.SUBTITLE, subIndex) + return self.__tc.findTrack(self.__pattern.getId(), TrackType.SUBTITLE, subIndex) else: return None @@ -313,9 +309,9 @@ class PatternDetailsScreen(Screen): patternDescriptor['show_id'] = self.show_obj['id'] patternDescriptor['pattern'] = self.getPatternFromInput() - if self.pattern_obj: + if self.__pattern is not None: - if self.__pc.updatePattern(self.pattern_obj['id'], patternDescriptor): + if self.__pc.updatePattern(self.__pattern.getId(), patternDescriptor): self.dismiss(patternDescriptor) else: #TODO: Meldung @@ -335,29 +331,29 @@ class PatternDetailsScreen(Screen): # Save pattern when just created before adding streams - if self.pattern_obj: + if self.__pattern is not None: #self.pattern_obj if event.button.id == "button_add_audio_stream": - self.app.push_screen(TrackDetailsScreen(trackType = TrackType.AUDIO, patternId = self.pattern_obj['id'], subIndex = len(self.audioStreamsTable.rows)), self.handle_add_track) + self.app.push_screen(TrackDetailsScreen(trackType = TrackType.AUDIO, patternId = self.__pattern.getId(), subIndex = len(self.audioStreamsTable.rows)), self.handle_add_track) - selectedAudioTrackId = self.getSelectedAudioTrackId() - if selectedAudioTrackId is not None: + selectedAudioTrack = self.getSelectedAudioTrack() + if selectedAudioTrack is not None: if event.button.id == "button_edit_audio_stream": - self.app.push_screen(TrackDetailsScreen(trackId = selectedAudioTrackId), self.handle_edit_track) + self.app.push_screen(TrackDetailsScreen(track = selectedAudioTrack), self.handle_edit_track) if event.button.id == "button_delete_audio_stream": - self.app.push_screen(TrackDeleteScreen(trackId = selectedAudioTrackId), self.handle_delete_track) + self.app.push_screen(TrackDeleteScreen(trackI= selectedAudioTrack), self.handle_delete_track) if event.button.id == "button_add_subtitle_stream": - self.app.push_screen(TrackDetailsScreen(trackType = TrackType.SUBTITLE, patternId = self.pattern_obj['id'], subIndex = len(self.subtitleStreamsTable.rows)), self.handle_add_track) + self.app.push_screen(TrackDetailsScreen(trackType = TrackType.SUBTITLE, patternId = self.__pattern.getId(), subIndex = len(self.subtitleStreamsTable.rows)), self.handle_add_track) - selectedSubtitleTrackId = self.getSelectedSubtitleTrackId() - if selectedSubtitleTrackId is not None: + selectedSubtitleTrack = self.getSelectedSubtitleTrack() + if selectedSubtitleTrack is not None: if event.button.id == "button_edit_subtitle_stream": - self.app.push_screen(TrackDetailsScreen(trackId = selectedSubtitleTrackId), self.handle_edit_track) + self.app.push_screen(TrackDetailsScreen(track = selectedSubtitleTrack), self.handle_edit_track) if event.button.id == "button_delete_subtitle_stream": - self.app.push_screen(TrackDeleteScreen(trackId = selectedSubtitleTrackId), self.handle_delete_track) + self.app.push_screen(TrackDeleteScreen(track = selectedSubtitleTrack), self.handle_delete_track) if event.button.id == "patternbutton": @@ -373,27 +369,31 @@ class PatternDetailsScreen(Screen): def handle_add_track(self, trackDescriptor): - dispoList = trackDescriptor['disposition_list'] - - if trackDescriptor['type'] == TrackType.AUDIO: + dispoSet = trackDescriptor.getDispositionSet() + trackType = trackDescriptor.getType() + subIndex = trackDescriptor.getSubIndex() + language = trackDescriptor.getLanguage() + title = trackDescriptor.getTitle() + + if trackType == TrackType.AUDIO: - row = (trackDescriptor['sub_index'], + row = (subIndex, " ", - trackDescriptor['language'].label(), - trackDescriptor['title'], - 'Yes' if TrackDisposition.DEFAULT in dispoList else 'No', - 'Yes' if TrackDisposition.FORCED in dispoList else 'No') + 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 trackDescriptor['type'] == TrackType.SUBTITLE: + if trackType == TrackType.SUBTITLE: - row = (trackDescriptor['sub_index'], + row = (subIndex, " ", - trackDescriptor['language'].label(), - trackDescriptor['title'], - 'Yes' if TrackDisposition.DEFAULT in dispoList else 'No', - 'Yes' if TrackDisposition.FORCED in dispoList else 'No') + 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)) diff --git a/bin/ffx/show_controller.py b/bin/ffx/show_controller.py index 5de5a84..1c213d7 100644 --- a/bin/ffx/show_controller.py +++ b/bin/ffx/show_controller.py @@ -8,7 +8,7 @@ class ShowController(): def __init__(self, context): self.context = context - self.Session = self.context['database_session'] # convenience + self.Session = self.context['database']['session'] # convenience def getShowDesciptor(self, showId): diff --git a/bin/ffx/show_delete_screen.py b/bin/ffx/show_delete_screen.py index af1579c..d85a74b 100644 --- a/bin/ffx/show_delete_screen.py +++ b/bin/ffx/show_delete_screen.py @@ -48,7 +48,7 @@ class ShowDeleteScreen(Screen): super().__init__() self.context = self.app.getContext() - self.Session = self.context['database_session'] # convenience + self.Session = self.context['database']['session'] # convenience self.__sc = ShowController(context = self.context) self.show_obj = self.__sc.getShowDesciptor(showId) if showId is not None else {} diff --git a/bin/ffx/show_details_screen.py b/bin/ffx/show_details_screen.py index 6afd855..00139c8 100644 --- a/bin/ffx/show_details_screen.py +++ b/bin/ffx/show_details_screen.py @@ -76,7 +76,7 @@ class ShowDetailsScreen(Screen): super().__init__() self.context = self.app.getContext() - self.Session = self.context['database_session'] # convenience + self.Session = self.context['database']['session'] # convenience self.__sc = ShowController(context = self.context) self.__pc = PatternController(context = self.context) diff --git a/bin/ffx/shows_screen.py b/bin/ffx/shows_screen.py index 00e160c..d6ecc39 100644 --- a/bin/ffx/shows_screen.py +++ b/bin/ffx/shows_screen.py @@ -57,7 +57,7 @@ class ShowsScreen(Screen): self.context = self.app.getContext() - self.Session = self.context['database_session'] # convenience + self.Session = self.context['database']['session'] # convenience diff --git a/bin/ffx/tag_controller.py b/bin/ffx/tag_controller.py new file mode 100644 index 0000000..527476e --- /dev/null +++ b/bin/ffx/tag_controller.py @@ -0,0 +1,193 @@ +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 + + +class TagController(): + + def __init__(self, context): + + self.context = context + self.Session = self.context['database']['session'] # convenience + + + def addMediaTag(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 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 + + else: + return False + + except Exception as ex: + raise click.ClickException(f"TrackController.addTrack(): {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 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)) + + 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() + else: + return {} + + except Exception as ex: + raise click.ClickException(f"TrackController.getTrackDescriptor(): {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(): + + trackDescriptor = self.getTrackDict(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.commit() + return True + + + return False + + + except Exception as ex: + raise click.ClickException(f"TrackController.deleteTrack(): {repr(ex)}") + finally: + s.close() diff --git a/bin/ffx/track_controller.py b/bin/ffx/track_controller.py index 846c58d..37e918f 100644 --- a/bin/ffx/track_controller.py +++ b/bin/ffx/track_controller.py @@ -9,13 +9,15 @@ from .iso_language import IsoLanguage from .track_type import TrackType +from ffx.model.track_tag import TrackTag + class TrackController(): def __init__(self, context): self.context = context - self.Session = self.context['database_session'] # convenience + self.Session = self.context['database']['session'] # convenience def addTrack(self, trackDescriptor): @@ -23,19 +25,20 @@ class TrackController(): 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()), + track = Track(pattern_id = int(trackDescriptor.getPatternId()), + track_type = int(trackDescriptor.getType().index()), + sub_index = int(trackDescriptor.getSubIndex()), + disposition_flags = int(TrackDisposition.toFlags(trackDescriptor.getDispositionSet()))) - title = str(trackDescriptor['title']), + s.add(track) + s.commit() - disposition_flags = int(TrackDisposition.toFlags(trackDescriptor['disposition_list']))) + for k,v in trackDescriptor.getTags().items(): - s.add(track) + tag = TrackTag(track_id = track.id, + key = k, + value = v) + s.add(tag) s.commit() except Exception as ex: @@ -55,9 +58,9 @@ class TrackController(): 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'])) + # track.language = str(trackDescriptor['language'].threeLetter()) + # track.title = str(trackDescriptor['title']) + track.disposition_flags = int(TrackDisposition.toFlags(trackDescriptor.getDispositionSet())) s.commit() @@ -71,31 +74,54 @@ class TrackController(): finally: s.close() - - def findAllTracks(self, patternId): +# +# 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: 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.value) - - 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.value) - for subtitleTrack in q_subtitle.all(): - trackDescriptors[TrackType.SUBTITLE.label()].append(subtitleTrack.id) + q = s.query(Track).filter(Track.pattern_id == int(patternId), Track.track_type == TrackType.AUDIO.index()) + return [a for a in q.all()] + except Exception as ex: + raise click.ClickException(f"TrackController.findAudioTracks(): {repr(ex)}") + finally: + s.close() - return trackDescriptors + def findSubtitleTracks(self, patternId): + try: + s = self.Session() + + q = s.query(Track).filter(Track.pattern_id == int(patternId), Track.track_type == TrackType.SUBTITLE.index()) + return [s for s in q.all()] except Exception as ex: - raise click.ClickException(f"TrackController.findAllTracks(): {repr(ex)}") + raise click.ClickException(f"TrackController.findSubtitleTracks(): {repr(ex)}") finally: s.close() @@ -107,8 +133,9 @@ class TrackController(): q = s.query(Track).filter(Track.pattern_id == int(patternId), Track.track_type == trackType.value, Track.sub_index == int(subIndex)) if q.count(): - track = q.first() - return int(track.id) + #track = q.first() + #return int(track.id) + return q.first() else: return None @@ -118,23 +145,23 @@ class TrackController(): 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() - else: - return {} - - except Exception as ex: - raise click.ClickException(f"TrackController.getTrackDescriptor(): {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() +# else: +# return {} +# +# except Exception as ex: +# raise click.ClickException(f"TrackController.getTrackDescriptor(): {repr(ex)}") +# finally: +# s.close() def deleteTrack(self, trackId): diff --git a/bin/ffx/track_delete_screen.py b/bin/ffx/track_delete_screen.py index 24c9d11..988ebb7 100644 --- a/bin/ffx/track_delete_screen.py +++ b/bin/ffx/track_delete_screen.py @@ -58,7 +58,7 @@ class TrackDeleteScreen(Screen): super().__init__() self.context = self.app.getContext() - self.Session = self.context['database_session'] # convenience + self.Session = self.context['database']['session'] # convenience if trackId is None: raise click.ClickException('TrackDeleteScreen.init(): trackId is required to be set') diff --git a/bin/ffx/track_descriptor.py b/bin/ffx/track_descriptor.py index c692d71..14e0467 100644 --- a/bin/ffx/track_descriptor.py +++ b/bin/ffx/track_descriptor.py @@ -6,20 +6,72 @@ from .track_disposition import TrackDisposition class TrackDescriptor(): + 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' + FFPROBE_DISPOSITION_KEY = 'disposition' FFPROBE_TAGS_KEY = 'tags' def __init__(self, **kwargs): - # self.__index = int(kwargs['index']) if 'index' in kwargs.keys() else -1 - # self.__subIndex = int(kwargs['sub_index']) if 'sub_index' in kwargs.keys() else -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") + self.__patternId = kwargs[TrackDescriptor.PATTERN_ID_KEY] + else: + self.__patternId = -1 + + if TrackDescriptor.INDEX_KEY in kwargs.keys(): + if type(kwargs[TrackDescriptor.INDEX_KEY]) is not int: + raise TypeError(f"TrackDesciptor.__init__(): Argument {TrackDescriptor.INDEX_KEY} is required to be of type int") + self.__index = kwargs[TrackDescriptor.INDEX_KEY] + else: + self.__index = -1 - self.__trackType = kwargs['trackType'] if 'trackType' in kwargs.keys() else TrackType.UNKNOWN + 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") + 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") + self.__trackType = kwargs[TrackDescriptor.TRACK_TYPE_KEY] + else: + self.__trackType = TrackType.UNKNOWN - self.__trackTags = kwargs['tags'] if 'tags' in kwargs.keys() else {} - self.__dispositionSet = kwargs['dispositionSet'] if 'dispositionSet' in kwargs.keys() else set() + if TrackDescriptor.TAGS_KEY in kwargs.keys(): + if type(kwargs[TrackDescriptor.TAGS_KEY]) is not dict: + raise TypeError(f"TrackDesciptor.__init__(): Argument {TrackDescriptor.TAGS_KEY} is required to be of type dict") + self.__trackTags = kwargs[TrackDescriptor.TAGS_KEY] + else: + self.__trackTags = {} + + if TrackDescriptor.DISPOSITION_SET_KEY in kwargs.keys(): + if type(kwargs[TrackDescriptor.DISPOSITION_SET_KEY]) is not set: + raise TypeError(f"TrackDesciptor.__init__(): Argument {TrackDescriptor.DISPOSITION_SET_KEY} is required to be of type set") + for d in kwargs[TrackDescriptor.DISPOSITION_SET_KEY]: + if type(d) is not TrackDisposition: + raise TypeError(f"TrackDesciptor.__init__(): All elements of argument set {TrackDescriptor.DISPOSITION_SET_KEY} is required to be of type TrackDisposition") + self.__dispositionSet = kwargs[TrackDescriptor.DISPOSITION_SET_KEY] + else: + self.__dispositionSet = set() - self.__audioLayout = kwargs['audioLayout'] if self.__trackType == TrackType.AUDIO and 'audioLayout' in kwargs.keys() else AudioLayout.LAYOUT_UNDEFINED + if TrackDescriptor.AUDIO_LAYOUT_KEY in kwargs.keys(): + if type(kwargs[TrackDescriptor.AUDIO_LAYOUT_KEY]) is not AudioLayout: + raise TypeError(f"TrackDesciptor.__init__(): Argument {TrackDescriptor.AUDIO_LAYOUT_KEY} is required to be of type AudioLayout") + self.__audioLayout = kwargs[TrackDescriptor.AUDIO_LAYOUT_KEY] + else: + self.__audioLayout = AudioLayout.LAYOUT_UNDEFINED @classmethod @@ -70,14 +122,31 @@ class TrackDescriptor(): if trackType != TrackType.UNKNOWN: - return cls(trackType = trackType, - dispositionSet = {t for d in (k for (k,v) in streamObj[TrackDescriptor.FFPROBE_DISPOSITION_KEY].items() if v) if (t := TrackDisposition.find(d)) if t is not None} if TrackDescriptor.FFPROBE_DISPOSITION_KEY in streamObj.keys() else set(), - tags = streamObj[TrackDescriptor.FFPROBE_TAGS_KEY] if TrackDescriptor.FFPROBE_TAGS_KEY in streamObj.keys() else {}, - audioLayout = AudioLayout.identify(streamObj) if trackType == TrackType.AUDIO.label() else AudioLayout.LAYOUT_UNDEFINED) + kwargs = {} + kwargs[TrackDescriptor.TRACK_TYPE_KEY] = trackType + kwargs[TrackDescriptor.DISPOSITION_SET_KEY] = {t for d in (k for (k,v) in streamObj[TrackDescriptor.FFPROBE_DISPOSITION_KEY].items() if v) + if (t := TrackDisposition.find(d)) if t is not None} if TrackDescriptor.FFPROBE_DISPOSITION_KEY in streamObj.keys() else set() + kwargs[TrackDescriptor.TAGS_KEY] = streamObj[TrackDescriptor.FFPROBE_TAGS_KEY] if TrackDescriptor.FFPROBE_TAGS_KEY in streamObj.keys() else {} + kwargs[TrackDescriptor.AUDIO_LAYOUT_KEY] = AudioLayout.identify(streamObj) if trackType == TrackType.AUDIO.label() else AudioLayout.LAYOUT_UNDEFINED + + return cls(**kwargs) else: return None + def getPatternId(self): + return self.__patternId + + def getIndex(self): + return self.__index + + def getSubIndex(self): + return self.__subIndex + + + def getType(self): + return self.__trackType + def getLanguage(self): if 'language' in self.__trackTags.keys(): return IsoLanguage.findThreeLetter(self.__trackTags['language']) @@ -95,4 +164,7 @@ class TrackDescriptor(): def getTags(self): - return self.__trackTags \ No newline at end of file + return self.__trackTags + + def getDispositionSet(self): + return self.__dispositionSet diff --git a/bin/ffx/track_details_screen.py b/bin/ffx/track_details_screen.py index 45149b2..8917a1f 100644 --- a/bin/ffx/track_details_screen.py +++ b/bin/ffx/track_details_screen.py @@ -18,6 +18,9 @@ from .track_type import TrackType from .iso_language import IsoLanguage from .track_disposition import TrackDisposition +from .audio_layout import AudioLayout + +from .track_descriptor import TrackDescriptor # Screen[dict[int, str, int]] @@ -75,58 +78,94 @@ class TrackDetailsScreen(Screen): } """ - def __init__(self, trackId = None, patternId = None, trackType : TrackType = None, subIndex = None): + def __init__(self, trackDescriptor = None, patternId = None, trackType : TrackType = None, subIndex = None): super().__init__() self.context = self.app.getContext() - self.Session = self.context['database_session'] # convenience + self.Session = self.context['database']['session'] # convenience self.__tc = TrackController(context = self.context) self.__pc = PatternController(context = self.context) - self.track_obj = self.__tc.getTrackDescriptor(trackId) if trackId is not None else {} - - if self.track_obj: - self.trackType = self.track_obj['type'] - self.subIndex = self.track_obj['sub_index'] - self.pattern_obj = self.__pc.getPatternDescriptor(self.track_obj['pattern_id']) - self.track_obj['is_new'] = False + + 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.__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 {} else: - self.trackType = trackType - self.subIndex = subIndex - self.pattern_obj = self.__pc.getPatternDescriptor(patternId) if patternId is not None else {} - self.track_obj['is_new'] = True - - if self.trackType is None: - raise click.ClickException('Track type is required to be set') - if self.subIndex is None: - raise click.ClickException('Sub index for track is required to be set') + self.__trackType = trackDescriptor.getType() + self.__subIndex = trackDescriptor.getSubIndex() + self.__trackDescriptor = trackDescriptor + self.__pattern = self.__pc.getPattern(self.__trackDescriptor.getPatternId()) + + # self.__trackDescriptor = trackDescriptor + # self.__trackType = 0 + # self.__subIndex = 0 + # self.__patternDescriptor = 0 + +# if self.track_obj: +# self.trackType = self.track_obj['type'] +# self.subIndex = self.track_obj['sub_index'] +# self.pattern_obj = self.__pc.getPatternDescriptor(self.track_obj['pattern_id']) +# self.track_obj['is_new'] = False +# else: +# self.trackType = trackType +# self.subIndex = subIndex +# self.pattern_obj = self.__pc.getPatternDescriptor(patternId) if patternId is not None else {} +# self.track_obj['is_new'] = True + +# if self.trackType is None: +# raise click.ClickException('Track type is required to be set') +# if self.subIndex is None: +# raise click.ClickException('Sub index for track is required to be set') def on_mount(self): - if self.pattern_obj: - self.query_one("#patternlabel", Static).update(str(self.pattern_obj['pattern'])) + 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)) + if self.__subIndex is not None: + self.query_one("#subindexlabel", Static).update(str(self.__subIndex)) - for d in TrackDisposition: + if self.__trackDescriptor is not None: - dispositionIsSet = (self.track_obj - and 'disposition_list' in self.track_obj.keys() - and d in self.track_obj['disposition_list']) + for d in TrackDisposition: - disposition = (d.label(), d.index(), dispositionIsSet) - self.query_one("#dispositions_selection_list", SelectionList).add_option(disposition) + dispositionIsSet = (self.__trackDescriptor is not None + and d in self.__trackDescriptor.getDispositionSet()) - if 'language' in self.track_obj.keys(): - self.query_one("#language_select", Select).value = self.track_obj['language'].label() + dispositionOption = (d.label(), d.index(), dispositionIsSet) + self.query_one("#dispositions_selection_list", SelectionList).add_option(dispositionOption) - if 'title' in self.track_obj.keys(): - self.query_one("#title_input", Input).value = str(self.track_obj['title']) + self.query_one("#language_select", Select).value = self.__trackDescriptor.getLanguage().label() + self.query_one("#title_input", Input).value = self.__trackDescriptor.getTitle() + +# if 'language' in self.track_obj.keys(): +# self.query_one("#language_select", Select).value = self.track_obj['language'].label() +# +# if 'title' in self.track_obj.keys(): +# self.query_one("#title_input", Input).value = str(self.track_obj['title']) @@ -148,7 +187,7 @@ class TrackDetailsScreen(Screen): with Grid(): # 1 - yield Static(f"New {self.trackType.label()} stream" if self.track_obj['is_new'] else f"Edit {self.trackType.label()} stream", id="toplabel", classes="five") + yield Static(f"New {self.__trackType.label()} stream" if self.__isNew else f"Edit {self.__trackType.label()} stream", id="toplabel", classes="five") # 2 yield Static("for pattern") @@ -220,50 +259,52 @@ class TrackDetailsScreen(Screen): def getTrackDescriptorFromInput(self): - trackDescriptor = {} - - trackDescriptor['pattern_id'] = int(self.pattern_obj['id']) - - trackDescriptor['type'] = TrackType(self.trackType) - trackDescriptor['sub_index'] = self.subIndex + kwargs = {} - trackDescriptor['language'] = IsoLanguage.find(str(self.query_one("#language_select", Select).value)) - trackDescriptor['title'] = str(self.query_one("#title_input", Input).value) + kwargs[TrackDescriptor.PATTERN_ID_KEY] = int(self.__pattern.getId()) + kwargs[TrackDescriptor.INDEX_KEY] = -1 + kwargs[TrackDescriptor.SUB_INDEX_KEY] = self.__subIndex - disposition_flags = sum([2**f for f in self.query_one("#dispositions_selection_list", SelectionList).selected]) + kwargs[TrackDescriptor.TRACK_TYPE_KEY] = self.__trackType + kwargs[TrackDescriptor.TAGS_KEY] = {} - trackDescriptor['disposition_list'] = TrackDisposition.toList(disposition_flags) + dispositionFlags = sum([2**f for f in self.query_one("#dispositions_selection_list", SelectionList).selected]) + kwargs[TrackDescriptor.DISPOSITION_SET_KEY] = TrackDisposition.toSet(dispositionFlags) + kwargs[TrackDescriptor.AUDIO_LAYOUT_KEY] = AudioLayout.LAYOUT_UNDEFINED - return trackDescriptor + return TrackDescriptor(**kwargs) # 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": trackDescriptor = self.getTrackDescriptorFromInput() - # Check for multiple default/forced disposition flags - trackIdList = self.__tc.findAllTracks(self.pattern_obj['id'])[self.trackType.label()] - - descriptorList = [d for d in (self.__tc.getTrackDescriptor(t) for t in trackIdList) - if d['type'] == self.trackType - and d['sub_index'] != self.subIndex] - + if self.__trackType == TrackType.AUDIO: + trackList = self.__tc.findAudioTracks(self.__pattern.getId()) + elif self.__trackType == TrackType.SUBTITLE: + trackList = self.__tc.findSubtitleTracks(self.__pattern.getId()) + else: + trackList = [] - numDefaultTracks = [d for d in descriptorList if TrackDisposition.DEFAULT in d['disposition_list']] - numForcedTracks = [d for d in descriptorList if TrackDisposition.FORCED in d['disposition_list']] + # descriptorList = [d for d in (self.__tc.getTrackDescriptor(t) for t in trackList) + # if d['type'] == self.__trackType + # and d['sub_index'] != self.__subIndex] - doubleDefaultOrForced = ((TrackDisposition.DEFAULT in trackDescriptor['disposition_list'] and numDefaultTracks) - or (TrackDisposition.FORCED in trackDescriptor['disposition_list'] and numForcedTracks)) + siblingTrackList = [t for t in trackList if t.getType() == self.__trackType and t.getSubIndex() != self.__subIndex] + 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()]) - if doubleDefaultOrForced: + if ((TrackDisposition.DEFAULT in trackDescriptor.getDispositionSet() and numDefaultTracks) + or (TrackDisposition.FORCED in trackDescriptor.getDispositionSet() and numForcedTracks)): self.query_one("#messagestatic", Static).update("Cannot add another stream with disposition flag 'debug' or 'forced' set") @@ -271,15 +312,15 @@ class TrackDetailsScreen(Screen): self.query_one("#messagestatic", Static).update(" ") - if self.track_obj['is_new']: + if self.__isNew: self.__tc.addTrack(trackDescriptor) self.dismiss(trackDescriptor) else: - trackId = self.__tc.findTrack(self.pattern_obj['id'], self.trackType, self.subIndex) - + trackId = self.__tc.findTrack(self.__pattern.getId(), self.__trackType, self.__subIndex) + if self.__tc.updateTrack(trackId, trackDescriptor): self.dismiss(trackDescriptor) diff --git a/bin/ffx/track_disposition.py b/bin/ffx/track_disposition.py index 0865503..42ea02f 100644 --- a/bin/ffx/track_disposition.py +++ b/bin/ffx/track_disposition.py @@ -1,5 +1,7 @@ +import difflib, click + from enum import Enum -import difflib + class TrackDisposition(Enum): @@ -23,6 +25,7 @@ class TrackDisposition(Enum): DEPENDENT = {"name": "dependent", "index": 16} STILL_IMAGE = {"name": "still_image", "index": 17} + def label(self): return str(self.value['name']) @@ -31,22 +34,27 @@ class TrackDisposition(Enum): @staticmethod - def toFlags(dispositionList): + def toFlags(dispositionSet): """Flags stored in integer bits (2**index)""" + if type(dispositionSet) is not set: + raise click.ClickException('TrackDisposition.toFlags(): Argument is not of type set') + flags = 0 - for d in dispositionList: + for d in dispositionSet: + if type(d) is not TrackDisposition: + raise click.ClickException('TrackDisposition.toFlags(): Element not of type TrackDisposition') flags += 2 ** d.index() return flags @staticmethod - def toList(flags): - - dispositionList = [] + def toSet(flags): + dispositionSet = set() for d in TrackDisposition: if flags & int(2 ** d.index()): - dispositionList += [d] - return dispositionList + dispositionSet.add(d) + return dispositionSet + @staticmethod def find(label):