combine track datatables

click-textual
Maveno 12 months ago
parent ff93875a07
commit ca57e981a6

@ -188,28 +188,6 @@ class FileProperties():
return json.loads(ffprobeOutput)['streams'] return json.loads(ffprobeOutput)['streams']
# def getTrackDescriptor(self, streamObj):
# """Convert the stream describing json object into a track descriptor"""
#
# trackType = streamObj['codec_type']
#
# descriptor = {}
#
# if trackType in [t.label() for t in TrackType]:
#
# descriptor['type'] = trackType
#
# descriptor = {}
# descriptor['disposition_list'] = [t for d in (k for (k,v) in streamObj['disposition'].items() if v) if (t := TrackDisposition.find(d)) if t is not None]
#
# descriptor['tags'] = streamObj['tags'] if 'tags' in streamObj.keys() else {}
#
# if trackType == TrackType.AUDIO.label():
# descriptor['layout'] = AudioLayout.identify(streamObj)
#
# return descriptor
def getMediaDescriptor(self): def getMediaDescriptor(self):
return MediaDescriptor.fromFfprobe(self.getFormatData(), self.getStreamData()) return MediaDescriptor.fromFfprobe(self.getFormatData(), self.getStreamData())

@ -34,8 +34,8 @@ class MediaDetailsScreen(Screen):
CSS = """ CSS = """
Grid { Grid {
grid-size: 4 9; grid-size: 4 7;
grid-rows: 8 2 2 2 8 2 8 2 8; grid-rows: 8 2 2 2 8 2 8;
grid-columns: 25 125 10 75; grid-columns: 25 125 10 75;
height: 100%; height: 100%;
width: 100%; width: 100%;
@ -149,7 +149,7 @@ class MediaDetailsScreen(Screen):
# #
# def updateAudioTracks(self): # def updateAudioTracks(self):
# #
# self.audioStreamsTable.clear() # self.tracksTable.clear()
# #
# if self.__pattern is not None: # if self.__pattern is not None:
# #
@ -166,7 +166,7 @@ class MediaDetailsScreen(Screen):
# 'Yes' if TrackDisposition.DEFAULT in dispoSet else 'No', # 'Yes' if TrackDisposition.DEFAULT in dispoSet else 'No',
# 'Yes' if TrackDisposition.FORCED in dispoSet else 'No') # 'Yes' if TrackDisposition.FORCED in dispoSet else 'No')
# #
# self.audioStreamsTable.add_row(*map(str, row)) # self.tracksTable.add_row(*map(str, row))
# #
# def updateSubtitleTracks(self): # def updateSubtitleTracks(self):
# #
@ -220,8 +220,9 @@ class MediaDetailsScreen(Screen):
row = (mediaTagKey, mediaTagValue) # Convert each element to a string before adding row = (mediaTagKey, mediaTagValue) # Convert each element to a string before adding
self.mediaTagsTable.add_row(*map(str, row)) self.mediaTagsTable.add_row(*map(str, row))
self.updateAudioTracks(self.__mediaDescriptor.getAudioTracks()) #self.updateAudioTracks(self.__mediaDescriptor.getAudioTracks())
self.updateSubtitleTracks(self.__mediaDescriptor.getSubtitleTracks()) #self.updateSubtitleTracks(self.__mediaDescriptor.getSubtitleTracks())
self.updateTracks()
if self.__mediaFilenamePattern is not None: if self.__mediaFilenamePattern is not None:
@ -295,42 +296,34 @@ class MediaDetailsScreen(Screen):
def updateAudioTracks(self, audioTracks): def updateTracks(self):
self.audioStreamsTable.clear() self.tracksTable.clear()
for at in audioTracks: trackDescriptorList = self.__mediaDescriptor.getAllTracks()
dispoSet = at.getDispositionSet() typeCounter = {}
row = (at.getSubIndex(), for td in trackDescriptorList:
" ",
at.getLanguage().label(),
at.getTitle(),
'Yes' if TrackDisposition.DEFAULT in dispoSet else 'No',
'Yes' if TrackDisposition.FORCED in dispoSet else 'No')
self.audioStreamsTable.add_row(*map(str, row))
def updateSubtitleTracks(self, subtitleTracks):
self.subtitleStreamsTable.clear()
for st in subtitleTracks: trackType = td.getType()
if not trackType in typeCounter.keys():
typeCounter[trackType] = 0
dispoSet = st.getDispositionSet() dispoSet = td.getDispositionSet()
row = (st.getSubIndex(), row = (td.getIndex(),
trackType.label(),
typeCounter[trackType],
" ", " ",
st.getLanguage().label(), td.getLanguage().label(),
st.getTitle(), td.getTitle(),
'Yes' if TrackDisposition.DEFAULT in dispoSet else 'No', 'Yes' if TrackDisposition.DEFAULT in dispoSet else 'No',
'Yes' if TrackDisposition.FORCED in dispoSet else 'No') 'Yes' if TrackDisposition.FORCED in dispoSet else 'No')
self.subtitleStreamsTable.add_row(*map(str, row)) self.tracksTable.add_row(*map(str, row))
typeCounter[trackType] += 1
def compose(self): def compose(self):
@ -352,36 +345,24 @@ class MediaDetailsScreen(Screen):
# Define the columns with headers # Define the columns with headers
self.column_key_track_tag_key = self.mediaTagsTable.add_column("Key", width=20) self.column_key_track_tag_key = self.mediaTagsTable.add_column("Key", width=20)
self.column_key_track_tag_value = self.mediaTagsTable.add_column("Value", width=90) self.column_key_track_tag_value = self.mediaTagsTable.add_column("Value", width=100)
self.mediaTagsTable.cursor_type = 'row' self.mediaTagsTable.cursor_type = 'row'
self.tracksTable = DataTable()
self.audioStreamsTable = DataTable()
# Define the columns with headers
self.column_key_audio_subid = self.audioStreamsTable.add_column("Subindex", width=20)
self.column_key_audio_layout = self.audioStreamsTable.add_column("Layout", width=20)
self.column_key_audio_language = self.audioStreamsTable.add_column("Language", width=20)
self.column_key_audio_title = self.audioStreamsTable.add_column("Title", width=30)
self.column_key_audio_default = self.audioStreamsTable.add_column("Default", width=10)
self.column_key_audio_forced = self.audioStreamsTable.add_column("Forced", width=10)
self.audioStreamsTable.cursor_type = 'row'
self.subtitleStreamsTable = DataTable()
# Define the columns with headers # Define the columns with headers
self.column_key_subtitle_subid = self.subtitleStreamsTable.add_column("Subindex", width=20) self.column_key_track_index = self.tracksTable.add_column("Index", width=5)
self.column_key_subtitle_spacer = self.subtitleStreamsTable.add_column(" ", width=20) self.column_key_track_type = self.tracksTable.add_column("Type", width=10)
self.column_key_subtitle_language = self.subtitleStreamsTable.add_column("Language", width=20) self.column_key_track_sub_index = self.tracksTable.add_column("Subindex", width=5)
self.column_key_subtitle_title = self.subtitleStreamsTable.add_column("Title", width=30) self.column_key_track_layout = self.tracksTable.add_column("Layout", width=10)
self.column_key_subtitle_default = self.subtitleStreamsTable.add_column("Default", width=10) self.column_key_track_language = self.tracksTable.add_column("Language", width=15)
self.column_key_subtitle_forced = self.subtitleStreamsTable.add_column("Forced", width=10) self.column_key_track_title = self.tracksTable.add_column("Title", width=48)
self.column_key_track_default = self.tracksTable.add_column("Default", width=8)
self.column_key_track_forced = self.tracksTable.add_column("Forced", width=8)
self.subtitleStreamsTable.cursor_type = 'row' self.tracksTable.cursor_type = 'row'
# Create the DataTable widget # Create the DataTable widget
@ -392,8 +373,6 @@ class MediaDetailsScreen(Screen):
self.differencesTable.cursor_type = 'row' self.differencesTable.cursor_type = 'row'
yield Header() yield Header()
with Grid(): with Grid():
@ -427,17 +406,17 @@ class MediaDetailsScreen(Screen):
yield Static(" ", classes="three") yield Static(" ", classes="three")
# 7 # 7
yield Static("Audio Streams") yield Static("Streams")
yield self.audioStreamsTable yield self.tracksTable
yield Static(" ") yield Static(" ")
# 8 # # 8
yield Static(" ", classes="three") # yield Static(" ", classes="three")
#
# 9 # # 9
yield Static("Subtitle Streams") # yield Static("Subtitle Streams")
yield self.subtitleStreamsTable # yield self.subtitleStreamsTable
yield Static(" ") # yield Static(" ")
# 1 # 1
@ -467,7 +446,7 @@ class MediaDetailsScreen(Screen):
# yield Static("") # yield Static("")
# yield Static("") # yield Static("")
# # 6 # # 6
# yield self.audioStreamsTable # yield self.tracksTable
# #
# # 7 # # 7
# yield Static(" ", classes="five") # yield Static(" ", classes="five")
@ -511,10 +490,10 @@ class MediaDetailsScreen(Screen):
# #
# # Fetch the currently selected row when 'Enter' is pressed # # Fetch the currently selected row when 'Enter' is pressed
# #selected_row_index = self.table.cursor_row # #selected_row_index = self.table.cursor_row
# row_key, col_key = self.audioStreamsTable.coordinate_to_cell_key(self.audioStreamsTable.cursor_coordinate) # row_key, col_key = self.tracksTable.coordinate_to_cell_key(self.tracksTable.cursor_coordinate)
# #
# if row_key is not None: # if row_key is not None:
# selected_track_data = self.audioStreamsTable.get_row(row_key) # selected_track_data = self.tracksTable.get_row(row_key)
# #
# subIndex = int(selected_track_data[0]) # subIndex = int(selected_track_data[0])
# #
@ -589,7 +568,7 @@ class MediaDetailsScreen(Screen):
# if self.__pattern is not None: # if self.__pattern is not None:
# #
# 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.getId(), subIndex = len(self.audioStreamsTable.rows)), self.handle_add_track) # self.app.push_screen(TrackDetailsScreen(trackType = TrackType.AUDIO, patternId = self.__pattern.getId(), subIndex = len(self.tracksTable.rows)), self.handle_add_track)
# #
# selectedAudioTrack = self.getSelectedAudioTrackDescriptor() # selectedAudioTrack = self.getSelectedAudioTrackDescriptor()
# if selectedAudioTrack is not None: # if selectedAudioTrack is not None:
@ -638,7 +617,7 @@ class MediaDetailsScreen(Screen):
# 'Yes' if TrackDisposition.DEFAULT in dispoSet else 'No', # 'Yes' if TrackDisposition.DEFAULT in dispoSet else 'No',
# 'Yes' if TrackDisposition.FORCED in dispoSet else 'No') # 'Yes' if TrackDisposition.FORCED in dispoSet else 'No')
# #
# self.audioStreamsTable.add_row(*map(str, row)) # self.tracksTable.add_row(*map(str, row))
# #
# if trackType == TrackType.SUBTITLE: # if trackType == TrackType.SUBTITLE:
# #
@ -657,12 +636,12 @@ class MediaDetailsScreen(Screen):
# try: # try:
# if trackDescriptor.getType() == TrackType.AUDIO: # if trackDescriptor.getType() == TrackType.AUDIO:
# #
# row_key, col_key = self.audioStreamsTable.coordinate_to_cell_key(self.audioStreamsTable.cursor_coordinate) # row_key, col_key = self.tracksTable.coordinate_to_cell_key(self.tracksTable.cursor_coordinate)
# #
# self.audioStreamsTable.update_cell(row_key, self.column_key_audio_language, trackDescriptor.getLanguage().label()) # self.tracksTable.update_cell(row_key, self.column_key_track_language, trackDescriptor.getLanguage().label())
# self.audioStreamsTable.update_cell(row_key, self.column_key_audio_title, trackDescriptor.getTitle()) # self.tracksTable.update_cell(row_key, self.column_key_track_title, trackDescriptor.getTitle())
# self.audioStreamsTable.update_cell(row_key, self.column_key_audio_default, 'Yes' if TrackDisposition.DEFAULT in trackDescriptor.getDispositionSet() else 'No') # self.tracksTable.update_cell(row_key, self.column_key_track_default, 'Yes' if TrackDisposition.DEFAULT in trackDescriptor.getDispositionSet() else 'No')
# self.audioStreamsTable.update_cell(row_key, self.column_key_audio_forced, 'Yes' if TrackDisposition.FORCED in trackDescriptor.getDispositionSet() else 'No') # self.tracksTable.update_cell(row_key, self.column_key_track_forced, 'Yes' if TrackDisposition.FORCED in trackDescriptor.getDispositionSet() else 'No')
# #
# if trackDescriptor.getType() == TrackType.SUBTITLE: # if trackDescriptor.getType() == TrackType.SUBTITLE:
# #

@ -32,7 +32,7 @@ class Track(Base):
track_type = Column(Integer) # TrackType track_type = Column(Integer) # TrackType
index = Column(Integer) index = Column(Integer)
sub_index = Column(Integer) # sub_index = Column(Integer)
# v1.x # v1.x
pattern_id = Column(Integer, ForeignKey('patterns.id', ondelete="CASCADE")) pattern_id = Column(Integer, ForeignKey('patterns.id', ondelete="CASCADE"))
@ -57,7 +57,7 @@ class Track(Base):
@classmethod @classmethod
def fromStreamObj(cls, streamObj, subIndex, patternId): def fromStreamObj(cls, streamObj, patternId):
"""{ """{
'index': 4, 'index': 4,
'codec_name': 'hdmv_pgs_subtitle', 'codec_name': 'hdmv_pgs_subtitle',
@ -128,8 +128,8 @@ class Track(Base):
if trackType in [t.label() for t in TrackType]: if trackType in [t.label() for t in TrackType]:
# sub_index = int(subIndex),
return cls(pattern_id = patternId, return cls(pattern_id = patternId,
sub_index = int(subIndex),
track_type = trackType, track_type = trackType,
disposition_flags = sum([2**t.index() for (k,v) in streamObj['disposition'].items() if v and (t := TrackDisposition.find(k)) is not None])) disposition_flags = sum([2**t.index() for (k,v) in streamObj['disposition'].items() if v and (t := TrackDisposition.find(k)) is not None]))
@ -149,9 +149,6 @@ class Track(Base):
def getIndex(self): def getIndex(self):
return int(self.index) if self.index is not None else -1 return int(self.index) if self.index is not None else -1
def getSubIndex(self):
return int(self.sub_index) if self.sub_index is not None else -1
def getLanguage(self): 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.UNDEFINED return IsoLanguage.findThreeLetter(tags['language']) if 'language' in tags.keys() else IsoLanguage.UNDEFINED
@ -167,7 +164,7 @@ class Track(Base):
return {str(t.key):str(t.value) for t in self.track_tags} return {str(t.key):str(t.value) for t in self.track_tags}
def getDescriptor(self) -> TrackDescriptor: def getDescriptor(self, subIndex : int = -1) -> TrackDescriptor:
kwargs = {} kwargs = {}
@ -175,7 +172,9 @@ class Track(Base):
kwargs[TrackDescriptor.PATTERN_ID_KEY] = self.getPatternId() kwargs[TrackDescriptor.PATTERN_ID_KEY] = self.getPatternId()
kwargs[TrackDescriptor.INDEX_KEY] = self.getIndex() kwargs[TrackDescriptor.INDEX_KEY] = self.getIndex()
kwargs[TrackDescriptor.SUB_INDEX_KEY] = self.getSubIndex()
if subIndex > -1:
kwargs[TrackDescriptor.SUB_INDEX_KEY] = subIndex
kwargs[TrackDescriptor.TRACK_TYPE_KEY] = self.getType() kwargs[TrackDescriptor.TRACK_TYPE_KEY] = self.getType()
kwargs[TrackDescriptor.DISPOSITION_SET_KEY] = self.getDispositionSet() kwargs[TrackDescriptor.DISPOSITION_SET_KEY] = self.getDispositionSet()

@ -8,6 +8,7 @@ from textual.containers import Grid
from ffx.model.show import Show from ffx.model.show import Show
from ffx.model.pattern import Pattern from ffx.model.pattern import Pattern
from ffx.model.track import Track
from .pattern_controller import PatternController from .pattern_controller import PatternController
from .show_controller import ShowController from .show_controller import ShowController
@ -30,8 +31,8 @@ class PatternDetailsScreen(Screen):
CSS = """ CSS = """
Grid { Grid {
grid-size: 5 12; grid-size: 5 9;
grid-rows: 2 2 2 2 2 6 2 2 6 2 2 2; grid-rows: 2 2 2 2 2 6 2 2 2;
grid-columns: 25 25 25 25 25; grid-columns: 25 25 25 25 25;
height: 100%; height: 100%;
width: 100%; width: 100%;
@ -68,6 +69,10 @@ class PatternDetailsScreen(Screen):
height: 100%; height: 100%;
border: solid green; border: solid green;
} }
#tracks_table {
row-span: 4;
}
""" """
def __init__(self, patternId = None, showId = None): def __init__(self, patternId = None, showId = None):
@ -105,47 +110,38 @@ class PatternDetailsScreen(Screen):
s.close() s.close()
def updateAudioTracks(self): def updateTracks(self):
self.audioStreamsTable.clear() self.tracksTable.clear()
if self.__pattern is not None: if self.__pattern is not None:
audioTracks = self.__tc.findAudioTracks(self.__pattern.getId()) tracks = self.__tc.findTracks(self.__pattern.getId())
for at in audioTracks: typeCounter = {}
dispoSet = at.getDispositionSet() for tr in tracks:
row = (at.getSubIndex(), td : TrackDescriptor = tr.getDescriptor()
" ",
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)) trackType = td.getType()
if not trackType in typeCounter.keys():
def updateSubtitleTracks(self): typeCounter[trackType] = 0
self.subtitleStreamsTable.clear()
if self.__pattern is not None:
subtitleTracks = self.__tc.findSubtitleTracks(self.__pattern.getId()) dispoSet = td.getDispositionSet()
for st in subtitleTracks: row = (td.getIndex(),
trackType.label(),
dispoSet = st.getDispositionSet() typeCounter[trackType],
row = (st.getSubIndex(),
" ", " ",
st.getLanguage().label(), td.getLanguage().label(),
st.getTitle(), td.getTitle(),
'Yes' if TrackDisposition.DEFAULT in dispoSet else 'No', 'Yes' if TrackDisposition.DEFAULT in dispoSet else 'No',
'Yes' if TrackDisposition.FORCED in dispoSet else 'No') 'Yes' if TrackDisposition.FORCED in dispoSet else 'No')
self.subtitleStreamsTable.add_row(*map(str, row)) self.tracksTable.add_row(*map(str, row))
typeCounter[trackType] += 1
def on_mount(self): def on_mount(self):
@ -157,38 +153,25 @@ class PatternDetailsScreen(Screen):
self.query_one("#pattern_input", Input).value = str(self.__pattern.getPattern()) self.query_one("#pattern_input", Input).value = str(self.__pattern.getPattern())
self.updateAudioTracks() self.updateTracks()
self.updateSubtitleTracks()
def compose(self): def compose(self):
self.audioStreamsTable = DataTable(classes="five") self.tracksTable = DataTable(id="tracks_table", classes="five")
# Define the columns with headers self.column_key_track_index = self.tracksTable.add_column("Index", width=5)
self.column_key_audio_subid = self.audioStreamsTable.add_column("Subindex", width=20) self.column_key_track_type = self.tracksTable.add_column("Type", width=10)
self.column_key_audio_layout = self.audioStreamsTable.add_column("Layout", width=20) self.column_key_track_sub_index = self.tracksTable.add_column("Subindex", width=5)
self.column_key_audio_language = self.audioStreamsTable.add_column("Language", width=20) self.column_key_track_layout = self.tracksTable.add_column("Layout", width=10)
self.column_key_audio_title = self.audioStreamsTable.add_column("Title", width=30) self.column_key_track_language = self.tracksTable.add_column("Language", width=15)
self.column_key_audio_default = self.audioStreamsTable.add_column("Default", width=10) self.column_key_track_title = self.tracksTable.add_column("Title", width=48)
self.column_key_audio_forced = self.audioStreamsTable.add_column("Forced", width=10) self.column_key_track_default = self.tracksTable.add_column("Default", width=8)
self.column_key_track_forced = self.tracksTable.add_column("Forced", width=8)
self.audioStreamsTable.cursor_type = 'row'
self.tracksTable.cursor_type = 'row'
self.subtitleStreamsTable = DataTable(classes="five") self.subtitleStreamsTable = DataTable(classes="five")
# Define the columns with headers
self.column_key_subtitle_subid = self.subtitleStreamsTable.add_column("Subindex", width=20)
self.column_key_subtitle_spacer = self.subtitleStreamsTable.add_column(" ", width=20)
self.column_key_subtitle_language = self.subtitleStreamsTable.add_column("Language", width=20)
self.column_key_subtitle_title = self.subtitleStreamsTable.add_column("Title", width=30)
self.column_key_subtitle_default = self.subtitleStreamsTable.add_column("Default", width=10)
self.column_key_subtitle_forced = self.subtitleStreamsTable.add_column("Forced", width=10)
self.subtitleStreamsTable.cursor_type = 'row'
yield Header() yield Header()
with Grid(): with Grid():
@ -200,7 +183,7 @@ class PatternDetailsScreen(Screen):
# 2 # 2
yield Static("from show") yield Static("from show")
yield Static("", id="showlabel", classes="three") yield Static("", id="showlabel", classes="three")
yield Button("Substitute pattern", id="patternbutton") yield Button("Substitute pattern", id="pattern_button")
# 3 # 3
yield Static(" ", classes="five") yield Static(" ", classes="five")
@ -208,42 +191,27 @@ class PatternDetailsScreen(Screen):
yield Static(" ", classes="five") yield Static(" ", classes="five")
# 5 # 5
yield Static("Audio streams") yield Static("Streams")
yield Static(" ") yield Static(" ")
if self.__pattern is not None: if self.__pattern is not None:
yield Button("Add", id="button_add_audio_stream") yield Button("Add", id="button_add_track")
yield Button("Edit", id="button_edit_audio_stream") yield Button("Edit", id="button_edit_track")
yield Button("Delete", id="button_delete_audio_stream") yield Button("Delete", id="button_delete_track")
else: else:
yield Static("") yield Static("")
yield Static("") yield Static("")
yield Static("") yield Static("")
# 6 # 6
yield self.audioStreamsTable yield self.tracksTable
# 7 # 7
yield Static(" ", classes="five") yield Static(" ", classes="five")
# 8 # 8
yield Static("Subtitle streams")
yield Static(" ")
if self.__pattern is not None:
yield Button("Add", id="button_add_subtitle_stream")
yield Button("Edit", id="button_edit_subtitle_stream")
yield Button("Delete", id="button_delete_subtitle_stream")
else:
yield Static("")
yield Static("")
yield Static("")
# 9
yield self.subtitleStreamsTable
# 10
yield Static(" ", classes="five") yield Static(" ", classes="five")
# 11 # 9
yield Button("Save", id="save_button") yield Button("Save", id="save_button")
yield Button("Cancel", id="cancel_button") yield Button("Cancel", id="cancel_button")
@ -255,7 +223,7 @@ class PatternDetailsScreen(Screen):
def getSelectedAudioTrackDescriptor(self): def getSelectedTrackDescriptor(self):
if not self.__pattern: if not self.__pattern:
return None return None
@ -264,14 +232,15 @@ class PatternDetailsScreen(Screen):
# Fetch the currently selected row when 'Enter' is pressed # Fetch the currently selected row when 'Enter' is pressed
#selected_row_index = self.table.cursor_row #selected_row_index = self.table.cursor_row
row_key, col_key = self.audioStreamsTable.coordinate_to_cell_key(self.audioStreamsTable.cursor_coordinate) row_key, col_key = self.tracksTable.coordinate_to_cell_key(self.tracksTable.cursor_coordinate)
if row_key is not None: if row_key is not None:
selected_track_data = self.audioStreamsTable.get_row(row_key) selected_track_data = self.tracksTable.get_row(row_key)
subIndex = int(selected_track_data[0]) trackIndex = int(selected_track_data[0])
trackSubIndex = int(selected_track_data[2])
return self.__tc.findTrack(self.__pattern.getId(), TrackType.AUDIO, subIndex).getDescriptor() return self.__tc.getTrack(self.__pattern.getId(), trackIndex).getDescriptor(subIndex=trackSubIndex)
else: else:
return None return None
@ -280,32 +249,6 @@ class PatternDetailsScreen(Screen):
return None return None
def getSelectedSubtitleTrackDescriptor(self) -> TrackDescriptor:
if not self.__pattern is None:
return None
try:
# Fetch the currently selected row when 'Enter' is pressed
#selected_row_index = self.table.cursor_row
row_key, col_key = self.subtitleStreamsTable.coordinate_to_cell_key(self.subtitleStreamsTable.cursor_coordinate)
if row_key is not None:
selected_track_data = self.subtitleStreamsTable.get_row(row_key)
subIndex = int(selected_track_data[0])
return self.__tc.findTrack(self.__pattern.getId(), TrackType.SUBTITLE, subIndex).getDescriptor()
else:
return None
except CellDoesNotExist:
return None
# Event handler for button press # 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
@ -327,14 +270,6 @@ class PatternDetailsScreen(Screen):
patternId = self.__pc.addPattern(patternDescriptor) patternId = self.__pc.addPattern(patternDescriptor)
if patternId is not None: if patternId is not None:
# Add dummy video track
kwargs = {}
kwargs[TrackDescriptor.INDEX_KEY] = 0
kwargs[TrackDescriptor.SUB_INDEX_KEY] = 0
kwargs[TrackDescriptor.PATTERN_ID_KEY] = patternId
kwargs[TrackDescriptor.TRACK_TYPE_KEY] = TrackType.VIDEO
self.__tc.addTrack(TrackDescriptor(**kwargs))
self.dismiss(patternDescriptor) self.dismiss(patternDescriptor)
else: else:
#TODO: Meldung #TODO: Meldung
@ -349,36 +284,19 @@ class PatternDetailsScreen(Screen):
# Save pattern when just created before adding streams # Save pattern when just created before adding streams
if self.__pattern is not None: if self.__pattern is not None:
numVideoStreams = len(self.__pattern.getMediaDescriptor().getVideoTracks()) numTracks = len(self.tracksTable.rows)
numAudioStreams = len(self.audioStreamsTable.rows)
numSubtitleStreams = len(self.subtitleStreamsTable.rows)
addedTrackIndex = numVideoStreams + numAudioStreams + numSubtitleStreams
#NOTE: Track index if event.button.id == "button_add_track":
self.app.push_screen(TrackDetailsScreen(patternId = self.__pattern.getId(), index = numTracks), self.handle_add_track)
if event.button.id == "button_add_audio_stream": selectedTrack = self.getSelectedTrackDescriptor()
self.app.push_screen(TrackDetailsScreen(trackType = TrackType.AUDIO, patternId = self.__pattern.getId(), index = addedTrackIndex, subIndex = numAudioStreams), self.handle_add_track) if selectedTrack is not None:
if event.button.id == "button_edit_track":
self.app.push_screen(TrackDetailsScreen(trackDescriptor = selectedTrack), self.handle_edit_track)
if event.button.id == "button_delete_track":
self.app.push_screen(TrackDeleteScreen(trackDescriptor = selectedTrack), self.handle_delete_track)
selectedAudioTrack = self.getSelectedAudioTrackDescriptor() if event.button.id == "pattern_button":
if selectedAudioTrack is not None:
if event.button.id == "button_edit_audio_stream":
self.app.push_screen(TrackDetailsScreen(trackDescriptor = selectedAudioTrack), self.handle_edit_track)
if event.button.id == "button_delete_audio_stream":
self.app.push_screen(TrackDeleteScreen(trackDescriptor = selectedAudioTrack), self.handle_delete_track)
if event.button.id == "button_add_subtitle_stream":
self.app.push_screen(TrackDetailsScreen(trackType = TrackType.SUBTITLE, patternId = self.__pattern.getId(), index = addedTrackIndex, subIndex = numSubtitleStreams), self.handle_add_track)
selectedSubtitleTrack = self.getSelectedSubtitleTrackDescriptor()
if selectedSubtitleTrack is not None:
if event.button.id == "button_edit_subtitle_stream":
self.app.push_screen(TrackDetailsScreen(trackDescriptor = selectedSubtitleTrack), self.handle_edit_track)
if event.button.id == "button_delete_subtitle_stream":
self.app.push_screen(TrackDeleteScreen(trackDescriptor = selectedSubtitleTrack), self.handle_delete_track)
if event.button.id == "patternbutton":
INDICATOR_PATTERN = '([sS][0-9]+[eE][0-9]+)' INDICATOR_PATTERN = '([sS][0-9]+[eE][0-9]+)'
@ -390,70 +308,41 @@ class PatternDetailsScreen(Screen):
self.query_one("#pattern_input", Input).value = pattern.replace(patternMatch.group(1), INDICATOR_PATTERN) self.query_one("#pattern_input", Input).value = pattern.replace(patternMatch.group(1), INDICATOR_PATTERN)
def handle_add_track(self, trackDescriptor): def handle_add_track(self, trackDescriptor : TrackDescriptor):
dispoSet = trackDescriptor.getDispositionSet() dispoSet = trackDescriptor.getDispositionSet()
trackType = trackDescriptor.getType() trackType = trackDescriptor.getType()
index = trackDescriptor.getIndex()
subIndex = trackDescriptor.getSubIndex() subIndex = trackDescriptor.getSubIndex()
language = trackDescriptor.getLanguage() language = trackDescriptor.getLanguage()
title = trackDescriptor.getTitle() title = trackDescriptor.getTitle()
if trackType == TrackType.AUDIO: row = (index,
trackType.label(),
row = (subIndex, subIndex,
" ", " ",
language.label(), language.label(),
title, title,
'Yes' if TrackDisposition.DEFAULT in dispoSet else 'No', 'Yes' if TrackDisposition.DEFAULT in dispoSet else 'No',
'Yes' if TrackDisposition.FORCED in dispoSet else 'No') 'Yes' if TrackDisposition.FORCED in dispoSet else 'No')
self.audioStreamsTable.add_row(*map(str, row)) self.tracksTable.add_row(*map(str, row))
if trackType == TrackType.SUBTITLE:
row = (subIndex,
" ",
language.label(),
title,
'Yes' if TrackDisposition.DEFAULT in dispoSet else 'No',
'Yes' if TrackDisposition.FORCED in dispoSet else 'No')
self.subtitleStreamsTable.add_row(*map(str, row))
def handle_edit_track(self, trackDescriptor : TrackDescriptor): def handle_edit_track(self, trackDescriptor : TrackDescriptor):
try: try:
if trackDescriptor.getType() == TrackType.AUDIO:
row_key, col_key = self.audioStreamsTable.coordinate_to_cell_key(self.audioStreamsTable.cursor_coordinate)
self.audioStreamsTable.update_cell(row_key, self.column_key_audio_language, trackDescriptor.getLanguage().label()) row_key, col_key = self.tracksTable.coordinate_to_cell_key(self.tracksTable.cursor_coordinate)
self.audioStreamsTable.update_cell(row_key, self.column_key_audio_title, trackDescriptor.getTitle())
self.audioStreamsTable.update_cell(row_key, self.column_key_audio_default, 'Yes' if TrackDisposition.DEFAULT in trackDescriptor.getDispositionSet() else 'No')
self.audioStreamsTable.update_cell(row_key, self.column_key_audio_forced, 'Yes' if TrackDisposition.FORCED in trackDescriptor.getDispositionSet() else 'No')
if trackDescriptor.getType() == TrackType.SUBTITLE: self.tracksTable.update_cell(row_key, self.column_key_track_language, trackDescriptor.getLanguage().label())
self.tracksTable.update_cell(row_key, self.column_key_track_title, trackDescriptor.getTitle())
row_key, col_key = self.subtitleStreamsTable.coordinate_to_cell_key(self.subtitleStreamsTable.cursor_coordinate) self.tracksTable.update_cell(row_key, self.column_key_track_default, 'Yes' if TrackDisposition.DEFAULT in trackDescriptor.getDispositionSet() else 'No')
self.tracksTable.update_cell(row_key, self.column_key_track_forced, 'Yes' if TrackDisposition.FORCED in trackDescriptor.getDispositionSet() else 'No')
self.subtitleStreamsTable.update_cell(row_key, self.column_key_subtitle_language, trackDescriptor.getLanguage().label())
self.subtitleStreamsTable.update_cell(row_key, self.column_key_subtitle_title, trackDescriptor.getTitle())
self.subtitleStreamsTable.update_cell(row_key, self.column_key_subtitle_default, 'Yes' if TrackDisposition.DEFAULT in trackDescriptor.getDispositionSet() else 'No')
self.subtitleStreamsTable.update_cell(row_key, self.column_key_subtitle_forced, 'Yes' if TrackDisposition.FORCED in trackDescriptor.getDispositionSet() else 'No')
except CellDoesNotExist: except CellDoesNotExist:
pass pass
def handle_delete_track(self, trackDescriptor : TrackDescriptor): def handle_delete_track(self, trackDescriptor : TrackDescriptor):
self.updateTracks()
try:
if trackDescriptor.getType() == TrackType.AUDIO:
self.updateAudioTracks()
if trackDescriptor.getType() == TrackType.SUBTITLE:
self.updateSubtitleTracks()
except CellDoesNotExist:
pass

@ -21,14 +21,13 @@ class TrackController():
self.Session = self.context['database']['session'] # convenience self.Session = self.context['database']['session'] # convenience
def addTrack(self, trackDescriptor): def addTrack(self, trackDescriptor : TrackDescriptor):
try: try:
s = self.Session() s = self.Session()
track = Track(pattern_id = int(trackDescriptor.getPatternId()), track = Track(pattern_id = int(trackDescriptor.getPatternId()),
track_type = int(trackDescriptor.getType().index()), track_type = int(trackDescriptor.getType().index()),
index = int(trackDescriptor.getIndex()), index = int(trackDescriptor.getIndex()),
sub_index = int(trackDescriptor.getSubIndex()),
disposition_flags = int(TrackDisposition.toFlags(trackDescriptor.getDispositionSet()))) disposition_flags = int(TrackDisposition.toFlags(trackDescriptor.getDispositionSet())))
s.add(track) s.add(track)
@ -89,6 +88,32 @@ class TrackController():
finally: finally:
s.close() s.close()
def findTracks(self, patternId):
try:
s = self.Session()
q = s.query(Track).filter(Track.pattern_id == int(patternId))
return [a for a in q.all()]
except Exception as ex:
raise click.ClickException(f"TrackController.findTracks(): {repr(ex)}")
finally:
s.close()
#TODO: mit optionalem Parameter lösen ^
def findVideoTracks(self, patternId):
try:
s = self.Session()
q = s.query(Track).filter(Track.pattern_id == int(patternId), Track.track_type == TrackType.VIDEO.index())
return [a for a in q.all()]
except Exception as ex:
raise click.ClickException(f"TrackController.findVideoTracks(): {repr(ex)}")
finally:
s.close()
def findAudioTracks(self, patternId): def findAudioTracks(self, patternId):
@ -117,11 +142,11 @@ class TrackController():
s.close() s.close()
def findTrack(self, patternId : int, trackType : TrackType, subIndex : int) -> Track: def getTrack(self, patternId : int, index: int) -> Track:
try: try:
s = self.Session() s = self.Session()
q = s.query(Track).filter(Track.pattern_id == int(patternId), Track.track_type == trackType.index(), Track.sub_index == int(subIndex)) q = s.query(Track).filter(Track.pattern_id == int(patternId), Track.index == int(index))
if q.count(): if q.count():
return q.first() return q.first()
@ -129,37 +154,35 @@ class TrackController():
return None return None
except Exception as ex: except Exception as ex:
raise click.ClickException(f"TrackController.findTrack(): {repr(ex)}") raise click.ClickException(f"TrackController.getTrack(): {repr(ex)}")
finally: finally:
s.close() s.close()
def deleteTrack(self, trackId): def deleteTrack(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():
patternId = int(q.first().pattern_id)
track = q.first() q_siblings = s.query(Track).filter(Track.pattern_id == patternId).order_by(Track.index)
q_siblings = s.query(Track).filter(Track.pattern_id == track.getPatternId(), Track.track_type == track.getType().index()).order_by(Track.sub_index)
subIndex = 0 index = 0
for track in q_siblings.all(): for track in q_siblings.all():
if track.sub_index == track.getSubIndex(): if track.id == int(trackId):
s.delete(track) s.delete(track)
else: else:
track.sub_index = subIndex track.index = index
subIndex += 1 index += 1
s.commit() s.commit()
return True return True
return False return False
except Exception as ex: except Exception as ex:
raise click.ClickException(f"TrackController.deleteTrack(): {repr(ex)}") raise click.ClickException(f"TrackController.deleteTrack(): {repr(ex)}")
finally: finally:

@ -7,6 +7,7 @@ from textual.widgets import Header, Footer, Static, Button
from textual.containers import Grid from textual.containers import Grid
from ffx.model.pattern import Pattern from ffx.model.pattern import Pattern
from ffx.model.track import Track
from ffx.track_descriptor import TrackDescriptor from ffx.track_descriptor import TrackDescriptor
from .track_controller import TrackController from .track_controller import TrackController
@ -121,7 +122,7 @@ class TrackDeleteScreen(Screen):
if event.button.id == "delete_button": if event.button.id == "delete_button":
track = self.__tc.findTrack(self.__trackDescriptor.getPatternId(), self.__trackDescriptor.getType(), self.__trackDescriptor.getSubIndex()) track = self.__tc.getTrack(self.__trackDescriptor.getPatternId(), self.__trackDescriptor.getIndex())
if track is None: if track is None:
raise click.ClickException(f"Track is none: patternId={self.__trackDescriptor.getPatternId()} type={self.__trackDescriptor.getType()} subIndex={self.__trackDescriptor.getSubIndex()}") raise click.ClickException(f"Track is none: patternId={self.__trackDescriptor.getPatternId()} type={self.__trackDescriptor.getType()} subIndex={self.__trackDescriptor.getSubIndex()}")

@ -47,12 +47,11 @@ class TrackDescriptor():
if TrackDescriptor.SUB_INDEX_KEY in kwargs.keys(): if TrackDescriptor.SUB_INDEX_KEY in kwargs.keys():
if type(kwargs[TrackDescriptor.SUB_INDEX_KEY]) is not int: if type(kwargs[TrackDescriptor.SUB_INDEX_KEY]) is not int:
raise TypeError(f"TrackDesciptor.__init__(): Argument {TrackDescriptor.SUB_INDEX_KEY} is required to be of type dict") raise TypeError(f"TrackDesciptor.__init__(): Argument {TrackDescriptor.SUB_INDEX_KEY} is required to be of type int")
self.__subIndex = kwargs[TrackDescriptor.SUB_INDEX_KEY] self.__subIndex = kwargs[TrackDescriptor.SUB_INDEX_KEY]
else: else:
self.__subIndex = -1 self.__subIndex = -1
if TrackDescriptor.TRACK_TYPE_KEY in kwargs.keys(): if TrackDescriptor.TRACK_TYPE_KEY in kwargs.keys():
if type(kwargs[TrackDescriptor.TRACK_TYPE_KEY]) is not TrackType: 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") raise TypeError(f"TrackDesciptor.__init__(): Argument {TrackDescriptor.TRACK_TYPE_KEY} is required to be of type TrackType")

@ -7,6 +7,7 @@ from textual.widgets import Header, Footer, Static, Button, SelectionList, Selec
from textual.containers import Grid from textual.containers import Grid
from ffx.model.pattern import Pattern from ffx.model.pattern import Pattern
from ffx.model.track import Track
from .track_controller import TrackController from .track_controller import TrackController
from .pattern_controller import PatternController from .pattern_controller import PatternController
@ -32,9 +33,9 @@ class TrackDetailsScreen(Screen):
CSS = """ CSS = """
Grid { Grid {
grid-size: 5 20; grid-size: 5 21;
grid-rows: 2 2 2 2 2 3 2 2 2 2 2 6 2 2 6 2 2 2 2 6; grid-rows: 2 2 2 2 2 3 2 2 3 2 2 2 2 2 6 2 2 6 2 2 2;
grid-columns: 25 25 25 25 225; grid-columns: 25 25 25 25 125;
height: 100%; height: 100%;
width: 100%; width: 100%;
padding: 1; padding: 1;
@ -79,6 +80,10 @@ class TrackDetailsScreen(Screen):
height: 100%; height: 100%;
border: solid green; border: solid green;
} }
.yellow {
tint: yellow 40%;
}
""" """
def __init__(self, trackDescriptor : TrackDescriptor = None, patternId = None, trackType : TrackType = None, index = None, subIndex = None): def __init__(self, trackDescriptor : TrackDescriptor = None, patternId = None, trackType : TrackType = None, index = None, subIndex = None):
@ -126,11 +131,14 @@ class TrackDetailsScreen(Screen):
def on_mount(self): def on_mount(self):
self.query_one("#index_label", Static).update(str(self.__index) if self.__index is not None else '-')
self.query_one("#subindex_label", Static).update(str(self.__subIndex)if self.__subIndex is not None else '-')
if self.__pattern is not None: if self.__pattern is not None:
self.query_one("#patternlabel", Static).update(self.__pattern.getPattern()) self.query_one("#pattern_label", Static).update(self.__pattern.getPattern())
if self.__subIndex is not None: if self.__trackType is not None:
self.query_one("#subindexlabel", Static).update(str(self.__subIndex)) self.query_one("#type_select", Select).value = self.__trackType.label()
for d in TrackDisposition: for d in TrackDisposition:
@ -153,7 +161,7 @@ class TrackDetailsScreen(Screen):
# Define the columns with headers # Define the columns with headers
self.column_key_track_tag_key = self.trackTagsTable.add_column("Key", width=10) self.column_key_track_tag_key = self.trackTagsTable.add_column("Key", width=10)
self.column_key_track_tag_value = self.trackTagsTable.add_column("Value", width=30) self.column_key_track_tag_value = self.trackTagsTable.add_column("Value", width=125)
self.trackTagsTable.cursor_type = 'row' self.trackTagsTable.cursor_type = 'row'
@ -165,71 +173,83 @@ class TrackDetailsScreen(Screen):
with Grid(): with Grid():
# 1 # 1
yield Static(f"New {self.__trackType.label()} stream" if self.__isNew else f"Edit {self.__trackType.label()} stream", id="toplabel", classes="five") yield Static(f"New stream" if self.__isNew else f"Edit stream", id="toplabel", classes="five")
# 2 # 2
yield Static("for pattern") yield Static("for pattern")
yield Static("", id="patternlabel", classes="four") yield Static("", id="pattern_label", classes="four")
# 3 # 3
yield Static("sub index") yield Static(" ", classes="five")
yield Static("", id="subindexlabel", classes="four")
# 4 # 4
yield Static(" ", classes="five") yield Static("Index / Subindex")
yield Static("", id="index_label", classes="two")
yield Static("", id="subindex_label", classes="two")
# 5 # 5
yield Static(" ", classes="five") yield Static(" ", classes="five")
# 6 # 6
yield Static("Type")
yield Select.from_values([t.label() for t in TrackType], classes="four", id="type_select")
# 7
yield Static(" ", classes="five")
# 9
yield Static(" ", classes="five")
# 10
yield Static("Language") yield Static("Language")
yield Select.from_values(languages, classes="four", id="language_select") yield Select.from_values(languages, classes="four", id="language_select")
# 7 # 11
yield Static(" ", classes="five") yield Static(" ", classes="five")
# 8 # 12
yield Static("Title") yield Static("Title")
yield Input(id="title_input", classes="four") yield Input(id="title_input", classes="four")
# 9 # 13
yield Static(" ", classes="five") yield Static(" ", classes="five")
# 10 # 14
yield Static(" ", classes="five") yield Static(" ", classes="five")
# 11 # 15
yield Static("Stream tags") yield Static("Stream tags")
yield Static(" ") yield Static(" ")
yield Button("Add", id="button_add_stream_tag") yield Button("Add", id="button_add_stream_tag")
yield Button("Edit", id="button_edit_stream_tag") yield Button("Edit", id="button_edit_stream_tag")
yield Button("Delete", id="button_delete_stream_tag") yield Button("Delete", id="button_delete_stream_tag")
# 12 # 16
yield self.trackTagsTable yield self.trackTagsTable
# 13 # 17
yield Static(" ", classes="five") yield Static(" ", classes="five")
# 14 # 18
yield Static("Stream dispositions", classes="five") yield Static("Stream dispositions", classes="five")
# 15 # 19
yield SelectionList[int]( yield SelectionList[int](
classes="five", classes="five",
id = "dispositions_selection_list" id = "dispositions_selection_list"
) )
# 16 # 20
yield Static(" ", classes="five") yield Static(" ", classes="five")
# 17 # 21
yield Static(" ", classes="five") yield Static(" ", classes="five")
# 18 # 22
yield Button("Save", id="save_button") yield Button("Save", id="save_button")
yield Button("Cancel", id="cancel_button") yield Button("Cancel", id="cancel_button")
# 19 # 23
yield Static(" ", classes="five") yield Static(" ", classes="five")
# 20 # 24
yield Static(" ", classes="five", id="messagestatic") yield Static(" ", classes="five", id="messagestatic")
@ -243,9 +263,9 @@ class TrackDetailsScreen(Screen):
kwargs[TrackDescriptor.PATTERN_ID_KEY] = int(self.__pattern.getId()) kwargs[TrackDescriptor.PATTERN_ID_KEY] = int(self.__pattern.getId())
kwargs[TrackDescriptor.INDEX_KEY] = self.__index kwargs[TrackDescriptor.INDEX_KEY] = self.__index
kwargs[TrackDescriptor.SUB_INDEX_KEY] = self.__subIndex kwargs[TrackDescriptor.SUB_INDEX_KEY] = self.__subIndex #!
kwargs[TrackDescriptor.TRACK_TYPE_KEY] = self.__trackType kwargs[TrackDescriptor.TRACK_TYPE_KEY] = TrackType.fromLabel(self.query_one("#type_select", Select).value)
trackTags = {} trackTags = {}
language = self.query_one("#language_select", Select).value language = self.query_one("#language_select", Select).value
@ -298,10 +318,10 @@ class TrackDetailsScreen(Screen):
# Check if the button pressed is the one we are interested in # 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()
# Check for multiple default/forced disposition flags # Check for multiple default/forced disposition flags
if self.__trackType == TrackType.VIDEO:
trackList = self.__tc.findVideoTracks(self.__pattern.getId())
if self.__trackType == TrackType.AUDIO: if self.__trackType == TrackType.AUDIO:
trackList = self.__tc.findAudioTracks(self.__pattern.getId()) trackList = self.__tc.findAudioTracks(self.__pattern.getId())
elif self.__trackType == TrackType.SUBTITLE: elif self.__trackType == TrackType.SUBTITLE:
@ -314,6 +334,9 @@ class TrackDetailsScreen(Screen):
numDefaultTracks = len([t for t in siblingTrackList if TrackDisposition.DEFAULT in t.getDispositionSet()]) 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()]) numForcedTracks = len([t for t in siblingTrackList if TrackDisposition.FORCED in t.getDispositionSet()])
self.__subIndex = len(trackList)
trackDescriptor = self.getTrackDescriptorFromInput()
if ((TrackDisposition.DEFAULT in trackDescriptor.getDispositionSet() and numDefaultTracks) if ((TrackDisposition.DEFAULT in trackDescriptor.getDispositionSet() and numDefaultTracks)
or (TrackDisposition.FORCED in trackDescriptor.getDispositionSet() and numForcedTracks)): or (TrackDisposition.FORCED in trackDescriptor.getDispositionSet() and numForcedTracks)):
@ -330,7 +353,7 @@ class TrackDetailsScreen(Screen):
else: else:
track = self.__tc.findTrack(self.__pattern.getId(), self.__trackType, self.__subIndex) track = self.__tc.getTrack(self.__pattern.getId(), self.__index)
if self.__tc.updateTrack(track.getId(), trackDescriptor): if self.__tc.updateTrack(track.getId(), trackDescriptor):
self.dismiss(trackDescriptor) self.dismiss(trackDescriptor)

Loading…
Cancel
Save