2024-12-03 17:46:43 -05:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-12-03 18:58:15 -05:00
|
|
|
for (let current of playing) {
|
|
|
|
const previous = lastPlaying[current.sessionKey];
|
|
|
|
lastPlaying[current.sessionKey] = current;
|
|
|
|
if (previous == null) {
|
|
|
|
continue;
|
2024-12-03 17:46:43 -05:00
|
|
|
}
|
|
|
|
|
2024-12-03 18:58:15 -05:00
|
|
|
let filters = [];
|
|
|
|
if (previous.source == 'plex')
|
|
|
|
filters = config.scrobble.plex.filters;
|
|
|
|
|
|
|
|
if (!applyFilter(previous, filters)) {
|
|
|
|
logger.debug(previous, 'No filters got triggered. Ignoring.');
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!previous) {
|
|
|
|
logger.info(current, "A new session has started.");
|
2024-12-03 17:46:43 -05:00
|
|
|
} else {
|
2024-12-03 18:58:15 -05:00
|
|
|
if (checkIfCanScrobble(current, previous, now)) {
|
|
|
|
logger.info(previous, "Scrobble");
|
|
|
|
lastScrobbleTimes[previous.mediaKey] = now;
|
2024-12-03 17:46:43 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-03 18:58:15 -05:00
|
|
|
function applyFilter(track, filters) {
|
|
|
|
if (!filters)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (let filter of filters) {
|
|
|
|
if (filter.library && !filter.library.some(l => l == track.library))
|
|
|
|
continue;
|
|
|
|
if (filter.ip && !filter.ip.some(l => l == track.ip))
|
|
|
|
continue;
|
|
|
|
if (filter.deviceId && !filter.deviceId.some(l => l == track.deviceId))
|
|
|
|
continue;
|
|
|
|
if (filter.platform && !filter.platform.some(l => l == track.platform))
|
|
|
|
continue;
|
|
|
|
if (filter.product && !filter.product.some(l => l == track.product))
|
|
|
|
continue;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-12-03 17:46:43 -05:00
|
|
|
function checkIfCanScrobble(current, last, now) {
|
2024-12-03 18:58:15 -05:00
|
|
|
if (!last)
|
|
|
|
return;
|
|
|
|
|
|
|
|
const scrobbleDuration = isInt(config.scrobble.minimum.duration) ? Number(config.scrobble.minimum.duration) : 30;
|
|
|
|
const scrobblePercent = isInt(config.scrobble.minimum.percent) ? Number(config.scrobble.minimum.percent) : 30;
|
2024-12-03 17:46:43 -05:00
|
|
|
|
|
|
|
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;
|