Move selected / move unplayed working

This commit is contained in:
Keith Edmunds 2022-08-15 12:29:36 +01:00
parent eff80d684e
commit d5950ab29a
5 changed files with 125 additions and 105 deletions

View File

@ -556,29 +556,33 @@ class PlaylistRows(Base):
return plrs return plrs
@staticmethod @staticmethod
def move_to_playlist(session: Session, def get_last_used_row(session: Session, playlist_id: int) -> Optional[int]:
playlistrow_ids: List[int], """Return the last used row for playlist, or None if no rows"""
destination_playlist_id: int) -> None:
"""
Move the list of playlistrow_ids to the end of destination_playlist
"""
# Find last row of destination playlist return session.execute(
last_row = session.execute(
select(func.max(PlaylistRows.row_number)) select(func.max(PlaylistRows.row_number))
.where(PlaylistRows.playlist_id == destination_playlist_id) .where(PlaylistRows.playlist_id == playlist_id)
).scalar_one() ).scalar_one()
if last_row is None:
last_row = 0
# Update the PlaylistRows entries @classmethod
for plr_id in playlistrow_ids: def get_unplayed_rows(cls, session: Session,
last_row += 1 playlist_id: int) -> List[int]:
plr = session.get(PlaylistRows, plr_id) """
plr.row_number = last_row For passed playlist, return a list of track rows that
plr.playlist_id = destination_playlist_id have not been played.
"""
session.commit() plrs = session.execute(
select(cls)
.where(
cls.playlist_id == playlist_id,
cls.track_id.is_not(None),
cls.played.is_(False)
)
.order_by(cls.row_number)
).scalars().all()
return plrs
# @classmethod # @classmethod
# def get_playlist_rows(cls, playlist_id: int) -> \ # def get_playlist_rows(cls, playlist_id: int) -> \

View File

