You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
197 lines
6.9 KiB
Bash
197 lines
6.9 KiB
Bash
4 years ago
|
#!/usr/bin/env bash
|
||
|
# vim:ts=4:sts=4:sw=4:et
|
||
|
#
|
||
|
# Author: Hari Sekhon
|
||
|
# Date: 2020-07-24 19:05:25 +0100 (Fri, 24 Jul 2020)
|
||
|
#
|
||
2 years ago
|
# https://github.com/HariSekhon/Spotify-Playlists
|
||
4 years ago
|
#
|
||
|
# License: see accompanying Hari Sekhon LICENSE file
|
||
|
#
|
||
|
# If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish
|
||
|
#
|
||
2 years ago
|
# https://www.linkedin.com/in/HariSekhon
|
||
4 years ago
|
#
|
||
|
|
||
|
set -euo pipefail
|
||
|
[ -n "${DEBUG:-}" ] && set -x
|
||
|
srcdir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
|
|
||
1 year ago
|
# shellcheck disable=SC1090,SC1091
|
||
4 years ago
|
. "$srcdir/lib/spotify.sh"
|
||
|
|
||
|
# shellcheck disable=SC2034,SC2154
|
||
|
usage_description="
|
||
|
Deletes Spotify URIs from a given playlist
|
||
|
|
||
4 years ago
|
Playlist must be specified as the first argument and can be either a Spotify playlist ID or a full playlist name (see spotify_playlists.sh)
|
||
4 years ago
|
|
||
|
Can take file(s) with URIs as arguments or read from standard input for chaining with other tools
|
||
|
|
||
4 years ago
|
Input formats can be IDs or any standard Spotify URI format, eg:
|
||
|
|
||
|
spotify:track:<ID>
|
||
|
https://open.spotify.com/track/<ID>
|
||
|
<ID>
|
||
|
|
||
4 years ago
|
or can be prefixed with track position in the playlist (zero-indexed) if you only want to delete a single instance of the song (useful when removing only duplicates), separated by either space:
|
||
4 years ago
|
|
||
|
<track_position> spotify:track:<ID>
|
||
|
<track_position> https://open.spotify.com/track/<ID>
|
||
|
<track_position> <ID>
|
||
|
|
||
4 years ago
|
or a colon:
|
||
|
|
||
|
<track_position>:spotify:track:<ID>
|
||
|
<track_position>:https://open.spotify.com/track/<ID>
|
||
|
<track_position>:<ID>
|
||
|
|
||
4 years ago
|
|
||
4 years ago
|
Useful for chaining with other tools (eg. spotify_playlist_tracks_uri.sh / spotify_search_uri.sh in this repo, or
|
||
|
tracks_already_in_playlists.sh in the HariSekhon/Spotify-Playlists github repo) or loading from saved spotify format
|
||
|
playlists (eg. TODO playlists dumped by spotify_backup*.sh / spotify_playlist_tracks_uri.sh)
|
||
|
|
||
4 years ago
|
Caveat: won't check the tracks are already in the playlist, will simply fire batch delete API calls and count the number of tracks requested to be removed, so repeated runs of the same URIs fed in will give the same results, which might mislead you to thinking they weren't remove the first time around, when they've already been removed
|
||
|
|
||
|
|
||
4 years ago
|
$usage_playlist_help
|
||
4 years ago
|
|
||
4 years ago
|
$usage_auth_help
|
||
4 years ago
|
"
|
||
|
|
||
|
# used by usage() in lib/utils.sh
|
||
|
# shellcheck disable=SC2034
|
||
|
usage_args="<playlist_name_or_id> [<file1> <file2> ...]"
|
||
|
|
||
|
help_usage "$@"
|
||
|
|
||
|
min_args 1 "$@"
|
||
|
|
||
|
playlist="$1"
|
||
|
shift || :
|
||
|
|
||
|
# requires authorized token
|
||
|
export SPOTIFY_PRIVATE=1
|
||
|
|
||
|
spotify_token
|
||
|
|
||
|
# this script returns the ID if it's already in the correct format, otherwise queries and returns the playlist ID for the playlist
|
||
|
playlist_id="$(SPOTIFY_PLAYLIST_EXACT_MATCH=1 "$srcdir/spotify_playlist_name_to_id.sh" "$playlist")"
|
||
|
|
||
|
playlist_name="$("$srcdir/spotify_playlist_id_to_name.sh" "$playlist_id")"
|
||
|
|
||
|
# playlist ID obtained from 'spotify_playlists.sh'
|
||
|
url_path="/v1/playlists/$playlist_id/tracks"
|
||
|
|
||
|
count=0
|
||
|
|
||
4 years ago
|
snapshot_id=""
|
||
|
|
||
4 years ago
|
# Takes track IDs or track position:ID for more specific deletes
|
||
4 years ago
|
delete_from_playlist(){
|
||
|
if [ $# -lt 1 ]; then
|
||
|
echo "Error: no IDs passed to delete_from_playlist()" >&2
|
||
|
exit 1
|
||
|
fi
|
||
|
local uri_array=""
|
||
4 years ago
|
local track_position
|
||
|
local id
|
||
4 years ago
|
for id in "$@"; do
|
||
4 years ago
|
if [[ "$id" =~ ^[[:digit:]]+: ]]; then
|
||
|
# extract first column for track position
|
||
|
track_position="${id%%:*}"
|
||
4 years ago
|
# don't try to calculate this, the numbers aren't as predicted during testing, use snapshot consistency instead
|
||
|
#((track_position-=count))
|
||
|
# keep zero-indexed for compatability with other tools - no longer necessary, they return zero indexed now
|
||
4 years ago
|
#((track_position-=1)) # convert one-indexed (eg. from grep) to zero-indexed for Spotify API
|
||
4 years ago
|
id="${id#*:}"
|
||
|
# requires explicit track URI type since could also be episodes added to playlist
|
||
|
uri_array+="{\"uri\": \"spotify:track:$id\", \"positions\": [$track_position]}, "
|
||
4 years ago
|
if [ -z "$snapshot_id" ]; then
|
||
|
# get the Snapshot ID of the playlist before we start deleting for consistency and use this for all rounds of deletions
|
||
|
snapshot_id="$("$srcdir/spotify_api.sh" "${url_path%/tracks}?fields=snapshot_id" | jq -r '.snapshot_id')"
|
||
|
fi
|
||
4 years ago
|
else
|
||
|
# requires explicit track URI type since could also be episodes added to playlist
|
||
|
uri_array+="{\"uri\": \"spotify:track:$id\"}, "
|
||
|
fi
|
||
4 years ago
|
done
|
||
|
uri_array="${uri_array%, }"
|
||
|
timestamp "removing ${#@} tracks from playlist '$playlist_name'"
|
||
4 years ago
|
json_payload='{"tracks": '"[$uri_array]"
|
||
|
#if [ -n "$snapshot_id" ] && [ "$snapshot_id" != null ]; then
|
||
|
# let it send null and fail as we should never get back null
|
||
|
if [ -n "$snapshot_id" ]; then
|
||
|
json_payload+=", \"snapshot_id\": \"$snapshot_id\""
|
||
|
fi
|
||
|
json_payload+="}"
|
||
4 years ago
|
local output
|
||
4 years ago
|
output="$("$srcdir/spotify_api.sh" "$url_path" -X DELETE -d "$json_payload")"
|
||
4 years ago
|
#die_if_error_field "$output"
|
||
4 years ago
|
warn_if_error_field "$output"
|
||
4 years ago
|
((count+=${#@}))
|
||
4 years ago
|
# don't take the new snapshot ID - use the one from before we start deleting for consistency otherwise the second round of deletes will fail
|
||
|
#snapshot_id="$(jq -r '.snapshot_id' <<< "$output")"
|
||
|
#if is_blank "$snapshot_id"; then
|
||
|
# die "Spotify API returned blank snapshot id, please investigate with DEBUG=1 mode"
|
||
|
#fi
|
||
|
#if [ "$snapshot_id" = null ]; then
|
||
|
# die "Spotify API returned snapshot_id '$snapshot_id', please investigate with DEBUG=1 mode"
|
||
|
#fi
|
||
4 years ago
|
}
|
||
|
|
||
4 years ago
|
delete_URIs_from_playlist(){
|
||
4 years ago
|
declare -a ids
|
||
|
ids=()
|
||
|
while read -r track_uri; do
|
||
4 years ago
|
track_position=""
|
||
4 years ago
|
if [[ "$track_uri" =~ ^[[:digit:]]+[:[:space:]]+ ]]; then
|
||
4 years ago
|
# extract first column for track position
|
||
4 years ago
|
track_position="${track_uri%%[:[:space:]]*}"
|
||
4 years ago
|
|
||
|
# remove first column of track position
|
||
4 years ago
|
track_uri="${track_uri#*[:[:space:]]}"
|
||
4 years ago
|
|
||
|
# strip any remaining leading whitespace without subshelling to sed
|
||
4 years ago
|
track_uri="${track_uri#"${track_uri%%[!:[:space:]]*}"}"
|
||
4 years ago
|
fi
|
||
4 years ago
|
if is_blank "$track_uri"; then
|
||
|
continue
|
||
|
fi
|
||
|
if is_local_uri "$track_uri"; then
|
||
|
continue
|
||
|
fi
|
||
4 years ago
|
if [ -n "${SPOTIFY_DELETE_IGNORE_IRREGULAR_IDS:-}" ]; then
|
||
|
id="$(validate_spotify_uri "$track_uri" || :)"
|
||
4 years ago
|
if ! is_spotify_playlist_id "$id"; then
|
||
4 years ago
|
timestamp "skipping deleting irregular ID '$id'"
|
||
|
continue
|
||
|
fi
|
||
|
else
|
||
|
id="$(validate_spotify_uri "$track_uri")"
|
||
|
fi
|
||
4 years ago
|
|
||
4 years ago
|
if [ -n "$track_position" ]; then
|
||
|
ids+=("$track_position:$id")
|
||
|
else
|
||
|
ids+=("$id")
|
||
|
fi
|
||
4 years ago
|
|
||
4 years ago
|
if [ "${#ids[@]}" -eq 100 ]; then
|
||
4 years ago
|
delete_from_playlist "${ids[@]}"
|
||
4 years ago
|
sleep 1
|
||
4 years ago
|
ids=()
|
||
|
fi
|
||
4 years ago
|
done
|
||
4 years ago
|
|
||
4 years ago
|
if [ "${#ids[@]}" -gt 0 ]; then
|
||
|
delete_from_playlist "${ids[@]}"
|
||
|
fi
|
||
4 years ago
|
}
|
||
|
|
||
|
for filename in "${@:-/dev/stdin}"; do
|
||
4 years ago
|
delete_URIs_from_playlist < "$filename"
|
||
4 years ago
|
done
|
||
|
|
||
|
timestamp "$count tracks deleted from playlist '$playlist_name'"
|