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

Add more tracing around recently discovered usage

FoxxMD (Apr 2, 2026, 1:31 AM UTC) a4acf020 fe601269

+24 -10
+4
src/backend/common/TupleMap.ts
··· 33 33 return this.map.values(); 34 34 } 35 35 36 + keys() { 37 + return this.map.keys().map(x => JSON.parse(x)); 38 + } 39 + 36 40 forEach(callbackfn: (value: Z, key: [X, Y], map: Map<string, Z>) => void, thisArg?: any): void { 37 41 this.map.forEach((value, key) => { 38 42 callbackfn.call(thisArg, value, JSON.parse(key), this.map);
+17 -8
src/backend/sources/AbstractSource.ts
··· 31 31 sleep, 32 32 sortByOldestPlayDate, 33 33 } from "../utils.js"; 34 - import { sortByNewestPlayDate } from '../../core/PlayUtils.js'; 34 + import { genGroupIdStr, sortByNewestPlayDate } from '../../core/PlayUtils.js'; 35 35 import { formatNumber } from '../../core/DataUtils.js'; 36 36 import { timeToHumanTimestamp } from "../../core/TimeUtils.js"; 37 37 import { todayAwareFormat } from "../../core/TimeUtils.js"; ··· 198 198 199 199 protected addPlayToDiscovered = (play: PlayObject) => { 200 200 const platformId = this.multiPlatform ? genGroupId(play) : SINGLE_USER_PLATFORM_ID; 201 - const list = this.recentDiscoveredPlays.get(platformId) ?? new FixedSizeList<ProgressAwarePlayObject>(200); 201 + let list: FixedSizeList<ProgressAwarePlayObject> = this.recentDiscoveredPlays.get(platformId); 202 + if(list === undefined) { 203 + this.logger.trace(`Creating new discovered list for platform ${genGroupIdStr(platformId)}`); 204 + list = new FixedSizeList<ProgressAwarePlayObject>(200); 205 + this.recentDiscoveredPlays.set(platformId, list); 206 + } 207 + this.logger.trace(`Adding new discovered play to discovered list ${genGroupIdStr(platformId)} with ${list.length} existing`); 202 208 list.add(play); 203 - this.recentDiscoveredPlays.set(platformId, list); 204 209 this.tracksDiscovered++; 205 - this.logger.trace(new Error('addPlayToDiscovered Call site trace')); 206 210 this.logger.info(`Discovered => ${buildTrackString(play)}`); 207 211 this.emitEvent('discovered', {play}); 208 212 this.discoveredCounter.labels(this.getPrometheusLabels()).inc(); ··· 219 223 data.sort(sortByOldestPlayDate); 220 224 return data; 221 225 } 226 + this.logger.trace(`Tried to get non-existent recently discovered for platform ${genGroupIdStr(platformId)}`); 222 227 return []; 223 228 } 224 229 225 230 protected getExistingDiscoveredLists = (play: PlayObject, opts: {checkAll?: boolean} = {}): PlayObject[][] => { 226 231 const lists: PlayObject[][] = []; 227 - if(opts.checkAll !== true) { 228 - lists.push(this.getRecentlyDiscoveredPlaysByPlatform(this.multiPlatform ? genGroupId(play) : SINGLE_USER_PLATFORM_ID)); 232 + if(opts.checkAll !== true || !this.multiPlatform) { 233 + const plat = this.multiPlatform ? genGroupId(play) : SINGLE_USER_PLATFORM_ID; 234 + lists.push(this.getRecentlyDiscoveredPlaysByPlatform(plat)); 235 + if(lists.every(x => x.length === 0)) { 236 + this.logger.trace(`Got empty discovered list for platform ${genGroupIdStr(plat)}`); 237 + } 229 238 } else { 230 239 // get as many as we can, optionally filtering by user 231 - this.recentDiscoveredPlays.forEach((list, platformId) => { 240 + this.recentDiscoveredPlays.keys().forEach((platformId) => { 232 241 if(play.meta.user !== undefined) { 233 242 if(platformId[1] === NO_USER || platformId[1] === play.meta.user) { 234 243 lists.push(this.getRecentlyDiscoveredPlaysByPlatform(platformId)); ··· 264 273 265 274 discover = async (plays: PlayObject[], options: { checkAll?: boolean, [key: string]: any } = {}): Promise<PlayObject[]> => { 266 275 267 - this.logger.trace(new Error('discover Call site trace')); 276 + this.logger.trace(`Discover on ${plays.length} plays`); 268 277 const newDiscoveredPlays: PlayObject[] = []; 269 278 270 279 for await(const play of pMapIterable(plays, this.staggerMappers.preCompare(async x => await this.transformPlay(x, TRANSFORM_HOOK.preCompare)), {concurrency: 2})) {
+3 -2
src/backend/sources/MemorySource.ts
··· 9 9 CALCULATED_PLAYER_STATUSES, 10 10 InternalConfig, PlayerStateDataMaybePlay, 11 11 PlayPlatformId, 12 - ProgressAwarePlayObject} from "../common/infrastructure/Atomic.js"; 12 + ProgressAwarePlayObject, 13 + SINGLE_USER_PLATFORM_ID} from "../common/infrastructure/Atomic.js"; 13 14 import { SourceType, SourceConfig } from '../common/infrastructure/config/source/sources.js'; 14 15 import { PollingOptions } from "../common/infrastructure/config/common.js"; 15 16 import { ··· 341 342 } 342 343 return [false, `${stPrefix} ${EXPECTED_NON_DISCOVERED_REASON}`] 343 344 } else { 344 - const discoveredPlays = this.getRecentlyDiscoveredPlaysByPlatform(genGroupId(candidate)); 345 + const discoveredPlays = this.getRecentlyDiscoveredPlaysByPlatform(this.multiPlatform ? genGroupId(candidate) : SINGLE_USER_PLATFORM_ID); 345 346 if (discoveredPlays.length === 0 || !playObjDataMatch(discoveredPlays[0], candidate)) { 346 347 // if most recent stateful play is not this track we'll add it 347 348 return [true,`${stPrefix} added after ${thresholdResultSummary(thresholdResults)}. Matched other recent play but could not determine time frame due to missing duration. Allowed due to not being last played track.`];