From 4eabf4a02a2b36db9cbe3f8f33f0ab3c71c354f9 Mon Sep 17 00:00:00 2001 From: Keith Edmunds Date: Mon, 27 Nov 2023 21:46:19 +0000 Subject: [PATCH] WIP V3: ready for testing --- app/musicmuster.py | 87 +--------- app/playlistmodel.py | 17 +- app/playlists.py | 370 +------------------------------------------ 3 files changed, 16 insertions(+), 458 deletions(-) diff --git a/app/musicmuster.py b/app/musicmuster.py index eac669e..520d895 100755 --- a/app/musicmuster.py +++ b/app/musicmuster.py @@ -868,7 +868,7 @@ class Window(QMainWindow, Ui_MainWindow): dlg = TrackSelectDialog( session=session, new_row_number=self.active_tab().selected_model_row_number(), - model=self.active_model() + model=self.active_model(), ) dlg.exec() @@ -1067,12 +1067,6 @@ class Window(QMainWindow, Ui_MainWindow): # Clear next track self.clear_next() - # Set current track playlist_tab colour - # TODO Reimplement without reference to self.current_track.playlist_tab - # current_tab = self.current_track.playlist_tab - # if current_tab: - # self.set_tab_colour(current_tab, QColor(Config.COLOUR_CURRENT_TAB)) - # Restore volume if -3dB active if self.btnDrop3db.isChecked(): self.btnDrop3db.setChecked(False) @@ -1422,85 +1416,6 @@ class Window(QMainWindow, Ui_MainWindow): # Enable controls self.enable_play_next_controls() - def set_next_plr_id( - self, next_plr_id: Optional[int], playlist_tab: PlaylistTab - ) -> None: - """ - Set passed plr_id as next track to play, or clear next track if None - - Actions required: - - Update playing_track - - Tell playlist tabs to update their 'next track' highlighting - - Update headers - - Set playlist tab colours - - Populate ‘info’ tabs - """ - - return - # with Session() as session: - # # Update self.next_track PlaylistTrack structure - # self.next_track = NextTrack() - # if next_plr_id: - # next_plr = session.get(PlaylistRows, next_plr_id) - # if next_plr: - # self.next_track.set_plr(session, next_plr) - # self.signals.set_next_track_signal.emit(next_plr.playlist_id) - - # # Update headers - # self.update_headers() - - # TODO: reimlement - # # Set playlist tab colours - # self._set_next_track_playlist_tab_colours(old_next_track) - - # if next_plr_id: - # # Populate 'info' tabs with Wikipedia info, but queue it - # # because it isn't quick - # if self.next_track.title: - # QTimer.singleShot( - # 0, - # lambda: self.tabInfolist.open_in_wikipedia( - # self.next_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. - return - # TODO Reimplement - # 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_10ms(self) -> None: """ Called every 10ms diff --git a/app/playlistmodel.py b/app/playlistmodel.py index 8bbdefb..0d0f673 100644 --- a/app/playlistmodel.py +++ b/app/playlistmodel.py @@ -5,7 +5,7 @@ from datetime import datetime, timedelta from enum import auto, Enum from operator import attrgetter from pprint import pprint -from typing import cast, List, Optional +from typing import List, Optional from PyQt6.QtCore import ( QAbstractTableModel, @@ -144,10 +144,7 @@ class PlaylistModel(QAbstractTableModel): ) def add_track_to_header( - self, - row_number: int, - track_id: int, - note: Optional[str] = None + self, row_number: int, track_id: int, note: Optional[str] = None ) -> None: """ Add track to existing header row @@ -934,7 +931,7 @@ class PlaylistModel(QAbstractTableModel): except obs.error.OBSSDKError as e: log.error(f"OBS SDK error ({e})") return - + def open_in_audacity(self, row_number: int) -> None: """ Open track at passed row number in Audacity @@ -1423,12 +1420,16 @@ class PlaylistProxyModel(QSortFilterProxyModel): def move_track_add_note( self, new_row_number: int, existing_prd: PlaylistRowData, note: str ) -> None: - return self.playlist_model.move_track_add_note(new_row_number, existing_prd, note) + return self.playlist_model.move_track_add_note( + new_row_number, existing_prd, note + ) def move_track_to_header( self, header_row_number: int, existing_prd: PlaylistRowData, note: Optional[str] ) -> None: - return self.playlist_model.move_track_to_header(header_row_number, existing_prd) + return self.playlist_model.move_track_to_header( + header_row_number, existing_prd, note + ) def open_in_audacity(self, row_number: int) -> None: return self.playlist_model.open_in_audacity(row_number) diff --git a/app/playlists.py b/app/playlists.py index f188a5c..c3a21db 100644 --- a/app/playlists.py +++ b/app/playlists.py @@ -1,8 +1,3 @@ -import subprocess - -import obsws_python as obs # type: ignore - -from datetime import datetime, timedelta from pprint import pprint from typing import Callable, cast, List, Optional, TYPE_CHECKING @@ -11,11 +6,9 @@ from PyQt6.QtCore import ( QModelIndex, QObject, QItemSelection, - QItemSelectionModel, Qt, - # QTimer, ) -from PyQt6.QtGui import QAction, QDropEvent, QKeyEvent +from PyQt6.QtGui import QAction, QKeyEvent from PyQt6.QtWidgets import ( QAbstractItemDelegate, QAbstractItemView, @@ -34,29 +27,21 @@ from PyQt6.QtWidgets import ( QStyleOption, ) -from dbconfig import Session, scoped_session +from dbconfig import Session from dialogs import TrackSelectDialog from classes import MusicMusterSignals, track_sequence from config import Config from helpers import ( ask_yes_no, - file_is_unreadable, - get_relative_date, ms_to_mmss, - open_in_audacity, - send_mail, - set_track_metadata, show_warning, ) -from log import log -from models import PlaylistRows, Settings, Tracks, NoteColours +from models import Settings if TYPE_CHECKING: from musicmuster import Window from playlistmodel import PlaylistModel, PlaylistProxyModel -# HEADER_NOTES_COLUMN = 2 - class EscapeDelegate(QStyledItemDelegate): """ @@ -180,7 +165,6 @@ class PlaylistTab(QTableView): self.proxy_model = PlaylistProxyModel(self.playlist_model) self.setItemDelegate(EscapeDelegate(self, self.playlist_model)) self.setAlternatingRowColors(True) - # self.setEditTriggers(QAbstractItemView.EditTrigger.DoubleClicked) self.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel) self.setDragDropMode(QAbstractItemView.DragDropMode.InternalMove) self.setDragDropOverwriteMode(False) @@ -205,13 +189,9 @@ class PlaylistTab(QTableView): self.signals = MusicMusterSignals() self.signals.span_cells_signal.connect(self._span_cells) - # Call self.eventFilter() for events - # self.installEventFilter(self) - # Initialise miscellaneous instance variables self.search_text: str = "" self.sort_undo: List[int] = [] - # self.edit_cell_type: Optional[int] # Selection model self.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection) @@ -358,7 +338,7 @@ class PlaylistTab(QTableView): return self.proxy_model.mapToSource(selected_index).row() return selected_index.row() - def selected_model_row_numbers(self) -> Optional[List[int]]: + def selected_model_row_numbers(self) -> List[int]: """ Return a list of model row numbers corresponding to the selected rows or an empty list. @@ -366,7 +346,7 @@ class PlaylistTab(QTableView): selected_indexes = self._selected_row_indexes() if selected_indexes is None: - return None + return [] if hasattr(self.proxy_model, "mapToSource"): return [self.proxy_model.mapToSource(a).row() for a in selected_indexes] return [a.row() for a in selected_indexes] @@ -407,137 +387,6 @@ class PlaylistTab(QTableView): return "" return self.playlist_model.get_row_track_path(model_row_number) - # def lookup_row_in_songfacts(self) -> None: - # """ - # If there is a selected row and it is a track row, - # look up its title in songfacts. - - # If multiple rows are selected, only consider the first one. - - # Otherwise return. - # """ - - # self._look_up_row(website="songfacts") - - # def lookup_row_in_wikipedia(self) -> None: - # """ - # If there is a selected row and it is a track row, - # look up its title in wikipedia. - - # If multiple rows are selected, only consider the first one. - - # Otherwise return. - # """ - - # self._look_up_row(website="wikipedia") - - # def scroll_current_to_top(self) -> None: - # """Scroll currently-playing row to top""" - - # current_row = track_sequence.now.plr_rownum - # if current_row is not None: - # self._scroll_to_top(current_row) - - # def scroll_next_to_top(self) -> None: - # """Scroll nextly-playing row to top""" - - # next_row = track_sequence.now.plr_rownum - # if next_row is not None: - # self._scroll_to_top(next_row) - - def set_search(self, text: str) -> None: - """Set search text and find first match""" - - self.search_text = text - if not text: - # Search string has been reset - return - self._search(next=True) - - # def search_next(self) -> None: - # """ - # Select next row containg self.search_string. - # """ - - # self._search(next=True) - - # def search_previous(self) -> None: - # """ - # Select previous row containg self.search_string. - # """ - - # self._search(next=False) - - # def select_next_row(self) -> None: - # """ - # Select next or first row. Don't select section headers. - - # Wrap at last row. - # """ - - # selected_rows = self._get_selected_rows() - # # we will only handle zero or one selected rows - # if len(selected_rows) > 1: - # return - # # select first row if none selected - # if len(selected_rows) == 0: - # row_number = 0 - # else: - # row_number = selected_rows[0] + 1 - # if row_number >= self.rowCount(): - # row_number = 0 - - # # Don't select section headers - # wrapped = False - # track_id = self._get_row_track_id(row_number) - # while not track_id: - # row_number += 1 - # if row_number >= self.rowCount(): - # if wrapped: - # # we're already wrapped once, so there are no - # # non-headers - # return - # row_number = 0 - # wrapped = True - # track_id = self._get_row_track_id(row_number) - - # self.selectRow(row_number) - - # def select_previous_row(self) -> None: - # """ - # Select previous or last track. Don't select section headers. - # Wrap at first row. - # """ - - # selected_rows = self._get_selected_rows() - # # we will only handle zero or one selected rows - # if len(selected_rows) > 1: - # return - # # select last row if none selected - # last_row = self.rowCount() - 1 - # if len(selected_rows) == 0: - # row_number = last_row - # else: - # row_number = selected_rows[0] - 1 - # if row_number < 0: - # row_number = last_row - - # # Don't select section headers - # wrapped = False - # track_id = self._get_row_track_id(row_number) - # while not track_id: - # row_number -= 1 - # if row_number < 0: - # if wrapped: - # # we're already wrapped once, so there are no - # # non-notes - # return - # row_number = last_row - # wrapped = True - # track_id = self._get_row_track_id(row_number) - - # self.selectRow(row_number) - def set_row_as_next_track(self) -> None: """ Set selected row as next track @@ -660,22 +509,12 @@ class PlaylistTab(QTableView): if track_row: self._add_context_menu("Info", lambda: self._info_row(model_row_number)) - # Track path TODO + # Track path if track_row: self._add_context_menu( "Copy track path", lambda: self._copy_path(model_row_number) ) - def _calculate_end_time( - self, start: Optional[datetime], duration: int - ) -> Optional[datetime]: - """Return datetime 'duration' ms after 'start'""" - - if start is None: - return None - - return start + timedelta(milliseconds=duration) - def _column_resize(self, column_number: int, _old: int, _new: int) -> None: """ Called when column width changes. Save new width to database. @@ -776,132 +615,18 @@ class PlaylistTab(QTableView): info.setDefaultButton(QMessageBox.StandardButton.Cancel) info.exec() - def _look_up_row(self, website: str) -> None: - """ - If there is a selected row and it is a track row, - look up its title in the passed website - - If multiple rows are selected, only consider the first one. - - Otherwise return. - """ - - print("playlists_v3:_look_up_row()") - return - # selected_row = self._get_selected_row() - # if not selected_row: - # return - - # if not self._get_row_track_id(selected_row): - # return - - # title = self._get_row_title(selected_row) - - # if website == "wikipedia": - # QTimer.singleShot( - # 0, lambda: self.musicmuster.tabInfolist.open_in_wikipedia(title) - # ) - # elif website == "songfacts": - # QTimer.singleShot( - # 0, lambda: self.musicmuster.tabInfolist.open_in_songfacts(title) - # ) - # else: - # return - def _mark_as_unplayed(self, row_numbers: List[int]) -> None: """Rescan track""" self.playlist_model.mark_unplayed(row_numbers) self.clear_selection() - def _obs_change_scene(self, current_row: int) -> None: - """ - Try to change OBS scene to the name passed - """ - - check_row = current_row - while True: - # If we have a note and it has a scene change command, - # execute it - note_text = self._get_row_note(check_row) - if note_text: - match_obj = scene_change_re.search(note_text) - if match_obj: - scene_name = match_obj.group(1) - if scene_name: - try: - cl = obs.ReqClient( - host=Config.OBS_HOST, - port=Config.OBS_PORT, - password=Config.OBS_PASSWORD, - ) - except ConnectionRefusedError: - log.error("OBS connection refused") - return - - try: - cl.set_current_program_scene(scene_name) - log.info(f"OBS scene changed to '{scene_name}'") - return - except obs.error.OBSSDKError as e: - log.error(f"OBS SDK error ({e})") - return - # After current track row, only check header rows and stop - # at first non-header row - check_row -= 1 - if check_row < 0: - break - if self._get_row_track_id(check_row): - break - def _rescan(self, row_number: int) -> None: """Rescan track""" self.playlist_model.rescan_track(row_number) self.clear_selection() - # def _reset_next(self, old_plrid: int, new_plrid: int) -> None: - # """ - # Called when set_next_track_signal signal received. - - # Actions required: - # - 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 - # """ - - # with Session() as session: - # # Get plrs - # old_plr = new_plr = None - # if old_plrid: - # old_plr = session.get(PlaylistRows, old_plrid) - - # # Unmark next track - # if old_plr and old_plr.playlist_id == self.playlist_id: - # self._set_row_colour_default(old_plr.plr_rownum) - - # # Mark next track - # if new_plrid: - # new_plr = session.get(PlaylistRows, new_plrid) - # if not new_plr: - # log.error(f"_reset_next({new_plrid=}): plr not found") - # return - # if new_plr.playlist_id == self.playlist_id: - # self._set_row_colour_next(new_plr.plr_rownum) - - # # Update start/stop times - # self._update_start_end_times(session) - - # self.clear_selection() - - def _run_subprocess(self, args): - """Run args in subprocess""" - - subprocess.call(args) - def scroll_to_top(self, row_number: int) -> None: """ Scroll to put passed row_number Config.SCROLL_TOP_MARGIN from the @@ -914,65 +639,6 @@ class PlaylistTab(QTableView): row_index = self.proxy_model.index(row_number, 0) self.scrollTo(row_index, QAbstractItemView.ScrollHint.PositionAtTop) - # def _search(self, next: bool = True) -> None: - # """ - # Select next/previous row containg self.search_string. Start from - # top selected row if there is one, else from top. - - # Wrap at last/first row. - # """ - - # if not self.search_text: - # return - - # selected_row = self._get_selected_row() - # if next: - # if selected_row is not None and selected_row < self.rowCount() - 1: - # starting_row = selected_row + 1 - # else: - # starting_row = 0 - # else: - # if selected_row is not None and selected_row > 0: - # starting_row = selected_row - 1 - # else: - # starting_row = self.rowCount() - 1 - - # wrapped = False - # match_row = None - # 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_number) - # if title and needle in title.lower(): - # match_row = row_number - # break - # artist = self._get_row_artist(row_number) - # if artist and needle in artist.lower(): - # match_row = row_number - # break - # note = self._get_row_note(row_number) - # if note and needle in note.lower(): - # match_row = row_number - # break - # if next: - # row_number += 1 - # if wrapped and row_number >= starting_row: - # break - # if row_number >= self.rowCount(): - # row_number = 0 - # wrapped = True - # else: - # row_number -= 1 - # if wrapped and row_number <= starting_row: - # break - # if row_number < 0: - # row_number = self.rowCount() - 1 - # wrapped = True - - # if match_row is not None: - # self.selectRow(row_number) - def select_duplicate_rows(self) -> None: """ Select the last of any rows with duplicate tracks in current playlist. @@ -1036,30 +702,6 @@ class PlaylistTab(QTableView): else: self.setColumnWidth(column_number, Config.DEFAULT_COLUMN_WIDTH) - # def _set_row_note_colour(self, session: scoped_session, row_number: int) -> None: - # """ - # Set row note colour - # """ - - # # Sanity check: this should be a track row and thus have a - # # track associated - # if not self._get_row_track_id(row_number): - # if os.environ["MM_ENV"] == "PRODUCTION": - # send_mail( - # Config.ERRORS_TO, - # Config.ERRORS_FROM, - # "playlists:_set_row_note_colour() on header row", - # stackprinter.format(), - # ) - # # stackprinter.show(add_summary=True, style="darkbg") - # print(f"playists:_set_row_note_colour() called on track row ({row_number=}") - # return - - # # Set colour - # note_text = self._get_row_note(row_number) - # note_colour = NoteColours.get_colour(session, note_text) - # self._set_cell_colour(row_number, ROW_NOTES, note_colour) - def _span_cells(self, row: int, column: int, rowSpan: int, columnSpan: int) -> None: """ Implement spanning of cells, initiated by signal