From d5c886d718e1e67316d698a9225f0a737363ed5c Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Tue, 9 Jun 2026 13:28:44 +0000 Subject: [PATCH] fix(listenbrainz): Handle 'import' listen_type with multiple scrobbles #625 --- .../server/endpointListenbrainzRoutes.ts | 4 +- .../sources/EndpointListenbrainzSource.ts | 38 +++++++++++-------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/backend/server/endpointListenbrainzRoutes.ts b/src/backend/server/endpointListenbrainzRoutes.ts index d984bb45..840123a1 100644 --- a/src/backend/server/endpointListenbrainzRoutes.ts +++ b/src/backend/server/endpointListenbrainzRoutes.ts @@ -65,10 +65,10 @@ export const setupLZEndpointRoutes = (app: Express, parentLogger: Logger, scrobb if(isDebugMode()) { logger.debug({body: req.body}, "Recieved request Body"); } - const playerState = playStateFromRequest(req.body); + const playerStates = playStateFromRequest(req.body); for (const source of validSources) { - await source.handle(playerState); + await source.handle(playerStates); } }); diff --git a/src/backend/sources/EndpointListenbrainzSource.ts b/src/backend/sources/EndpointListenbrainzSource.ts index d8cf7678..857592f3 100644 --- a/src/backend/sources/EndpointListenbrainzSource.ts +++ b/src/backend/sources/EndpointListenbrainzSource.ts @@ -89,15 +89,18 @@ export class EndpointListenbrainzSource extends MemorySource { return true; } - handle = async (stateData: PlayerStateData) => { + handle = async (stateData: PlayerStateData[]) => { - await this.processRecentPlays([stateData]); + // if request was an import (multiple plays) then we don't want to process for "now playing" player + // so only process if we only have one payload in the request + if(stateData.length === 1) { + await this.processRecentPlays(stateData); + } - if (stateData.play.meta.nowPlaying === false && this.isValidScrobble(stateData.play)) { - const discovered = await this.discover([stateData.play]); - if (discovered.length > 0) { - await this.scrobble(discovered); - } + const discoverable = stateData.filter(x => x.play.meta.nowPlaying === false && this.isValidScrobble(x.play)); + const discovered = await this.discover(discoverable.map(x => x.play)); + if (discovered.length > 0) { + await this.scrobble(discovered); } this.componentRepo.updateById(this.dbComponent.id, {lastActiveAt: dayjs()}); } @@ -105,20 +108,23 @@ export class EndpointListenbrainzSource extends MemorySource { getNewPlayer = (logger: Logger, id: PlayPlatformId, opts: PlayerStateOptions) => new NowPlayingPlayerState(logger, id, opts); } -export const playStateFromRequest = (obj: SubmitPayload): PlayerStateData => { +export const playStateFromRequest = (obj: SubmitPayload): PlayerStateData[] => { const { listen_type, payload, } = obj; - const play = listenPayloadToPlay(payload[0], listen_type === 'playing_now'); - play.meta.sourceSOT = SOURCE_SOT.HISTORY; - return { - platformId: [play.meta.deviceId, NO_USER], - play, - status: listenTypeAsPlayerStatus(listen_type), - stateUpdatedAt: dayjs() - } + const playStates: PlayerStateData[] = payload.map((x) => { + const play = listenPayloadToPlay(x, listen_type === 'playing_now'); + play.meta.sourceSOT = SOURCE_SOT.HISTORY; + return { + platformId: [play.meta.deviceId, NO_USER], + play, + status: listenTypeAsPlayerStatus(listen_type), + stateUpdatedAt: dayjs() + } + }); + return playStates; } export const listenTypeAsPlayerStatus = (event: string): ReportedPlayerStatus => { -- 2.51.2