Compare commits
13 Commits
0ea12eb9d9
...
c9f774f2e4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9f774f2e4 | ||
|
|
d9a3dd0ec4 | ||
|
|
d84cf91e9d | ||
|
|
346509f6ca | ||
|
|
4c1ee0b1ca | ||
|
|
bc7d6818aa | ||
|
|
0f8409879c | ||
|
|
a95aa918b1 | ||
|
|
7361086da5 | ||
|
|
e40a4ab57a | ||
|
|
e733e7025d | ||
|
|
b520178e3a | ||
|
|
9e07e73167 |
@ -104,14 +104,16 @@ class FileImporter:
|
|||||||
# variable or an instance variable are effectively the same thing.
|
# variable or an instance variable are effectively the same thing.
|
||||||
workers: dict[str, DoTrackImport] = {}
|
workers: dict[str, DoTrackImport] = {}
|
||||||
|
|
||||||
def __init__(self, base_model: PlaylistModel, row_number: int) -> None:
|
def __init__(
|
||||||
|
self, base_model: PlaylistModel, row_number: Optional[int] = None
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Initialise the FileImporter singleton instance.
|
Initialise the FileImporter singleton instance.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
log.debug(f"FileImporter.__init__({base_model=}, {row_number=})")
|
|
||||||
|
|
||||||
# Create ModelData
|
# Create ModelData
|
||||||
|
if not row_number:
|
||||||
|
row_number = base_model.rowCount()
|
||||||
self.model_data = ThreadData(base_model=base_model, row_number=row_number)
|
self.model_data = ThreadData(base_model=base_model, row_number=row_number)
|
||||||
|
|
||||||
# Data structure to track files to import
|
# Data structure to track files to import
|
||||||
|
|||||||
27
app/log.py
27
app/log.py
@ -1,7 +1,6 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# Standard library imports
|
# Standard library imports
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from functools import wraps
|
|
||||||
import logging
|
import logging
|
||||||
import logging.config
|
import logging.config
|
||||||
import logging.handlers
|
import logging.handlers
|
||||||
@ -121,30 +120,4 @@ def handle_exception(exc_type, exc_value, exc_traceback):
|
|||||||
QMessageBox.critical(None, "Application Error", msg)
|
QMessageBox.critical(None, "Application Error", msg)
|
||||||
|
|
||||||
|
|
||||||
def truncate_large(obj, limit=5):
|
|
||||||
"""Helper to truncate large lists or other iterables."""
|
|
||||||
if isinstance(obj, (list, tuple, set)):
|
|
||||||
if len(obj) > limit:
|
|
||||||
return f"{type(obj).__name__}(len={len(obj)}, items={list(obj)[:limit]}...)"
|
|
||||||
|
|
||||||
return repr(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def log_call(func):
|
|
||||||
@wraps(func)
|
|
||||||
def wrapper(*args, **kwargs):
|
|
||||||
args_repr = [truncate_large(a) for a in args]
|
|
||||||
kwargs_repr = [f"{k}={truncate_large(v)}" for k, v in kwargs.items()]
|
|
||||||
params_repr = ", ".join(args_repr + kwargs_repr)
|
|
||||||
log.debug(f"call {func.__name__}({params_repr})", stacklevel=2)
|
|
||||||
try:
|
|
||||||
result = func(*args, **kwargs)
|
|
||||||
log.debug(f"return {func.__name__}: {truncate_large(result)}", stacklevel=2)
|
|
||||||
return result
|
|
||||||
except Exception as e:
|
|
||||||
log.debug(f"exception in {func.__name__}: {e}", stacklevel=2)
|
|
||||||
raise
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
|
|
||||||
sys.excepthook = handle_exception
|
sys.excepthook = handle_exception
|
||||||
|
|||||||
@ -73,7 +73,7 @@ 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
|
||||||
from models import db, Playdates, PlaylistRows, Playlists, Queries, Settings, Tracks
|
from models import db, Playdates, PlaylistRows, Playlists, Queries, Settings, Tracks
|
||||||
from playlistrow import PlaylistRow, TrackSequence
|
from playlistrow import PlaylistRow, TrackSequence
|
||||||
from playlistmodel import PlaylistModel, PlaylistProxyModel
|
from playlistmodel import PlaylistModel, PlaylistProxyModel
|
||||||
@ -478,7 +478,6 @@ class ManageQueries(ItemlistManager):
|
|||||||
|
|
||||||
self.populate_table(query_list)
|
self.populate_table(query_list)
|
||||||
|
|
||||||
@log_call
|
|
||||||
def delete_item(self, query_id: int) -> None:
|
def delete_item(self, query_id: int) -> None:
|
||||||
"""delete query"""
|
"""delete query"""
|
||||||
|
|
||||||
@ -491,6 +490,7 @@ class ManageQueries(ItemlistManager):
|
|||||||
"Delete query",
|
"Delete query",
|
||||||
f"Delete query '{query.name}': " "Are you sure?",
|
f"Delete query '{query.name}': " "Are you sure?",
|
||||||
):
|
):
|
||||||
|
log.debug(f"manage_queries: delete {query=}")
|
||||||
self.session.delete(query)
|
self.session.delete(query)
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
|
|
||||||
@ -583,7 +583,6 @@ class ManageTemplates(ItemlistManager):
|
|||||||
|
|
||||||
self.populate_table(template_list)
|
self.populate_table(template_list)
|
||||||
|
|
||||||
@log_call
|
|
||||||
def delete_item(self, template_id: int) -> None:
|
def delete_item(self, template_id: int) -> None:
|
||||||
"""delete template"""
|
"""delete template"""
|
||||||
|
|
||||||
@ -607,6 +606,7 @@ class ManageTemplates(ItemlistManager):
|
|||||||
else:
|
else:
|
||||||
self.musicmuster.playlist_section.tabPlaylist.removeTab(open_idx)
|
self.musicmuster.playlist_section.tabPlaylist.removeTab(open_idx)
|
||||||
|
|
||||||
|
log.debug(f"manage_templates: delete {template=}")
|
||||||
self.session.delete(template)
|
self.session.delete(template)
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
|
|
||||||
@ -1235,6 +1235,7 @@ class Window(QMainWindow):
|
|||||||
for playlist_id, idx in open_playlist_ids.items():
|
for playlist_id, idx in open_playlist_ids.items():
|
||||||
playlist = session.get(Playlists, playlist_id)
|
playlist = session.get(Playlists, playlist_id)
|
||||||
if playlist:
|
if playlist:
|
||||||
|
log.debug(f"Set {playlist=} tab to {idx=}")
|
||||||
playlist.tab = idx
|
playlist.tab = idx
|
||||||
|
|
||||||
# Save window attributes
|
# Save window attributes
|
||||||
@ -1452,7 +1453,6 @@ class Window(QMainWindow):
|
|||||||
|
|
||||||
# # # # # # # # # # Playlist management functions # # # # # # # # # #
|
# # # # # # # # # # Playlist management functions # # # # # # # # # #
|
||||||
|
|
||||||
@log_call
|
|
||||||
def _create_playlist(
|
def _create_playlist(
|
||||||
self, session: Session, name: str, template_id: int
|
self, session: Session, name: str, template_id: int
|
||||||
) -> Playlists:
|
) -> Playlists:
|
||||||
@ -1461,9 +1461,10 @@ class Window(QMainWindow):
|
|||||||
if template_id > 0, and return the Playlists object.
|
if template_id > 0, and return the Playlists object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
log.debug(f" _create_playlist({name=}, {template_id=})")
|
||||||
|
|
||||||
return Playlists(session, name, template_id)
|
return Playlists(session, name, template_id)
|
||||||
|
|
||||||
@log_call
|
|
||||||
def _open_playlist(self, playlist: Playlists, is_template: bool = False) -> int:
|
def _open_playlist(self, playlist: Playlists, is_template: bool = False) -> int:
|
||||||
"""
|
"""
|
||||||
With passed playlist:
|
With passed playlist:
|
||||||
@ -1474,6 +1475,8 @@ class Window(QMainWindow):
|
|||||||
return: tab index
|
return: tab index
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
log.debug(f" _open_playlist({playlist=}, {is_template=})")
|
||||||
|
|
||||||
# Create base model and proxy model
|
# Create base model and proxy model
|
||||||
base_model = PlaylistModel(playlist.id, is_template)
|
base_model = PlaylistModel(playlist.id, is_template)
|
||||||
proxy_model = PlaylistProxyModel()
|
proxy_model = PlaylistProxyModel()
|
||||||
@ -1492,7 +1495,6 @@ class Window(QMainWindow):
|
|||||||
|
|
||||||
return idx
|
return idx
|
||||||
|
|
||||||
@log_call
|
|
||||||
def create_playlist_from_template(self, session: Session, template_id: int) -> None:
|
def create_playlist_from_template(self, session: Session, 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
|
||||||
@ -1514,8 +1516,7 @@ class Window(QMainWindow):
|
|||||||
self._open_playlist(playlist)
|
self._open_playlist(playlist)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
@log_call
|
def delete_playlist(self) -> None:
|
||||||
def delete_playlist(self, checked: bool = False) -> None:
|
|
||||||
"""
|
"""
|
||||||
Delete current playlist
|
Delete current playlist
|
||||||
"""
|
"""
|
||||||
@ -1534,7 +1535,7 @@ class Window(QMainWindow):
|
|||||||
else:
|
else:
|
||||||
log.error("Failed to retrieve playlist")
|
log.error("Failed to retrieve playlist")
|
||||||
|
|
||||||
def open_existing_playlist(self, checked: bool = False) -> None:
|
def open_existing_playlist(self) -> None:
|
||||||
"""Open existing playlist"""
|
"""Open existing playlist"""
|
||||||
|
|
||||||
with db.Session() as session:
|
with db.Session() as session:
|
||||||
@ -1546,7 +1547,7 @@ class Window(QMainWindow):
|
|||||||
self._open_playlist(playlist)
|
self._open_playlist(playlist)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
def save_as_template(self, checked: bool = False) -> None:
|
def save_as_template(self) -> None:
|
||||||
"""Save current playlist as template"""
|
"""Save current playlist as template"""
|
||||||
|
|
||||||
with db.Session() as session:
|
with db.Session() as session:
|
||||||
@ -1622,7 +1623,7 @@ class Window(QMainWindow):
|
|||||||
|
|
||||||
# # # # # # # # # # Manage templates and queries # # # # # # # # # #
|
# # # # # # # # # # Manage templates and queries # # # # # # # # # #
|
||||||
|
|
||||||
def manage_queries_wrapper(self, checked: bool = False) -> None:
|
def manage_queries_wrapper(self):
|
||||||
"""
|
"""
|
||||||
Simply instantiate the manage_queries class
|
Simply instantiate the manage_queries class
|
||||||
"""
|
"""
|
||||||
@ -1630,7 +1631,7 @@ class Window(QMainWindow):
|
|||||||
with db.Session() as session:
|
with db.Session() as session:
|
||||||
_ = ManageQueries(session, self)
|
_ = ManageQueries(session, self)
|
||||||
|
|
||||||
def manage_templates_wrapper(self, checked: bool = False) -> None:
|
def manage_templates_wrapper(self):
|
||||||
"""
|
"""
|
||||||
Simply instantiate the manage_queries class
|
Simply instantiate the manage_queries class
|
||||||
"""
|
"""
|
||||||
@ -1640,12 +1641,12 @@ class Window(QMainWindow):
|
|||||||
|
|
||||||
# # # # # # # # # # Miscellaneous functions # # # # # # # # # #
|
# # # # # # # # # # Miscellaneous functions # # # # # # # # # #
|
||||||
|
|
||||||
def select_duplicate_rows(self, checked: bool = False) -> None:
|
def select_duplicate_rows(self) -> 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) -> None:
|
||||||
"""Get git tag and database name"""
|
"""Get git tag and database name"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -1674,7 +1675,7 @@ class Window(QMainWindow):
|
|||||||
self.track_sequence.set_next(None)
|
self.track_sequence.set_next(None)
|
||||||
self.update_headers()
|
self.update_headers()
|
||||||
|
|
||||||
def clear_selection(self, checked: bool = False) -> None:
|
def clear_selection(self) -> None:
|
||||||
"""Clear row selection"""
|
"""Clear row selection"""
|
||||||
|
|
||||||
# Unselect any selected rows
|
# Unselect any selected rows
|
||||||
@ -1684,7 +1685,7 @@ class Window(QMainWindow):
|
|||||||
# Clear the search bar
|
# Clear the search bar
|
||||||
self.search_playlist_clear()
|
self.search_playlist_clear()
|
||||||
|
|
||||||
def close_playlist_tab(self, checked: bool = False) -> bool:
|
def close_playlist_tab(self) -> bool:
|
||||||
"""
|
"""
|
||||||
Close active playlist tab, called by menu item
|
Close active playlist tab, called by menu item
|
||||||
"""
|
"""
|
||||||
@ -1770,7 +1771,6 @@ class Window(QMainWindow):
|
|||||||
self.signals.search_songfacts_signal.connect(self.open_songfacts_browser)
|
self.signals.search_songfacts_signal.connect(self.open_songfacts_browser)
|
||||||
self.signals.search_wikipedia_signal.connect(self.open_wikipedia_browser)
|
self.signals.search_wikipedia_signal.connect(self.open_wikipedia_browser)
|
||||||
|
|
||||||
@log_call
|
|
||||||
def current_row_or_end(self) -> int:
|
def current_row_or_end(self) -> int:
|
||||||
"""
|
"""
|
||||||
If a row or rows are selected, return the row number of the first
|
If a row or rows are selected, return the row number of the first
|
||||||
@ -1782,14 +1782,14 @@ class Window(QMainWindow):
|
|||||||
return self.current.selected_row_numbers[0]
|
return self.current.selected_row_numbers[0]
|
||||||
return self.current.base_model.rowCount()
|
return self.current.base_model.rowCount()
|
||||||
|
|
||||||
def debug(self, checked: bool = False) -> None:
|
def debug(self):
|
||||||
"""Invoke debugger"""
|
"""Invoke debugger"""
|
||||||
|
|
||||||
import ipdb # type: ignore
|
import ipdb # type: ignore
|
||||||
|
|
||||||
ipdb.set_trace()
|
ipdb.set_trace()
|
||||||
|
|
||||||
def download_played_tracks(self, checked: bool = False) -> None:
|
def download_played_tracks(self) -> None:
|
||||||
"""Download a CSV of played tracks"""
|
"""Download a CSV of played tracks"""
|
||||||
|
|
||||||
dlg = DownloadCSV(self)
|
dlg = DownloadCSV(self)
|
||||||
@ -1820,7 +1820,6 @@ class Window(QMainWindow):
|
|||||||
if self.track_sequence.current:
|
if self.track_sequence.current:
|
||||||
self.track_sequence.current.drop3db(self.footer_section.btnDrop3db.isChecked())
|
self.track_sequence.current.drop3db(self.footer_section.btnDrop3db.isChecked())
|
||||||
|
|
||||||
@log_call
|
|
||||||
def enable_escape(self, enabled: bool) -> None:
|
def enable_escape(self, enabled: bool) -> None:
|
||||||
"""
|
"""
|
||||||
Manage signal to enable/disable handling ESC character.
|
Manage signal to enable/disable handling ESC character.
|
||||||
@ -1829,10 +1828,11 @@ class Window(QMainWindow):
|
|||||||
so we need to disable it here while editing.
|
so we need to disable it here while editing.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
log.debug(f"enable_escape({enabled=})")
|
||||||
|
|
||||||
if "clear_selection" in self.menu_actions:
|
if "clear_selection" in self.menu_actions:
|
||||||
self.menu_actions["clear_selection"].setEnabled(enabled)
|
self.menu_actions["clear_selection"].setEnabled(enabled)
|
||||||
|
|
||||||
@log_call
|
|
||||||
def end_of_track_actions(self) -> None:
|
def end_of_track_actions(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -1867,7 +1867,7 @@ class Window(QMainWindow):
|
|||||||
# if not self.stop_autoplay:
|
# if not self.stop_autoplay:
|
||||||
# self.play_next()
|
# self.play_next()
|
||||||
|
|
||||||
def export_playlist_tab(self, checked: bool = False) -> None:
|
def export_playlist_tab(self) -> None:
|
||||||
"""Export the current playlist to an m3u file"""
|
"""Export the current playlist to an m3u file"""
|
||||||
|
|
||||||
playlist_id = self.current.playlist_id
|
playlist_id = self.current.playlist_id
|
||||||
@ -1908,7 +1908,7 @@ class Window(QMainWindow):
|
|||||||
"\n"
|
"\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
def fade(self, checked: bool = False) -> None:
|
def fade(self) -> None:
|
||||||
"""Fade currently playing track"""
|
"""Fade currently playing track"""
|
||||||
|
|
||||||
if self.track_sequence.current:
|
if self.track_sequence.current:
|
||||||
@ -1944,7 +1944,7 @@ class Window(QMainWindow):
|
|||||||
# 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) -> None:
|
||||||
"""
|
"""
|
||||||
Pass import files call to file_importer module
|
Pass import files call to file_importer module
|
||||||
"""
|
"""
|
||||||
@ -1954,7 +1954,7 @@ class Window(QMainWindow):
|
|||||||
self.importer = FileImporter(self.current.base_model, self.current_row_or_end())
|
self.importer = FileImporter(self.current.base_model, self.current_row_or_end())
|
||||||
self.importer.start()
|
self.importer.start()
|
||||||
|
|
||||||
def insert_header(self, checked: bool = False) -> None:
|
def insert_header(self) -> None:
|
||||||
"""Show dialog box to enter header text and add to playlist"""
|
"""Show dialog box to enter header text and add to playlist"""
|
||||||
|
|
||||||
# Get header text
|
# Get header text
|
||||||
@ -1969,7 +1969,7 @@ class Window(QMainWindow):
|
|||||||
note=dlg.textValue(),
|
note=dlg.textValue(),
|
||||||
)
|
)
|
||||||
|
|
||||||
def insert_track(self, checked: bool = False) -> None:
|
def insert_track(self) -> None:
|
||||||
"""Show dialog box to select and add track from database"""
|
"""Show dialog box to select and add track from database"""
|
||||||
|
|
||||||
dlg = TrackInsertDialog(
|
dlg = TrackInsertDialog(
|
||||||
@ -1978,7 +1978,6 @@ class Window(QMainWindow):
|
|||||||
)
|
)
|
||||||
dlg.exec()
|
dlg.exec()
|
||||||
|
|
||||||
@log_call
|
|
||||||
def load_last_playlists(self) -> None:
|
def load_last_playlists(self) -> None:
|
||||||
"""Load the playlists that were open when the last session closed"""
|
"""Load the playlists that were open when the last session closed"""
|
||||||
|
|
||||||
@ -1986,6 +1985,7 @@ class Window(QMainWindow):
|
|||||||
with db.Session() as session:
|
with db.Session() as session:
|
||||||
for playlist in Playlists.get_open(session):
|
for playlist in Playlists.get_open(session):
|
||||||
if playlist:
|
if playlist:
|
||||||
|
log.debug(f"load_last_playlists() loaded {playlist=}")
|
||||||
# Create tab
|
# Create tab
|
||||||
playlist_ids.append(self._open_playlist(playlist))
|
playlist_ids.append(self._open_playlist(playlist))
|
||||||
|
|
||||||
@ -2001,7 +2001,7 @@ class Window(QMainWindow):
|
|||||||
Playlists.clear_tabs(session, playlist_ids)
|
Playlists.clear_tabs(session, playlist_ids)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
def lookup_row_in_songfacts(self, checked: bool = False) -> None:
|
def lookup_row_in_songfacts(self) -> None:
|
||||||
"""
|
"""
|
||||||
Display songfacts page for title in highlighted row
|
Display songfacts page for title in highlighted row
|
||||||
"""
|
"""
|
||||||
@ -2012,7 +2012,7 @@ class Window(QMainWindow):
|
|||||||
|
|
||||||
self.signals.search_songfacts_signal.emit(track_info.title)
|
self.signals.search_songfacts_signal.emit(track_info.title)
|
||||||
|
|
||||||
def lookup_row_in_wikipedia(self, checked: bool = False) -> None:
|
def lookup_row_in_wikipedia(self) -> None:
|
||||||
"""
|
"""
|
||||||
Display Wikipedia page for title in highlighted row or next track
|
Display Wikipedia page for title in highlighted row or next track
|
||||||
"""
|
"""
|
||||||
@ -2023,7 +2023,7 @@ class Window(QMainWindow):
|
|||||||
|
|
||||||
self.signals.search_wikipedia_signal.emit(track_info.title)
|
self.signals.search_wikipedia_signal.emit(track_info.title)
|
||||||
|
|
||||||
def mark_rows_for_moving(self, checked: bool = False) -> None:
|
def mark_rows_for_moving(self) -> None:
|
||||||
"""
|
"""
|
||||||
Cut rows ready for pasting.
|
Cut rows ready for pasting.
|
||||||
"""
|
"""
|
||||||
@ -2037,7 +2037,6 @@ class Window(QMainWindow):
|
|||||||
f"mark_rows_for_moving(): {self.move_source_rows=} {self.move_source_model=}"
|
f"mark_rows_for_moving(): {self.move_source_rows=} {self.move_source_model=}"
|
||||||
)
|
)
|
||||||
|
|
||||||
@log_call
|
|
||||||
def move_playlist_rows(self, row_numbers: list[int]) -> None:
|
def move_playlist_rows(self, row_numbers: list[int]) -> None:
|
||||||
"""
|
"""
|
||||||
Move passed playlist rows to another playlist
|
Move passed playlist rows to another playlist
|
||||||
@ -2075,7 +2074,7 @@ class Window(QMainWindow):
|
|||||||
# Reset track_sequences
|
# Reset track_sequences
|
||||||
self.track_sequence.update()
|
self.track_sequence.update()
|
||||||
|
|
||||||
def move_selected(self, checked: bool = False) -> None:
|
def move_selected(self) -> None:
|
||||||
"""
|
"""
|
||||||
Move selected rows to another playlist
|
Move selected rows to another playlist
|
||||||
"""
|
"""
|
||||||
@ -2086,7 +2085,7 @@ class Window(QMainWindow):
|
|||||||
|
|
||||||
self.move_playlist_rows(selected_rows)
|
self.move_playlist_rows(selected_rows)
|
||||||
|
|
||||||
def move_unplayed(self, checked: bool = False) -> None:
|
def move_unplayed(self) -> None:
|
||||||
"""
|
"""
|
||||||
Move unplayed rows to another playlist
|
Move unplayed rows to another playlist
|
||||||
"""
|
"""
|
||||||
@ -2118,8 +2117,7 @@ class Window(QMainWindow):
|
|||||||
|
|
||||||
webbrowser.get("browser").open_new_tab(url)
|
webbrowser.get("browser").open_new_tab(url)
|
||||||
|
|
||||||
@log_call
|
def paste_rows(self, dummy_for_profiling: int | None = None) -> None:
|
||||||
def paste_rows(self, checked: bool = False) -> None:
|
|
||||||
"""
|
"""
|
||||||
Paste earlier cut rows.
|
Paste earlier cut rows.
|
||||||
"""
|
"""
|
||||||
@ -2152,8 +2150,7 @@ class Window(QMainWindow):
|
|||||||
if set_next_row:
|
if set_next_row:
|
||||||
to_playlist_model.set_next_row(set_next_row)
|
to_playlist_model.set_next_row(set_next_row)
|
||||||
|
|
||||||
@log_call
|
def play_next(self, position: Optional[float] = None) -> None:
|
||||||
def play_next(self, position: Optional[float] = None, checked: bool = False) -> None:
|
|
||||||
"""
|
"""
|
||||||
Play next track, optionally from passed position.
|
Play next track, optionally from passed position.
|
||||||
|
|
||||||
@ -2348,7 +2345,7 @@ class Window(QMainWindow):
|
|||||||
if ok:
|
if ok:
|
||||||
log.debug("quicklog: " + dlg.textValue())
|
log.debug("quicklog: " + dlg.textValue())
|
||||||
|
|
||||||
def rename_playlist(self, checked: bool = False) -> None:
|
def rename_playlist(self) -> None:
|
||||||
"""
|
"""
|
||||||
Rename current playlist
|
Rename current playlist
|
||||||
"""
|
"""
|
||||||
@ -2404,7 +2401,7 @@ class Window(QMainWindow):
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def resume(self, checked: bool = False) -> None:
|
def resume(self) -> None:
|
||||||
"""
|
"""
|
||||||
Resume playing last track. We may be playing the next track
|
Resume playing last track. We may be playing the next track
|
||||||
or none; take care of both eventualities.
|
or none; take care of both eventualities.
|
||||||
@ -2445,7 +2442,7 @@ class Window(QMainWindow):
|
|||||||
)
|
)
|
||||||
self.track_sequence.current.start_time -= dt.timedelta(milliseconds=elapsed_ms)
|
self.track_sequence.current.start_time -= dt.timedelta(milliseconds=elapsed_ms)
|
||||||
|
|
||||||
def search_playlist(self, checked: bool = False) -> None:
|
def search_playlist(self) -> None:
|
||||||
"""Show text box to search playlist"""
|
"""Show text box to search playlist"""
|
||||||
|
|
||||||
# Disable play controls so that 'return' in search box doesn't
|
# Disable play controls so that 'return' in search box doesn't
|
||||||
@ -2503,8 +2500,7 @@ class Window(QMainWindow):
|
|||||||
height = Settings.get_setting(session, "mainwindow_height").f_int or 100
|
height = Settings.get_setting(session, "mainwindow_height").f_int or 100
|
||||||
self.setGeometry(x, y, width, height)
|
self.setGeometry(x, y, width, height)
|
||||||
|
|
||||||
@log_call
|
def set_selected_track_next(self) -> None:
|
||||||
def set_selected_track_next(self, checked: bool = False) -> None:
|
|
||||||
"""
|
"""
|
||||||
Set currently-selected row on visible playlist tab as next track
|
Set currently-selected row on visible playlist tab as next track
|
||||||
"""
|
"""
|
||||||
@ -2517,7 +2513,6 @@ class Window(QMainWindow):
|
|||||||
# else:
|
# else:
|
||||||
# log.error("No active tab")
|
# log.error("No active tab")
|
||||||
|
|
||||||
@log_call
|
|
||||||
def set_tab_colour(self, widget: PlaylistTab, colour: QColor) -> None:
|
def set_tab_colour(self, widget: PlaylistTab, colour: QColor) -> None:
|
||||||
"""
|
"""
|
||||||
Find the tab containing the widget and set the text colour
|
Find the tab containing the widget and set the text colour
|
||||||
@ -2579,8 +2574,7 @@ class Window(QMainWindow):
|
|||||||
|
|
||||||
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) -> None:
|
||||||
def stop(self, checked: bool = False) -> None:
|
|
||||||
"""Stop playing immediately"""
|
"""Stop playing immediately"""
|
||||||
|
|
||||||
self.stop_autoplay = True
|
self.stop_autoplay = True
|
||||||
@ -2714,7 +2708,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)
|
||||||
# Re-enable 10ms timer (see above)
|
# Re-enable 10ms timer (see above)
|
||||||
log.debug(f"issue287: {self.timer10.isActive()=}")
|
|
||||||
if not self.timer10.isActive():
|
if not self.timer10.isActive():
|
||||||
self.timer10.start(10)
|
self.timer10.start(10)
|
||||||
log.debug("issue223: update_clocks: 10ms timer enabled")
|
log.debug("issue223: update_clocks: 10ms timer enabled")
|
||||||
|
|||||||
@ -46,7 +46,7 @@ from helpers import (
|
|||||||
remove_substring_case_insensitive,
|
remove_substring_case_insensitive,
|
||||||
set_track_metadata,
|
set_track_metadata,
|
||||||
)
|
)
|
||||||
from log import log, log_call
|
from log import log
|
||||||
from models import db, NoteColours, Playdates, PlaylistRows, Tracks
|
from models import db, NoteColours, Playdates, PlaylistRows, Tracks
|
||||||
from playlistrow import PlaylistRow, TrackSequence
|
from playlistrow import PlaylistRow, TrackSequence
|
||||||
import repository
|
import repository
|
||||||
@ -79,6 +79,8 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
) -> None:
|
) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
|
log.debug("PlaylistModel.__init__()")
|
||||||
|
|
||||||
self.playlist_id = playlist_id
|
self.playlist_id = playlist_id
|
||||||
self.is_template = is_template
|
self.is_template = is_template
|
||||||
self.track_sequence = TrackSequence()
|
self.track_sequence = TrackSequence()
|
||||||
@ -143,7 +145,6 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
return header_row
|
return header_row
|
||||||
|
|
||||||
@log_call
|
|
||||||
def add_track_to_header(
|
def add_track_to_header(
|
||||||
self, playlist_id: int, track_id: int, note: str = ""
|
self, playlist_id: int, track_id: int, note: str = ""
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -241,7 +242,6 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
return QBrush()
|
return QBrush()
|
||||||
|
|
||||||
@log_call
|
|
||||||
def begin_reset_model(self, playlist_id: int) -> None:
|
def begin_reset_model(self, playlist_id: int) -> None:
|
||||||
"""
|
"""
|
||||||
Reset model if playlist_id is ours
|
Reset model if playlist_id is ours
|
||||||
@ -256,7 +256,6 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
return len(Col)
|
return len(Col)
|
||||||
|
|
||||||
@log_call
|
|
||||||
def current_track_started(self) -> None:
|
def current_track_started(self) -> None:
|
||||||
"""
|
"""
|
||||||
Notification from musicmuster that the current track has just
|
Notification from musicmuster that the current track has just
|
||||||
@ -272,6 +271,8 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
- update track times
|
- update track times
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
log.debug(f"{self}: current_track_started()")
|
||||||
|
|
||||||
if not self.track_sequence.current:
|
if not self.track_sequence.current:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -379,7 +380,6 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
return QVariant()
|
return QVariant()
|
||||||
|
|
||||||
@log_call
|
|
||||||
def delete_rows(self, row_numbers: list[int]) -> None:
|
def delete_rows(self, row_numbers: list[int]) -> None:
|
||||||
"""
|
"""
|
||||||
Delete passed rows from model
|
Delete passed rows from model
|
||||||
@ -462,13 +462,15 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
@log_call
|
|
||||||
def end_reset_model(self, playlist_id: int) -> None:
|
def end_reset_model(self, playlist_id: int) -> None:
|
||||||
"""
|
"""
|
||||||
End model reset if this is our playlist
|
End model reset if this is our playlist
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
log.debug(f"{self}: end_reset_model({playlist_id=})")
|
||||||
|
|
||||||
if playlist_id != self.playlist_id:
|
if playlist_id != self.playlist_id:
|
||||||
|
log.debug(f"{self}: end_reset_model: not us ({self.playlist_id=})")
|
||||||
return
|
return
|
||||||
with db.Session() as session:
|
with db.Session() as session:
|
||||||
self.refresh_data(session)
|
self.refresh_data(session)
|
||||||
@ -548,13 +550,14 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
return boldfont
|
return boldfont
|
||||||
|
|
||||||
@log_call
|
|
||||||
def get_duplicate_rows(self) -> list[int]:
|
def get_duplicate_rows(self) -> list[int]:
|
||||||
"""
|
"""
|
||||||
Return a list of duplicate rows. If track appears in rows 2, 3 and 4, return [3, 4]
|
Return a list of duplicate rows. If track appears in rows 2, 3 and 4, return [3, 4]
|
||||||
(ie, ignore the first, not-yet-duplicate, track).
|
(ie, ignore the first, not-yet-duplicate, track).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
log.debug(f"{self}: get_duplicate_rows() called")
|
||||||
|
|
||||||
found = []
|
found = []
|
||||||
result = []
|
result = []
|
||||||
|
|
||||||
@ -567,9 +570,9 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
else:
|
else:
|
||||||
found.append(track_id)
|
found.append(track_id)
|
||||||
|
|
||||||
|
log.debug(f"{self}: get_duplicate_rows() returned: {result=}")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@log_call
|
|
||||||
def _get_new_row_number(self, proposed_row_number: Optional[int]) -> int:
|
def _get_new_row_number(self, proposed_row_number: Optional[int]) -> int:
|
||||||
"""
|
"""
|
||||||
Sanitises proposed new row number.
|
Sanitises proposed new row number.
|
||||||
@ -578,6 +581,8 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
If not given, return row number to add to end of model.
|
If not given, return row number to add to end of model.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
log.debug(f"{self}: _get_new_row_number({proposed_row_number=})")
|
||||||
|
|
||||||
if proposed_row_number is None or proposed_row_number > len(self.playlist_rows):
|
if proposed_row_number is None or proposed_row_number > len(self.playlist_rows):
|
||||||
# We are adding to the end of the list
|
# We are adding to the end of the list
|
||||||
new_row_number = len(self.playlist_rows)
|
new_row_number = len(self.playlist_rows)
|
||||||
@ -587,6 +592,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
else:
|
else:
|
||||||
new_row_number = proposed_row_number
|
new_row_number = proposed_row_number
|
||||||
|
|
||||||
|
log.debug(f"{self}: get_new_row_number() return: {new_row_number=}")
|
||||||
return new_row_number
|
return new_row_number
|
||||||
|
|
||||||
def get_row_info(self, row_number: int) -> PlaylistRow:
|
def get_row_info(self, row_number: int) -> PlaylistRow:
|
||||||
@ -596,7 +602,6 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
return self.playlist_rows[row_number]
|
return self.playlist_rows[row_number]
|
||||||
|
|
||||||
@log_call
|
|
||||||
def get_row_track_id(self, row_number: int) -> Optional[int]:
|
def get_row_track_id(self, row_number: int) -> Optional[int]:
|
||||||
"""
|
"""
|
||||||
Return id of track associated with row or None if no track associated
|
Return id of track associated with row or None if no track associated
|
||||||
@ -713,7 +718,6 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
]
|
]
|
||||||
self.invalidate_row(row_number, roles)
|
self.invalidate_row(row_number, roles)
|
||||||
|
|
||||||
@log_call
|
|
||||||
def insert_row(
|
def insert_row(
|
||||||
self,
|
self,
|
||||||
proposed_row_number: Optional[int],
|
proposed_row_number: Optional[int],
|
||||||
@ -724,6 +728,8 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
Insert a row.
|
Insert a row.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
log.debug(f"{self}: insert_row({proposed_row_number=}, {track_id=}, {note=})")
|
||||||
|
|
||||||
new_row_number = self._get_new_row_number(proposed_row_number)
|
new_row_number = self._get_new_row_number(proposed_row_number)
|
||||||
|
|
||||||
super().beginInsertRows(QModelIndex(), new_row_number, new_row_number)
|
super().beginInsertRows(QModelIndex(), new_row_number, new_row_number)
|
||||||
@ -756,12 +762,13 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
list(range(new_row_number, len(self.playlist_rows))), roles_to_invalidate
|
list(range(new_row_number, len(self.playlist_rows))), roles_to_invalidate
|
||||||
)
|
)
|
||||||
|
|
||||||
@log_call
|
|
||||||
def invalidate_row(self, modified_row: int, roles: list[Qt.ItemDataRole]) -> None:
|
def invalidate_row(self, modified_row: int, roles: list[Qt.ItemDataRole]) -> None:
|
||||||
"""
|
"""
|
||||||
Signal to view to refresh invalidated row
|
Signal to view to refresh invalidated row
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
log.debug(f"issue285: {self}: invalidate_row({modified_row=})")
|
||||||
|
|
||||||
self.dataChanged.emit(
|
self.dataChanged.emit(
|
||||||
self.index(modified_row, 0),
|
self.index(modified_row, 0),
|
||||||
self.index(modified_row, self.columnCount() - 1),
|
self.index(modified_row, self.columnCount() - 1),
|
||||||
@ -775,6 +782,8 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
Signal to view to refresh invlidated rows
|
Signal to view to refresh invlidated rows
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
log.debug(f"issue285: {self}: invalidate_rows({modified_rows=})")
|
||||||
|
|
||||||
for modified_row in modified_rows:
|
for modified_row in modified_rows:
|
||||||
# only invalidate required roles
|
# only invalidate required roles
|
||||||
self.invalidate_row(modified_row, roles)
|
self.invalidate_row(modified_row, roles)
|
||||||
@ -788,7 +797,6 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
return self.playlist_rows[row_number].path == ""
|
return self.playlist_rows[row_number].path == ""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@log_call
|
|
||||||
def is_played_row(self, row_number: int) -> bool:
|
def is_played_row(self, row_number: int) -> bool:
|
||||||
"""
|
"""
|
||||||
Return True if row is an unplayed track row, else False
|
Return True if row is an unplayed track row, else False
|
||||||
@ -824,7 +832,6 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
]
|
]
|
||||||
self.invalidate_rows(row_numbers, roles)
|
self.invalidate_rows(row_numbers, roles)
|
||||||
|
|
||||||
@log_call
|
|
||||||
def move_rows(self, from_rows: list[int], to_row_number: int) -> bool:
|
def move_rows(self, from_rows: list[int], to_row_number: int) -> bool:
|
||||||
"""
|
"""
|
||||||
Move the playlist rows in from_rows to to_row. Return True if successful
|
Move the playlist rows in from_rows to to_row. Return True if successful
|
||||||
@ -846,46 +853,11 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
)
|
)
|
||||||
from_rows.remove(self.track_sequence.current.row_number)
|
from_rows.remove(self.track_sequence.current.row_number)
|
||||||
|
|
||||||
from_rows = sorted(set(from_rows))
|
# Row moves must be wrapped in beginMoveRows .. endMoveRows and
|
||||||
if (min(from_rows) < 0 or max(from_rows) >= self.rowCount()
|
# the row range must be contiguous. Process the highest rows
|
||||||
or to_row_number < 0 or to_row_number > self.rowCount()):
|
# first so the lower row numbers are unchanged
|
||||||
log.debug("move_rows: invalid indexes")
|
|
||||||
return False
|
|
||||||
|
|
||||||
if to_row_number in from_rows:
|
row_groups = self._reversed_contiguous_row_groups([a.row_number for a in from_rows])
|
||||||
return False # Destination within rows to be moved
|
|
||||||
|
|
||||||
# # Remove rows from bottom to top to avoid index shifting
|
|
||||||
# for row in sorted(from_rows, reverse=True):
|
|
||||||
# self.beginRemoveRows(QModelIndex(), row, row)
|
|
||||||
# del self.playlist_rows[row]
|
|
||||||
# # At this point self.playlist_rows has been updated but the
|
|
||||||
# # underlying database has not (that's done below after
|
|
||||||
# # inserting the rows)
|
|
||||||
# self.endRemoveRows()
|
|
||||||
|
|
||||||
# # Adjust insertion point after removal
|
|
||||||
# if to_row_number > max(from_rows):
|
|
||||||
# rows_below_dest = len([r for r in from_rows if r < to_row_number])
|
|
||||||
# insertion_point = to_row_number - rows_below_dest
|
|
||||||
# else:
|
|
||||||
# insertion_point = to_row_number
|
|
||||||
|
|
||||||
# # Insert rows at the destination
|
|
||||||
# plrid_to_new_row_number: list[dict[int, int]] = []
|
|
||||||
# for offset, row_data in enumerate(rows_to_move):
|
|
||||||
# row_number = insertion_point + offset
|
|
||||||
# self.beginInsertRows(QModelIndex(), row_number, row_number)
|
|
||||||
# self.playlist_rows[row_number] = row_data
|
|
||||||
# plrid_to_new_row_number.append({row_data.playlistrow_id: row_number})
|
|
||||||
# self.endInsertRows()
|
|
||||||
|
|
||||||
# Notify model going to change
|
|
||||||
self.beginResetModel()
|
|
||||||
# Update database
|
|
||||||
repository.move_rows(from_rows, self.playlist_id, to_row_number)
|
|
||||||
# Notify model changed
|
|
||||||
self.endResetModel()
|
|
||||||
|
|
||||||
# Handle the moves in row_group chunks
|
# Handle the moves in row_group chunks
|
||||||
for row_group in row_groups:
|
for row_group in row_groups:
|
||||||
@ -937,7 +909,6 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
super().endInsertRows()
|
super().endInsertRows()
|
||||||
|
|
||||||
@log_call
|
|
||||||
def move_rows_between_playlists(
|
def move_rows_between_playlists(
|
||||||
self,
|
self,
|
||||||
from_rows: list[PlaylistRow],
|
from_rows: list[PlaylistRow],
|
||||||
@ -948,6 +919,11 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
Move the playlist rows given to to_row and below of to_playlist.
|
Move the playlist rows given to to_row and below of to_playlist.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
log.debug(
|
||||||
|
f"{self}: move_rows_between_playlists({from_rows=}, "
|
||||||
|
f"{to_row_number=}, {to_playlist_id=}"
|
||||||
|
)
|
||||||
|
|
||||||
# Don't move current row
|
# Don't move current row
|
||||||
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
|
||||||
@ -986,7 +962,6 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
self.track_sequence.update()
|
self.track_sequence.update()
|
||||||
self.update_track_times()
|
self.update_track_times()
|
||||||
|
|
||||||
@log_call
|
|
||||||
def move_track_add_note(
|
def move_track_add_note(
|
||||||
self, new_row_number: int, existing_plr: PlaylistRow, note: str
|
self, new_row_number: int, existing_plr: PlaylistRow, note: str
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -994,6 +969,10 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
Move existing_rat track to new_row_number and append note to any existing note
|
Move existing_rat track to new_row_number and append note to any existing note
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
log.debug(
|
||||||
|
f"{self}: move_track_add_note({new_row_number=}, {existing_plr=}, {note=}"
|
||||||
|
)
|
||||||
|
|
||||||
if note:
|
if note:
|
||||||
playlist_row = self.playlist_rows[existing_plr.row_number]
|
playlist_row = self.playlist_rows[existing_plr.row_number]
|
||||||
if playlist_row.note:
|
if playlist_row.note:
|
||||||
@ -1007,30 +986,14 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
self.move_rows([existing_plr.row_number], new_row_number)
|
self.move_rows([existing_plr.row_number], new_row_number)
|
||||||
self.signals.resize_rows_signal.emit(self.playlist_id)
|
self.signals.resize_rows_signal.emit(self.playlist_id)
|
||||||
|
|
||||||
@log_call
|
|
||||||
def move_track_to_header(
|
|
||||||
self,
|
|
||||||
header_row_number: int,
|
|
||||||
existing_rat: RowAndTrack,
|
|
||||||
note: Optional[str],
|
|
||||||
) -> None:
|
|
||||||
"""
|
|
||||||
Add the existing_rat track details to the existing header at header_row_number
|
|
||||||
"""
|
|
||||||
|
|
||||||
if existing_rat.track_id:
|
|
||||||
if note and existing_rat.note:
|
|
||||||
note += "\n" + existing_rat.note
|
|
||||||
self.add_track_to_header(header_row_number, existing_rat.track_id, note)
|
|
||||||
self.delete_rows([existing_rat.row_number])
|
|
||||||
|
|
||||||
@log_call
|
|
||||||
def obs_scene_change(self, row_number: int) -> None:
|
def obs_scene_change(self, row_number: int) -> None:
|
||||||
"""
|
"""
|
||||||
Check this row and any preceding headers for OBS scene change command
|
Check this row and any preceding headers for OBS scene change command
|
||||||
and execute any found
|
and execute any found
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
log.debug(f"{self}: obs_scene_change({row_number=})")
|
||||||
|
|
||||||
# Check any headers before this row
|
# Check any headers before this row
|
||||||
idx = row_number - 1
|
idx = row_number - 1
|
||||||
while self.is_header_row(idx):
|
while self.is_header_row(idx):
|
||||||
@ -1059,7 +1022,6 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
log.warning(f"{self}: OBS connection refused")
|
log.warning(f"{self}: OBS connection refused")
|
||||||
return
|
return
|
||||||
|
|
||||||
@log_call
|
|
||||||
def previous_track_ended(self) -> None:
|
def previous_track_ended(self) -> None:
|
||||||
"""
|
"""
|
||||||
Notification from musicmuster that the previous track has ended.
|
Notification from musicmuster that the previous track has ended.
|
||||||
@ -1069,6 +1031,8 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
- update display
|
- update display
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
log.debug(f"{self}: previous_track_ended()")
|
||||||
|
|
||||||
# Sanity check
|
# Sanity check
|
||||||
if not self.track_sequence.previous:
|
if not self.track_sequence.previous:
|
||||||
log.error(
|
log.error(
|
||||||
@ -1124,12 +1088,13 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
self.playlist_rows[row_number] = PlaylistRow(refreshed_row)
|
self.playlist_rows[row_number] = PlaylistRow(refreshed_row)
|
||||||
|
|
||||||
@log_call
|
|
||||||
def remove_track(self, row_number: int) -> None:
|
def remove_track(self, row_number: int) -> None:
|
||||||
"""
|
"""
|
||||||
Remove track from row, retaining row as a header row
|
Remove track from row, retaining row as a header row
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
log.debug(f"{self}: remove_track({row_number=})")
|
||||||
|
|
||||||
self.playlist_rows[row_number].track_id = None
|
self.playlist_rows[row_number].track_id = None
|
||||||
|
|
||||||
# only invalidate required roles
|
# only invalidate required roles
|
||||||
@ -1159,31 +1124,6 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
self.signals.resize_rows_signal.emit(self.playlist_id)
|
self.signals.resize_rows_signal.emit(self.playlist_id)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
@log_call
|
|
||||||
def reset_track_sequence_row_numbers(self) -> None:
|
|
||||||
"""
|
|
||||||
Signal handler for when row ordering has changed.
|
|
||||||
|
|
||||||
Example: row 4 is marked as next. Row 2 is deleted. The PlaylistRows table will
|
|
||||||
be correctly updated with change of row number, but track_sequence.next will still
|
|
||||||
contain row_number==4. This function fixes up the track_sequence row numbers by
|
|
||||||
looking up the playlistrow_id and retrieving the row number from the database.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Check the track_sequence.next, current and previous plrs and
|
|
||||||
# update the row number
|
|
||||||
with db.Session() as session:
|
|
||||||
for ts in [
|
|
||||||
track_sequence.next,
|
|
||||||
track_sequence.current,
|
|
||||||
track_sequence.previous,
|
|
||||||
]:
|
|
||||||
if ts:
|
|
||||||
ts.update_playlist_and_row(session)
|
|
||||||
session.commit()
|
|
||||||
|
|
||||||
self.update_track_times()
|
|
||||||
|
|
||||||
def remove_comments(self, row_numbers: list[int]) -> None:
|
def remove_comments(self, row_numbers: list[int]) -> None:
|
||||||
"""
|
"""
|
||||||
Remove comments from passed rows
|
Remove comments from passed rows
|
||||||
@ -1222,10 +1162,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
]
|
]
|
||||||
self.invalidate_rows(row_numbers, roles)
|
self.invalidate_rows(row_numbers, roles)
|
||||||
|
|
||||||
@log_call
|
def _reversed_contiguous_row_groups(self, row_numbers: list[int]) -> list[list[int]]:
|
||||||
def _reversed_contiguous_row_groups(
|
|
||||||
self, row_numbers: list[int]
|
|
||||||
) -> list[list[int]]:
|
|
||||||
"""
|
"""
|
||||||
Take the list of row numbers and split into groups of contiguous rows. Return as a list
|
Take the list of row numbers and split into groups of contiguous rows. Return as a list
|
||||||
of lists with the highest row numbers first.
|
of lists with the highest row numbers first.
|
||||||
@ -1235,6 +1172,8 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
return: [[20, 21], [17], [13], [9, 10], [7], [2, 3, 4, 5]]
|
return: [[20, 21], [17], [13], [9, 10], [7], [2, 3, 4, 5]]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
log.debug(f"{self}: _reversed_contiguous_row_groups({row_numbers=} called")
|
||||||
|
|
||||||
result: list[list[int]] = []
|
result: list[list[int]] = []
|
||||||
temp: list[int] = []
|
temp: list[int] = []
|
||||||
last_value = row_numbers[0] - 1
|
last_value = row_numbers[0] - 1
|
||||||
@ -1250,11 +1189,12 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
result.append(temp)
|
result.append(temp)
|
||||||
result.reverse()
|
result.reverse()
|
||||||
|
|
||||||
|
log.debug(f"{self}: _reversed_contiguous_row_groups() returned: {result=}")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def remove_section_timer_markers(self, header_text: str) -> str:
|
def remove_section_timer_markers(self, header_text: str) -> str:
|
||||||
"""
|
"""
|
||||||
Remove characters used to mark section timings from
|
Remove characters used to mark section timeings from
|
||||||
passed header text.
|
passed header text.
|
||||||
|
|
||||||
Remove text using to signal header colours if colour entry
|
Remove text using to signal header colours if colour entry
|
||||||
@ -1387,7 +1327,6 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@log_call
|
|
||||||
def set_selected_rows(self, playlist_id: int, selected_row_numbers: list[int]) -> None:
|
def set_selected_rows(self, playlist_id: int, selected_row_numbers: list[int]) -> None:
|
||||||
"""
|
"""
|
||||||
Handle signal_playlist_selected_rows to keep track of which rows
|
Handle signal_playlist_selected_rows to keep track of which rows
|
||||||
@ -1607,7 +1546,6 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
@log_call
|
|
||||||
def update_or_insert(self, track_id: int, row_number: int) -> None:
|
def update_or_insert(self, track_id: int, row_number: int) -> None:
|
||||||
"""
|
"""
|
||||||
If the passed track_id exists in this playlist, update the
|
If the passed track_id exists in this playlist, update the
|
||||||
@ -1631,12 +1569,13 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
else:
|
else:
|
||||||
self.insert_row(proposed_row_number=row_number, track_id=track_id)
|
self.insert_row(proposed_row_number=row_number, track_id=track_id)
|
||||||
|
|
||||||
@log_call
|
|
||||||
def update_track_times(self) -> None:
|
def update_track_times(self) -> None:
|
||||||
"""
|
"""
|
||||||
Update track start/end times in self.playlist_rows
|
Update track start/end times in self.playlist_rows
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
log.debug(f"issue285: {self}: update_track_times()")
|
||||||
|
|
||||||
next_start_time: Optional[dt.datetime] = None
|
next_start_time: Optional[dt.datetime] = None
|
||||||
update_rows: list[int] = []
|
update_rows: list[int] = []
|
||||||
row_count = len(self.playlist_rows)
|
row_count = len(self.playlist_rows)
|
||||||
|
|||||||
@ -44,7 +44,7 @@ from helpers import (
|
|||||||
show_OK,
|
show_OK,
|
||||||
show_warning,
|
show_warning,
|
||||||
)
|
)
|
||||||
from log import log, log_call
|
from log import log
|
||||||
from models import db, Settings
|
from models import db, Settings
|
||||||
from playlistrow import TrackSequence
|
from playlistrow import TrackSequence
|
||||||
from playlistmodel import PlaylistModel, PlaylistProxyModel
|
from playlistmodel import PlaylistModel, PlaylistProxyModel
|
||||||
@ -359,8 +359,7 @@ class PlaylistTab(QTableView):
|
|||||||
# Deselect edited line
|
# Deselect edited line
|
||||||
self.clear_selection()
|
self.clear_selection()
|
||||||
|
|
||||||
@log_call
|
def dropEvent(self, event: Optional[QDropEvent], dummy: int | None = None) -> None:
|
||||||
def dropEvent(self, event: Optional[QDropEvent]) -> None:
|
|
||||||
"""
|
"""
|
||||||
Move dropped rows
|
Move dropped rows
|
||||||
"""
|
"""
|
||||||
@ -396,6 +395,9 @@ class PlaylistTab(QTableView):
|
|||||||
destination_index = to_index
|
destination_index = to_index
|
||||||
|
|
||||||
to_model_row = self.model().mapToSource(destination_index).row()
|
to_model_row = self.model().mapToSource(destination_index).row()
|
||||||
|
log.debug(
|
||||||
|
f"PlaylistTab.dropEvent(): {from_rows=}, {destination_index=}, {to_model_row=}"
|
||||||
|
)
|
||||||
|
|
||||||
# Sanity check
|
# Sanity check
|
||||||
base_model_row_count = self.get_base_model().rowCount()
|
base_model_row_count = self.get_base_model().rowCount()
|
||||||
@ -678,6 +680,8 @@ class PlaylistTab(QTableView):
|
|||||||
Called when column width changes. Save new width to database.
|
Called when column width changes. Save new width to database.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
log.debug(f"_column_resize({column_number=}, {_old=}, {_new=}")
|
||||||
|
|
||||||
header = self.horizontalHeader()
|
header = self.horizontalHeader()
|
||||||
if not header:
|
if not header:
|
||||||
return
|
return
|
||||||
@ -722,7 +726,6 @@ class PlaylistTab(QTableView):
|
|||||||
cb.clear(mode=cb.Mode.Clipboard)
|
cb.clear(mode=cb.Mode.Clipboard)
|
||||||
cb.setText(track_path, mode=cb.Mode.Clipboard)
|
cb.setText(track_path, mode=cb.Mode.Clipboard)
|
||||||
|
|
||||||
@log_call
|
|
||||||
def current_track_started(self) -> None:
|
def current_track_started(self) -> None:
|
||||||
"""
|
"""
|
||||||
Called when track starts playing
|
Called when track starts playing
|
||||||
@ -810,7 +813,6 @@ class PlaylistTab(QTableView):
|
|||||||
else:
|
else:
|
||||||
return TrackInfo(track_id, selected_row)
|
return TrackInfo(track_id, selected_row)
|
||||||
|
|
||||||
@log_call
|
|
||||||
def get_selected_row(self) -> Optional[int]:
|
def get_selected_row(self) -> Optional[int]:
|
||||||
"""
|
"""
|
||||||
Return selected row number. If no rows or multiple rows selected, return None
|
Return selected row number. If no rows or multiple rows selected, return None
|
||||||
@ -822,7 +824,6 @@ class PlaylistTab(QTableView):
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@log_call
|
|
||||||
def get_selected_rows(self) -> list[int]:
|
def get_selected_rows(self) -> list[int]:
|
||||||
"""Return a list of model-selected row numbers sorted by row"""
|
"""Return a list of model-selected row numbers sorted by row"""
|
||||||
|
|
||||||
@ -835,7 +836,6 @@ class PlaylistTab(QTableView):
|
|||||||
|
|
||||||
return sorted(list(set([self.model().mapToSource(a).row() for a in selected_indexes])))
|
return sorted(list(set([self.model().mapToSource(a).row() for a in selected_indexes])))
|
||||||
|
|
||||||
@log_call
|
|
||||||
def get_top_visible_row(self) -> int:
|
def get_top_visible_row(self) -> int:
|
||||||
"""
|
"""
|
||||||
Get the viewport of the table view
|
Get the viewport of the table view
|
||||||
@ -958,6 +958,8 @@ class PlaylistTab(QTableView):
|
|||||||
If playlist_id is us, resize rows
|
If playlist_id is us, resize rows
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
log.debug(f"resize_rows({playlist_id=}) {self.playlist_id=}")
|
||||||
|
|
||||||
if playlist_id and playlist_id != self.playlist_id:
|
if playlist_id and playlist_id != self.playlist_id:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -1004,7 +1006,6 @@ class PlaylistTab(QTableView):
|
|||||||
# Reset selection mode
|
# Reset selection mode
|
||||||
self.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)
|
self.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)
|
||||||
|
|
||||||
@log_call
|
|
||||||
def source_model_selected_row_number(self) -> Optional[int]:
|
def source_model_selected_row_number(self) -> Optional[int]:
|
||||||
"""
|
"""
|
||||||
Return the model row number corresponding to the selected row or None
|
Return the model row number corresponding to the selected row or None
|
||||||
@ -1015,7 +1016,6 @@ class PlaylistTab(QTableView):
|
|||||||
return None
|
return None
|
||||||
return self.model().mapToSource(selected_index).row()
|
return self.model().mapToSource(selected_index).row()
|
||||||
|
|
||||||
@log_call
|
|
||||||
def selected_model_row_numbers(self) -> list[int]:
|
def selected_model_row_numbers(self) -> list[int]:
|
||||||
"""
|
"""
|
||||||
Return a list of model row numbers corresponding to the selected rows or
|
Return a list of model row numbers corresponding to the selected rows or
|
||||||
@ -1058,6 +1058,8 @@ class PlaylistTab(QTableView):
|
|||||||
def _set_column_widths(self) -> None:
|
def _set_column_widths(self) -> None:
|
||||||
"""Column widths from settings"""
|
"""Column widths from settings"""
|
||||||
|
|
||||||
|
log.debug("_set_column_widths()")
|
||||||
|
|
||||||
header = self.horizontalHeader()
|
header = self.horizontalHeader()
|
||||||
if not header:
|
if not header:
|
||||||
return
|
return
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user