From 0bfbc369520ba3b97ab008c8cb07289ce76fb323 Mon Sep 17 00:00:00 2001 From: Tom Date: Thu, 5 Dec 2024 18:52:36 +0000 Subject: [PATCH] Improved time upkeep for playing and pausing. --- models/session.js | 11 ++------- services/recorder.js | 54 ++++++++++++++++++++++++-------------------- 2 files changed, 31 insertions(+), 34 deletions(-) diff --git a/models/session.js b/models/session.js index b02627c..782f8af 100644 --- a/models/session.js +++ b/models/session.js @@ -2,7 +2,8 @@ class Session { #id = null; #started = null; #current = null; - #lastScrobble = null; + lastScrobbleTimestamp = 0; + lastUpdateTimestamp = 0; pauseDuration = 0; playDuration = 0; @@ -26,14 +27,6 @@ class Session { get started() { return this.#started; } - - get lastScrobbleTimestamp() { - return this.#lastScrobble; - } - - set lastScrobbleTimestamp(value) { - this.#lastScrobble = value; - } } module.exports = Session; \ No newline at end of file diff --git a/services/recorder.js b/services/recorder.js index ea3f6eb..798b0af 100644 --- a/services/recorder.js +++ b/services/recorder.js @@ -8,7 +8,6 @@ class Recorder { #scrobblers = []; #config = null; #logger = null; - #lastTick = null; constructor(sessions, trackers, scrobblers, config, logger) { this.#sessions = sessions; @@ -17,12 +16,10 @@ class Recorder { this.#scrobblers = scrobblers; this.#config = config; this.#logger = logger; - this.#lastTick = Date.now(); } async record() { const now = Date.now(); - const timeDiff = now - this.#lastTick; const media = await this.#trackers.poll(); const contexts = media.map(m => this.#fetchContext(m)); @@ -31,25 +28,30 @@ class Recorder { const stopped = sessionIds.filter(sessionId => !contexts.some(context => sessionId == context.session.id)) .map(sessionId => this.#sessions.get(sessionId)) .map(session => this.#fetchContext(session.playing)); - const contextEnded = stopped.filter(context => this.#canScrobble(context.session, null, context.session.playing, now)); + const contextEnded = stopped.filter(context => this.#canScrobble(context.session, null, context.session.playing)); for (let context of contextEnded) context.session.playDuration = now - (context.session.lastScrobbleTimestamp || context.session.started) - context.session.pauseDuration; // Find ongoing sessions that have moved on to the next song. - const finishedPlaying = contexts.filter(context => this.#listen(context, now, timeDiff)); + const finishedPlaying = contexts.filter(context => this.#listen(context, now)); // Scrobble const scrobbling = finishedPlaying.concat(contextEnded); - for (let context of scrobbling) + for (let context of scrobbling) { await this.#scrobble(context); + if (context.session.playing == null) + continue; + + context.session.playDuration = context.extraDuration; + context.session.pauseDuration = 0; + } + // Remove dead sessions. for (let context of stopped) { this.#sessions.remove(context.session.id); } - - this.#lastTick = now; } #fetchContext(media) { @@ -60,39 +62,41 @@ class Recorder { this.#sessions.add(session); } - return { session, media, tracker } + return { session, media, tracker, extraDuration: 0 } } - #listen(context, timestamp, timeDiff) { + #listen(context, timestamp) { const session = context.session; const current = context.media; const previous = context.session.playing; session.playing = current; - session.playDuration = timestamp - (session.lastScrobbleTimestamp || session.started) - session.pauseDuration; if (!previous) { this.#logger.info(current, "A new session has started."); + session.lastUpdateTimestamp = timestamp; return false; } - if (session.playing.state == "paused" || previous.state == "paused") - session.pauseDuration += timeDiff - (session.playing.progress - previous.progress); + const updated = current.progress != previous.progress || current.id != previous.id || current.state != previous.state; + if (!updated) + return false; - if (this.#canScrobble(session, current, previous, timestamp)) { - session.pauseDuration = 0; - session.lastScrobbleTimestamp = timestamp - (timeDiff - (previous.duration - previous.progress)); - return true; - } else if (current.progress < previous.progress && session.playing.id != previous.id) { - session.pauseDuration = 0; - if (current.progress < timeDiff) - session.lastScrobbleTimestamp = timestamp - session.playing.progress; - else - session.lastScrobbleTimestamp = timestamp; + const timeDiff = timestamp - session.lastUpdateTimestamp; + const progressDiff = Math.max(0, Math.min(current.progress - previous.progress, timeDiff)); + session.playDuration += progressDiff; + session.pauseDuration += timeDiff - progressDiff; + + const canScrobble = this.#canScrobble(session, current, previous); + if (canScrobble || current.id != previous.id) { + context.extraDuration = Math.min(current.progress, timeDiff - (previous.duration - previous.progress)); + session.lastScrobbleTimestamp = timestamp; } - return false; + + session.lastUpdateTimestamp = timestamp; + return canScrobble; } - #canScrobble(session, current, previous, timestamp) { + #canScrobble(session, current, previous) { if (previous == null) return false;