diff --git a/proto/library.proto b/proto/library.proto index 4be63f6..353cec5 100644 --- a/proto/library.proto +++ b/proto/library.proto @@ -11,6 +11,7 @@ service Library { rpc DeletePlaylist(DeletePlaylistRequest) returns (google.protobuf.Empty); rpc AddTrackToPlaylist(AddTrackToPlaylistRequest) returns (google.protobuf.Empty); rpc RemoveTrackFromPlaylist(RemoveTrackFromPlaylistRequest) returns (google.protobuf.Empty); + rpc SwapTracks(SwapTracksRequest) returns (TrackList); } message TrackList { @@ -56,3 +57,9 @@ message RemoveTrackFromPlaylistRequest { uint32 playlist_id = 1; uint32 track_rank = 2; } + +message SwapTracksRequest { + uint32 playlist_id = 1; + uint32 a_rank = 2; + uint32 b_rank = 3; +} diff --git a/src/database/tracks.rs b/src/database/tracks.rs index 19dc52a..ca8cd8b 100644 --- a/src/database/tracks.rs +++ b/src/database/tracks.rs @@ -340,3 +340,31 @@ pub async fn remove_track_from_playlist( Ok(len == 1) } + +pub async fn swap_playlist_tracks( + pool: &Pool, + playlist_id: u32, + a_rank: u32, + b_rank: u32, +) -> Result { + let manager = pool.get().await.unwrap(); + let mut connection = manager.lock().unwrap(); + + let tx = connection.transaction()?; + + tx.execute( + "UPDATE playlist_tracks SET rank = CASE WHEN rank = ?2 THEN - ?3 - 1 ELSE - ?2 - 1 END WHERE playlist_id = ?1 AND rank IN (?2, ?3)", + [playlist_id, a_rank, b_rank], + )?; + + let len = tx.execute("UPDATE playlist_tracks SET rank = - rank - 1 WHERE playlist_id = ?1 AND rank IN (- ?2 - 1, - ?3 - 1)", [playlist_id, a_rank, b_rank])?; + + if len != 2 { + tx.rollback()?; + return Ok(false); + } + + tx.commit()?; + + Ok(true) +} diff --git a/src/library.rs b/src/library.rs index 5c9ab34..9f08975 100644 --- a/src/library.rs +++ b/src/library.rs @@ -8,14 +8,14 @@ use walkdir::{DirEntry, WalkDir}; use crate::{ checksum::generate_hash, database::tracks::{ - add_track_to_playlist, create_playlist, delete_playlist, get_playlists, get_tracks, - insert_tracks, remove_track_from_playlist, + add_track_to_playlist, create_playlist, delete_playlist, get_playlist, get_playlists, + get_tracks, insert_tracks, remove_track_from_playlist, swap_playlist_tracks, }, music::metadata::{extract_track_data, TrackMetadata}, proto::library::{ library_server::Library, AddTrackToPlaylistRequest, CreatePlaylistRequest, CreatePlaylistResponse, DeletePlaylistRequest, ListPlaylistsResponse, - RemoveTrackFromPlaylistRequest, Track, TrackList, + RemoveTrackFromPlaylistRequest, SwapTracksRequest, Track, TrackList, }, state::GrooveState, }; @@ -139,6 +139,33 @@ impl Library for LibraryService { Ok(Response::new(())) } + + async fn swap_tracks( + &self, + request: Request, + ) -> Result, Status> { + let input = request.get_ref(); + + let Ok(success) = + swap_playlist_tracks(&self.pool, input.playlist_id, input.a_rank, input.b_rank).await + else { + return Err(Status::internal("")); + }; + + if !success { + return Err(Status::internal("")); + } + + let Ok(playlist) = get_playlist(&self.pool, input.playlist_id).await else { + return Err(Status::internal("")); + }; + + let response = TrackList { + tracks: playlist.tracks, + }; + + Ok(Response::new(response)) + } } pub async fn index_path(path: PathBuf, db: &Pool, path_id: u64) -> Result<(), rusqlite::Error> {