@ -7,6 +7,7 @@ import sys
from datetime import datetime, timedelta from datetime import datetime, timedelta
# from typing import Callable, Dict, List, Optional, Tuple # from typing import Callable, Dict, List, Optional, Tuple
from typing import List
from PyQt5.QtCore import QDate, QEvent, Qt, QTime, QTimer from PyQt5.QtCore import QDate, QEvent, Qt, QTime, QTimer
from PyQt5.QtGui import QColor from PyQt5.QtGui import QColor
@ -175,8 +176,7 @@ class Window(QMainWindow, Ui_MainWindow):
# self.actionSelect_played_tracks.triggered.connect(self.select_played) # self.actionSelect_played_tracks.triggered.connect(self.select_played)
self.actionSelect_previous_track.triggered.connect( self.actionSelect_previous_track.triggered.connect(
self.select_previous_row) self.select_previous_row)
# self.actionSelect_unplayed_tracks.triggered.connect( self.actionMoveUnplayed.triggered.connect(self.move_unplayed)
# self.select_unplayed)
self.actionSetNext.triggered.connect( self.actionSetNext.triggered.connect(
lambda: self.tabPlaylist.currentWidget().set_selected_as_next()) lambda: self.tabPlaylist.currentWidget().set_selected_as_next())
self.actionSkipToNext.triggered.connect(self.play_next) self.actionSkipToNext.triggered.connect(self.play_next)
@ -485,9 +485,10 @@ class Window(QMainWindow, Ui_MainWindow):
self.create_playlist_tab(session, playlist) self.create_playlist_tab(session, playlist)
playlist.mark_open(session) playlist.mark_open(session)
def move_selected(self) -> None: def move_playlist_rows(self, session: Session,
playlistrows: List[PlaylistRows]) -> None:
""" """
Move selected rows to another playlist Move passed playlist rows to another playlist
Actions required: Actions required:
- identify destination playlist - identify destination playlist
@ -496,8 +497,10 @@ class Window(QMainWindow, Ui_MainWindow):
- update destination playlist display if loaded - update destination playlist display if loaded
""" """
if not playlistrows:
log.debug(f"musicmuster.move_playlist_rows({playlistrows=}")
# Identify destination playlist # Identify destination playlist
with Session() as session:
visible_tab = self.visible_playlist_tab() visible_tab = self.visible_playlist_tab()
source_playlist = visible_tab.playlist_id source_playlist = visible_tab.playlist_id
playlists = [] playlists = []
@ -508,21 +511,28 @@ class Window(QMainWindow, Ui_MainWindow):
playlists.append(playlist) playlists.append(playlist)
# Get destination playlist id # Get destination playlist id
dlg = SelectPlaylistDialog(self, playlists=playlists, dlg = SelectPlaylistDialog(self, playlists=playlists, session=session)
session=session)
dlg.exec() dlg.exec()
if not dlg.playlist: if not dlg.playlist:
return return
destination_playlist = dlg.playlist destination_playlist_id = dlg.playlist.id
# Update playlist for the rows in the database
plr_ids = visible_tab.get_selected_playlistrow_ids()
PlaylistRows.move_to_playlist(
session, plr_ids, destination_playlist.id
)
# Remove moved rows from display # Remove moved rows from display
visible_tab.remove_selected_rows() visible_tab.remove_rows([plr.row_number for plr in playlistrows])
# Update playlist for the rows in the database
last_row = PlaylistRows.get_last_used_row(session,
destination_playlist_id)
if last_row is not None:
next_row = last_row + 1
else:
next_row = 0
for plr in playlistrows:
plr.row_number = next_row
plr.playlist_id = destination_playlist_id
# Reset played as it's not been played on this playlist
plr.played = False
# Update destination playlist_tab if visible (if not visible, it # Update destination playlist_tab if visible (if not visible, it
# will be re-populated when it is opened) # will be re-populated when it is opened)
@ -534,6 +544,28 @@ class Window(QMainWindow, Ui_MainWindow):
if destionation_playlist_tab: if destionation_playlist_tab:
destionation_playlist_tab.populate(session, dlg.playlist.id) destionation_playlist_tab.populate(session, dlg.playlist.id)
def move_selected(self) -> None:
"""
Move selected rows to another playlist
"""
with Session() as session:
self.move_playlist_rows(
session,
self.visible_playlist_tab().get_selected_playlistrows(session)
)
def move_unplayed(self) -> None:
"""
Move unplayed rows to another playlist
"""
playlist_id = self.visible_playlist_tab().playlist_id
with Session() as session:
unplayed_playlist_rows = PlaylistRows.get_unplayed_rows(
session, playlist_id)
self.move_playlist_rows(session, unplayed_playlist_rows)
def play_next(self) -> None: def play_next(self) -> None:
""" """
Play next track. Play next track.
@ -640,7 +672,7 @@ class Window(QMainWindow, Ui_MainWindow):
"""Tidy up and reset search bar""" """Tidy up and reset search bar"""
# Clear the search text # Clear the search text
self.visible_playlist_tab().search("") self.visible_playlist_tab().set_search("")
# Clean up search bar # Clean up search bar
self.txtSearch.setText("") self.txtSearch.setText("")
self.txtSearch.setHidden(True) self.txtSearch.setHidden(True)
@ -681,11 +713,6 @@ class Window(QMainWindow, Ui_MainWindow):
"""Select previous or first row in playlist""" """Select previous or first row in playlist"""
self.visible_playlist_tab().select_previous_row() self.visible_playlist_tab().select_previous_row()
#
# def select_unplayed(self) -> None:
# """Select all unplayed tracks in playlist"""
#
# self.visible_playlist_tab().select_unplayed_tracks()
def set_main_window_size(self) -> None: def set_main_window_size(self) -> None:
"""Set size of window from database""" """Set size of window from database"""

View File

