diff --git a/.gitignore b/.gitignore index d7d4895..bd1fb64 100644 --- a/.gitignore +++ b/.gitignore @@ -43,4 +43,5 @@ ex.json test.json api.json svgs.html +lyrics.js diff --git a/.prettierrc b/.prettierrc index f2e4215..00d0fb2 100644 --- a/.prettierrc +++ b/.prettierrc @@ -4,5 +4,6 @@ "trailingComma": "all", "bracketSpacing": true, "arrowParens": "always", - "proseWrap": "always" + "proseWrap": "always", + "endOfLine": "auto" } \ No newline at end of file diff --git a/MMM-OnSpotify.js b/MMM-OnSpotify.js index 274cd5b..18a55b5 100644 --- a/MMM-OnSpotify.js +++ b/MMM-OnSpotify.js @@ -36,6 +36,8 @@ Module.register("MMM-OnSpotify", { deviceFilter: [], deviceFilterExclude: false, filterNoticeSubtitle: true, + // Changes the language sent to Spotify + language: config.language, updateInterval: { isPlaying: 1, @@ -143,7 +145,7 @@ Module.register("MMM-OnSpotify", { typeof this.config.theming.experimentalCSSOverridesForMM2 === "object"; //////////////////////////////////////////////////////////////////////////////////////////// - this.version = "3.3.0"; + this.version = "3.3.1"; //////////////////////////////////////////////////////////////////////////////////////////// this.displayUser = @@ -205,6 +207,7 @@ Module.register("MMM-OnSpotify", { accessToken: this.config.accessToken, refreshToken: this.config.refreshToken, }, + language: this.config.language, }); this.updateFetchingLoop(this.config.updateInterval[this.lastStatus]); }, @@ -364,8 +367,9 @@ Module.register("MMM-OnSpotify", { this.userData = { ...payload, subtitleOverride: - this.playerData && this.config.filteredDeviceNotice ? - this.playerData.notAllowedDevice : false, + this.playerData && this.config.filteredDeviceNotice + ? this.playerData.notAllowedDevice + : false, }; this.userData.age = Date.now(); this.requestUserData = false; @@ -664,8 +668,7 @@ Module.register("MMM-OnSpotify", { ? (this.config.theming.textAnimations = this.config.textAnimations) : null; typeof this.config.scrollAnimations === "boolean" - ? (this.config.theming.scrollAnimations = - this.config.scrollAnimations) + ? (this.config.theming.scrollAnimations = this.config.scrollAnimations) : null; typeof this.config.spotifyVectorAnimations === "boolean" ? (this.config.theming.spotifyVectorAnimations = diff --git a/README.md b/README.md index 8e306ef..7976126 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ Once you finish, you are all set with the basic configuration. Scroll down to se deviceFilter: [], deviceFilterExclude: false, filterNoticeSubtitle: true, + language: config.language, // Update intervals [SEE BELOW] isPlaying: 1, isEmpty: 2, @@ -129,6 +130,7 @@ experimentalCSSOverridesForMM2: [ | deviceFilter
`list[]` | List of device names to filter from the module, by default, its an inclusion list, you can change this using `deviceFilterExclude` (making it an exclusion list). When a filtered device plays `displayWhenEmpty` shows. Example: `["Sonos Bedroom", "DESKTOP-ABCD123"]` | | deviceFilterExclude
`false` | Inverts the `deviceFilter` list, making it exclude devices | | filterNoticeSubtitle
`true`| Changes the subtitle of `displayWhenEmpty`, to not show a false status if the `deviceFilter` is set | +| language
`config.language`| Changes the language in which titles of songs are shown. When it is not set, it depends on `config.language`. Example: `en-US` (Or `false` if you prefer the default api response) | ### Polling Intervals: | Key | Description | diff --git a/auth/authorization.js b/auth/authorization.js index a2e1668..3cfa7ac 100644 --- a/auth/authorization.js +++ b/auth/authorization.js @@ -11,10 +11,9 @@ * and Raywoo implementation: * https://github.com/raywo/MMM-NowPlayingOnSpotify/tree/master/authorization */ - +const querystring = require("querystring"); const express = require("express"); const request = require("request"); -const querystring = require("querystring"); const cookieParser = require("cookie-parser"); const bodyParser = require("body-parser"); @@ -75,9 +74,9 @@ function getAuthOptions(code) { grant_type: "authorization_code", }, headers: { - Authorization: - "Basic " + - Buffer.from(client_id + ":" + client_secret).toString("base64"), + Authorization: `Basic ${Buffer.from( + `${client_id}:${client_secret}`, + ).toString("base64")}`, }, json: true, }; @@ -88,19 +87,18 @@ function redirectToSuccess(response, body) { let refresh_token = body.refresh_token; // we can also pass the token to the browser to make requests from there response.redirect( - "/#" + - querystring.stringify({ - access_token: access_token, - refresh_token: refresh_token, - client_id: client_id, - client_secret: client_secret, - }), + `/#${querystring.stringify({ + access_token: access_token, + refresh_token: refresh_token, + client_id: client_id, + client_secret: client_secret, + })}`, ); } let stateKey = "spotify_auth_state"; let app = express(); -app.use(express.static(__dirname + "/client")); +app.use(express.static(`${__dirname}/client`)); app.use(cookieParser()); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); diff --git a/node_helper.js b/node_helper.js index dd64050..e2f9f0f 100644 --- a/node_helper.js +++ b/node_helper.js @@ -30,7 +30,7 @@ module.exports = NodeHelper.create({ case "SET_CREDENTIALS_REFRESH": this.fetcher = new SpotifyFetcher(payload); this.fetchSpotifyData("PLAYER"); - this.preferences = payload.preferences + this.preferences = payload.preferences; break; case "REFRESH_PLAYER": this.fetchSpotifyData("PLAYER"); @@ -61,7 +61,12 @@ module.exports = NodeHelper.create({ switch (type) { case "PLAYER": // CASE S1 The data is OK and the target is a filtered device - if (data && data.device && data.device.name && !this.isAllowedDevice(data.device.name)) { + if ( + data && + data.device && + data.device.name && + !this.isAllowedDevice(data.device.name) + ) { this.sendSocketNotification("PLAYER_DATA", { statusIsPlayerEmpty: true, statusIsNewSong: false, @@ -69,15 +74,15 @@ module.exports = NodeHelper.create({ statusIsChangeToMediaPlayer: false, statusPlayerUpdating: false, statusIsDeviceChange: false, - notAllowedDevice: data.device.name + notAllowedDevice: data.device.name, }); this.lastMediaUri = "empty"; this.lastPlayerStatus = false; this.lastPlayerStatusCount = 0; this.lastDeviceName = "unknown"; - break + break; } - // CASE S2 The data is OK and the target is in a private session + // CASE S2 The data is OK and the target is in a private session if (data && data.device && data.device.is_private_session) { let payload = { /* Player data */ @@ -97,7 +102,7 @@ module.exports = NodeHelper.create({ statusPlayerUpdating: false, statusIsDeviceChange: this.lastDeviceName !== data.device.name ? true : false, - notAllowedDevice: false + notAllowedDevice: false, }; this.sendSocketNotification("PLAYER_DATA", payload); this.lastMediaUri = "privatesession"; @@ -150,7 +155,7 @@ module.exports = NodeHelper.create({ statusPlayerUpdating: false, statusIsDeviceChange: this.lastDeviceName !== data.device.name ? true : false, - notAllowedDevice: false + notAllowedDevice: false, }; this.sendSocketNotification("PLAYER_DATA", payload); this.lastMediaUri = data.item.uri; @@ -169,7 +174,7 @@ module.exports = NodeHelper.create({ statusIsChangeToMediaPlayer: false, statusPlayerUpdating: true, statusIsDeviceChange: false, - notAllowedDevice: false + notAllowedDevice: false, }); this.lastPlayerStatusCount = this.lastPlayerStatusCount + 1; this.lastPlayerStatus = true; @@ -182,7 +187,7 @@ module.exports = NodeHelper.create({ statusIsChangeToMediaPlayer: false, statusPlayerUpdating: false, statusIsDeviceChange: false, - notAllowedDevice: false + notAllowedDevice: false, }); this.lastMediaUri = "empty"; this.lastPlayerStatus = false; @@ -197,7 +202,7 @@ module.exports = NodeHelper.create({ statusIsChangeToMediaPlayer: false, statusPlayerUpdating: false, statusIsDeviceChange: false, - notAllowedDevice: false + notAllowedDevice: false, }); this.lastMediaUri = "empty"; this.lastPlayerStatus = false; @@ -254,10 +259,14 @@ module.exports = NodeHelper.create({ }, isAllowedDevice: function (currentDevice) { - if (!this.preferences.deviceFilter || this.preferences.deviceFilter.length < 1) return true - return this.preferences.deviceFilterExclude ? - !this.preferences.deviceFilter.includes(currentDevice) : - this.preferences.deviceFilter.includes(currentDevice) + if ( + !this.preferences.deviceFilter || + this.preferences.deviceFilter.length < 1 + ) + return true; + return this.preferences.deviceFilterExclude + ? !this.preferences.deviceFilter.includes(currentDevice) + : this.preferences.deviceFilter.includes(currentDevice); }, processArtists: (artists) => artists.map((artist) => artist.name).join(", "), processImages: (images) => { diff --git a/package.json b/package.json index ac436e9..2f9b865 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mmm-onspotify", - "version": "3.3.0", + "version": "3.3.1", "description": "A MagicMirror2 module to display Spotify Connect data. Highly customizable. Allows DynamicTheming.", "repository": { "type": "git", diff --git a/translations/en.json b/translations/en.json index 3ff6669..df48104 100644 --- a/translations/en.json +++ b/translations/en.json @@ -3,17 +3,17 @@ "FILTERED_PLAYING": "Nothing is playing in this room.", "PRODUCT_WARNING": "The current account is not using Spotify Premium. This module requieres a Premium account.", "CONNECTION_WARNING": "There was a connection error. Slowing the polling of api.spotify/...", - "CONNECTION_ERROR": "Connection errors persists. Slowing the polling more. Hiding old player data.", - "VIBRANT_NOT_LOADED": "(vendor) Vibrant did not load correctly.", - "USER_CSS_ERROR": "There was an error extracting CSS information. (The duration of animations/transitions should be in \"ms\")", + "CONNECTION_ERROR": "Connection errors persist. Slowing the polling rate to seconds. Emptying player as the data is outdated.", + "VIBRANT_NOT_LOADED": "[VENDOR] Vibrant library did not load correctly.", + "USER_CSS_ERROR": "An error ocurred while extracting CSS information. (The duration of animations/transitions should be in \"ms\")", "STABLISHING_CONNECTION": "Connecting to Spotify...", "NOTHING_PLAYING": "Nothing is playing", "SPOTIFY_HEADER": "MMM-OnSpotify", - "DEBUG_COLORS": "Debug palettes are shown because is set to true", - "SPOTIFYCODE_EXPERIMENTAL": " is enabled. The api to generate codes is not public, and can end without prior notice. (https://github.com/Fabrizz/MMM-OnSpotify#other)", + "DEBUG_COLORS": " is enabled. The module will log Vibrant data when color processing is requested.", + "SPOTIFYCODE_EXPERIMENTAL": " is enabled. The API used is not documented, as its an Spotify service. This service could stop working at any time. (https://github.com/Fabrizz/MMM-OnSpotify#other)", "RESUME": "Resuming module", "SUSPEND": "Suspending module", - "LIVELYRICS_NOTICE": "MMM-LiveLyrics found! - Check MMM-LL logs to see more. (https://github.com/Fabrizz/MMM-LiveLyrics)", - "CSSOVERRIDE_NOTICE": "You are overriding default MM2 CSS variables. Check for spelling errors and contrast issues", - "CSSOVERRIDE_MALFORMED": "There was an error parsing your \"experimentalCSSOverridesForMM2\" array. Remove your custom config entry or check it for errors" + "LIVELYRICS_NOTICE": "MMM-LiveLyrics found! - LiveLyrics should log the connection to this module. (https://github.com/Fabrizz/MMM-LiveLyrics)", + "CSSOVERRIDE_NOTICE": "You are overriding default MM2 CSS variables. Check spelling errors (and contrast issues)", + "CSSOVERRIDE_MALFORMED": "There was an error parsing your \"experimentalCSSOverridesForMM2\" array. This entry should be an array, check the docs for more information." } \ No newline at end of file diff --git a/utils/SpotifyDomBuilder.js b/utils/SpotifyDomBuilder.js index 752f92d..f41ee35 100644 --- a/utils/SpotifyDomBuilder.js +++ b/utils/SpotifyDomBuilder.js @@ -1191,19 +1191,15 @@ class SpotifyDomBuilder { document .getElementById("VSNO-TARGET-SUBTITLE") .classList.remove("scroll"); - document.getElementById("VSNO-TARGET-TITLE").classList.remove("scroll"); + document + .getElementById("VSNO-TARGET-TITLE") + .classList.remove("scroll"); } } } catch (error) { - console.info( - `%c· MMM-OnSpotify %c %c[INFO]%c Unknown unsync, could not change scroller status`, - "background-color:#84CC16;color:black;border-radius:0.4em", - "", - "background-color:orange;color:black", - "", - ); + /* This effect is based on the current TS of the song, the module reference is going to always be some ms behind, + here we catch, any even if it never happens, the error thown if the player changes state */ } - } /* Utils */ diff --git a/utils/SpotifyFetcher.js b/utils/SpotifyFetcher.js index dd8e26a..8a68ceb 100644 --- a/utils/SpotifyFetcher.js +++ b/utils/SpotifyFetcher.js @@ -16,6 +16,7 @@ module.exports = class SpotifyFetcher { constructor(payload) { this.credentials = payload.credentials; this.preferences = payload.preferences; + this.language = payload.language; this.tokenExpiresAt = moment(); } @@ -40,6 +41,10 @@ module.exports = class SpotifyFetcher { requestData(type) { let sl = "v1/me/top/artists?limit=9"; + const headers = new Headers(); + headers.append("Authorization", `Bearer ${this.credentials.accessToken}`); + // TODO: Check if using &locale= (or &market=) has any different effect, as its not documented correctly + if (this.language) headers.append("Accept-Language", this.language); switch (type) { case "PLAYER": return fetch( @@ -47,9 +52,7 @@ module.exports = class SpotifyFetcher { { method: "GET", referrerPolicy: "no-referrer", - headers: { - Authorization: `Bearer ${this.credentials.accessToken}`, - }, + headers: headers, }, ) .then((res) => { @@ -75,7 +78,7 @@ module.exports = class SpotifyFetcher { return fetch(new URL("v1/me", userBase), { method: "GET", referrerPolicy: "no-referrer", - headers: { Authorization: `Bearer ${this.credentials.accessToken}` }, + headers: headers, }) .then((res) => { if (!res.ok && res.status === 429) @@ -98,7 +101,7 @@ module.exports = class SpotifyFetcher { return fetch(new URL("v1/me/player/queue", userBase), { method: "GET", referrerPolicy: "no-referrer", - headers: { Authorization: `Bearer ${this.credentials.accessToken}` }, + headers: headers, }) .then((res) => { if (!res.ok && res.status === 429) @@ -123,9 +126,7 @@ module.exports = class SpotifyFetcher { return fetch(new URL(sl, userBase), { method: "GET", referrerPolicy: "no-referrer", - headers: { - Authorization: `Bearer ${this.credentials.accessToken}`, - }, + headers: headers, }) .then((res) => { if (!res.ok && res.status === 429) @@ -148,9 +149,7 @@ module.exports = class SpotifyFetcher { return fetch(new URL("v1/me/player/recently-played", userBase), { method: "GET", referrerPolicy: "no-referrer", - headers: { - Authorization: `Bearer ${this.credentials.accessToken}`, - }, + headers: headers, }) .then((res) => { if (!res.ok && res.status === 429)