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

fix(listenbrainz): Handle 'import' listen_type with multiple scrobbles

#625

FoxxMD (Jun 9, 2026, 1:28 PM UTC) d5c886d7 e9935140

+24 -18
+2 -2
src/backend/server/endpointListenbrainzRoutes.ts
··· 65 65 if(isDebugMode()) { 66 66 logger.debug({body: req.body}, "Recieved request Body"); 67 67 } 68 - const playerState = playStateFromRequest(req.body); 68 + const playerStates = playStateFromRequest(req.body); 69 69 70 70 for (const source of validSources) { 71 - await source.handle(playerState); 71 + await source.handle(playerStates); 72 72 } 73 73 }); 74 74
+22 -16
src/backend/sources/EndpointListenbrainzSource.ts
··· 89 89 return true; 90 90 } 91 91 92 - handle = async (stateData: PlayerStateData) => { 92 + handle = async (stateData: PlayerStateData[]) => { 93 93 94 - await this.processRecentPlays([stateData]); 94 + // if request was an import (multiple plays) then we don't want to process for "now playing" player 95 + // so only process if we only have one payload in the request 96 + if(stateData.length === 1) { 97 + await this.processRecentPlays(stateData); 98 + } 95 99 96 - if (stateData.play.meta.nowPlaying === false && this.isValidScrobble(stateData.play)) { 97 - const discovered = await this.discover([stateData.play]); 98 - if (discovered.length > 0) { 99 - await this.scrobble(discovered); 100 - } 100 + const discoverable = stateData.filter(x => x.play.meta.nowPlaying === false && this.isValidScrobble(x.play)); 101 + const discovered = await this.discover(discoverable.map(x => x.play)); 102 + if (discovered.length > 0) { 103 + await this.scrobble(discovered); 101 104 } 102 105 this.componentRepo.updateById(this.dbComponent.id, {lastActiveAt: dayjs()}); 103 106 } ··· 105 108 getNewPlayer = (logger: Logger, id: PlayPlatformId, opts: PlayerStateOptions) => new NowPlayingPlayerState(logger, id, opts); 106 109 } 107 110 108 - export const playStateFromRequest = (obj: SubmitPayload): PlayerStateData => { 111 + export const playStateFromRequest = (obj: SubmitPayload): PlayerStateData[] => { 109 112 const { 110 113 listen_type, 111 114 payload, 112 115 } = obj; 113 116 114 - const play = listenPayloadToPlay(payload[0], listen_type === 'playing_now'); 115 - play.meta.sourceSOT = SOURCE_SOT.HISTORY; 116 - return { 117 - platformId: [play.meta.deviceId, NO_USER], 118 - play, 119 - status: listenTypeAsPlayerStatus(listen_type), 120 - stateUpdatedAt: dayjs() 121 - } 117 + const playStates: PlayerStateData[] = payload.map((x) => { 118 + const play = listenPayloadToPlay(x, listen_type === 'playing_now'); 119 + play.meta.sourceSOT = SOURCE_SOT.HISTORY; 120 + return { 121 + platformId: [play.meta.deviceId, NO_USER], 122 + play, 123 + status: listenTypeAsPlayerStatus(listen_type), 124 + stateUpdatedAt: dayjs() 125 + } 126 + }); 127 + return playStates; 122 128 } 123 129 124 130 export const listenTypeAsPlayerStatus = (event: string): ReportedPlayerStatus => {