0
votes

I'm trying to set up a simple shortcut to add the current playing Spotify track to a spotify playlist to keep record of the ones I like when I hear them on the flow.

I'm using applescript that for.

What I've found so far is that there are Spotify APIs that should meet my needs, but i'm failing at the last step.

More precisely, since I want to modify a private playlist, this api (1) : https://developer.spotify.com/web-api/console/post-playlist-tracks/ demands an authorization token. This token can be retrieved manually, and so far so good, this Applescript is working :

-- Main

set userID to "XXXXX"
-- my real userID can be retrieved with https://developer.spotify.com/web-api/console/get-current-user/#complete

set SelectedPlaylistID to "YYYY"
-- my target playlist can be retrieved with https://developer.spotify.com/web-api/console/get-playlists/#complete

set BearerID to "ZZZZ"
-- ZZZZ can be retrieved manually on page (1) 

tell application "Spotify"
  set currentSpotifyID to id of current track as string
end tell

set currentlyPlayingTrack to trim_line(currentSpotifyID, "spotify:track:", 0)
set theURL to "'https://api.spotify.com/v1/users/" & userID & "/playlists/" & SelectedPlaylistID & "/tracks?uris=spotify%3Atrack%3A" & currentlyPlayingTrack & "' -H 'Accept: application/json' -H 'Authorization: Bearer " & BearerID & "'"
-- built from https://developer.spotify.com/web-api/console/post-playlist-tracks/#complete

set theCommand to "curl -X POST " & theURL
do shell script theCommand


-- trim subroutine; not the subject here; mentioned for completeness, you can skip it

on trim_line(this_text, trim_chars, trim_indicator)
-- 0 = beginning, 1 = end, 2 = both
set x to the length of the trim_chars
-- TRIM BEGINNING
if the trim_indicator is in {0, 2} then
    repeat while this_text begins with the trim_chars
        try
            set this_text to characters (x + 1) thru -1 of this_text as string
        on error
            -- the text contains nothing but the trim characters
            return ""
        end try
    end repeat
end if
-- TRIM ENDING
if the trim_indicator is in {1, 2} then
    repeat while this_text ends with the trim_chars
        try
            set this_text to characters 1 thru -(x + 1) of this_text as string
        on error
            -- the text contains nothing but the trim characters
            return ""
        end try
    end repeat
end if
return this_text
end trim_line

Now comes the tricky part.

BearerID is temporary so I need to retrieve it manually each time, which defeats the purpose of a shortcut.

I would like to retrieve "ZZZZ", BearerID, automatically.

There's a tutorial on authorization https://developer.spotify.com/web-api/authorization-guide/#disqus_thread which frankly, I can't sort out.

I thought I had a solution in implementing request 4) in Authorization Code Flow with :

set base64encodedCredentials to "WWWW"
-- where WWWW is the result of 
-- $ echo - n 'CCCC:SSSS' | base64
-- "CCCC" being my Spotify ClientID and "SSSS" my Client Secret as given on https://beta.developer.spotify.com/dashboard/applications/

set BearerID to "ZZZZ"
-- where ZZZZ is the expired token

set authorizationURL to "-H 'Authorization: Basic " & base64encodedCredentials & "' -d grant_type=authorization_code -d code=" & BearerID & "-d redirect_uri=https%3A%2F%2Fwww.foo.com%2Fauth https://accounts.spotify.com/api/token"

set theAuthorization to "curl -X 'POST' " & authorizationURL

do shell script theAuthorization   

but I get an error

"{\"error\":\"invalid_client\",\"error_description\":\"Invalid client\"}"

So basically I haven't found the correct way to automatically retrieve the authorization token in Spotify.

Any idea ?

1

1 Answers

0
votes

Looks like you're using parts of the authorization code flow and parts of the client credentials flow.

If you want to use client credentials (which will allow you to get general Spotify data but not user specific data), you need to set your grant_type to client_credentials and remove the redirect uri. Change that line to:

set authorizationURL to "-H 'Authorization: Basic " & base64encodedCredentials & "' -d grant_type=client_credentials https://accounts.spotify.com/api/token"

If you want to use authorization code (which will allow you to get user specific data), you need to follow a more complicated flow. It's important to note that this flow requires user interaction to grant your app the scopes you request. This is quite difficult to do from a script, but here is an example in bash: https://gist.github.com/hughrawlinson/358afa57a04c8c0f1ce4f1fd86604a73

Luckily, you only need to get user action once, because once you have the refresh token you can refresh the user's access token in the background (Step 7 in the authorization guide).

The reason your current approach doesn't work is that what you're referring to as a BearerID is actually an Access Token, but you're using it as a "code" for this flow (Step 4 in the authorization guide you mentioned).

I would recommend setting up a small server for this, which can run in the background and handle the authentication and refresh. See examples here with authorization code flow in Python and in Node.js.

Let me know if you have any questions!