In this guide I will explain how to create a cloudflare worker that returns the current song you are listening to on spotify.
You may of seen my use of it on root of my site:
This great guide by Ben Wiz covers exactly this.
make sure you add the scopes
user-read-currently-playing
anduser-read-playback-position
Once you have the client_id
, client_secret
, and refresh_token
, set the enviroment variable REFRESH_TOKEN
with the value being the refresh_token
.
Additionally add the enviroment variable CLIENT_ENCODED
with the value of <client_id>:<client_secret>
base64 encoded.
make sure all you are base64 encoding is the
client_id
andclient_secret
seperated by a single colon!
The access_token
generated from the refresh token is only valid for an hour, so it needs to keep being refreshed. This will be done with a cron trigger.
Add a cron trigger to your cloudflare worker that runs every 30 minutes
or */30 * * * *
The worker needs to hold onto the access_token
it generates every 30 minutes.
Create a KV Namespace with the name SPOTIFY
and add it to the KV Namespace bindings of your worker.
Located in the settings for your worker, below enviroment variables.
Hit the Quick edit button on your cloudflare worker and paste in this:
addEventListener("fetch", event => {
event.respondWith(handleRequest(event.request).catch(
(err) => JsonResponse({"error": err})
))
})
addEventListener("scheduled", event => {
event.waitUntil(
handleSchedule(event.scheduledTime)
)
})
async function handleSchedule(scheduledDate) {
await RefreshAccessToken();
}
async function handleRequest(request) {
const data = await NowPlaying();
return JsonResponse(data);
}
const REFRESH_URL = "https://accounts.spotify.com/api/token?grant_type=refresh_token&refresh_token=";
const NOW_PLAYING_URL = "https://api.spotify.com/v1/me/player/currently-playing";
const NO_DATA = {
artist: "",
artist_url: "",
url: "",
track: "",
image: "",
is_playing: false,
progress: 0,
track_length: 0,
ok: false,
}
function JsonResponse(data) {
return new Response(JSON.stringify(data), {
headers: {
"Access-Control-Allow-Origin": "*",
"content-type": "application/json;charset=UTF-8"
}
})
}
function ExtractData(json) {
try {
const {
progress_ms: progress = 0,
item: {
album: {
images: [
{ url: image }
]
},
artists: [
{
name: artist,
external_urls: {
spotify: artist_url
}
}
],
external_urls: { spotify: url },
duration_ms: track_length,
name: track,
},
is_playing,
} = json;
return {
artist,
artist_url,
url,
track,
image,
is_playing,
progress,
track_length,
ok: true,
}
} catch (_) {
return NO_DATA
}
}
async function RefreshAccessToken() {
const resp = await fetch(REFRESH_URL + REFRESH_TOKEN, {
method: 'POST',
headers: {
Authorization: `Basic ${CLIENT_ENCODED}`,
'Content-Type': 'application/x-www-form-urlencoded'
},
});
const { access_token } = await resp.json();
await SPOTIFY.put("ACCESS_TOKEN", access_token);
return access_token;
}
async function NowPlaying() {
const ACCESS_TOKEN = await SPOTIFY.get("ACCESS_TOKEN");
const resp = await fetch(NOW_PLAYING_URL, {
method: "GET",
headers: {
"Authorization": `Bearer ${ACCESS_TOKEN}`,
"Accept": "application/json",
"Content-Type": "application/json"
},
});
// Status 204 - No Content success
if (!resp.ok || resp.status === 204) {
return NO_DATA;
}
const json = await resp.json();
return ExtractData(json);
}
Now make a request to your worker to make sure it works. The code is simple and you can modify/edit the code to better suite your needs.