click-textual
Maveno 1 year ago
parent eaee3b34da
commit 1c9f67e47a

@ -4,6 +4,8 @@ import os, sys, subprocess, json, click, time, re
from ffx.ffx_app import FfxApp from ffx.ffx_app import FfxApp
from ffx.database import databaseContext
from ffx.media_descriptor import MediaDescriptor from ffx.media_descriptor import MediaDescriptor
from ffx.file_properties import FileProperties from ffx.file_properties import FileProperties
@ -221,8 +223,9 @@ def searchSubtitleFiles(dir, prefix):
@click.pass_context @click.pass_context
def ffx(ctx): def ffx(ctx):
"""FFX""" """FFX"""
ctx.obj = {} ctx.obj = {}
pass ctx.obj['database'] = databaseContext()
# Define a subcommand # Define a subcommand
@ -239,13 +242,18 @@ def help():
@click.argument('filename', nargs=1)
@ffx.command() @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: try:
fp = FileProperties(filename) fp = FileProperties(ctx.obj, filename)
md = fp.getMediaDescriptor() md = fp.getMediaDescriptor()
print(md.getTags()) print(md.getTags())
@ -315,6 +323,9 @@ def inspect(filename):
def shows(ctx): def shows(ctx):
# if 'database' not in ctx.obj.keys():
# ctx.obj['database'] = databaseContext()
app = FfxApp(ctx.obj) app = FfxApp(ctx.obj)
app.run() app.run()
@ -402,6 +413,10 @@ def convert(ctx,
context = ctx.obj context = ctx.obj
if 'database' not in context.keys():
context['database'] = databaseContext()
click.echo(f"\nVideo encoder: {video_encoder}") click.echo(f"\nVideo encoder: {video_encoder}")
qualityTokens = quality.split(',') qualityTokens = quality.split(',')

@ -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

@ -6,12 +6,12 @@ from textual.widgets import Header, Footer, Placeholder, Label
from sqlalchemy import create_engine from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import sessionmaker
from ffx.model.show import Base, Show # from ffx.model.show import Base, Show
from ffx.model.pattern import Pattern # from ffx.model.pattern import Pattern
from ffx.model.track import Track # from ffx.model.track import Track
from ffx.model.media_tag import MediaTag # from ffx.model.media_tag import MediaTag
from ffx.model.track_tag import TrackTag # from ffx.model.track_tag import TrackTag
from .shows_screen import ShowsScreen from .shows_screen import ShowsScreen
from .warning_screen import WarningScreen from .warning_screen import WarningScreen
@ -35,29 +35,6 @@ class FfxApp(App):
# Data 'input' variable # Data 'input' variable
self.context = context 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: def on_mount(self) -> None:
self.push_screen(ShowsScreen()) self.push_screen(ShowsScreen())

@ -1,6 +1,7 @@
import os, re, click, json import os, re, click, json
from .media_descriptor import MediaDescriptor from .media_descriptor import MediaDescriptor
from .pattern_controller import PatternController
#from .track_type import TrackType #from .track_type import TrackType
#from .audio_layout import AudioLayout #from .audio_layout import AudioLayout
@ -18,7 +19,7 @@ class FileProperties():
EPISODE_INDICATOR_MATCH = '[eE]([0-9]+)' EPISODE_INDICATOR_MATCH = '[eE]([0-9]+)'
def __init__(self, sourcePath): def __init__(self, context, sourcePath):
# Separate basedir, basename and extension for current source file # Separate basedir, basename and extension for current source file
self.__sourcePath = sourcePath self.__sourcePath = sourcePath
@ -55,7 +56,10 @@ class FileProperties():
file_index += 1 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 [] # 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}") # print(f"season={season} episode={episode} file={file_index}")

@ -32,18 +32,25 @@ class Pattern(Base):
media_tags = relationship('MediaTag', back_populates='pattern', cascade="all, delete") media_tags = relationship('MediaTag', back_populates='pattern', cascade="all, delete")
def getDescriptor(self): # def getDescriptor(self):
#
descriptor = {} # descriptor = {}
descriptor['id'] = int(self.id) # descriptor['id'] = int(self.id)
descriptor['pattern'] = str(self.pattern) # descriptor['pattern'] = str(self.pattern)
descriptor['show_id'] = int(self.show_id) # descriptor['show_id'] = int(self.show_id)
#
descriptor['tags'] = {} # descriptor['tags'] = {}
for t in self.media_tags: # for t in self.media_tags:
descriptor['tags'][str(t.key)] = str(t.value) # descriptor['tags'][str(t.key)] = str(t.value)
#
return descriptor # return descriptor
def getShow(self):
pass
def getTracks(self):
pass
def getMediaDescriptor(self): def getMediaDescriptor(self):

@ -9,6 +9,7 @@ from ffx.track_type import TrackType
from ffx.iso_language import IsoLanguage from ffx.iso_language import IsoLanguage
from ffx.track_disposition import TrackDisposition from ffx.track_disposition import TrackDisposition
from ffx.track_descriptor import TrackDescriptor
class Track(Base): class Track(Base):
@ -28,6 +29,8 @@ class Track(Base):
# P=pattern_id+sub_index+track_type # P=pattern_id+sub_index+track_type
track_type = Column(Integer) # TrackType track_type = Column(Integer) # TrackType
index = Column(Integer)
sub_index = Column(Integer) sub_index = Column(Integer)
# v1.x # v1.x
@ -39,7 +42,7 @@ class Track(Base):
# title = Column(String) # 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) disposition_flags = Column(Integer)
@ -49,15 +52,14 @@ class Track(Base):
trackType = kwargs.pop('track_type', None) trackType = kwargs.pop('track_type', None)
if trackType is not None: if trackType is not None:
self.track_type = int(trackType.value) self.track_type = int(trackType)
# language = kwargs.pop('language', None) # language = kwargs.pop('language', None)
# if language is not None: # if language is not None:
# self.language = str(language.threeLetter()) # self.language = str(language.threeLetter())
dispositionList = kwargs.pop('disposition_flags', None) dispositionSet = kwargs.pop(TrackDescriptor.DISPOSITION_SET_KEY, set())
if dispositionList is not None: self.disposition_flags = int(TrackDisposition.toFlags(dispositionSet))
self.disposition_flags = int(TrackDisposition.toFlags(dispositionList))
super().__init__(**kwargs) super().__init__(**kwargs)
@ -177,14 +179,14 @@ class Track(Base):
def getLanguage(self): def getLanguage(self):
tags = {t.key:t.value for t in self.track_tags} 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): def getTitle(self):
tags = {t.key:t.value for t in self.track_tags} tags = {t.key:t.value for t in self.track_tags}
return tags['title'] if 'title' in tags.keys() else '' return tags['title'] if 'title' in tags.keys() else ''
def getDispositionList(self): def getDispositionSet(self):
return TrackDisposition.toList(self.disposition_flags) return TrackDisposition.toSet(self.disposition_flags)
def getTags(self): def getTags(self):
return {str(k.value):str(v.value) for (k,v) in self.track_tags} return {str(k.value):str(v.value) for (k,v) in self.track_tags}

