feat(api): wire M6a routes; alpha artist list uses covers query
Replace handleListArtists alpha branch with ListArtistsAlphaWithCovers (drops N+1 per-artist album lookup). Register /api/home and /api/library/albums in api.go. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -46,11 +46,13 @@ func Mount(r chi.Router, pool *pgxpool.Pool, logger *slog.Logger, events *playev
|
|||||||
authed.Get("/artists/{id}/tracks", h.handleGetArtistTracks)
|
authed.Get("/artists/{id}/tracks", h.handleGetArtistTracks)
|
||||||
authed.Get("/albums/{id}", h.handleGetAlbum)
|
authed.Get("/albums/{id}", h.handleGetAlbum)
|
||||||
authed.Get("/albums/{id}/cover", h.handleGetCover)
|
authed.Get("/albums/{id}/cover", h.handleGetCover)
|
||||||
|
authed.Get("/library/albums", h.handleListLibraryAlbums)
|
||||||
authed.Get("/tracks/{id}", h.handleGetTrack)
|
authed.Get("/tracks/{id}", h.handleGetTrack)
|
||||||
authed.Get("/tracks/{id}/stream", h.handleGetStream)
|
authed.Get("/tracks/{id}/stream", h.handleGetStream)
|
||||||
authed.Get("/search", h.handleSearch)
|
authed.Get("/search", h.handleSearch)
|
||||||
authed.Get("/radio", h.handleRadio)
|
authed.Get("/radio", h.handleRadio)
|
||||||
authed.Get("/discover/suggestions", h.handleListSuggestions)
|
authed.Get("/discover/suggestions", h.handleListSuggestions)
|
||||||
|
authed.Get("/home", h.handleGetHome)
|
||||||
authed.Post("/events", h.handleEvents)
|
authed.Post("/events", h.handleEvents)
|
||||||
authed.Post("/likes/tracks/{id}", h.handleLikeTrack)
|
authed.Post("/likes/tracks/{id}", h.handleLikeTrack)
|
||||||
authed.Delete("/likes/tracks/{id}", h.handleUnlikeTrack)
|
authed.Delete("/likes/tracks/{id}", h.handleUnlikeTrack)
|
||||||
|
|||||||
+52
-34
@@ -184,8 +184,10 @@ func (h *handlers) handleGetArtistTracks(w http.ResponseWriter, r *http.Request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// handleListArtists implements GET /api/artists with two sort modes
|
// handleListArtists implements GET /api/artists with two sort modes
|
||||||
// (alpha|newest) and enveloped pagination. album_count per artist is
|
// (alpha|newest) and enveloped pagination. The alpha branch uses
|
||||||
// computed via an N+1 — acceptable at limit=200.
|
// ListArtistsAlphaWithCovers to ship cover_url + album_count in a
|
||||||
|
// single query. The newest branch keeps its N+1 album_count lookup
|
||||||
|
// (less hot path, no covers exposed).
|
||||||
func (h *handlers) handleListArtists(w http.ResponseWriter, r *http.Request) {
|
func (h *handlers) handleListArtists(w http.ResponseWriter, r *http.Request) {
|
||||||
sort := r.URL.Query().Get("sort")
|
sort := r.URL.Query().Get("sort")
|
||||||
if sort == "" {
|
if sort == "" {
|
||||||
@@ -200,45 +202,61 @@ func (h *handlers) handleListArtists(w http.ResponseWriter, r *http.Request) {
|
|||||||
writeErr(w, http.StatusBadRequest, "bad_request", err.Error())
|
writeErr(w, http.StatusBadRequest, "bad_request", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
q := dbq.New(h.pool)
|
q := dbq.New(h.pool)
|
||||||
var artists []dbq.Artist
|
|
||||||
switch sort {
|
switch sort {
|
||||||
case "newest":
|
case "newest":
|
||||||
artists, err = q.ListArtistsNewest(r.Context(), dbq.ListArtistsNewestParams{
|
artists, err := q.ListArtistsNewest(r.Context(), dbq.ListArtistsNewestParams{
|
||||||
Limit: int32(limit), Offset: int32(offset),
|
Limit: int32(limit), Offset: int32(offset),
|
||||||
})
|
})
|
||||||
default: // alpha
|
if err != nil {
|
||||||
artists, err = q.ListArtistsAlpha(r.Context(), dbq.ListArtistsAlphaParams{
|
h.logger.Error("api: list artists newest", "err", err)
|
||||||
Limit: int32(limit), Offset: int32(offset),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
h.logger.Error("api: list artists failed", "err", err)
|
|
||||||
writeErr(w, http.StatusInternalServerError, "server_error", "lookup failed")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
total, err := q.CountArtists(r.Context())
|
|
||||||
if err != nil {
|
|
||||||
h.logger.Error("api: count artists failed", "err", err)
|
|
||||||
writeErr(w, http.StatusInternalServerError, "server_error", "count failed")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
items := make([]ArtistRef, 0, len(artists))
|
|
||||||
for _, a := range artists {
|
|
||||||
albums, aerr := q.ListAlbumsByArtist(r.Context(), a.ID)
|
|
||||||
if aerr != nil {
|
|
||||||
h.logger.Error("api: list albums for artist failed", "err", aerr)
|
|
||||||
writeErr(w, http.StatusInternalServerError, "server_error", "lookup failed")
|
writeErr(w, http.StatusInternalServerError, "server_error", "lookup failed")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
items = append(items, artistRefFrom(a, len(albums)))
|
total, terr := q.CountArtists(r.Context())
|
||||||
|
if terr != nil {
|
||||||
|
h.logger.Error("api: count artists", "err", terr)
|
||||||
|
writeErr(w, http.StatusInternalServerError, "server_error", "count failed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
items := make([]ArtistRef, 0, len(artists))
|
||||||
|
for _, a := range artists {
|
||||||
|
albums, aerr := q.ListAlbumsByArtist(r.Context(), a.ID)
|
||||||
|
if aerr != nil {
|
||||||
|
h.logger.Error("api: list albums for artist", "err", aerr)
|
||||||
|
writeErr(w, http.StatusInternalServerError, "server_error", "lookup failed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
items = append(items, artistRefFrom(a, len(albums)))
|
||||||
|
}
|
||||||
|
writeJSON(w, http.StatusOK, Page[ArtistRef]{
|
||||||
|
Items: items, Total: int(total), Limit: limit, Offset: offset,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
|
||||||
|
default: // alpha
|
||||||
|
rows, err := q.ListArtistsAlphaWithCovers(r.Context(), dbq.ListArtistsAlphaWithCoversParams{
|
||||||
|
Limit: int32(limit), Offset: int32(offset),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
h.logger.Error("api: list artists alpha", "err", err)
|
||||||
|
writeErr(w, http.StatusInternalServerError, "server_error", "lookup failed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
total, terr := q.CountArtists(r.Context())
|
||||||
|
if terr != nil {
|
||||||
|
h.logger.Error("api: count artists", "err", terr)
|
||||||
|
writeErr(w, http.StatusInternalServerError, "server_error", "count failed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
items := make([]ArtistRef, 0, len(rows))
|
||||||
|
for _, row := range rows {
|
||||||
|
items = append(items, artistRefFromCovered(row.Artist, int(row.AlbumCount), row.CoverAlbumID))
|
||||||
|
}
|
||||||
|
writeJSON(w, http.StatusOK, Page[ArtistRef]{
|
||||||
|
Items: items, Total: int(total), Limit: limit, Offset: offset,
|
||||||
|
})
|
||||||
|
return
|
||||||
}
|
}
|
||||||
writeJSON(w, http.StatusOK, Page[ArtistRef]{
|
|
||||||
Items: items,
|
|
||||||
Total: int(total),
|
|
||||||
Limit: limit,
|
|
||||||
Offset: offset,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user