const axios = require("axios"); const logger = require("../logging"); const fs = require("fs/promises"); const fss = require("fs"); const Song = require("../../models/song"); class SpotifyTracker { #config = null; #token = null; #cache = []; #auth = null; provider = "spotify"; constructor(config, token = null) { this.#config = config; this.#token = token; this.#auth = new Buffer.from(config.client_id + ':' + config.client_secret).toString('base64'); } get name() { return this.#config.name; } get scrobblerNames() { return this.#config.scrobblers; } async poll(useCache = false) { if (this.#token == null) return []; if (useCache) return this.#cache; if (this.#token.expires_at < Date.now() + 300) await this.#refreshTokenIfNeeded(); try { const response = await axios.get("https://api.spotify.com/v1/me/player/currently-playing", { headers: { "Authorization": "Bearer " + this.#token.access_token } } ); if (!response.data) { this.#cache = []; return this.#cache; } this.#cache = [this.#transform(response.data, this.#config.name)]; return this.#cache; } catch (ex) { logger.error(ex, "Failed to get currently playing data from Spotify."); return []; } } 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); } #transform(data, source) { 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, source, "spotify"); } async #refreshTokenIfNeeded() { if (!this.#token || this.#token.expires_at && this.#token.expires_at - Date.now() > 900) return false; const response = await axios.post("https://accounts.spotify.com/api/token", { client_id: this.#config.client_id, refresh_token: this.#token.refresh_token, grant_type: "refresh_token" }, { headers: { "Authorization": "Basic " + this.#auth, "Content-Type": "application/x-www-form-urlencoded" } } ); const data = response.data; data["expires_at"] = Date.now() + data["expires_in"] * 1000; if (!data["refresh_token"]) data["refresh_token"] = this.#token.refresh_token; this.#token = data; await fs.writeFile("credentials.spotify.json", JSON.stringify(data)); logger.debug("Updated access token for Spotify."); return true; } } module.exports = SpotifyTracker;