Skip to content

Instantly share code, notes, and snippets.

@anonymous1184
Last active November 11, 2022 13:26
Show Gist options
  • Save anonymous1184/5a2d38e43a5233fa03024e9441a9ff79 to your computer and use it in GitHub Desktop.
Save anonymous1184/5a2d38e43a5233fa03024e9441a9ff79 to your computer and use it in GitHub Desktop.
Spotify API
[SPOTIFY]
port = 1234
client_id = 0123456789abcdf0123456789abcdf
client_secret = 0123456789abcdf0123456789abcdf
scopes = user-read-currently-playing user-read-playback-state
access_token =
refresh_token =
expires_at =

; Version: 2022.11.11.1
; About: https://redd.it/on9gb5
#Include %A_LineFile%\..\Socket.ahk
#Include %A_LineFile%\..\WinHttpRequest.ahk
class Spotify_Api {
__New(Ini, Section := "SPOTIFY") {
IniRead buffer, % Ini, % Section
if (!buffer)
throw Exception("Invalid configuration file.", -1)
for _,line in StrSplit(buffer, "`n", "`n") {
parts := StrSplit(line, "=",, 2)
this["_" parts[1]] := parts[2]
}
if (!this._client_id || !this._client_secret)
throw Exception("Client ID/Secret error.", -1, Ini)
if (!this._port)
throw Exception("Port not defined.", -1, Ini)
else if (this._port < 1024 && !A_IsAdmin)
throw Exception("Invalid port for HTTP server.", -1, Ini)
this._ini := Ini
this._section := Section
this.http := new WinHttpRequest()
if (!this._access_token)
this.Auth()
while (!this._access_token)
Sleep 50
}
Auth() {
server := new SocketTCP()
server.OnAccept := ObjBindMethod(this, "_Socket")
server.Bind(["0.0.0.0", this._port])
server.Listen()
this._access_token := ""
redirect := this.http.encodeURIComponent("http://localhost:" this._port "/")
Run % "https://accounts.spotify.com/authorize"
. "?client_id=" this._client_id
. "&redirect_uri=" redirect
. "&response_type=code"
. "&scope=" RegExReplace(this._scopes, "\h+", "%20")
}
Rest(Method, Endpoint, Body := "", Headers := "") {
this._Refresh()
if (!IsObject(Headers))
Headers := {}
Headers.Authorization := "Bearer " this._access_token
http := ObjBindMethod(this.http, Method)
try
response := http.Call(Endpoint, Body, Headers)
catch
return {"error":true, "error_description":"Couldn't complete request."}
try
return response.Json
catch
return {"error":true, "error_description":"JSON parse error."}
}
; Private
_Access(Code) {
ep := "https://accounts.spotify.com/api/token"
body := { "client_id": this._client_id
, "client_secret": this._client_secret
, "code": Code
, "grant_type": "authorization_code"
, "redirect_uri": "http://localhost:" this._port "/" }
try {
response := this.http.Post(ep, body).Json
if (!response.HasKey("access_token"))
throw
} catch e {
throw Exception("Couldn't get authorization code.", -1)
}
this._Persist("access_token", response.access_token)
this._Persist("expires_at", this._Epoch() + response.expires_in)
if (response.refresh_token)
this._Persist("refresh_token", response.refresh_token)
}
_Epoch() {
epoch := A_NowUTC
epoch -= 19700101, Seconds
return epoch
}
_Persist(Key, Value) {
this["_" Key] := Value
IniWrite % " " Value, % this._ini, % this._section, % Key
}
_Refresh() {
epoch := this._Epoch()
if (epoch < this._expires_at)
return
ep := "https://accounts.spotify.com/api/token"
body := { "client_id": this._client_id
, "client_secret": this._client_secret
, "grant_type": "refresh_token"
, "refresh_token": this._refresh_token }
try {
response := this.http.Post(ep, body).Json
if (!response.HasKey("access_token"))
throw
} catch e {
this.Auth()
return
}
this._Persist("access_token", response.access_token)
this._Persist("expires_at", epoch + response.expires_in)
if (response.refresh_token)
this._Persist("refresh_token", response.refresh_token)
}
_Socket(Instance) {
conn := Instance.Accept()
request := conn.RecvText()
request := StrSplit(request, A_Space,, 3)[2]
isError := InStr(request, "error")
RegExMatch(request, "code=\K[^&]+", code)
if (code || isError) {
txt := "HTTP/1.1 200 OK" "`r`n"
txt .= "Connection: Close" "`r`n"
txt .= "Content-Type: text/html" "`r`n"
txt .= "`r`n"
; Content in quirks mode
txt .= "<title>Spotify Authorization</title>"
txt .= "<h1>You can close this tab now.</h1>"
} else {
txt := "HTTP/1.1 204 No Content" "`r`n"
txt .= "Connection: Close" "`r`n"
txt .= "`r`n"
}
conn.SendText(txt)
conn.Disconnect()
if (code)
this._Access(code)
if (code || isError)
Instance.Disconnect()
if (isError)
throw Exception("Permission not granted, can't continue.", -1)
}
__Delete() {
ObjRelease(this.http)
this.http := ""
}
}

; Version: 2022.11.11.1
; About: https://redd.it/on9gb5
#Include %A_LineFile%\..\Spotify_Api.ahk
class Spotify_Status extends Spotify_Api {
CurrentlyPlaying() {
endpoint := "https://api.spotify.com/v1/me/player/currently-playing"
response := this.Rest("GET", endpoint)
if (response.HasKey("error")) {
MsgBox 0x40010, Error, CurrentlyPlaying(), % response.error_description
Exit
}
return response
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment