const plex = require("./plex"); const logger = require("./logging") const config = require("../config/configuration"); const Session = require("../models/session"); const sessions = require("../services/session-manager"); const spotify = require("./spotify"); let lastTick = Date.now(); async function poll() { const now = Date.now(); const timeDiff = now - lastTick; const playing = []; await spotify.loadCredentials(); await spotify.refreshTokenIfNeeded(); const spotifyTrack = await spotify.getCurrentlyPlaying(); if (spotifyTrack != null) playing.push(spotifyTrack); 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 current of playing) { let session = sessions.get(current.sessionKey); if (session == null) { session = new Session(current.sessionKey); sessions.add(session); } const previous = session.playing; session.playing = current; if (previous == null) { logger.info(current, "A new session has started."); continue; } if (session.playing.state == "paused" || previous.state == "paused") { session.pauseDuration += timeDiff - (session.playing.playtime - previous.playtime); } if (checkIfCanScrobble(session, previous, now, timeDiff)) { logger.info(previous, "Scrobble"); 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; } } 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"); } sessions.remove(sessionId); logger.debug("Deleted old session (" + sessionId + ")"); } lastTick = now; } function applyFilter(track, filters) { if (!filters || filters.length == 0) return true; 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; } function checkIfCanScrobble(session, previous, now, timeDiff) { if (!previous) return false; let filters = []; if (previous.source == 'plex') filters = config.plex.filters; if (!applyFilter(previous, filters)) { logger.debug(previous, 'No filters got triggered. Ignoring.'); return false; } const scrobbleDuration = config.scrobble.minimum.duration || 240; const scrobblePercent = config.scrobble.minimum.percent || 50; 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; return newPlayback && canBeScrobbled; } function isInt(value) { return !isNaN(value) && parseInt(Number(value)) == value && !isNaN(parseInt(value, 10)); } module.exports = poll;