Each pair is a Music (Artist/Album/01 Title) or Soundtracks (Album/01 Title) library. The new Fixer tab takes the master, then the slave, through four reviewed steps: folder structure, album tags (optional MusicBrainz lookup; the slave reuses the fixed master's tags), folder names and file names, then links the two libraries. Every fix is an undoable operation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
95 lines
2.2 KiB
Go
95 lines
2.2 KiB
Go
package api
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/cdnutter/playlink/internal/engine"
|
|
)
|
|
|
|
// ---- step-by-step fixer ----
|
|
|
|
func (s *Server) handleFixer(w http.ResponseWriter, r *http.Request) {
|
|
id, _ := idParam(r)
|
|
p, err := s.st.Pair(r.Context(), id)
|
|
if err != nil {
|
|
opErr(w, err)
|
|
return
|
|
}
|
|
out := map[string]any{"state": engine.FixerStateOf(p), "steps": engine.FixerSteps, "plan": nil,
|
|
"items": []engine.FixItem{}, "albums": []*engine.AlbumFix{}}
|
|
if plan, items, albums := s.eng.FixerPlan(id); plan != nil {
|
|
out["plan"], out["items"], out["albums"] = plan, items, albums
|
|
}
|
|
writeJSON(w, 200, out)
|
|
}
|
|
|
|
func (s *Server) handleFixCheck(w http.ResponseWriter, r *http.Request) {
|
|
id, _ := idParam(r)
|
|
var body engine.FixCheck
|
|
if !readJSON(w, r, &body) {
|
|
return
|
|
}
|
|
j, err := s.eng.StartFixCheck(id, body)
|
|
if err != nil {
|
|
opErr(w, err)
|
|
return
|
|
}
|
|
writeJSON(w, 202, j)
|
|
}
|
|
|
|
func (s *Server) handleFixApply(w http.ResponseWriter, r *http.Request) {
|
|
id, _ := idParam(r)
|
|
var body engine.FixApply
|
|
if !readJSON(w, r, &body) {
|
|
return
|
|
}
|
|
if !s.preAction(w, r, id, "fixer") {
|
|
return
|
|
}
|
|
j, err := s.eng.StartFixApply(id, body)
|
|
if err != nil {
|
|
opErr(w, err)
|
|
return
|
|
}
|
|
s.log.Info("fixer apply started", "pair", id, "selected", len(body.IDs))
|
|
writeJSON(w, 202, j)
|
|
}
|
|
|
|
func (s *Server) handleFixStep(w http.ResponseWriter, r *http.Request) {
|
|
id, _ := idParam(r)
|
|
var body struct {
|
|
Side string `json:"side"` // master, slave, or "" for the link step
|
|
Step string `json:"step"`
|
|
State string `json:"state"` // done, skipped or "" (to do)
|
|
}
|
|
if !readJSON(w, r, &body) {
|
|
return
|
|
}
|
|
if err := s.eng.SetStep(r.Context(), id, body.Side, body.Step, body.State, ""); err != nil {
|
|
opErr(w, err)
|
|
return
|
|
}
|
|
s.handleFixer(w, r)
|
|
}
|
|
|
|
func (s *Server) handleFixRelease(w http.ResponseWriter, r *http.Request) {
|
|
id, _ := idParam(r)
|
|
var body struct {
|
|
Album int `json:"album"`
|
|
Release string `json:"release"` // a MusicBrainz release id, or "search"
|
|
}
|
|
if !readJSON(w, r, &body) {
|
|
return
|
|
}
|
|
if j := s.eng.Job(id); j != nil && j.State == "running" {
|
|
writeErr(w, 409, engine.ErrBusy)
|
|
return
|
|
}
|
|
a, err := s.eng.ChooseRelease(r.Context(), id, body.Album, body.Release)
|
|
if err != nil {
|
|
opErr(w, err)
|
|
return
|
|
}
|
|
writeJSON(w, 200, a)
|
|
}
|