diff --git a/app/musicmuster.py b/app/musicmuster.py index c3b244d..f44235e 100755 --- a/app/musicmuster.py +++ b/app/musicmuster.py @@ -142,7 +142,7 @@ class CartButton(QPushButton): class PlaylistTrack: """ Used to provide a single reference point for specific playlist tracks, - typicall the previous, current and next track. + typically the previous, current and next track. """ def __init__(self) -> None: @@ -257,7 +257,7 @@ class MusicMusterSignals(QObject): emit-a-signal-from-another-class-to-main-class """ - update_row_note_signal = pyqtSignal(int) + set_next_track_signal = pyqtSignal(int, int) class Window(QMainWindow, Ui_MainWindow): @@ -579,8 +579,7 @@ class Window(QMainWindow, Ui_MainWindow): self.actionSelect_previous_track.triggered.connect( self.select_previous_row) self.actionMoveUnplayed.triggered.connect(self.move_unplayed) - self.actionSetNext.triggered.connect( - lambda: self.tabPlaylist.currentWidget().set_selected_as_next()) + self.actionSetNext.triggered.connect(self.set_next_track_from_mm) self.actionSkipToNext.triggered.connect(self.play_next) self.actionStop.triggered.connect(self.stop) self.btnDrop3db.clicked.connect(self.drop3db) @@ -1285,43 +1284,47 @@ class Window(QMainWindow, Ui_MainWindow): def resume(self) -> None: """ - Resume playing stopped track + Resume playing last track. We may be playing the next track + or none; take care of both eventualities. Actions required: - Return if no saved position - - Store saved position - - Store next track - - Set previous track to be next track - - Call play_next() from saved position - - Reset next track + - Resume last track + - If a track is playing, make that the next track """ # Return if no saved position if not self.previous_track_position: return - # Note resume point - resume_from = self.previous_track_position - - # Remember what was to have been the next track - original_next_plr_id = self.next_track.plr_id - original_next_plr_playlist_tab = self.next_track.playlist_tab + # Note any playing track as this will become the next track + playing_track = None + if self.current_track.track_id: + playing_track = self.current_track with Session() as session: - # Set next track to be the last one played - self.next_track = self.previous_track - self.previous_track = PlaylistTrack() + # Set next plr to be track to resume + plr_resume = session.get(PlaylistRows, self.previous_track.plr_id) + if not plr_resume: + return + plr_tab_resume = self.previous_track.playlist_tab + if not plr_tab_resume: + return # Resume last track - self.play_next(resume_from) + self._set_next_plr(session, plr_resume, plr_tab_resume) + self.play_next(self.previous_track_position) - # Reset next track if there was one - if original_next_plr_id: - next_plr = session.get(PlaylistRows, original_next_plr_id) - if not next_plr or not original_next_plr_playlist_tab: + # If a track was playing when we were called, get details to + # set it as the next track + if playing_track: + plr_next = session.get(PlaylistRows, playing_track.plr_id) + if not plr_next: return - self.this_is_the_next_playlist_row( - session, next_plr, original_next_plr_playlist_tab) + plr_tab_next = playing_track.playlist_tab + if not plr_tab_next: + return + self._set_next_plr(session, plr_next, plr_tab_next) def save_as_template(self) -> None: """Save current playlist as template""" @@ -1494,57 +1497,58 @@ class Window(QMainWindow, Ui_MainWindow): # May also be called when last tab is closed pass - def this_is_the_next_playlist_row( - self, session: scoped_session, plr: PlaylistRows, - next_track_playlist_tab: PlaylistTab) -> None: + def set_next_track_from_playlist(self, playlist_tab: PlaylistTab, + next_plr_id: int) -> None: """ - This is notification from a playlist tab that it holds the next - playlist row to be played. + Called from playlist tab to notify us of next track + """ + + with Session() as session: + next_plr = session.get(PlaylistRows, next_plr_id) + if not next_plr: + return + + self._set_next_plr(session, next_plr, playlist_tab) + + def set_next_track_from_mm(self) -> None: + """ + Set currently-selected row on visible playlist tab as next track Actions required: - - Clear next track if on other tab - - Reset tab colour if on other tab - - Note next playlist tab - - Set next next_track_playlist_tab tab colour - - Note next track + - Update self.next_track PlaylistTrack structure + - Set playlist tab colours + - Tell playlist tabs to update their 'next track' highlighting - Update headers - Populate ‘info’ tabs - """ - if plr.track_id is None: - return + with Session() as session: + playlist_tab = self.visible_playlist_tab() + selected_plrs = playlist_tab.get_selected_playlistrows(session) + if len(selected_plrs) != 1: + log.error(f"set_next_track:_from_mm {selected_plrs=}") + return + next_plr = selected_plrs[0] + self._set_next_plr(session, next_plr, playlist_tab) - # If we already have a next tab lined up and it's neither - # the "new" next tab nor the current track tab then we need - # to reset the tab colour. - if ( - self.next_track.playlist_tab and - self.next_track.playlist_tab != next_track_playlist_tab and - self.next_track.playlist_tab != self.current_track.playlist_tab - ): - self.set_tab_colour(self.next_track.playlist_tab, - QColor(Config.COLOUR_NORMAL_TAB)) + def _set_next_plr(self, session: scoped_session, + next_plr: PlaylistRows, + playlist_tab: PlaylistTab) -> None: + """ + Set next_plr as the next track to play + """ - # Discard now-incorrect next_track PlaylistTrack and tell - # playlist_tab too - self.next_track.playlist_tab.clear_next() - self.clear_next() + # Build PlaylistTrack structure for new next track + old_next_track = self.next_track + self.next_track = PlaylistTrack() + self.next_track.set_plr(session, next_plr, playlist_tab) - # Populate self.next_track - self.next_track.set_plr(session, plr, next_track_playlist_tab) - if self.next_track.playlist_tab: - if self.current_track.playlist_tab != self.next_track.playlist_tab: - self.set_tab_colour(self.next_track.playlist_tab, - QColor(Config.COLOUR_NEXT_TAB)) + # Set playlist tab colours + self._set_next_track_playlist_tab_colours(old_next_track) - # Populate footer if we're not currently playing - if not self.playing and self.next_track.track_id: - self.label_track_length.setText( - helpers.ms_to_mmss(self.next_track.duration) - ) - self.label_fade_length.setText(helpers.ms_to_mmss( - self.next_track.fade_length)) + # Tell playlist tabs to update themselves + self.signals.set_next_track_signal.emit(old_next_track.plr_id, + next_plr.id) # Update headers self.update_headers() @@ -1556,6 +1560,40 @@ class Window(QMainWindow, Ui_MainWindow): 1, lambda: self.tabInfolist.open_in_wikipedia(track_title) ) + def _set_next_track_playlist_tab_colours( + self, old_next_track: Optional[PlaylistTrack]) -> None: + """ + Set playlist tab colour for next track. self.next_track needs + to be set before calling. + """ + + # If the original next playlist tab isn't the same as the + # new one or the current track, it needs its colour reset. + if ( + old_next_track and + old_next_track.playlist_tab and + old_next_track.playlist_tab not in [ + self.next_track.playlist_tab, + self.current_track.playlist_tab + ]): + self.set_tab_colour(old_next_track.playlist_tab, + QColor(Config.COLOUR_NORMAL_TAB)) + # If the new next playlist tab isn't the same as the + # old one or the current track, it needs its colour set. + if old_next_track: + old_tab = old_next_track.playlist_tab + else: + old_tab = None + if ( + self.next_track and + self.next_track.playlist_tab and + self.next_track.playlist_tab not in [ + old_tab, + self.current_track.playlist_tab + ]): + self.set_tab_colour(self.next_track.playlist_tab, + QColor(Config.COLOUR_NEXT_TAB)) + def tick(self) -> None: """ Carry out clock tick actions. diff --git a/app/playlists.py b/app/playlists.py index 2bb4328..4c21cfd 100644 --- a/app/playlists.py +++ b/app/playlists.py @@ -191,13 +191,14 @@ class PlaylistTab(QTableWidget): # Call self.eventFilter() for events self.viewport().installEventFilter(self) - self.itemSelectionChanged.connect(self._select_event) - self.search_text: str = "" self.edit_cell_type: Optional[int] self.selecting_in_progress = False + # Connect signals self.horizontalHeader().sectionResized.connect(self._column_resize) + self.itemSelectionChanged.connect(self._select_event) + self.signals.set_next_track_signal.connect(self._reset_next) # Load playlist rows self.populate_display(session, self.playlist_id) @@ -647,7 +648,8 @@ class PlaylistTab(QTableWidget): # Set next track next_row = self._find_next_track_row(session, current_row + 1) if next_row: - self._set_next(session, next_row) + self.musicmuster.set_next_track_from_playlist( + self, self._get_row_plr_id(next_row)) # Display row as current track self._set_row_colour_current(current_row) @@ -732,7 +734,6 @@ class PlaylistTab(QTableWidget): row_number = self._get_next_track_row_number() if not row_number: return - self.musicmuster.clear_next() self._set_row_colour_default(row_number) self.clear_selection() @@ -868,14 +869,6 @@ class PlaylistTab(QTableWidget): self.selectRow(row_number) - def set_selected_as_next(self) -> None: - """Sets the select track as next to play""" - - row_number = self._get_selected_row() - if row_number is not None: - with Session() as session: - self._set_next(session, row_number) - def tab_visible(self) -> None: """Called when tab becomes visible""" @@ -890,7 +883,6 @@ class PlaylistTab(QTableWidget): def _add_track(self, row_number: int) -> None: """Add a track to a section header making it a normal track row""" - print(f"_add_track({row_number=})") with Session() as session: # Add track to playlist row plr = self._get_row_plr(session, row_number) @@ -1709,53 +1701,44 @@ class PlaylistTab(QTableWidget): return item - def _set_next(self, session: scoped_session, row_number: int) -> None: + def _reset_next(self, old_plrid: int, new_plrid: int) -> None: """ - Set passed row as next playlist row to play. + Called when set_next_track_signal signal received. Actions required: - - Check row has a track - - Check track is readable - - Notify musicmuster - - Display row as next track - - Update start/stop times + - If old_plrid points to this playlist: + - Remove existing next track + - If new_plrid points to this playlist: + - Set track as next + - Display row as next track + - Update start/stop times """ - # Check row has a track - track_id = self._get_row_track_id(row_number) - if not track_id: - log.error( - f"playlists._set_next({row_number=}) has no track associated" - ) - return + with Session() as session: + # Get plrs + old_plr = new_plr = None + if old_plrid: + old_plr = session.get(PlaylistRows, old_plrid) + if not new_plrid: + log.error(f"_reset_next called with {new_plrid=}") + return + new_plr = session.get(PlaylistRows, new_plrid) + if not new_plr: + log.error(f"_reset_next({new_plrid=}): plr not found") + return - track = session.get(Tracks, track_id) - if not track: - log.error(f"playlists._set_next({row_number=}): Track not found") - return + # Unmark next track + if old_plr and old_plr.playlist_id == self.playlist_id: + self._set_row_colour_default(old_plr.row_number) - # Check track is readable - if file_is_unreadable(track.path): - return None + # Mark next track + if new_plr and new_plr.playlist_id == self.playlist_id: + self._set_row_colour_next(new_plr.row_number) - # Clear any existing next track - next_track_row = self._get_next_track_row_number() - if next_track_row: - self._set_row_colour_default(next_track_row) + # Update start/stop times + self._update_start_end_times(session) - # Notify musicmuster - plr = self._get_row_plr(session, row_number) - if not plr: - log.debug(f"playists._set_next({row_number=}) can't retrieve plr") - else: - self.musicmuster.this_is_the_next_playlist_row(session, plr, self) - - # Display row as next track - self._set_row_colour_next(row_number) - - # Update start/stop times self.clear_selection() - self._update_start_end_times(session) def _set_played_row(self, session: scoped_session, row_number: int) -> None: