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

Issue:
support saving cover images without `image` feature
**Describe the bug**
After application update, I get an error in the playback window stating "No playback found. Please make sure there is a running Spotify devlice and try to connect to one using the 'SwitchDevice' command. Which when I run, shows no devices. This worked perfectly fine yesterday before the update, using my own app ID like outlined in the spotify-connect setup. Using arch with the AUR build of spotify-player.

**Expected behaviour**
Song is played through the player like intended

**Log and backtrace**
2024-05-30T18:52:59.493062Z  INFO spotify_player: Configurations: Configs { app_config: AppConfig { theme: "dracula", client_id: "", client_port: 8080, player_event_hook_command: None, playback_format: "{track} • {artists}\n{album}\n{metadata}", tracks_playback_limit: 50, proxy: None, ap_port: None, app_refresh_duration_in_ms: 32, playback_refresh_duration_in_ms: 0, page_size_in_rows: 20, play_icon: "▶", pause_icon: "▌▌", liked_icon: "♥", border_type: Plain, progress_bar_type: Rectangle, playback_window_position: Top, playback_window_width: 6, enable_cover_image_cache: true, default_device: "spotify-player", device: DeviceConfig { name: "TofuBox", device_type: "speaker", volume: 85, bitrate: 320, audio_cache: false, normalization: false } }, keymap_config: KeymapConfig { keymaps: [Keymap { key_sequence: KeySequence { keys: [None(Char('n'))] }, command: NextTrack }, Keymap { key_sequence: KeySequence { keys: [None(Char('p'))] }, command: PreviousTrack }, Keymap { key_sequence: KeySequence { keys: [None(Char('.'))] }, command: PlayRandom }, Keymap { key_sequence: KeySequence { keys: [None(Char(' '))] }, command: ResumePause }, Keymap { key_sequence: KeySequence { keys: [Ctrl(Char('r'))] }, command: Repeat }, Keymap { key_sequence: KeySequence { keys: [Alt(Char('r'))] }, command: ToggleFakeTrackRepeatMode }, Keymap { key_sequence: KeySequence { keys: [Ctrl(Char('s'))] }, command: Shuffle }, Keymap { key_sequence: KeySequence { keys: [None(Char('+'))] }, command: VolumeUp }, Keymap { key_sequence: KeySequence { keys: [None(Char('-'))] }, command: VolumeDown }, Keymap { key_sequence: KeySequence { keys: [None(Char('_'))] }, command: Mute }, Keymap { key_sequence: KeySequence { keys: [None(Char('>'))] }, command: SeekForward }, Keymap { key_sequence: KeySequence { keys: [None(Char('<'))] }, command: SeekBackward }, Keymap { key_sequence: KeySequence { keys: [None(Enter)] }, command: ChooseSelected }, Keymap { key_sequence: KeySequence { keys: [None(Char('r'))] }, command: RefreshPlayback }, Keymap { key_sequence: KeySequence { keys: [None(Char('/'))] }, command: Search }, Keymap { key_sequence: KeySequence { keys: [None(Char('z'))] }, command: Queue }, Keymap { key_sequence: KeySequence { keys: [Ctrl(Char('z'))] }, command: AddSelectedItemToQueue }, Keymap { key_sequence: KeySequence { keys: [None(Char('Z'))] }, command: AddSelectedItemToQueue }, Keymap { key_sequence: KeySequence { keys: [Ctrl(Char(' '))] }, command: ShowActionsOnSelectedItem }, Keymap { key_sequence: KeySequence { keys: [None(Char('g')), None(Char('a'))] }, command: ShowActionsOnSelectedItem }, Keymap { key_sequence: KeySequence { keys: [None(Char('a'))] }, command: ShowActionsOnCurrentTrack }, Keymap { key_sequence: KeySequence { keys: [None(Tab)] }, command: FocusNextWindow }, Keymap { key_sequence: KeySequence { keys: [None(BackTab)] }, command: FocusPreviousWindow }, Keymap { key_sequence: KeySequence { keys: [None(Char('T'))] }, command: SwitchTheme }, Keymap { key_sequence: KeySequence { keys: [None(Char('D'))] }, command: SwitchDevice }, Keymap { key_sequence: KeySequence { keys: [None(Char('u')), None(Char('p'))] }, command: BrowseUserPlaylists }, Keymap { key_sequence: KeySequence { keys: [None(Char('u')), None(Char('a'))] }, command: BrowseUserFollowedArtists }, Keymap { key_sequence: KeySequence { keys: [None(Char('u')), None(Char('A'))] }, command: BrowseUserSavedAlbums }, Keymap { key_sequence: KeySequence { keys: [None(Char('g')), None(Char(' '))] }, command: CurrentlyPlayingContextPage }, Keymap { key_sequence: KeySequence { keys: [None(Char('g')), None(Char('t'))] }, command: TopTrackPage }, Keymap { key_sequence: KeySequence { keys: [None(Char('g')), None(Char('r'))] }, command: RecentlyPlayedTrackPage }, Keymap { key_sequence: KeySequence { keys: [None(Char('g')), None(Char('y'))] }, command: LikedTrackPage }, Keymap { key_sequence: KeySequence { keys: [None(Char('g')), None(Char('l'))] }, command: LibraryPage }, Keymap { key_sequence: KeySequence { keys: [None(Char('g')), None(Char('s'))] }, command: SearchPage }, Keymap { key_sequence: KeySequence { keys: [None(Char('g')), None(Char('b'))] }, command: BrowsePage }, Keymap { key_sequence: KeySequence { keys: [None(Backspace)] }, command: PreviousPage }, Keymap { key_sequence: KeySequence { keys: [Ctrl(Char('q'))] }, command: PreviousPage }, Keymap { key_sequence: KeySequence { keys: [None(Char('O'))] }, command: OpenSpotifyLinkFromClipboard }, Keymap { key_sequence: KeySequence { keys: [None(Char('?'))] }, command: OpenCommandHelp }, Keymap { key_sequence: KeySequence { keys: [Ctrl(Char('h'))] }, command: OpenCommandHelp }, Keymap { key_sequence: KeySequence { keys: [None(Char('q'))] }, command: Quit }, Keymap { key_sequence: KeySequence { keys: [Ctrl(Char('c'))] }, command: Quit }, Keymap { key_sequence: KeySequence { keys: [None(Esc)] }, command: ClosePopup }, Keymap { key_sequence: KeySequence { keys: [None(Char('j'))] }, command: SelectNextOrScrollDown }, Keymap { key_sequence: KeySequence { keys: [Ctrl(Char('n'))] }, command: SelectNextOrScrollDown }, Keymap { key_sequence: KeySequence { keys: [None(Down)] }, command: SelectNextOrScrollDown }, Keymap { key_sequence: KeySequence { keys: [None(Char('k'))] }, command: SelectPreviousOrScrollUp }, Keymap { key_sequence: KeySequence { keys: [Ctrl(Char('p'))] }, command: SelectPreviousOrScrollUp }, Keymap { key_sequence: KeySequence { keys: [None(Up)] }, command: SelectPreviousOrScrollUp }, Keymap { key_sequence: KeySequence { keys: [None(PageUp)] }, command: PageSelectPreviousOrScrollUp }, Keymap { key_sequence: KeySequence { keys: [Ctrl(Char('b'))] }, command: PageSelectPreviousOrScrollUp }, Keymap { key_sequence: KeySequence { keys: [None(PageDown)] }, command: PageSelectNextOrScrollDown }, Keymap { key_sequence: KeySequence { keys: [Ctrl(Char('f'))] }, command: PageSelectNextOrScrollDown }, Keymap { key_sequence: KeySequence { keys: [None(Char('g')), None(Char('g'))] }, command: SelectFirstOrScrollToTop }, Keymap { key_sequence: KeySequence { keys: [None(Home)] }, command: SelectFirstOrScrollToTop }, Keymap { key_sequence: KeySequence { keys: [None(Char('G'))] }, command: SelectLastOrScrollToBottom }, Keymap { key_sequence: KeySequence { keys: [None(End)] }, command: SelectLastOrScrollToBottom }, Keymap { key_sequence: KeySequence { keys: [None(Char('s')), None(Char('t'))] }, command: SortTrackByTitle }, Keymap { key_sequence: KeySequence { keys: [None(Char('s')), None(Char('a'))] }, command: SortTrackByArtists }, Keymap { key_sequence: KeySequence { keys: [None(Char('s')), None(Char('A'))] }, command: SortTrackByAlbum }, Keymap { key_sequence: KeySequence { keys: [None(Char('s')), None(Char('d'))] }, command: SortTrackByDuration }, Keymap { key_sequence: KeySequence { keys: [None(Char('s')), None(Char('D'))] }, command: SortTrackByAddedDate }, Keymap { key_sequence: KeySequence { keys: [None(Char('s')), None(Char('r'))] }, command: ReverseTrackOrder }, Keymap { key_sequence: KeySequence { keys: [Ctrl(Char('k'))] }, command: MovePlaylistItemUp }, Keymap { key_sequence: KeySequence { keys: [Ctrl(Char('j'))] }, command: MovePlaylistItemDown }, Keymap { key_sequence: KeySequence { keys: [None(Char('N'))] }, command: CreatePlaylist }] }, theme_config: ThemeConfig { themes: [Theme { name: "default", palette: Palette { background: None, foreground: None, black: Color { color: Black }, blue: Color { color: Blue }, cyan: Color { color: Cyan }, green: Color { color: Green }, magenta: Color { color: Magenta }, red: Color { color: Red }, white: Color { color: Gray }, yellow: Color { color: Yellow }, bright_black: Color { color: DarkGray }, bright_white: Color { color: White }, bright_red: Color { color: LightRed }, bright_magenta: Color { color: LightMagenta }, bright_green: Color { color: LightGreen }, bright_cyan: Color { color: LightCyan }, bright_blue: Color { color: LightBlue }, bright_yellow: Color { color: LightYellow } }, component_style: ComponentStyle { block_title: None, border: None, playback_track: None, playback_artists: None, playback_album: None, playback_metadata: None, playback_progress_bar: None, current_playing: None, page_desc: None, playlist_desc: None, table_header: None, selection: None } }, Theme { name: "dracula", palette: Palette { background: None, foreground: None, black: Color { color: Black }, blue: Color { color: Blue }, cyan: Color { color: Cyan }, green: Color { color: Green }, magenta: Color { color: Magenta }, red: Color { color: Red }, white: Color { color: Gray }, yellow: Color { color: Yellow }, bright_black: Color { color: DarkGray }, bright_white: Color { color: White }, bright_red: Color { color: LightRed }, bright_magenta: Color { color: LightMagenta }, bright_green: Color { color: LightGreen }, bright_cyan: Color { color: LightCyan }, bright_blue: Color { color: LightBlue }, bright_yellow: Color { color: LightYellow } }, component_style: ComponentStyle { block_title: None, border: None, playback_track: None, playback_artists: None, playback_album: None, playback_metadata: None, playback_progress_bar: None, current_playing: None, page_desc: None, playlist_desc: None, table_header: None, selection: None } }] }, cache_folder: "/home/bunta/.cache/spotify-player" }
2024-05-30T18:52:59.493110Z  INFO spotify_player::state::data: Loading Playlists data from /home/bunta/.cache/spotify-player/Playlists_cache.json...
2024-05-30T18:52:59.493355Z  INFO spotify_player::state::data: Successfully loaded Playlists data!
2024-05-30T18:52:59.493357Z  INFO spotify_player::state::data: Loading FollowedArtists data from /home/bunta/.cache/spotify-player/FollowedArtists_cache.json...
2024-05-30T18:52:59.493867Z  INFO spotify_player::state::data: Successfully loaded FollowedArtists data!
2024-05-30T18:52:59.493870Z  INFO spotify_player::state::data: Loading SavedAlbums data from /home/bunta/.cache/spotify-player/SavedAlbums_cache.json...
2024-05-30T18:52:59.496945Z  INFO spotify_player::state::data: Successfully loaded SavedAlbums data!
2024-05-30T18:52:59.496948Z  INFO spotify_player::state::data: Loading SavedTracks data from /home/bunta/.cache/spotify-player/SavedTracks_cache.json...
2024-05-30T18:52:59.552559Z  INFO spotify_player::state::data: Successfully loaded SavedTracks data!
2024-05-30T18:52:59.800779Z  INFO spotify_player::auth: Successfully used the cached credentials to create a new session!
2024-05-30T18:52:59.824800Z  INFO spotify_player::token: Getting new authentication token...
2024-05-30T18:52:59.859452Z  INFO spotify_player::token: Got new token: Token { access_token: "", expires_in: TimeDelta { secs: 3600, nanos: 0 }, expires_at: Some(2024-05-30T19:52:59.859443714Z), refresh_token: None, scopes: {} }
2024-05-30T18:52:59.945338Z  INFO spotify_player: No playback found on startup, trying to connect to an available device...
2024-05-30T18:52:59.945581Z  INFO spotify_player: Starting a client socket at 127.0.0.1:8080
2024-05-30T18:53:00.023272Z  INFO client_request{request=GetCurrentUser}: spotify_player::client: Successfully handled the client request, took: 77ms
2024-05-30T18:53:00.141955Z  INFO client_request{request=GetUserPlaylists}: spotify_player::client: Successfully handled the client request, took: 196ms
2024-05-30T18:53:00.227497Z  INFO client_request{request=GetUserFollowedArtists}: spotify_player::client: Successfully handled the client request, took: 281ms
2024-05-30T18:53:00.994133Z  WARN client_request{request=ConnectDevice}: spotify_player::client: No device found. Please make sure you already setup Spotify Connect support as described in https://github.com/aome510/spotify-player#spotify-connect.
2024-05-30T18:53:02.040931Z  WARN client_request{request=ConnectDevice}: spotify_player::client: No device found. Please make sure you already setup Spotify Connect support as described in https://github.com/aome510/spotify-player#spotify-connect.
2024-05-30T18:53:03.087858Z  WARN client_request{request=ConnectDevice}: spotify_player::client: No device found. Please make sure you already setup Spotify Connect support as described in https://github.com/aome510/spotify-player#spotify-connect.
2024-05-30T18:53:03.187384Z  INFO client_request{request=GetUserSavedAlbums}: spotify_player::client: Successfully handled the client request, took: 3241ms
2024-05-30T18:53:03.951409Z  INFO spotify_player::client::handlers: Current context ID (None) is different from the expected ID (Some(Tracks(TracksId { uri: "tracks:user-liked-tracks", kind: "Liked Tracks" }))), update the context state
2024-05-30T18:53:04.134058Z  WARN client_request{request=ConnectDevice}: spotify_player::client: No device found. Please make sure you already setup Spotify Connect support as described in https://github.com/aome510/spotify-player#spotify-connect.
2024-05-30T18:53:05.180055Z  WARN client_request{request=ConnectDevice}: spotify_player::client: No device found. Please make sure you already setup Spotify Connect support as described in https://github.com/aome510/spotify-player#spotify-connect.
2024-05-30T18:53:05.959074Z  INFO client_request{request=GetUserSavedTracks}: spotify_player::client: Successfully handled the client request, took: 6013ms
2024-05-30T18:53:06.231118Z  WARN client_request{request=ConnectDevice}: spotify_player::client: No device found. Please make sure you already setup Spotify Connect support as described in https://github.com/aome510/spotify-player#spotify-connect.
2024-05-30T18:53:07.291926Z  WARN client_request{request=ConnectDevice}: spotify_player::client: No device found. Please make sure you already setup Spotify Connect support as described in https://github.com/aome510/spotify-player#spotify-connect.
2024-05-30T18:53:07.629658Z ERROR 
spotify_player::client::handlers: Failed to handle client request: http error: status code 404 Not Found
2024-05-30T18:53:08.348564Z  WARN client_request{request=ConnectDevice}: spotify_player::client: No device found. Please make sure you already setup Spotify Connect support as described in https://github.com/aome510/spotify-player#spotify-connect.
2024-05-30T18:53:08.500652Z  INFO client_request{request=GetUserSavedTracks}: spotify_player::client: Successfully handled the client request, took: 5316ms
2024-05-30T18:53:09.391650Z  WARN client_request{request=ConnectDevice}: spotify_player::client: No device found. Please make sure you already setup Spotify Connect support as described in https://github.com/aome510/spotify-player#spotify-connect.
2024-05-30T18:53:10.439741Z  WARN client_request{request=ConnectDevice}: spotify_player::client: No device found. Please make sure you already setup Spotify Connect support as described in https://github.com/aome510/spotify-player#spotify-connect.
2024-05-30T18:53:10.439812Z  INFO client_request{request=ConnectDevice}: spotify_player::client: Successfully handled the client request, took: 10493ms

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Environment**

