Compare commits

...

6 Commits

Author SHA1 Message Date
Keith Edmunds
37ccf7c325 Fix moving tracks between playlists 2021-06-06 15:57:32 +01:00
Keith Edmunds
823d0b6628 Fix error closing playlist 2021-06-06 14:51:46 +01:00
Keith Edmunds
ec760ca0d4 Allow adding more than one file at a time 2021-06-06 14:48:58 +01:00
Keith Edmunds
0ca9bfec0a Segregate adding notes, tracks to onscreen playlist and database 2021-06-06 14:47:14 +01:00
Keith Edmunds
e14bed34bd Improve repr for mode:Playlists 2021-06-06 14:44:15 +01:00
Keith Edmunds
6677577df5 Wire up Tracks, Stop menu. Fixed #6. 2021-06-06 14:43:27 +01:00
5 changed files with 79 additions and 57 deletions

View File

@ -164,7 +164,7 @@ class Playlists(Base):
back_populates="playlists") back_populates="playlists")
def __repr__(self): def __repr__(self):
return (f"<Playlist(id={self.id}, name={self.name}>") return (f"<Playlists(id={self.id}, name={self.name}>")
def add_track(self, session, track, row=None): def add_track(self, session, track, row=None):
""" """
@ -175,6 +175,8 @@ class Playlists(Base):
if not row: if not row:
row = PlaylistTracks.new_row(session, self.id) row = PlaylistTracks.new_row(session, self.id)
DEBUG(f"Playlists:add_track({session=}, {track=}, {row=})")
glue = PlaylistTracks(row=row) glue = PlaylistTracks(row=row)
glue.track_id = track.id glue.track_id = track.id
self.tracks.append(glue) self.tracks.append(glue)
@ -284,10 +286,14 @@ class PlaylistTracks(Base):
f"{from_playlist_id}, row={row}, " f"{from_playlist_id}, row={row}, "
f"to_playlist_id={to_playlist_id})" f"to_playlist_id={to_playlist_id})"
) )
new_row = ( max_row = session.query(func.max(PlaylistTracks.row)).filter(
session.query(func.max(PlaylistTracks.row)).filter(
PlaylistTracks.playlist_id == to_playlist_id).scalar() PlaylistTracks.playlist_id == to_playlist_id).scalar()
) + 1 if max_row is None:
# Destination playlist is empty; use row 0
new_row = 0
else:
# Destination playlist has tracks; add to end
new_row = max_row + 1
record = session.query(PlaylistTracks).filter( record = session.query(PlaylistTracks).filter(
PlaylistTracks.playlist_id == from_playlist_id, PlaylistTracks.playlist_id == from_playlist_id,
PlaylistTracks.row == row PlaylistTracks.row == row

View File

@ -28,7 +28,7 @@ from config import Config
from model import (Notes, Playdates, Playlists, PlaylistTracks, from model import (Notes, Playdates, Playlists, PlaylistTracks,
Session, Settings, Tracks) Session, Settings, Tracks)
from playlists import Playlist from playlists import Playlist
from songdb import add_path_to_db from songdb import create_track_from_file
from ui.dlg_search_database_ui import Ui_Dialog from ui.dlg_search_database_ui import Ui_Dialog
from ui.dlg_SelectPlaylist_ui import Ui_dlgSelectPlaylist from ui.dlg_SelectPlaylist_ui import Ui_dlgSelectPlaylist
from ui.main_window_ui import Ui_MainWindow from ui.main_window_ui import Ui_MainWindow
@ -65,7 +65,7 @@ class Window(QMainWindow, Ui_MainWindow):
def add_file(self): def add_file(self):
dlg = QFileDialog() dlg = QFileDialog()
dlg.setFileMode(QFileDialog.ExistingFile) dlg.setFileMode(QFileDialog.ExistingFiles)
dlg.setViewMode(QFileDialog.Detail) dlg.setViewMode(QFileDialog.Detail)
dlg.setDirectory(Config.ROOT) dlg.setDirectory(Config.ROOT)
dlg.setNameFilter("Music files (*.flac *.mp3)") dlg.setNameFilter("Music files (*.flac *.mp3)")
@ -73,10 +73,14 @@ class Window(QMainWindow, Ui_MainWindow):
if dlg.exec_(): if dlg.exec_():
with Session() as session: with Session() as session:
for fname in dlg.selectedFiles(): for fname in dlg.selectedFiles():
track = add_path_to_db(session, fname) track = create_track_from_file(session, fname)
self.visible_playlist().add_to_playlist(session, track) # Add to playlist on screen
# If we don't specify "repaint=False", playlist will
# also be saved to database
self.visible_playlist().insert_track(session, track)
def set_main_window_size(self): def set_main_window_size(self):
"Set size of window from database"
with Session() as session: with Session() as session:
record = Settings.get_int(session, "mainwindow_x") record = Settings.get_int(session, "mainwindow_x")
@ -140,6 +144,7 @@ class Window(QMainWindow, Ui_MainWindow):
self.actionSkip_next.triggered.connect(self.play_next) self.actionSkip_next.triggered.connect(self.play_next)
self.actionSkipToEnd.triggered.connect(self.test_skip_to_end) self.actionSkipToEnd.triggered.connect(self.test_skip_to_end)
self.actionSkipToFade.triggered.connect(self.test_skip_to_fade) self.actionSkipToFade.triggered.connect(self.test_skip_to_fade)
self.actionStop.triggered.connect(self.stop)
self.actionTestFunction.triggered.connect(self.test_function) self.actionTestFunction.triggered.connect(self.test_function)
self.btnAddFile.clicked.connect(self.add_file) self.btnAddFile.clicked.connect(self.add_file)
self.btnAddNote.clicked.connect(self.insert_note) self.btnAddNote.clicked.connect(self.insert_note)
@ -176,7 +181,9 @@ class Window(QMainWindow, Ui_MainWindow):
def close_playlist(self): def close_playlist(self):
with Session() as session: with Session() as session:
self.visible_playlist().db.close(session) playlist_db = session.query(Playlists).filter(
Playlists.id == self.visible_playlist().id).one()
playlist_db.close(session)
index = self.tabPlaylist.currentIndex() index = self.tabPlaylist.currentIndex()
self.tabPlaylist.removeTab(index) self.tabPlaylist.removeTab(index)
@ -196,7 +203,7 @@ class Window(QMainWindow, Ui_MainWindow):
DEBUG(f"musicmuster.create_note(text={text}): row={row}") DEBUG(f"musicmuster.create_note(text={text}): row={row}")
note = Notes.add_note( note = Notes.add_note(
session, self.visible_playlist().db.id, row, text) session, self.visible_playlist().id, row, text)
return note return note
@ -266,7 +273,7 @@ class Window(QMainWindow, Ui_MainWindow):
if ok: if ok:
with Session() as session: with Session() as session:
note = self.create_note(session, dlg.textValue()) note = self.create_note(session, dlg.textValue())
self.visible_playlist().add_note(session, note) self.visible_playlist().insert_note(session, note)
def load_last_playlists(self): def load_last_playlists(self):
"Load the playlists that we loaded at end of last session" "Load the playlists that we loaded at end of last session"
@ -293,10 +300,8 @@ class Window(QMainWindow, Ui_MainWindow):
"Move selected rows to another playlist" "Move selected rows to another playlist"
with Session() as session: with Session() as session:
playlists = list( playlists = [p for p in Playlists.get_all_playlists(session)
set(Playlists.get_all_playlists(session)) - if p.id != self.visible_playlist().id]
{self.visible_playlist().db}
)
dlg = SelectPlaylistDialog(self, playlists=playlists) dlg = SelectPlaylistDialog(self, playlists=playlists)
dlg.exec() dlg.exec()
if not dlg.plid: if not dlg.plid:
@ -305,23 +310,30 @@ class Window(QMainWindow, Ui_MainWindow):
# If destination playlist is visible, we need to add the moved # If destination playlist is visible, we need to add the moved
# tracks to it. If not, they will be automatically loaded when # tracks to it. If not, they will be automatically loaded when
# the playlistis opened. # the playlistis opened.
destination_playlist = None destination_visible_playlist_tab = None
for tab in range(self.tabPlaylist.count()): for tab in range(self.tabPlaylist.count()):
if self.tabPlaylist.widget(tab).db.id == dlg.plid: if self.tabPlaylist.widget(tab).id == dlg.plid:
destination_playlist = self.tabPlaylist.widget(tab) destination_visible_playlist_tab = (
self.tabPlaylist.widget(tab))
break break
rows = [] rows = []
for (row, track_id) in ( for (row, track_id) in (
self.visible_playlist().get_selected_rows_and_tracks()): self.visible_playlist().get_selected_rows_and_tracks()):
rows.append(row) rows.append(row)
track = Tracks.track_from_id(session, track_id)
if destination_visible_playlist_tab:
# Insert with repaint=False to not update database
destination_visible_playlist_tab.insert_track(
session, track, repaint=False)
# Update database # Update database
PlaylistTracks.move_track( PlaylistTracks.move_track(
session, self.visible_playlist().db.id, row, dlg.plid) session, self.visible_playlist().id, row, dlg.plid)
# Update destination playlist if visible # Update destination playlist if visible
if destination_playlist: if destination_visible_playlist_tab:
destination_playlist.add_track( destination_visible_playlist_tab.repaint()
session, Tracks.track_from_id(session, track_id))
# Update source playlist # Update source playlist
self.visible_playlist().remove_rows(rows) self.visible_playlist().remove_rows(rows)
@ -687,7 +699,11 @@ class DbDialog(QDialog):
def add_track(self, track_id): def add_track(self, track_id):
track = Tracks.track_from_id(self.session, track_id) track = Tracks.track_from_id(self.session, track_id)
self.parent().visible_playlist().add_to_playlist(self.session, track) # Add to playlist on screen
# If we don't specify "repaint=False", playlist will
# also be saved to database
self.parent().visible_playlist().insert_track(
self.session, track)
# Select search text to make it easier for next search # Select search text to make it easier for next search
self.select_searchtext() self.select_searchtext()

View File

@ -167,19 +167,21 @@ class Playlist(QTableWidget):
# ########## Externally called functions ########## # ########## Externally called functions ##########
def add_note(self, session, note, repaint=True): def insert_note(self, session, note, repaint=True):
""" """
Add note to playlist Add note to playlist
If a row is selected, add note above. Otherwise, add to end of If a row is selected, add note above. Otherwise, add to end of
playlist. playlist.
Return the row number that track is now in.
""" """
if self.selectionModel().hasSelection(): if self.selectionModel().hasSelection():
row = self.currentRow() row = self.currentRow()
else: else:
row = self.rowCount() row = self.rowCount()
DEBUG(f"playlist.add_note(): row={row}") DEBUG(f"playlist.inset_note(): row={row}")
# Does note end with a time? # Does note end with a time?
start_time = None start_time = None
@ -216,38 +218,26 @@ class Playlist(QTableWidget):
self._save_playlist(session) self._save_playlist(session)
self._repaint(clear_selection=False) self._repaint(clear_selection=False)
def add_to_playlist(self, session, data, repaint=True): return row
"""
Add data to playlist. Data may be either a Tracks object or a
Notes object.
"""
DEBUG(f"playlists.add_to_playlist(session={session}, data={data})") def insert_track(self, session, track, repaint=True):
if isinstance(data, Tracks):
self.add_track(session, data, repaint=repaint)
elif isinstance(data, Notes):
self.add_note(session, data, repaint=repaint)
def add_track(self, session, track, repaint=True):
""" """
Add track to playlist Insert track into on-screen playlist.
If a row is selected, add track above. Otherwise, add to end of If a row is selected, add track above. Otherwise, add to end of
playlist. playlist.
Return the row number that track is now in.
""" """
if self.selectionModel().hasSelection(): if self.selectionModel().hasSelection():
row = self.currentRow() row = self.currentRow()
else: else:
row = self.rowCount() row = self.rowCount()
DEBUG(f"playlists.add_track(track={track}), row={row}") DEBUG(
f"playlists.insert_track({session=}, {track=}, {repaint=}), "
# We need to add ourself to the session f"{row=}"
playlist_db = session.query(Playlists).filter( )
Playlists.id == self.id).one()
playlist_db.add_track(session, track, row)
self.insertRow(row) self.insertRow(row)
@ -275,6 +265,8 @@ class Playlist(QTableWidget):
self._save_playlist(session) self._save_playlist(session)
self._repaint(clear_selection=False) self._repaint(clear_selection=False)
return row
def clear_current(self): def clear_current(self):
"Clear current track" "Clear current track"
@ -384,8 +376,12 @@ class Playlist(QTableWidget):
self.setRowCount(0) self.setRowCount(0)
# Now add data in row order # Now add data in row order
for item in sorted(data, key=lambda x: x[0]): for i in sorted(data, key=lambda x: x[0]):
self.add_to_playlist(session, item[1], repaint=False) item = i[1]
if isinstance(item, Tracks):
self.insert_track(session, item, repaint=False)
elif isinstance(item, Notes):
self.insert_note(session, item, repaint=False)
# Scroll to top # Scroll to top
scroll_to = self.item(0, self.COL_INDEX) scroll_to = self.item(0, self.COL_INDEX)

View File

@ -34,8 +34,12 @@ def main():
INFO("Finished") INFO("Finished")
def add_path_to_db(session, path): def create_track_from_file(session, path):
"Add passed path to database along with metadata" """
Create track in database from passed path.
Return track.
"""
track = Tracks.get_or_create(session, path) track = Tracks.get_or_create(session, path)
tag = TinyTag.get(path) tag = TinyTag.get(path)
@ -61,7 +65,7 @@ def add_path_to_db(session, path):
fd, temp_path = tempfile.mkstemp() fd, temp_path = tempfile.mkstemp()
shutil.copyfile(path, temp_path) shutil.copyfile(path, temp_path)
except Exception as err: except Exception as err:
DEBUG(f"songdb.add_path_to_db({path}): err1: {str(err)}") DEBUG(f"songdb.create_track_from_file({path}): err1: {str(err)}")
return return
# Overwrite original file with normalised output # Overwrite original file with normalised output
@ -78,7 +82,7 @@ def add_path_to_db(session, path):
dst[tag] = src[tag] dst[tag] = src[tag]
dst.save() dst.save()
except Exception as err: except Exception as err:
DEBUG(f"songdb.add_path_to_db({path}): err2: {str(err)}") DEBUG(f"songdb.create_track_from_file({path}): err2: {str(err)}")
# Restore original file # Restore original file
shutil.copyfile(path, temp_path) shutil.copyfile(path, temp_path)
finally: finally:
@ -190,7 +194,7 @@ def update_db(session):
for path in list(os_paths - db_paths): for path in list(os_paths - db_paths):
# TODO # TODO
INFO(f"Adding to dataabase: {path}") INFO(f"Adding to dataabase: {path}")
add_path_to_db(session, path) create_track_from_file(session, path)
if __name__ == '__main__' and '__file__' in globals(): if __name__ == '__main__' and '__file__' in globals():

View File

@ -734,7 +734,7 @@ border: 1px solid rgb(85, 87, 83);</string>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1164</width> <width>1164</width>
<height>26</height> <height>29</height>
</rect> </rect>
</property> </property>
<widget class="QMenu" name="menuFile"> <widget class="QMenu" name="menuFile">
@ -771,7 +771,7 @@ border: 1px solid rgb(85, 87, 83);</string>
<addaction name="actionPlay_next"/> <addaction name="actionPlay_next"/>
<addaction name="actionSkip_next"/> <addaction name="actionSkip_next"/>
<addaction name="actionFade"/> <addaction name="actionFade"/>
<addaction name="actionS_top"/> <addaction name="actionStop"/>
<addaction name="action_Resume_previous"/> <addaction name="action_Resume_previous"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionSetNext"/> <addaction name="actionSetNext"/>
@ -858,7 +858,7 @@ border: 1px solid rgb(85, 87, 83);</string>
<string>Ctrl+S</string> <string>Ctrl+S</string>
</property> </property>
</action> </action>
<action name="actionS_top"> <action name="actionStop">
<property name="icon"> <property name="icon">
<iconset resource="icons.qrc"> <iconset resource="icons.qrc">
<normaloff>:/icons/stop</normaloff>:/icons/stop</iconset> <normaloff>:/icons/stop</normaloff>:/icons/stop</iconset>