Rockbox open source high quality audio player as a Music Player Daemon
mpris rockbox mpd libadwaita audio rust zig deno
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

Improve Now Playing fallback and macOS visibility

Fall back to queue.first() when current index isn't set, use track.path
(instead of id) to detect metadata changes, and report Stopped as Paused
when a track is present so the macOS Now Playing widget remains visible.

+13 -5
+4 -1
gpui/src/controller.rs
··· 126 126 } 127 127 128 128 // Push current playback state to the OS notification bar. 129 + // Fall back to queue.first() when current_idx is not yet set 130 + // (playlist loaded but audio engine still initialising on open). 129 131 let _ = cx.update(|app| { 130 132 let s = state_for_poll.read(app); 131 - np.update(s.current_track(), s.status, s.position); 133 + let track = s.current_track().or_else(|| s.queue.first()); 134 + np.update(track, s.status, s.position); 132 135 }); 133 136 } 134 137 }
+9 -4
gpui/src/now_playing.rs
··· 80 80 81 81 /// Push the current playback state and track metadata to the OS. 82 82 pub fn update(&mut self, track: Option<&Track>, status: PlaybackStatus, position: u64) { 83 - let track_id = track.map(|t| t.id.as_str()).unwrap_or(""); 83 + // Use path (always populated) not id (often empty for queue tracks) to detect changes. 84 + let track_path = track.map(|t| t.path.as_str()).unwrap_or(""); 84 85 // album_art is a bare filename served by rockboxd's cover HTTP server. 85 86 let cover_url = track 86 87 .and_then(|t| t.album_art.as_deref()) ··· 92 93 // any queue data arrives causes macOS to deregister the app from Now Playing, 93 94 // and a subsequent set_playback(Paused/Playing) then fails to re-show the widget. 94 95 let had_track = !self.last_track_id.is_empty(); 95 - let has_track = !track_id.is_empty(); 96 + let has_track = !track_path.is_empty(); 96 97 if !had_track && !has_track { 97 98 return; 98 99 } 99 100 100 - let track_changed = track_id != self.last_track_id; 101 + let track_changed = track_path != self.last_track_id; 101 102 let cover_changed = cover_url != self.last_cover_url; 102 103 103 104 // souvlaki's macOS set_playback_metadata() replaces nowPlayingInfo with a fresh ··· 106 107 // same tick ensures the progress merge sees the fresh metadata dict, so the 107 108 // final nowPlayingInfo always contains both metadata and elapsed time. 108 109 if track_changed || cover_changed { 109 - self.last_track_id = track_id.to_string(); 110 + self.last_track_id = track_path.to_string(); 110 111 self.last_cover_url = cover_url.clone(); 111 112 112 113 if has_track { ··· 140 141 } 141 142 142 143 let progress = Some(MediaPosition(Duration::from_secs(position))); 144 + // macOS hides the Now Playing widget for Stopped state. When a track is loaded 145 + // but Rockbox hasn't started the audio engine yet (e.g. on first open), show as 146 + // Paused so the controls appear immediately — the user can press play from there. 143 147 let playback = match status { 144 148 PlaybackStatus::Playing => MediaPlayback::Playing { progress }, 145 149 PlaybackStatus::Paused => MediaPlayback::Paused { progress }, 150 + PlaybackStatus::Stopped if has_track => MediaPlayback::Paused { progress }, 146 151 PlaybackStatus::Stopped => MediaPlayback::Stopped, 147 152 }; 148 153 let _ = self.controls.set_playback(playback);