Files
MusicServer/internal/server/library_handler.go
T
Sansan a705d7b476 Store each client's play queue in the database
The queue lived in memory and was shared by all clients, so a restart or a
finished sync cleared it. Every client now has its own queue in the new
queue and queue_song tables (added to the undeployed migration 000006).

The music endpoints return the queue ID in the X-Queue-Id header and expect
it back. A request without an ID, or with an ended one, starts a new queue.
/music/reset ends the queue and returns a new ID, and a sync ends all
running queues. Ended queues are kept as history.

The deprecated legacy routes without the /api/v1 prefix are removed.
2026-10-07 20:03:03 +02:00

60 lines
1.9 KiB
Go

package server
import (
"context"
"net/http"
"music-server/internal/backend"
"github.com/danielgtaylor/huma/v2"
"github.com/google/uuid"
)
type uuidInput struct {
UUID string `path:"uuid" format:"uuid" doc:"UUID of the soundtrack or song"`
}
func (in *uuidInput) id() uuid.UUID {
// The format is validated by Huma before the handler runs.
return uuid.MustParse(in.UUID)
}
func (s *Server) registerLibrary(a huma.API) {
l := s.library
op := func(id, path, summary, description string, errs ...int) huma.Operation {
return huma.Operation{
OperationID: id,
Method: http.MethodGet,
Path: path,
Summary: summary,
Description: description,
Tags: []string{"library"},
Errors: errs,
}
}
register(s, a, route{op: op("list-soundtracks", "/soundtracks", "List soundtracks",
"Returns all soundtracks, sorted by name.")},
func(ctx context.Context, _ *struct{}) (*jsonBody[[]backend.Soundtrack], error) {
return ok(l.Soundtracks(ctx))
})
register(s, a, route{op: op("get-soundtrack", "/soundtracks/{uuid}", "Get soundtrack",
"Returns a soundtrack with its songs, sorted by name.", http.StatusNotFound)},
func(ctx context.Context, in *uuidInput) (*jsonBody[backend.SoundtrackWithSongList], error) {
return ok(l.Soundtrack(ctx, in.id()))
})
register(s, a, route{op: op("get-song", "/songs/{uuid}", "Get song",
"Returns information about a song.", http.StatusNotFound)},
func(ctx context.Context, in *uuidInput) (*jsonBody[backend.Song], error) {
return ok(l.Song(ctx, in.id()))
})
file := op("get-song-file", "/songs/{uuid}/file", "Stream song",
"Returns the audio file of a song.", http.StatusNotFound)
file.Responses = fileResponses("The song (MP3)", audioMPEG)
register(s, a, route{op: file, locked: true},
func(ctx context.Context, in *uuidInput) (*huma.StreamResponse, error) {
return streamSong(l.SongPath(ctx, in.id()))
})
}