[READ-ONLY] Mirror of https://github.com/FoxxMD/multi-scrobbler. Scrobble plays from multiple sources to multiple clients docs.multi-scrobbler.app
deezer docker jellyfin koito lastfm listenbrainz maloja mopidy mpris music music-assistant plex scrobble self-hosted spotify subsonic tautulli youtube-music
0

Configure Feed

Select the types of activity you want to include in your feed.

feat: Increase transform play concurrency

Since MB rate limiting is done by api calient we can safely increase number of plays transformed without risking MB errors from rate limits

FoxxMD (Apr 6, 2026, 7:12 PM UTC) bb519150 7c177dfe

+6 -6
+4 -4
src/backend/scrobblers/AbstractScrobbleClient.ts
··· 234 234 pcInits.push(t.staggerOpts?.initialInterval ?? 0); 235 235 pcMaxStagger.push(t.staggerOpts?.maxRandomStagger ?? 0) 236 236 } 237 - this.staggerMappers.preCompare = staggerMapper<PlayObject, PlayObject>({initialInterval: Math.max(...pcInits), maxRandomStagger: Math.max(...pcMaxStagger), concurrency: 2}); 237 + this.staggerMappers.preCompare = staggerMapper<PlayObject, PlayObject>({initialInterval: Math.max(...pcInits), maxRandomStagger: Math.max(...pcMaxStagger), concurrency: 3}); 238 238 } 239 239 240 240 if(existing.length > 0) { ··· 245 245 eInits.push(t.staggerOpts?.initialInterval ?? 0); 246 246 eMaxStagger.push(t.staggerOpts?.maxRandomStagger ?? 0) 247 247 } 248 - this.staggerMappers.existing = staggerMapper<ScrobbledPlayObject, ScrobbledPlayObject>({initialInterval: Math.max(...eInits), maxRandomStagger: Math.max(...eMaxStagger), concurrency: 2}); 248 + this.staggerMappers.existing = staggerMapper<ScrobbledPlayObject, ScrobbledPlayObject>({initialInterval: Math.max(...eInits), maxRandomStagger: Math.max(...eMaxStagger), concurrency: 3}); 249 249 } 250 250 } 251 251 ··· 505 505 506 506 const playObj = await this.transformPlay(playObjPre, TRANSFORM_HOOK.candidate); 507 507 508 - const dtInvariantMatches = (await pMap(this.scrobbledPlayObjs.data, this.staggerMappers.existing(async x => ({...x, play: await this.transformPlay(x.play, TRANSFORM_HOOK.existing)})), {concurrency: 2})) 508 + const dtInvariantMatches = (await pMap(this.scrobbledPlayObjs.data, this.staggerMappers.existing(async x => ({...x, play: await this.transformPlay(x.play, TRANSFORM_HOOK.existing)})), {concurrency: 3})) 509 509 .filter(x => playObjDataMatch(playObj, x.play)); 510 510 511 511 if (dtInvariantMatches.length === 0) { ··· 950 950 951 951 queueScrobble = async (data: PlayObject | PlayObject[], source: string) => { 952 952 const plays = (Array.isArray(data) ? data : [data]).map(x => ({...x, meta: {...x.meta, seenAt: dayjs()}})); 953 - for await(const play of pMapIterable(plays, this.staggerMappers.preCompare(async x => await this.transformPlay(x, TRANSFORM_HOOK.preCompare)), {concurrency: 2})) { 953 + for await(const play of pMapIterable(plays, this.staggerMappers.preCompare(async x => await this.transformPlay(x, TRANSFORM_HOOK.preCompare)), {concurrency: 3})) { 954 954 try { 955 955 const existingQueued = await this.existingScrobble(play, this.queuedScrobbles.map(x => x.play), false); 956 956 // want to be very confident of this
+2 -2
src/backend/sources/AbstractSource.ts
··· 268 268 discover = async (plays: PlayObject[], options: { checkAll?: boolean, signal?: AbortSignal, [key: string]: any } = {}): Promise<PlayObject[]> => { 269 269 const newDiscoveredPlays: PlayObject[] = []; 270 270 271 - for await(const play of pMapIterable(plays, this.staggerMappers.preCompare(async x => await this.transformPlay(x, TRANSFORM_HOOK.preCompare)), {concurrency: 2})) { 271 + for await(const play of pMapIterable(plays, this.staggerMappers.preCompare(async x => await this.transformPlay(x, TRANSFORM_HOOK.preCompare)), {concurrency: 3})) { 272 272 options.signal?.throwIfAborted(); 273 273 if(!(await this.alreadyDiscovered(play, options))) { 274 274 options.signal?.throwIfAborted() ··· 302 302 } 303 303 newDiscoveredPlays.sort(sortByOldestPlayDate); 304 304 this.emitter.emit('discoveredToScrobble', { 305 - data: await pMap(newDiscoveredPlays, this.staggerMappers.postCompare(async (x) => await this.transformPlay(x, TRANSFORM_HOOK.postCompare)), {concurrency: 2}), 305 + data: await pMap(newDiscoveredPlays, this.staggerMappers.postCompare(async (x) => await this.transformPlay(x, TRANSFORM_HOOK.postCompare)), {concurrency: 3}), 306 306 options: { 307 307 ...options, 308 308 checkTime: newDiscoveredPlays[newDiscoveredPlays.length-1].data.playDate.add(2, 'second'),