diff --git a/app/playlists.py b/app/playlists.py index 5e6af7e..030053d 100644 --- a/app/playlists.py +++ b/app/playlists.py @@ -1291,7 +1291,10 @@ class PlaylistTab(QTableWidget): return ' [' + duration + caveat + ']' def _get_selected_row(self) -> Optional[int]: - """Return row_number number of first selected row, or None if none selected""" + """ + Return row_number number of first selected row, + or None if none selected + """ if not self.selectionModel().hasSelection(): return None @@ -1304,7 +1307,8 @@ class PlaylistTab(QTableWidget): # Use a set to deduplicate result (a selected row will have all # items in that row selected) return sorted( - [row_number for row_number in set([a.row() for a in self.selectedItems()])] + [row_number for row_number in + set([a.row() for a in self.selectedItems()])] ) def _info_row(self, track_id: int) -> None: @@ -1387,6 +1391,7 @@ class PlaylistTab(QTableWidget): plr.row_number = new_row_number self.insert_row(session, plr) self.save_playlist(session) + self.hide_or_show_played_tracks() # Queue up time calculations to take place after UI has # updated QTimer.singleShot(0, self._update_start_end_times) @@ -1432,7 +1437,7 @@ class PlaylistTab(QTableWidget): return None - def _remove_track(self, row: int) -> None: + def _remove_track(self, row_number: int) -> None: """Remove track from row, making it a section header""" # Get confirmation @@ -1442,7 +1447,7 @@ class PlaylistTab(QTableWidget): # Update playlist_rows record with Session() as session: - plr = self._get_row_plr(session, row) + plr = self._get_row_plr(session, row_number) if not plr: return @@ -1454,47 +1459,47 @@ class PlaylistTab(QTableWidget): # Clear track text items for i in range(2, len(columns)): - _ = self._set_item_text(row, i, "") + _ = self._set_item_text(row_number, i, "") # Remove row duration - self._set_row_duration(row, 0) + self._set_row_duration(row_number, 0) # Remove row start gap - self._set_row_start_gap(row, None) + self._set_row_start_gap(row_number, None) # Remote track_id from row - _ = self._set_row_userdata(row, self.ROW_TRACK_ID, 0) + _ = self._set_row_userdata(row_number, self.ROW_TRACK_ID, 0) # Span the rows - self.setSpan(row, HEADER_NOTES_COLUMN, 1, len(columns) - 1) + self.setSpan(row_number, HEADER_NOTES_COLUMN, 1, len(columns) - 1) # Set note text in correct column for section head - self._set_row_header_text(session, row, plr.note) + self._set_row_header_text(session, row_number, plr.note) self.clear_selection() # Set track start/end times after track list is populated QTimer.singleShot(0, self._update_start_end_times) - def _rescan(self, row: int, track_id: int) -> None: + def _rescan(self, row_number: int, track_id: int) -> None: """Rescan track""" with Session() as session: track = session.get(Tracks, track_id) if track: if file_is_unreadable(track.path): - self._set_row_colour_unreadable(row) + self._set_row_colour_unreadable(row_number) else: - self._set_row_colour_default(row) + self._set_row_colour_default(row_number) set_track_metadata(session, track) - self._update_row_track_info(session, row, track) + self._update_row_track_info(session, row_number, track) else: - _ = self._set_row_track_id(row, 0) - note_text = self._get_row_note(row) + _ = self._set_row_track_id(row_number, 0) + note_text = self._get_row_note(row_number) if note_text is None: note_text = "" else: note_text += f"{track_id=} not found" - self._set_row_header_text(session, row, note_text) + self._set_row_header_text(session, row_number, note_text) log.error( f"playlists._rescan({track_id=}): " "Track not found" ) - self._set_row_colour_unreadable(row) + self._set_row_colour_unreadable(row_number) self._update_start_end_times() self.clear_selection() @@ -1504,25 +1509,24 @@ class PlaylistTab(QTableWidget): subprocess.call(args) - def _scroll_to_top(self, row: int) -> None: + def _scroll_to_top(self, row_number: int) -> None: """ - Scroll to put passed row Config.SCROLL_TOP_MARGIN from the + Scroll to put passed row_number Config.SCROLL_TOP_MARGIN from the top. """ - if row is None: + if row_number is None: return padding_required = Config.SCROLL_TOP_MARGIN - top_row = row + top_row = row_number - if row > Config.SCROLL_TOP_MARGIN: - # We can't scroll to a hidden row. Calculate target_row as the - # one that is ideal to be at the top. Then count upwards from - # passed row until we either reach the target, pass it or reach - # row 0. - # target_row = max(0, row - Config.SCROLL_TOP_MARGIN + 1) - for i in range(row - 1, -1, -1): + if row_number > Config.SCROLL_TOP_MARGIN: + # We can't scroll to a hidden row. Calculate target_row as + # the one that is ideal to be at the top. Then count upwards + # from passed row_number until we either reach the target, + # pass it or reach row_number 0. + for i in range(row_number - 1, -1, -1): if self.isRowHidden(i): continue if padding_required == 0: @@ -1558,39 +1562,39 @@ class PlaylistTab(QTableWidget): wrapped = False match_row = None - row = starting_row + row_number = starting_row needle = self.search_text.lower() while True: # Check for match in title, artist or notes - title = self._get_row_title(row) + title = self._get_row_title(row_number) if title and needle in title.lower(): - match_row = row + match_row = row_number break - artist = self._get_row_artist(row) + artist = self._get_row_artist(row_number) if artist and needle in artist.lower(): - match_row = row + match_row = row_number break - note = self._get_row_note(row) + note = self._get_row_note(row_number) if note and needle in note.lower(): - match_row = row + match_row = row_number break if next: - row += 1 - if wrapped and row >= starting_row: + row_number += 1 + if wrapped and row_number >= starting_row: break - if row >= self.rowCount(): - row = 0 + if row_number >= self.rowCount(): + row_number = 0 wrapped = True else: - row -= 1 - if wrapped and row <= starting_row: + row_number -= 1 + if wrapped and row_number <= starting_row: break - if row < 0: - row = self.rowCount() - 1 + if row_number < 0: + row_number = self.rowCount() - 1 wrapped = True if match_row is not None: - self.selectRow(row) + self.selectRow(row_number) def _select_event(self) -> None: """ @@ -1614,8 +1618,8 @@ class PlaylistTab(QTableWidget): QTimer.singleShot(0, lambda: self._wikipedia(selected_rows[0])) ms = 0 - for row in selected_rows: - ms += self._get_row_duration(row) + for row_number in selected_rows: + ms += self._get_row_duration(row_number) if ms > 0: self.musicmuster.lblSumPlaytime.setText( @@ -1623,7 +1627,7 @@ class PlaylistTab(QTableWidget): else: self.musicmuster.lblSumPlaytime.setText("") - def _set_cell_colour(self, row: int, column: int, + def _set_cell_colour(self, row_number: int, column: int, colour: Optional[str] = None) -> None: """ Set or reset a cell background colour @@ -1634,7 +1638,7 @@ class PlaylistTab(QTableWidget): else: brush = QBrush(QColor(colour)) - item = self.item(row, column) + item = self.item(row_number, column) if item: item.setBackground(brush) @@ -1654,7 +1658,7 @@ class PlaylistTab(QTableWidget): else: self.setColumnWidth(idx, Config.DEFAULT_COLUMN_WIDTH) - def _set_item_text(self, row: int, column: int, + def _set_item_text(self, row_number: int, column: int, text: Optional[str]) -> QTableWidgetItem: """ Set text for item if it exists, else create it, and return item @@ -1663,10 +1667,10 @@ class PlaylistTab(QTableWidget): if not text: text = "" - item = self.item(row, column) + item = self.item(row_number, column) if not item: item = QTableWidgetItem(text) - self.setItem(row, column, item) + self.setItem(row_number, column, item) else: item.setText(text) @@ -1720,20 +1724,21 @@ class PlaylistTab(QTableWidget): self.clear_selection() self._update_start_end_times() - def _set_played_row(self, session: scoped_session, row: int) -> None: + def _set_played_row(self, session: scoped_session, + row_number: int) -> None: """Mark this row as played""" - _ = self._set_row_userdata(row, self.PLAYED, True) - self._set_row_bold(row, False) + _ = self._set_row_userdata(row_number, self.PLAYED, True) + self._set_row_bold(row_number, False) - plr = self._get_row_plr(session, row) + plr = self._get_row_plr(session, row_number) if not plr: return plr.played = True session.flush() - def _set_row_artist(self, row: int, + def _set_row_artist(self, row_number: int, artist: Optional[str]) -> QTableWidgetItem: """ Set row artist. @@ -1744,9 +1749,9 @@ class PlaylistTab(QTableWidget): if not artist: artist = "" - return self._set_item_text(row, ARTIST, artist) + return self._set_item_text(row_number, ARTIST, artist) - def _set_row_bitrate(self, row: int, + def _set_row_bitrate(self, row_number: int, bitrate: Optional[int]) -> QTableWidgetItem: """Set bitrate of this row.""" @@ -1756,7 +1761,7 @@ class PlaylistTab(QTableWidget): bitrate = Config.BITRATE_LOW_THRESHOLD - 1 else: bitrate_str = str(bitrate) - bitrate_item = self._set_item_text(row, BITRATE, bitrate_str) + bitrate_item = self._set_item_text(row_number, BITRATE, bitrate_str) if bitrate < Config.BITRATE_LOW_THRESHOLD: cell_colour = Config.COLOUR_BITRATE_LOW @@ -1769,7 +1774,7 @@ class PlaylistTab(QTableWidget): return bitrate_item - def _set_row_bold(self, row: int, bold: bool = True) -> None: + def _set_row_bold(self, row_number: int, bold: bool = True) -> None: """ Make row bold (bold=True) or not bold. @@ -1781,11 +1786,11 @@ class PlaylistTab(QTableWidget): for column in range(self.columnCount()): if column == ROW_NOTES: continue - item = self.item(row, column) + item = self.item(row_number, column) if item: item.setFont(boldfont) - def _set_row_colour(self, row: int, + def _set_row_colour(self, row_number: int, colour: Optional[str] = None) -> None: """ Set or reset row background colour @@ -1799,48 +1804,49 @@ class PlaylistTab(QTableWidget): for column in range(1, self.columnCount()): if column in [START_GAP, BITRATE]: continue - item = self.item(row, column) + item = self.item(row_number, column) if item: item.setBackground(brush) - def _set_row_colour_current(self, row: int) -> None: + def _set_row_colour_current(self, row_number: int) -> None: """ Set current track row colour """ - self._set_row_colour(row, Config.COLOUR_CURRENT_PLAYLIST) + self._set_row_colour(row_number, Config.COLOUR_CURRENT_PLAYLIST) - def _set_row_colour_default(self, row: int) -> None: + def _set_row_colour_default(self, row_number: int) -> None: """ Set default row colour """ - self._set_row_colour(row, None) + self._set_row_colour(row_number, None) - def _set_row_colour_next(self, row: int) -> None: + def _set_row_colour_next(self, row_number: int) -> None: """ Set next track row colour """ - self._set_row_colour(row, Config.COLOUR_NEXT_PLAYLIST) + self._set_row_colour(row_number, Config.COLOUR_NEXT_PLAYLIST) - def _set_row_colour_unreadable(self, row: int) -> None: + def _set_row_colour_unreadable(self, row_number: int) -> None: """ Set unreadable row colour """ - self._set_row_colour(row, Config.COLOUR_UNREADABLE) + self._set_row_colour(row_number, Config.COLOUR_UNREADABLE) - def _set_row_duration(self, row: int, + def _set_row_duration(self, row_number: int, ms: Optional[int]) -> QTableWidgetItem: """Set duration of this row. Also set in row metadata""" - duration_item = self._set_item_text(row, DURATION, ms_to_mmss(ms)) - self._set_row_userdata(row, self.ROW_DURATION, ms) + duration_item = self._set_item_text( + row_number, DURATION, ms_to_mmss(ms)) + self._set_row_userdata(row_number, self.ROW_DURATION, ms) return duration_item - def _set_row_end_time(self, row: int, + def _set_row_end_time(self, row_number: int, time: Optional[datetime]) -> QTableWidgetItem: """Set row end time""" @@ -1852,7 +1858,7 @@ class PlaylistTab(QTableWidget): except AttributeError: time_str = "" - return self._set_item_text(row, END_TIME, time_str) + return self._set_item_text(row_number, END_TIME, time_str) def _set_row_header_text(self, session: scoped_session, row_number: int, text: str) -> None: @@ -1880,18 +1886,14 @@ class PlaylistTab(QTableWidget): self._set_row_colour(row_number, note_colour) - def _set_row_last_played(self, row: int, last_played: Optional[datetime]) \ - -> QTableWidgetItem: + def _set_row_last_played_time( + self, row_number: int, + last_played: Optional[datetime]) -> QTableWidgetItem: """Set row last played time""" last_played_str = get_relative_date(last_played) - return self._set_item_text(row, LASTPLAYED, last_played_str) - - def _set_row_not_bold(self, row: int) -> None: - """Set row to not be bold""" - - self._set_row_bold(row, False) + return self._set_item_text(row_number, LASTPLAYED, last_played_str) def _set_row_note_colour(self, session: scoped_session, row_number: int) -> None: @@ -1936,14 +1938,15 @@ class PlaylistTab(QTableWidget): # Set colour self._set_row_note_colour(session, row_number) - def _set_row_plr_id(self, row: int, plr_id: int) -> QTableWidgetItem: + def _set_row_plr_id(self, row_number: int, + plr_id: int) -> QTableWidgetItem: """ Set PlaylistRows id """ - return self._set_row_userdata(row, self.PLAYLISTROW_ID, plr_id) + return self._set_row_userdata(row_number, self.PLAYLISTROW_ID, plr_id) - def _set_row_start_gap(self, row: int, + def _set_row_start_gap(self, row_number: int, start_gap: Optional[int]) -> QTableWidgetItem: """ Set start gap on row, set backgroud colour. @@ -1953,15 +1956,17 @@ class PlaylistTab(QTableWidget): if not start_gap: start_gap = 0 - start_gap_item = self._set_item_text(row, START_GAP, str(start_gap)) + start_gap_item = self._set_item_text( + row_number, START_GAP, str(start_gap)) if start_gap >= 500: - start_gap_item.setBackground(QColor(Config.COLOUR_LONG_START)) + brush = QBrush(QColor(Config.COLOUR_LONG_START)) else: - start_gap_item.setBackground(QColor("white")) + brush = QBrush() + start_gap_item.setBackground(brush) return start_gap_item - def _set_row_start_time(self, row: int, + def _set_row_start_time(self, row_number: int, time: Optional[datetime]) -> QTableWidgetItem: """Set row start time""" @@ -1973,21 +1978,21 @@ class PlaylistTab(QTableWidget): except AttributeError: time_str = "" - return self._set_item_text(row, START_TIME, time_str) + return self._set_item_text(row_number, START_TIME, time_str) - def _set_row_times(self, row: int, start: datetime, + def _set_row_times(self, row_number: int, start: datetime, duration: int) -> Optional[datetime]: """ Set row start and end times, return end time """ - self._set_row_start_time(row, start) + self._set_row_start_time(row_number, start) end_time = self._calculate_end_time(start, duration) - self._set_row_end_time(row, end_time) + self._set_row_end_time(row_number, end_time) return end_time - def _set_row_title(self, row: int, + def _set_row_title(self, row_number: int, title: Optional[str]) -> QTableWidgetItem: """ Set row title. @@ -1996,33 +2001,35 @@ class PlaylistTab(QTableWidget): if not title: title = "" - return self._set_item_text(row, TITLE, title) + return self._set_item_text(row_number, TITLE, title) - def _set_row_track_id(self, row: int, track_id: int) -> QTableWidgetItem: + def _set_row_track_id(self, row_number: int, + track_id: int) -> QTableWidgetItem: """ Set track id """ - return self._set_row_userdata(row, self.ROW_TRACK_ID, track_id) + return self._set_row_userdata(row_number, self.ROW_TRACK_ID, track_id) - def _set_row_track_path(self, row: int, path: str) -> QTableWidgetItem: + def _set_row_track_path(self, row_number: int, + path: str) -> QTableWidgetItem: """ Set track path """ - return self._set_row_userdata(row, self.TRACK_PATH, path) + return self._set_row_userdata(row_number, self.TRACK_PATH, path) - def _set_row_userdata(self, row: int, role: int, + def _set_row_userdata(self, row_number: int, role: int, value: Optional[Union[str, int]]) \ -> QTableWidgetItem: """ Set passed userdata in USERDATA column """ - item = self.item(row, USERDATA) + item = self.item(row_number, USERDATA) if not item: item = QTableWidgetItem() - self.setItem(row, USERDATA, item) + self.setItem(row_number, USERDATA, item) item.setData(role, value) @@ -2062,7 +2069,7 @@ class PlaylistTab(QTableWidget): _ = self._set_row_bitrate(row, track.bitrate) _ = self._set_row_duration(row, track.duration) _ = self._set_row_end_time(row, None) - _ = self._set_row_last_played( + _ = self._set_row_last_played_time( row, Playdates.last_played(session, track.id)) _ = self._set_row_start_gap(row, track.start_gap) _ = self._set_row_start_time(row, None) @@ -2141,39 +2148,40 @@ class PlaylistTab(QTableWidget): next_track_row = self._get_next_track_row_number() played_rows = self._get_played_rows(session) - for row in range(self.rowCount()): + for row_number in range(self.rowCount()): # Don't change start times for tracks that have been # played other than current/next row - if row in played_rows and row not in [ + if row_number in played_rows and row_number not in [ current_track_row, next_track_row]: continue # Get any timing from header row (that's all we need) - if self._get_row_track_id(row) == 0: + if self._get_row_track_id(row_number) == 0: note_time = self._get_note_text_time( - self._get_row_note(row)) + self._get_row_note(row_number)) if note_time: next_start_time = note_time continue # We have a track. Skip if it is unreadable - if file_is_unreadable(self._get_row_path(row)): + if file_is_unreadable(self._get_row_path(row_number)): continue # Set next track start from end of current track - if row == next_track_row: + if row_number == next_track_row: if current_track_end_time: next_start_time = self._set_row_times( - row, current_track_end_time, - self._get_row_duration(row)) + row_number, current_track_end_time, + self._get_row_duration(row_number)) continue # Else set track times below - if row == current_track_row: + if row_number == current_track_row: if not current_track_start_time: continue - self._set_row_start_time(row, current_track_start_time) - self._set_row_end_time(row, current_track_end_time) + self._set_row_start_time(row_number, + current_track_start_time) + self._set_row_end_time(row_number, current_track_end_time) # Next track may be above us so only reset # next_start_time if it's not set if not next_start_time: @@ -2182,19 +2190,20 @@ class PlaylistTab(QTableWidget): if not next_start_time: # Clear any existing times - self._set_row_start_time(row, None) - self._set_row_end_time(row, None) + self._set_row_start_time(row_number, None) + self._set_row_end_time(row_number, None) continue # If we're between the current and next row, zero out # times if (current_track_row and next_track_row and - current_track_row < row < next_track_row): - self._set_row_start_time(row, None) - self._set_row_end_time(row, None) + current_track_row < row_number < next_track_row): + self._set_row_start_time(row_number, None) + self._set_row_end_time(row_number, None) else: next_start_time = self._set_row_times( - row, next_start_time, self._get_row_duration(row)) + row_number, next_start_time, + self._get_row_duration(row_number)) self._update_section_headers(session)