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.
86 lines
2.8 KiB
Go
86 lines
2.8 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"reflect"
|
|
|
|
"music-server/internal/backend"
|
|
"music-server/internal/logging"
|
|
|
|
"github.com/danielgtaylor/huma/v2"
|
|
)
|
|
|
|
func (s *Server) registerSync(a huma.API) {
|
|
registry := a.OpenAPI().Components.Schemas
|
|
progress := huma.Operation{
|
|
OperationID: "get-sync-progress",
|
|
Method: http.MethodGet,
|
|
Path: "/sync/progress",
|
|
Summary: "Sync progress or result",
|
|
Description: "While a sync is running, returns its progress. Otherwise returns the result of the last sync.",
|
|
Tags: []string{"sync"},
|
|
Responses: map[string]*huma.Response{
|
|
"200": {
|
|
Description: "Progress of the running sync, or the result of the last sync",
|
|
Content: map[string]*huma.MediaType{"application/json": {Schema: &huma.Schema{OneOf: []*huma.Schema{
|
|
registry.Schema(reflect.TypeFor[backend.ProgressResponse](), true, ""),
|
|
registry.Schema(reflect.TypeFor[backend.SyncResponse](), true, ""),
|
|
}}}},
|
|
},
|
|
},
|
|
}
|
|
register(s, a, route{op: progress},
|
|
func(ctx context.Context, _ *struct{}) (*jsonBody[any], error) {
|
|
if s.sync.IsSyncing() {
|
|
return ok[any](s.sync.Progress(), nil)
|
|
}
|
|
return ok[any](s.sync.Result(), nil)
|
|
})
|
|
|
|
start := func(id, path, summary, description string, full bool) {
|
|
register(s, a, route{
|
|
op: huma.Operation{
|
|
OperationID: id,
|
|
Method: http.MethodPost,
|
|
Path: path,
|
|
Summary: summary,
|
|
Description: description,
|
|
Tags: []string{"sync"},
|
|
Errors: []int{http.StatusLocked},
|
|
},
|
|
}, func(ctx context.Context, _ *struct{}) (*jsonBody[string], error) {
|
|
message := "Start syncing soundtracks"
|
|
if full {
|
|
message += " full"
|
|
}
|
|
if !s.sync.Start(full) {
|
|
return nil, huma.Error423Locked("Syncing is in progress")
|
|
}
|
|
logging.GetLogger().Info(message)
|
|
return ok(message, nil)
|
|
})
|
|
}
|
|
start("start-sync", "/sync", "Sync changed soundtracks",
|
|
"Starts syncing the music folder in the background, skipping soundtracks that have not changed. "+
|
|
"Poll /sync/progress for the result.", false)
|
|
start("start-full-sync", "/sync/full", "Sync all soundtracks",
|
|
"Starts a full sync in the background that re-reads every soundtrack and removes songs whose files are gone. "+
|
|
"Poll /sync/progress for the result.", true)
|
|
|
|
reset := huma.Operation{
|
|
OperationID: "reset-library",
|
|
Method: http.MethodPost,
|
|
Path: "/sync/reset",
|
|
Summary: "Delete all soundtracks and songs",
|
|
Description: "For debugging only.",
|
|
Tags: []string{"sync"},
|
|
Hidden: true,
|
|
}
|
|
register(s, a, route{op: reset, locked: true},
|
|
func(ctx context.Context, _ *struct{}) (*jsonBody[string], error) {
|
|
logging.GetLogger().Info("Resetting soundtracks database")
|
|
return ok("Soundtracks and songs are deleted from the database", s.sync.ResetDB(ctx))
|
|
})
|
|
}
|