Improved time upkeep for playing and pausing.

This commit is contained in:
Tom 2024-12-05 18:52:36 +00:00
parent 5100d18ac6
commit 0bfbc36952
2 changed files with 31 additions and 34 deletions

View File

@ -2,7 +2,8 @@ class Session {
#id = null; #id = null;
#started = null; #started = null;
#current = null; #current = null;
#lastScrobble = null; lastScrobbleTimestamp = 0;
lastUpdateTimestamp = 0;
pauseDuration = 0; pauseDuration = 0;
playDuration = 0; playDuration = 0;
@ -26,14 +27,6 @@ class Session {
get started() { get started() {
return this.#started; return this.#started;
} }
get lastScrobbleTimestamp() {
return this.#lastScrobble;
}
set lastScrobbleTimestamp(value) {
this.#lastScrobble = value;
}
} }
module.exports = Session; module.exports = Session;

View File

@ -8,7 +8,6 @@ class Recorder {
#scrobblers = []; #scrobblers = [];
#config = null; #config = null;
#logger = null; #logger = null;
#lastTick = null;
constructor(sessions, trackers, scrobblers, config, logger) { constructor(sessions, trackers, scrobblers, config, logger) {
this.#sessions = sessions; this.#sessions = sessions;
@ -17,12 +16,10 @@ class Recorder {
this.#scrobblers = scrobblers; this.#scrobblers = scrobblers;
this.#config = config; this.#config = config;
this.#logger = logger; this.#logger = logger;
this.#lastTick = Date.now();
} }
async record() { async record() {
const now = Date.now(); const now = Date.now();
const timeDiff = now - this.#lastTick;
const media = await this.#trackers.poll(); const media = await this.#trackers.poll();
const contexts = media.map(m => this.#fetchContext(m)); 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)) const stopped = sessionIds.filter(sessionId => !contexts.some(context => sessionId == context.session.id))
.map(sessionId => this.#sessions.get(sessionId)) .map(sessionId => this.#sessions.get(sessionId))
.map(session => this.#fetchContext(session.playing)); .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) for (let context of contextEnded)
context.session.playDuration = now - (context.session.lastScrobbleTimestamp || context.session.started) - context.session.pauseDuration; context.session.playDuration = now - (context.session.lastScrobbleTimestamp || context.session.started) - context.session.pauseDuration;
// Find ongoing sessions that have moved on to the next song. // 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 // Scrobble
const scrobbling = finishedPlaying.concat(contextEnded); const scrobbling = finishedPlaying.concat(contextEnded);
for (let context of scrobbling) for (let context of scrobbling) {
await this.#scrobble(context); await this.#scrobble(context);
if (context.session.playing == null)
continue;
context.session.playDuration = context.extraDuration;
context.session.pauseDuration = 0;
}
// Remove dead sessions. // Remove dead sessions.
for (let context of stopped) { for (let context of stopped) {
this.#sessions.remove(context.session.id); this.#sessions.remove(context.session.id);
} }
this.#lastTick = now;
} }
#fetchContext(media) { #fetchContext(media) {
@ -60,39 +62,41 @@ class Recorder {
this.#sessions.add(session); 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 session = context.session;
const current = context.media; const current = context.media;
const previous = context.session.playing; const previous = context.session.playing;
session.playing = current; session.playing = current;
session.playDuration = timestamp - (session.lastScrobbleTimestamp || session.started) - session.pauseDuration;
if (!previous) { if (!previous) {
this.#logger.info(current, "A new session has started."); this.#logger.info(current, "A new session has started.");
session.lastUpdateTimestamp = timestamp;
return false; return false;
} }
if (session.playing.state == "paused" || previous.state == "paused") const updated = current.progress != previous.progress || current.id != previous.id || current.state != previous.state;
session.pauseDuration += timeDiff - (session.playing.progress - previous.progress); if (!updated)
return false;
if (this.#canScrobble(session, current, previous, timestamp)) { const timeDiff = timestamp - session.lastUpdateTimestamp;
session.pauseDuration = 0; const progressDiff = Math.max(0, Math.min(current.progress - previous.progress, timeDiff));
session.lastScrobbleTimestamp = timestamp - (timeDiff - (previous.duration - previous.progress)); session.playDuration += progressDiff;
return true; session.pauseDuration += timeDiff - progressDiff;
} else if (current.progress < previous.progress && session.playing.id != previous.id) {
session.pauseDuration = 0; const canScrobble = this.#canScrobble(session, current, previous);
if (current.progress < timeDiff) if (canScrobble || current.id != previous.id) {
session.lastScrobbleTimestamp = timestamp - session.playing.progress; context.extraDuration = Math.min(current.progress, timeDiff - (previous.duration - previous.progress));
else session.lastScrobbleTimestamp = timestamp;
session.lastScrobbleTimestamp = timestamp;
} }
return false;
session.lastUpdateTimestamp = timestamp;
return canScrobble;
} }
#canScrobble(session, current, previous, timestamp) { #canScrobble(session, current, previous) {
if (previous == null) if (previous == null)
return false; return false;