Compare commits
9 Commits
ffb1b238f4
...
d6bb3d04d8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d6bb3d04d8 | ||
|
|
a0ded4b73d | ||
|
|
6496ea2ac4 | ||
|
|
c61df17dd5 | ||
|
|
747f28f4f9 | ||
|
|
5f0da55a24 | ||
|
|
498923c3b3 | ||
|
|
b34e0a014a | ||
|
|
f9c33120f5 |
@ -218,6 +218,12 @@ class InsertTrack:
|
|||||||
note: str
|
note: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SelectedRows:
|
||||||
|
playlist_id: int
|
||||||
|
rows: list[int]
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class TrackAndPlaylist:
|
class TrackAndPlaylist:
|
||||||
playlist_id: int
|
playlist_id: int
|
||||||
@ -233,26 +239,60 @@ class MusicMusterSignals(QObject):
|
|||||||
- https://stackoverflow.com/questions/62654525/emit-a-signal-from-another-class-to-main-class
|
- https://stackoverflow.com/questions/62654525/emit-a-signal-from-another-class-to-main-class
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Used to en/disable escape as a shortcut key to "clear selection".
|
||||||
|
# We disable it when editing a field in the playlist because we use
|
||||||
|
# escape there to abandon an edit.
|
||||||
enable_escape_signal = pyqtSignal(bool)
|
enable_escape_signal = pyqtSignal(bool)
|
||||||
|
|
||||||
|
# Signals that the next-cued track has changed. Used to update
|
||||||
|
# playlist headers.
|
||||||
next_track_changed_signal = pyqtSignal()
|
next_track_changed_signal = pyqtSignal()
|
||||||
|
|
||||||
|
# Signals that the playlist_id passed should resize all rows.
|
||||||
resize_rows_signal = pyqtSignal(int)
|
resize_rows_signal = pyqtSignal(int)
|
||||||
|
|
||||||
|
# Signal to open browser at songfacts or wikipedia page matching
|
||||||
|
# passed string.
|
||||||
search_songfacts_signal = pyqtSignal(str)
|
search_songfacts_signal = pyqtSignal(str)
|
||||||
search_wikipedia_signal = pyqtSignal(str)
|
search_wikipedia_signal = pyqtSignal(str)
|
||||||
|
|
||||||
|
# Displays a warning dialog
|
||||||
show_warning_signal = pyqtSignal(str, str)
|
show_warning_signal = pyqtSignal(str, str)
|
||||||
signal_add_track_to_header = pyqtSignal(int)
|
|
||||||
|
# Signal to add a track to a header row
|
||||||
|
signal_add_track_to_header = pyqtSignal(TrackAndPlaylist)
|
||||||
|
|
||||||
|
# Signal to receving model that rows will be / have been inserter
|
||||||
signal_begin_insert_rows = pyqtSignal(InsertRows)
|
signal_begin_insert_rows = pyqtSignal(InsertRows)
|
||||||
signal_end_insert_rows = pyqtSignal(int)
|
signal_end_insert_rows = pyqtSignal(int)
|
||||||
|
|
||||||
|
# TBD
|
||||||
signal_insert_track = pyqtSignal(InsertTrack)
|
signal_insert_track = pyqtSignal(InsertTrack)
|
||||||
signal_playlist_selected_rows = pyqtSignal(int, list)
|
|
||||||
|
# Keep track of which rows are selected (between playlist and model)
|
||||||
|
signal_playlist_selected_rows = pyqtSignal(SelectedRows)
|
||||||
|
|
||||||
|
# Signal to model that selected row is to be next row
|
||||||
signal_set_next_row = pyqtSignal(int)
|
signal_set_next_row = pyqtSignal(int)
|
||||||
|
|
||||||
# signal_set_next_track takes a PlaylistRow as an argument. We can't
|
# signal_set_next_track takes a PlaylistRow as an argument. We can't
|
||||||
# specify that here as it requires us to import PlaylistRow from
|
# specify that here as it requires us to import PlaylistRow from
|
||||||
# playlistrow.py, which itself imports MusicMusterSignals
|
# playlistrow.py, which itself imports MusicMusterSignals
|
||||||
|
|
||||||
|
# TBD
|
||||||
signal_set_next_track = pyqtSignal(object)
|
signal_set_next_track = pyqtSignal(object)
|
||||||
|
|
||||||
|
# Emited when a track starts playing
|
||||||
signal_track_started = pyqtSignal(TrackAndPlaylist)
|
signal_track_started = pyqtSignal(TrackAndPlaylist)
|
||||||
|
|
||||||
|
# Used by model to signal spanning of cells to playlist for headers
|
||||||
span_cells_signal = pyqtSignal(int, int, int, int, int)
|
span_cells_signal = pyqtSignal(int, int, int, int, int)
|
||||||
|
|
||||||
|
# Dispay status message to user
|
||||||
status_message_signal = pyqtSignal(str, int)
|
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):
|
def __post_init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|||||||
51
app/ds.py
51
app/ds.py
@ -434,21 +434,6 @@ def tracks_filtered(filter: Filter) -> list[TrackDTO]:
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
def track_set_intro(track_id: int, intro: int) -> None:
|
|
||||||
"""
|
|
||||||
Set track intro time
|
|
||||||
"""
|
|
||||||
|
|
||||||
with db.Session() as session:
|
|
||||||
session.execute(
|
|
||||||
update(Tracks)
|
|
||||||
.where(Tracks.id == track_id)
|
|
||||||
.values(intro=intro)
|
|
||||||
)
|
|
||||||
|
|
||||||
session.commit()
|
|
||||||
|
|
||||||
|
|
||||||
# @log_call
|
# @log_call
|
||||||
def track_update(
|
def track_update(
|
||||||
track_id: int, metadata: dict[str, str | int | float]
|
track_id: int, metadata: dict[str, str | int | float]
|
||||||
@ -918,7 +903,6 @@ def playlist_remove_rows(playlist_id: int, row_numbers: list[int]) -> None:
|
|||||||
)
|
)
|
||||||
# Fixup row number to remove gaps
|
# Fixup row number to remove gaps
|
||||||
_playlist_check_playlist(session, playlist_id, fix=True)
|
_playlist_check_playlist(session, playlist_id, fix=True)
|
||||||
|
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
|
|
||||||
@ -1029,6 +1013,38 @@ def playlistrows_by_playlist(
|
|||||||
return dto_list
|
return dto_list
|
||||||
|
|
||||||
|
|
||||||
|
def playlistrow_update_note(playlistrow_id: int, note: str) -> PlaylistRowDTO:
|
||||||
|
"""
|
||||||
|
Update the note on a playlist row
|
||||||
|
"""
|
||||||
|
|
||||||
|
with db.Session() as session:
|
||||||
|
plr = session.get(PlaylistRows, playlistrow_id)
|
||||||
|
|
||||||
|
if not plr:
|
||||||
|
raise ApplicationError(f"Can't retrieve Playlistrow ({playlistrow_id=})")
|
||||||
|
|
||||||
|
plr.note = note
|
||||||
|
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
new_plr = playlistrow_by_id(playlistrow_id)
|
||||||
|
if not new_plr:
|
||||||
|
raise ApplicationError(f"Can't retrieve new Playlistrow ({playlistrow_id=})")
|
||||||
|
|
||||||
|
return new_plr
|
||||||
|
|
||||||
|
|
||||||
|
def playlistrow_played(playlistrow_id: int, status: bool) -> None:
|
||||||
|
"""Update played status of row"""
|
||||||
|
|
||||||
|
with db.Session() as session:
|
||||||
|
session.execute(
|
||||||
|
update(PlaylistRows).where(PlaylistRows.id == playlistrow_id).values(played=status)
|
||||||
|
)
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
|
||||||
# Playdates
|
# Playdates
|
||||||
# @log_call
|
# @log_call
|
||||||
def playdates_get_last(track_id: int, limit: int = 5) -> str:
|
def playdates_get_last(track_id: int, limit: int = 5) -> str:
|
||||||
@ -1058,6 +1074,9 @@ def playdates_update(track_id: int, when: dt.datetime | None = None) -> None:
|
|||||||
Update playdates for passed track
|
Update playdates for passed track
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if not when:
|
||||||
|
when = dt.datetime.now()
|
||||||
|
|
||||||
with db.Session() as session:
|
with db.Session() as session:
|
||||||
_ = Playdates(session, track_id, when)
|
_ = Playdates(session, track_id, when)
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
# Standard library imports
|
# Standard library imports
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from slugify import slugify # type: ignore
|
from slugify import slugify # type: ignore
|
||||||
from typing import Callable, Optional
|
from typing import Callable
|
||||||
import argparse
|
import argparse
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
@ -65,30 +65,31 @@ import stackprinter # type: ignore
|
|||||||
from classes import (
|
from classes import (
|
||||||
ApplicationError,
|
ApplicationError,
|
||||||
Filter,
|
Filter,
|
||||||
|
InsertTrack,
|
||||||
MusicMusterSignals,
|
MusicMusterSignals,
|
||||||
PlaylistDTO,
|
PlaylistDTO,
|
||||||
QueryDTO,
|
QueryDTO,
|
||||||
TrackAndPlaylist,
|
TrackAndPlaylist,
|
||||||
TrackInfo,
|
TrackInfo,
|
||||||
)
|
)
|
||||||
|
|
||||||
from config import Config
|
from config import Config
|
||||||
from dialogs import TrackInsertDialog
|
from dialogs import TrackInsertDialog
|
||||||
from file_importer import FileImporter
|
from file_importer import FileImporter
|
||||||
from helpers import ask_yes_no, file_is_unreadable, get_name
|
from helpers import ask_yes_no, file_is_unreadable, get_name
|
||||||
from log import log, log_call
|
from log import log, log_call
|
||||||
from playlistrow import PlaylistRow, TrackSequence
|
|
||||||
from playlistmodel import PlaylistModel, PlaylistProxyModel
|
from playlistmodel import PlaylistModel, PlaylistProxyModel
|
||||||
|
from playlistrow import PlaylistRow, TrackSequence
|
||||||
from playlists import PlaylistTab
|
from playlists import PlaylistTab
|
||||||
import ds
|
|
||||||
from querylistmodel import QuerylistModel
|
from querylistmodel import QuerylistModel
|
||||||
from ui import icons_rc # noqa F401
|
|
||||||
from ui.dlg_SelectPlaylist_ui import Ui_dlgSelectPlaylist # type: ignore
|
from ui.dlg_SelectPlaylist_ui import Ui_dlgSelectPlaylist # type: ignore
|
||||||
from ui.downloadcsv_ui import Ui_DateSelect # 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_header_ui import Ui_HeaderSection # type: ignore
|
||||||
from ui.main_window_playlist_ui import Ui_PlaylistSection # 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
|
from utilities import check_db, update_bitrates
|
||||||
|
import ds
|
||||||
import helpers
|
import helpers
|
||||||
|
|
||||||
|
|
||||||
@ -660,15 +661,6 @@ class ManageTemplates(ItemlistManager):
|
|||||||
ds.playlist_rename(template_id, new_name)
|
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:
|
class PreviewManager:
|
||||||
"""
|
"""
|
||||||
Manage track preview player
|
Manage track preview player
|
||||||
@ -676,10 +668,10 @@ class PreviewManager:
|
|||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
mixer.init()
|
mixer.init()
|
||||||
self.intro: Optional[int] = None
|
self.intro: int | None = None
|
||||||
self.path: str = ""
|
self.path: str = ""
|
||||||
self.row_number: Optional[int] = None
|
self.row_number: int | None = None
|
||||||
self.start_time: Optional[dt.datetime] = None
|
self.start_time: dt.datetime | None = None
|
||||||
self.track_id: int = 0
|
self.track_id: int = 0
|
||||||
|
|
||||||
def back(self, ms: int) -> None:
|
def back(self, ms: int) -> None:
|
||||||
@ -1035,7 +1027,7 @@ class TemplateSelectorDialog(QDialog):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, templates: list[tuple[str, int]], template_prompt: Optional[str]
|
self, templates: list[tuple[str, int]], template_prompt: str | None
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.templates = templates
|
self.templates = templates
|
||||||
@ -1110,7 +1102,7 @@ class FooterSection(QWidget, Ui_FooterSection):
|
|||||||
|
|
||||||
class Window(QMainWindow):
|
class Window(QMainWindow):
|
||||||
def __init__(
|
def __init__(
|
||||||
self, parent: Optional[QWidget] = None, *args: list, **kwargs: dict
|
self, parent: QWidget | None = None, *args: list, **kwargs: dict
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
|
||||||
@ -1127,45 +1119,48 @@ class Window(QMainWindow):
|
|||||||
layout.addWidget(self.playlist_section)
|
layout.addWidget(self.playlist_section)
|
||||||
layout.addWidget(self.footer_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("bottom")
|
||||||
self.footer_section.widgetFadeVolume.hideAxis("left")
|
self.footer_section.widgetFadeVolume.hideAxis("left")
|
||||||
self.footer_section.widgetFadeVolume.setDefaultPadding(0)
|
self.footer_section.widgetFadeVolume.setDefaultPadding(0)
|
||||||
self.footer_section.widgetFadeVolume.setBackground(Config.FADE_CURVE_BACKGROUND)
|
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
|
# Add menu bar
|
||||||
self.clock_counter = 0
|
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.timer10.start(10)
|
||||||
self.timer100.start(100)
|
self.timer100.start(100)
|
||||||
self.timer500.start(500)
|
self.timer500.start(500)
|
||||||
self.timer1000.start(1000)
|
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.catch_return_key = False
|
||||||
self.importer: Optional[FileImporter] = None
|
self.importer: FileImporter | None = None
|
||||||
self.current = Current()
|
self.current = Current()
|
||||||
self.track_sequence = TrackSequence()
|
self.track_sequence = TrackSequence()
|
||||||
|
self.signals = MusicMusterSignals()
|
||||||
|
self.connect_signals_slots()
|
||||||
|
|
||||||
webbrowser.register(
|
webbrowser.register(
|
||||||
"browser",
|
"browser",
|
||||||
@ -1177,12 +1172,12 @@ class Window(QMainWindow):
|
|||||||
self.action_quicklog = QShortcut(QKeySequence("Ctrl+L"), self)
|
self.action_quicklog = QShortcut(QKeySequence("Ctrl+L"), self)
|
||||||
self.action_quicklog.activated.connect(self.quicklog)
|
self.action_quicklog.activated.connect(self.quicklog)
|
||||||
|
|
||||||
|
# Load playlists
|
||||||
self.load_last_playlists()
|
self.load_last_playlists()
|
||||||
self.stop_autoplay = False
|
|
||||||
|
|
||||||
# # # # # # # # # # Overrides # # # # # # # # # #
|
# # # # # # # # # # Overrides # # # # # # # # # #
|
||||||
|
|
||||||
def closeEvent(self, event: Optional[QCloseEvent]) -> None:
|
def closeEvent(self, event: QCloseEvent | None) -> None:
|
||||||
"""Handle attempt to close main window"""
|
"""Handle attempt to close main window"""
|
||||||
|
|
||||||
if not event:
|
if not event:
|
||||||
@ -1209,7 +1204,7 @@ class Window(QMainWindow):
|
|||||||
mainwindow_width=self.width(),
|
mainwindow_width=self.width(),
|
||||||
mainwindow_x=self.x(),
|
mainwindow_x=self.x(),
|
||||||
mainwindow_y=self.y(),
|
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():
|
for name, value in attributes_to_save.items():
|
||||||
ds.setting_set(name, value)
|
ds.setting_set(name, value)
|
||||||
@ -1218,16 +1213,17 @@ class Window(QMainWindow):
|
|||||||
|
|
||||||
# # # # # # # # # # Internal utility functions # # # # # # # # # #
|
# # # # # # # # # # Internal utility functions # # # # # # # # # #
|
||||||
|
|
||||||
def active_tab(self) -> PlaylistTab:
|
def _active_tab(self) -> PlaylistTab:
|
||||||
return self.playlist_section.tabPlaylist.currentWidget()
|
return self.playlist_section.tabPlaylist.currentWidget()
|
||||||
|
|
||||||
# # # # # # # # # # Menu functions # # # # # # # # # #
|
# # # # # # # # # # Menu functions # # # # # # # # # #
|
||||||
|
|
||||||
def create_action(
|
def create_action(
|
||||||
self, text: str, handler: Callable, shortcut: Optional[str] = None
|
self, text: str, handler: Callable, shortcut: str | None = None
|
||||||
) -> QAction:
|
) -> 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)
|
action = QAction(text, self)
|
||||||
@ -1280,34 +1276,6 @@ class Window(QMainWindow):
|
|||||||
|
|
||||||
menu.addAction(action)
|
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(
|
def get_new_playlist_dynamic_submenu_items(
|
||||||
self,
|
self,
|
||||||
) -> list[dict[str, str | int | bool]]:
|
) -> list[dict[str, str | int | bool]]:
|
||||||
@ -1375,36 +1343,33 @@ class Window(QMainWindow):
|
|||||||
|
|
||||||
return submenu_items
|
return submenu_items
|
||||||
|
|
||||||
def show_query(self, query_id: int) -> None:
|
def populate_dynamic_submenu(self):
|
||||||
"""
|
"""Dynamically populates submenus when they are selected."""
|
||||||
Show query dialog with query_id selected
|
submenu = self.sender() # Get the submenu that triggered the event
|
||||||
"""
|
|
||||||
|
|
||||||
# Keep a reference else it will be gc'd
|
# Find which submenu it is
|
||||||
self.query_dialog = QueryDialog(query_id)
|
for key, stored_submenu in self.dynamic_submenus.items():
|
||||||
if self.query_dialog.exec():
|
if submenu == stored_submenu:
|
||||||
new_row_number = self.current_row_or_end()
|
submenu.clear()
|
||||||
base_model = self.current.base_model
|
# Dynamically call the correct function
|
||||||
for track_id in self.query_dialog.selected_tracks:
|
items = getattr(self, f"get_{key}_items")()
|
||||||
# Check whether track is already in playlist
|
for item in items:
|
||||||
move_existing = False
|
# Check for separator
|
||||||
existing_prd = base_model.is_track_in_playlist(track_id)
|
if "separator" in item and item["separator"]:
|
||||||
if existing_prd is not None:
|
submenu.addSeparator()
|
||||||
if ask_yes_no(
|
continue
|
||||||
"Duplicate row",
|
action = QAction(item["text"], self)
|
||||||
"Track already in playlist. " "Move to new location?",
|
|
||||||
default_yes=True,
|
|
||||||
):
|
|
||||||
move_existing = True
|
|
||||||
|
|
||||||
if move_existing and existing_prd:
|
# Extract handler and arguments
|
||||||
base_model.move_track_add_note(
|
handler = getattr(self, item["handler"], None)
|
||||||
new_row_number, existing_prd, note=""
|
args = item.get("args", ())
|
||||||
)
|
|
||||||
else:
|
|
||||||
base_model.insert_row(track_id)
|
|
||||||
|
|
||||||
new_row_number += 1
|
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 # # # # # # # # # #
|
# # # # # # # # # # Playlist management functions # # # # # # # # # #
|
||||||
|
|
||||||
@ -1412,7 +1377,7 @@ class Window(QMainWindow):
|
|||||||
def _create_playlist(self, name: str, template_id: int) -> PlaylistDTO:
|
def _create_playlist(self, name: str, template_id: int) -> PlaylistDTO:
|
||||||
"""
|
"""
|
||||||
Create a playlist in the database, populate it from the template
|
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)
|
return ds.playlist_create(name, template_id)
|
||||||
@ -1449,7 +1414,7 @@ class Window(QMainWindow):
|
|||||||
# @log_call
|
# @log_call
|
||||||
def create_playlist_from_template(self, template_id: int) -> None:
|
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:
|
if template_id == 0:
|
||||||
@ -1457,7 +1422,6 @@ class Window(QMainWindow):
|
|||||||
selected_template_id = self.solicit_template_to_use()
|
selected_template_id = self.solicit_template_to_use()
|
||||||
if selected_template_id is None:
|
if selected_template_id is None:
|
||||||
return
|
return
|
||||||
else:
|
|
||||||
template_id = selected_template_id
|
template_id = selected_template_id
|
||||||
|
|
||||||
playlist_name = self.get_playlist_name()
|
playlist_name = self.get_playlist_name()
|
||||||
@ -1520,7 +1484,7 @@ class Window(QMainWindow):
|
|||||||
|
|
||||||
def get_playlist_name(
|
def get_playlist_name(
|
||||||
self, default: str = "", prompt: str = "Playlist name:"
|
self, default: str = "", prompt: str = "Playlist name:"
|
||||||
) -> Optional[str]:
|
) -> str | None:
|
||||||
"""Get a name from the user"""
|
"""Get a name from the user"""
|
||||||
|
|
||||||
dlg = QInputDialog(self)
|
dlg = QInputDialog(self)
|
||||||
@ -1548,8 +1512,8 @@ class Window(QMainWindow):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def solicit_template_to_use(
|
def solicit_template_to_use(
|
||||||
self, template_prompt: Optional[str] = None
|
self, template_prompt: str | None = None
|
||||||
) -> Optional[int]:
|
) -> int | None:
|
||||||
"""
|
"""
|
||||||
Have user select a template. Return the template.id, or None if they cancel.
|
Have user select a template. Return the template.id, or None if they cancel.
|
||||||
template_id of zero means don't use a template.
|
template_id of zero means don't use a template.
|
||||||
@ -1583,12 +1547,48 @@ class Window(QMainWindow):
|
|||||||
|
|
||||||
_ = ManageTemplates(self)
|
_ = 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 # # # # # # # # # #
|
# # # # # # # # # # Miscellaneous functions # # # # # # # # # #
|
||||||
|
|
||||||
def select_duplicate_rows(self, checked: bool = False) -> None:
|
def select_duplicate_rows(self, checked: bool = False) -> None:
|
||||||
"""Call playlist to select duplicate rows"""
|
"""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:
|
def about(self, checked: bool = False) -> None:
|
||||||
"""Get git tag and database name"""
|
"""Get git tag and database name"""
|
||||||
@ -1615,14 +1615,14 @@ class Window(QMainWindow):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
self.track_sequence.set_next(None)
|
self.track_sequence.set_next(None)
|
||||||
self.update_headers()
|
self.signals.next_track_changed_signal.emit()
|
||||||
|
|
||||||
def clear_selection(self, checked: bool = False) -> None:
|
def clear_selection(self, checked: bool = False) -> None:
|
||||||
"""Clear row selection"""
|
"""Clear row selection"""
|
||||||
|
|
||||||
# Unselect any selected rows
|
# Unselect any selected rows
|
||||||
if self.active_tab():
|
if self._active_tab():
|
||||||
self.active_tab().clear_selection()
|
self._active_tab().clear_selection()
|
||||||
|
|
||||||
# Clear the search bar
|
# Clear the search bar
|
||||||
self.search_playlist_clear()
|
self.search_playlist_clear()
|
||||||
@ -1776,6 +1776,7 @@ class Window(QMainWindow):
|
|||||||
# @log_call
|
# @log_call
|
||||||
def end_of_track_actions(self) -> None:
|
def end_of_track_actions(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
Called by track_ended_signal
|
||||||
|
|
||||||
Actions required:
|
Actions required:
|
||||||
- Reset track_sequence objects
|
- Reset track_sequence objects
|
||||||
@ -1788,9 +1789,6 @@ class Window(QMainWindow):
|
|||||||
if self.track_sequence.current:
|
if self.track_sequence.current:
|
||||||
self.track_sequence.move_current_to_previous()
|
self.track_sequence.move_current_to_previous()
|
||||||
|
|
||||||
# Tell playlist previous track has finished
|
|
||||||
self.current.base_model.previous_track_ended()
|
|
||||||
|
|
||||||
# Reset clocks
|
# Reset clocks
|
||||||
self.footer_section.frame_fade.setStyleSheet("")
|
self.footer_section.frame_fade.setStyleSheet("")
|
||||||
self.footer_section.frame_silent.setStyleSheet("")
|
self.footer_section.frame_silent.setStyleSheet("")
|
||||||
@ -1804,10 +1802,6 @@ class Window(QMainWindow):
|
|||||||
self.catch_return_key = False
|
self.catch_return_key = False
|
||||||
self.show_status_message("Play controls: Enabled", 0)
|
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:
|
def export_playlist_tab(self, checked: bool = False) -> None:
|
||||||
"""Export the current playlist to an m3u file"""
|
"""Export the current playlist to an m3u file"""
|
||||||
|
|
||||||
@ -1852,7 +1846,7 @@ class Window(QMainWindow):
|
|||||||
if self.track_sequence.current:
|
if self.track_sequence.current:
|
||||||
self.track_sequence.current.fade()
|
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,
|
Return the tab index for the passed playlist_id if it is displayed,
|
||||||
else return None.
|
else return None.
|
||||||
@ -1875,12 +1869,12 @@ class Window(QMainWindow):
|
|||||||
self.hide_played_tracks = True
|
self.hide_played_tracks = True
|
||||||
self.footer_section.btnHidePlayed.setText("Show played")
|
self.footer_section.btnHidePlayed.setText("Show played")
|
||||||
if Config.HIDE_PLAYED_MODE == Config.HIDE_PLAYED_MODE_SECTIONS:
|
if Config.HIDE_PLAYED_MODE == Config.HIDE_PLAYED_MODE_SECTIONS:
|
||||||
self.active_tab().hide_played_sections()
|
self._active_tab().hide_played_sections()
|
||||||
else:
|
else:
|
||||||
self.current.base_model.hide_played_tracks(True)
|
self.current.base_model.hide_played_tracks(True)
|
||||||
|
|
||||||
# Reset row heights
|
# Reset row heights
|
||||||
self.active_tab().resize_rows()
|
self._active_tab().resize_rows()
|
||||||
|
|
||||||
def import_files_wrapper(self, checked: bool = False) -> None:
|
def import_files_wrapper(self, checked: bool = False) -> None:
|
||||||
"""
|
"""
|
||||||
@ -1902,12 +1896,18 @@ class Window(QMainWindow):
|
|||||||
dlg.resize(500, 100)
|
dlg.resize(500, 100)
|
||||||
ok = dlg.exec()
|
ok = dlg.exec()
|
||||||
if ok:
|
if ok:
|
||||||
self.current.base_model.insert_row(note=dlg.textValue())
|
self.signals.signal_insert_track.emit(
|
||||||
|
InsertTrack(
|
||||||
|
playlist_id=self.current.base_model.playlist_id,
|
||||||
|
track_id=None,
|
||||||
|
note=dlg.textValue()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def insert_track(self, checked: bool = False) -> None:
|
def insert_track(self, checked: bool = False) -> None:
|
||||||
"""Show dialog box to select and add track from database"""
|
"""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()
|
dlg.exec()
|
||||||
|
|
||||||
# @log_call
|
# @log_call
|
||||||
@ -1921,7 +1921,7 @@ class Window(QMainWindow):
|
|||||||
playlist_ids.append(self._open_playlist(playlist))
|
playlist_ids.append(self._open_playlist(playlist))
|
||||||
|
|
||||||
# Set active tab
|
# Set active tab
|
||||||
value = ds.setting_get("active_tab")
|
value = ds.setting_get("active_index")
|
||||||
if value is not None and value >= 0:
|
if value is not None and value >= 0:
|
||||||
self.playlist_section.tabPlaylist.setCurrentIndex(value)
|
self.playlist_section.tabPlaylist.setCurrentIndex(value)
|
||||||
|
|
||||||
@ -1956,7 +1956,7 @@ class Window(QMainWindow):
|
|||||||
# paste
|
# paste
|
||||||
self.move_source = MoveSource(
|
self.move_source = MoveSource(
|
||||||
model=self.current.base_model,
|
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=}")
|
log.debug(f"mark_rows_for_moving(): {self.move_source=}")
|
||||||
@ -1967,6 +1967,9 @@ class Window(QMainWindow):
|
|||||||
Move passed playlist rows to another playlist
|
Move passed playlist rows to another playlist
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if not row_numbers:
|
||||||
|
return
|
||||||
|
|
||||||
# Identify destination playlist
|
# Identify destination playlist
|
||||||
playlists = []
|
playlists = []
|
||||||
source_playlist_id = self.current.playlist_id
|
source_playlist_id = self.current.playlist_id
|
||||||
@ -2000,11 +2003,7 @@ class Window(QMainWindow):
|
|||||||
Move selected rows to another playlist
|
Move selected rows to another playlist
|
||||||
"""
|
"""
|
||||||
|
|
||||||
selected_rows = self.current.selected_row_numbers
|
self.move_playlist_rows(self.current.selected_row_numbers)
|
||||||
if not selected_rows:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.move_playlist_rows(selected_rows)
|
|
||||||
|
|
||||||
def move_unplayed(self, checked: bool = False) -> None:
|
def move_unplayed(self, checked: bool = False) -> None:
|
||||||
"""
|
"""
|
||||||
@ -2061,8 +2060,8 @@ class Window(QMainWindow):
|
|||||||
from_rows, to_row, to_playlist_model.playlist_id
|
from_rows, to_row, to_playlist_model.playlist_id
|
||||||
)
|
)
|
||||||
|
|
||||||
self.active_tab().resize_rows()
|
self._active_tab().resize_rows()
|
||||||
self.active_tab().clear_selection()
|
self._active_tab().clear_selection()
|
||||||
|
|
||||||
# If we move a row to immediately under the current track, make
|
# If we move a row to immediately under the current track, make
|
||||||
# that moved row the next track
|
# that moved row the next track
|
||||||
@ -2075,7 +2074,7 @@ class Window(QMainWindow):
|
|||||||
|
|
||||||
# @log_call
|
# @log_call
|
||||||
def play_next(
|
def play_next(
|
||||||
self, position: Optional[float] = None, checked: bool = False
|
self, position: float | None = None, checked: bool = False
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Play next track, optionally from passed position.
|
Play next track, optionally from passed position.
|
||||||
@ -2105,7 +2104,7 @@ class Window(QMainWindow):
|
|||||||
return
|
return
|
||||||
|
|
||||||
# Issue #223 concerns a very short pause (maybe 0.1s) sometimes
|
# 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
|
# disable timer10 for a short time. Timer is re-enabled in
|
||||||
# update_clocks.
|
# update_clocks.
|
||||||
|
|
||||||
@ -2116,9 +2115,11 @@ class Window(QMainWindow):
|
|||||||
if self.track_sequence.current:
|
if self.track_sequence.current:
|
||||||
self.track_sequence.current.fade()
|
self.track_sequence.current.fade()
|
||||||
|
|
||||||
# Move next track to current track.
|
# Move next track to current track. end_of_track_actions() will
|
||||||
# end_of_track_actions() will have saved current track to
|
# 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
|
# previous_track
|
||||||
|
|
||||||
self.track_sequence.move_next_to_current()
|
self.track_sequence.move_next_to_current()
|
||||||
if self.track_sequence.current is None:
|
if self.track_sequence.current is None:
|
||||||
raise ApplicationError("No current track")
|
raise ApplicationError("No current track")
|
||||||
@ -2146,6 +2147,9 @@ class Window(QMainWindow):
|
|||||||
self.catch_return_key = True
|
self.catch_return_key = True
|
||||||
self.show_status_message("Play controls: Disabled", 0)
|
self.show_status_message("Play controls: Disabled", 0)
|
||||||
|
|
||||||
|
# Record playdate
|
||||||
|
ds.playdates_update(self.track_sequence.current.track_id)
|
||||||
|
|
||||||
# Notify others
|
# Notify others
|
||||||
self.signals.signal_track_started.emit(
|
self.signals.signal_track_started.emit(
|
||||||
TrackAndPlaylist(
|
TrackAndPlaylist(
|
||||||
@ -2154,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
|
# Update headers
|
||||||
self.update_headers()
|
self.update_headers()
|
||||||
|
|
||||||
@ -2181,7 +2170,7 @@ class Window(QMainWindow):
|
|||||||
|
|
||||||
if self.footer_section.btnPreview.isChecked():
|
if self.footer_section.btnPreview.isChecked():
|
||||||
# Get track path for first selected track if there is one
|
# 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:
|
if not track_info:
|
||||||
# Otherwise get track_id to next track to play
|
# Otherwise get track_id to next track to play
|
||||||
if self.track_sequence.next:
|
if self.track_sequence.next:
|
||||||
@ -2190,10 +2179,9 @@ class Window(QMainWindow):
|
|||||||
self.track_sequence.next.track_id,
|
self.track_sequence.next.track_id,
|
||||||
self.track_sequence.next.row_number,
|
self.track_sequence.next.row_number,
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
return
|
|
||||||
if not track_info:
|
if not track_info:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.preview_manager.row_number = track_info.row_number
|
self.preview_manager.row_number = track_info.row_number
|
||||||
track = ds.track_by_id(track_info.track_id)
|
track = ds.track_by_id(track_info.track_id)
|
||||||
if not track:
|
if not track:
|
||||||
@ -2247,7 +2235,7 @@ class Window(QMainWindow):
|
|||||||
return
|
return
|
||||||
|
|
||||||
intro = round(self.preview_manager.get_playtime() / 100) * 100
|
intro = round(self.preview_manager.get_playtime() / 100) * 100
|
||||||
ds.track_set_intro(track_id, intro)
|
ds.track_update(track_id, dict(intro=intro))
|
||||||
self.preview_manager.set_intro(intro)
|
self.preview_manager.set_intro(intro)
|
||||||
self.current.base_model.refresh_row(row_number)
|
self.current.base_model.refresh_row(row_number)
|
||||||
roles = [
|
roles = [
|
||||||
@ -2381,7 +2369,7 @@ class Window(QMainWindow):
|
|||||||
# Disable play controls so that 'return' in search box doesn't
|
# Disable play controls so that 'return' in search box doesn't
|
||||||
# play next track
|
# play next track
|
||||||
self.catch_return_key = True
|
self.catch_return_key = True
|
||||||
self.txtSearch.setHidden(False)
|
self.txtSearch.setVisible(True)
|
||||||
self.txtSearch.setFocus()
|
self.txtSearch.setFocus()
|
||||||
# Select any text that may already be there
|
# Select any text that may already be there
|
||||||
self.txtSearch.selectAll()
|
self.txtSearch.selectAll()
|
||||||
@ -2400,13 +2388,13 @@ class Window(QMainWindow):
|
|||||||
|
|
||||||
self.current.proxy_model.set_incremental_search(self.txtSearch.text())
|
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
|
Return RowAndTrack info for selected track. If no selected track, return for
|
||||||
next track. If no next track, return None.
|
next track. If no next track, return None.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
row_number: Optional[int] = None
|
row_number: int | None = None
|
||||||
|
|
||||||
if self.current.selected_row_numbers:
|
if self.current.selected_row_numbers:
|
||||||
row_number = self.current.selected_row_numbers[0]
|
row_number = self.current.selected_row_numbers[0]
|
||||||
@ -2440,20 +2428,6 @@ class Window(QMainWindow):
|
|||||||
|
|
||||||
self.signals.signal_set_next_row.emit(self.current.playlist_id)
|
self.signals.signal_set_next_row.emit(self.current.playlist_id)
|
||||||
self.clear_selection()
|
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:
|
def show_current(self) -> None:
|
||||||
"""Scroll to show current track"""
|
"""Scroll to show current track"""
|
||||||
@ -2463,10 +2437,9 @@ class Window(QMainWindow):
|
|||||||
|
|
||||||
def show_warning(self, title: str, body: str) -> None:
|
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)
|
QMessageBox.warning(self, title, body)
|
||||||
|
|
||||||
def show_next(self) -> None:
|
def show_next(self) -> None:
|
||||||
@ -2477,6 +2450,7 @@ class Window(QMainWindow):
|
|||||||
|
|
||||||
def show_status_message(self, message: str, timing: int) -> None:
|
def show_status_message(self, message: str, timing: int) -> None:
|
||||||
"""
|
"""
|
||||||
|
Handle status_message_signal.
|
||||||
Show status message in status bar for timing milliseconds
|
Show status message in status bar for timing milliseconds
|
||||||
Clear message if message is null string
|
Clear message if message is null string
|
||||||
"""
|
"""
|
||||||
@ -2488,7 +2462,7 @@ class Window(QMainWindow):
|
|||||||
self.statusbar.clearMessage()
|
self.statusbar.clearMessage()
|
||||||
|
|
||||||
def show_track(self, playlist_track: PlaylistRow) -> None:
|
def show_track(self, playlist_track: PlaylistRow) -> None:
|
||||||
"""Scroll to show track in plt"""
|
"""Scroll to show track"""
|
||||||
|
|
||||||
# Switch to the correct tab
|
# Switch to the correct tab
|
||||||
playlist_id = playlist_track.playlist_id
|
playlist_id = playlist_track.playlist_id
|
||||||
@ -2506,20 +2480,19 @@ class Window(QMainWindow):
|
|||||||
f"show_track() can't find current playlist tab {playlist_id=}"
|
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
|
# @log_call
|
||||||
def stop(self, checked: bool = False) -> None:
|
def stop(self, checked: bool = False) -> None:
|
||||||
"""Stop playing immediately"""
|
"""Stop playing immediately"""
|
||||||
|
|
||||||
self.stop_autoplay = True
|
|
||||||
if self.track_sequence.current:
|
if self.track_sequence.current:
|
||||||
self.track_sequence.current.stop()
|
self.track_sequence.current.stop()
|
||||||
|
|
||||||
def tab_change(self) -> None:
|
def tab_change(self) -> None:
|
||||||
"""Called when active tab changed"""
|
"""Called when active tab changed"""
|
||||||
|
|
||||||
self.active_tab().tab_live()
|
self._active_tab().tab_live()
|
||||||
|
|
||||||
def tick_10ms(self) -> None:
|
def tick_10ms(self) -> None:
|
||||||
"""
|
"""
|
||||||
@ -2538,9 +2511,11 @@ class Window(QMainWindow):
|
|||||||
try:
|
try:
|
||||||
self.track_sequence.current.check_for_end_of_track()
|
self.track_sequence.current.check_for_end_of_track()
|
||||||
|
|
||||||
# Update intro counter if applicable and, if updated, return
|
# Update intro counter if applicable and, if updated,
|
||||||
# because playing an intro takes precedence over timing a
|
# return because playing an intro uses the intro field to
|
||||||
|
# show timing and this takes precedence over timing a
|
||||||
# preview.
|
# preview.
|
||||||
|
|
||||||
intro_ms_remaining = self.track_sequence.current.time_remaining_intro()
|
intro_ms_remaining = self.track_sequence.current.time_remaining_intro()
|
||||||
if intro_ms_remaining > 0:
|
if intro_ms_remaining > 0:
|
||||||
self.footer_section.label_intro_timer.setText(
|
self.footer_section.label_intro_timer.setText(
|
||||||
@ -2559,7 +2534,6 @@ class Window(QMainWindow):
|
|||||||
# current track ended during servicing tick
|
# current track ended during servicing tick
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Ensure preview button is reset if preview finishes playing
|
|
||||||
# Update preview timer
|
# Update preview timer
|
||||||
if self.footer_section.btnPreview.isChecked():
|
if self.footer_section.btnPreview.isChecked():
|
||||||
if self.preview_manager.is_playing():
|
if self.preview_manager.is_playing():
|
||||||
@ -2571,6 +2545,8 @@ class Window(QMainWindow):
|
|||||||
f"{int(minutes)}:{seconds:04.1f}"
|
f"{int(minutes)}:{seconds:04.1f}"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
# Ensure preview button is reset if preview has finished
|
||||||
|
# playing
|
||||||
self.footer_section.btnPreview.setChecked(False)
|
self.footer_section.btnPreview.setChecked(False)
|
||||||
self.footer_section.label_intro_timer.setText("0.0")
|
self.footer_section.label_intro_timer.setText("0.0")
|
||||||
self.footer_section.label_intro_timer.setStyleSheet("")
|
self.footer_section.label_intro_timer.setStyleSheet("")
|
||||||
|
|||||||
@ -36,6 +36,7 @@ from classes import (
|
|||||||
InsertRows,
|
InsertRows,
|
||||||
InsertTrack,
|
InsertTrack,
|
||||||
MusicMusterSignals,
|
MusicMusterSignals,
|
||||||
|
SelectedRows,
|
||||||
TrackAndPlaylist,
|
TrackAndPlaylist,
|
||||||
)
|
)
|
||||||
from config import Config
|
from config import Config
|
||||||
@ -95,9 +96,11 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
self.signals.signal_add_track_to_header.connect(self.add_track_to_header)
|
self.signals.signal_add_track_to_header.connect(self.add_track_to_header)
|
||||||
self.signals.signal_begin_insert_rows.connect(self.begin_insert_rows)
|
self.signals.signal_begin_insert_rows.connect(self.begin_insert_rows)
|
||||||
self.signals.signal_end_insert_rows.connect(self.end_insert_rows)
|
self.signals.signal_end_insert_rows.connect(self.end_insert_rows)
|
||||||
|
self.signals.signal_insert_track.connect(self.insert_row_signal_handler)
|
||||||
self.signals.signal_playlist_selected_rows.connect(self.set_selected_rows)
|
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_set_next_row.connect(self.set_next_row)
|
||||||
self.signals.signal_track_started.connect(self.track_started)
|
self.signals.signal_track_started.connect(self.track_started)
|
||||||
|
self.signals.track_ended_signal.connect(self.previous_track_ended)
|
||||||
|
|
||||||
# Populate self.playlist_rows
|
# Populate self.playlist_rows
|
||||||
for dto in ds.playlistrows_by_playlist(self.playlist_id):
|
for dto in ds.playlistrows_by_playlist(self.playlist_id):
|
||||||
@ -265,7 +268,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
track_id = play_track.track_id
|
track_id = play_track.track_id
|
||||||
# Sanity check - 1
|
# Sanity check - 1
|
||||||
if not track_id:
|
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
|
# Sanity check - 2
|
||||||
if self.track_sequence.current is None:
|
if self.track_sequence.current is None:
|
||||||
@ -294,7 +297,9 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
# Update previous row in case we're hiding played rows
|
# Update previous row in case we're hiding played rows
|
||||||
if self.track_sequence.previous and self.track_sequence.previous.row_number:
|
if self.track_sequence.previous and self.track_sequence.previous.row_number:
|
||||||
# only invalidate required roles
|
# only invalidate required roles
|
||||||
self.invalidate_row(self.track_sequence.previous.row_number, roles_to_invalidate)
|
self.invalidate_row(
|
||||||
|
self.track_sequence.previous.row_number, roles_to_invalidate
|
||||||
|
)
|
||||||
|
|
||||||
# Update all other track times
|
# Update all other track times
|
||||||
self.update_track_times()
|
self.update_track_times()
|
||||||
@ -677,11 +682,14 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
self.invalidate_row(row_number, roles)
|
self.invalidate_row(row_number, roles)
|
||||||
|
|
||||||
# @log_call
|
# @log_call
|
||||||
def insert_row(self, track_id: Optional[int] = None, note: str = "",) -> None:
|
def insert_row_signal_handler(self, row_data: InsertTrack) -> None:
|
||||||
"""
|
"""
|
||||||
Insert a row.
|
Handle the signal_insert_track signal
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if row_data.playlist_id != self.playlist_id:
|
||||||
|
return
|
||||||
|
|
||||||
new_row_number = self._get_new_row_number()
|
new_row_number = self._get_new_row_number()
|
||||||
|
|
||||||
super().beginInsertRows(QModelIndex(), new_row_number, new_row_number)
|
super().beginInsertRows(QModelIndex(), new_row_number, new_row_number)
|
||||||
@ -689,8 +697,8 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
_ = ds.playlist_insert_row(
|
_ = ds.playlist_insert_row(
|
||||||
playlist_id=self.playlist_id,
|
playlist_id=self.playlist_id,
|
||||||
row_number=new_row_number,
|
row_number=new_row_number,
|
||||||
track_id=track_id,
|
track_id=row_data.track_id,
|
||||||
note=note,
|
note=row_data.note,
|
||||||
)
|
)
|
||||||
# Need to refresh self.playlist_rows because row numbers will have
|
# Need to refresh self.playlist_rows because row numbers will have
|
||||||
# changed
|
# changed
|
||||||
@ -796,14 +804,16 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
if self.track_sequence.current:
|
if self.track_sequence.current:
|
||||||
current_row = self.track_sequence.current.row_number
|
current_row = self.track_sequence.current.row_number
|
||||||
if current_row in from_rows:
|
if current_row in from_rows:
|
||||||
log.debug(
|
log.debug("move_rows: Removing {current_row=} from {from_rows=}")
|
||||||
"move_rows: Removing {current_row=} from {from_rows=}"
|
|
||||||
)
|
|
||||||
from_rows.remove(self.track_sequence.current.row_number)
|
from_rows.remove(self.track_sequence.current.row_number)
|
||||||
|
|
||||||
from_rows = sorted(set(from_rows))
|
from_rows = sorted(set(from_rows))
|
||||||
if (min(from_rows) < 0 or max(from_rows) >= self.rowCount()
|
if (
|
||||||
or to_row_number < 0 or to_row_number > self.rowCount()):
|
min(from_rows) < 0
|
||||||
|
or max(from_rows) >= self.rowCount()
|
||||||
|
or to_row_number < 0
|
||||||
|
or to_row_number > self.rowCount()
|
||||||
|
):
|
||||||
log.debug("move_rows: invalid indexes")
|
log.debug("move_rows: invalid indexes")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -861,15 +871,16 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
# Prepare source model
|
# Prepare source model
|
||||||
super().beginRemoveRows(QModelIndex(), min(row_group), max(row_group))
|
super().beginRemoveRows(QModelIndex(), min(row_group), max(row_group))
|
||||||
# Prepare destination model
|
# Prepare destination model
|
||||||
insert_rows = InsertRows(to_playlist_id,
|
insert_rows = InsertRows(
|
||||||
to_row_number,
|
to_playlist_id, to_row_number, to_row_number + len(row_group)
|
||||||
to_row_number + len(row_group)
|
|
||||||
)
|
)
|
||||||
self.signals.signal_begin_insert_rows.emit(insert_rows)
|
self.signals.signal_begin_insert_rows.emit(insert_rows)
|
||||||
ds.playlist_move_rows(from_rows=row_group,
|
ds.playlist_move_rows(
|
||||||
|
from_rows=row_group,
|
||||||
from_playlist_id=self.playlist_id,
|
from_playlist_id=self.playlist_id,
|
||||||
to_row=to_row_number,
|
to_row=to_row_number,
|
||||||
to_playlist_id=to_playlist_id)
|
to_playlist_id=to_playlist_id,
|
||||||
|
)
|
||||||
self.signals.signal_end_insert_rows.emit(to_playlist_id)
|
self.signals.signal_end_insert_rows.emit(to_playlist_id)
|
||||||
super().endRemoveRows()
|
super().endRemoveRows()
|
||||||
|
|
||||||
@ -953,15 +964,19 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
return
|
return
|
||||||
|
|
||||||
# @log_call
|
# @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:
|
Actions required:
|
||||||
- sanity check
|
- sanity check
|
||||||
- update display
|
- update display
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if playlist_id != self.playlist_id:
|
||||||
|
# Not for us
|
||||||
|
return
|
||||||
|
|
||||||
# Sanity check
|
# Sanity check
|
||||||
if not self.track_sequence.previous:
|
if not self.track_sequence.previous:
|
||||||
log.error(
|
log.error(
|
||||||
@ -1013,7 +1028,9 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
plrid = self.playlist_rows[row_number].playlistrow_id
|
plrid = self.playlist_rows[row_number].playlistrow_id
|
||||||
refreshed_row = ds.playlistrow_by_id(plrid)
|
refreshed_row = ds.playlistrow_by_id(plrid)
|
||||||
if not refreshed_row:
|
if not refreshed_row:
|
||||||
raise ApplicationError(f"Failed to retrieve row {self.playlist_id=}, {row_number=}")
|
raise ApplicationError(
|
||||||
|
f"Failed to retrieve row {self.playlist_id=}, {row_number=}"
|
||||||
|
)
|
||||||
|
|
||||||
self.playlist_rows[row_number] = PlaylistRow(refreshed_row)
|
self.playlist_rows[row_number] = PlaylistRow(refreshed_row)
|
||||||
|
|
||||||
@ -1230,16 +1247,16 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
# @log_call
|
# @log_call
|
||||||
def set_selected_rows(self, playlist_id: int, selected_row_numbers: list[int]) -> None:
|
def set_selected_rows(self, selected_rows: SelectedRows) -> None:
|
||||||
"""
|
"""
|
||||||
Handle signal_playlist_selected_rows to keep track of which rows
|
Handle signal_playlist_selected_rows to keep track of which rows
|
||||||
are selected in the view
|
are selected in the view
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if playlist_id != self.playlist_id:
|
if selected_rows.playlist_id != self.playlist_id:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.selected_rows = [self.playlist_rows[a] for a in selected_row_numbers]
|
self.selected_rows = [self.playlist_rows[a] for a in selected_rows.rows]
|
||||||
|
|
||||||
# @log_call
|
# @log_call
|
||||||
def set_next_row(self, playlist_id: int) -> None:
|
def set_next_row(self, playlist_id: int) -> None:
|
||||||
@ -1247,9 +1264,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
Handle signal_set_next_row
|
Handle signal_set_next_row
|
||||||
"""
|
"""
|
||||||
|
|
||||||
log.debug(f"{self}: set_next_row({playlist_id=})")
|
|
||||||
if playlist_id != self.playlist_id:
|
if playlist_id != self.playlist_id:
|
||||||
# Not for us
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if len(self.selected_rows) == 0:
|
if len(self.selected_rows) == 0:
|
||||||
@ -1294,7 +1309,10 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
role: int = Qt.ItemDataRole.EditRole,
|
role: int = Qt.ItemDataRole.EditRole,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""
|
"""
|
||||||
Update model with edited data
|
Update model with edited data. Here we simply update the
|
||||||
|
playlist_row in self.playlist_rows. The act of doing that will
|
||||||
|
trigger a database update in the @setter property in the
|
||||||
|
PlaylistRow class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not index.isValid() or role != Qt.ItemDataRole.EditRole:
|
if not index.isValid() or role != Qt.ItemDataRole.EditRole:
|
||||||
@ -1304,14 +1322,19 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
column = index.column()
|
column = index.column()
|
||||||
plr = self.playlist_rows[row_number]
|
plr = self.playlist_rows[row_number]
|
||||||
|
|
||||||
if column == Col.TITLE.value:
|
if column == Col.NOTE.value:
|
||||||
plr.title = str(value)
|
plr.note = value
|
||||||
|
|
||||||
|
elif column == Col.TITLE.value:
|
||||||
|
plr.title = value
|
||||||
|
|
||||||
elif column == Col.ARTIST.value:
|
elif column == Col.ARTIST.value:
|
||||||
plr.artist = str(value)
|
plr.artist = value
|
||||||
|
|
||||||
elif column == Col.INTRO.value:
|
elif column == Col.INTRO.value:
|
||||||
plr.intro = int(round(float(value), 1) * 1000)
|
intro = int(round(float(value), 1) * 1000)
|
||||||
elif column == Col.NOTE.value:
|
plr.intro = intro
|
||||||
plr.note = str(value)
|
|
||||||
else:
|
else:
|
||||||
raise ApplicationError(f"setData called with unexpected column ({column=})")
|
raise ApplicationError(f"setData called with unexpected column ({column=})")
|
||||||
|
|
||||||
@ -1445,7 +1468,9 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
]
|
]
|
||||||
self.invalidate_rows(track_rows, roles)
|
self.invalidate_rows(track_rows, roles)
|
||||||
else:
|
else:
|
||||||
self.insert_row(track_id=track_id)
|
self.insert_row_signal_handler(
|
||||||
|
InsertTrack(playlist_id=self.playlist_id, track_id=track_id, note="")
|
||||||
|
)
|
||||||
|
|
||||||
# @log_call
|
# @log_call
|
||||||
def update_track_times(self) -> None:
|
def update_track_times(self) -> None:
|
||||||
@ -1471,7 +1496,10 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
update_rows, self.track_sequence.current.start_time
|
update_rows, self.track_sequence.current.start_time
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.track_sequence.next and self.track_sequence.next.playlist_id == self.playlist_id:
|
if (
|
||||||
|
self.track_sequence.next
|
||||||
|
and self.track_sequence.next.playlist_id == self.playlist_id
|
||||||
|
):
|
||||||
next_track_row = self.track_sequence.next.row_number
|
next_track_row = self.track_sequence.next.row_number
|
||||||
|
|
||||||
for row_number in range(row_count):
|
for row_number in range(row_count):
|
||||||
@ -1576,15 +1604,22 @@ class PlaylistProxyModel(QSortFilterProxyModel):
|
|||||||
):
|
):
|
||||||
# This row isn't our previous track: hide it
|
# This row isn't our previous track: hide it
|
||||||
return False
|
return False
|
||||||
if self.track_sequence.current and self.track_sequence.current.start_time:
|
if (
|
||||||
|
self.track_sequence.current
|
||||||
|
and self.track_sequence.current.start_time
|
||||||
|
):
|
||||||
# This row is our previous track. Don't hide it
|
# This row is our previous track. Don't hide it
|
||||||
# until HIDE_AFTER_PLAYING_OFFSET milliseconds
|
# until HIDE_AFTER_PLAYING_OFFSET milliseconds
|
||||||
# after current track has started
|
# after current track has started
|
||||||
if self.track_sequence.current.start_time and dt.datetime.now() > (
|
if (
|
||||||
|
self.track_sequence.current.start_time
|
||||||
|
and dt.datetime.now()
|
||||||
|
> (
|
||||||
self.track_sequence.current.start_time
|
self.track_sequence.current.start_time
|
||||||
+ dt.timedelta(
|
+ dt.timedelta(
|
||||||
milliseconds=Config.HIDE_AFTER_PLAYING_OFFSET
|
milliseconds=Config.HIDE_AFTER_PLAYING_OFFSET
|
||||||
)
|
)
|
||||||
|
)
|
||||||
):
|
):
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -71,8 +71,12 @@ class PlaylistRow:
|
|||||||
return ""
|
return ""
|
||||||
|
|
||||||
@artist.setter
|
@artist.setter
|
||||||
def artist(self, value: str) -> None:
|
def artist(self, artist: str) -> None:
|
||||||
print(f"set artist attribute for {self=}, {value=}")
|
if not self.dto.track:
|
||||||
|
raise ApplicationError(f"No track_id when trying to set artist ({self})")
|
||||||
|
|
||||||
|
self.dto.track.artist = artist
|
||||||
|
ds.track_update(self.track_id, dict(artist=str(artist)))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bitrate(self):
|
def bitrate(self):
|
||||||
@ -103,8 +107,12 @@ class PlaylistRow:
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
@intro.setter
|
@intro.setter
|
||||||
def intro(self, value: int) -> None:
|
def intro(self, intro: int) -> None:
|
||||||
print(f"set intro attribute for {self=}, {value=}")
|
if not self.dto.track:
|
||||||
|
raise ApplicationError(f"No track_id when trying to set intro ({self})")
|
||||||
|
|
||||||
|
self.dto.track.intro = intro
|
||||||
|
ds.track_update(self.track_id, dict(intro=str(intro)))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def lastplayed(self):
|
def lastplayed(self):
|
||||||
@ -142,8 +150,12 @@ class PlaylistRow:
|
|||||||
return ""
|
return ""
|
||||||
|
|
||||||
@title.setter
|
@title.setter
|
||||||
def title(self, value: str) -> None:
|
def title(self, title: str) -> None:
|
||||||
print(f"set title attribute for {self=}, {value=}")
|
if not self.dto.track:
|
||||||
|
raise ApplicationError(f"No track_id when trying to set title ({self})")
|
||||||
|
|
||||||
|
self.dto.track.title = title
|
||||||
|
ds.track_update(self.track_id, dict(title=str(title)))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def track_id(self):
|
def track_id(self):
|
||||||
@ -175,20 +187,18 @@ class PlaylistRow:
|
|||||||
return self.dto.note
|
return self.dto.note
|
||||||
|
|
||||||
@note.setter
|
@note.setter
|
||||||
def note(self, value: str) -> None:
|
def note(self, note: str) -> None:
|
||||||
# TODO set up write access to db
|
self.dto.note = note
|
||||||
print(f"set note attribute for {self=}, {value=}")
|
ds.playlistrow_update_note(self.playlistrow_id, str(note))
|
||||||
# self.dto.note = value
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def played(self):
|
def played(self):
|
||||||
return self.dto.played
|
return self.dto.played
|
||||||
|
|
||||||
@played.setter
|
@played.setter
|
||||||
def played(self, value: bool = True) -> None:
|
def played(self, value: bool) -> None:
|
||||||
# TODO set up write access to db
|
self.dto.played = True
|
||||||
print(f"set played attribute for {self=}")
|
ds.playlistrow_played(self.playlistrow_id, value)
|
||||||
# self.dto.played = value
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def playlist_id(self):
|
def playlist_id(self):
|
||||||
@ -204,7 +214,8 @@ class PlaylistRow:
|
|||||||
|
|
||||||
@row_number.setter
|
@row_number.setter
|
||||||
def row_number(self, value: int) -> None:
|
def row_number(self, value: int) -> None:
|
||||||
# TODO do we need to set up write access to db?
|
# This does not update the database which must take place
|
||||||
|
# elsewhere
|
||||||
self.dto.row_number = value
|
self.dto.row_number = value
|
||||||
|
|
||||||
def check_for_end_of_track(self) -> None:
|
def check_for_end_of_track(self) -> None:
|
||||||
@ -226,7 +237,7 @@ class PlaylistRow:
|
|||||||
self.fade_graph.clear()
|
self.fade_graph.clear()
|
||||||
# Ensure that player is released
|
# Ensure that player is released
|
||||||
self.music.fade(0)
|
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
|
self.end_of_track_signalled = True
|
||||||
|
|
||||||
def drop3db(self, enable: bool) -> None:
|
def drop3db(self, enable: bool) -> None:
|
||||||
@ -244,7 +255,8 @@ class PlaylistRow:
|
|||||||
|
|
||||||
self.resume_marker = self.music.get_position()
|
self.resume_marker = self.music.get_position()
|
||||||
self.music.fade(fade_seconds)
|
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:
|
def is_playing(self) -> bool:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -40,6 +40,7 @@ from classes import (
|
|||||||
Col,
|
Col,
|
||||||
MusicMusterSignals,
|
MusicMusterSignals,
|
||||||
PlaylistStyle,
|
PlaylistStyle,
|
||||||
|
SelectedRows,
|
||||||
TrackAndPlaylist,
|
TrackAndPlaylist,
|
||||||
TrackInfo
|
TrackInfo
|
||||||
)
|
)
|
||||||
@ -471,7 +472,9 @@ class PlaylistTab(QTableView):
|
|||||||
selected_row_numbers = self.get_selected_rows()
|
selected_row_numbers = self.get_selected_rows()
|
||||||
|
|
||||||
# Signal selected rows to model
|
# Signal selected rows to model
|
||||||
self.signals.signal_playlist_selected_rows.emit(self.playlist_id, selected_row_numbers)
|
self.signals.signal_playlist_selected_rows.emit(
|
||||||
|
SelectedRows(self.playlist_id, selected_row_numbers)
|
||||||
|
)
|
||||||
|
|
||||||
# Put sum of selected tracks' duration in status bar
|
# Put sum of selected tracks' duration in status bar
|
||||||
# If no rows are selected, we have nothing to do
|
# If no rows are selected, we have nothing to do
|
||||||
|
|||||||
@ -1,131 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>Dialog</class>
|
|
||||||
<widget class="QDialog" name="Dialog">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>584</width>
|
|
||||||
<height>377</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Dialog</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QLabel" name="label">
|
|
||||||
<property name="text">
|
|
||||||
<string>Title:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="1">
|
|
||||||
<widget class="QLineEdit" name="searchString"/>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0" colspan="2">
|
|
||||||
<widget class="QListWidget" name="matchList"/>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0" colspan="2">
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="lblNote">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>46</width>
|
|
||||||
<height>16777215</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>&Note:</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>txtNote</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLineEdit" name="txtNote"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="0" colspan="2">
|
|
||||||
<widget class="QLabel" name="dbPath">
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="0" colspan="2">
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
|
||||||
<item>
|
|
||||||
<widget class="QRadioButton" name="radioTitle">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Title</string>
|
|
||||||
</property>
|
|
||||||
<property name="checked">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QRadioButton" name="radioArtist">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Artist</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<spacer name="horizontalSpacer_2">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>40</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="btnAdd">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Add</string>
|
|
||||||
</property>
|
|
||||||
<property name="default">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="btnAddClose">
|
|
||||||
<property name="text">
|
|
||||||
<string>A&dd and close</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="btnClose">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Close</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
||||||
@ -1,83 +0,0 @@
|
|||||||
# Form implementation generated from reading ui file 'dlg_TrackSelect.ui'
|
|
||||||
#
|
|
||||||
# Created by: PyQt6 UI code generator 6.5.3
|
|
||||||
#
|
|
||||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
|
||||||
# run again. Do not edit this file unless you know what you are doing.
|
|
||||||
|
|
||||||
|
|
||||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
|
||||||
|
|
||||||
|
|
||||||
class Ui_Dialog(object):
|
|
||||||
def setupUi(self, Dialog):
|
|
||||||
Dialog.setObjectName("Dialog")
|
|
||||||
Dialog.resize(584, 377)
|
|
||||||
self.gridLayout = QtWidgets.QGridLayout(Dialog)
|
|
||||||
self.gridLayout.setObjectName("gridLayout")
|
|
||||||
self.label = QtWidgets.QLabel(parent=Dialog)
|
|
||||||
self.label.setObjectName("label")
|
|
||||||
self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
|
|
||||||
self.searchString = QtWidgets.QLineEdit(parent=Dialog)
|
|
||||||
self.searchString.setObjectName("searchString")
|
|
||||||
self.gridLayout.addWidget(self.searchString, 0, 1, 1, 1)
|
|
||||||
self.matchList = QtWidgets.QListWidget(parent=Dialog)
|
|
||||||
self.matchList.setObjectName("matchList")
|
|
||||||
self.gridLayout.addWidget(self.matchList, 1, 0, 1, 2)
|
|
||||||
self.horizontalLayout = QtWidgets.QHBoxLayout()
|
|
||||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
|
||||||
self.lblNote = QtWidgets.QLabel(parent=Dialog)
|
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Preferred)
|
|
||||||
sizePolicy.setHorizontalStretch(0)
|
|
||||||
sizePolicy.setVerticalStretch(0)
|
|
||||||
sizePolicy.setHeightForWidth(self.lblNote.sizePolicy().hasHeightForWidth())
|
|
||||||
self.lblNote.setSizePolicy(sizePolicy)
|
|
||||||
self.lblNote.setMaximumSize(QtCore.QSize(46, 16777215))
|
|
||||||
self.lblNote.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeading|QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
|
|
||||||
self.lblNote.setObjectName("lblNote")
|
|
||||||
self.horizontalLayout.addWidget(self.lblNote)
|
|
||||||
self.txtNote = QtWidgets.QLineEdit(parent=Dialog)
|
|
||||||
self.txtNote.setObjectName("txtNote")
|
|
||||||
self.horizontalLayout.addWidget(self.txtNote)
|
|
||||||
self.gridLayout.addLayout(self.horizontalLayout, 2, 0, 1, 2)
|
|
||||||
self.dbPath = QtWidgets.QLabel(parent=Dialog)
|
|
||||||
self.dbPath.setText("")
|
|
||||||
self.dbPath.setObjectName("dbPath")
|
|
||||||
self.gridLayout.addWidget(self.dbPath, 3, 0, 1, 2)
|
|
||||||
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
|
|
||||||
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
|
|
||||||
self.radioTitle = QtWidgets.QRadioButton(parent=Dialog)
|
|
||||||
self.radioTitle.setChecked(True)
|
|
||||||
self.radioTitle.setObjectName("radioTitle")
|
|
||||||
self.horizontalLayout_2.addWidget(self.radioTitle)
|
|
||||||
self.radioArtist = QtWidgets.QRadioButton(parent=Dialog)
|
|
||||||
self.radioArtist.setObjectName("radioArtist")
|
|
||||||
self.horizontalLayout_2.addWidget(self.radioArtist)
|
|
||||||
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
|
|
||||||
self.horizontalLayout_2.addItem(spacerItem)
|
|
||||||
self.btnAdd = QtWidgets.QPushButton(parent=Dialog)
|
|
||||||
self.btnAdd.setDefault(True)
|
|
||||||
self.btnAdd.setObjectName("btnAdd")
|
|
||||||
self.horizontalLayout_2.addWidget(self.btnAdd)
|
|
||||||
self.btnAddClose = QtWidgets.QPushButton(parent=Dialog)
|
|
||||||
self.btnAddClose.setObjectName("btnAddClose")
|
|
||||||
self.horizontalLayout_2.addWidget(self.btnAddClose)
|
|
||||||
self.btnClose = QtWidgets.QPushButton(parent=Dialog)
|
|
||||||
self.btnClose.setObjectName("btnClose")
|
|
||||||
self.horizontalLayout_2.addWidget(self.btnClose)
|
|
||||||
self.gridLayout.addLayout(self.horizontalLayout_2, 4, 0, 1, 2)
|
|
||||||
self.lblNote.setBuddy(self.txtNote)
|
|
||||||
|
|
||||||
self.retranslateUi(Dialog)
|
|
||||||
QtCore.QMetaObject.connectSlotsByName(Dialog)
|
|
||||||
|
|
||||||
def retranslateUi(self, Dialog):
|
|
||||||
_translate = QtCore.QCoreApplication.translate
|
|
||||||
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
|
|
||||||
self.label.setText(_translate("Dialog", "Title:"))
|
|
||||||
self.lblNote.setText(_translate("Dialog", "&Note:"))
|
|
||||||
self.radioTitle.setText(_translate("Dialog", "&Title"))
|
|
||||||
self.radioArtist.setText(_translate("Dialog", "&Artist"))
|
|
||||||
self.btnAdd.setText(_translate("Dialog", "&Add"))
|
|
||||||
self.btnAddClose.setText(_translate("Dialog", "A&dd and close"))
|
|
||||||
self.btnClose.setText(_translate("Dialog", "&Close"))
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -91,6 +91,6 @@ def update_bitrates() -> None:
|
|||||||
for track in ds.tracks_all():
|
for track in ds.tracks_all():
|
||||||
try:
|
try:
|
||||||
t = get_tags(track.path)
|
t = get_tags(track.path)
|
||||||
ds.track_update(t)
|
ds.track_update(track.track_id, t._asdict())
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
continue
|
continue
|
||||||
|
|||||||
@ -11,6 +11,7 @@ from app.helpers import get_all_track_metadata
|
|||||||
from app import ds, playlistmodel
|
from app import ds, playlistmodel
|
||||||
from app.models import db
|
from app.models import db
|
||||||
from classes import (
|
from classes import (
|
||||||
|
InsertTrack,
|
||||||
TrackAndPlaylist,
|
TrackAndPlaylist,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -39,7 +40,13 @@ class TestMMMiscTracks(unittest.TestCase):
|
|||||||
track_path = self.test_tracks[row % len(self.test_tracks)]
|
track_path = self.test_tracks[row % len(self.test_tracks)]
|
||||||
metadata = get_all_track_metadata(track_path)
|
metadata = get_all_track_metadata(track_path)
|
||||||
track = ds.track_create(metadata)
|
track = ds.track_create(metadata)
|
||||||
self.model.insert_row(track_id=track.track_id, note=f"{row=}")
|
self.model.insert_row_signal_handler(
|
||||||
|
InsertTrack(
|
||||||
|
playlist_id=self.playlist.playlist_id,
|
||||||
|
track_id=track.track_id,
|
||||||
|
note=f"{row=}",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
db.drop_all()
|
db.drop_all()
|
||||||
@ -59,9 +66,21 @@ class TestMMMiscTracks(unittest.TestCase):
|
|||||||
|
|
||||||
# Fake selected row in model
|
# Fake selected row in model
|
||||||
self.model.selected_rows = [self.model.playlist_rows[START_ROW]]
|
self.model.selected_rows = [self.model.playlist_rows[START_ROW]]
|
||||||
self.model.insert_row(note="start+")
|
self.model.insert_row_signal_handler(
|
||||||
|
InsertTrack(
|
||||||
|
playlist_id=self.playlist.playlist_id,
|
||||||
|
track_id=None,
|
||||||
|
note="start+"
|
||||||
|
)
|
||||||
|
)
|
||||||
self.model.selected_rows = [self.model.playlist_rows[END_ROW]]
|
self.model.selected_rows = [self.model.playlist_rows[END_ROW]]
|
||||||
self.model.insert_row(note="-")
|
self.model.insert_row_signal_handler(
|
||||||
|
InsertTrack(
|
||||||
|
playlist_id=self.playlist.playlist_id,
|
||||||
|
track_id=None,
|
||||||
|
note="-+"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
prd = self.model.playlist_rows[START_ROW]
|
prd = self.model.playlist_rows[START_ROW]
|
||||||
qv_value = self.model._display_role(
|
qv_value = self.model._display_role(
|
||||||
@ -99,7 +118,13 @@ class TestMMMiscNoPlaylist(unittest.TestCase):
|
|||||||
track_path = self.test_tracks[0]
|
track_path = self.test_tracks[0]
|
||||||
metadata = get_all_track_metadata(track_path)
|
metadata = get_all_track_metadata(track_path)
|
||||||
track = ds.track_create(metadata)
|
track = ds.track_create(metadata)
|
||||||
model.insert_row(track_id=track.track_id)
|
model.insert_row_signal_handler(
|
||||||
|
InsertTrack(
|
||||||
|
playlist_id=playlist.playlist_id,
|
||||||
|
track_id=track.track_id,
|
||||||
|
note="",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
prd = model.playlist_rows[model.rowCount() - 1]
|
prd = model.playlist_rows[model.rowCount() - 1]
|
||||||
# test repr
|
# test repr
|
||||||
@ -121,7 +146,13 @@ class TestMMMiscRowMove(unittest.TestCase):
|
|||||||
self.playlist = ds.playlist_create(self.PLAYLIST_NAME, template_id=0)
|
self.playlist = ds.playlist_create(self.PLAYLIST_NAME, template_id=0)
|
||||||
self.model = playlistmodel.PlaylistModel(self.playlist.playlist_id, is_template=False)
|
self.model = playlistmodel.PlaylistModel(self.playlist.playlist_id, is_template=False)
|
||||||
for row in range(self.ROWS_TO_CREATE):
|
for row in range(self.ROWS_TO_CREATE):
|
||||||
self.model.insert_row(note=str(row))
|
self.model.insert_row_signal_handler(
|
||||||
|
InsertTrack(
|
||||||
|
playlist_id=self.playlist.playlist_id,
|
||||||
|
track_id=None,
|
||||||
|
note=str(row),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
db.drop_all()
|
db.drop_all()
|
||||||
@ -132,7 +163,13 @@ class TestMMMiscRowMove(unittest.TestCase):
|
|||||||
note_text = "test text"
|
note_text = "test text"
|
||||||
|
|
||||||
assert self.model.rowCount() == self.ROWS_TO_CREATE
|
assert self.model.rowCount() == self.ROWS_TO_CREATE
|
||||||
self.model.insert_row(note=note_text)
|
self.model.insert_row_signal_handler(
|
||||||
|
InsertTrack(
|
||||||
|
playlist_id=self.playlist.playlist_id,
|
||||||
|
track_id=None,
|
||||||
|
note=note_text
|
||||||
|
)
|
||||||
|
)
|
||||||
assert self.model.rowCount() == self.ROWS_TO_CREATE + 1
|
assert self.model.rowCount() == self.ROWS_TO_CREATE + 1
|
||||||
prd = self.model.playlist_rows[self.model.rowCount() - 1]
|
prd = self.model.playlist_rows[self.model.rowCount() - 1]
|
||||||
# Test against edit_role because display_role for headers is
|
# Test against edit_role because display_role for headers is
|
||||||
@ -153,7 +190,13 @@ class TestMMMiscRowMove(unittest.TestCase):
|
|||||||
# Fake selected row in model
|
# Fake selected row in model
|
||||||
self.model.selected_rows = [self.model.playlist_rows[insert_row]]
|
self.model.selected_rows = [self.model.playlist_rows[insert_row]]
|
||||||
|
|
||||||
self.model.insert_row(note=note_text)
|
self.model.insert_row_signal_handler(
|
||||||
|
InsertTrack(
|
||||||
|
playlist_id=self.playlist.playlist_id,
|
||||||
|
track_id=None,
|
||||||
|
note=note_text
|
||||||
|
)
|
||||||
|
)
|
||||||
assert self.model.rowCount() == self.ROWS_TO_CREATE + 1
|
assert self.model.rowCount() == self.ROWS_TO_CREATE + 1
|
||||||
prd = self.model.playlist_rows[insert_row]
|
prd = self.model.playlist_rows[insert_row]
|
||||||
# Test against edit_role because display_role for headers is
|
# Test against edit_role because display_role for headers is
|
||||||
@ -169,7 +212,13 @@ class TestMMMiscRowMove(unittest.TestCase):
|
|||||||
note_text = "test text"
|
note_text = "test text"
|
||||||
insert_row = 6
|
insert_row = 6
|
||||||
|
|
||||||
self.model.insert_row(note=note_text)
|
self.model.insert_row_signal_handler(
|
||||||
|
InsertTrack(
|
||||||
|
playlist_id=self.playlist.playlist_id,
|
||||||
|
track_id=None,
|
||||||
|
note=note_text
|
||||||
|
)
|
||||||
|
)
|
||||||
assert self.model.rowCount() == self.ROWS_TO_CREATE + 1
|
assert self.model.rowCount() == self.ROWS_TO_CREATE + 1
|
||||||
|
|
||||||
# Fake selected row in model
|
# Fake selected row in model
|
||||||
@ -206,7 +255,13 @@ class TestMMMiscRowMove(unittest.TestCase):
|
|||||||
playlist_dst.playlist_id, is_template=False
|
playlist_dst.playlist_id, is_template=False
|
||||||
)
|
)
|
||||||
for row in range(self.ROWS_TO_CREATE):
|
for row in range(self.ROWS_TO_CREATE):
|
||||||
model_dst.insert_row(note=str(row))
|
model_dst.insert_row_signal_handler(
|
||||||
|
InsertTrack(
|
||||||
|
playlist_id=playlist_dst.playlist_id,
|
||||||
|
track_id=None,
|
||||||
|
note=str(row)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
model_src.move_rows_between_playlists(from_rows, to_row, playlist_dst.playlist_id)
|
model_src.move_rows_between_playlists(from_rows, to_row, playlist_dst.playlist_id)
|
||||||
|
|
||||||
@ -227,7 +282,13 @@ class TestMMMiscRowMove(unittest.TestCase):
|
|||||||
playlist_dst.playlist_id, is_template=False
|
playlist_dst.playlist_id, is_template=False
|
||||||
)
|
)
|
||||||
for row in range(self.ROWS_TO_CREATE):
|
for row in range(self.ROWS_TO_CREATE):
|
||||||
model_dst.insert_row(note=str(row))
|
model_dst.insert_row_signal_handler(
|
||||||
|
InsertTrack(
|
||||||
|
playlist_id=playlist_dst.playlist_id,
|
||||||
|
track_id=None,
|
||||||
|
note=str(row)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
model_src.move_rows_between_playlists(from_rows, to_row, playlist_dst.playlist_id)
|
model_src.move_rows_between_playlists(from_rows, to_row, playlist_dst.playlist_id)
|
||||||
|
|
||||||
@ -255,7 +316,13 @@ class TestMMMiscRowMove(unittest.TestCase):
|
|||||||
playlist_dst.playlist_id, is_template=False
|
playlist_dst.playlist_id, is_template=False
|
||||||
)
|
)
|
||||||
for row in range(self.ROWS_TO_CREATE):
|
for row in range(self.ROWS_TO_CREATE):
|
||||||
model_dst.insert_row(note=str(row))
|
model_dst.insert_row_signal_handler(
|
||||||
|
InsertTrack(
|
||||||
|
playlist_id=playlist_dst.playlist_id,
|
||||||
|
track_id=None,
|
||||||
|
note=str(row)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
model_src.move_rows_between_playlists(from_rows, to_row, playlist_dst.playlist_id)
|
model_src.move_rows_between_playlists(from_rows, to_row, playlist_dst.playlist_id)
|
||||||
|
|
||||||
|
|||||||
@ -99,7 +99,7 @@ class MyTestCase(unittest.TestCase):
|
|||||||
model = playlistmodel.PlaylistModel(playlist.playlist_id, is_template=False)
|
model = playlistmodel.PlaylistModel(playlist.playlist_id, is_template=False)
|
||||||
|
|
||||||
# Add a track with a note
|
# Add a track with a note
|
||||||
model.insert_row(track_id=self.track1.track_id, note=note_text)
|
model.insert_row_signal_handler(track_id=self.track1.track_id, note=note_text)
|
||||||
|
|
||||||
# Retrieve playlist
|
# Retrieve playlist
|
||||||
all_playlists = ds.playlists_all()
|
all_playlists = ds.playlists_all()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user