From 77e015ca1320d1f0eff922b7b21b88ea179d7539 Mon Sep 17 00:00:00 2001 From: svemagie <869694+svemagie@users.noreply.github.com> Date: Mon, 9 Mar 2026 18:07:57 +0100 Subject: [PATCH] Fix Funkwhale sync keying and legacy stats backfill --- ...atch-listening-endpoint-runtime-guards.mjs | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/scripts/patch-listening-endpoint-runtime-guards.mjs b/scripts/patch-listening-endpoint-runtime-guards.mjs index 8f783cec..b074ad34 100644 --- a/scripts/patch-listening-endpoint-runtime-guards.mjs +++ b/scripts/patch-listening-endpoint-runtime-guards.mjs @@ -245,6 +245,76 @@ const patchSpecs = [ "node_modules/@indiekit/indiekit/node_modules/@rmdes/indiekit-endpoint-funkwhale/lib/sync.js", ], }, + { + name: "funkwhale-sync-legacy-backfill", + marker: "detect legacy sync keys and force one-time full backfill", + oldSnippet: ` if (Number.isNaN(latestDate.getTime())) { + console.warn( + "[Funkwhale] Invalid listenedAt in latest record; falling back to full sync" + ); + latestDate = new Date(0); + } + + console.log( + \`[Funkwhale] Syncing listenings since: \${latestDate.toISOString()}\` + );`, + newSnippet: ` if (Number.isNaN(latestDate.getTime())) { + console.warn( + "[Funkwhale] Invalid listenedAt in latest record; falling back to full sync" + ); + latestDate = new Date(0); + } + + const totalDocs = await collection.countDocuments(); + const needsLegacyBackfill = totalDocs <= 1 || !latest?.funkwhaleId; + if (needsLegacyBackfill && totalDocs > 0) { + // detect legacy sync keys and force one-time full backfill + console.warn( + "[Funkwhale] Detected legacy sync keys; forcing full resync for accurate statistics" + ); + latestDate = new Date(0); + await collection.deleteMany({ + $or: [{ funkwhaleId: { $exists: false } }, { funkwhaleId: null }], + }); + } + + console.log( + \`[Funkwhale] Syncing listenings since: \${latestDate.toISOString()}\` + );`, + candidates: [ + "node_modules/@rmdes/indiekit-endpoint-funkwhale/lib/sync.js", + "node_modules/@indiekit/indiekit/node_modules/@rmdes/indiekit-endpoint-funkwhale/lib/sync.js", + ], + }, + { + name: "funkwhale-sync-stable-listening-id", + marker: "use stable listening event key (fid) for sync upserts", + oldSnippet: ` return { + funkwhaleId: listening.id, + trackId: track.id, + trackTitle: track.title, + trackFid: track.fid, + artistName: artist?.name || getArtistName(track),`, + newSnippet: ` const stableListeningId = + listening.fid || + [ + track?.fid || track?.id || "unknown-track", + listening.creation_date || "unknown-date", + ].join(":"); + + return { + // use stable listening event key (fid) for sync upserts + funkwhaleId: stableListeningId, + listeningFid: listening.fid || null, + trackId: track.id, + trackTitle: track.title, + trackFid: track.fid, + artistName: artist?.name || getArtistName(track),`, + candidates: [ + "node_modules/@rmdes/indiekit-endpoint-funkwhale/lib/sync.js", + "node_modules/@indiekit/indiekit/node_modules/@rmdes/indiekit-endpoint-funkwhale/lib/sync.js", + ], + }, { name: "funkwhale-stats-date-coercion", marker: "support string and Date listenedAt values in period filters",