Compare commits

...

4 Commits

Author SHA1 Message Date
Keith Edmunds
436f6b4fa9 Export playlist working 2022-08-13 13:32:25 +01:00
Keith Edmunds
9485b244f5 Export played tracks csv works 2022-08-13 12:57:37 +01:00
Keith Edmunds
63acc025f9 Close tab works 2022-08-13 12:27:38 +01:00
Keith Edmunds
066b20a571 Close playlist from menubar 2022-08-13 12:03:35 +01:00
3 changed files with 172 additions and 161 deletions

View File

@ -249,13 +249,20 @@ class Playdates(Base):
return last_played[0] return last_played[0]
else: else:
return None return None
#
# @staticmethod @staticmethod
# def played_after(session: Session, since: datetime) -> List["Playdates"]: def played_after(session: Session, since: datetime) -> List["Playdates"]:
# """Return a list of Playdates objects since passed time""" """Return a list of Playdates objects since passed time"""
#
# return session.query(Playdates).filter( return (
# Playdates.lastplayed >= since).all() session.execute(
select(Playdates)
.where(Playdates.lastplayed >= since)
.order_by(Playdates.lastplayed)
)
.scalars()
.all()
)
# #
# @staticmethod # @staticmethod
# def remove_track(session: Session, track_id: int) -> None: # def remove_track(session: Session, track_id: int) -> None:
@ -289,7 +296,6 @@ class Playlists(Base):
def __repr__(self) -> str: def __repr__(self) -> str:
return f"<Playlists(id={self.id}, name={self.name}>" return f"<Playlists(id={self.id}, name={self.name}>"
# #
# def __init__(self, session: Session, name: str) -> None: # def __init__(self, session: Session, name: str) -> None:
# self.name = name # self.name = name
@ -308,13 +314,11 @@ class Playlists(Base):
# row = self.next_free_row(session, self.id) # row = self.next_free_row(session, self.id)
# #
# xPlaylistTracks(session, self.id, track_id, row) # xPlaylistTracks(session, self.id, track_id, row)
#
# def close(self, session: Session) -> None: def close(self, session: Session) -> None:
# """Record playlist as no longer loaded""" """Mark playlist as unloaded"""
#
# self.loaded = False self.loaded = False
# session.add(self)
# session.flush()
@classmethod @classmethod
def get_all(cls, session: Session) -> List["Playlists"]: def get_all(cls, session: Session) -> List["Playlists"]:
@ -506,40 +510,40 @@ class PlaylistRows(Base):
# Ensure new row numbers are available to the caller # Ensure new row numbers are available to the caller
session.commit() session.commit()
@staticmethod @classmethod
def get_played_rows(session: Session, def get_played_rows(cls, session: Session,
playlist_id: int) -> List[int]: 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. have been played.
""" """
plrs = session.execute( plrs = session.execute(
select(PlaylistRows.row_number) select(cls)
.where( .where(
PlaylistRows.playlist_id == playlist_id, cls.playlist_id == playlist_id,
PlaylistRows.played.is_(True) cls.played.is_(True)
) )
.order_by(PlaylistRows.row_number) .order_by(cls.row_number)
).scalars().all() ).scalars().all()
return plrs return plrs
@staticmethod @classmethod
def get_rows_with_tracks(session: Session, def get_rows_with_tracks(cls, session: Session,
playlist_id: int) -> List[int]: 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 contain tracks
""" """
plrs = session.execute( plrs = session.execute(
select(PlaylistRows.row_number) select(cls)
.where( .where(
PlaylistRows.playlist_id == playlist_id, cls.playlist_id == playlist_id,
PlaylistRows.track_id.is_not(None) cls.track_id.is_not(None)
) )
.order_by(PlaylistRows.row_number) .order_by(cls.row_number)
).scalars().all() ).scalars().all()
return plrs return plrs

View File

@ -7,14 +7,13 @@ 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 PyQt5.QtCore import QDate, QProcess, Qt, QTime, QTimer, QUrl from PyQt5.QtCore import QDate, QEvent, Qt, QTime, QTimer
from PyQt5.QtCore import QEvent, Qt, QTimer
from PyQt5.QtGui import QColor from PyQt5.QtGui import QColor
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QApplication, QApplication,
QDialog, QDialog,
# QFileDialog, QFileDialog,
# QInputDialog, # QInputDialog,
QLabel, QLabel,
# QLineEdit, # QLineEdit,
@ -39,7 +38,7 @@ from playlists import PlaylistTab
from sqlalchemy.orm.exc import DetachedInstanceError from sqlalchemy.orm.exc import DetachedInstanceError
# 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 # type: ignore from ui.dlg_SelectPlaylist_ui import Ui_dlgSelectPlaylist # type: ignore
# from ui.downloadcsv_ui import Ui_DateSelect from ui.downloadcsv_ui import Ui_DateSelect # type: ignore
from config import Config from config import Config
from ui.main_window_ui import Ui_MainWindow # type: ignore from ui.main_window_ui import Ui_MainWindow # type: ignore
# from utilities import create_track_from_file, update_db # from utilities import create_track_from_file, update_db
@ -170,12 +169,13 @@ class Window(QMainWindow, Ui_MainWindow):
def connect_signals_slots(self) -> None: def connect_signals_slots(self) -> None:
# self.actionAdd_note.triggered.connect(self.create_note) # self.actionAdd_note.triggered.connect(self.create_note)
self.action_Clear_selection.triggered.connect(self.clear_selection) self.action_Clear_selection.triggered.connect(self.clear_selection)
# self.actionClosePlaylist.triggered.connect(self.close_playlist_tab) self.actionClosePlaylist.triggered.connect(self.close_playlist_tab)
# self.actionDownload_CSV_of_played_tracks.triggered.connect( self.actionDownload_CSV_of_played_tracks.triggered.connect(
# self.download_played_tracks) self.download_played_tracks)
self.actionEnable_controls.triggered.connect( self.actionEnable_controls.triggered.connect(
self.enable_play_next_controls) 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.actionImport.triggered.connect(self.import_track)
self.actionFade.triggered.connect(self.fade) self.actionFade.triggered.connect(self.fade)
# self.actionMoveSelected.triggered.connect(self.move_selected) # self.actionMoveSelected.triggered.connect(self.move_selected)
@ -198,7 +198,7 @@ class Window(QMainWindow, Ui_MainWindow):
# self.btnHidePlayed.clicked.connect(self.hide_played) # self.btnHidePlayed.clicked.connect(self.hide_played)
self.btnFade.clicked.connect(self.fade) self.btnFade.clicked.connect(self.fade)
self.btnStop.clicked.connect(self.stop) self.btnStop.clicked.connect(self.stop)
# self.tabPlaylist.tabCloseRequested.connect(self.close_tab) self.tabPlaylist.tabCloseRequested.connect(self.close_tab)
# self.txtSearch.returnPressed.connect(self.search_playlist_return) # self.txtSearch.returnPressed.connect(self.search_playlist_return)
# self.txtSearch.textChanged.connect(self.search_playlist_update) # self.txtSearch.textChanged.connect(self.search_playlist_update)
# #
@ -216,32 +216,36 @@ class Window(QMainWindow, Ui_MainWindow):
# with Session() as session: # with Session() as session:
# playlist = Playlists(session, dlg.textValue()) # playlist = Playlists(session, dlg.textValue())
# self.create_playlist_tab(session, playlist) # self.create_playlist_tab(session, playlist)
#
# def close_playlist_tab(self) -> None: def close_playlist_tab(self) -> None:
# """Close active playlist tab""" """
# Close active playlist tab, called by menu item
# self.close_tab(self.tabPlaylist.currentIndex()) """
#
# def close_tab(self, index: int) -> None: self.close_tab(self.tabPlaylist.currentIndex())
# """
# Close tab unless it holds the curren or next track def close_tab(self, tab_index: int) -> None:
# """ """
# Close active playlist tab unless it holds the curren or next track.
# if hasattr(self.tabPlaylist.widget(index), 'playlist_id'): Called from close_playlist_tab() or by clicking close button on tab.
# if self.tabPlaylist.widget(index) == ( """
# self.current_track_playlist_tab):
# self.statusbar.showMessage( # Don't close current track playlist
# "Can't close current track playlist", 5000) if self.tabPlaylist.widget(tab_index) == (
# return self.current_track_playlist_tab):
# if self.tabPlaylist.widget(index) == self.next_track_playlist_tab: self.statusbar.showMessage(
# self.statusbar.showMessage( "Can't close current track playlist", 5000)
# "Can't close next track playlist", 5000) return
# return
# # It's OK to close this playlist so remove from open playlist list # Don't close next track playlist
# self.tabPlaylist.widget(index).close() if self.tabPlaylist.widget(tab_index) == self.next_track_playlist_tab:
# self.statusbar.showMessage(
# # Close regardless of tab type "Can't close next track playlist", 5000)
# self.tabPlaylist.removeTab(index) return
# Close playlist and remove tab
self.tabPlaylist.widget(tab_index).close()
self.tabPlaylist.removeTab(tab_index)
# #
# def create_note(self) -> None: # def create_note(self) -> None:
# """Call playlist to create note""" # """Call playlist to create note"""
@ -271,32 +275,32 @@ class Window(QMainWindow, Ui_MainWindow):
self.actionPlay_next.setEnabled(False) self.actionPlay_next.setEnabled(False)
self.statusbar.showMessage("Play controls: Disabled", 0) self.statusbar.showMessage("Play controls: Disabled", 0)
#
# def download_played_tracks(self) -> None: def download_played_tracks(self) -> None:
# """Download a CSV of played tracks""" """Download a CSV of played tracks"""
#
# dlg = DownloadCSV(self) dlg = DownloadCSV(self)
# if dlg.exec(): if dlg.exec():
# start_dt = dlg.ui.dateTimeEdit.dateTime().toPyDateTime() start_dt = dlg.ui.dateTimeEdit.dateTime().toPyDateTime()
# # Get output filename # Get output filename
# pathspec: Tuple[str, str] = QFileDialog.getSaveFileName( pathspec = QFileDialog.getSaveFileName(
# self, 'Save CSV of tracks played', self, 'Save CSV of tracks played',
# directory="/tmp/playlist.csv", directory="/tmp/playlist.csv",
# filter="CSV files (*.csv)" filter="CSV files (*.csv)"
# ) )
# if not pathspec: if not pathspec:
# return return
#
# path: str = pathspec[0] path = pathspec[0]
# if not path.endswith(".csv"): if not path.endswith(".csv"):
# path += ".csv" path += ".csv"
#
# with open(path, "w") as f: with open(path, "w") as f:
# with Session() as session: with Session() as session:
# for playdate in Playdates.played_after(session, start_dt): for playdate in Playdates.played_after(session, start_dt):
# f.write( f.write(
# f"{playdate.track.artist},{playdate.track.title}\n" f"{playdate.track.artist},{playdate.track.title}\n"
# ) )
# #
# def drop3db(self) -> None: # def drop3db(self) -> None:
# """Drop music level by 3db if button checked""" # """Drop music level by 3db if button checked"""
@ -360,42 +364,45 @@ class Window(QMainWindow, Ui_MainWindow):
# Enable controls # Enable controls
self.enable_play_next_controls() self.enable_play_next_controls()
#
# def export_playlist_tab(self) -> None: def export_playlist_tab(self) -> None:
# """Export the current playlist to an m3u file""" """Export the current playlist to an m3u file"""
#
# if not self.visible_playlist_tab(): if not self.visible_playlist_tab():
# return return
#
# with Session() as session: playlist_id = self.visible_playlist_tab().playlist_id
# playlist = Playlists.get_by_id(
# session, self.visible_playlist_tab().playlist_id) with Session() as session:
# # Get output filename
# pathspec: Tuple[str, str] = QFileDialog.getSaveFileName( # Get output filename
# self, 'Save Playlist', playlist = session.get(Playlists, playlist_id)
# directory=f"{playlist.name}.m3u", pathspec = QFileDialog.getSaveFileName(
# filter="M3U files (*.m3u);;All files (*.*)" self, 'Save Playlist',
# ) directory=f"{playlist.name}.m3u",
# if not pathspec: filter="M3U files (*.m3u);;All files (*.*)"
# return )
# if not pathspec:
# path: str = pathspec[0] return
# if not path.endswith(".m3u"): path = pathspec[0]
# path += ".m3u" if not path.endswith(".m3u"):
# path += ".m3u"
# with open(path, "w") as f:
# # Required directive on first line # Get list of track rows for this playlist
# f.write("#EXTM3U\n") plrs = PlaylistRows.get_rows_with_tracks(session, playlist_id)
# for _, track in playlist.tracks.items(): with open(path, "w") as f:
# f.write( # Required directive on first line
# "#EXTINF:" f.write("#EXTM3U\n")
# f"{int(track.duration / 1000)}," for track in [a.track for a in plrs]:
# f"{track.title} - " f.write(
# f"{track.artist}" "#EXTINF:"
# "\n" f"{int(track.duration / 1000)},"
# f"{track.path}" f"{track.title} - "
# "\n" f"{track.artist}"
# ) "\n"
f"{track.path}"
"\n"
)
def fade(self) -> None: def fade(self) -> None:
"""Fade currently playing track""" """Fade currently playing track"""
@ -970,18 +977,18 @@ class Window(QMainWindow, Ui_MainWindow):
# item = self.ui.matchList.currentItem() # item = self.ui.matchList.currentItem()
# track = item.data(Qt.UserRole) # track = item.data(Qt.UserRole)
# self.ui.dbPath.setText(track.path) # self.ui.dbPath.setText(track.path)
#
#
# class DownloadCSV(QDialog): class DownloadCSV(QDialog):
# def __init__(self, parent=None): def __init__(self, parent=None):
# super().__init__(parent) super().__init__(parent)
#
# self.ui = Ui_DateSelect() self.ui = Ui_DateSelect()
# self.ui.setupUi(self) self.ui.setupUi(self)
# self.ui.dateTimeEdit.setDate(QDate.currentDate()) self.ui.dateTimeEdit.setDate(QDate.currentDate())
# self.ui.dateTimeEdit.setTime(QTime(19, 59, 0)) self.ui.dateTimeEdit.setTime(QTime(19, 59, 0))
# self.ui.buttonBox.accepted.connect(self.accept) self.ui.buttonBox.accepted.connect(self.accept)
# self.ui.buttonBox.rejected.connect(self.reject) self.ui.buttonBox.rejected.connect(self.reject)
class SelectPlaylistDialog(QDialog): class SelectPlaylistDialog(QDialog):

View File

@ -349,23 +349,15 @@ class PlaylistTab(QTableWidget):
return [self._get_playlistrow_id(a) for a in self._selected_rows()] return [self._get_playlistrow_id(a) for a in self._selected_rows()]
# def closeEvent(self, event) -> None: def closeEvent(self, event) -> None:
# """Save column widths""" """Handle closing playist tab"""
#
# log.debug(f"playlists.closeEvent()") with Session() as session:
# with Session() as session: # Record playlist as closed
# for column in range(self.columnCount()): playlist = session.get(Playlists, self.playlist_id)
# width = self.columnWidth(column) playlist.close(session)
# name = f"playlist_col_{str(column)}_width"
# record = Settings.get_int_settings(session, name) event.accept()
# if record.f_int != self.columnWidth(column):
# record.update(session, {'f_int': width})
#
# # Record playlist as closed
# playlist = Playlists.get_by_id(session, self.playlist_id)
# playlist.close(session)
#
# event.accept()
def clear_next(self, session) -> None: def clear_next(self, session) -> None:
"""Clear next track marker""" """Clear next track marker"""
@ -930,7 +922,10 @@ class PlaylistTab(QTableWidget):
current_row: Optional[int] = self._get_current_track_row() current_row: Optional[int] = self._get_current_track_row()
next_row: Optional[int] = self._get_next_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() unreadable: List[int] = self._get_unreadable_track_rows()
if self.row_filter: if self.row_filter:
@ -1344,9 +1339,14 @@ class PlaylistTab(QTableWidget):
if starting_row is None: if starting_row is None:
starting_row = 0 starting_row = 0
track_rows = PlaylistRows.get_rows_with_tracks(session, track_rows = [
self.playlist_id) p.row_number for p in PlaylistRows.get_rows_with_tracks(
played_rows = PlaylistRows.get_played_rows(session, self.playlist_id) 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()): for row in range(starting_row, self.rowCount()):
if row not in track_rows or row in played_rows: if row not in track_rows or row in played_rows:
continue continue