[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: Add seenAt to source/client play meta

FoxxMD (Mar 24, 2026, 7:38 PM UTC) e62e1ac2 e68b46f6

+6 -2
+1 -1
src/backend/scrobblers/AbstractScrobbleClient.ts
··· 847 847 } 848 848 849 849 queueScrobble = async (data: PlayObject | PlayObject[], source: string) => { 850 - const plays = Array.isArray(data) ? data : [data]; 850 + const plays = (Array.isArray(data) ? data : [data]).map(x => ({...x, meta: {...x.meta, seenAt: dayjs()}})); 851 851 const sm = staggerMapper<PlayObject, PlayObject>({concurrency: 2}); 852 852 for await(const play of pMapIterable(plays, sm(async x => await this.transformPlay(x, TRANSFORM_HOOK.preCompare)), {concurrency: 2})) { 853 853 try {
+5 -1
src/backend/utils/PlayTransformUtils.ts
··· 15 15 WhenConditionsConfig, 16 16 WhenParts 17 17 } from "../common/infrastructure/Transform.js"; 18 + import dayjs from "dayjs"; 18 19 19 20 export const isWhenCondition = (val: unknown): val is WhenParts<string> => { 20 21 if (val !== null && typeof val === 'object') { ··· 173 174 } 174 175 175 176 export const baseFormatPlayObj = (data: any, play: PlayObjectLifecycleless): PlayObject => { 176 - return { 177 + const basePlay = { 177 178 data: { 178 179 ...play.data 179 180 }, 180 181 meta: { 182 + seenAt: dayjs(), 181 183 ...play.meta, 182 184 lifecycle: { 183 185 input: data, ··· 186 188 } 187 189 } 188 190 } 191 + basePlay.meta.lifecycle.original.meta.seenAt = basePlay.meta.seenAt; 192 + return basePlay; 189 193 } 190 194 191 195 export const defaultLifecycle = (extra?: PlayLifecycle): PlayLifecycle => {