[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.

remove scrobbledPlays and prepare queue next query

FoxxMD (May 6, 2026, 2:56 AM UTC) cfb4131d b8494986

+54 -29
+54 -27
src/backend/common/database/drizzle/repositories/PlayRepository.ts
··· 8 8 import { MarkOptional, MarkRequired, PathValue } from "ts-essentials"; 9 9 import { genGroupIdStrFromPlay, removeEmptyArrays, removeUndefinedKeys } from "../../../../utils.js"; 10 10 import dayjs, { Dayjs } from "dayjs"; 11 - import { RelationsFieldFilter, eq, inArray, ne, notInArray, desc, asc, and, sql } from "drizzle-orm"; 11 + import { RelationsFieldFilter, eq, inArray, ne, notInArray, desc, asc, and, sql, Placeholder } from "drizzle-orm"; 12 12 import { CompactableProperty, RetentionOptions, retentionPlayTypes } from "../../../infrastructure/config/database.js"; 13 13 import { shortTodayAwareFormat } from "../../../../../core/TimeUtils.js"; 14 14 import { buildDateCompare, CompareDateOp, ComponentConstrainedRepoOpts, DrizzleBaseRepository, DrizzleRepositoryOpts, PaginatedQueryResponse, PaginatedResponse } from "./BaseRepository.js"; ··· 53 53 export class DrizzlePlayRepository extends DrizzleBaseRepository<'plays'> { 54 54 55 55 protected hasQueueNextPrepared?: ReturnType<typeof this.prepareHasQueueNext> 56 + protected getQueueNextPrepared?: ReturnType<typeof this.prepareGetQueueNext> 56 57 57 58 constructor(db: ReturnType<typeof getDb>, opts: DrizzleRepositoryOpts = {}) { 58 59 super(db, 'plays', 'Plays', opts); ··· 389 390 return recentPlatformIds.map(x => x.platformId); 390 391 } 391 392 393 + protected prepareGetQueueNext = () => this.db.query.plays.findFirst({ 394 + where: { 395 + componentId: sql.placeholder('componentId'), 396 + queueStates: { 397 + queueName: sql.placeholder('queueName'), 398 + queueStatus: 'queued', 399 + retries: { 400 + lte: sql.placeholder('retries') 401 + } 402 + }, 403 + }, 404 + with: { 405 + queueStates: true 406 + }, 407 + orderBy: { 408 + seenAt: 'asc' 409 + }, 410 + }).prepare() 411 + 392 412 public getQueueNext = async (queueName: string, opts: {order?: 'asc' | 'desc', retries?: number} & ComponentConstrainedRepoOpts = {}): Promise<MarkRequired<PlaySelectRel, 'queueStates'> | undefined> => { 393 413 const { 394 - retries, 414 + retries = 0, 395 415 order = 'asc', 396 416 componentId = this.componentId 397 417 } = opts; 398 418 399 - let where: FindWhere<'plays'> = { 400 - componentId 401 - } 419 + // let where: FindWhere<'plays'> = { 420 + // componentId 421 + // } 402 422 403 - if(retries !== undefined) { 404 - where.queueStates = { 405 - queueName, 406 - queueStatus: 'queued', 407 - retries: { 408 - lte: retries 409 - } 410 - } 411 - } else { 412 - where.queueStates = { 413 - queueName, 414 - queueStatus: 'queued' 415 - } 423 + // if(retries !== undefined) { 424 + // where.queueStates = { 425 + // queueName, 426 + // queueStatus: 'queued', 427 + // retries: { 428 + // lte: retries 429 + // } 430 + // } 431 + // } else { 432 + // where.queueStates = { 433 + // queueName, 434 + // queueStatus: 'queued' 435 + // } 436 + // } 437 + 438 + // const res = await this.db.query.plays.findFirst({ 439 + // where: where, 440 + // orderBy: { 441 + // seenAt: order 442 + // }, 443 + // with: { 444 + // queueStates: true 445 + // } 446 + // }); 447 + 448 + if(this.getQueueNextPrepared === undefined) { 449 + this.getQueueNextPrepared = this.prepareGetQueueNext(); 416 450 } 417 451 418 - const res = await this.db.query.plays.findFirst({ 419 - where: where, 420 - orderBy: { 421 - seenAt: order 422 - }, 423 - with: { 424 - queueStates: true 425 - } 426 - }); 452 + const res = await this.getQueueNextPrepared.execute({queueName, retries, componentId}); 453 + 427 454 if(res === undefined) { 428 455 return undefined; 429 456 }
-2
src/backend/scrobblers/AbstractScrobbleClient.ts
··· 96 96 protected MAX_INITIAL_SCROBBLES_FETCH = this.MAX_STORED_SCROBBLES; 97 97 98 98 scrobbleSOTRanges: PaginatedTimeRangeOptions[] = []; 99 - scrobbledPlayObjs: FixedSizeList<ScrobbledPlayObject>; 100 99 tracksScrobbled: number = 0; 101 100 102 101 lastScrobbleAttempt: Dayjs = dayjs(0) ··· 170 169 this.deadLogger = childLogger(this.logger, CLIENT_DEAD_QUEUE); 171 170 this.notifier = notifier; 172 171 this.emitter = emitter; 173 - this.scrobbledPlayObjs = new FixedSizeList<ScrobbledPlayObject>(this.MAX_STORED_SCROBBLES); 174 172 this.scrobbleQueue = new Queue<{payload: QueuedScrobble<PlayObject>}, {scrobbled: ScrobbledPlayObject}>({ 175 173 storage: new MemoryStorage(), 176 174 concurrency: 1