Repository: aome510/spotify-player (language: rust)

Issue:
`spotify_player get key playback` command is slow
**Describe the bug**
Using the `get key playback` to retrieve Playback data, it is slow as it makes a new network request to retrieve the data instead of spotify-player grabbing from it's state directly.
This is an issue for me since i want to retrieve data from spotify-player in my statusbar, and it will slow it down significantly.

A workaround for this would be to use MPRIS and retrieval of D-Bus data, which already by itself is much quicker.

**To Reproduce**
```
$ time spotify_player get key playback >/dev/null
real    0m 0.37s
user    0m 0.00s
sys     0m 0.00s
```
(feels like an entire second, or two)

**Expected behavior**
spotify-player fetches the playback data from it's own state, being near instant.



Discussion / hints:

Fix patch:
diff --git a/spotify_player/src/cli/client.rs b/spotify_player/src/cli/client.rs
index 5043e62a..19a21b19 100644
--- a/spotify_player/src/cli/client.rs
+++ b/spotify_player/src/cli/client.rs
@@ -89,7 +89,7 @@ async fn handle_socket_request(
     }
 
     match request {
-        Request::Get(GetRequest::Key(key)) => handle_get_key_request(client, key).await,
+        Request::Get(GetRequest::Key(key)) => handle_get_key_request(client, state, key).await,
         Request::Get(GetRequest::Item(item_type, id_or_name)) => {
             handle_get_item_request(client, item_type, id_or_name).await
         }
@@ -145,13 +145,10 @@ async fn handle_socket_request(
     }
 }
 
-async fn handle_get_key_request(client: &Client, key: Key) -> Result<Vec<u8>> {
+async fn handle_get_key_request(client: &Client, state: &SharedState, key: Key) -> Result<Vec<u8>> {
     Ok(match key {
         Key::Playback => {
-            let playback = client
-                .spotify
-                .current_playback(None, None::<Vec<_>>)
-                .await?;
+            let playback = state.player.read().current_playback();
             serde_json::to_vec(&playback)?
         }
         Key::Devices => {
@@ -357,8 +354,22 @@ async fn handle_playback_request(
         }
     };
 
-    client.handle_player_request(state, player_request).await?;
-    client.update_playback(state);
+    tokio::task::spawn({
+        let client = client.clone();
+        let state = state.clone();
+        async move {
+            match client.handle_player_request(&state, player_request).await {
+                Ok(()) => {
+                    client.update_playback(&state);
+                }
+                Err(err) => {
+                    tracing::warn!(
+                        "Failed to handle a player request for playback CLI command: {err:#}"
+                    );
+                }
+            }
+        }
+    });
     Ok(())
 }
 
diff --git a/spotify_player/src/state/player.rs b/spotify_player/src/state/player.rs
index 05805d75..66f3ea09 100644
--- a/spotify_player/src/state/player.rs
+++ b/spotify_player/src/state/player.rs
@@ -15,6 +15,37 @@ pub struct PlayerState {
 }
 
 impl PlayerState {
+    /// gets the current playback state
+    ///
+    /// # Note
+    /// Because playback data stored inside the player state is buffered and cached,
+    /// this function tries to estimate the current playback based on the available state data.
+    pub fn current_playback(&self) -> Option<rspotify_model::CurrentPlaybackContext> {
+        let mut playback = self.playback.clone()?;
+
+        // update the playback's progress based on the `playback_last_updated_time`
+        playback.progress = playback.progress.map(|d| {
+            d + if playback.is_playing {
+                chrono::Duration::from_std(self.playback_last_updated_time.unwrap().elapsed())
+                    .unwrap()
+            } else {
+                chrono::Duration::zero()
+            }
+        });
+
+        // update the playback's metadata based on the `buffered_playback` metadata
+        if let Some(ref p) = self.buffered_playback {
+            playback.device.name = p.device_name.clone();
+            playback.device.id = p.device_id.clone();
+            playback.is_playing = p.is_playing;
+            playback.device.volume_percent = p.volume;
+            playback.repeat_state = p.repeat_state;
+            playback.shuffle_state = p.shuffle_state;
+        }
+
+        Some(playback)
+    }
+
     /// gets the current playing track
     pub fn current_playing_track(&self) -> Option<&rspotify_model::FullTrack> {
         match self.playback {


Test patch: