WIP: track ending signal
This commit is contained in:
parent
5f0da55a24
commit
747f28f4f9
@ -291,7 +291,8 @@ class MusicMusterSignals(QObject):
|
||||
# Dispay status message to user
|
||||
status_message_signal = pyqtSignal(str, int)
|
||||
|
||||
track_ended_signal = pyqtSignal()
|
||||
# Emitted when track ends or is manually faded
|
||||
track_ended_signal = pyqtSignal(int)
|
||||
|
||||
def __post_init__(self):
|
||||
super().__init__()
|
||||
|
||||
@ -1063,6 +1063,9 @@ def playdates_update(track_id: int, when: dt.datetime | None = None) -> None:
|
||||
Update playdates for passed track
|
||||
"""
|
||||
|
||||
if not when:
|
||||
when = dt.datetime.now()
|
||||
|
||||
with db.Session() as session:
|
||||
_ = Playdates(session, track_id, when)
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
# Standard library imports
|
||||
from __future__ import annotations
|
||||
from slugify import slugify # type: ignore
|
||||
from typing import Callable, Optional
|
||||
from typing import Callable
|
||||
import argparse
|
||||
from dataclasses import dataclass
|
||||
import datetime as dt
|
||||
@ -72,24 +72,24 @@ from classes import (
|
||||
TrackAndPlaylist,
|
||||
TrackInfo,
|
||||
)
|
||||
|
||||
from config import Config
|
||||
from dialogs import TrackInsertDialog
|
||||
from file_importer import FileImporter
|
||||
from helpers import ask_yes_no, file_is_unreadable, get_name
|
||||
from log import log, log_call
|
||||
from playlistrow import PlaylistRow, TrackSequence
|
||||
from playlistmodel import PlaylistModel, PlaylistProxyModel
|
||||
from playlistrow import PlaylistRow, TrackSequence
|
||||
from playlists import PlaylistTab
|
||||
import ds
|
||||
from querylistmodel import QuerylistModel
|
||||
from ui import icons_rc # noqa F401
|
||||
from ui.dlg_SelectPlaylist_ui import Ui_dlgSelectPlaylist # type: ignore
|
||||
from ui.downloadcsv_ui import Ui_DateSelect # type: ignore
|
||||
from ui import icons_rc # noqa F401
|
||||
from ui.main_window_footer_ui import Ui_FooterSection # type: ignore
|
||||
from ui.main_window_header_ui import Ui_HeaderSection # type: ignore
|
||||
from ui.main_window_playlist_ui import Ui_PlaylistSection # type: ignore
|
||||
from ui.main_window_footer_ui import Ui_FooterSection # type: ignore
|
||||
|
||||
from utilities import check_db, update_bitrates
|
||||
import ds
|
||||
import helpers
|
||||
|
||||
|
||||
@ -661,15 +661,6 @@ class ManageTemplates(ItemlistManager):
|
||||
ds.playlist_rename(template_id, new_name)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ItemlistManagerCallbacks:
|
||||
delete: Callable[[int], None]
|
||||
edit: Callable[[int], None]
|
||||
favourite: Callable[[int, bool], None]
|
||||
new_item: Callable[[], None]
|
||||
rename: Callable[[int], Optional[str]]
|
||||
|
||||
|
||||
class PreviewManager:
|
||||
"""
|
||||
Manage track preview player
|
||||
@ -677,10 +668,10 @@ class PreviewManager:
|
||||
|
||||
def __init__(self) -> None:
|
||||
mixer.init()
|
||||
self.intro: Optional[int] = None
|
||||
self.intro: int | None = None
|
||||
self.path: str = ""
|
||||
self.row_number: Optional[int] = None
|
||||
self.start_time: Optional[dt.datetime] = None
|
||||
self.row_number: int | None = None
|
||||
self.start_time: dt.datetime | None = None
|
||||
self.track_id: int = 0
|
||||
|
||||
def back(self, ms: int) -> None:
|
||||
@ -1036,7 +1027,7 @@ class TemplateSelectorDialog(QDialog):
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, templates: list[tuple[str, int]], template_prompt: Optional[str]
|
||||
self, templates: list[tuple[str, int]], template_prompt: str | None
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.templates = templates
|
||||
@ -1111,7 +1102,7 @@ class FooterSection(QWidget, Ui_FooterSection):
|
||||
|
||||
class Window(QMainWindow):
|
||||
def __init__(
|
||||
self, parent: Optional[QWidget] = None, *args: list, **kwargs: dict
|
||||
self, parent: QWidget | None = None, *args: list, **kwargs: dict
|
||||
) -> None:
|
||||
super().__init__(parent)
|
||||
|
||||
@ -1128,45 +1119,48 @@ class Window(QMainWindow):
|
||||
layout.addWidget(self.playlist_section)
|
||||
layout.addWidget(self.footer_section)
|
||||
|
||||
self.setWindowTitle(Config.MAIN_WINDOW_TITLE)
|
||||
# Add menu bar
|
||||
self.create_menu_bar()
|
||||
|
||||
self.timer10: QTimer = QTimer()
|
||||
self.timer100: QTimer = QTimer()
|
||||
self.timer500: QTimer = QTimer()
|
||||
self.timer1000: QTimer = QTimer()
|
||||
|
||||
self.set_main_window_size()
|
||||
self.lblSumPlaytime = QLabel("")
|
||||
self.statusbar = self.statusBar()
|
||||
if self.statusbar:
|
||||
self.statusbar.addPermanentWidget(self.lblSumPlaytime)
|
||||
self.txtSearch = QLineEdit()
|
||||
self.txtSearch.setHidden(True)
|
||||
self.statusbar.addWidget(self.txtSearch)
|
||||
self.hide_played_tracks = False
|
||||
self.preview_manager = PreviewManager()
|
||||
|
||||
self.footer_section.widgetFadeVolume.hideAxis("bottom")
|
||||
self.footer_section.widgetFadeVolume.hideAxis("left")
|
||||
self.footer_section.widgetFadeVolume.setDefaultPadding(0)
|
||||
self.footer_section.widgetFadeVolume.setBackground(Config.FADE_CURVE_BACKGROUND)
|
||||
|
||||
self.move_source: MoveSource | None = None
|
||||
self.setWindowTitle(Config.MAIN_WINDOW_TITLE)
|
||||
|
||||
self.disable_selection_timing = False
|
||||
self.clock_counter = 0
|
||||
# Add menu bar
|
||||
self.create_menu_bar()
|
||||
|
||||
# Configure main window
|
||||
self.set_main_window_size()
|
||||
self.lblSumPlaytime = QLabel("")
|
||||
self.statusbar = self.statusBar()
|
||||
if not self.statusbar:
|
||||
raise ApplicationError("Can't create status bar")
|
||||
self.statusbar.addPermanentWidget(self.lblSumPlaytime)
|
||||
self.txtSearch = QLineEdit()
|
||||
self.txtSearch.setHidden(True)
|
||||
self.statusbar.addWidget(self.txtSearch)
|
||||
self.hide_played_tracks = False
|
||||
|
||||
# Timers
|
||||
self.timer10: QTimer = QTimer()
|
||||
self.timer100: QTimer = QTimer()
|
||||
self.timer500: QTimer = QTimer()
|
||||
self.timer1000: QTimer = QTimer()
|
||||
self.timer10.start(10)
|
||||
self.timer100.start(100)
|
||||
self.timer500.start(500)
|
||||
self.timer1000.start(1000)
|
||||
self.signals = MusicMusterSignals()
|
||||
self.connect_signals_slots()
|
||||
|
||||
# Misc
|
||||
self.preview_manager = PreviewManager()
|
||||
self.move_source: MoveSource | None = None
|
||||
self.disable_selection_timing = False
|
||||
self.catch_return_key = False
|
||||
self.importer: Optional[FileImporter] = None
|
||||
self.importer: FileImporter | None = None
|
||||
self.current = Current()
|
||||
self.track_sequence = TrackSequence()
|
||||
self.signals = MusicMusterSignals()
|
||||
self.connect_signals_slots()
|
||||
|
||||
webbrowser.register(
|
||||
"browser",
|
||||
@ -1178,12 +1172,12 @@ class Window(QMainWindow):
|
||||
self.action_quicklog = QShortcut(QKeySequence("Ctrl+L"), self)
|
||||
self.action_quicklog.activated.connect(self.quicklog)
|
||||
|
||||
# Load playlists
|
||||
self.load_last_playlists()
|
||||
self.stop_autoplay = False
|
||||
|
||||
# # # # # # # # # # Overrides # # # # # # # # # #
|
||||
|
||||
def closeEvent(self, event: Optional[QCloseEvent]) -> None:
|
||||
def closeEvent(self, event: QCloseEvent | None) -> None:
|
||||
"""Handle attempt to close main window"""
|
||||
|
||||
if not event:
|
||||
@ -1210,7 +1204,7 @@ class Window(QMainWindow):
|
||||
mainwindow_width=self.width(),
|
||||
mainwindow_x=self.x(),
|
||||
mainwindow_y=self.y(),
|
||||
active_tab=self.playlist_section.tabPlaylist.currentIndex(),
|
||||
active_index=self.playlist_section.tabPlaylist.currentIndex(),
|
||||
)
|
||||
for name, value in attributes_to_save.items():
|
||||
ds.setting_set(name, value)
|
||||
@ -1219,16 +1213,17 @@ class Window(QMainWindow):
|
||||
|
||||
# # # # # # # # # # Internal utility functions # # # # # # # # # #
|
||||
|
||||
def active_tab(self) -> PlaylistTab:
|
||||
def _active_tab(self) -> PlaylistTab:
|
||||
return self.playlist_section.tabPlaylist.currentWidget()
|
||||
|
||||
# # # # # # # # # # Menu functions # # # # # # # # # #
|
||||
|
||||
def create_action(
|
||||
self, text: str, handler: Callable, shortcut: Optional[str] = None
|
||||
self, text: str, handler: Callable, shortcut: str | None = None
|
||||
) -> QAction:
|
||||
"""
|
||||
Helper function to create an action, bind it to a method, and set a shortcut if provided.
|
||||
Helper function for menu creation. Create an action, bind it to a
|
||||
method, and set a shortcut if provided.
|
||||
"""
|
||||
|
||||
action = QAction(text, self)
|
||||
@ -1281,34 +1276,6 @@ class Window(QMainWindow):
|
||||
|
||||
menu.addAction(action)
|
||||
|
||||
def populate_dynamic_submenu(self):
|
||||
"""Dynamically populates submenus when they are selected."""
|
||||
submenu = self.sender() # Get the submenu that triggered the event
|
||||
|
||||
# Find which submenu it is
|
||||
for key, stored_submenu in self.dynamic_submenus.items():
|
||||
if submenu == stored_submenu:
|
||||
submenu.clear()
|
||||
# Dynamically call the correct function
|
||||
items = getattr(self, f"get_{key}_items")()
|
||||
for item in items:
|
||||
# Check for separator
|
||||
if "separator" in item and item["separator"]:
|
||||
submenu.addSeparator()
|
||||
continue
|
||||
action = QAction(item["text"], self)
|
||||
|
||||
# Extract handler and arguments
|
||||
handler = getattr(self, item["handler"], None)
|
||||
args = item.get("args", ())
|
||||
|
||||
if handler:
|
||||
# Use a lambda to pass arguments to the function
|
||||
action.triggered.connect(lambda _, h=handler, a=args: h(a))
|
||||
|
||||
submenu.addAction(action)
|
||||
break
|
||||
|
||||
def get_new_playlist_dynamic_submenu_items(
|
||||
self,
|
||||
) -> list[dict[str, str | int | bool]]:
|
||||
@ -1376,41 +1343,33 @@ class Window(QMainWindow):
|
||||
|
||||
return submenu_items
|
||||
|
||||
def show_query(self, query_id: int) -> None:
|
||||
"""
|
||||
Show query dialog with query_id selected
|
||||
"""
|
||||
def populate_dynamic_submenu(self):
|
||||
"""Dynamically populates submenus when they are selected."""
|
||||
submenu = self.sender() # Get the submenu that triggered the event
|
||||
|
||||
# Keep a reference else it will be gc'd
|
||||
self.query_dialog = QueryDialog(query_id)
|
||||
if self.query_dialog.exec():
|
||||
new_row_number = self.current_row_or_end()
|
||||
base_model = self.current.base_model
|
||||
for track_id in self.query_dialog.selected_tracks:
|
||||
# Check whether track is already in playlist
|
||||
move_existing = False
|
||||
existing_prd = base_model.is_track_in_playlist(track_id)
|
||||
if existing_prd is not None:
|
||||
if ask_yes_no(
|
||||
"Duplicate row",
|
||||
"Track already in playlist. " "Move to new location?",
|
||||
default_yes=True,
|
||||
):
|
||||
move_existing = True
|
||||
# Find which submenu it is
|
||||
for key, stored_submenu in self.dynamic_submenus.items():
|
||||
if submenu == stored_submenu:
|
||||
submenu.clear()
|
||||
# Dynamically call the correct function
|
||||
items = getattr(self, f"get_{key}_items")()
|
||||
for item in items:
|
||||
# Check for separator
|
||||
if "separator" in item and item["separator"]:
|
||||
submenu.addSeparator()
|
||||
continue
|
||||
action = QAction(item["text"], self)
|
||||
|
||||
if move_existing and existing_prd:
|
||||
base_model.move_track_add_note(
|
||||
new_row_number, existing_prd, note=""
|
||||
)
|
||||
else:
|
||||
self.signals.signal_insert_track.emit(
|
||||
InsertTrack(
|
||||
playlist_id=base_model.playlist_id,
|
||||
track_id=track_id,
|
||||
note="",
|
||||
)
|
||||
)
|
||||
new_row_number += 1
|
||||
# Extract handler and arguments
|
||||
handler = getattr(self, item["handler"], None)
|
||||
args = item.get("args", ())
|
||||
|
||||
if handler:
|
||||
# Use a lambda to pass arguments to the function
|
||||
action.triggered.connect(lambda _, h=handler, a=args: h(a))
|
||||
|
||||
submenu.addAction(action)
|
||||
break
|
||||
|
||||
# # # # # # # # # # Playlist management functions # # # # # # # # # #
|
||||
|
||||
@ -1418,7 +1377,7 @@ class Window(QMainWindow):
|
||||
def _create_playlist(self, name: str, template_id: int) -> PlaylistDTO:
|
||||
"""
|
||||
Create a playlist in the database, populate it from the template
|
||||
if template_id > 0, and return the Playlists object.
|
||||
if template_id > 0, and return the PlaylistDTO object.
|
||||
"""
|
||||
|
||||
return ds.playlist_create(name, template_id)
|
||||
@ -1455,7 +1414,7 @@ class Window(QMainWindow):
|
||||
# @log_call
|
||||
def create_playlist_from_template(self, template_id: int) -> None:
|
||||
"""
|
||||
Prompt for new playlist name and create from passed template_id
|
||||
Prompt for new playlist name and create from passed template_id.
|
||||
"""
|
||||
|
||||
if template_id == 0:
|
||||
@ -1463,7 +1422,6 @@ class Window(QMainWindow):
|
||||
selected_template_id = self.solicit_template_to_use()
|
||||
if selected_template_id is None:
|
||||
return
|
||||
else:
|
||||
template_id = selected_template_id
|
||||
|
||||
playlist_name = self.get_playlist_name()
|
||||
@ -1526,7 +1484,7 @@ class Window(QMainWindow):
|
||||
|
||||
def get_playlist_name(
|
||||
self, default: str = "", prompt: str = "Playlist name:"
|
||||
) -> Optional[str]:
|
||||
) -> str | None:
|
||||
"""Get a name from the user"""
|
||||
|
||||
dlg = QInputDialog(self)
|
||||
@ -1554,8 +1512,8 @@ class Window(QMainWindow):
|
||||
return None
|
||||
|
||||
def solicit_template_to_use(
|
||||
self, template_prompt: Optional[str] = None
|
||||
) -> Optional[int]:
|
||||
self, template_prompt: str | None = None
|
||||
) -> int | None:
|
||||
"""
|
||||
Have user select a template. Return the template.id, or None if they cancel.
|
||||
template_id of zero means don't use a template.
|
||||
@ -1589,12 +1547,48 @@ class Window(QMainWindow):
|
||||
|
||||
_ = ManageTemplates(self)
|
||||
|
||||
def show_query(self, query_id: int) -> None:
|
||||
"""
|
||||
Show query dialog with query_id selected
|
||||
"""
|
||||
|
||||
# Keep a reference else it will be gc'd
|
||||
self.query_dialog = QueryDialog(query_id)
|
||||
if self.query_dialog.exec():
|
||||
new_row_number = self.current_row_or_end()
|
||||
base_model = self.current.base_model
|
||||
for track_id in self.query_dialog.selected_tracks:
|
||||
# Check whether track is already in playlist
|
||||
move_existing = False
|
||||
existing_prd = base_model.is_track_in_playlist(track_id)
|
||||
if existing_prd is not None:
|
||||
if ask_yes_no(
|
||||
"Duplicate row",
|
||||
"Track already in playlist. " "Move to new location?",
|
||||
default_yes=True,
|
||||
):
|
||||
move_existing = True
|
||||
|
||||
if move_existing and existing_prd:
|
||||
base_model.move_track_add_note(
|
||||
new_row_number, existing_prd, note=""
|
||||
)
|
||||
else:
|
||||
self.signals.signal_insert_track.emit(
|
||||
InsertTrack(
|
||||
playlist_id=base_model.playlist_id,
|
||||
track_id=track_id,
|
||||
note="",
|
||||
)
|
||||
)
|
||||
new_row_number += 1
|
||||
|
||||
# # # # # # # # # # Miscellaneous functions # # # # # # # # # #
|
||||
|
||||
def select_duplicate_rows(self, checked: bool = False) -> None:
|
||||
"""Call playlist to select duplicate rows"""
|
||||
|
||||
self.active_tab().select_duplicate_rows()
|
||||
self._active_tab().select_duplicate_rows()
|
||||
|
||||
def about(self, checked: bool = False) -> None:
|
||||
"""Get git tag and database name"""
|
||||
@ -1621,14 +1615,14 @@ class Window(QMainWindow):
|
||||
"""
|
||||
|
||||
self.track_sequence.set_next(None)
|
||||
self.update_headers()
|
||||
self.signals.next_track_changed_signal.emit()
|
||||
|
||||
def clear_selection(self, checked: bool = False) -> None:
|
||||
"""Clear row selection"""
|
||||
|
||||
# Unselect any selected rows
|
||||
if self.active_tab():
|
||||
self.active_tab().clear_selection()
|
||||
if self._active_tab():
|
||||
self._active_tab().clear_selection()
|
||||
|
||||
# Clear the search bar
|
||||
self.search_playlist_clear()
|
||||
@ -1782,6 +1776,7 @@ class Window(QMainWindow):
|
||||
# @log_call
|
||||
def end_of_track_actions(self) -> None:
|
||||
"""
|
||||
Called by track_ended_signal
|
||||
|
||||
Actions required:
|
||||
- Reset track_sequence objects
|
||||
@ -1794,10 +1789,6 @@ class Window(QMainWindow):
|
||||
if self.track_sequence.current:
|
||||
self.track_sequence.move_current_to_previous()
|
||||
|
||||
# Tell playlist previous track has finished
|
||||
# TODO: it should just catch track_ended_signal
|
||||
self.current.base_model.previous_track_ended()
|
||||
|
||||
# Reset clocks
|
||||
self.footer_section.frame_fade.setStyleSheet("")
|
||||
self.footer_section.frame_silent.setStyleSheet("")
|
||||
@ -1811,10 +1802,6 @@ class Window(QMainWindow):
|
||||
self.catch_return_key = False
|
||||
self.show_status_message("Play controls: Enabled", 0)
|
||||
|
||||
# autoplay
|
||||
# if not self.stop_autoplay:
|
||||
# self.play_next()
|
||||
|
||||
def export_playlist_tab(self, checked: bool = False) -> None:
|
||||
"""Export the current playlist to an m3u file"""
|
||||
|
||||
@ -1859,7 +1846,7 @@ class Window(QMainWindow):
|
||||
if self.track_sequence.current:
|
||||
self.track_sequence.current.fade()
|
||||
|
||||
def get_tab_index_for_playlist(self, playlist_id: int) -> Optional[int]:
|
||||
def get_tab_index_for_playlist(self, playlist_id: int) -> int | None:
|
||||
"""
|
||||
Return the tab index for the passed playlist_id if it is displayed,
|
||||
else return None.
|
||||
@ -1882,12 +1869,12 @@ class Window(QMainWindow):
|
||||
self.hide_played_tracks = True
|
||||
self.footer_section.btnHidePlayed.setText("Show played")
|
||||
if Config.HIDE_PLAYED_MODE == Config.HIDE_PLAYED_MODE_SECTIONS:
|
||||
self.active_tab().hide_played_sections()
|
||||
self._active_tab().hide_played_sections()
|
||||
else:
|
||||
self.current.base_model.hide_played_tracks(True)
|
||||
|
||||
# Reset row heights
|
||||
self.active_tab().resize_rows()
|
||||
self._active_tab().resize_rows()
|
||||
|
||||
def import_files_wrapper(self, checked: bool = False) -> None:
|
||||
"""
|
||||
@ -1920,7 +1907,7 @@ class Window(QMainWindow):
|
||||
def insert_track(self, checked: bool = False) -> None:
|
||||
"""Show dialog box to select and add track from database"""
|
||||
|
||||
dlg = TrackInsertDialog(parent=self, playlist_id=self.active_tab().playlist_id)
|
||||
dlg = TrackInsertDialog(parent=self, playlist_id=self.current.playlist_id)
|
||||
dlg.exec()
|
||||
|
||||
# @log_call
|
||||
@ -1934,7 +1921,7 @@ class Window(QMainWindow):
|
||||
playlist_ids.append(self._open_playlist(playlist))
|
||||
|
||||
# Set active tab
|
||||
value = ds.setting_get("active_tab")
|
||||
value = ds.setting_get("active_index")
|
||||
if value is not None and value >= 0:
|
||||
self.playlist_section.tabPlaylist.setCurrentIndex(value)
|
||||
|
||||
@ -1969,7 +1956,7 @@ class Window(QMainWindow):
|
||||
# paste
|
||||
self.move_source = MoveSource(
|
||||
model=self.current.base_model,
|
||||
rows=[a.row_number for a in self.current.base_model.selected_rows],
|
||||
rows=self.current.selected_row_numbers
|
||||
)
|
||||
|
||||
log.debug(f"mark_rows_for_moving(): {self.move_source=}")
|
||||
@ -1980,6 +1967,9 @@ class Window(QMainWindow):
|
||||
Move passed playlist rows to another playlist
|
||||
"""
|
||||
|
||||
if not row_numbers:
|
||||
return
|
||||
|
||||
# Identify destination playlist
|
||||
playlists = []
|
||||
source_playlist_id = self.current.playlist_id
|
||||
@ -2013,11 +2003,7 @@ class Window(QMainWindow):
|
||||
Move selected rows to another playlist
|
||||
"""
|
||||
|
||||
selected_rows = self.current.selected_row_numbers
|
||||
if not selected_rows:
|
||||
return
|
||||
|
||||
self.move_playlist_rows(selected_rows)
|
||||
self.move_playlist_rows(self.current.selected_row_numbers)
|
||||
|
||||
def move_unplayed(self, checked: bool = False) -> None:
|
||||
"""
|
||||
@ -2074,8 +2060,8 @@ class Window(QMainWindow):
|
||||
from_rows, to_row, to_playlist_model.playlist_id
|
||||
)
|
||||
|
||||
self.active_tab().resize_rows()
|
||||
self.active_tab().clear_selection()
|
||||
self._active_tab().resize_rows()
|
||||
self._active_tab().clear_selection()
|
||||
|
||||
# If we move a row to immediately under the current track, make
|
||||
# that moved row the next track
|
||||
@ -2088,7 +2074,7 @@ class Window(QMainWindow):
|
||||
|
||||
# @log_call
|
||||
def play_next(
|
||||
self, position: Optional[float] = None, checked: bool = False
|
||||
self, position: float | None = None, checked: bool = False
|
||||
) -> None:
|
||||
"""
|
||||
Play next track, optionally from passed position.
|
||||
@ -2118,7 +2104,7 @@ class Window(QMainWindow):
|
||||
return
|
||||
|
||||
# Issue #223 concerns a very short pause (maybe 0.1s) sometimes
|
||||
# when starting to play at track. Resolution appears to be to
|
||||
# just after a track starts playing. Resolution appears to be to
|
||||
# disable timer10 for a short time. Timer is re-enabled in
|
||||
# update_clocks.
|
||||
|
||||
@ -2129,9 +2115,11 @@ class Window(QMainWindow):
|
||||
if self.track_sequence.current:
|
||||
self.track_sequence.current.fade()
|
||||
|
||||
# Move next track to current track.
|
||||
# end_of_track_actions() will have saved current track to
|
||||
# Move next track to current track. end_of_track_actions() will
|
||||
# have been called when previous track ended or when fade() was
|
||||
# called above, and that in turn will have saved current track to
|
||||
# previous_track
|
||||
|
||||
self.track_sequence.move_next_to_current()
|
||||
if self.track_sequence.current is None:
|
||||
raise ApplicationError("No current track")
|
||||
@ -2159,6 +2147,9 @@ class Window(QMainWindow):
|
||||
self.catch_return_key = True
|
||||
self.show_status_message("Play controls: Disabled", 0)
|
||||
|
||||
# Record playdate
|
||||
ds.playdates_update(self.track_sequence.current.track_id)
|
||||
|
||||
# Notify others
|
||||
self.signals.signal_track_started.emit(
|
||||
TrackAndPlaylist(
|
||||
@ -2167,21 +2158,6 @@ class Window(QMainWindow):
|
||||
)
|
||||
)
|
||||
|
||||
# TODO: ensure signal_track_started does all this:
|
||||
# self.active_tab().current_track_started()
|
||||
# Update playdates
|
||||
# Set toolips for hdrPreviousTrack (but let's do that dynamically
|
||||
# on hover in future)
|
||||
# with s-e-s-s-i-o-n:
|
||||
# last_played = Playdates.last_played_tracks(s-e-s-s-i-o-n)
|
||||
# tracklist = []
|
||||
# for lp in last_played:
|
||||
# track = s-e-s-s-i-o-n.get(Tracks, lp.track_id)
|
||||
# tracklist.append(f"{track.title} ({track.artist})")
|
||||
# tt = "<br>".join(tracklist)
|
||||
|
||||
# self.header_section.hdrPreviousTrack.setToolTip(tt)
|
||||
|
||||
# Update headers
|
||||
self.update_headers()
|
||||
|
||||
@ -2194,7 +2170,7 @@ class Window(QMainWindow):
|
||||
|
||||
if self.footer_section.btnPreview.isChecked():
|
||||
# Get track path for first selected track if there is one
|
||||
track_info = self.active_tab().get_selected_row_track_info()
|
||||
track_info = self._active_tab().get_selected_row_track_info()
|
||||
if not track_info:
|
||||
# Otherwise get track_id to next track to play
|
||||
if self.track_sequence.next:
|
||||
@ -2203,10 +2179,9 @@ class Window(QMainWindow):
|
||||
self.track_sequence.next.track_id,
|
||||
self.track_sequence.next.row_number,
|
||||
)
|
||||
else:
|
||||
return
|
||||
if not track_info:
|
||||
return
|
||||
|
||||
self.preview_manager.row_number = track_info.row_number
|
||||
track = ds.track_by_id(track_info.track_id)
|
||||
if not track:
|
||||
@ -2394,7 +2369,7 @@ class Window(QMainWindow):
|
||||
# Disable play controls so that 'return' in search box doesn't
|
||||
# play next track
|
||||
self.catch_return_key = True
|
||||
self.txtSearch.setHidden(False)
|
||||
self.txtSearch.setVisible(True)
|
||||
self.txtSearch.setFocus()
|
||||
# Select any text that may already be there
|
||||
self.txtSearch.selectAll()
|
||||
@ -2413,13 +2388,13 @@ class Window(QMainWindow):
|
||||
|
||||
self.current.proxy_model.set_incremental_search(self.txtSearch.text())
|
||||
|
||||
def selected_or_next_track_info(self) -> Optional[PlaylistRow]:
|
||||
def selected_or_next_track_info(self) -> PlaylistRow | None:
|
||||
"""
|
||||
Return RowAndTrack info for selected track. If no selected track, return for
|
||||
next track. If no next track, return None.
|
||||
"""
|
||||
|
||||
row_number: Optional[int] = None
|
||||
row_number: int | None = None
|
||||
|
||||
if self.current.selected_row_numbers:
|
||||
row_number = self.current.selected_row_numbers[0]
|
||||
@ -2453,20 +2428,6 @@ class Window(QMainWindow):
|
||||
|
||||
self.signals.signal_set_next_row.emit(self.current.playlist_id)
|
||||
self.clear_selection()
|
||||
# playlist_tab = self.active_tab()
|
||||
# if playlist_tab:
|
||||
# playlist_tab.set_row_as_next_track()
|
||||
# else:
|
||||
# log.error("No active tab")
|
||||
|
||||
# @log_call
|
||||
def set_tab_colour(self, widget: PlaylistTab, colour: QColor) -> None:
|
||||
"""
|
||||
Find the tab containing the widget and set the text colour
|
||||
"""
|
||||
|
||||
idx = self.playlist_section.tabPlaylist.indexOf(widget)
|
||||
self.playlist_section.tabPlaylist.tabBar().setTabTextColor(idx, colour)
|
||||
|
||||
def show_current(self) -> None:
|
||||
"""Scroll to show current track"""
|
||||
@ -2476,10 +2437,9 @@ class Window(QMainWindow):
|
||||
|
||||
def show_warning(self, title: str, body: str) -> None:
|
||||
"""
|
||||
Display a warning dialog
|
||||
Handle show_warning_signal and display a warning dialog
|
||||
"""
|
||||
|
||||
print(f"show_warning({title=}, {body=})")
|
||||
QMessageBox.warning(self, title, body)
|
||||
|
||||
def show_next(self) -> None:
|
||||
@ -2490,6 +2450,7 @@ class Window(QMainWindow):
|
||||
|
||||
def show_status_message(self, message: str, timing: int) -> None:
|
||||
"""
|
||||
Handle status_message_signal.
|
||||
Show status message in status bar for timing milliseconds
|
||||
Clear message if message is null string
|
||||
"""
|
||||
@ -2501,7 +2462,7 @@ class Window(QMainWindow):
|
||||
self.statusbar.clearMessage()
|
||||
|
||||
def show_track(self, playlist_track: PlaylistRow) -> None:
|
||||
"""Scroll to show track in plt"""
|
||||
"""Scroll to show track"""
|
||||
|
||||
# Switch to the correct tab
|
||||
playlist_id = playlist_track.playlist_id
|
||||
@ -2519,20 +2480,19 @@ class Window(QMainWindow):
|
||||
f"show_track() can't find current playlist tab {playlist_id=}"
|
||||
)
|
||||
|
||||
self.active_tab().scroll_to_top(playlist_track.row_number)
|
||||
self._active_tab().scroll_to_top(playlist_track.row_number)
|
||||
|
||||
# @log_call
|
||||
def stop(self, checked: bool = False) -> None:
|
||||
"""Stop playing immediately"""
|
||||
|
||||
self.stop_autoplay = True
|
||||
if self.track_sequence.current:
|
||||
self.track_sequence.current.stop()
|
||||
|
||||
def tab_change(self) -> None:
|
||||
"""Called when active tab changed"""
|
||||
|
||||
self.active_tab().tab_live()
|
||||
self._active_tab().tab_live()
|
||||
|
||||
def tick_10ms(self) -> None:
|
||||
"""
|
||||
@ -2551,9 +2511,11 @@ class Window(QMainWindow):
|
||||
try:
|
||||
self.track_sequence.current.check_for_end_of_track()
|
||||
|
||||
# Update intro counter if applicable and, if updated, return
|
||||
# because playing an intro takes precedence over timing a
|
||||
# Update intro counter if applicable and, if updated,
|
||||
# return because playing an intro uses the intro field to
|
||||
# show timing and this takes precedence over timing a
|
||||
# preview.
|
||||
|
||||
intro_ms_remaining = self.track_sequence.current.time_remaining_intro()
|
||||
if intro_ms_remaining > 0:
|
||||
self.footer_section.label_intro_timer.setText(
|
||||
@ -2572,7 +2534,6 @@ class Window(QMainWindow):
|
||||
# current track ended during servicing tick
|
||||
pass
|
||||
|
||||
# Ensure preview button is reset if preview finishes playing
|
||||
# Update preview timer
|
||||
if self.footer_section.btnPreview.isChecked():
|
||||
if self.preview_manager.is_playing():
|
||||
@ -2584,6 +2545,8 @@ class Window(QMainWindow):
|
||||
f"{int(minutes)}:{seconds:04.1f}"
|
||||
)
|
||||
else:
|
||||
# Ensure preview button is reset if preview has finished
|
||||
# playing
|
||||
self.footer_section.btnPreview.setChecked(False)
|
||||
self.footer_section.label_intro_timer.setText("0.0")
|
||||
self.footer_section.label_intro_timer.setStyleSheet("")
|
||||
|
||||
@ -100,6 +100,7 @@ class PlaylistModel(QAbstractTableModel):
|
||||
self.signals.signal_playlist_selected_rows.connect(self.set_selected_rows)
|
||||
self.signals.signal_set_next_row.connect(self.set_next_row)
|
||||
self.signals.signal_track_started.connect(self.track_started)
|
||||
self.signals.track_ended_signal.connect(self.previous_track_ended)
|
||||
|
||||
# Populate self.playlist_rows
|
||||
for dto in ds.playlistrows_by_playlist(self.playlist_id):
|
||||
@ -267,7 +268,7 @@ class PlaylistModel(QAbstractTableModel):
|
||||
track_id = play_track.track_id
|
||||
# Sanity check - 1
|
||||
if not track_id:
|
||||
raise ApplicationError("current_track_started() called with no track_id")
|
||||
raise ApplicationError("track_started() called with no track_id")
|
||||
|
||||
# Sanity check - 2
|
||||
if self.track_sequence.current is None:
|
||||
@ -960,15 +961,19 @@ class PlaylistModel(QAbstractTableModel):
|
||||
return
|
||||
|
||||
# @log_call
|
||||
def previous_track_ended(self) -> None:
|
||||
def previous_track_ended(self, playlist_id: int) -> None:
|
||||
"""
|
||||
Notification from musicmuster that the previous track has ended.
|
||||
Notification from track_ended_signal that the previous track has ended.
|
||||
|
||||
Actions required:
|
||||
- sanity check
|
||||
- update display
|
||||
"""
|
||||
|
||||
if playlist_id != self.playlist_id:
|
||||
# Not for us
|
||||
return
|
||||
|
||||
# Sanity check
|
||||
if not self.track_sequence.previous:
|
||||
log.error(
|
||||
|
||||
@ -203,7 +203,7 @@ class PlaylistRow:
|
||||
self.fade_graph.clear()
|
||||
# Ensure that player is released
|
||||
self.music.fade(0)
|
||||
self.signals.track_ended_signal.emit()
|
||||
self.signals.track_ended_signal.emit(self.playlist_id)
|
||||
self.end_of_track_signalled = True
|
||||
|
||||
def drop3db(self, enable: bool) -> None:
|
||||
@ -221,7 +221,8 @@ class PlaylistRow:
|
||||
|
||||
self.resume_marker = self.music.get_position()
|
||||
self.music.fade(fade_seconds)
|
||||
self.signals.track_ended_signal.emit()
|
||||
self.signals.track_ended_signal.emit(self.playlist_id)
|
||||
self.end_of_track_signalled = True
|
||||
|
||||
def is_playing(self) -> bool:
|
||||
"""
|
||||
|
||||
Loading…
Reference in New Issue
Block a user