Compare commits

..

No commits in common. "539b9a505521de2458c2afa85b05e58eef853654" and "1b91e330c1550d55dfc34d6b78f1bb860092cddf" have entirely different histories.

4 changed files with 30 additions and 123 deletions

View File

@ -1,44 +0,0 @@
class Session {
constructor(id) {
this._id = id;
this._started = Date.now();
this._current = null;
this._previous = null;
this._lastScrobble = null;
this._pauseDuration = 0;
}
get id() {
return this._id;
}
get playing() {
return this._current;
}
set playing(value) {
this._current = value;
}
get started() {
return this._started;
}
get lastScrobbleTimestamp() {
return this._lastScrobble;
}
set lastScrobbleTimestamp(value) {
this._lastScrobble = value;
}
get pauseDuration() {
return this._pauseDuration;
}
set pauseDuration(value) {
this._pauseDuration = value;
}
}
module.exports = Session;

View File

@ -1,15 +1,13 @@
const plex = require("./plex"); const plex = require("./plex");
const logger = require("./logging") const logger = require("./logging")
const config = require("../config/configuration"); const config = require("../config/configuration");
const Session = require("../models/session");
const sessions = require("../services/session-manager");
const spotify = require("./spotify"); const spotify = require("./spotify");
let lastTick = Date.now(); const lastPlaying = {};
const lastScrobbleTimes = {};
async function poll() { async function poll() {
const now = Date.now(); const now = Date.now();
const timeDiff = now - lastTick;
const playing = []; const playing = [];
await spotify.loadCredentials(); await spotify.loadCredentials();
@ -27,50 +25,31 @@ async function poll() {
} }
for (let current of playing) { for (let current of playing) {
let session = sessions.get(current.sessionKey); const previous = lastPlaying[current.sessionKey];
if (session == null) { lastPlaying[current.sessionKey] = current;
session = new Session(current.sessionKey);
sessions.add(session);
}
const previous = session.playing;
session.playing = current;
if (previous == null) { if (previous == null) {
logger.info(current, "A new session has started."); logger.info(current, "A new session has started.");
continue; continue;
} }
if (session.playing.state == "paused" || previous.state == "paused") { if (checkIfCanScrobble(current, previous, now)) {
session.pauseDuration += timeDiff - (session.playing.playtime - previous.playtime);
}
if (checkIfCanScrobble(session, previous, now, timeDiff)) {
logger.info(previous, "Scrobble"); logger.info(previous, "Scrobble");
session.pauseDuration = 0; lastScrobbleTimes[previous.mediaKey] = now;
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(); // Scrobble then remove lingering sessions
for (let sessionId of ids) { for (let key in lastPlaying) {
if (playing.some(p => p.sessionKey == sessionId)) if (!playing.some(p => p.sessionKey == key)) {
continue; const track = lastPlaying[key];
if (checkIfCanScrobble(null, track, now)) {
session.playing = null; logger.info(track, "Scrobble");
if (checkIfCanScrobble(session, session.playing, now, timeDiff)) { lastScrobbleTimes[track.mediaKey] = now;
logger.info(session.playing, "Scrobble"); }
delete lastPlaying[key];
logger.debug("Deleted old session.", key);
} }
sessions.remove(sessionId);
logger.debug("Deleted old session (" + sessionId + ")");
} }
lastTick = now;
} }
function applyFilter(track, filters) { function applyFilter(track, filters) {
@ -93,7 +72,7 @@ function applyFilter(track, filters) {
return false; return false;
} }
function checkIfCanScrobble(session, previous, now, timeDiff) { function checkIfCanScrobble(current, previous, now) {
if (!previous) if (!previous)
return false; return false;
@ -106,15 +85,20 @@ function checkIfCanScrobble(session, previous, now, timeDiff) {
return false; return false;
} }
const scrobbleDuration = config.scrobble.minimum.duration || 240; const scrobbleDuration = isInt(config.scrobble.minimum.duration) ? Number(config.scrobble.minimum.duration) : 30;
const scrobblePercent = config.scrobble.minimum.percent || 50; const scrobblePercent = isInt(config.scrobble.minimum.percent) ? Number(config.scrobble.minimum.percent) : 30;
const current = session.playing; if (previous) {
const durationPlayed = now - (session.lastScrobbleTimestamp ?? session.started) - session.pauseDuration; const newPlayback = current == null || current.playtime < previous.playtime;
const newPlayback = current == null || current.playtime < previous.playtime && current.playtime < timeDiff; const canBeScrobbled = previous.playtime > scrobbleDuration * 1000 || previous.playtime / previous.duration > scrobblePercent;
const canBeScrobbled = durationPlayed > scrobbleDuration * 1000 || durationPlayed / previous.duration > scrobblePercent / 100.0;
return newPlayback && canBeScrobbled; 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) { function isInt(value) {

View File

@ -1,33 +0,0 @@
class SessionManager {
constructor() {
this._sessions = {};
}
add(session) {
if (!session || !session.id)
return;
this._sessions[session.id] = session;
}
contains(sessionId) {
return this._sessions[sessionId] != null;
}
get(sessionId) {
return this._sessions[sessionId];
}
getSessionIds() {
return Object.keys(this._sessions);
}
remove(sessionId) {
if (!sessionId)
return;
delete this._sessions[sessionId];
}
}
module.exports = new SessionManager();

View File

@ -34,7 +34,7 @@ async function refreshTokenIfNeeded() {
} }
await fs.writeFile("credentials.spotify.json", JSON.stringify(data)); await fs.writeFile("credentials.spotify.json", JSON.stringify(data));
token = data; token = data;
logger.debug("Updated access token for Spotify."); logger.info("Updated access token for Spotify.");
return true; return true;
} catch (ex) { } catch (ex) {
logger.error(ex, "Failed to get Spotify oauth."); logger.error(ex, "Failed to get Spotify oauth.");