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>
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user