From d12e1b8549b9f30ac6ad24fa22e11cfd08e837d7 Mon Sep 17 00:00:00 2001 From: Thomas Schlage Date: Sat, 3 May 2025 23:37:17 +0200 Subject: [PATCH] Send playback position as player event (#1495) * Send play progress as PlayerEvent::PositionChanged * Replaced PlayerEvent::PositionChanged with set_progress_callback() method * Revert "Replaced PlayerEvent::PositionChanged with set_progress_callback() method" This reverts commit f26e3de07b667fd764416a79ae682882063e0688. * Added opt-in config in PlayerConfig for progress event * Added doc comments and set default position interval to 1sec for standalone * Remove handling of PositionChanged in standalone binary * Fixed wrong event handling --- CHANGELOG.md | 1 + playback/src/config.rs | 4 ++++ playback/src/player.rs | 26 ++++++++++++++++++++++++++ src/main.rs | 1 + src/player_event_handler.rs | 2 ++ 5 files changed, 34 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 84b34578..cca28afe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [connect] Add `volume_steps` to `ConnectConfig` (breaking) - [connect] Add and enforce rustdoc - [playback] Add `track` field to `PlayerEvent::RepeatChanged` (breaking) +- [playback] Add `PlayerEvent::PositionChanged` event to notify about the current playback position - [core] Add `request_with_options` and `request_with_protobuf_and_options` to `SpClient` - [oauth] Add `OAuthClient` and `OAuthClientBuilder` structs to achieve a more customizable login process diff --git a/playback/src/config.rs b/playback/src/config.rs index cdb455ce..b2ece190 100644 --- a/playback/src/config.rs +++ b/playback/src/config.rs @@ -139,6 +139,9 @@ pub struct PlayerConfig { // pass function pointers so they can be lazily instantiated *after* spawning a thread // (thereby circumventing Send bounds that they might not satisfy) pub ditherer: Option, + /// Setting this will enable periodically sending events during playback informing about the playback position + /// To consume the PlayerEvent::PositionChanged event, listen to events via `Player::get_player_event_channel()`` + pub position_update_interval: Option, } impl Default for PlayerConfig { @@ -156,6 +159,7 @@ impl Default for PlayerConfig { normalisation_knee_db: 5.0, passthrough: false, ditherer: Some(mk_ditherer::), + position_update_interval: None, } } } diff --git a/playback/src/player.rs b/playback/src/player.rs index 296c26e5..8e0281e6 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -85,6 +85,7 @@ struct PlayerInternal { player_id: usize, play_request_id_generator: SeqGenerator, + last_progress_update: Instant, } static PLAYER_COUNTER: AtomicUsize = AtomicUsize::new(0); @@ -195,6 +196,14 @@ pub enum PlayerEvent { track_id: SpotifyId, position_ms: u32, }, + /// Requires `PlayerConfig::position_update_interval` to be set to Some. + /// Once set this event will be sent periodically while playing the track to inform about the + /// current playback position + PositionChanged { + play_request_id: u64, + track_id: SpotifyId, + position_ms: u32, + }, Seeked { play_request_id: u64, track_id: SpotifyId, @@ -481,6 +490,7 @@ impl Player { player_id, play_request_id_generator: SeqGenerator::new(0), + last_progress_update: Instant::now(), }; // While PlayerInternal is written as a future, it still contains blocking code. @@ -1340,6 +1350,22 @@ impl Future for PlayerInternal { position_ms: new_stream_position_ms, }); } + + if let Some(interval) = + self.config.position_update_interval + { + let last_progress_update_since_ms = + now.duration_since(self.last_progress_update); + + if last_progress_update_since_ms > interval { + self.last_progress_update = now; + self.send_event(PlayerEvent::PositionChanged { + play_request_id, + track_id, + position_ms: new_stream_position_ms, + }); + } + } } Err(e) => { error!("Skipping to next track, unable to decode samples for track <{:?}>: {:?}", track_id, e); diff --git a/src/main.rs b/src/main.rs index 6169fac5..1c4c4f2f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1818,6 +1818,7 @@ fn get_setup() -> Setup { normalisation_release_cf, normalisation_knee_db, ditherer, + position_update_interval: None, } }; diff --git a/src/player_event_handler.rs b/src/player_event_handler.rs index 21cfe01c..11c3c1d8 100644 --- a/src/player_event_handler.rs +++ b/src/player_event_handler.rs @@ -243,6 +243,8 @@ impl EventHandler { ); env_vars.insert("FILTER", filter.to_string()); } + // Ignore event irrelevant for standalone binary like PositionChanged + _ => {} } if !env_vars.is_empty() {