@ -156,7 +156,7 @@ class PlaylistTab(QTableWidget):
self.populate(session, self.playlist_id) self.populate(session, self.playlist_id)
def __repr__(self) -> str: def __repr__(self) -> str:
return f"<PlaylistTab(id={self.playlist_id}" return f"<PlaylistTab(id={self.playlist_id}>"
# ########## Events other than cell editing ########## # ########## Events other than cell editing ##########
@ -472,7 +472,15 @@ class PlaylistTab(QTableWidget):
Return a list of PlaylistRow ids of the selected rows Return a list of PlaylistRow ids of the selected rows
""" """
return [self._get_playlistrow_id(a) for a in self._selected_rows()] return [self._get_playlistrow_id(a) for a in self._get_selected_rows()]
def get_selected_playlistrows(self, session: Session) -> Optional[List]:
"""
Return a list of PlaylistRows of the selected rows
"""
plr_ids = self.get_selected_playlistrow_ids()
return [session.get(PlaylistRows, a) for a in plr_ids]
def insert_header(self, session: Session, note: str, def insert_header(self, session: Session, note: str,
repaint: bool = True) -> None: repaint: bool = True) -> None:
@ -497,12 +505,6 @@ class PlaylistTab(QTableWidget):
if repaint: if repaint:
self.update_display(session) self.update_display(session)
# #
# def _get_selected_rows(self) -> List[int]:
# """Return a sorted list of selected row numbers"""
#
# rows = self.selectionModel().selectedRows()
# return sorted([row.row() for row in rows])
#
# def get_selected_title(self) -> Optional[str]: # def get_selected_title(self) -> Optional[str]:
# """Return title of selected row or None""" # """Return title of selected row or None"""
# #
@ -747,13 +749,18 @@ class PlaylistTab(QTableWidget):
# self.save_playlist(session) # self.save_playlist(session)
self.update_display(session) self.update_display(session)
def remove_selected_rows(self) -> None: def remove_rows(self, row_numbers: List[int]) -> None:
"""Remove selected rows from display""" """Remove passed rows from display"""
# Remove rows from display. Do so in reverse order so that # Remove rows from display. Do so in reverse order so that
# row numbers remain valid. # row numbers remain valid.
for row in sorted(self._selected_rows(), reverse=True): for row in sorted(row_numbers, reverse=True):
self.removeRow(row) self.removeRow(row)
def remove_selected_rows(self) -> None:
"""Remove selected rows from display"""
self.remove_rows(self._get_selected_rows())
# Reset drag mode # Reset drag mode
self.setDragEnabled(False) self.setDragEnabled(False)
@ -868,7 +875,7 @@ class PlaylistTab(QTableWidget):
row: int row: int
selected_rows: List[int] selected_rows: List[int]
selected_rows = self._selected_rows() selected_rows = self._get_selected_rows()
# we will only handle zero or one selected rows # we will only handle zero or one selected rows
if len(selected_rows) > 1: if len(selected_rows) > 1:
return return
@ -915,7 +922,7 @@ class PlaylistTab(QTableWidget):
row: int row: int
selected_rows: List[int] selected_rows: List[int]
selected_rows = self._selected_rows() selected_rows = self._get_selected_rows()
# we will only handle zero or one selected rows # we will only handle zero or one selected rows
if len(selected_rows) > 1: if len(selected_rows) > 1:
return return
@ -943,16 +950,6 @@ class PlaylistTab(QTableWidget):
track_id = self._get_row_track_id(row) track_id = self._get_row_track_id(row)
self.selectRow(row) self.selectRow(row)
#
# def select_unplayed_tracks(self) -> None:
# """Select all unplayed tracks in playlist"""
#
# try:
# self.selecting_in_progress = True
# self._select_tracks(played=False)
# finally:
# self.selecting_in_progress = False
# self._select_event()
def set_searchtext(self, text: Optional[str]) -> None: def set_searchtext(self, text: Optional[str]) -> None:
"""Set the search text and find first match""" """Set the search text and find first match"""
@ -1393,14 +1390,6 @@ class PlaylistTab(QTableWidget):
return track_id return track_id
# def _get_unplayed_track_rows(self) -> Optional[List[int]]:
# """Return rows marked as unplayed, or None"""
#
# unplayed_rows: Set[int] = set(self._meta_notset(RowMeta.PLAYED))
# notes_rows: Set[int] = set(self._get_notes_rows())
#
# return list(unplayed_rows - notes_rows)
def _get_selected_row(self) -> Optional[int]: def _get_selected_row(self) -> Optional[int]:
"""Return row number of first selected row, or None if none selected""" """Return row number of first selected row, or None if none selected"""
@ -1409,6 +1398,13 @@ class PlaylistTab(QTableWidget):
else: else:
return self.selectionModel().selectedRows()[0].row() return self.selectionModel().selectedRows()[0].row()
def _get_selected_rows(self) -> List[int]:
"""Return a list of selected row numbers"""
# Use a set to deduplicate result (a selected row will have all
# items in that row selected)
return [row for row in set([a.row() for a in self.selectedItems()])]
def _get_unreadable_track_rows(self) -> List[int]: def _get_unreadable_track_rows(self) -> List[int]:
"""Return rows marked as unreadable, or None""" """Return rows marked as unreadable, or None"""
@ -1680,7 +1676,7 @@ class PlaylistTab(QTableWidget):
if self.selecting_in_progress: if self.selecting_in_progress:
return return
selected_rows = self._selected_rows() selected_rows = self._get_selected_rows()
# If no rows are selected, we have nothing to do # If no rows are selected, we have nothing to do
if len(selected_rows) == 0: if len(selected_rows) == 0:
self.musicmuster.lblSumPlaytime.setText("") self.musicmuster.lblSumPlaytime.setText("")
@ -1697,13 +1693,6 @@ class PlaylistTab(QTableWidget):
else: else:
self.musicmuster.lblSumPlaytime.setText("") self.musicmuster.lblSumPlaytime.setText("")
def _selected_rows(self) -> List[int]:
"""Return a list of selected row numbers"""
# Use a set to deduplicate result (a selected row will have all
# items in that row selected)
return [row for row in set([a.row() for a in self.selectedItems()])]
def _set_column_widths(self, session: Session) -> None: def _set_column_widths(self, session: Session) -> None:
"""Column widths from settings""" """Column widths from settings"""

