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 @@ -323,16 +585,58 @@ border: 1px solid rgb(85, 87, 83); + + + + + + + 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)