Files
navidrome/server/nativeapi/queue.go
Deluan Quintão 8fcd8ba61a feat(server): add index-based play queue endpoints to native API (#4210)
* Add migration converting playqueue current to index

* refactor

Signed-off-by: Deluan <deluan@navidrome.org>

* fix(queue): ensure valid current index and improve test coverage

Signed-off-by: Deluan <deluan@navidrome.org>

---------

Signed-off-by: Deluan <deluan@navidrome.org>
2025-06-10 23:00:44 -04:00

77 lines
2.1 KiB
Go

package nativeapi
import (
"encoding/json"
"errors"
"net/http"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/model/request"
"github.com/navidrome/navidrome/utils/slice"
)
type queuePayload struct {
Ids []string `json:"ids"`
Current int `json:"current"`
Position int64 `json:"position"`
}
func getQueue(ds model.DataStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
user, _ := request.UserFrom(ctx)
repo := ds.PlayQueue(ctx)
pq, err := repo.Retrieve(user.ID)
if err != nil && !errors.Is(err, model.ErrNotFound) {
log.Error(ctx, "Error retrieving queue", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if pq == nil {
pq = &model.PlayQueue{}
}
resp, err := json.Marshal(pq)
if err != nil {
log.Error(ctx, "Error marshalling queue", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(resp)
}
}
func saveQueue(ds model.DataStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var payload queuePayload
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
user, _ := request.UserFrom(ctx)
client, _ := request.ClientFrom(ctx)
items := slice.Map(payload.Ids, func(id string) model.MediaFile {
return model.MediaFile{ID: id}
})
if len(payload.Ids) > 0 && (payload.Current < 0 || payload.Current >= len(payload.Ids)) {
http.Error(w, "current index out of bounds", http.StatusBadRequest)
return
}
pq := &model.PlayQueue{
UserID: user.ID,
Current: payload.Current,
Position: max(payload.Position, 0),
ChangedBy: client,
Items: items,
}
if err := ds.PlayQueue(ctx).Store(pq); err != nil {
log.Error(ctx, "Error saving queue", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusNoContent)
}
}