Add the step-by-step fixer and library types
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>
This commit is contained in:
1 parent
b11d0806c1
commit
428935dc58
19 files changed
+3503
-23
No files matched your search
+13
-1
@@ -122,6 +122,11 @@ func (s *Server) Handler() http.Handler {
|
||||
r.Get("/renames", s.handleRenamePreview)
|
||||
r.Post("/renames", s.handleRenames)
|
||||
r.Post("/link-identical", s.handleLinkIdentical)
|
||||
r.Get("/fixer", s.handleFixer)
|
||||
r.Post("/fixer/check", s.handleFixCheck)
|
||||
r.Post("/fixer/apply", s.handleFixApply)
|
||||
r.Put("/fixer/step", s.handleFixStep)
|
||||
r.Post("/fixer/release", s.handleFixRelease)
|
||||
r.Get("/tree", s.handleTree)
|
||||
r.Post("/build", s.handleBuild)
|
||||
r.Post("/remove", s.handleRemove)
|
||||
@@ -322,13 +327,14 @@ func (s *Server) handleBrowse(w http.ResponseWriter, r *http.Request) {
|
||||
type pairView struct {
|
||||
store.Pair
|
||||
Naming engine.NamingConfig `json:"naming"`
|
||||
Fixer engine.FixerState `json:"fixer"`
|
||||
Summary store.Summary `json:"summary"`
|
||||
Job *engine.Job `json:"job"`
|
||||
}
|
||||
|
||||
func (s *Server) view(r *http.Request, p store.Pair) pairView {
|
||||
sum, _ := s.st.Summary(r.Context(), p.ID)
|
||||
return pairView{Pair: p, Naming: engine.NamingOf(p), Summary: sum, Job: s.eng.Job(p.ID)}
|
||||
return pairView{Pair: p, Naming: engine.NamingOf(p), Fixer: engine.FixerStateOf(p), Summary: sum, Job: s.eng.Job(p.ID)}
|
||||
}
|
||||
|
||||
func (s *Server) handleListPairs(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -367,6 +373,7 @@ type pairInput struct {
|
||||
ApproxMatching bool `json:"approx_matching"`
|
||||
ApproxThreshold int `json:"approx_threshold"`
|
||||
AutoOrganize string `json:"auto_organize"`
|
||||
LibraryType string `json:"library_type"`
|
||||
// Naming is optional: when absent, the pair keeps its current naming.
|
||||
Naming *engine.NamingConfig `json:"naming,omitempty"`
|
||||
}
|
||||
@@ -377,6 +384,9 @@ func (in pairInput) apply(p *store.Pair) {
|
||||
p.ScanIntervalMinutes, p.RetentionDays, p.SettleMinutes = in.ScanIntervalMinutes, in.RetentionDays, in.SettleMinutes
|
||||
p.ApproxMatching, p.ApproxThreshold = in.ApproxMatching, in.ApproxThreshold
|
||||
p.AutoOrganize = in.AutoOrganize
|
||||
if in.LibraryType != "" {
|
||||
p.LibraryType = in.LibraryType
|
||||
}
|
||||
if in.Naming != nil {
|
||||
p.OrganizeConfig, _ = json.Marshal(in.Naming)
|
||||
}
|
||||
@@ -420,6 +430,8 @@ func (s *Server) handleSavePair(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
in.apply(&p)
|
||||
engine.ChangeLibraryType(old, &p)
|
||||
engine.ChangeLibraryType(old, &p)
|
||||
warns, err := s.eng.NormalizePair(&p)
|
||||
if err != nil {
|
||||
writeErr(w, 400, err)
|
||||
|
||||
Reference in new issue
Block a user