package api import ( "net/http" "strings" "git.fabledsword.com/bvandeusen/minstrel/internal/apierror" "git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq" "git.fabledsword.com/bvandeusen/minstrel/internal/library" ) // maxReplayGainIDs caps one lookup. A player asks for the tracks around the // one it is about to play, never a whole library. const maxReplayGainIDs = 200 // replayGainResp is the wire shape for GET /api/tracks/replay-gain. Tracks // with no gain known are absent from items: no adjustment. type replayGainResp struct { Items map[string]library.ReplayGain `json:"items"` } // handleGetReplayGain implements GET /api/tracks/replay-gain?ids=a,b,c (M464 // #4997): ReplayGain 2.0 values (dB to -18 LUFS, linear peaks) for each track, // and for its album when the whole album is measured. // // A lookup rather than fields on TrackRef. Tracks reach the player through // about fifteen surfaces in two wire shapes (TrackRef and playlist entries), // and only the player uses the gain. One call from the player, made for the // tracks it is about to play, cannot be forgotten by a surface added later, // and costs nothing on the pages that never play anything. Android takes the // same values from the sync feed instead, so they are there offline. func (h *handlers) handleGetReplayGain(w http.ResponseWriter, r *http.Request) { raw := strings.TrimSpace(r.URL.Query().Get("ids")) if raw == "" { writeJSON(w, http.StatusOK, replayGainResp{Items: map[string]library.ReplayGain{}}) return } parts := strings.Split(raw, ",") if len(parts) > maxReplayGainIDs { writeErr(w, apierror.BadRequest("too_many_ids", "at most 200 ids per request")) return } ids := stringsToUUIDs(parts) gains, err := library.ReplayGainForTracks(r.Context(), dbq.New(h.pool), ids) if err != nil { writeErrWithLog(w, h.logger, "replay gain: lookup", apierror.InternalMsg("lookup failed", err)) return } items := make(map[string]library.ReplayGain, len(gains)) for id, g := range gains { items[uuidToString(id)] = g } writeJSON(w, http.StatusOK, replayGainResp{Items: items}) }