diff --git a/backend/explore/explore.go b/backend/explore/explore.go index bb5bf80..75c374d 100644 --- a/backend/explore/explore.go +++ b/backend/explore/explore.go @@ -827,14 +827,28 @@ func scalePopularity(listens int) int { // filterAndCap removes low-scoring results, special-purpose // MusicBrainz artists, and limits each entity slice to maxResults. func filterAndCap(result *MBSearchResult) { - // Filter artists by minimum blended score and remove SPAs. + // Filter artists: remove SPAs and zero-popularity artists + // that aren't exact name matches. if len(result.Artists) > 0 { filtered := result.Artists[:0] for _, a := range result.Artists { - if a.Score >= minBlendedScore && !mbSpecialPurposeArtists[a.MBID] { - filtered = append(filtered, a) + if mbSpecialPurposeArtists[a.MBID] { + continue } + + if a.Score < minBlendedScore { + continue + } + + // Keep artists with popularity data. Also keep artists + // without popularity if they have a high enough score + // (likely exact or close name matches). + if !a.HasPopularity && a.Score < minZeroPopScore { + continue + } + + filtered = append(filtered, a) } result.Artists = filtered @@ -899,9 +913,13 @@ const ( // minBlendedScore is the floor for artists and recordings // after popularity reranking and tier adjustment (0–100 scale). - // At 50, artists with zero LB popularity and partial name - // matches are filtered out. - minBlendedScore = 50 + minBlendedScore = 25 + + // minZeroPopScore is the floor for artists with zero LB + // popularity data. Higher than minBlendedScore so that + // obscure artists without any listening history are filtered + // unless they're a very strong name match (exact or near-exact). + minZeroPopScore = 50 ) // tierBonus maps artist name-match tiers to percentage score multipliers. @@ -984,9 +1002,10 @@ func (e *Service) boostWithIndexPopularity(result *MBSearchResult) { // Build per-entity maps from the batch result. artistPop := make(map[string]int, len(result.Artists)) - for _, a := range result.Artists { + for i, a := range result.Artists { if pop, ok := popMap[a.MBID]; ok { artistPop[a.MBID] = pop + result.Artists[i].HasPopularity = true } } @@ -1095,6 +1114,15 @@ func (e *Service) boostWithPopularity(result *MBSearchResult) { } } + // Mark artists that have popularity data. + if artistPop != nil { + for i := range result.Artists { + if _, ok := artistPop[result.Artists[i].MBID]; ok { + result.Artists[i].HasPopularity = true + } + } + } + // Rerank each entity type. rerankArtists(result.Artists, artistPop) rerankRecordings(result.Recordings, recordingPop) diff --git a/backend/explore/types.go b/backend/explore/types.go index 5b13a00..a49028f 100644 --- a/backend/explore/types.go +++ b/backend/explore/types.go @@ -26,6 +26,7 @@ type MBArtist struct { Disambiguation string `json:"disambiguation"` Score int `json:"score"` OriginalScore int `json:"-"` // MB search relevance, preserved across reranking + HasPopularity bool `json:"-"` // true if LB/index had listen data for this artist } // MBReleaseGroup is a Wails-friendly projection of a MusicBrainz