@ -10,7 +10,7 @@ class PatternController():
def __init__(self, context): def __init__(self, context):
self.context = context self.context = context
self.Session = self.context['database_session'] # convenience self.Session = self.context['database']['session'] # convenience
def addPattern(self, patternDescriptor): def addPattern(self, patternDescriptor):
@ -79,19 +79,21 @@ class PatternController():
s.close() s.close()
def getPatternDescriptor(self, patternId): def getPattern(self, patternId):
try: try:
s = self.Session() s = self.Session()
q = s.query(Pattern).filter(Pattern.id == int(patternId)) q = s.query(Pattern).filter(Pattern.id == int(patternId))
if q.count(): if q.count():
pattern = q.first() # pattern = q.first()
#return self.getPatternDict(pattern) #return self.getPatternDict(pattern)
return pattern.getDescriptor() return q.first()
else:
return None
except Exception as ex: except Exception as ex:
raise click.ClickException(f"PatternController.getPatternDescriptor(): {repr(ex)}") raise click.ClickException(f"PatternController.getPattern(): {repr(ex)}")
finally: finally:
s.close() s.close()
@ -120,37 +122,44 @@ class PatternController():
def matchFilename(self, filename): def matchFilename(self, filename):
SEASON_PATTERN = '[sS]([0-9]+)' #SEASON_PATTERN = '[sS]([0-9]+)'
EPISODE_PATTERN = '[eE]([0-9]+)' #EPISODE_PATTERN = '[eE]([0-9]+)'
result = {} #result = {}
try: try:
s = self.Session() s = self.Session()
q = s.query(Pattern) q = s.query(Pattern)
for pattern in q.all(): matchedPatterns = [p for p in q.all() if re.search(p.pattern, filename)]
match = re.search(pattern.pattern, filename)
if match:
result['pattern_id'] = pattern.id if matchedPatterns:
result['show_id'] = pattern.show_id return matchedPatterns[0]
else:
result['indicator'] = match.group(1) return None
seasonMatch = re.search(SEASON_PATTERN, result['indicator'])
if seasonMatch:
result['season'] = int(seasonMatch.group(1))
episodeMatch = re.search(EPISODE_PATTERN, result['indicator']) # for pattern in q.all():
if episodeMatch: #
result['episode'] = int(episodeMatch.group(1)) # 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: except Exception as ex:
raise click.ClickException(f"PatternController.matchFilename(): {repr(ex)}") raise click.ClickException(f"PatternController.findPattern(): {repr(ex)}")
finally: finally:
s.close() s.close()

