spotify_cli/endpoints/playlists/
remove_items_from_playlist.rs

1/// Remove one or more items from a playlist.
2///
3/// See: https://developer.spotify.com/documentation/web-api/reference/remove-tracks-playlist
4use crate::http::api::SpotifyApi;
5use crate::http::client::HttpError;
6use crate::http::endpoints::Endpoint;
7use serde_json::Value;
8
9/// Remove all instances of the given URIs from a playlist
10pub async fn remove_items_from_playlist(
11    client: &SpotifyApi,
12    playlist_id: &str,
13    uris: &[String],
14) -> Result<Option<Value>, HttpError> {
15    let tracks: Vec<serde_json::Value> = uris
16        .iter()
17        .map(|uri| serde_json::json!({ "uri": uri }))
18        .collect();
19
20    let body = serde_json::json!({
21        "tracks": tracks
22    });
23
24    client
25        .delete_json(&Endpoint::PlaylistTracks { id: playlist_id }.path(), &body)
26        .await
27}
28
29/// Remove items at specific positions from a playlist
30/// Takes a list of (uri, position) tuples
31/// Note: Positions must be provided in descending order to avoid index shifting issues
32pub async fn remove_items_at_positions(
33    client: &SpotifyApi,
34    playlist_id: &str,
35    items: &[(String, usize)],
36) -> Result<Option<Value>, HttpError> {
37    // Sort by position descending - remove from end first to avoid index shifting
38    let mut sorted_items: Vec<(String, usize)> = items.to_vec();
39    sorted_items.sort_by(|a, b| b.1.cmp(&a.1));
40
41    // Send each item separately to avoid the URI-based removal behavior
42    let tracks: Vec<serde_json::Value> = sorted_items
43        .iter()
44        .map(|(uri, pos)| serde_json::json!({ "uri": uri, "positions": [*pos] }))
45        .collect();
46
47    let body = serde_json::json!({
48        "tracks": tracks
49    });
50
51    client
52        .delete_json(&Endpoint::PlaylistTracks { id: playlist_id }.path(), &body)
53        .await
54}