diff --git a/app/models.py b/app/models.py index abc8d95..58b9398 100644 --- a/app/models.py +++ b/app/models.py @@ -296,7 +296,6 @@ class Playlists(Base): def __repr__(self) -> str: return f"" - # # def __init__(self, session: Session, name: str) -> None: # self.name = name @@ -315,7 +314,7 @@ class Playlists(Base): # row = self.next_free_row(session, self.id) # # xPlaylistTracks(session, self.id, track_id, row) -# + def close(self, session: Session) -> None: """Mark playlist as unloaded""" @@ -511,40 +510,40 @@ class PlaylistRows(Base): # Ensure new row numbers are available to the caller session.commit() - @staticmethod - def get_played_rows(session: Session, + @classmethod + def get_played_rows(cls, session: Session, playlist_id: int) -> List[int]: """ - For passed playlist, return a list of row numbers that + For passed playlist, return a list of rows that have been played. """ plrs = session.execute( - select(PlaylistRows.row_number) + select(cls) .where( - PlaylistRows.playlist_id == playlist_id, - PlaylistRows.played.is_(True) + cls.playlist_id == playlist_id, + cls.played.is_(True) ) - .order_by(PlaylistRows.row_number) + .order_by(cls.row_number) ).scalars().all() return plrs - @staticmethod - def get_rows_with_tracks(session: Session, - playlist_id: int) -> List[int]: + @classmethod + def get_rows_with_tracks(cls, session: Session, + playlist_id: int) -> List[int]: """ - For passed playlist, return a list of all row numbers that + For passed playlist, return a list of rows that contain tracks """ plrs = session.execute( - select(PlaylistRows.row_number) + select(cls) .where( - PlaylistRows.playlist_id == playlist_id, - PlaylistRows.track_id.is_not(None) + cls.playlist_id == playlist_id, + cls.track_id.is_not(None) ) - .order_by(PlaylistRows.row_number) + .order_by(cls.row_number) ).scalars().all() return plrs diff --git a/app/musicmuster.py b/app/musicmuster.py index 814583d..b90c77a 100755 --- a/app/musicmuster.py +++ b/app/musicmuster.py @@ -172,10 +172,10 @@ class Window(QMainWindow, Ui_MainWindow): self.actionClosePlaylist.triggered.connect(self.close_playlist_tab) self.actionDownload_CSV_of_played_tracks.triggered.connect( self.download_played_tracks) - # ***kae self.actionEnable_controls.triggered.connect( self.enable_play_next_controls) -# self.actionExport_playlist.triggered.connect(self.export_playlist_tab) + self.actionExport_playlist.triggered.connect(self.export_playlist_tab) + # ***kae # self.actionImport.triggered.connect(self.import_track) self.actionFade.triggered.connect(self.fade) # self.actionMoveSelected.triggered.connect(self.move_selected) @@ -364,42 +364,45 @@ class Window(QMainWindow, Ui_MainWindow): # Enable controls self.enable_play_next_controls() -# -# def export_playlist_tab(self) -> None: -# """Export the current playlist to an m3u file""" -# -# if not self.visible_playlist_tab(): -# return -# -# with Session() as session: -# playlist = Playlists.get_by_id( -# session, self.visible_playlist_tab().playlist_id) -# # Get output filename -# pathspec: Tuple[str, str] = QFileDialog.getSaveFileName( -# self, 'Save Playlist', -# directory=f"{playlist.name}.m3u", -# filter="M3U files (*.m3u);;All files (*.*)" -# ) -# if not pathspec: -# return -# -# path: str = pathspec[0] -# if not path.endswith(".m3u"): -# path += ".m3u" -# -# with open(path, "w") as f: -# # Required directive on first line -# f.write("#EXTM3U\n") -# for _, track in playlist.tracks.items(): -# f.write( -# "#EXTINF:" -# f"{int(track.duration / 1000)}," -# f"{track.title} - " -# f"{track.artist}" -# "\n" -# f"{track.path}" -# "\n" -# ) + + def export_playlist_tab(self) -> None: + """Export the current playlist to an m3u file""" + + if not self.visible_playlist_tab(): + return + + playlist_id = self.visible_playlist_tab().playlist_id + + with Session() as session: + + # Get output filename + playlist = session.get(Playlists, playlist_id) + pathspec = QFileDialog.getSaveFileName( + self, 'Save Playlist', + directory=f"{playlist.name}.m3u", + filter="M3U files (*.m3u);;All files (*.*)" + ) + if not pathspec: + return + path = pathspec[0] + if not path.endswith(".m3u"): + path += ".m3u" + + # Get list of track rows for this playlist + plrs = PlaylistRows.get_rows_with_tracks(session, playlist_id) + with open(path, "w") as f: + # Required directive on first line + f.write("#EXTM3U\n") + for track in [a.track for a in plrs]: + f.write( + "#EXTINF:" + f"{int(track.duration / 1000)}," + f"{track.title} - " + f"{track.artist}" + "\n" + f"{track.path}" + "\n" + ) def fade(self) -> None: """Fade currently playing track""" diff --git a/app/playlists.py b/app/playlists.py index bf6c3bd..844124c 100644 --- a/app/playlists.py +++ b/app/playlists.py @@ -922,7 +922,10 @@ class PlaylistTab(QTableWidget): current_row: Optional[int] = self._get_current_track_row() next_row: Optional[int] = self._get_next_track_row() - played = PlaylistRows.get_played_rows(session, self.playlist_id) + played = [ + p.row_number for p in PlaylistRows.get_played_rows( + session, self.playlist_id) + ] unreadable: List[int] = self._get_unreadable_track_rows() if self.row_filter: @@ -1336,9 +1339,14 @@ class PlaylistTab(QTableWidget): if starting_row is None: starting_row = 0 - track_rows = PlaylistRows.get_rows_with_tracks(session, - self.playlist_id) - played_rows = PlaylistRows.get_played_rows(session, self.playlist_id) + track_rows = [ + p.row_number for p in PlaylistRows.get_rows_with_tracks( + session, self.playlist_id) + ] + played_rows = [ + p.row_number for p in PlaylistRows.get_played_rows( + session, self.playlist_id) + ] for row in range(starting_row, self.rowCount()): if row not in track_rows or row in played_rows: continue