Added scrobbling detection for Plex via polling.
This commit is contained in:
4
services/logging.js
Normal file
4
services/logging.js
Normal file
@ -0,0 +1,4 @@
|
||||
const pino = require("pino");
|
||||
const logger = pino(pino.destination({ dest: 'logs/.log', sync: false }));
|
||||
|
||||
module.exports = logger;
|
52
services/plex.js
Normal file
52
services/plex.js
Normal file
@ -0,0 +1,52 @@
|
||||
const axios = require("axios");
|
||||
const config = require("../config/configuration");
|
||||
|
||||
let cache = {};
|
||||
|
||||
async function getCurrentlyPlaying(cached = false) {
|
||||
if (!config.plex.token || !config.plex.url) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const key = config.plex.url + "|" + config.plex.token;
|
||||
if (cached) {
|
||||
return cache[key] || [];
|
||||
}
|
||||
|
||||
const response = await axios.get(config.plex.url + "/status/sessions", {
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
"X-Plex-Token": config.plex.token
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.data.MediaContainer?.Metadata) {
|
||||
cache[key] = [];
|
||||
return []
|
||||
}
|
||||
|
||||
cache[key] = response.data.MediaContainer.Metadata.map(media => ({
|
||||
"library": media.librarySectionTitle,
|
||||
"track": media.title,
|
||||
"album": media.parentTitle,
|
||||
"artist": media.grandparentTitle,
|
||||
"year": media.parentYear,
|
||||
"duration": media.duration,
|
||||
"playtime": media.viewOffset,
|
||||
"lastListenedAt": media.lastViewedAt,
|
||||
"mediaKey": media.guid.substring(media.guid.lastIndexOf('/') + 1),
|
||||
"sessionKey": media.sessionKey,
|
||||
"ip": media.Player.address,
|
||||
"state": media.Player.state,
|
||||
"deviceId": media.Player.machineIdentifier,
|
||||
"platform": media.Player.platform,
|
||||
"platformVersion": media.Player.platformVersion,
|
||||
"product": media.Player.product,
|
||||
"version": media.Player.version,
|
||||
}));
|
||||
return cache[key];
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getCurrentlyPlaying
|
||||
}
|
75
services/poll.js
Normal file
75
services/poll.js
Normal file
@ -0,0 +1,75 @@
|
||||
const plex = require("./plex");
|
||||
const logger = require("./logging")
|
||||
const config = require("../config/configuration");
|
||||
|
||||
const lastPlaying = {};
|
||||
const lastScrobbleTimes = {};
|
||||
|
||||
async function poll() {
|
||||
const now = Date.now();
|
||||
const playing = [];
|
||||
|
||||
try {
|
||||
const data = await plex.getCurrentlyPlaying();
|
||||
playing.push.apply(playing, data);
|
||||
} catch (ex) {
|
||||
logger.error(ex, "Could not fetch currently playing data from Plex.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (let media of playing) {
|
||||
if (!lastScrobbleTimes[media.mediaKey]) {
|
||||
lastScrobbleTimes[media.mediaKey] = 1;
|
||||
}
|
||||
|
||||
const lastTrack = lastPlaying[media.sessionKey];
|
||||
if (!lastTrack) {
|
||||
logger.info(media, "A new session has started.");
|
||||
} else {
|
||||
if (checkIfCanScrobble(media, lastTrack, now)) {
|
||||
logger.info(lastTrack, "Scrobble");
|
||||
lastScrobbleTimes[lastTrack.mediaKey] = now;
|
||||
}
|
||||
}
|
||||
|
||||
lastPlaying[media.sessionKey] = media;
|
||||
}
|
||||
|
||||
// Scrobble then remove lingering sessions
|
||||
for (let key in lastPlaying) {
|
||||
if (!playing.some(p => p.sessionKey == key)) {
|
||||
const track = lastPlaying[key];
|
||||
if (checkIfCanScrobble(null, track, now)) {
|
||||
logger.info(track, "Scrobble");
|
||||
lastScrobbleTimes[track.mediaKey] = now;
|
||||
}
|
||||
delete lastPlaying[key];
|
||||
logger.debug("Deleted old session.", key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkIfCanScrobble(current, last, now) {
|
||||
const scrobbleDuration = isInt(config.scrobble.duration) ? Number(config.scrobble.duration) : 30;
|
||||
const scrobblePercent = isInt(config.scrobble.percent) ? Number(config.scrobble.percent) : 30;
|
||||
|
||||
if (last) {
|
||||
const newPlayback = current == null || current.playtime < last.playtime;
|
||||
const canBeScrobbled = last.playtime > scrobbleDuration * 1000 || last.playtime / last.duration > scrobblePercent;
|
||||
|
||||
if (newPlayback && canBeScrobbled) {
|
||||
const sameSong = current != null && current.mediaKey == last.mediaKey;
|
||||
const lastTime = lastScrobbleTimes[last.mediaKey];
|
||||
return !sameSong || !lastTime || now - lastTime > scrobbleDuration;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function isInt(value) {
|
||||
return !isNaN(value) &&
|
||||
parseInt(Number(value)) == value &&
|
||||
!isNaN(parseInt(value, 10));
|
||||
}
|
||||
|
||||
module.exports = poll;
|
Reference in New Issue
Block a user