package subsonic import ( "encoding/json" "git.noroutes.net/NoRoutes/vibetunes/internal/database" ) func init() { registerExtension("indexBasedQueue", 1) register("getPlayQueueByIndex", handleGetPlayQueueByIndex) register("savePlayQueueByIndex", handleSavePlayQueueByIndex) } // The indexBasedQueue extension shares the same play_queues storage as // the classic getPlayQueue/savePlayQueue (internal/subsonic/playqueue.go) // — the table has always tracked the current *index*, not a song ID; the // classic endpoints are the ones that lossily resolve index -> song ID // for their "current" attribute. Duplicate song IDs in the queue are // therefore already handled correctly by construction: saving stores a // plain ordered ID list, and both current-track values (index here, // looked-up ID for the classic endpoint) are derived from the same // unambiguous index. func handleGetPlayQueueByIndex(rc *Context) *apiError { db := rc.Server.DB q, err := database.GetPlayQueue(db, rc.User.ID) if err == database.ErrNotFound { rc.Response = newResponse() return nil } if err != nil { return newError(ErrGeneric, err.Error()) } var ids []string if uerr := json.Unmarshal([]byte(q.SongIDsJSON), &ids); uerr != nil { return newError(ErrGeneric, "corrupt saved play queue") } out := &PlayQueueByIndex{ CurrentIndex: q.CurrentIndex, Position: int64(q.PositionMs), Username: rc.User.Username, Changed: isoTime(q.UpdatedAt), ChangedBy: q.Client, } for _, id := range ids { song, serr := database.GetSong(db, id) if serr != nil { continue } mf, merr := database.GetMediaFile(db, song.MediaFileID) if merr != nil { continue } out.Entry = append(out.Entry, childFromSong(db, song, mf, albumOfSong(db, song), rc.User.ID)) } rc.Response = newResponse() rc.Response.PlayQueueByIndex = out return nil } func handleSavePlayQueueByIndex(rc *Context) *apiError { ids := rc.Params.GetAll("id") position := rc.Params.Int("position", 0) client := rc.Params.Get("c") currentIndex := 0 if len(ids) > 0 { var aerr *apiError currentIndex, aerr = rc.Params.RequiredInt("currentIndex") if aerr != nil { return aerr } if currentIndex < 0 || currentIndex >= len(ids) { return newError(ErrGeneric, "currentIndex out of range") } } encoded, err := json.Marshal(ids) if err != nil { return newError(ErrGeneric, err.Error()) } if err := database.SavePlayQueue(rc.Server.DB, rc.User.ID, string(encoded), currentIndex, position, client); err != nil { return newError(ErrGeneric, err.Error()) } rc.Response = newResponse() return nil }