Compare commits

..

No commits in common. "436f6b4fa948da572742e4434d97e7b7c1bcb9e5" and "f1796451ae97c6406ef0b1fbb2a32a6e4710fae4" have entirely different histories.

3 changed files with 161 additions and 172 deletions

View File

@ -249,20 +249,13 @@ 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 ( # return session.query(Playdates).filter(
session.execute( # Playdates.lastplayed >= since).all()
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:
@ -296,6 +289,7 @@ 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
@ -314,11 +308,13 @@ 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:
"""Mark playlist as unloaded""" # """Record playlist as no longer loaded"""
#
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"]:
@ -510,40 +506,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()
@classmethod @staticmethod
def get_played_rows(cls, session: Session, def get_played_rows(session: Session,
playlist_id: int) -> List[int]: playlist_id: int) -> List[int]:
""" """
For passed playlist, return a list of rows that For passed playlist, return a list of row numbers that
have been played. have been played.
""" """
plrs = session.execute( plrs = session.execute(
select(cls) select(PlaylistRows.row_number)
.where( .where(
cls.playlist_id == playlist_id, PlaylistRows.playlist_id == playlist_id,
cls.played.is_(True) PlaylistRows.played.is_(True)
) )
.order_by(cls.row_number) .order_by(PlaylistRows.row_number)
).scalars().all() ).scalars().all()
return plrs return plrs
@classmethod @staticmethod
def get_rows_with_tracks(cls, session: Session, def get_rows_with_tracks(session: Session,
playlist_id: int) -> List[int]: playlist_id: int) -> List[int]:
""" """
For passed playlist, return a list of rows that For passed playlist, return a list of all row numbers that
contain tracks contain tracks
""" """
plrs = session.execute( plrs = session.execute(
select(cls) select(PlaylistRows.row_number)
.where( .where(
cls.playlist_id == playlist_id, PlaylistRows.playlist_id == playlist_id,
cls.track_id.is_not(None) PlaylistRows.track_id.is_not(None)
) )
.order_by(cls.row_number) .order_by(PlaylistRows.row_number)
).scalars().all() ).scalars().all()
return plrs return plrs

View File

@ -7,13 +7,14 @@ 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, QEvent, Qt, QTime, QTimer # from PyQt5.QtCore import QDate, QProcess, Qt, QTime, QTimer, QUrl
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,
@ -38,7 +39,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 # type: ignore # from ui.downloadcsv_ui import Ui_DateSelect
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
@ -169,13 +170,12 @@ 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,36 +216,32 @@ 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())
#
self.close_tab(self.tabPlaylist.currentIndex()) # def close_tab(self, index: int) -> None:
# """
def close_tab(self, tab_index: int) -> None: # Close tab unless it holds the curren or next track
""" # """
Close active playlist tab unless it holds the curren or next track. #
Called from close_playlist_tab() or by clicking close button on tab. # if hasattr(self.tabPlaylist.widget(index), 'playlist_id'):
""" # if self.tabPlaylist.widget(index) == (
# self.current_track_playlist_tab):
# Don't close current track playlist # self.statusbar.showMessage(
if self.tabPlaylist.widget(tab_index) == ( # "Can't close current track playlist", 5000)
self.current_track_playlist_tab): # return
self.statusbar.showMessage( # if self.tabPlaylist.widget(index) == self.next_track_playlist_tab:
"Can't close current track playlist", 5000) # self.statusbar.showMessage(
return # "Can't close next track playlist", 5000)
# return
# Don't close next track playlist # # It's OK to close this playlist so remove from open playlist list
if self.tabPlaylist.widget(tab_index) == self.next_track_playlist_tab: # self.tabPlaylist.widget(index).close()
self.statusbar.showMessage( #
"Can't close next track playlist", 5000) # # Close regardless of tab type
return # self.tabPlaylist.removeTab(index)
# 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"""
@ -275,32 +271,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 = QFileDialog.getSaveFileName( # pathspec: Tuple[str, str] = 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 = pathspec[0] # path: str = 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"""
@ -364,45 +360,42 @@ 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
#
playlist_id = self.visible_playlist_tab().playlist_id # with Session() as session:
# playlist = Playlists.get_by_id(
with Session() as session: # session, self.visible_playlist_tab().playlist_id)
# # Get output filename
# Get output filename # pathspec: Tuple[str, str] = QFileDialog.getSaveFileName(
playlist = session.get(Playlists, playlist_id) # self, 'Save Playlist',
pathspec = QFileDialog.getSaveFileName( # directory=f"{playlist.name}.m3u",
self, 'Save Playlist', # filter="M3U files (*.m3u);;All files (*.*)"
directory=f"{playlist.name}.m3u", # )
filter="M3U files (*.m3u);;All files (*.*)" # if not pathspec:
) # return
if not pathspec: #
return # path: str = pathspec[0]
path = pathspec[0] # if not path.endswith(".m3u"):
if not path.endswith(".m3u"): # path += ".m3u"
path += ".m3u" #
# with open(path, "w") as f:
# Get list of track rows for this playlist # # Required directive on first line
plrs = PlaylistRows.get_rows_with_tracks(session, playlist_id) # f.write("#EXTM3U\n")
with open(path, "w") as f: # for _, track in playlist.tracks.items():
# Required directive on first line # f.write(
f.write("#EXTM3U\n") # "#EXTINF:"
for track in [a.track for a in plrs]: # f"{int(track.duration / 1000)},"
f.write( # f"{track.title} - "
"#EXTINF:" # f"{track.artist}"
f"{int(track.duration / 1000)}," # "\n"
f"{track.title} - " # f"{track.path}"
f"{track.artist}" # "\n"
"\n" # )
f"{track.path}"
"\n"
)
def fade(self) -> None: def fade(self) -> None:
"""Fade currently playing track""" """Fade currently playing track"""
@ -977,18 +970,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,15 +349,23 @@ 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:
"""Handle closing playist tab""" # """Save column widths"""
#
with Session() as session: # log.debug(f"playlists.closeEvent()")
# Record playlist as closed # with Session() as session:
playlist = session.get(Playlists, self.playlist_id) # for column in range(self.columnCount()):
playlist.close(session) # width = self.columnWidth(column)
# name = f"playlist_col_{str(column)}_width"
event.accept() # record = Settings.get_int_settings(session, name)
# 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"""
@ -922,10 +930,7 @@ 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 = [ played = PlaylistRows.get_played_rows(session, self.playlist_id)
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:
@ -1339,14 +1344,9 @@ class PlaylistTab(QTableWidget):
if starting_row is None: if starting_row is None:
starting_row = 0 starting_row = 0
track_rows = [ track_rows = PlaylistRows.get_rows_with_tracks(session,
p.row_number for p in PlaylistRows.get_rows_with_tracks( self.playlist_id)
session, self.playlist_id) played_rows = PlaylistRows.get_played_rows(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