const plex = require("./plex"); const logger = require("./logging") const config = require("../config/configuration"); const spotify = require("./spotify"); const lastPlaying = {}; const lastScrobbleTimes = {}; async function poll() { const now = Date.now(); 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) { const previous = lastPlaying[current.sessionKey]; lastPlaying[current.sessionKey] = current; if (previous == null) { logger.info(current, "A new session has started."); continue; } if (checkIfCanScrobble(current, previous, now)) { logger.info(previous, "Scrobble"); lastScrobbleTimes[previous.mediaKey] = now; } } // 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 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(current, previous, now) { if (!previous) return false; let filters = []; if (previous.source == 'plex') filters = config.scrobble.plex.filters; if (!applyFilter(previous, filters)) { logger.debug(previous, 'No filters got triggered. Ignoring.'); return false; } 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; if (previous) { const newPlayback = current == null || current.playtime < previous.playtime; const canBeScrobbled = previous.playtime > scrobbleDuration * 1000 || previous.playtime / previous.duration > scrobblePercent; if (newPlayback && canBeScrobbled) { const sameSong = current != null && current.mediaKey == previous.mediaKey; const lastTime = lastScrobbleTimes[previous.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;