-
-
Notifications
You must be signed in to change notification settings - Fork 39
fix(mpris): Fix position tracking and seeking #85
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Fix relative seek calculation (was treating offset as absolute position) - Add SetPosition support for widgets using absolute positioning - Sync MPRIS position with player events via set_position() - Emit Seeked signal so external clients update their displays - Update song_progress_ms on seek so UI updates even when paused
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,12 +21,12 @@ pub enum MprisEvent { | |
| Next, | ||
| Previous, | ||
| Stop, | ||
| Seek(i64), // Offset in microseconds | ||
| Seek(i64), // Relative offset in microseconds | ||
| SetPosition(i64), // Absolute position in microseconds | ||
| } | ||
|
|
||
| /// Commands to send TO the MPRIS server to update its state | ||
| #[derive(Debug, Clone)] | ||
| #[allow(dead_code)] // SetPosition kept for future use | ||
| pub enum MprisCommand { | ||
| Metadata { | ||
| title: String, | ||
|
|
@@ -36,7 +36,8 @@ pub enum MprisCommand { | |
| art_url: Option<String>, | ||
| }, | ||
| PlaybackStatus(bool), // true = playing, false = paused | ||
| Position(u64), // position in milliseconds (for future use) | ||
| Position(u64), // position in milliseconds (silent update) | ||
| Seeked(u64), // position in milliseconds (emits Seeked signal to notify clients) | ||
| Volume(u8), // 0-100 | ||
| Stopped, | ||
| } | ||
|
|
@@ -126,6 +127,11 @@ impl MprisManager { | |
| let _ = tx.send(MprisEvent::Seek(offset.as_micros())); | ||
| }); | ||
|
|
||
| let tx = event_tx.clone(); | ||
| player.connect_set_position(move |_player, _track_id, position| { | ||
| let _ = tx.send(MprisEvent::SetPosition(position.as_micros())); | ||
| }); | ||
|
|
||
| // Spawn the player event loop | ||
| tokio::task::spawn_local(player.run()); | ||
|
|
||
|
|
@@ -167,8 +173,17 @@ impl MprisManager { | |
| } | ||
| } | ||
| MprisCommand::Position(position_ms) => { | ||
| // Silent position update (for regular playback progress) | ||
| player.set_position(Time::from_millis(position_ms as i64)); | ||
| } | ||
| MprisCommand::Seeked(position_ms) => { | ||
| // Update position AND emit Seeked signal so clients know to refresh | ||
| let time = Time::from_millis(position_ms as i64); | ||
| player.set_position(time); | ||
| if let Err(e) = player.seeked(time).await { | ||
|
Comment on lines
175
to
+183
|
||
| eprintln!("MPRIS: Failed to emit Seeked signal: {}", e); | ||
| } | ||
| } | ||
| MprisCommand::Volume(volume_percent) => { | ||
| let volume = (volume_percent as f64) / 100.0; | ||
| if let Err(e) = player.set_volume(volume).await { | ||
|
|
@@ -223,12 +238,16 @@ impl MprisManager { | |
| .send(MprisCommand::PlaybackStatus(is_playing)); | ||
| } | ||
|
|
||
| /// Update playback position | ||
| #[allow(dead_code)] // Kept for future use | ||
| /// Update playback position (silent, no signal emitted) | ||
| pub fn set_position(&self, position_ms: u64) { | ||
| let _ = self.command_tx.send(MprisCommand::Position(position_ms)); | ||
| } | ||
|
|
||
| /// Update position AND emit Seeked signal (use when position jumps due to seeking) | ||
| pub fn emit_seeked(&self, position_ms: u64) { | ||
| let _ = self.command_tx.send(MprisCommand::Seeked(position_ms)); | ||
| } | ||
|
|
||
| /// Update volume (0-100) | ||
| pub fn set_volume(&self, volume_percent: u8) { | ||
| let _ = self.command_tx.send(MprisCommand::Volume(volume_percent)); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
new_position_msis computed asi64and then cast withas u32. Ifcurrent_ms + offset_ms(orposition_micros/1000) exceedsu32::MAX, the cast will wrap/truncate and can seek to an unintended position. Clamp to an upper bound before converting (e.g., clamp tou32::MAXor track duration if available), or keep the value asu64until the final validated conversion tou32forplayer.seek().