diff --git a/app/model.py b/app/model.py index 5608e4e..b591391 100644 --- a/app/model.py +++ b/app/model.py @@ -200,16 +200,29 @@ class Playlists(Base): return ( session.query(Playlists) - .filter(Playlists.loaded == True) + .filter(Playlists.loaded == True) # noqa E712 .order_by(Playlists.last_used.desc()) ).all() @staticmethod def get_all_playlists(): - "Returns a list of (id, name) of all playlists" + "Returns a list of all playlists" return session.query(Playlists).all() + @staticmethod + def get_all_closed_playlists(): + "Returns a list of all playlists not currently open" + + return ( + session.query(Playlists) + .filter( + (Playlists.loaded == False) | # noqa E712 + (Playlists.loaded == None) + ) + .order_by(Playlists.last_used.desc()) + ).all() + @staticmethod def get_name(plid): """ @@ -271,6 +284,24 @@ class PlaylistTracks(Base): session.add(plt) session.commit() + @staticmethod + def move_track(from_playlist_id, row, to_playlist_id): + DEBUG( + f"PlaylistTracks.move_tracks({from_playlist_id=}, {row=}, " + f"{to_playlist_id=})" + ) + new_row = ( + session.query(func.max(PlaylistTracks.row)).filter( + PlaylistTracks.playlist_id == to_playlist_id).scalar() + ) + 1 + record = session.query(PlaylistTracks).filter( + PlaylistTracks.playlist_id == from_playlist_id, + PlaylistTracks.row == row + ).one() + record.playlist_id = to_playlist_id + record.row = new_row + session.commit() + @staticmethod def remove_track(playlist_id, row): DEBUG( diff --git a/app/musicmuster.py b/app/musicmuster.py index 3dca2f3..10d0df2 100755 --- a/app/musicmuster.py +++ b/app/musicmuster.py @@ -25,7 +25,7 @@ import helpers import music from config import Config -from model import Playdates, Playlists, Settings, Tracks +from model import Playdates, Playlists, PlaylistTracks, Settings, Tracks from playlists import Playlist from songdb import add_path_to_db from ui.dlg_search_database_ui import Ui_Dialog @@ -214,7 +214,42 @@ class Window(QMainWindow, Ui_MainWindow): playlist_table = Playlist() playlist_table.db = playlist_db playlist_table.populate() - self.tabPlaylist.addTab(playlist_table, playlist_db.name) + idx = self.tabPlaylist.addTab(playlist_table, playlist_db.name) + self.tabPlaylist.setCurrentIndex(idx) + + def move_selected(self): + "Move selected rows to another playlist" + + playlists = list( + set(Playlists.get_all_playlists()) - {self.visible_playlist().db} + ) + dlg = SelectPlaylistDialog(self, playlists=playlists) + dlg.exec() + if not dlg.plid: + return + + # If destination playlist is visible, we need to add the moved + # tracks to it. If not, they will be automatically loaded when + # the playlistis opened. + destination_playlist = None + for tab in range(self.tabPlaylist.count()): + if self.tabPlaylist.widget(tab).db.id == dlg.plid: + destination_playlist = self.tabPlaylist.widget(tab) + break + + rows = [] + for (row, track_id) in ( + self.visible_playlist().get_selected_rows_and_tracks()): + rows.append(row) + # Update database + PlaylistTracks.move_track( + self.visible_playlist().db.id, row, dlg.plid) + # Update destination playlist if visible + if destination_playlist: + destination_playlist.add_track(Tracks.track_from_id(track_id)) + + # Update source playlist + self.visible_playlist().remove_rows(rows) def play_next(self): """ @@ -293,12 +328,13 @@ class Window(QMainWindow, Ui_MainWindow): dlg = DbDialog(self) dlg.exec() - def select_playlist(self): - # TODO don't show those that are currently open - dlg = SelectPlaylistDialog(self) + def open_playlist(self): + playlists = Playlists.get_all_closed_playlists() + dlg = SelectPlaylistDialog(self, playlists=playlists) dlg.exec() - playlist = Playlists.get_playlist_by_id(dlg.plid) - self.load_playlist(playlist) + if dlg.plid: + playlist = Playlists.open(dlg.plid) + self.load_playlist(playlist) def set_next_track(self): "Set selected track as next" @@ -353,7 +389,7 @@ class Window(QMainWindow, Ui_MainWindow): self.update_headers() def tab_change(self): - "User has changed tabs, so refresh next track" + "User has changed tabs" pass @@ -566,13 +602,17 @@ class DbDialog(QDialog): class SelectPlaylistDialog(QDialog): - def __init__(self, parent=None): + def __init__(self, parent=None, playlists=None): super().__init__(parent) + + if playlists is None: + return self.ui = Ui_dlgSelectPlaylist() self.ui.setupUi(self) self.ui.lstPlaylists.itemDoubleClicked.connect(self.list_doubleclick) self.ui.buttonBox.accepted.connect(self.open) self.ui.buttonBox.rejected.connect(self.close) + self.plid = None record = Settings.get_int("select_playlist_dialog_width") width = record.f_int or 800 @@ -580,9 +620,7 @@ class SelectPlaylistDialog(QDialog): height = record.f_int or 600 self.resize(width, height) - for (plid, plname) in [ - (a.id, a.name) for a in Playlists.get_all_playlists() - ]: + for (plid, plname) in [(a.id, a.name) for a in playlists]: p = QListWidgetItem() p.setText(plname) p.setData(Qt.UserRole, plid) diff --git a/app/playlists.py b/app/playlists.py index dcda704..0798b79 100644 --- a/app/playlists.py +++ b/app/playlists.py @@ -266,6 +266,32 @@ class Playlist(QTableWidget): next_row = self._meta_get_next() return self._get_row_id(next_row) + def get_selected_rows_and_tracks(self): + "Return a list of selected (rows, track_id) tuples" + + if not self.selectionModel().hasSelection(): + return None + + result = [] + + for row in [r.row() for r in self.selectionModel().selectedRows()]: + track_id = self._get_row_id(row) + result.append((row, track_id)) + + return result + + def remove_rows(self, rows): + "Remove rows passed in rows list" + + # Row number will change as we delete rows. We could use + # QPersistentModelIndex, but easier just to remove them lowest + # row first + + for row in sorted(rows, reverse=True): + self.removeRow(row) + + self._repaint(save_playlist=False) + def get_selected_title(self): "Return title of selected row or None" @@ -322,6 +348,10 @@ class Playlist(QTableWidget): self._repaint() + def repaint(self): + # Called when we change tabs + self._repaint(save_playlist=False) + def set_selected_as_next(self): """ Sets the selected track as the next track. diff --git a/app/ui/main_window.ui b/app/ui/main_window.ui index 4f51f97..1e8e4ca 100644 --- a/app/ui/main_window.ui +++ b/app/ui/main_window.ui @@ -6,7 +6,7 @@ 0 0 - 1114 + 1164 857 @@ -750,8 +750,8 @@ border: 1px solid rgb(85, 87, 83); 0 0 - 1114 - 18 + 1164 + 29 @@ -773,6 +773,8 @@ border: 1px solid rgb(85, 87, 83); + + @@ -951,6 +953,11 @@ border: 1px solid rgb(85, 87, 83); Dele&te... + + + Mo&ve selected tracks to... + +