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

Issue:
shortcuts for moving items in a playlist? 
first of all, epic work! really love it, it only takes 40-55mb ram, i have image & notify enabled, orig. spotify uses up to 800mb, sometimes even more ... crazy

i would wish me a feature, i'm change the order of my songs quite alot (at least at the beginning), would  it be possible to move the songs around?
something like:
"ctrl-arrowUp" & "ctrl-arrowDown" or with shift? or dwm-style with "j" & "k" or whatever makes sense to you ;)

thanks for all the work which went into this project, as soon as i get my pay, i'll sent you a coffee ;)

cheers
hardy


Discussion / hints:
maybe you should setup a "buy me a coffee", i'm sure, you made a few people quite happy ;)
Hi @deltahoch3, thank you for the kind words and great to hear that you like this project. 

Regarding "buy me a coffee", I don't really need one lol. I only work on this project whenever I feel like I want to. I would prefer contributions instead xD as I couldn't work on all the requested features.

Fix patch:
diff --git a/README.md b/README.md
index 88a1edd6..38c76e17 100644
--- a/README.md
+++ b/README.md
@@ -372,6 +372,8 @@ List of supported commands:
 | `SortTrackByDuration`          | sort the track table (if any) by track's duration                       | `s d`              |
 | `SortTrackByAddedDate`         | sort the track table (if any) by track's added date                     | `s D`              |
 | `ReverseOrder`                 | reverse the order of the track table (if any)                           | `s r`              |
+| `MovePlaylistItemUp`           | move playlist item up one position                                      | `C-k`              |
+| `MovePlaylistItemDown`         | move playlist item down one position                                    | `C-j`              |
 
 To add new shortcuts or modify the default shortcuts, please refer to the [keymaps section](docs/config.md#keymaps) in the configuration documentation.
 
diff --git a/spotify_player/src/client/mod.rs b/spotify_player/src/client/mod.rs
index 5c393558..bdd35691 100644
--- a/spotify_player/src/client/mod.rs
+++ b/spotify_player/src/client/mod.rs
@@ -375,6 +375,23 @@ impl Client {
                     state.player.write().queue = Some(queue);
                 }
             }
+            ClientRequest::ReorderPlaylistItems {
+                playlist_id,
+                insert_index,
+                range_start,
+                range_length,
+                snapshot_id,
+            } => {
+                self.reorder_playlist_items(
+                    state,
+                    playlist_id,
+                    insert_index,
+                    range_start,
+                    range_length,
+                    snapshot_id.as_deref(),
+                )
+                .await?;
+            }
         };
 
         tracing::info!(
@@ -866,6 +883,46 @@ impl Client {
         Ok(())
     }
 
+    /// reorder items in a playlist
+    pub async fn reorder_playlist_items(
+        &self,
+        state: &SharedState,
+        playlist_id: PlaylistId<'_>,
+        insert_index: usize,
+        range_start: usize,
+        range_length: Option<usize>,
+        snapshot_id: Option<&str>,
+    ) -> Result<()> {
+        let insert_before = match insert_index > range_start {
+            true => insert_index + 1,
+            false => insert_index,
+        };
+
+        self.spotify
+            .playlist_reorder_items(
+                playlist_id.clone(),
+                Some(range_start as i32),
+                Some(insert_before as i32),
+                range_length.map(|range_length| range_length as u32),
+                snapshot_id,
+            )
+            .await?;
+
+        // After making a reorder request, update the playlist in-memory data stored inside the app caches.
+        if let Some(Context::Playlist { tracks, .. }) = state
+            .data
+            .write()
+            .caches
+            .context
+            .get_mut(&playlist_id.uri())
+        {
+            let track = tracks.remove(range_start);
+            tracks.insert(insert_index, track);
+        }
+
+        Ok(())
+    }
+
     /// adds a Spotify item to current user's library.
     /// Before adding new item, the function checks if that item already exists in the library
     /// to avoid adding a duplicated item.
diff --git a/spotify_player/src/command.rs b/spotify_player/src/command.rs
index 4887df90..eeecb7bd 100644
--- a/spotify_player/src/command.rs
+++ b/spotify_player/src/command.rs
@@ -66,6 +66,9 @@ pub enum Command {
     SortTrackByDuration,
     SortTrackByAddedDate,
     ReverseTrackOrder,
+
+    MovePlaylistItemUp,
+    MovePlaylistItemDown,
 }
 
 #[derive(Debug, Copy, Clone)]
@@ -196,6 +199,8 @@ impl Command {
             Self::SortTrackByDuration => "sort the track table (if any) by track's duration",
             Self::SortTrackByAddedDate => "sort the track table (if any) by track's added date",
             Self::ReverseTrackOrder => "reverse the order of the track table (if any)",
+            Self::MovePlaylistItemUp => "move playlist item up one position",
+            Self::MovePlaylistItemDown => "move playlist item down one position",
         }
     }
 }
