Compare commits
No commits in common. "75b814e26ca833fbba4c6a8a36a1aca3e4886833" and "7f046ae86b9e2fc407f6ae2086ce70810a789291" have entirely different histories.
75b814e26c
...
7f046ae86b
@ -1,9 +1,7 @@
|
|||||||
import inspect
|
|
||||||
import os
|
import os
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
|
|
||||||
from config import Config
|
from config import Config
|
||||||
from contextlib import contextmanager
|
|
||||||
from sqlalchemy.orm import (sessionmaker, scoped_session)
|
from sqlalchemy.orm import (sessionmaker, scoped_session)
|
||||||
|
|
||||||
MM_ENV = os.environ.get('MM_ENV', 'PRODUCTION')
|
MM_ENV = os.environ.get('MM_ENV', 'PRODUCTION')
|
||||||
@ -37,20 +35,4 @@ engine = sqlalchemy.create_engine(
|
|||||||
pool_pre_ping=True
|
pool_pre_ping=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Session = scoped_session(sessionmaker(bind=engine))
|
||||||
# Session = scoped_session(sessionmaker(bind=engine))
|
|
||||||
@contextmanager
|
|
||||||
def Session():
|
|
||||||
frame = inspect.stack()[2]
|
|
||||||
file = frame.filename
|
|
||||||
function = frame.function
|
|
||||||
lineno = frame.lineno
|
|
||||||
print(f"Session acquired, {file=}, {function=}, {lineno=}")
|
|
||||||
# yield scoped_session(sessionmaker(bind=engine))
|
|
||||||
Session = scoped_session(sessionmaker(bind=engine))
|
|
||||||
yield Session
|
|
||||||
print(" Session released")
|
|
||||||
Session.commit()
|
|
||||||
print(" Session committed")
|
|
||||||
Session.close()
|
|
||||||
print(" Session closed")
|
|
||||||
|
|||||||
@ -190,13 +190,14 @@ class Playdates(Base):
|
|||||||
tracks: RelationshipProperty = relationship(
|
tracks: RelationshipProperty = relationship(
|
||||||
"Tracks", back_populates="playdates", lazy="joined")
|
"Tracks", back_populates="playdates", lazy="joined")
|
||||||
|
|
||||||
def __init__(self, session: Session, track_id: int) -> None:
|
def __init__(self, session: Session, track: "Tracks") -> None:
|
||||||
"""Record that track was played"""
|
"""Record that track was played"""
|
||||||
|
|
||||||
DEBUG(f"add_playdate({track_id=})")
|
DEBUG(f"add_playdate(track={track})")
|
||||||
|
|
||||||
self.lastplayed = datetime.now()
|
self.lastplayed = datetime.now()
|
||||||
self.track_id = track_id
|
self.track_id = track.id
|
||||||
|
track.update_lastplayed(session)
|
||||||
session.add(self)
|
session.add(self)
|
||||||
session.flush()
|
session.flush()
|
||||||
|
|
||||||
@ -593,13 +594,9 @@ class Tracks(Base):
|
|||||||
.order_by(cls.title)
|
.order_by(cls.title)
|
||||||
).all()
|
).all()
|
||||||
|
|
||||||
@staticmethod
|
def update_lastplayed(self, session: Session) -> None:
|
||||||
def update_lastplayed(session: Session, track_id: int) -> None:
|
self.lastplayed = datetime.now()
|
||||||
"""Update the last_played field to current datetime"""
|
session.add(self)
|
||||||
|
|
||||||
rec = session.query(Tracks).get(track_id)
|
|
||||||
rec.lastplayed = datetime.now()
|
|
||||||
session.add(rec)
|
|
||||||
session.flush()
|
session.flush()
|
||||||
|
|
||||||
def update_artist(self, session: Session, artist: str) -> None:
|
def update_artist(self, session: Session, artist: str) -> None:
|
||||||
|
|||||||
@ -24,13 +24,12 @@ from PyQt5.QtWidgets import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
import dbconfig
|
import dbconfig
|
||||||
from dbconfig import Session
|
|
||||||
import helpers
|
import helpers
|
||||||
import music
|
import music
|
||||||
|
|
||||||
from config import Config
|
from config import Config
|
||||||
from models import (Base, Playdates, Playlists, PlaylistTracks,
|
from models import (Playdates, Playlists, PlaylistTracks,
|
||||||
Settings, Tracks)
|
Session, Settings, Tracks)
|
||||||
from playlists import PlaylistTab
|
from playlists import PlaylistTab
|
||||||
from utilities import create_track_from_file
|
from utilities import create_track_from_file
|
||||||
from sqlalchemy.orm.exc import DetachedInstanceError
|
from sqlalchemy.orm.exc import DetachedInstanceError
|
||||||
@ -39,20 +38,6 @@ from ui.dlg_SelectPlaylist_ui import Ui_dlgSelectPlaylist
|
|||||||
from ui.main_window_ui import Ui_MainWindow
|
from ui.main_window_ui import Ui_MainWindow
|
||||||
|
|
||||||
|
|
||||||
class TrackData:
|
|
||||||
def __init__(self, track):
|
|
||||||
self.id = track.id
|
|
||||||
self.title = track.title
|
|
||||||
self.artist = track.artist
|
|
||||||
self.duration = track.duration
|
|
||||||
self.start_gap = track.start_gap
|
|
||||||
self.fade_at = track.fade_at
|
|
||||||
self.silence_at = track.silence_at
|
|
||||||
self.path = track.path
|
|
||||||
self.mtime = track.mtime
|
|
||||||
self.lastplayed = track.lastplayed
|
|
||||||
|
|
||||||
|
|
||||||
class Window(QMainWindow, Ui_MainWindow):
|
class Window(QMainWindow, Ui_MainWindow):
|
||||||
def __init__(self, parent=None) -> None:
|
def __init__(self, parent=None) -> None:
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
@ -518,8 +503,6 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
DEBUG("musicmuster.play_next(): no next track selected", True)
|
DEBUG("musicmuster.play_next(): no next track selected", True)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.kae()
|
|
||||||
|
|
||||||
with Session() as session:
|
with Session() as session:
|
||||||
# If there's currently a track playing, fade it.
|
# If there's currently a track playing, fade it.
|
||||||
self.stop_playing(fade=True)
|
self.stop_playing(fade=True)
|
||||||
@ -528,6 +511,10 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
self.current_track = self.next_track
|
self.current_track = self.next_track
|
||||||
self.next_track = None
|
self.next_track = None
|
||||||
|
|
||||||
|
# Ensure track is in session
|
||||||
|
if self.current_track not in session:
|
||||||
|
session.add(self.current_track)
|
||||||
|
|
||||||
# If current track on different playlist_tab to last, reset
|
# If current track on different playlist_tab to last, reset
|
||||||
# last track playlist_tab colour
|
# last track playlist_tab colour
|
||||||
# Set current track playlist_tab colour
|
# Set current track playlist_tab colour
|
||||||
@ -547,10 +534,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
self.music.play(self.current_track.path)
|
self.music.play(self.current_track.path)
|
||||||
|
|
||||||
# Tell database to record it as played
|
# Tell database to record it as played
|
||||||
Playdates(session, self.current_track.id)
|
Playdates(session, self.current_track)
|
||||||
|
|
||||||
# Set last_played date
|
|
||||||
Tracks.update_lastplayed(session, self.current_track.id)
|
|
||||||
|
|
||||||
# Tell playlist track is now playing
|
# Tell playlist track is now playing
|
||||||
self.current_track_playlist_tab.play_started(session)
|
self.current_track_playlist_tab.play_started(session)
|
||||||
@ -729,7 +713,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
QColor(Config.COLOUR_NEXT_TAB))
|
QColor(Config.COLOUR_NEXT_TAB))
|
||||||
|
|
||||||
# Note next track
|
# Note next track
|
||||||
self.next_track = TrackData(track)
|
self.next_track = track
|
||||||
|
|
||||||
# Update headers
|
# Update headers
|
||||||
self.update_headers()
|
self.update_headers()
|
||||||
@ -763,55 +747,58 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
|
|
||||||
# If track is playing, update track clocks time and colours
|
# If track is playing, update track clocks time and colours
|
||||||
if self.music.player and self.music.playing():
|
if self.music.player and self.music.playing():
|
||||||
self.playing = True
|
with Session() as session:
|
||||||
playtime: int = self.music.get_playtime()
|
self.playing = True
|
||||||
time_to_fade: int = (self.current_track.fade_at - playtime)
|
if self.current_track not in session:
|
||||||
time_to_silence: int = (
|
session.add(self.current_track)
|
||||||
self.current_track.silence_at - playtime)
|
playtime: int = self.music.get_playtime()
|
||||||
time_to_end: int = (self.current_track.duration - playtime)
|
time_to_fade: int = (self.current_track.fade_at - playtime)
|
||||||
|
time_to_silence: int = (
|
||||||
|
self.current_track.silence_at - playtime)
|
||||||
|
time_to_end: int = (self.current_track.duration - playtime)
|
||||||
|
|
||||||
# Elapsed time
|
# Elapsed time
|
||||||
if time_to_end < 500:
|
if time_to_end < 500:
|
||||||
self.label_elapsed_timer.setText(
|
self.label_elapsed_timer.setText(
|
||||||
helpers.ms_to_mmss(playtime)
|
helpers.ms_to_mmss(playtime)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.label_elapsed_timer.setText(
|
self.label_elapsed_timer.setText(
|
||||||
helpers.ms_to_mmss(playtime)
|
helpers.ms_to_mmss(playtime)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Time to fade
|
||||||
|
self.label_fade_timer.setText(helpers.ms_to_mmss(time_to_fade))
|
||||||
|
|
||||||
|
# If silent in the next 5 seconds, put warning colour on
|
||||||
|
# time to silence box and enable play controls
|
||||||
|
if time_to_silence <= 5500:
|
||||||
|
self.frame_silent.setStyleSheet(
|
||||||
|
f"background: {Config.COLOUR_ENDING_TIMER}"
|
||||||
|
)
|
||||||
|
self.enable_play_next_controls()
|
||||||
|
# Set warning colour on time to silence box when fade starts
|
||||||
|
elif time_to_fade <= 500:
|
||||||
|
self.frame_silent.setStyleSheet(
|
||||||
|
f"background: {Config.COLOUR_WARNING_TIMER}"
|
||||||
|
)
|
||||||
|
# Five seconds before fade starts, set warning colour on
|
||||||
|
# time to silence box and enable play controls
|
||||||
|
elif time_to_fade <= 5500:
|
||||||
|
self.frame_fade.setStyleSheet(
|
||||||
|
f"background: {Config.COLOUR_WARNING_TIMER}"
|
||||||
|
)
|
||||||
|
self.enable_play_next_controls()
|
||||||
|
else:
|
||||||
|
self.frame_silent.setStyleSheet("")
|
||||||
|
self.frame_fade.setStyleSheet("")
|
||||||
|
|
||||||
|
self.label_silent_timer.setText(
|
||||||
|
helpers.ms_to_mmss(time_to_silence)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Time to fade
|
# Time to end
|
||||||
self.label_fade_timer.setText(helpers.ms_to_mmss(time_to_fade))
|
self.label_end_timer.setText(helpers.ms_to_mmss(time_to_end))
|
||||||
|
|
||||||
# If silent in the next 5 seconds, put warning colour on
|
|
||||||
# time to silence box and enable play controls
|
|
||||||
if time_to_silence <= 5500:
|
|
||||||
self.frame_silent.setStyleSheet(
|
|
||||||
f"background: {Config.COLOUR_ENDING_TIMER}"
|
|
||||||
)
|
|
||||||
self.enable_play_next_controls()
|
|
||||||
# Set warning colour on time to silence box when fade starts
|
|
||||||
elif time_to_fade <= 500:
|
|
||||||
self.frame_silent.setStyleSheet(
|
|
||||||
f"background: {Config.COLOUR_WARNING_TIMER}"
|
|
||||||
)
|
|
||||||
# Five seconds before fade starts, set warning colour on
|
|
||||||
# time to silence box and enable play controls
|
|
||||||
elif time_to_fade <= 5500:
|
|
||||||
self.frame_fade.setStyleSheet(
|
|
||||||
f"background: {Config.COLOUR_WARNING_TIMER}"
|
|
||||||
)
|
|
||||||
self.enable_play_next_controls()
|
|
||||||
else:
|
|
||||||
self.frame_silent.setStyleSheet("")
|
|
||||||
self.frame_fade.setStyleSheet("")
|
|
||||||
|
|
||||||
self.label_silent_timer.setText(
|
|
||||||
helpers.ms_to_mmss(time_to_silence)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Time to end
|
|
||||||
self.label_end_timer.setText(helpers.ms_to_mmss(time_to_end))
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if self.playing:
|
if self.playing:
|
||||||
@ -996,6 +983,7 @@ class SelectPlaylistDialog(QDialog):
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
try:
|
try:
|
||||||
Base.metadata.create_all(dbconfig.engine)
|
Base.metadata.create_all(dbconfig.engine)
|
||||||
|
Session = dbconfig.Session
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
win = Window()
|
win = Window()
|
||||||
win.show()
|
win.show()
|
||||||
|
|||||||
@ -341,7 +341,7 @@ class PlaylistTab(QTableWidget):
|
|||||||
# Add track details to columns
|
# Add track details to columns
|
||||||
mss_item: QTableWidgetItem = QTableWidgetItem(str(track.start_gap))
|
mss_item: QTableWidgetItem = QTableWidgetItem(str(track.start_gap))
|
||||||
if track.start_gap and track.start_gap >= 500:
|
if track.start_gap and track.start_gap >= 500:
|
||||||
mss_item.setBackground(QColor(Config.COLOUR_LONG_START))
|
item.setBackground(QColor(Config.COLOUR_LONG_START))
|
||||||
self.setItem(row, self.COL_MSS, mss_item)
|
self.setItem(row, self.COL_MSS, mss_item)
|
||||||
|
|
||||||
title_item: QTableWidgetItem = QTableWidgetItem(track.title)
|
title_item: QTableWidgetItem = QTableWidgetItem(track.title)
|
||||||
@ -485,7 +485,7 @@ class PlaylistTab(QTableWidget):
|
|||||||
self._insert_note(session, item, repaint=False)
|
self._insert_note(session, item, repaint=False)
|
||||||
|
|
||||||
# Scroll to top
|
# Scroll to top
|
||||||
scroll_to: QTableWidgetItem = self.item(0, 0)
|
scroll_to: QTableWidgetItem = self.item(0, self.COL_TITLE)
|
||||||
self.scrollToItem(scroll_to, QAbstractItemView.PositionAtTop)
|
self.scrollToItem(scroll_to, QAbstractItemView.PositionAtTop)
|
||||||
|
|
||||||
# We possibly don't need to save the playlist here, but row
|
# We possibly don't need to save the playlist here, but row
|
||||||
@ -1170,12 +1170,12 @@ class PlaylistTab(QTableWidget):
|
|||||||
"\n\n"
|
"\n\n"
|
||||||
f"Path: {track.path}\n"
|
f"Path: {track.path}\n"
|
||||||
)
|
)
|
||||||
info: QMessageBox = QMessageBox(self)
|
info: QMessageBox = QMessageBox(self)
|
||||||
info.setIcon(QMessageBox.Information)
|
info.setIcon(QMessageBox.Information)
|
||||||
info.setText(txt)
|
info.setText(txt)
|
||||||
info.setStandardButtons(QMessageBox.Ok)
|
info.setStandardButtons(QMessageBox.Ok)
|
||||||
info.setDefaultButton(QMessageBox.Cancel)
|
info.setDefaultButton(QMessageBox.Cancel)
|
||||||
info.exec()
|
info.exec()
|
||||||
|
|
||||||
def _insert_note(self, session: Session, note: Notes,
|
def _insert_note(self, session: Session, note: Notes,
|
||||||
row: Optional[int] = None, repaint: bool = True) -> None:
|
row: Optional[int] = None, repaint: bool = True) -> None:
|
||||||
@ -1364,10 +1364,6 @@ class PlaylistTab(QTableWidget):
|
|||||||
# Get the row number of all selected items and put into a set
|
# Get the row number of all selected items and put into a set
|
||||||
# to deduplicate
|
# to deduplicate
|
||||||
sel_rows: Set[int] = set([item.row() for item in self.selectedItems()])
|
sel_rows: Set[int] = set([item.row() for item in self.selectedItems()])
|
||||||
# If no rows are selected, we have nothing to do
|
|
||||||
if len(sel_rows) == 0:
|
|
||||||
return
|
|
||||||
|
|
||||||
notes_rows: Set[int] = set(self._get_notes_rows())
|
notes_rows: Set[int] = set(self._get_notes_rows())
|
||||||
ms: int = 0
|
ms: int = 0
|
||||||
with Session() as session:
|
with Session() as session:
|
||||||
@ -1392,7 +1388,7 @@ class PlaylistTab(QTableWidget):
|
|||||||
self.clearSelection()
|
self.clearSelection()
|
||||||
|
|
||||||
if played:
|
if played:
|
||||||
rows = self._get_played_track_rows()
|
rows = self. _get_played_track_rows()
|
||||||
else:
|
else:
|
||||||
rows = self._get_unplayed_track_rows()
|
rows = self._get_unplayed_track_rows()
|
||||||
|
|
||||||
|
|||||||
@ -246,7 +246,7 @@ def update_db(session):
|
|||||||
for path in list(os_paths - db_paths):
|
for path in list(os_paths - db_paths):
|
||||||
DEBUG(f"songdb.update_db: {path=} not in database")
|
DEBUG(f"songdb.update_db: {path=} not in database")
|
||||||
# is filename in database?
|
# is filename in database?
|
||||||
track = Tracks.get_by_filename(session, os.path.basename(path))
|
track = Tracks.get_from_filename(session, os.path.basename(path))
|
||||||
if not track:
|
if not track:
|
||||||
messages.append(f"Track missing from database: {path}")
|
messages.append(f"Track missing from database: {path}")
|
||||||
else:
|
else:
|
||||||
@ -262,7 +262,7 @@ def update_db(session):
|
|||||||
# Remote any tracks from database whose paths don't exist
|
# Remote any tracks from database whose paths don't exist
|
||||||
for path in list(db_paths - os_paths):
|
for path in list(db_paths - os_paths):
|
||||||
# Manage tracks listed in database but where path is invalid
|
# Manage tracks listed in database but where path is invalid
|
||||||
track = Tracks.get_by_path(session, path)
|
track = Tracks.get_from_path(session, path)
|
||||||
messages.append(f"Remove from database: {path=} {track=}")
|
messages.append(f"Remove from database: {path=} {track=}")
|
||||||
|
|
||||||
# Remove references from Playdates
|
# Remove references from Playdates
|
||||||
|
|||||||
@ -229,8 +229,6 @@ def test_get_selected_row(qtbot, monkeypatch, session):
|
|||||||
qtbot.mouseClick(
|
qtbot.mouseClick(
|
||||||
playlist_tab.viewport(), Qt.LeftButton, pos=rect.center()
|
playlist_tab.viewport(), Qt.LeftButton, pos=rect.center()
|
||||||
)
|
)
|
||||||
row_number = playlist_tab.get_selected_row()
|
|
||||||
assert row_number == 0
|
|
||||||
|
|
||||||
|
|
||||||
def test_set_next(qtbot, monkeypatch, session):
|
def test_set_next(qtbot, monkeypatch, session):
|
||||||
@ -250,9 +248,6 @@ def test_set_next(qtbot, monkeypatch, session):
|
|||||||
|
|
||||||
# Add some tracks
|
# Add some tracks
|
||||||
track1 = models.Tracks.get_by_filename(session, "isa.mp3")
|
track1 = models.Tracks.get_by_filename(session, "isa.mp3")
|
||||||
track1_title = track1.title
|
|
||||||
assert track1_title
|
|
||||||
|
|
||||||
playlist_tab.insert_track(session, track1)
|
playlist_tab.insert_track(session, track1)
|
||||||
track2 = models.Tracks.get_by_filename(session, "mom.mp3")
|
track2 = models.Tracks.get_by_filename(session, "mom.mp3")
|
||||||
playlist_tab.insert_track(session, track2)
|
playlist_tab.insert_track(session, track2)
|
||||||
@ -266,12 +261,11 @@ def test_set_next(qtbot, monkeypatch, session):
|
|||||||
qtbot.mouseClick(
|
qtbot.mouseClick(
|
||||||
playlist_tab.viewport(), Qt.LeftButton, pos=rect.center()
|
playlist_tab.viewport(), Qt.LeftButton, pos=rect.center()
|
||||||
)
|
)
|
||||||
selected_title = playlist_tab.get_selected_title()
|
# qtbot.wait(10000)
|
||||||
assert selected_title == track1_title
|
|
||||||
|
|
||||||
qtbot.keyPress(playlist_tab.viewport(), "N",
|
qtbot.keyPress(playlist_tab.viewport(), "N",
|
||||||
modifier=Qt.ControlModifier)
|
modifier=Qt.ControlModifier)
|
||||||
qtbot.wait(1000)
|
qtbot.wait(2000)
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def test_kae(monkeypatch, session):
|
def test_kae(monkeypatch, session):
|
||||||
@ -283,3 +277,4 @@ def test_kae(monkeypatch, session):
|
|||||||
# monkeypatch.setattr(dbconfig, "Session", session)
|
# monkeypatch.setattr(dbconfig, "Session", session)
|
||||||
# monkeypatch.setattr(models, "Session", session)
|
# monkeypatch.setattr(models, "Session", session)
|
||||||
# monkeypatch.setattr(playlists, "Session", session)
|
# monkeypatch.setattr(playlists, "Session", session)
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user