@ -50,7 +50,7 @@ class PatternDeleteScreen(Screen):
super().__init__() super().__init__()
self.context = self.app.getContext() 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.__pc = PatternController(context = self.context)
self.__sc = ShowController(context = self.context) self.__sc = ShowController(context = self.context)

@ -73,13 +73,13 @@ class PatternDetailsScreen(Screen):
super().__init__() super().__init__()
self.context = self.app.getContext() 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.__pc = PatternController(context = self.context)
self.__sc = ShowController(context = self.context) self.__sc = ShowController(context = self.context)
self.__tc = TrackController(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 {} 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()] return [{'id': int(p.id), 'pattern': p.pattern} for p in q.all()]
except Exception as ex: except Exception as ex:
click.ClickException(f"loadPatterns(): {repr(ex)}") raise click.ClickException(f"loadTracks(): {repr(ex)}")
finally: finally:
s.close() s.close()
@ -107,19 +107,18 @@ class PatternDetailsScreen(Screen):
self.audioStreamsTable.clear() 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) dispoSet = at.getDispositionSet()
dispoList = ad['disposition_list']
row = (ad['sub_index'], row = (at.getSubIndex(),
" ", " ",
ad['language'].label(), at.getLanguage().label(),
ad['title'], at.getTitle(),
'Yes' if TrackDisposition.DEFAULT in dispoList else 'No', 'Yes' if TrackDisposition.DEFAULT in dispoSet else 'No',
'Yes' if TrackDisposition.FORCED in dispoList else 'No') 'Yes' if TrackDisposition.FORCED in dispoSet else 'No')
self.audioStreamsTable.add_row(*map(str, row)) self.audioStreamsTable.add_row(*map(str, row))
@ -127,33 +126,30 @@ class PatternDetailsScreen(Screen):
self.subtitleStreamsTable.clear() 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) dispoSet = st.getDispositionSet()
dispoList = sd['disposition_list']
row = (sd['sub_index'], row = (st.getSubIndex(),
" ", " ",
sd['language'].label(), st.getLanguage().label(),
sd['title'], st.getTitle(),
'Yes' if TrackDisposition.DEFAULT in dispoList else 'No', 'Yes' if TrackDisposition.DEFAULT in dispoSet else 'No',
'Yes' if TrackDisposition.FORCED in dispoList else 'No') 'Yes' if TrackDisposition.FORCED in dispoSet else 'No')
self.subtitleStreamsTable.add_row(*map(str, row)) self.subtitleStreamsTable.add_row(*map(str, row))
def on_mount(self): def on_mount(self):
if self.pattern_obj:
self.query_one("#pattern_input", Input).value = str(self.pattern_obj['pattern'])
if self.show_obj: if self.show_obj:
self.query_one("#showlabel", Static).update(f"{self.show_obj['id']} - {self.show_obj['name']} ({self.show_obj['year']})") self.query_one("#showlabel", Static).update(f"{self.show_obj['id']} - {self.show_obj['name']} ({self.show_obj['year']})")
if self.__pattern is not None:
if self.pattern_obj:
self.query_one("#pattern_input", Input).value = str(self.__pattern.getPattern())
self.updateAudioTracks() self.updateAudioTracks()
self.updateSubtitleTracks() self.updateSubtitleTracks()
@ -192,7 +188,7 @@ class PatternDetailsScreen(Screen):
with Grid(): with Grid():
# 1 # 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") yield Input(type="text", id="pattern_input", classes="four")
# 2 # 2
@ -209,7 +205,7 @@ class PatternDetailsScreen(Screen):
yield Static("Audio streams") yield Static("Audio streams")
yield Static(" ") yield Static(" ")
if self.pattern_obj: if self.__pattern is not None:
yield Button("Add", id="button_add_audio_stream") yield Button("Add", id="button_add_audio_stream")
yield Button("Edit", id="button_edit_audio_stream") yield Button("Edit", id="button_edit_audio_stream")
yield Button("Delete", id="button_delete_audio_stream") yield Button("Delete", id="button_delete_audio_stream")
@ -227,7 +223,7 @@ class PatternDetailsScreen(Screen):
yield Static("Subtitle streams") yield Static("Subtitle streams")
yield Static(" ") yield Static(" ")
if self.pattern_obj: if self.__pattern is not None:
yield Button("Add", id="button_add_subtitle_stream") yield Button("Add", id="button_add_subtitle_stream")
yield Button("Edit", id="button_edit_subtitle_stream") yield Button("Edit", id="button_edit_subtitle_stream")
yield Button("Delete", id="button_delete_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 return None
try: try:
@ -269,7 +265,7 @@ class PatternDetailsScreen(Screen):
subIndex = int(selected_track_data[0]) 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: else:
return None return None
@ -278,9 +274,9 @@ class PatternDetailsScreen(Screen):
return None return None
def getSelectedSubtitleTrackId(self): def getSelectedSubtitleTrack(self):
if not self.pattern_obj: if not self.__pattern is None:
return None return None
try: try:
@ -294,7 +290,7 @@ class PatternDetailsScreen(Screen):
subIndex = int(selected_track_data[0]) 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: else:
return None return None
@ -313,9 +309,9 @@ class PatternDetailsScreen(Screen):
patternDescriptor['show_id'] = self.show_obj['id'] patternDescriptor['show_id'] = self.show_obj['id']
patternDescriptor['pattern'] = self.getPatternFromInput() 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) self.dismiss(patternDescriptor)
else: else:
#TODO: Meldung #TODO: Meldung
@ -335,29 +331,29 @@ class PatternDetailsScreen(Screen):
# Save pattern when just created before adding streams # Save pattern when just created before adding streams
if self.pattern_obj: if self.__pattern is not None:
#self.pattern_obj #self.pattern_obj
if event.button.id == "button_add_audio_stream": 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() selectedAudioTrack = self.getSelectedAudioTrack()
if selectedAudioTrackId is not None: if selectedAudioTrack is not None:
if event.button.id == "button_edit_audio_stream": 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": 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": 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() selectedSubtitleTrack = self.getSelectedSubtitleTrack()
if selectedSubtitleTrackId is not None: if selectedSubtitleTrack is not None:
if event.button.id == "button_edit_subtitle_stream": 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": 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": if event.button.id == "patternbutton":
@ -373,27 +369,31 @@ class PatternDetailsScreen(Screen):
def handle_add_track(self, trackDescriptor): def handle_add_track(self, trackDescriptor):
dispoList = trackDescriptor['disposition_list'] dispoSet = trackDescriptor.getDispositionSet()
trackType = trackDescriptor.getType()
if trackDescriptor['type'] == TrackType.AUDIO: subIndex = trackDescriptor.getSubIndex()
language = trackDescriptor.getLanguage()
title = trackDescriptor.getTitle()
if trackType == TrackType.AUDIO:
row = (trackDescriptor['sub_index'], row = (subIndex,
" ", " ",
trackDescriptor['language'].label(), language.label(),
trackDescriptor['title'], title,
'Yes' if TrackDisposition.DEFAULT in dispoList else 'No', 'Yes' if TrackDisposition.DEFAULT in dispoSet else 'No',
'Yes' if TrackDisposition.FORCED in dispoList else 'No') 'Yes' if TrackDisposition.FORCED in dispoSet else 'No')
self.audioStreamsTable.add_row(*map(str, row)) 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(), language.label(),
trackDescriptor['title'], title,
'Yes' if TrackDisposition.DEFAULT in dispoList else 'No', 'Yes' if TrackDisposition.DEFAULT in dispoSet else 'No',
'Yes' if TrackDisposition.FORCED in dispoList else 'No') 'Yes' if TrackDisposition.FORCED in dispoSet else 'No')
self.subtitleStreamsTable.add_row(*map(str, row)) self.subtitleStreamsTable.add_row(*map(str, row))

