Compare commits

..

No commits in common. "9335e3e32f96ddf6f79fd966beb9e65fe80fc470" and "0bfbc369520ba3b97ab008c8cb07289ce76fb323" have entirely different histories.

7 changed files with 14 additions and 62 deletions

2
app.js
View File

@ -10,7 +10,7 @@ const Recorder = require("./services/recorder");
const MalojaScrobbler = require("./services/scrobblers/maloja-scrobbler"); const MalojaScrobbler = require("./services/scrobblers/maloja-scrobbler");
const maloja = new MalojaScrobbler(config.maloja, logger); const maloja = new MalojaScrobbler(config.maloja);
const spotify = new SpotifyTracker(config.spotify); const spotify = new SpotifyTracker(config.spotify);
(async () => await spotify.loadCredentials())(); (async () => await spotify.loadCredentials())();
const plex = new PlexTracker(config.plex); const plex = new PlexTracker(config.plex);

7
package-lock.json generated
View File

@ -10,7 +10,6 @@
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"ajv": "^8.17.1", "ajv": "^8.17.1",
"async-await-queue": "^2.1.4",
"axios": "^1.7.8", "axios": "^1.7.8",
"express": "^4.21.1", "express": "^4.21.1",
"express-rate-limit": "^7.4.1", "express-rate-limit": "^7.4.1",
@ -60,12 +59,6 @@
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/async-await-queue": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/async-await-queue/-/async-await-queue-2.1.4.tgz",
"integrity": "sha512-3DpDtxkKO0O/FPlWbk/CrbexjuSxWm1CH1bXlVNVyMBIkKHhT5D85gzHmGJokG3ibNGWQ7pHBmStxUW/z/0LYQ==",
"license": "MIT"
},
"node_modules/asynckit": { "node_modules/asynckit": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",

View File

@ -10,7 +10,6 @@
"description": "", "description": "",
"dependencies": { "dependencies": {
"ajv": "^8.17.1", "ajv": "^8.17.1",
"async-await-queue": "^2.1.4",
"axios": "^1.7.8", "axios": "^1.7.8",
"express": "^4.21.1", "express": "^4.21.1",
"express-rate-limit": "^7.4.1", "express-rate-limit": "^7.4.1",

View File

@ -62,7 +62,7 @@ class Recorder {
this.#sessions.add(session); this.#sessions.add(session);
} }
return { session, media, tracker, extraDuration: 0, scrobble: null } return { session, media, tracker, extraDuration: 0 }
} }
#listen(context, timestamp) { #listen(context, timestamp) {
@ -88,12 +88,8 @@ class Recorder {
const canScrobble = this.#canScrobble(session, current, previous); const canScrobble = this.#canScrobble(session, current, previous);
if (canScrobble || current.id != previous.id) { if (canScrobble || current.id != previous.id) {
context.extraDuration = Math.max(Math.min(current.progress, timeDiff - (previous.duration - previous.progress)), 0); context.extraDuration = Math.min(current.progress, timeDiff - (previous.duration - previous.progress));
context.extraDuration += Math.max(0, session.playDuration - previous.duration);
session.lastScrobbleTimestamp = timestamp; session.lastScrobbleTimestamp = timestamp;
if (canScrobble)
context.scrobble = previous;
} }
session.lastUpdateTimestamp = timestamp; session.lastUpdateTimestamp = timestamp;
@ -110,11 +106,11 @@ class Recorder {
const newPlayback = current == null || current.progress < previous.progress; const newPlayback = current == null || current.progress < previous.progress;
const canBeScrobbled = session.playDuration > scrobbleDuration * 1000 || session.playDuration / previous.duration > scrobblePercent / 100.0; const canBeScrobbled = session.playDuration > scrobbleDuration * 1000 || session.playDuration / previous.duration > scrobblePercent / 100.0;
return newPlayback && canBeScrobbled || session.playDuration >= previous.duration; return newPlayback && canBeScrobbled;
} }
async #scrobble(context) { async #scrobble(context) {
this.#logger.info(context.scrobble, "Scrobble"); this.#logger.info(context, "Scrobble");
for (var scrobblerName of context.tracker.scrobblerNames) { for (var scrobblerName of context.tracker.scrobblerNames) {
const scrobbler = this.#scrobblers.find(s => s.name == scrobblerName); const scrobbler = this.#scrobblers.find(s => s.name == scrobblerName);
@ -124,9 +120,7 @@ class Recorder {
} }
try { try {
const duration = context.session.playDuration; await scrobbler.scrobble(context.media, Date.now() - Math.min(context.media.duration, context.session.playDuration));
const start = Date.now() - context.session.playDuration - context.session.pauseDuration;
await scrobbler.queue(context.scrobble, duration, start);
} catch (ex) { } catch (ex) {
this.#logger.error(ex, "Could not send to maloja."); this.#logger.error(ex, "Could not send to maloja.");
} }

View File

@ -1,12 +1,10 @@
const axios = require("axios"); const axios = require("axios");
const Scrobbler = require("./scrobbler");
class MalojaScrobbler extends Scrobbler { class MalojaScrobbler {
#config = null; #config = null;
#counter = 0; #counter = 0;
constructor(config, logger) { constructor(config) {
super(logger);
this.#config = config; this.#config = config;
if (!config.name) if (!config.name)
@ -14,7 +12,7 @@ class MalojaScrobbler extends Scrobbler {
if (!config.url) if (!config.url)
throw new Error(`Invalid url for Maloja scrobbler '${this.name}'.`); throw new Error(`Invalid url for Maloja scrobbler '${this.name}'.`);
if (!config.token) if (!config.token)
throw new Error(`Invalid token for Maloja scrobbler '${this.name}'.`); throw new Error(`Invalid token for Maloja scrobbler '${this.name}'.`)
} }
get counter() { get counter() {
@ -25,18 +23,17 @@ class MalojaScrobbler extends Scrobbler {
return this.#config.name; return this.#config.name;
} }
async scrobble(song, duration, start) { async scrobble(song, progress, start) {
const url = new URL(this.#config.url); const url = new URL(this.#config.url);
url.pathname += "/apis/mlj_1/newscrobble"; url.pathname += "/apis/mlj_1/newscrobble";
url.search = "?key=" + this.#config.token; url.search = "?key=" + this.#config.token;
await axios.post(url.toString(), { await axios.post(url.toString(), {
title: song.name, title: song.name,
album: song.album, album: song.album,
artists: song.artists, artists: song.artists,
duration: Math.round(duration / 1000), duration: Math.round(progress / 1000),
length: Math.round(song.duration / 1000), length: Math.round(song.duration / 1000),
time: Math.round(start / 1000) //time: start
}); });
this.#counter++; this.#counter++;

View File

@ -1,31 +0,0 @@
const { Queue } = require("async-await-queue");
class Scrobbler {
#queue = null;
#logger = null;
constructor(logger) {
this.#queue = new Queue(1, 300);
this.#logger = logger;
}
async queue(media, duration, start) {
const id = Symbol();
try {
await this.#queue.wait(id, 0);
await this.scrobble(media, duration, start);
} catch (ex) {
this.#logger.console.error(media, "Failed to scrobble: " + ex.message);
} finally {
this.#queue.end(id, duration, start);
}
}
async scrobble(media, duration, start) {
console.log("This should not be running, ever.");
}
}
module.exports = Scrobbler;

View File

@ -2,7 +2,7 @@ const axios = require("axios");
const config = require("../config/configuration") const config = require("../config/configuration")
const logger = require("./logging"); const logger = require("./logging");
const fs = require("fs/promises"); const fs = require("fs/promises");
const fss = require("fs"); const fss = require("fs")
let token = null; let token = null;
let cache = {} let cache = {}