Added song data class. Added trackers.
This commit is contained in:
		
							
								
								
									
										16
									
								
								models/song.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								models/song.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | class Song { | ||||||
|  |   constructor(id, name, album, artists, year, duration, progress, session, state, source) { | ||||||
|  |     this.id = id; | ||||||
|  |     this.name = name; | ||||||
|  |     this.album = album; | ||||||
|  |     this.artists = artists; | ||||||
|  |     this.year = year; | ||||||
|  |     this.duration = duration; | ||||||
|  |     this.progress = progress; | ||||||
|  |     this.session = session; | ||||||
|  |     this.state = state; | ||||||
|  |     this.source = source; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | module.exports = Song; | ||||||
							
								
								
									
										13
									
								
								services/trackers/AggregateTracker.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								services/trackers/AggregateTracker.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | class AggregateTracker { | ||||||
|  |   constructor(trackers) { | ||||||
|  |     this._trackers = trackers; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   poll() { | ||||||
|  |     const media = [] | ||||||
|  |     for (let tracker of this._trackers) | ||||||
|  |       media.push.apply(tracker.poll()); | ||||||
|  |  | ||||||
|  |     return media; | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										59
									
								
								services/trackers/PlexTracker.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								services/trackers/PlexTracker.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | |||||||
|  | const axios = require("axios"); | ||||||
|  | const Song = require("../../models/song"); | ||||||
|  |  | ||||||
|  | class PlexTracker { | ||||||
|  |   constructor(config) { | ||||||
|  |     this._config = config; | ||||||
|  |     this._cache = [] | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   async poll(useCache = false) { | ||||||
|  |     if (!this._config.token || !this._config.url) | ||||||
|  |       return []; | ||||||
|  |     if (useCache) | ||||||
|  |       return cache; | ||||||
|  |  | ||||||
|  |     const response = await axios.get(this._config.url + "/status/sessions", { | ||||||
|  |       headers: { | ||||||
|  |         "Accept": "application/json", | ||||||
|  |         "X-Plex-Token": this._config.token | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     if (!response.data.MediaContainer?.Metadata) { | ||||||
|  |       cache = []; | ||||||
|  |       return cache; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const filtered = response.data.MediaContainer?.Metadata.filter(m => this.#filter(m)); | ||||||
|  |     cache = filtered.map(this.#transform); | ||||||
|  |     return cache; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   #filter(data) { | ||||||
|  |     if (!this._config.filters || this._config.filters.length == 0) | ||||||
|  |       return true; | ||||||
|  |  | ||||||
|  |     for (let filter of this._config.filters) { | ||||||
|  |       if (filter.library && !filter.library.some(l => l == data.librarySectionTitle)) | ||||||
|  |         continue; | ||||||
|  |       if (filter.ip && !filter.ip.some(l => l == data.address)) | ||||||
|  |         continue; | ||||||
|  |       if (filter.deviceId && !filter.deviceId.some(l => l == data.machineIdentifier)) | ||||||
|  |         continue; | ||||||
|  |       if (filter.platform && !filter.platform.some(l => l == data.Player.platform)) | ||||||
|  |         continue; | ||||||
|  |       if (filter.product && !filter.product.some(l => l == data.Player.product)) | ||||||
|  |         continue; | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   #transform(data) { | ||||||
|  |     const id = data.guid.substring(data.guid.lastIndexOf('/') + 1); | ||||||
|  |     return new Song(id, data.title, data.parentTitle, data.grandparentTitle, data.parentYear, data.duration, data.viewOffset, data.sessionKey, data.Player.state, "plex"); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | module.exports = PlexTracker; | ||||||
							
								
								
									
										83
									
								
								services/trackers/SpotifyTracker.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								services/trackers/SpotifyTracker.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | |||||||
|  | const axios = require("axios"); | ||||||
|  | const logger = require("./logging"); | ||||||
|  | const fs = require("fs/promises"); | ||||||
|  | const Song = require("../../models/song"); | ||||||
|  |  | ||||||
|  | class SpotifyTracker { | ||||||
|  |   constructor(config, token) { | ||||||
|  |     this._token = token; | ||||||
|  |     this._cache = null; | ||||||
|  |     this._config = config; | ||||||
|  |     this._auth = new Buffer.from(config.client_id + ':' + config.client_secret).toString('base64'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   async poll(useCache = false) { | ||||||
|  |     if (token == null) | ||||||
|  |       return null; | ||||||
|  |     if (useCache) | ||||||
|  |       return cache; | ||||||
|  |  | ||||||
|  |     if (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 " + token["access_token"] | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       ); | ||||||
|  |  | ||||||
|  |       if (!response.data) { | ||||||
|  |         cache = null; | ||||||
|  |         return null; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       cache = [this.#transform(response.data)]; | ||||||
|  |       return cache; | ||||||
|  |     } catch (ex) { | ||||||
|  |       logger.error(ex, "Failed to get currently playing data from Spotify."); | ||||||
|  |       return []; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   #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() { | ||||||
|  |     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; | ||||||
		Reference in New Issue
	
	Block a user