@ -8,7 +8,7 @@ class ShowController():
def __init__(self, context): def __init__(self, context):
self.context = context self.context = context
self.Session = self.context['database_session'] # convenience self.Session = self.context['database']['session'] # convenience
def getShowDesciptor(self, showId): def getShowDesciptor(self, showId):

@ -48,7 +48,7 @@ class ShowDeleteScreen(Screen):
super().__init__() super().__init__()
self.context = self.app.getContext() 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.__sc = ShowController(context = self.context)
self.show_obj = self.__sc.getShowDesciptor(showId) if showId is not None else {} self.show_obj = self.__sc.getShowDesciptor(showId) if showId is not None else {}

@ -76,7 +76,7 @@ class ShowDetailsScreen(Screen):
super().__init__() super().__init__()
self.context = self.app.getContext() 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.__sc = ShowController(context = self.context)
self.__pc = PatternController(context = self.context) self.__pc = PatternController(context = self.context)

@ -57,7 +57,7 @@ class ShowsScreen(Screen):
self.context = self.app.getContext() self.context = self.app.getContext()
self.Session = self.context['database_session'] # convenience self.Session = self.context['database']['session'] # convenience

@ -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()

@ -9,13 +9,15 @@ from .iso_language import IsoLanguage
from .track_type import TrackType from .track_type import TrackType
from ffx.model.track_tag import TrackTag
class TrackController(): class TrackController():
def __init__(self, context): def __init__(self, context):
self.context = context self.context = context
self.Session = self.context['database_session'] # convenience self.Session = self.context['database']['session'] # convenience
def addTrack(self, trackDescriptor): def addTrack(self, trackDescriptor):
@ -23,19 +25,20 @@ class TrackController():
try: try:
s = self.Session() s = self.Session()
track = Track(pattern_id = int(trackDescriptor['pattern_id']), track = Track(pattern_id = int(trackDescriptor.getPatternId()),
track_type = int(trackDescriptor.getType().index()),
track_type = int(trackDescriptor['type'].value), sub_index = int(trackDescriptor.getSubIndex()),
disposition_flags = int(TrackDisposition.toFlags(trackDescriptor.getDispositionSet())))
sub_index = int(trackDescriptor['sub_index']),
language = str(trackDescriptor['language'].threeLetter()),
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() s.commit()
except Exception as ex: except Exception as ex:
@ -55,9 +58,9 @@ class TrackController():
track = q.first() track = q.first()
track.sub_index = int(trackDescriptor['sub_index']) track.sub_index = int(trackDescriptor['sub_index'])
track.language = str(trackDescriptor['language'].threeLetter()) # track.language = str(trackDescriptor['language'].threeLetter())
track.title = str(trackDescriptor['title']) # track.title = str(trackDescriptor['title'])
track.disposition_flags = int(TrackDisposition.toFlags(trackDescriptor['disposition_list'])) track.disposition_flags = int(TrackDisposition.toFlags(trackDescriptor.getDispositionSet()))
s.commit() s.commit()
@ -71,31 +74,54 @@ class TrackController():
finally: finally:
s.close() 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: try:
s = self.Session() s = self.Session()
trackDescriptors = {} q = s.query(Track).filter(Track.pattern_id == int(patternId), Track.track_type == TrackType.AUDIO.index())
trackDescriptors[TrackType.AUDIO.label()] = [] return [a for a in q.all()]
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)
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: except Exception as ex:
raise click.ClickException(f"TrackController.findAllTracks(): {repr(ex)}") raise click.ClickException(f"TrackController.findSubtitleTracks(): {repr(ex)}")
finally: finally:
s.close() 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)) q = s.query(Track).filter(Track.pattern_id == int(patternId), Track.track_type == trackType.value, Track.sub_index == int(subIndex))
if q.count(): if q.count():
track = q.first() #track = q.first()
return int(track.id) #return int(track.id)
return q.first()
else: else:
return None return None
@ -118,23 +145,23 @@ class TrackController():
s.close() s.close()
def getTrackDescriptor(self, trackId): # def getTrackDescriptor(self, trackId):
#
try: # try:
s = self.Session() # s = self.Session()
q = s.query(Track).filter(Track.id == int(trackId)) # q = s.query(Track).filter(Track.id == int(trackId))
#
if q.count(): # if q.count():
track = q.first() # track = q.first()
#return self.getTrackDict(track) # #return self.getTrackDict(track)
return track.getDescriptor() # return track.getDescriptor()
else: # else:
return {} # return {}
#
except Exception as ex: # except Exception as ex:
raise click.ClickException(f"TrackController.getTrackDescriptor(): {repr(ex)}") # raise click.ClickException(f"TrackController.getTrackDescriptor(): {repr(ex)}")
finally: # finally:
s.close() # s.close()
def deleteTrack(self, trackId): def deleteTrack(self, trackId):

