refactor(playlists): #411 R2 — generic registry-driven system endpoints
Replaces the per-kind refresh/shuffle handlers with one generic
pair driven off the kind registry, in lockstep across both clients.
Server:
- systemPlaylistKind gains Singleton; RefreshableSystemKind(key)
exported. for_you/discover singleton; songs_like_artist not.
- New generic POST /api/playlists/system/{kind}/refresh and
GET /api/playlists/system/{kind}/shuffle ({kind} = raw
system_variant). Non-singleton/unknown kind → 404. Deleted
playlists_{foryou,discover}_refresh.go and the per-kind shuffle
wrappers; serveSystemPlaylistShuffle core kept.
- playlistRowView.refreshable: server-derived flag so clients show
the refresh affordance generically without hardcoding kinds.
Web (not drift-cached → uses the server flag):
- refreshSystem(variant) replaces refreshForYou/refreshDiscover;
systemShuffle drops the for_you→for-you mapping (raw variant).
- PlaylistCard + detail page gate the kebab/Refresh button on
playlist.refreshable; label is "Refresh {name}". Tests reworked;
obsolete refresh-foryou/discover api tests deleted.
Flutter (list tiles are drift-cache-sourced → derive, no migration):
- Playlist.refreshable getter = isSystem && variant !=
songs_like_artist (holds for all current + planned kinds).
- refreshSystem/systemShuffle use the raw variant; PlaylistCard +
detail screen gate kebab/Regenerate/source-tagging on refreshable
so songs_like_artist plays via get() (no by-kind endpoint).
Pure-plumbing refactor; CI verifies parity. Next (R3): the five
discovery mixes — each a candidate query + one registry entry.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -246,8 +246,29 @@ type systemPlaylistProducer func(
|
||||
) ([]builtPlaylist, error)
|
||||
|
||||
type systemPlaylistKind struct {
|
||||
Key string
|
||||
Produce systemPlaylistProducer
|
||||
Key string
|
||||
// Singleton kinds produce exactly one playlist per user (For-You,
|
||||
// Discover, the discovery mixes) and so can be addressed by kind:
|
||||
// the generic /system/{kind}/{refresh,shuffle} endpoints + the
|
||||
// per-tile refresh affordance only apply to these. Non-singleton
|
||||
// kinds (songs_like_artist → up to 3 per user) are played by
|
||||
// playlist id and have no by-kind endpoint.
|
||||
Singleton bool
|
||||
Produce systemPlaylistProducer
|
||||
}
|
||||
|
||||
// RefreshableSystemKind reports whether `key` is a known singleton
|
||||
// system-playlist kind — i.e. addressable by the generic by-kind
|
||||
// refresh/shuffle endpoints and eligible for the per-tile refresh
|
||||
// affordance. The API layer + clients use this so neither hardcodes
|
||||
// the kind list.
|
||||
func RefreshableSystemKind(key string) bool {
|
||||
for _, k := range systemPlaylistRegistry {
|
||||
if k.Key == key {
|
||||
return k.Singleton
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// systemPlaylistRegistry is the single source of truth for which
|
||||
@@ -258,9 +279,9 @@ type systemPlaylistKind struct {
|
||||
// it has no functional effect (atomic replace + per-playlist
|
||||
// collage are order-independent).
|
||||
var systemPlaylistRegistry = []systemPlaylistKind{
|
||||
{Key: "for_you", Produce: produceForYou},
|
||||
{Key: "songs_like_artist", Produce: produceSeedMixes},
|
||||
{Key: "discover", Produce: produceDiscover},
|
||||
{Key: "for_you", Singleton: true, Produce: produceForYou},
|
||||
{Key: "songs_like_artist", Singleton: false, Produce: produceSeedMixes},
|
||||
{Key: "discover", Singleton: true, Produce: produceDiscover},
|
||||
}
|
||||
|
||||
// produceForYou: today's seed from the user's top-5 played tracks
|
||||
|
||||
Reference in New Issue
Block a user