2024-12-03 17:46:43 -05:00
|
|
|
const plex = require("./plex");
|
|
|
|
const logger = require("./logging")
|
|
|
|
const config = require("../config/configuration");
|
2024-12-04 20:10:09 -05:00
|
|
|
const Session = require("../models/session");
|
|
|
|
const sessions = require("../services/session-manager");
|
2024-12-04 14:01:40 -05:00
|
|
|
const spotify = require("./spotify");
|
2024-12-03 17:46:43 -05:00
|
|
|
|
2024-12-04 20:10:09 -05:00
|
|
|
let lastTick = Date.now();
|
2024-12-03 17:46:43 -05:00
|
|
|
|
|
|
|
async function poll() {
|
|
|
|
const now = Date.now();
|
2024-12-04 20:10:09 -05:00
|
|
|
const timeDiff = now - lastTick;
|
2024-12-03 17:46:43 -05:00
|
|
|
const playing = [];
|
|
|
|
|
2024-12-04 14:01:40 -05:00
|
|
|
await spotify.loadCredentials();
|
|
|
|
await spotify.refreshTokenIfNeeded();
|
|
|
|
const spotifyTrack = await spotify.getCurrentlyPlaying();
|
|
|
|
if (spotifyTrack != null)
|
|
|
|
playing.push(spotifyTrack);
|
|
|
|
|
2024-12-03 17:46:43 -05:00
|
|
|
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) {
|
2024-12-04 20:10:09 -05:00
|
|
|
let session = sessions.get(current.sessionKey);
|
|
|
|
if (session == null) {
|
|
|
|
session = new Session(current.sessionKey);
|
|
|
|
sessions.add(session);
|
|
|
|
}
|
|
|
|
|
|
|
|
const previous = session.playing;
|
|
|
|
session.playing = current;
|
2024-12-03 18:58:15 -05:00
|
|
|
if (previous == null) {
|
2024-12-03 20:38:38 -05:00
|
|
|
logger.info(current, "A new session has started.");
|
2024-12-03 18:58:15 -05:00
|
|
|
continue;
|
2024-12-03 17:46:43 -05:00
|
|
|
}
|
|
|
|
|
2024-12-04 20:10:09 -05:00
|
|
|
if (session.playing.state == "paused" || previous.state == "paused") {
|
|
|
|
session.pauseDuration += timeDiff - (session.playing.playtime - previous.playtime);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (checkIfCanScrobble(session, previous, now, timeDiff)) {
|
2024-12-03 20:38:38 -05:00
|
|
|
logger.info(previous, "Scrobble");
|
2024-12-04 20:10:09 -05:00
|
|
|
session.pauseDuration = 0;
|
|
|
|
session.lastScrobbleTimestamp = now - (timeDiff - (previous.duration - previous.playtime));
|
|
|
|
} else if (session.playing.playtime < previous.playtime && session.playing.mediaKey != previous.mediaKey) {
|
|
|
|
session.pauseDuration = 0;
|
|
|
|
if (session.playing.playtime < timeDiff)
|
|
|
|
session.lastScrobbleTimestamp = now - session.playing.playtime;
|
|
|
|
else
|
|
|
|
session.lastScrobbleTimestamp = now;
|
2024-12-03 17:46:43 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-04 20:10:09 -05:00
|
|
|
const ids = sessions.getSessionIds();
|
|
|
|
for (let sessionId of ids) {
|
|
|
|
if (playing.some(p => p.sessionKey == sessionId))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
session.playing = null;
|
|
|
|
if (checkIfCanScrobble(session, session.playing, now, timeDiff)) {
|
|
|
|
logger.info(session.playing, "Scrobble");
|
2024-12-03 17:46:43 -05:00
|
|
|
}
|
2024-12-04 20:10:09 -05:00
|
|
|
sessions.remove(sessionId);
|
|
|
|
logger.debug("Deleted old session (" + sessionId + ")");
|
2024-12-03 17:46:43 -05:00
|
|
|
}
|
2024-12-04 20:10:09 -05:00
|
|
|
|
|
|
|
lastTick = now;
|
2024-12-03 17:46:43 -05:00
|
|
|
}
|
|
|
|
|
2024-12-03 18:58:15 -05:00
|
|
|
function applyFilter(track, filters) {
|
2024-12-04 14:01:40 -05:00
|
|
|
if (!filters || filters.length == 0)
|
|
|
|
return true;
|
2024-12-03 18:58:15 -05:00
|
|
|
|
|
|
|
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-04 20:10:09 -05:00
|
|
|
function checkIfCanScrobble(session, previous, now, timeDiff) {
|
2024-12-03 20:38:38 -05:00
|
|
|
if (!previous)
|
2024-12-04 14:01:40 -05:00
|
|
|
return false;
|
2024-12-03 18:58:15 -05:00
|
|
|
|
2024-12-03 20:38:38 -05:00
|
|
|
let filters = [];
|
|
|
|
if (previous.source == 'plex')
|
2024-12-04 17:28:33 -05:00
|
|
|
filters = config.plex.filters;
|
2024-12-03 20:38:38 -05:00
|
|
|
|
|
|
|
if (!applyFilter(previous, filters)) {
|
|
|
|
logger.debug(previous, 'No filters got triggered. Ignoring.');
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-12-04 20:10:09 -05:00
|
|
|
const scrobbleDuration = config.scrobble.minimum.duration || 240;
|
|
|
|
const scrobblePercent = config.scrobble.minimum.percent || 50;
|
2024-12-03 17:46:43 -05:00
|
|
|
|
2024-12-04 20:10:09 -05:00
|
|
|
const current = session.playing;
|
|
|
|
const durationPlayed = now - (session.lastScrobbleTimestamp ?? session.started) - session.pauseDuration;
|
|
|
|
const newPlayback = current == null || current.playtime < previous.playtime && current.playtime < timeDiff;
|
|
|
|
const canBeScrobbled = durationPlayed > scrobbleDuration * 1000 || durationPlayed / previous.duration > scrobblePercent / 100.0;
|
2024-12-03 17:46:43 -05:00
|
|
|
|
2024-12-04 20:10:09 -05:00
|
|
|
return newPlayback && canBeScrobbled;
|
2024-12-03 17:46:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
function isInt(value) {
|
|
|
|
return !isNaN(value) &&
|
|
|
|
parseInt(Number(value)) == value &&
|
|
|
|
!isNaN(parseInt(value, 10));
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = poll;
|