@ -58,7 +58,7 @@ class TrackDeleteScreen(Screen):
super().__init__() super().__init__()
self.context = self.app.getContext() self.context = self.app.getContext()
self.Session = self.context['database_session'] # convenience self.Session = self.context['database']['session'] # convenience
if trackId is None: if trackId is None:
raise click.ClickException('TrackDeleteScreen.init(): trackId is required to be set') raise click.ClickException('TrackDeleteScreen.init(): trackId is required to be set')

@ -6,20 +6,72 @@ from .track_disposition import TrackDisposition
class TrackDescriptor(): 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_DISPOSITION_KEY = 'disposition'
FFPROBE_TAGS_KEY = 'tags' FFPROBE_TAGS_KEY = 'tags'
def __init__(self, **kwargs): def __init__(self, **kwargs):
# self.__index = int(kwargs['index']) if 'index' in kwargs.keys() else -1 if TrackDescriptor.PATTERN_ID_KEY in kwargs.keys():
# self.__subIndex = int(kwargs['sub_index']) if 'sub_index' in kwargs.keys() else -1 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 {} if TrackDescriptor.TAGS_KEY in kwargs.keys():
self.__dispositionSet = kwargs['dispositionSet'] if 'dispositionSet' in kwargs.keys() else set() 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 @classmethod
@ -70,14 +122,31 @@ class TrackDescriptor():
if trackType != TrackType.UNKNOWN: if trackType != TrackType.UNKNOWN:
return cls(trackType = trackType, kwargs = {}
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(), kwargs[TrackDescriptor.TRACK_TYPE_KEY] = trackType
tags = streamObj[TrackDescriptor.FFPROBE_TAGS_KEY] if TrackDescriptor.FFPROBE_TAGS_KEY in streamObj.keys() else {}, kwargs[TrackDescriptor.DISPOSITION_SET_KEY] = {t for d in (k for (k,v) in streamObj[TrackDescriptor.FFPROBE_DISPOSITION_KEY].items() if v)
audioLayout = AudioLayout.identify(streamObj) if trackType == TrackType.AUDIO.label() else AudioLayout.LAYOUT_UNDEFINED) 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: else:
return None 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): def getLanguage(self):
if 'language' in self.__trackTags.keys(): if 'language' in self.__trackTags.keys():
return IsoLanguage.findThreeLetter(self.__trackTags['language']) return IsoLanguage.findThreeLetter(self.__trackTags['language'])
@ -95,4 +164,7 @@ class TrackDescriptor():
def getTags(self): def getTags(self):
return self.__trackTags return self.__trackTags
def getDispositionSet(self):
return self.__dispositionSet

@ -18,6 +18,9 @@ from .track_type import TrackType
from .iso_language import IsoLanguage from .iso_language import IsoLanguage
from .track_disposition import TrackDisposition from .track_disposition import TrackDisposition
from .audio_layout import AudioLayout
from .track_descriptor import TrackDescriptor
# Screen[dict[int, str, int]] # 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__() super().__init__()
self.context = self.app.getContext() 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.__tc = TrackController(context = self.context)
self.__pc = PatternController(context = self.context) self.__pc = PatternController(context = self.context)
self.track_obj = self.__tc.getTrackDescriptor(trackId) if trackId is not None else {}
INDEX_KEY = 'index'
if self.track_obj: SUB_INDEX_KEY = 'sub_index'
self.trackType = self.track_obj['type'] PATTERN_ID_KEY = 'pattern_id'
self.subIndex = self.track_obj['sub_index']
self.pattern_obj = self.__pc.getPatternDescriptor(self.track_obj['pattern_id']) TRACK_TYPE_KEY = 'track_type'
self.track_obj['is_new'] = False 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: else:
self.trackType = trackType self.__trackType = trackDescriptor.getType()
self.subIndex = subIndex self.__subIndex = trackDescriptor.getSubIndex()
self.pattern_obj = self.__pc.getPatternDescriptor(patternId) if patternId is not None else {} self.__trackDescriptor = trackDescriptor
self.track_obj['is_new'] = True self.__pattern = self.__pc.getPattern(self.__trackDescriptor.getPatternId())
if self.trackType is None: # self.__trackDescriptor = trackDescriptor
raise click.ClickException('Track type is required to be set') # self.__trackType = 0
if self.subIndex is None: # self.__subIndex = 0
raise click.ClickException('Sub index for track is required to be set') # 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): def on_mount(self):
if self.pattern_obj: if self.__pattern is not None:
self.query_one("#patternlabel", Static).update(str(self.pattern_obj['pattern'])) self.query_one("#patternlabel", Static).update(self.__pattern.getPattern())
if self.subIndex is not None: if self.__subIndex is not None:
self.query_one("#subindexlabel", Static).update(str(self.subIndex)) self.query_one("#subindexlabel", Static).update(str(self.__subIndex))
for d in TrackDisposition: if self.__trackDescriptor is not None:
dispositionIsSet = (self.track_obj for d in TrackDisposition:
and 'disposition_list' in self.track_obj.keys()
and d in self.track_obj['disposition_list'])
disposition = (d.label(), d.index(), dispositionIsSet) dispositionIsSet = (self.__trackDescriptor is not None
self.query_one("#dispositions_selection_list", SelectionList).add_option(disposition) and d in self.__trackDescriptor.getDispositionSet())
if 'language' in self.track_obj.keys(): dispositionOption = (d.label(), d.index(), dispositionIsSet)
self.query_one("#language_select", Select).value = self.track_obj['language'].label() self.query_one("#dispositions_selection_list", SelectionList).add_option(dispositionOption)
if 'title' in self.track_obj.keys(): self.query_one("#language_select", Select).value = self.__trackDescriptor.getLanguage().label()
self.query_one("#title_input", Input).value = str(self.track_obj['title']) 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(): with Grid():
# 1 # 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 # 2
yield Static("for pattern") yield Static("for pattern")
@ -220,50 +259,52 @@ class TrackDetailsScreen(Screen):
def getTrackDescriptorFromInput(self): def getTrackDescriptorFromInput(self):
trackDescriptor = {} kwargs = {}
trackDescriptor['pattern_id'] = int(self.pattern_obj['id'])
trackDescriptor['type'] = TrackType(self.trackType)
trackDescriptor['sub_index'] = self.subIndex
trackDescriptor['language'] = IsoLanguage.find(str(self.query_one("#language_select", Select).value)) kwargs[TrackDescriptor.PATTERN_ID_KEY] = int(self.__pattern.getId())
trackDescriptor['title'] = str(self.query_one("#title_input", Input).value)
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 # Event handler for button press
def on_button_pressed(self, event: Button.Pressed) -> None: def on_button_pressed(self, event: Button.Pressed) -> None:
# Check if the button pressed is the one we are interested in # Check if the button pressed is the one we are interested in
if event.button.id == "save_button": if event.button.id == "save_button":
trackDescriptor = self.getTrackDescriptorFromInput() trackDescriptor = self.getTrackDescriptorFromInput()
# Check for multiple default/forced disposition flags # Check for multiple default/forced disposition flags
trackIdList = self.__tc.findAllTracks(self.pattern_obj['id'])[self.trackType.label()] if self.__trackType == TrackType.AUDIO:
trackList = self.__tc.findAudioTracks(self.__pattern.getId())
descriptorList = [d for d in (self.__tc.getTrackDescriptor(t) for t in trackIdList) elif self.__trackType == TrackType.SUBTITLE:
if d['type'] == self.trackType trackList = self.__tc.findSubtitleTracks(self.__pattern.getId())
and d['sub_index'] != self.subIndex] else:
trackList = []
numDefaultTracks = [d for d in descriptorList if TrackDisposition.DEFAULT in d['disposition_list']] # descriptorList = [d for d in (self.__tc.getTrackDescriptor(t) for t in trackList)
numForcedTracks = [d for d in descriptorList if TrackDisposition.FORCED in d['disposition_list']] # if d['type'] == self.__trackType
# and d['sub_index'] != self.__subIndex]
doubleDefaultOrForced = ((TrackDisposition.DEFAULT in trackDescriptor['disposition_list'] and numDefaultTracks) siblingTrackList = [t for t in trackList if t.getType() == self.__trackType and t.getSubIndex() != self.__subIndex]
or (TrackDisposition.FORCED in trackDescriptor['disposition_list'] and numForcedTracks))
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") 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(" ") self.query_one("#messagestatic", Static).update(" ")
if self.track_obj['is_new']: if self.__isNew:
self.__tc.addTrack(trackDescriptor) self.__tc.addTrack(trackDescriptor)
self.dismiss(trackDescriptor) self.dismiss(trackDescriptor)
else: 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): if self.__tc.updateTrack(trackId, trackDescriptor):
self.dismiss(trackDescriptor) self.dismiss(trackDescriptor)