- OS: Arch 
- Application version: 0.18.2
- Application features: yay -S spotify-player

**Additional context**
I've also noticed over the past week or so, album art image cache's haven't been downloading. I have a script I made to display them in feh seperately, but for some reason that folder is no longer being updated even though the config/cache filepaths are correct.


Discussion / hints:
UPDATE: Installing the cargo package directly seemed to have fixed the playback issue, but I'm still not getting new image caches.
I don't know why you encountered the no playback found with AUR but image cache is only enabled when you have `image` feature enabled (disabled by default) and `enable_cover_image_cache` config option is true.
Strange, I've used this software for the past 6 months or so getting image cache's with the image feature off. 
![screenshot](https://github.com/aome510/spotify-player/assets/22734228/8d5e3eaa-d14c-4d89-850a-dd219ec3a1cb)
Is this possible to add this as a standard feature? I like to have the option to manage the album art myself without displaying it in the terminal. Regardless, thanks for the help and the cool software.

> Is this possible to add this as a standard feature?

Yeah, should be by making `enable_cover_image_cache` config option independent of `image` feature. 

Fix patch:
diff --git a/spotify_player/src/client/mod.rs b/spotify_player/src/client/mod.rs
index fa1ba1d5..757f010a 100644
--- a/spotify_player/src/client/mod.rs
+++ b/spotify_player/src/client/mod.rs
@@ -4,6 +4,8 @@ use std::{borrow::Cow, collections::HashMap, sync::Arc};
 use crate::config;
 use crate::{auth::AuthConfig, state::*};
 
+use std::io::Write;
+
 use anyhow::Context as _;
 use anyhow::Result;
 use librespot_core::session::Session;
@@ -1355,14 +1357,12 @@ impl Client {
         if !new_track {
             return Ok(());
         }
-        #[cfg(any(feature = "image", feature = "notify"))]
         self.handle_new_track_event(state).await?;
 
         Ok(())
     }
 
     // Handle new track event
-    #[cfg(any(feature = "image", feature = "notify"))]
     async fn handle_new_track_event(&self, state: &SharedState) -> Result<()> {
         let configs = config::get_config();
 
@@ -1384,11 +1384,13 @@ impl Client {
         .replace('/', ""); // remove invalid characters from the file's name
         let path = configs.cache_folder.join("image").join(path);
 
+        if configs.app_config.enable_cover_image_cache {
+            self.retrieve_image(url, &path, true).await?;
+        }
+
         #[cfg(feature = "image")]
         if !state.data.read().caches.images.contains_key(url) {
-            let bytes = self
-                .retrieve_image(url, &path, configs.app_config.enable_cover_image_cache)
-                .await?;
+            let bytes = self.retrieve_image(url, &path, false).await?;
             let image =
                 image::load_from_memory(&bytes).context("Failed to load image from memory")?;
             state
@@ -1401,16 +1403,10 @@ impl Client {
 
         // notify user about the playback's change if any
         #[cfg(feature = "notify")]
-        if configs.app_config.enable_notify {
-            // for Linux, ensure that the cached cover image is available to render the notification's thumbnail
-            #[cfg(all(unix, not(target_os = "macos")))]
-            self.retrieve_image(url, &path, true).await?;
-
-            if !configs.app_config.notify_streaming_only || self.stream_conn.lock().is_some() {
-                Self::notify_new_track(track, &path)?;
-            }
-            #[cfg(not(feature = "streaming"))]
-            Self::notify_new_track(track, &path, state)?;
+        if configs.app_config.enable_notify
+            && (!configs.app_config.notify_streaming_only || self.stream_conn.lock().is_some())
+        {
+            Self::notify_new_track(track, &path)?;
         }
 
         Ok(())
@@ -1488,13 +1484,15 @@ impl Client {
         let configs = config::get_config();
 
         n.appname("spotify_player")
-            .icon(cover_img_path.to_str().unwrap())
             .summary(&get_text_from_format_str(
                 &configs.app_config.notify_format.summary,
             ))
             .body(&get_text_from_format_str(
                 &configs.app_config.notify_format.body,
             ));
+        if cover_img_path.exists() {
+            n.icon(cover_img_path.to_str().context("valid cover_img_path")?);
+        }
         if configs.app_config.notify_timeout_in_secs > 0 {
             n.timeout(std::time::Duration::from_secs(
                 configs.app_config.notify_timeout_in_secs,
@@ -1507,17 +1505,14 @@ impl Client {
 
     /// Retrieve an image from a `url` or a cached `path`.
     /// If `saved` is specified, the retrieved image is saved to the cached `path`.
-    #[cfg(any(feature = "image", feature = "notify"))]
     async fn retrieve_image(
         &self,
         url: &str,
         path: &std::path::Path,
         saved: bool,
     ) -> Result<Vec<u8>> {
-        use std::io::Write;
-
         if path.exists() {
-            tracing::info!("Retrieving image from file: {}", path.display());
+            tracing::debug!("Retrieving image from file: {}", path.display());
             return Ok(std::fs::read(path)?);
         }
 


Test patch: