diff --git a/app/musicmuster.py b/app/musicmuster.py
index 4e4113a..130afc4 100755
--- a/app/musicmuster.py
+++ b/app/musicmuster.py
@@ -33,34 +33,70 @@ class Music:
}
def get_current_artist(self):
- return self.current_track['meta'].artist
+ try:
+ return self.current_track['meta'].artist
+ except AttributeError:
+ return ""
def get_current_duration(self):
- return self.current_track['meta'].duration
+ try:
+ return self.current_track['meta'].duration
+ except AttributeError:
+ return 0
def get_current_fade_at(self):
- return self.current_track['meta'].fade_at
+ try:
+ return self.current_track['meta'].fade_at
+ except AttributeError:
+ return 0
def get_current_playtime(self):
- return self.current_track['player'].get_time()
+ try:
+ return self.current_track['player'].get_time()
+ except AttributeError:
+ return 0
def get_current_silence_at(self):
- return self.current_track['meta'].silence_at
+ try:
+ return self.current_track['meta'].silence_at
+ except AttributeError:
+ return 0
def get_current_title(self):
- return self.current_track['meta'].title
+ try:
+ return self.current_track['meta'].title
+ except AttributeError:
+ return ""
- def get_last_artist(self):
- return self.last_track['meta'].artist
+ def get_current_track_id(self):
+ try:
+ return self.current_track['meta'].id
+ except AttributeError:
+ return 0
- def get_last_title(self):
- return self.last_track['meta'].title
+ def get_previous_artist(self):
+ try:
+ return self.previous_track['meta'].artist
+ except AttributeError:
+ return ""
+
+ def get_previous_title(self):
+ try:
+ return self.previous_track['meta'].title
+ except AttributeError:
+ return ""
def get_next_artist(self):
- return self.next_track['meta'].artist
+ try:
+ return self.next_track['meta'].artist
+ except AttributeError:
+ return ""
def get_next_title(self):
- return self.next_track['meta'].title
+ try:
+ return self.next_track['meta'].title
+ except AttributeError:
+ return ""
def play_next(self):
if self.previous_track['player']:
@@ -83,7 +119,7 @@ class Music:
else:
return False
- def resume_last(self):
+ def resume_previous(self):
pass
def set_next_track(self, id):
@@ -104,6 +140,7 @@ class Window(QMainWindow, Ui_MainWindow):
self.timer = QTimer()
self.connectSignalsSlots()
self.music = Music()
+ self.disable_play_next_controls()
record = Settings.get_int("mainwindow_x")
x = record.f_int or 1
@@ -147,11 +184,10 @@ class Window(QMainWindow, Ui_MainWindow):
record.update({'f_int': self.y()})
def connectSignalsSlots(self):
- self.fileButton.clicked.connect(self.selectFile)
- self.databaseButton.clicked.connect(self.selectFromDatabase)
+ self.actionSearch_database.triggered.connect(self.selectFromDatabase)
self.actionPlay_selected.triggered.connect(self.play_next)
self.actionPlay_next.triggered.connect(self.play_next)
- self.playlist.itemSelectionChanged.connect(self.set_next)
+ self.playlist.itemSelectionChanged.connect(self.set_next_track)
self.timer.timeout.connect(self.tick)
def selectFromDatabase(self):
@@ -160,19 +196,22 @@ class Window(QMainWindow, Ui_MainWindow):
def play_next(self):
self.music.play_next()
+ self.current_track.setText(
+ f"{self.music.get_current_title()} - "
+ f"{self.music.get_current_artist()}"
+ )
+ self.previous_track.setText(
+ f"{self.music.get_previous_title()} - "
+ f"{self.music.get_previous_artist()}"
+ )
+ self.set_next_track()
# Set time clocks
now = datetime.now()
self.label_start_tod.setText(now.strftime("%H:%M:%S"))
- fade_time = now + timedelta(
- milliseconds=self.music.get_current_fade_at())
- self.label_fade_tod.setText(fade_time.strftime("%H:%M:%S"))
silence_time = now + timedelta(
milliseconds=self.music.get_current_silence_at())
self.label_silent_tod.setText(silence_time.strftime("%H:%M:%S"))
- end_time = now + timedelta(
- milliseconds=self.music.get_current_duration())
- self.label_end_tod.setText(end_time.strftime("%H:%M:%S"))
def play_selected(self):
if self.playlist.selectionModel().hasSelection():
@@ -200,17 +239,42 @@ class Window(QMainWindow, Ui_MainWindow):
# track = Track(fname)
# self.add_to_playlist(track)
- def set_next(self):
+ def set_next_track(self):
+ """
+ Set the next track. In order of priority:
+
+ - the highlighted track so long as it's not the current track
+ - if the current track is highlighted, the next track if there
+ is one
+ - none, in which case disable play next controls
+ """
+
+ track_id = None
if self.playlist.selectionModel().hasSelection():
row = self.playlist.currentRow()
track_id = int(self.playlist.item(row, 0).text())
- DEBUG(f"set_next: track_id={track_id}")
+ if track_id == self.music.get_current_track_id():
+ # Current track highlighted: get next if it exists
+ try:
+ track_id = int(self.playlist.item(row + 1, 0).text())
+ except AttributeError:
+ # There is no next track
+ track_id = None
+ if track_id:
+ DEBUG(f"set_next_track: track_id={track_id}")
if self.music.set_next_track(track_id) != track_id:
ERROR("Can't set next track")
+ self.next_track.setText(
+ f"{self.music.get_next_title()} - "
+ f"{self.music.get_next_artist()}"
+ )
+ self.enable_play_next_controls()
+ else:
+ self.next_track.setText("")
+ self.disable_play_next_controls()
def tick(self):
- now = datetime.now()
- self.current_time.setText(now.strftime("%H:%M:%S"))
+ # self.current_time.setText(now.strftime("%H:%M:%S"))
if self.music.playing():
playtime = self.music.get_current_playtime()
self.label_elapsed_timer.setText(ms_to_mmss(playtime))
@@ -245,6 +309,15 @@ class Window(QMainWindow, Ui_MainWindow):
item = QTableWidgetItem(track.path)
pl.setItem(row, 7, item)
+ def disable_play_next_controls(self):
+ self.actionPlay_selected.setEnabled(False)
+ self.actionPlay_next.setEnabled(False)
+
+ def enable_play_next_controls(self):
+ self.actionPlay_selected.setEnabled(True)
+ self.actionPlay_next.setEnabled(True)
+
+
class DbDialog(QDialog):
def __init__(self, parent=None):
@@ -301,6 +374,8 @@ def ms_to_mmss(ms, decimals=0, negative=False):
minutes, remainder = divmod(ms, 60 * 1000)
seconds = remainder / 1000
+ if seconds == 60:
+ print(f"ms_to_mmss({ms}) gave 60 seconds")
return f"{sign}{minutes:.0f}:{seconds:02.{decimals}f}"
diff --git a/app/ui/icon-fade.png b/app/ui/icon-fade.png
new file mode 100644
index 0000000..bd4ce4d
Binary files /dev/null and b/app/ui/icon-fade.png differ
diff --git a/app/ui/icon-play-next.png b/app/ui/icon-play-next.png
new file mode 100644
index 0000000..cdde218
Binary files /dev/null and b/app/ui/icon-play-next.png differ
diff --git a/app/ui/icon-play.png b/app/ui/icon-play.png
new file mode 100644
index 0000000..1ad12c3
Binary files /dev/null and b/app/ui/icon-play.png differ
diff --git a/app/ui/icon-stop.png b/app/ui/icon-stop.png
new file mode 100644
index 0000000..9e4d165
Binary files /dev/null and b/app/ui/icon-stop.png differ
diff --git a/app/ui/icon_open_file.png b/app/ui/icon_open_file.png
new file mode 100644
index 0000000..9eb2915
Binary files /dev/null and b/app/ui/icon_open_file.png differ
diff --git a/app/ui/icon_search_database.png b/app/ui/icon_search_database.png
new file mode 100644
index 0000000..ccea506
Binary files /dev/null and b/app/ui/icon_search_database.png differ
diff --git a/app/ui/main_window.ui b/app/ui/main_window.ui
index dcd1a2c..f5b4c58 100644
--- a/app/ui/main_window.ui
+++ b/app/ui/main_window.ui
@@ -14,178 +14,89 @@
MainWindow
-
- -
-
-
- 10
+
+
-
+
+
+
+ 0
+ 0
+
-
-
-
-
- Elapsed
-
-
-
-
-
-
-
- Sans
- 40
- 75
- true
-
-
-
- 2:46
-
-
-
- -
-
-
-
- DejaVu Sans
- 16
-
-
-
- 10:17:37
-
-
- false
-
-
-
-
-
-
- -
-
-
- Fade at
-
-
-
-
-
-
-
- Sans
- 40
- 75
- true
-
-
-
- 0:53
-
-
-
- -
-
-
-
- DejaVu Sans
- 16
-
-
-
- 10:21:23
-
-
- false
-
-
-
-
-
-
- -
-
-
- background-color: rgb(252, 233, 79);
-
-
- Silent at
-
-
-
-
-
-
-
- Sans
- 40
- 75
- true
-
-
-
- 0:58
-
-
-
- -
-
-
-
- DejaVu Sans
- 16
-
-
-
- 10:21:28
-
-
- false
-
-
-
-
-
-
- -
-
-
- End at
-
-
-
-
-
-
-
- Sans
- 40
- 75
- true
-
-
-
- 1:00
-
-
-
- -
-
-
-
- DejaVu Sans
- 16
-
-
-
- 10:21:30
-
-
- false
-
-
-
-
-
-
-
+
+
+ 116
+ 16777215
+
+
+
+
+ Sans
+ 20
+
+
+
+ background-color: #f8d7da;
+border: 1px solid rgb(85, 87, 83);
+
+
+ Last track:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
- -
+
-
+
+
+
+ Sans
+ 20
+
+
+
+ background-color: #f8d7da;
+border: 1px solid rgb(85, 87, 83);
+
+
+ Before the goldrush - Neil Young [3:46]
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 116
+ 16777215
+
+
+
+
+ Sans
+ 20
+
+
+
+ background-color: #d4edda;
+border: 1px solid rgb(85, 87, 83);
+
+
+ Current track:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
@@ -194,7 +105,56 @@
- background-color: rgb(138, 226, 52);
+ background-color: #d4edda;
+border: 1px solid rgb(85, 87, 83);
+
+
+ During the goldrush - Neil Young [3:46]
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 116
+ 16777215
+
+
+
+
+ Sans
+ 20
+
+
+
+ background-color: #fff3cd;
+border: 1px solid rgb(85, 87, 83);
+
+
+ Next track:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ Sans
+ 20
+
+
+
+ background-color: #fff3cd;
border: 1px solid rgb(85, 87, 83);
@@ -202,7 +162,7 @@ border: 1px solid rgb(85, 87, 83);
- -
+
-
QAbstractItemView::NoEditTriggers
@@ -225,6 +185,9 @@ border: 1px solid rgb(85, 87, 83);
8
+
+ 0
+
Index
@@ -267,41 +230,340 @@ border: 1px solid rgb(85, 87, 83);
- -
-
-
- Select file
+
-
+
+
+ 5
-
-
- -
-
-
- Database
-
-
-
- -
-
-
-
- DejaVu Sans
- 25
- 75
- true
-
-
-
- 10:20:30
-
-
+
-
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
-
+
+
-
+
+
+ Started at:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ FreeSans
+ 16
+
+
+
+ 10:17:37
+
+
+ false
+
+
+
+
+
+ -
+
+
-
+
+
+ Silent at:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ FreeSans
+ 16
+
+
+
+ 10:21:28
+
+
+ false
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 20
+ 20
+
+
+
+
+ -
+
+
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
-
+
+
+ Elapsed time
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ FreeSans
+ 40
+ 50
+ false
+
+
+
+ 2:46
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 20
+ 20
+
+
+
+
+ -
+
+
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
-
+
+
+ Fade
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ FreeSans
+ 40
+ 50
+ false
+
+
+
+ 2:46
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 20
+ 20
+
+
+
+
+ -
+
+
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
-
+
+
+ Silent
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ FreeSans
+ 40
+ 50
+ false
+
+
+
+ 2:46
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 20
+ 20
+
+
+
+
+ -
+
+
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
-
+
+
+ End
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ FreeSans
+ 40
+ 50
+ false
+
+
+
+ 2:46
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
playlist
- current_time
- fileButton
current_track
- databaseButton
+ previous_track
+ next_track
+ horizontalSpacer
+ horizontalSpacer_2
+ horizontalSpacer_3
+ horizontalSpacer_4
+ frame_elapsed
+ frame
+ frame_elapsed_2
+ frame_elapsed_3
+ frame_elapsed_4
+ horizontalSpacer_5
+ current_track_2
+ next_track_2
+ previous_track_2
+
+ true
+
background-color: rgb(211, 215, 207);
+
+
+ toolBar
+
+
+ TopToolBarArea
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+ toolBar_2
+
+
+ TopToolBarArea
+
+
+ false
+
+
+
+
+ icon-play.pngicon-play.png
+
&Play selected
@@ -341,8 +645,48 @@ border: 1px solid rgb(85, 87, 83);
+
+
+ icon-play-next.pngicon-play-next.png
+
- Play &next
+ Skip to &next
+
+
+
+
+
+ icon_search_database.pngicon_search_database.png
+
+
+ Search &database
+
+
+
+
+
+ icon_open_file.pngicon_open_file.png
+
+
+ Add &file
+
+
+
+
+
+ icon-fade.pngicon-fade.png
+
+
+ F&ade
+
+
+
+
+
+ icon-stop.pngicon-stop.png
+
+
+ S&top
diff --git a/notes.otl b/notes.otl
index 49d71ed..45e6d4e 100644
--- a/notes.otl
+++ b/notes.otl
@@ -1,46 +1,70 @@
-Play track
- cause
- menu command (play or skip to next)
- autoplay
- back to last track
- track assignments (modify for "back to last track")
- last = current
- current = next
- next = None
- other
- update last / current / next track display
- timers to new current
- show track as played
- update start time for remaining tracks
- record track as played
- if playing, fade current over 1 second
-During playback
- Update timers
- Update time colours
- Status bar Total remaining playlist time
- Stop
- Fade
-Playlist management
- Save playlist
- Load playlist
- Add track to database and playlist
- Reorder playlist
- Remove track
- Add track
- Clear playlist
+First release
+ Play track
+ Cause
+ menu command (play next)
+ menu command (skip to next)
+ back to last track
+ track assignments (modify for "back to last track")
+ ✓ last = current
+ ✓ current = next
+ ✓ next = None
+ other
+ ✓ update last / current / next track display
+ timers to new current
+ update start time for remaining tracks
+ show track as played
+ if playing, fade current over 1 second
+ Log tracks played
+ During playback
+ ✓ Update timers
+ Update time colours
+ Status bar Total remaining playlist time
+ Stop
+ Fade
+ Playlist management
+ Automatically save playlist
+ Automatically load playlist
+ Add track to database and playlist
+ Reorder playlist
+ Remove track
+ ✓ Add track
+ Clear playlist
+ Clear selection
+ Click current does not change next
+ Display
+ ✓ Remember window size
+ ✓ Remember dialog size
+ ✓ Remember playlist column sizes
+ ✓ Timer font less bold
+ Make elapsed time and end time correct when track ends
+ Select next track if not already selected on track end
+ ✓ Top: previous, current, next track
+ Buttons
+ Play next
+ Skip to next
+ Fade
+ Stop
+ Fix icons in toolbar
+ Misc
+ ✓ Logging
+ ✓ Preferences
+ Database check for new/missing
+ Database check mtimes as well
+ ✓ Move clocks to bottom
+ Status bar
+ Timer colour warnings
+ Non-track playlist entries
+Later releases
+ Autoplay next track
Track properties
Copy track
Paste track
- Clear selection
Track volume analysis
Open track in Audacity
Wikipedia for song title
Song facts for song title
-Display
- ✓ Remember window size
- ✓ Remember dialog size
- ✓ Remember playlist column sizes
- Top: previous, current, next track
-Misc
- Logging
- Preferences
+ Named playlists
+ Song lookup as RompR
+ Visualise track volume
+ Script notes
+ Higher precision timer for some things (RTC)