- 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