diff --git a/spotify_player/src/config/keymap.rs b/spotify_player/src/config/keymap.rs
index 9f755afd..2baa6948 100644
--- a/spotify_player/src/config/keymap.rs
+++ b/spotify_player/src/config/keymap.rs
@@ -274,6 +274,14 @@ impl Default for KeymapConfig {
                     key_sequence: "s r".into(),
                     command: Command::ReverseTrackOrder,
                 },
+                Keymap {
+                    key_sequence: "C-k".into(),
+                    command: Command::MovePlaylistItemUp,
+                },
+                Keymap {
+                    key_sequence: "C-j".into(),
+                    command: Command::MovePlaylistItemDown,
+                },
             ],
         }
     }
diff --git a/spotify_player/src/event/mod.rs b/spotify_player/src/event/mod.rs
index 81e42d8c..caea5e20 100644
--- a/spotify_player/src/event/mod.rs
+++ b/spotify_player/src/event/mod.rs
@@ -50,6 +50,13 @@ pub enum ClientRequest {
     AddTrackToQueue(TrackId<'static>),
     AddTrackToPlaylist(PlaylistId<'static>, TrackId<'static>),
     DeleteTrackFromPlaylist(PlaylistId<'static>, TrackId<'static>),
+    ReorderPlaylistItems {
+        playlist_id: PlaylistId<'static>,
+        insert_index: usize,
+        range_start: usize,
+        range_length: Option<usize>,
+        snapshot_id: Option<String>,
+    },
     AddToLibrary(Item),
     DeleteFromLibrary(ItemId),
     ConnectDevice(Option<String>),
diff --git a/spotify_player/src/event/window.rs b/spotify_player/src/event/window.rs
index 4a1faf36..29956518 100644
--- a/spotify_player/src/event/window.rs
+++ b/spotify_player/src/event/window.rs
@@ -185,6 +185,58 @@ pub fn handle_command_for_track_table_window(
         Command::AddSelectedItemToQueue => {
             client_pub.send(ClientRequest::AddTrackToQueue(tracks[id].id.clone()))?;
         }
+        Command::MovePlaylistItemUp => {
+            if let PageState::Context {
+                id: Some(ContextId::Playlist(playlist_id)),
+                ..
+            } = ui.current_page()
+            {
+                if id > 0
+                    && data.user_data.playlists.iter().any(|playlist| {
+                        &playlist.id == playlist_id
+                            && Some(&playlist.owner.1)
+                                == data.user_data.user.as_ref().map(|user| &user.id)
+                    })
+                {
+                    let insert_index = id - 1;
+                    client_pub.send(ClientRequest::ReorderPlaylistItems {
+                        playlist_id: playlist_id.clone_static(),
+                        insert_index,
+                        range_start: id,
+                        range_length: None,
+                        snapshot_id: None,
+                    })?;
+                    ui.current_page_mut().select(insert_index);
+                };
+            }
+            ui.popup = None;
+        }
+        Command::MovePlaylistItemDown => {
+            if let PageState::Context {
+                id: Some(ContextId::Playlist(playlist_id)),
+                ..
+            } = ui.current_page()
+            {
+                let insert_index = id + 1;
+                if insert_index < tracks.len()
+                    && data.user_data.playlists.iter().any(|playlist| {
+                        &playlist.id == playlist_id
+                            && Some(&playlist.owner.1)
+                                == data.user_data.user.as_ref().map(|user| &user.id)
+                    })
+                {
+                    client_pub.send(ClientRequest::ReorderPlaylistItems {
+                        playlist_id: playlist_id.clone_static(),
+                        insert_index,
+                        range_start: id,
+                        range_length: None,
+                        snapshot_id: None,
+                    })?;
+                    ui.current_page_mut().select(insert_index);
+                };
+            }
+            ui.popup = None;
+        }
         _ => return Ok(false),
     }
     Ok(true)


Test patch: