From bc54be237b0e82113dac42e417f81e51b01a92ab Mon Sep 17 00:00:00 2001 From: Keith Edmunds Date: Tue, 10 Aug 2021 08:18:05 +0100 Subject: [PATCH] Check tracks for readability Check on load and on setting next track. Also provide info popup that shows path. --- app/config.py | 1 + app/musicmuster.py | 7 ++++- app/playlists.py | 70 ++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 68 insertions(+), 10 deletions(-) diff --git a/app/config.py b/app/config.py index d95a620..547ba36 100644 --- a/app/config.py +++ b/app/config.py @@ -14,6 +14,7 @@ class Config(object): COLOUR_NEXT_PLAYLIST = "#ffc107" COLOUR_NOTES_PLAYLIST = "#b8daff" COLOUR_PREVIOUS_HEADER = "#f8d7da" + COLOUR_UNREADABLE = "#dc3545" COLOUR_WARNING_TIMER = "#ffc107" DBFS_FADE = -12 DBFS_SILENCE = -50 diff --git a/app/musicmuster.py b/app/musicmuster.py index fb4d557..2a1d9eb 100755 --- a/app/musicmuster.py +++ b/app/musicmuster.py @@ -291,6 +291,11 @@ class Window(QMainWindow, Ui_MainWindow): self.previous_track_position = self.music.fade() self.end_of_track_actions() + def file_is_readable(self, path): + "Return True if path is readable else False" + + return os.access(self.next_track.path, os.R_OK) + def insert_note(self): "Add non-track row to playlist" @@ -415,7 +420,7 @@ class Window(QMainWindow, Ui_MainWindow): self.next_track = Tracks.get_track(session, next_track_id) self.next_track_playlist_tab = self.current_track_playlist_tab # Check we can read it - if not os.access(self.next_track.path, os.R_OK): + if not self.file_is_readable(self.next_track.path, os.R_OK): self.show_warning( "Can't read next track", self.next_track.path) diff --git a/app/playlists.py b/app/playlists.py index 7be5a04..be172a6 100644 --- a/app/playlists.py +++ b/app/playlists.py @@ -155,6 +155,8 @@ class PlaylistTab(QTableWidget): self.menu.addSeparator() act_delete = self.menu.addAction('Delete') act_delete.triggered.connect(lambda: self._delete_row(row)) + act_delete = self.menu.addAction('Info') + act_delete.triggered.connect(lambda: self._info_row(row)) return super(PlaylistTab, self).eventFilter(source, event) @@ -274,6 +276,9 @@ class PlaylistTab(QTableWidget): # Scroll to new row self.scrollToItem(titleitem, QAbstractItemView.PositionAtCenter) + if not self._track_path_is_readable(track.id): + self._meta_set_unreadable(row) + if repaint: self._save_playlist(session) self._repaint(clear_selection=False) @@ -529,11 +534,6 @@ class PlaylistTab(QTableWidget): duration = Tracks.get_duration(session, self._get_row_id(row)) return start + timedelta(milliseconds=duration) - def _can_read_track(self, track): - "Check track file is readable" - - return os.access(track.path, os.R_OK) - def _context_menu(self, pos): self.menu.exec_(self.mapToGlobal(pos)) @@ -608,6 +608,30 @@ class PlaylistTab(QTableWidget): except ValueError: return None + def _info_row(self, row): + "Display popup with info re row" + + id = self._get_row_id(row) + if row in self._meta_get_notes(): + note_text = self.item(row, self.COL_TITLE).text() + txt = f"Note: {note_text}" + else: + with Session() as session: + track = Tracks.get_track(session, id) + if not track: + txt = f"Track not found (track.id={id})" + else: + txt = f""" + Title: {track.title}\n + Artist: {track.artist}\n + Path: {track.path}""" + info = QMessageBox(self) + info.setIcon(QMessageBox.Information) + info.setText(txt) + info.setStandardButtons(QMessageBox.Ok) + info.setDefaultButton(QMessageBox.Cancel) + info.exec() + def _is_below(self, pos, index): rect = self.visualRect(index) margin = 2 @@ -724,6 +748,11 @@ class PlaylistTab(QTableWidget): return self._meta_find("note", one=False) + def _meta_get_unreadable(self): + "Return rows marked as unreadable, or None" + + return self._meta_find("unreadable", one=False) + def _meta_set_current(self, row): "Mark row as current track" @@ -745,6 +774,11 @@ class PlaylistTab(QTableWidget): self._meta_set(row, "note") + def _meta_set_unreadable(self, row): + "Mark row as unreadable" + + self._meta_set(row, "unreadable") + def _meta_set(self, row, metadata): "Set row metadata" @@ -763,8 +797,9 @@ class PlaylistTab(QTableWidget): def _set_next(self, row): """ - If passed row is track row, set that track as the next track to - be played and return track_id. Otherwise return None. + If passed row is track row, check track is readable and, if it is, + set that track as the next track to be played and return track_id. + Otherwise return None. """ DEBUG(f"_set_next({row})") @@ -774,9 +809,12 @@ class PlaylistTab(QTableWidget): track_id = self._get_row_id(row) if track_id: - self._meta_set_next(self.currentRow()) + if self._track_path_is_readable(track_id): + self._meta_set_next(self.currentRow()) + self.master_process.set_next_track(track_id) + else: + self._meta_set_unreadable(self.currentRow()) self._repaint() - self.master_process.set_next_track(track_id) def _repaint(self, clear_selection=True): "Set row colours, fonts, etc" @@ -793,6 +831,7 @@ class PlaylistTab(QTableWidget): current = self._meta_get_current() next = self._meta_get_next() notes = self._meta_get_notes() + unreadable = self._meta_get_unreadable() # Set colours and start times next_start_time = None @@ -813,6 +852,13 @@ class PlaylistTab(QTableWidget): ) self._set_row_bold(row) + elif row in unreadable: + # Set colour + self._set_row_colour( + row, QColor(Config.COLOUR_UNREADABLE) + ) + self._set_row_bold(row) + elif row == current: # Set start time self._set_row_start_time( @@ -990,3 +1036,9 @@ class PlaylistTab(QTableWidget): time_str = "" item = QTableWidgetItem(time_str) self.setItem(row, self.COL_START_TIME, item) + + def _track_path_is_readable(self, track_id): + "Returns True if track path is readable, else False" + + with Session() as session: + return os.access(Tracks.get_path(session, track_id), os.R_OK)