From 9e9d5d02d58e95a861b076a82512beeefee95fbd Mon Sep 17 00:00:00 2001 From: Ewan Croft Date: Thu, 13 Aug 2026 14:45:50 +0100 Subject: [PATCH] fix(tourmaline): compute mood and obscurity from full artist history MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both iterated data.topArtists (capped at 50 by play count), while genres.ts deliberately iterates the full artistPlayCounts map "to capture the long tail of less-played genres". Artist enrichment already fetches info for every unique artist a listener has, not just their top 50, so mood/obscurity were leaving that already-fetched data on the table — for any listener with more than 50 distinct artists (most listeners with real history), both scores were computed on an arbitrarily truncated, less representative subset for no reason. --- packages/tourmaline/src/lib/analysis/mood.ts | 5 ++++- packages/tourmaline/src/lib/analysis/obscurity.ts | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/tourmaline/src/lib/analysis/mood.ts b/packages/tourmaline/src/lib/analysis/mood.ts index 9f3375d..f0ea50a 100644 --- a/packages/tourmaline/src/lib/analysis/mood.ts +++ b/packages/tourmaline/src/lib/analysis/mood.ts @@ -228,7 +228,10 @@ export function buildMoodProfile( let totalWeight = 0; - for (const { name, count } of data.topArtists) { + // Full artist history, not just the top 50 — artistInfos already covers + // every artist by the time this runs, and genres.ts uses the same full + // set to capture the long tail rather than an arbitrary truncation. + for (const [name, count] of data.artistPlayCounts) { const info = artistInfos.get(name); if (!info) continue; diff --git a/packages/tourmaline/src/lib/analysis/obscurity.ts b/packages/tourmaline/src/lib/analysis/obscurity.ts index b25630e..86c6af2 100644 --- a/packages/tourmaline/src/lib/analysis/obscurity.ts +++ b/packages/tourmaline/src/lib/analysis/obscurity.ts @@ -23,7 +23,10 @@ export function calculateObscurity( let totalWeight = 0; let totalObscurityWeight = 0; - for (const { name, count } of data.topArtists) { + // Full artist history, not just the top 50 — artistInfos already covers + // every artist by the time this runs, and genres.ts uses the same full + // set to capture the long tail rather than an arbitrary truncation. + for (const [name, count] of data.artistPlayCounts) { const info = artistInfos.get(name); if (!info?.listenerCount) continue; -- 2.51.2