Create random Spotify playlist with bash, curl and the Spotify API
This post was automagically converted from Blogger. There may be dragons. Check any code you copy.
Untinkered with this script will
- add 140 random songs from the source-playlist(s) to the destination-playlist
- keep track of added files
- keep artist-separation at 50 songs (some songs “featuring” other artists might slip through)
- filter out titles with “bad” words, “live in”, “live at”, “remix”, “shitty version” … etc.
- filter out artists
- filter out songs by id
You need a access-token with enough permissions (scopes) to modify playlists. I created one with AAAALL the scopes for my personal use so I’m not entirely sure which ones are really needed.
On to the script …
#!/bin/bash
### figure out some stuff
dir="${BASH_SOURCE%/*}"
[[ ! -d "$dir" ]] && dir="$PWD"
me=$(basename -s .sh $0)
# SOURCE
source_playlists=(
"spotify:user:[username]:playlist:[playlist_id_here]" #MyAwesomeGiantPlaylist
"spotify:user:[username]:playlist:[another_id_here]" #AnotherGiantPlaylist
)
# DESTINATION
pluri_dest="spotify:user:[username_here]:playlist:[playlist_id_here]"
dir="$dir/$me"
mkdir -p $dir
playlist_file="$dir/$me.playlist"
payload_file="$dir/$me.payload"
filter_file="$dir/$me.filter" # avoid randomly adding files recently added
sfilter_file="$dir/$me.sfilter" # avoid adding explicitly "filtered" songs
wfilter_file="$dir/$me.wfilter" # avoid adding songs with "filtered" words
afilter_file="$dir/$me.afilter" # avoid adding songs with "filtered" artists
artist_sep=50
number_recent=0
addtorecent=true
# ------------------------------------------------------------------------------------------- ^_^ --
setAccessToken() {
access_token=$(cat ~/.config/.spotify_access_token)
}
touchFiles() {
touch $filter_file
touch $sfilter_file
touch $wfilter_file
touch $afilter_file
touch $payload_file
}
truncateFiles() {
tail -n $number_recent $filter_file > $filter_file.tmp
mv $filter_file.tmp $filter_file
}
updateListSpotify() {
[ -f $playlist_file.tmp ] && rm $playlist_file.tmp
for uri in "${source_playlists[@]}"; do
IFS=":" read -r foo foo playlist_source_user foo playlist_source_id <<< "$uri"
next="https://api.spotify.com/v1/users/$playlist_source_user/playlists/$playlist_source_id/tracks?offset=0&limit=100"
# keep GETTING while $next is not "null"
while [ "$next" != "null" ]; do
next="$next&fields=next,items(track(uri,name,artists(name)))"
echo "NEXT: $next"
output=$(curl -s -X GET \\
"$next" \\
-H "Authorization: Bearer $access_token")
jq -r '.items[].track | .uri + "|" + .name + "|" + .artists[0].name' <<< $output >> $playlist_file.tmp
next=$(jq -r .next <<< $output)
done
done
mv $playlist_file.tmp $playlist_file
number_recent=$(( $(wc -l < $playlist_file) - 1000 ))
}
addSong() {
local b=true
local len=$(wc -l < $playlist_file)
while $b; do
b=false
local song=$(cat $playlist_file | sed -n $[RANDOM % $len]p)
local id=$(echo $song | cut -d\\| -f1)
local title=$(echo $song | cut -d\\| -f2)
local artist=$(echo $song | cut -d\\| -f3)
# if song id is in recent- or static filter
# or song title has words we don't like
# or artist had song added in last n songs
# or artist is not supposed to be added
# we go again ...
$b || grep -qi "$id" $filter_file $sfilter_file && b=true
$b || grep -qi -Ff $wfilter_file <<< "$title" && b=true
$b || tail -n $artist_sep $filter_file | cut -d\\| -f3 | grep -qi "$artist" && b=true
$b || grep -qi "$artist" $afilter_file && b=true
done
$addtorecent && echo $song >> $filter_file
echo "\\"$id\\"" >> $payload_file.tmp
}
loopAdd() {
local i=0
local length=$1
while (( $i < $length )); do
addSong
let i++
done
jq --slurp '{ "uris" : . }' < $payload_file.tmp > $payload_file
rm $payload_file.tmp
}
playlistDo() {
## Replace : PUT
## Add : POST
curl -i -X $1 \\
"https://api.spotify.com/v1/users/$playlist_dest_user/playlists/$playlist_dest_id/tracks" \\
-H "Authorization: Bearer $access_token" \\
-H "Content-Type:application/json" \\
--data @$payload_file
}
## DO STUFF
setAccessToken
updateListSpotify
touchFiles
# ToDo: make this less shit.
loopAdd 100
playlistDo PUT # 100 files max at once
loopAdd 40
playlistDo POST # ... so we add 40 more here.
truncateFiles
exit 0