2024-12-04 23:18:35 -05:00
|
|
|
const axios = require("axios");
|
2024-12-05 03:26:31 -05:00
|
|
|
const logger = require("../logging");
|
2024-12-04 23:18:35 -05:00
|
|
|
const fs = require("fs/promises");
|
2024-12-05 03:26:31 -05:00
|
|
|
const fss = require("fs");
|
2024-12-04 23:18:35 -05:00
|
|
|
const Song = require("../../models/song");
|
|
|
|
|
|
|
|
class SpotifyTracker {
|
2024-12-05 03:26:31 -05:00
|
|
|
#config = null;
|
|
|
|
#token = null;
|
|
|
|
#cache = null;
|
|
|
|
#auth = null;
|
|
|
|
|
|
|
|
constructor(config, token = null) {
|
|
|
|
this.#config = config;
|
|
|
|
this.#token = token;
|
|
|
|
this.#cache = null;
|
|
|
|
this.#auth = new Buffer.from(config.client_id + ':' + config.client_secret).toString('base64');
|
2024-12-04 23:18:35 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
async poll(useCache = false) {
|
2024-12-05 03:26:31 -05:00
|
|
|
if (this.#token == null)
|
|
|
|
return [];
|
2024-12-04 23:18:35 -05:00
|
|
|
if (useCache)
|
2024-12-05 03:26:31 -05:00
|
|
|
return this.#cache;
|
2024-12-04 23:18:35 -05:00
|
|
|
|
2024-12-05 03:26:31 -05:00
|
|
|
if (this.#token.expires_at < Date.now() + 300)
|
2024-12-04 23:18:35 -05:00
|
|
|
await this.#refreshTokenIfNeeded();
|
|
|
|
|
|
|
|
try {
|
|
|
|
const response = await axios.get("https://api.spotify.com/v1/me/player/currently-playing",
|
|
|
|
{
|
|
|
|
headers: {
|
2024-12-05 03:26:31 -05:00
|
|
|
"Authorization": "Bearer " + this.#token.access_token
|
2024-12-04 23:18:35 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!response.data) {
|
2024-12-05 03:26:31 -05:00
|
|
|
this.#cache = [];
|
|
|
|
return this.#cache;
|
2024-12-04 23:18:35 -05:00
|
|
|
}
|
|
|
|
|
2024-12-05 03:26:31 -05:00
|
|
|
this.#cache = [this.#transform(response.data)];
|
|
|
|
return this.#cache;
|
2024-12-04 23:18:35 -05:00
|
|
|
} catch (ex) {
|
|
|
|
logger.error(ex, "Failed to get currently playing data from Spotify.");
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-05 03:26:31 -05:00
|
|
|
async loadCredentials() {
|
|
|
|
if (!fss.existsSync("credentials.spotify.json")) {
|
|
|
|
logger.info("No Spotify credentials found.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const content = await fs.readFile("credentials.spotify.json", "utf-8");
|
|
|
|
this.#token = JSON.parse(content);
|
|
|
|
}
|
|
|
|
|
2024-12-04 23:18:35 -05:00
|
|
|
#transform(data) {
|
|
|
|
const item = data.item;
|
|
|
|
const artists = item.artists.map(a => a.name);
|
|
|
|
const year = null;
|
|
|
|
const state = data.is_playing ? "playing" : "paused";
|
|
|
|
return new Song(item.id, item.name, item.album.name, artists, year, item.duration_ms, data.progress_ms, "spotify", state, "spotify");
|
|
|
|
}
|
|
|
|
|
|
|
|
async #refreshTokenIfNeeded() {
|
2024-12-05 03:26:31 -05:00
|
|
|
if (!this.#token || this.#token.expires_at && this.#token.expires_at - Date.now() > 900)
|
2024-12-04 23:18:35 -05:00
|
|
|
return false;
|
|
|
|
|
|
|
|
const response = await axios.post("https://accounts.spotify.com/api/token",
|
|
|
|
{
|
2024-12-05 03:26:31 -05:00
|
|
|
client_id: this.#config.client_id,
|
|
|
|
refresh_token: this.#token.refresh_token,
|
2024-12-04 23:18:35 -05:00
|
|
|
grant_type: "refresh_token"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
headers: {
|
2024-12-05 03:26:31 -05:00
|
|
|
"Authorization": "Basic " + this.#auth,
|
2024-12-04 23:18:35 -05:00
|
|
|
"Content-Type": "application/x-www-form-urlencoded"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
const data = response.data;
|
|
|
|
data["expires_at"] = Date.now() + data["expires_in"] * 1000;
|
|
|
|
if (!data["refresh_token"])
|
2024-12-05 03:26:31 -05:00
|
|
|
data["refresh_token"] = this.#token.refresh_token;
|
2024-12-04 23:18:35 -05:00
|
|
|
|
2024-12-05 03:26:31 -05:00
|
|
|
this.#token = data;
|
2024-12-04 23:18:35 -05:00
|
|
|
await fs.writeFile("credentials.spotify.json", JSON.stringify(data));
|
|
|
|
logger.debug("Updated access token for Spotify.");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = SpotifyTracker;
|