View File

@ -763,7 +763,7 @@ border: 1px solid rgb(85, 87, 83);</string>
<addaction name="actionDeletePlaylist"/> <addaction name="actionDeletePlaylist"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionMoveSelected"/> <addaction name="actionMoveSelected"/>
<addaction name="actionMove_unplayed"/> <addaction name="actionMoveUnplayed"/>
<addaction name="actionDownload_CSV_of_played_tracks"/> <addaction name="actionDownload_CSV_of_played_tracks"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionE_xit"/> <addaction name="actionE_xit"/>
@ -1000,7 +1000,7 @@ border: 1px solid rgb(85, 87, 83);</string>
<string>Select played tracks</string> <string>Select played tracks</string>
</property> </property>
</action> </action>
<action name="actionMove_unplayed"> <action name="actionMoveUnplayed">
<property name="text"> <property name="text">
<string>Move &amp;unplayed tracks to...</string> <string>Move &amp;unplayed tracks to...</string>
</property> </property>

View File

@ -430,8 +430,8 @@ class Ui_MainWindow(object):
self.actionSelect_previous_track.setObjectName("actionSelect_previous_track") self.actionSelect_previous_track.setObjectName("actionSelect_previous_track")
self.actionSelect_played_tracks = QtWidgets.QAction(MainWindow) self.actionSelect_played_tracks = QtWidgets.QAction(MainWindow)
self.actionSelect_played_tracks.setObjectName("actionSelect_played_tracks") self.actionSelect_played_tracks.setObjectName("actionSelect_played_tracks")
self.actionMove_unplayed = QtWidgets.QAction(MainWindow) self.actionMoveUnplayed = QtWidgets.QAction(MainWindow)
self.actionMove_unplayed.setObjectName("actionMove_unplayed") self.actionMoveUnplayed.setObjectName("actionMoveUnplayed")
self.actionAdd_note = QtWidgets.QAction(MainWindow) self.actionAdd_note = QtWidgets.QAction(MainWindow)
self.actionAdd_note.setObjectName("actionAdd_note") self.actionAdd_note.setObjectName("actionAdd_note")
self.actionEnable_controls = QtWidgets.QAction(MainWindow) self.actionEnable_controls = QtWidgets.QAction(MainWindow)
@ -458,7 +458,7 @@ class Ui_MainWindow(object):
self.menuFile.addAction(self.actionDeletePlaylist) self.menuFile.addAction(self.actionDeletePlaylist)
self.menuFile.addSeparator() self.menuFile.addSeparator()
self.menuFile.addAction(self.actionMoveSelected) self.menuFile.addAction(self.actionMoveSelected)
self.menuFile.addAction(self.actionMove_unplayed) self.menuFile.addAction(self.actionMoveUnplayed)
self.menuFile.addAction(self.actionDownload_CSV_of_played_tracks) self.menuFile.addAction(self.actionDownload_CSV_of_played_tracks)
self.menuFile.addSeparator() self.menuFile.addSeparator()
self.menuFile.addAction(self.actionE_xit) self.menuFile.addAction(self.actionE_xit)
@ -559,7 +559,7 @@ class Ui_MainWindow(object):
self.actionSelect_previous_track.setText(_translate("MainWindow", "Select previous track")) self.actionSelect_previous_track.setText(_translate("MainWindow", "Select previous track"))
self.actionSelect_previous_track.setShortcut(_translate("MainWindow", "K")) self.actionSelect_previous_track.setShortcut(_translate("MainWindow", "K"))
self.actionSelect_played_tracks.setText(_translate("MainWindow", "Select played tracks")) self.actionSelect_played_tracks.setText(_translate("MainWindow", "Select played tracks"))
self.actionMove_unplayed.setText(_translate("MainWindow", "Move &unplayed tracks to...")) self.actionMoveUnplayed.setText(_translate("MainWindow", "Move &unplayed tracks to..."))
self.actionAdd_note.setText(_translate("MainWindow", "Add note...")) self.actionAdd_note.setText(_translate("MainWindow", "Add note..."))
self.actionAdd_note.setShortcut(_translate("MainWindow", "Ctrl+T")) self.actionAdd_note.setShortcut(_translate("MainWindow", "Ctrl+T"))
self.actionEnable_controls.setText(_translate("MainWindow", "Enable controls")) self.actionEnable_controls.setText(_translate("MainWindow", "Enable controls"))