WIP V3: ready for testing

This commit is contained in:
Keith Edmunds 2023-11-27 21:46:19 +00:00
parent 00d7258afd
commit 4eabf4a02a
3 changed files with 16 additions and 458 deletions

View File

@ -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

View File

@ -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)

View File

@ -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