[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(database): Optimize queries for scrobble range ops

FoxxMD (May 6, 2026, 2:56 AM UTC) 8de382f5 691db921

+28 -2
+26
src/backend/common/database/drizzle/repositories/PlayRepository.ts
··· 59 59 60 60 protected hasQueueNextPrepared?: ReturnType<typeof this.prepareHasQueueNext> 61 61 protected getQueueNextPrepared?: ReturnType<typeof this.prepareGetQueueNext> 62 + protected getQueuedScrobbleRangePrepared?: ReturnType<typeof this.prepareGetQueuedScrobbleRange> 62 63 63 64 constructor(db: ReturnType<typeof getDb>, opts: DrizzleRepositoryOpts = {}) { 64 65 super(db, 'plays', 'Plays', opts); ··· 485 486 } 486 487 const nextId = await this.hasQueueNextPrepared.execute({queueName, retries}); 487 488 return nextId !== undefined; 489 + } 490 + 491 + protected prepareGetQueuedScrobbleRange = () => this.db.query.plays.findMany({ 492 + where: { 493 + componentId: this.componentId, 494 + queueStates: { 495 + queueName: sql.placeholder('queueName'), 496 + queueStatus: 'queued', 497 + retries: { 498 + lte: sql.placeholder('retries') 499 + } 500 + }, 501 + }, 502 + orderBy: { 503 + seenAt: 'asc', 504 + }, 505 + limit: sql.placeholder('limit') 506 + }).prepare() 507 + 508 + public getQueuedScrobbleRange = async (queueName: string, opts: {retries?: number, limit?: number} = {}): Promise<PlayObject[]> => { 509 + if(this.getQueuedScrobbleRangePrepared === undefined) { 510 + this.getQueuedScrobbleRangePrepared = this.prepareGetQueuedScrobbleRange(); 511 + } 512 + const res = await this.getQueuedScrobbleRangePrepared.execute({queueName, retries: opts.retries ?? 0, limit: opts.limit ?? 30}); 513 + return res.map(x => x.play); 488 514 } 489 515 490 516 public getQueued = async (queueName: string, opts: {
+2 -2
src/backend/scrobblers/AbstractScrobbleClient.ts
··· 587 587 } 588 588 589 589 handleQueuedScrobbleRanges = async (deadRetries: number = 3) => { 590 - const queued = (await this.playRepo.getQueued(CLIENT_INGRESS_QUEUE, {limit: 30})).data.map(x => asPlay(x.play)); 591 - const dead = (await this.playRepo.getQueued(CLIENT_DEAD_QUEUE, {limit: 30, retries: deadRetries})).data.map(x => asPlay(x.play)); 590 + const queued = await this.playRepo.getQueuedScrobbleRange(CLIENT_INGRESS_QUEUE); 591 + const dead = await this.playRepo.getQueuedScrobbleRange(CLIENT_DEAD_QUEUE, {retries: deadRetries}); 592 592 this.scrobbleSOTRanges = groupPlaysToTimeRanges(queued.concat(dead), this.scrobbleSOTRanges, {staleNowBuffer: this.config.options?.refreshStaleAfter}); 593 593 } 594 594