fix(server): unify discovery-mix producers + daily-rotate the deterministic ones
The five discovery-mix producers (Deep Cuts, Rediscover, New for you, On this day, First listens) were near-identical boilerplate that differed only in (a) which SQL query they ran and (b) whether to diversity-cap the result. Folded into one produceDiscoveryMix(spec) factory + a per-mix discoveryMixSpec slice. The registry composes the factory over the spec list so adding a new mix is one struct literal + a SQL query, never a new func. Also fixes the user-reported bug that several mixes 'show the same content from yesterday'. Audit of the SQL queries: - Deep Cuts: ORDER BY md5(t.id::text || $2::text) → day-keyed - On this day: ORDER BY w.c DESC, md5(...) → day-keyed - Rediscover: ORDER BY tier, c DESC, id → invariant - New for you: ORDER BY al.created_at DESC, disc, track → invariant - First listens: ORDER BY tier, al.id, disc, track → invariant The three invariant ones produced identical content day-over-day. The unified spec carries a dailyRotate bool: when set, the producer applies a daily-deterministic offset rotate-left of the candidate pool BEFORE diversify+truncate. Rotation (not shuffle) preserves contiguous-block ordering inside each day's slice — matters for First listens which is album-coherent. Set on Rediscover + First listens (where same-content-every-day is clearly a bug). Left off New for you because 'newest album first regardless of day' is the intended UX for that surface — daily rotation there would feel wrong. Daily rotation seed: rand.New(NewSource(int64(userIDHash(userID, dateStr)))) — same primitive used by For-You's pickHeadAndTail sampling so behavior is consistent across the system playlist family. No test file referenced the deleted produceXxx functions directly, only the registry, so this is a closed refactor.
This commit is contained in:
@@ -278,16 +278,23 @@ func RefreshableSystemKind(key string) bool {
|
||||
// here (plus its candidate query). Order is the materialize order;
|
||||
// it has no functional effect (atomic replace + per-playlist
|
||||
// collage are order-independent).
|
||||
var systemPlaylistRegistry = []systemPlaylistKind{
|
||||
{Key: "for_you", Singleton: true, Produce: produceForYou},
|
||||
{Key: "songs_like_artist", Singleton: false, Produce: produceSeedMixes},
|
||||
{Key: "discover", Singleton: true, Produce: produceDiscover},
|
||||
{Key: "deep_cuts", Singleton: true, Produce: produceDeepCuts},
|
||||
{Key: "rediscover", Singleton: true, Produce: produceRediscover},
|
||||
{Key: "new_for_you", Singleton: true, Produce: produceNewForYou},
|
||||
{Key: "on_this_day", Singleton: true, Produce: produceOnThisDay},
|
||||
{Key: "first_listens", Singleton: true, Produce: produceFirstListens},
|
||||
}
|
||||
var systemPlaylistRegistry = func() []systemPlaylistKind {
|
||||
out := []systemPlaylistKind{
|
||||
{Key: "for_you", Singleton: true, Produce: produceForYou},
|
||||
{Key: "songs_like_artist", Singleton: false, Produce: produceSeedMixes},
|
||||
{Key: "discover", Singleton: true, Produce: produceDiscover},
|
||||
}
|
||||
// The five discovery mixes share one Produce closure factory keyed
|
||||
// by a per-mix spec; spec list + factory live in system_mixes.go.
|
||||
for _, spec := range discoveryMixSpecs {
|
||||
out = append(out, systemPlaylistKind{
|
||||
Key: spec.variant,
|
||||
Singleton: true,
|
||||
Produce: produceDiscoveryMix(spec),
|
||||
})
|
||||
}
|
||||
return out
|
||||
}()
|
||||
|
||||
// systemForYouSourceLimits is a deeper candidate pool than the radio
|
||||
// default. On a self-hosted library without ListenBrainz similarity
|
||||
|
||||
Reference in New Issue
Block a user