Added Spotify tracking. Fixed filters when none given.
This commit is contained in:
parent
ad624172a6
commit
79b27b8e32
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,4 +1,5 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
logs/
|
logs/
|
||||||
config/*
|
config/*
|
||||||
!config/configuration.js
|
!config/configuration.js
|
||||||
|
credentials.*
|
@ -22,6 +22,11 @@ const configuration = {
|
|||||||
*/
|
*/
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
spotify: {
|
||||||
|
client_id: null,
|
||||||
|
client_secret: null,
|
||||||
|
redirect_uri: null
|
||||||
|
},
|
||||||
web: {
|
web: {
|
||||||
host: null,
|
host: null,
|
||||||
port: null
|
port: null
|
||||||
@ -43,6 +48,9 @@ if (config.has("scrobble.minimum.duration"))
|
|||||||
if (config.has("scrobble.minimum.percent"))
|
if (config.has("scrobble.minimum.percent"))
|
||||||
configuration.scrobble.minimum.percent = config.get("scrobble.minimum.percent");
|
configuration.scrobble.minimum.percent = config.get("scrobble.minimum.percent");
|
||||||
|
|
||||||
|
if (config.has("spotify"))
|
||||||
|
configuration.spotify = config.get("spotify");
|
||||||
|
|
||||||
if (config.has("web.host"))
|
if (config.has("web.host"))
|
||||||
configuration.web.host = config.get("web.host");
|
configuration.web.host = config.get("web.host");
|
||||||
if (config.has("web.port"))
|
if (config.has("web.port"))
|
||||||
|
48
controllers/home.js
Normal file
48
controllers/home.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
const axios = require("axios");
|
||||||
|
const config = require("../config/configuration");
|
||||||
|
const fs = require("fs/promises");
|
||||||
|
const logger = require("../services/logging");
|
||||||
|
const querystring = require('node:querystring');
|
||||||
|
|
||||||
|
function authorizeSpotify(req, res) {
|
||||||
|
res.redirect("https://accounts.spotify.com/authorize?" + querystring.stringify({
|
||||||
|
response_type: "code",
|
||||||
|
client_id: config.spotify.client_id,
|
||||||
|
scope: "user-read-playback-state user-read-currently-playing",
|
||||||
|
redirect_uri: config.spotify.redirect_uri,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function callback(req, res) {
|
||||||
|
const code = req.query.code;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios.post("https://accounts.spotify.com/api/token",
|
||||||
|
{
|
||||||
|
code: code,
|
||||||
|
redirect_uri: config.spotify.redirect_uri,
|
||||||
|
grant_type: "authorization_code"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Authorization": "Basic " + new Buffer.from(config.spotify.client_id + ':' + config.spotify.client_secret).toString('base64'),
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const data = response.data;
|
||||||
|
data["expires_at"] = Date.now() + data["expires_in"] * 1000;
|
||||||
|
await fs.writeFile("credentials.spotify.json", JSON.stringify(data));
|
||||||
|
|
||||||
|
res.redirect("/");
|
||||||
|
} catch (ex) {
|
||||||
|
logger.error(ex, "Failed to get Spotify oauth.");
|
||||||
|
res.send({ 'error': "Something went wrong with spotify's oauth flow" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
authorizeSpotify,
|
||||||
|
callback
|
||||||
|
}
|
@ -1,7 +1,16 @@
|
|||||||
|
const home = require("../controllers/home");
|
||||||
const router = require("express").Router();
|
const router = require("express").Router();
|
||||||
|
|
||||||
router.get("/", async (req, res) => {
|
router.get("/", async (req, res) => {
|
||||||
res.send("welcome to an empty page.");
|
res.send("welcome to an empty page.");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.get("/auth/spotify", (req, res) => {
|
||||||
|
home.authorizeSpotify(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/callback", (req, res) => {
|
||||||
|
home.callback(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
@ -2,7 +2,6 @@ const pino = require("pino");
|
|||||||
const logger = pino(pino.destination({ dest: 'logs/.log', sync: false }));
|
const logger = pino(pino.destination({ dest: 'logs/.log', sync: false }));
|
||||||
|
|
||||||
const environment = process.env.NODE_ENV || 'development';
|
const environment = process.env.NODE_ENV || 'development';
|
||||||
console.log(process.env.NODE_ENV);
|
|
||||||
if (environment == "production") {
|
if (environment == "production") {
|
||||||
logger.level = 30
|
logger.level = 30
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
const plex = require("./plex");
|
const plex = require("./plex");
|
||||||
const logger = require("./logging")
|
const logger = require("./logging")
|
||||||
const config = require("../config/configuration");
|
const config = require("../config/configuration");
|
||||||
|
const spotify = require("./spotify");
|
||||||
|
|
||||||
const lastPlaying = {};
|
const lastPlaying = {};
|
||||||
const lastScrobbleTimes = {};
|
const lastScrobbleTimes = {};
|
||||||
@ -9,6 +10,12 @@ async function poll() {
|
|||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const playing = [];
|
const playing = [];
|
||||||
|
|
||||||
|
await spotify.loadCredentials();
|
||||||
|
await spotify.refreshTokenIfNeeded();
|
||||||
|
const spotifyTrack = await spotify.getCurrentlyPlaying();
|
||||||
|
if (spotifyTrack != null)
|
||||||
|
playing.push(spotifyTrack);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await plex.getCurrentlyPlaying();
|
const data = await plex.getCurrentlyPlaying();
|
||||||
playing.push.apply(playing, data);
|
playing.push.apply(playing, data);
|
||||||
@ -46,8 +53,8 @@ async function poll() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function applyFilter(track, filters) {
|
function applyFilter(track, filters) {
|
||||||
if (!filters)
|
if (!filters || filters.length == 0)
|
||||||
return;
|
return true;
|
||||||
|
|
||||||
for (let filter of filters) {
|
for (let filter of filters) {
|
||||||
if (filter.library && !filter.library.some(l => l == track.library))
|
if (filter.library && !filter.library.some(l => l == track.library))
|
||||||
@ -67,7 +74,7 @@ function applyFilter(track, filters) {
|
|||||||
|
|
||||||
function checkIfCanScrobble(current, previous, now) {
|
function checkIfCanScrobble(current, previous, now) {
|
||||||
if (!previous)
|
if (!previous)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
let filters = [];
|
let filters = [];
|
||||||
if (previous.source == 'plex')
|
if (previous.source == 'plex')
|
||||||
|
98
services/spotify.js
Normal file
98
services/spotify.js
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
const axios = require("axios");
|
||||||
|
const config = require("../config/configuration")
|
||||||
|
const logger = require("./logging");
|
||||||
|
const fs = require("fs/promises");
|
||||||
|
const fss = require("fs")
|
||||||
|
|
||||||
|
let token = null;
|
||||||
|
let cache = {}
|
||||||
|
|
||||||
|
async function refreshTokenIfNeeded() {
|
||||||
|
if (!token || token["expires_at"] && token["expires_at"] - Date.now() > 900) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios.post("https://accounts.spotify.com/api/token",
|
||||||
|
{
|
||||||
|
client_id: config.spotify.client_id,
|
||||||
|
refresh_token: token["refresh_token"],
|
||||||
|
grant_type: "refresh_token"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Authorization": "Basic " + new Buffer.from(config.spotify.client_id + ':' + config.spotify.client_secret).toString('base64'),
|
||||||
|
"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"] = token["refresh_token"];
|
||||||
|
}
|
||||||
|
await fs.writeFile("credentials.spotify.json", JSON.stringify(data));
|
||||||
|
token = data;
|
||||||
|
logger.info("Updated access token for Spotify.");
|
||||||
|
return true;
|
||||||
|
} catch (ex) {
|
||||||
|
logger.error(ex, "Failed to get Spotify oauth.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadCredentials() {
|
||||||
|
if (!fss.existsSync("credentials.spotify.json")) {
|
||||||
|
logger.info("No Spotify credentials found.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = await fs.readFile("credentials.spotify.json", "utf-8");
|
||||||
|
token = JSON.parse(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getCurrentlyPlaying(cached = false) {
|
||||||
|
if (cached) {
|
||||||
|
return cache['spotify']
|
||||||
|
}
|
||||||
|
|
||||||
|
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['spotify'] = null;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const media = response.data.item;
|
||||||
|
cache['spotify'] = {
|
||||||
|
"track": media.name,
|
||||||
|
"album": media.album.name,
|
||||||
|
"artist": media.artists.map(a => a.name).join(', '),
|
||||||
|
"year": media.parentYear,
|
||||||
|
"duration": media.duration_ms,
|
||||||
|
"playtime": response.data.progress_ms,
|
||||||
|
"mediaKey": media.id,
|
||||||
|
"sessionKey": "spotify",
|
||||||
|
"state": response.data.is_playing ? "playing" : "paused",
|
||||||
|
"source": "spotify"
|
||||||
|
};
|
||||||
|
return cache['spotify'];
|
||||||
|
} catch (ex) {
|
||||||
|
logger.error(ex, "Failed to get currently playing data from Spotify.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
refreshTokenIfNeeded,
|
||||||
|
loadCredentials,
|
||||||
|
getCurrentlyPlaying
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user