@ -1,5 +1,7 @@
import difflib, click
from enum import Enum from enum import Enum
import difflib
class TrackDisposition(Enum): class TrackDisposition(Enum):
@ -23,6 +25,7 @@ class TrackDisposition(Enum):
DEPENDENT = {"name": "dependent", "index": 16} DEPENDENT = {"name": "dependent", "index": 16}
STILL_IMAGE = {"name": "still_image", "index": 17} STILL_IMAGE = {"name": "still_image", "index": 17}
def label(self): def label(self):
return str(self.value['name']) return str(self.value['name'])
@ -31,22 +34,27 @@ class TrackDisposition(Enum):
@staticmethod @staticmethod
def toFlags(dispositionList): def toFlags(dispositionSet):
"""Flags stored in integer bits (2**index)""" """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 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() flags += 2 ** d.index()
return flags return flags
@staticmethod @staticmethod
def toList(flags): def toSet(flags):
dispositionSet = set()
dispositionList = []
for d in TrackDisposition: for d in TrackDisposition:
if flags & int(2 ** d.index()): if flags & int(2 ** d.index()):
dispositionList += [d] dispositionSet.add(d)
return dispositionList return dispositionSet
@staticmethod @staticmethod
def find(label): def find(label):

Loading…
Cancel
Save