diff --git a/app/config.py b/app/config.py
index 655bdbe..ddb94a1 100644
--- a/app/config.py
+++ b/app/config.py
@@ -7,11 +7,13 @@ class Config(object):
COLOUR_CURRENT_HEADER = "#d4edda"
COLOUR_CURRENT_PLAYLIST = "#28a745"
COLOUR_ODD_PLAYLIST = "#f2f2f2"
+ COLOUR_ENDING_TIMER = "#dc3545"
COLOUR_EVEN_PLAYLIST = "#d9d9d9"
COLOUR_NEXT_HEADER = "#fff3cd"
COLOUR_NEXT_PLAYLIST = "#ffc107"
- COLOUR_NOTES_PLAYLIST = "#802020"
+ COLOUR_NOTES_PLAYLIST = "#dc3545"
COLOUR_PREVIOUS_HEADER = "#f8d7da"
+ COLOUR_WARNING_TIMER = "#ffc107"
DBFS_FADE = -12
DBFS_SILENCE = -50
DISPLAY_SQL = False
diff --git a/app/music.py b/app/music.py
index a672fd7..92f2b29 100644
--- a/app/music.py
+++ b/app/music.py
@@ -22,7 +22,7 @@ class Music:
"""
Fade the currently playing track.
- Return the current track path and position.
+ Return the current track position.
The actual management of fading runs in its own thread so as not
to hold up the UI during the fade.
@@ -31,15 +31,14 @@ class Music:
DEBUG("fade()")
if not self.playing():
- return (None, None)
+ return None
- path = self.track_path
position = self.player.get_position()
thread = threading.Thread(target=self._fade)
thread.start()
- return (path, position)
+ return position
def _fade(self):
"""
diff --git a/app/musicmuster.py b/app/musicmuster.py
index f7b4476..262332a 100755
--- a/app/musicmuster.py
+++ b/app/musicmuster.py
@@ -31,6 +31,7 @@ class Window(QMainWindow, Ui_MainWindow):
self.timer = QTimer()
self.even_tick = True
self.music = music.Music()
+ self.playing = False
self.playlist.music = self.music
self.connect_signals_slots()
self.disable_play_next_controls()
@@ -47,6 +48,7 @@ class Window(QMainWindow, Ui_MainWindow):
# Hard code to the only playlist we have for now
if self.playlist.load_playlist("Default"):
+ self.update_headers()
self.enable_play_next_controls()
self.timer.start(Config.TIMER_MS)
@@ -91,7 +93,7 @@ class Window(QMainWindow, Ui_MainWindow):
self.actionSearch_database.triggered.connect(self.search_database)
self.btnPrevious.clicked.connect(self.play_previous)
self.btnSearchDatabase.clicked.connect(self.search_database)
- self.btnSetNextTrack.clicked.connect(self.playlist.set_next_track)
+ self.btnSetNextTrack.clicked.connect(self.set_next_track)
self.btnSkipNext.clicked.connect(self.play_next)
self.btnStop.clicked.connect(self.fade)
@@ -109,18 +111,7 @@ class Window(QMainWindow, Ui_MainWindow):
def play_next(self):
self.playlist.play_next()
self.disable_play_next_controls()
- self.previous_track.setText(
- f"{self.playlist.get_previous_title()} - "
- f"{self.playlist.get_previous_artist()}"
- )
- self.current_track.setText(
- f"{self.playlist.get_current_title()} - "
- f"{self.playlist.get_current_artist()}"
- )
- self.next_track.setText(
- f"{self.playlist.get_next_title()} - "
- f"{self.playlist.get_next_artist()}"
- )
+ self.update_headers()
# Set time clocks
now = datetime.now()
@@ -142,8 +133,10 @@ class Window(QMainWindow, Ui_MainWindow):
dlg.exec()
def set_next_track(self):
- # TODO
- pass
+ "Set selected track as next"
+
+ self.playlist.set_selected_as_next()
+ self.update_headers()
def tick(self):
"""
@@ -166,28 +159,69 @@ class Window(QMainWindow, Ui_MainWindow):
return
if self.music.playing():
+ self.playing = True
playtime = self.music.get_playtime()
- self.label_elapsed_timer.setText(helpers.ms_to_mmss(playtime))
- self.label_fade_timer.setText(
- helpers.ms_to_mmss(
- self.playlist.get_current_fade_at() - playtime)
- )
+ time_to_fade = (self.playlist.get_current_fade_at() - playtime)
time_to_silence = (
self.playlist.get_current_silence_at() - playtime
)
- if time_to_silence < 500:
- self.label_silent_timer.setText("00:00")
+ time_to_end = (self.playlist.get_current_duration() - playtime)
+
+ # Elapsed time
+ if time_to_end < 500:
+ self.label_elapsed_timer.setText(
+ helpers.ms_to_mmss(self.playlist.get_current_duration())
+ )
+ else:
+ self.label_elapsed_timer.setText(helpers.ms_to_mmss(playtime))
+
+ # Time to fade
+ self.label_fade_timer.setText(helpers.ms_to_mmss(time_to_fade))
+
+ # Time to silence
+ if time_to_silence < 5000:
+ self.frame_silent.setStyleSheet(
+ f"background: {Config.COLOUR_ENDING_TIMER}"
+ )
+ self.enable_play_next_controls()
+ elif time_to_fade < 500:
+ self.frame_silent.setStyleSheet(
+ f"background: {Config.COLOUR_WARNING_TIMER}"
+ )
self.enable_play_next_controls()
else:
- self.label_silent_timer.setText(
- helpers.ms_to_mmss(time_to_silence)
- )
- self.label_end_timer.setText(
- helpers.ms_to_mmss(
- self.playlist.get_current_duration() - playtime))
+ self.frame_silent.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:
- # When music ends, update playlist display
- self.playlist.update_playlist_colours()
+ if self.playing:
+ self.label_end_timer.setText("00:00")
+ self.frame_silent.setStyleSheet("")
+ self.playing = False
+ self.playlist.music_ended()
+ self.update_headers()
+
+ def update_headers(self):
+ "Update last / current / next track headers"
+
+ self.previous_track.setText(
+ f"{self.playlist.get_previous_title()} - "
+ f"{self.playlist.get_previous_artist()}"
+ )
+ self.current_track.setText(
+ f"{self.playlist.get_current_title()} - "
+ f"{self.playlist.get_current_artist()}"
+ )
+ self.next_track.setText(
+ f"{self.playlist.get_next_title()} - "
+ f"{self.playlist.get_next_artist()}"
+ )
class DbDialog(QDialog):
diff --git a/app/playlists.py b/app/playlists.py
index b2748d2..0209ccd 100644
--- a/app/playlists.py
+++ b/app/playlists.py
@@ -42,7 +42,7 @@ class Playlist(QTableWidget):
self.current_track = None
self.next_track = None
- self.previous_track_path = None
+ self.previous_track = None
self.previous_track_position = None
self.played_tracks = []
@@ -66,10 +66,11 @@ class Playlist(QTableWidget):
"Save column widths"
for column in range(self.columnCount()):
+ width = self.columnWidth(column)
name = f"playlist_col_{str(column)}_width"
record = Settings.get_int(name)
if record.f_int != self.columnWidth(column):
- record.update({'f_int': self.columnWidth(column)})
+ record.update({'f_int': width})
def add_to_playlist(self, track):
"""
@@ -133,7 +134,7 @@ class Playlist(QTableWidget):
DEBUG(f"Moved row(s) {rows} to become row {drop_row}")
self.clearSelection()
- self.update_playlist_colours()
+ self.repaint()
def fade(self):
self.music.fade()
@@ -225,21 +226,22 @@ class Playlist(QTableWidget):
Return True if successful else False.
"""
+ DEBUG(f"load_playlist({name})")
+
for track in Playlists.get_playlist_by_name(name).get_tracks():
self.add_to_playlist(track)
# Set the first playable track as next to play
for row in range(self.rowCount()):
- if self.set_row_as_next_track(row):
+ if self.item(row, self.COL_INDEX):
self.meta_set_next(row)
+ self.tracks_changed()
return True
- self.update_playlist_colours()
-
return False
def meta_clear(self, row):
- "Clear metad ata for row"
+ "Clear metadata for row"
self.meta_set(row, None)
@@ -295,11 +297,21 @@ class Playlist(QTableWidget):
def meta_set_current(self, row):
"Mark row as current track"
+ DEBUG(f"meta_set_current({row})")
+
+ old_current = self.meta_get_current()
+ if old_current is not None:
+ self.meta_clear(old_current)
self.meta_set(row, "current")
def meta_set_next(self, row):
"Mark row as next track"
+ DEBUG(f"meta_set_next({row})")
+
+ old_next = self.meta_get_next()
+ if old_next is not None:
+ self.meta_clear(old_next)
self.meta_set(row, "next")
def meta_set_note(self, row):
@@ -322,10 +334,9 @@ class Playlist(QTableWidget):
Play (new) current.
Update playlist "current track" metadata
Cue up next track in playlist if there is one.
- Update playlist "next track" metadata
Tell database to record it as played
Remember it was played for this session
- Update playlist appearance.
+ Update metadata and headers, and repaint
"""
# If there is no next track set, return.
@@ -334,43 +345,30 @@ class Playlist(QTableWidget):
# If there's currently a track playing, fade it.
if self.music.playing():
- path, position = self.music.fade()
- self.previous_track_path = path
- self.previous_track_position = position
+ self.previous_track_position = self.music.fade()
+ self.previous_track = self.current_track
- # Move next track to current track.
+ # Shuffle tracks along
self.current_track = self.next_track
+ self.next_track = None
# Play (new) current.
self.music.play(self.current_track.path)
- # Update playlist "current track" metadata
- old_current = self.meta_get_current()
- if old_current is not None:
- self.meta_clear(old_current)
- current = self.meta_get_next()
- if current is not None:
- self.meta_set_current(current)
+ # Update metadata
+ self.meta_set_current(self.meta_get_next())
- # Cue up next track in playlist if there is one.
- track_id = 0
- next_row = 0
- if current is not None:
- start = current + 1
+ # Set up metadata for next track in playlist if there is one.
+ current_row = self.meta_get_current()
+ if current_row is not None:
+ start = current_row + 1
else:
start = 0
for row in range(start, self.rowCount()):
if self.item(row, self.COL_INDEX):
if int(self.item(row, self.COL_INDEX).text()) > 0:
- track_id = int(self.item(row, self.COL_INDEX).text())
- next_row = row
+ self.meta_set_next(row)
break
- if track_id:
- self.next_track = Tracks.get_track(track_id)
-
- # Update playlist "next track" metadata
- if next_row:
- self.meta_set_next(next_row)
# Tell database to record it as played
self.current_track.update_lastplayed()
@@ -379,33 +377,15 @@ class Playlist(QTableWidget):
# Remember it was played for this session
self.played_tracks.append(self.current_track.id)
- # Update playlist appearance.
- self.update_playlist_colours()
+ # Update display
+ self.tracks_changed()
- def set_next_track(self):
+ def set_selected_as_next(self):
"""
Sets the selected track as the next track.
"""
- # TODO
- pass
-
- # if self.selectionModel().hasSelection():
- # self.set_next_track_row(self.playlist.currentRow())
- # if self.selectionModel().hasSelection():
- # row = self.currentRow()
- # track_id = int(self.item(row, 0).text())
- # DEBUG(f"play_selected: track_id={track_id}")
- # # TODO: get_path may raise exception
- # music.play_track(track_id)
- # track = Tracks.get_track(id)
- # if not track:
- # ERROR(f"set_next_track({id}): can't find track")
- # return None
-
- # # self.next_track['player'] = vlc.MediaPlayer(track.path)
- # self.next_track = track
- # return track.id
+ self.set_row_as_next_track(self.currentRow())
def set_row_as_next_track(self, row):
"""
@@ -415,16 +395,21 @@ class Playlist(QTableWidget):
"""
if self.item(row, self.COL_INDEX):
- track_id = int(self.item(row, self.COL_INDEX).text())
- DEBUG(f"set_row_as_next_track: track_id={track_id}")
- self.next_track = Tracks.get_track(track_id)
- # Mark row as next track
- self.meta_set_next(row)
+ self.meta_set_current(row)
+ self.tracks_changed()
return True
return False
- def update_playlist_colours(self):
+ def music_ended(self):
+ "Update display"
+
+ self.previous_track = self.current_track
+ self.previous_track_position = 0
+ self.meta_clear(self.meta_get_current())
+ self.tracks_changed()
+
+ def repaint(self):
"Set row colours, fonts, etc"
self.clearSelection()
@@ -481,6 +466,50 @@ class Playlist(QTableWidget):
if self.item(row, j):
self.item(row, j).setFont(normal)
+ def tracks_changed(self):
+ """
+ One or more of current or next track has changed.
+
+ The row metadata is definitive because the same track may appear
+ more than once in the playlist, but only one track may be marked
+ as current or next.
+
+ Update self.current_track and self.next_track.
+ """
+
+ # Update instance variables
+ current_row = self.meta_get_current()
+ if current_row is not None:
+ track_id = int(self.item(current_row, self.COL_INDEX).text())
+ if not self.current_track or self.current_track.id != track_id:
+ self.current_track = Tracks.get_track(track_id)
+ else:
+ self.current_track = None
+
+ next_row = self.meta_get_next()
+ if next_row is not None:
+ track_id = int(self.item(next_row, self.COL_INDEX).text())
+ if not self.next_track or self.next_track.id != track_id:
+ self.next_track = Tracks.get_track(track_id)
+ else:
+ self.next_track = None
+
+ try:
+ DEBUG(f"tracks_changed() previous={self.previous_track.id}")
+ except AttributeError:
+ DEBUG("tracks_changed() previous=None")
+ try:
+ DEBUG(f"tracks_changed() current={self.current_track.id}")
+ except AttributeError:
+ DEBUG("tracks_changed() current=None")
+ try:
+ DEBUG(f"tracks_changed() next={self.next_track.id}")
+ except AttributeError:
+ DEBUG("tracks_changed() next=None")
+
+ # Update display
+ self.repaint()
+
class Window(QWidget):
def __init__(self):
diff --git a/app/ui/main_window.ui b/app/ui/main_window.ui
index 646aeff..3735fc9 100644
--- a/app/ui/main_window.ui
+++ b/app/ui/main_window.ui
@@ -491,7 +491,7 @@ border: 1px solid rgb(85, 87, 83);
-
-
+
@@ -547,7 +547,7 @@ border: 1px solid rgb(85, 87, 83);
-
-
+
@@ -603,7 +603,7 @@ border: 1px solid rgb(85, 87, 83);
-
-
+