Move tracks between playlists

This commit is contained in:
Keith Edmunds 2021-05-16 22:56:11 +01:00
parent 997627582f
commit 0465fb45c4
4 changed files with 123 additions and 17 deletions

View File

@ -200,16 +200,29 @@ class Playlists(Base):
return ( return (
session.query(Playlists) session.query(Playlists)
.filter(Playlists.loaded == True) .filter(Playlists.loaded == True) # noqa E712
.order_by(Playlists.last_used.desc()) .order_by(Playlists.last_used.desc())
).all() ).all()
@staticmethod @staticmethod
def get_all_playlists(): def get_all_playlists():
"Returns a list of (id, name) of all playlists" "Returns a list of all playlists"
return session.query(Playlists).all() 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 @staticmethod
def get_name(plid): def get_name(plid):
""" """
@ -271,6 +284,24 @@ class PlaylistTracks(Base):
session.add(plt) session.add(plt)
session.commit() 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 @staticmethod
def remove_track(playlist_id, row): def remove_track(playlist_id, row):
DEBUG( DEBUG(

View File

@ -25,7 +25,7 @@ import helpers
import music import music
from config import Config from config import Config
from model import Playdates, Playlists, Settings, Tracks from model import Playdates, Playlists, PlaylistTracks, Settings, Tracks
from playlists import Playlist from playlists import Playlist
from songdb import add_path_to_db from songdb import add_path_to_db
from ui.dlg_search_database_ui import Ui_Dialog from ui.dlg_search_database_ui import Ui_Dialog
@ -214,7 +214,42 @@ class Window(QMainWindow, Ui_MainWindow):
playlist_table = Playlist() playlist_table = Playlist()
playlist_table.db = playlist_db playlist_table.db = playlist_db
playlist_table.populate() 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): def play_next(self):
""" """
@ -293,12 +328,13 @@ class Window(QMainWindow, Ui_MainWindow):
dlg = DbDialog(self) dlg = DbDialog(self)
dlg.exec() dlg.exec()
def select_playlist(self): def open_playlist(self):
# TODO don't show those that are currently open playlists = Playlists.get_all_closed_playlists()
dlg = SelectPlaylistDialog(self) dlg = SelectPlaylistDialog(self, playlists=playlists)
dlg.exec() dlg.exec()
playlist = Playlists.get_playlist_by_id(dlg.plid) if dlg.plid:
self.load_playlist(playlist) playlist = Playlists.open(dlg.plid)
self.load_playlist(playlist)
def set_next_track(self): def set_next_track(self):
"Set selected track as next" "Set selected track as next"
@ -353,7 +389,7 @@ class Window(QMainWindow, Ui_MainWindow):
self.update_headers() self.update_headers()
def tab_change(self): def tab_change(self):
"User has changed tabs, so refresh next track" "User has changed tabs"
pass pass
@ -566,13 +602,17 @@ class DbDialog(QDialog):
class SelectPlaylistDialog(QDialog): class SelectPlaylistDialog(QDialog):
def __init__(self, parent=None): def __init__(self, parent=None, playlists=None):
super().__init__(parent) super().__init__(parent)
if playlists is None:
return
self.ui = Ui_dlgSelectPlaylist() self.ui = Ui_dlgSelectPlaylist()
self.ui.setupUi(self) self.ui.setupUi(self)
self.ui.lstPlaylists.itemDoubleClicked.connect(self.list_doubleclick) self.ui.lstPlaylists.itemDoubleClicked.connect(self.list_doubleclick)
self.ui.buttonBox.accepted.connect(self.open) self.ui.buttonBox.accepted.connect(self.open)
self.ui.buttonBox.rejected.connect(self.close) self.ui.buttonBox.rejected.connect(self.close)
self.plid = None
record = Settings.get_int("select_playlist_dialog_width") record = Settings.get_int("select_playlist_dialog_width")
width = record.f_int or 800 width = record.f_int or 800
@ -580,9 +620,7 @@ class SelectPlaylistDialog(QDialog):
height = record.f_int or 600 height = record.f_int or 600
self.resize(width, height) self.resize(width, height)
for (plid, plname) in [ for (plid, plname) in [(a.id, a.name) for a in playlists]:
(a.id, a.name) for a in Playlists.get_all_playlists()
]:
p = QListWidgetItem() p = QListWidgetItem()
p.setText(plname) p.setText(plname)
p.setData(Qt.UserRole, plid) p.setData(Qt.UserRole, plid)

View File

@ -266,6 +266,32 @@ class Playlist(QTableWidget):
next_row = self._meta_get_next() next_row = self._meta_get_next()
return self._get_row_id(next_row) 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): def get_selected_title(self):
"Return title of selected row or None" "Return title of selected row or None"
@ -322,6 +348,10 @@ class Playlist(QTableWidget):
self._repaint() self._repaint()
def repaint(self):
# Called when we change tabs
self._repaint(save_playlist=False)
def set_selected_as_next(self): def set_selected_as_next(self):
""" """
Sets the selected track as the next track. Sets the selected track as the next track.

View File

@ -6,7 +6,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1114</width> <width>1164</width>
<height>857</height> <height>857</height>
</rect> </rect>
</property> </property>
@ -750,8 +750,8 @@ border: 1px solid rgb(85, 87, 83);</string>
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1114</width> <width>1164</width>
<height>18</height> <height>29</height>
</rect> </rect>
</property> </property>
<widget class="QMenu" name="menuFile"> <widget class="QMenu" name="menuFile">
@ -773,6 +773,8 @@ border: 1px solid rgb(85, 87, 83);</string>
<addaction name="actionSearch_database"/> <addaction name="actionSearch_database"/>
<addaction name="actionAdd_file"/> <addaction name="actionAdd_file"/>
<addaction name="action_Clear_selection"/> <addaction name="action_Clear_selection"/>
<addaction name="separator"/>
<addaction name="actionMoveSelected"/>
</widget> </widget>
<widget class="QMenu" name="menu_Tracks"> <widget class="QMenu" name="menu_Tracks">
<property name="title"> <property name="title">
@ -951,6 +953,11 @@ border: 1px solid rgb(85, 87, 83);</string>
<string>Dele&amp;te...</string> <string>Dele&amp;te...</string>
</property> </property>
</action> </action>
<action name="actionMoveSelected">
<property name="text">
<string>Mo&amp;ve selected tracks to...</string>
</property>
</action>
</widget> </widget>
<resources> <resources>
<include location="icons.qrc"/> <include location="icons.qrc"/>