From 7b180e26db71002301e056467aa904d09a40b357 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 30 Sep 2026 10:21:45 -0700 Subject: [PATCH] Rename the Fixer to Tune-up Tab, URL (#/pair//tune-up; old fixer links still work), job names, History trigger and docs now say Tune-up. Co-Authored-By: Claude Opus 5.5 --- README.md | 4 ++-- internal/api/fixer.go | 6 +++--- internal/engine/fixer.go | 12 ++++++------ web/src/App.tsx | 2 +- web/src/api.ts | 2 +- web/src/pages/PairForm.tsx | 2 +- web/src/pages/PairPage.tsx | 6 +++--- web/src/pages/{Fixer.tsx => TuneUp.tsx} | 8 ++++---- 8 files changed, 21 insertions(+), 21 deletions(-) rename web/src/pages/{Fixer.tsx => TuneUp.tsx} (99%) diff --git a/README.md b/README.md index ebf9348..80e2019 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,12 @@ up disk space only once, while both libraries keep looking exactly the same to P It ships as a single Go binary with an embedded React web UI and SQLite state, packaged as a small Docker image, in the style of [qui](https://github.com/autobrr/qui). -## Workflow: fix, organize, then link +## Workflow: tune up, then link Each library pair has a **type**: **Music** (recommended layout `Artist/Album/01 Title.flac`) or **Soundtracks** for video game and film soundtracks (`Album/01 Title.flac`, named by game or film). The type sets the default naming; the *Organize* tab can change it. -The **Fixer** tab takes each library through the same steps, the **master first, then the slave**, and then links the two: +The **Tune-up** tab takes each library through the same steps, the **master first, then the slave**, and then links the two: 1. **Folder structure.** Every album in its own folder at the right depth. Songs outside an album folder move into one named from their tags; extra folders around an album (a composer folder around a soundtrack, `Metal/Artist/Album`, a `FLAC` folder inside an album) are diff --git a/internal/api/fixer.go b/internal/api/fixer.go index e228b04..8727fcb 100644 --- a/internal/api/fixer.go +++ b/internal/api/fixer.go @@ -6,7 +6,7 @@ import ( "github.com/cdnutter/playlink/internal/engine" ) -// ---- step-by-step fixer ---- +// ---- tune-up (step-by-step library fixing) ---- func (s *Server) handleFixer(w http.ResponseWriter, r *http.Request) { id, _ := idParam(r) @@ -43,7 +43,7 @@ func (s *Server) handleFixApply(w http.ResponseWriter, r *http.Request) { if !readJSON(w, r, &body) { return } - if !s.preAction(w, r, id, "fixer") { + if !s.preAction(w, r, id, "tune-up") { return } j, err := s.eng.StartFixApply(id, body) @@ -51,7 +51,7 @@ func (s *Server) handleFixApply(w http.ResponseWriter, r *http.Request) { opErr(w, err) return } - s.log.Info("fixer apply started", "pair", id, "selected", len(body.IDs)) + s.log.Info("tune-up apply started", "pair", id, "selected", len(body.IDs)) writeJSON(w, 202, j) } diff --git a/internal/engine/fixer.go b/internal/engine/fixer.go index 7286167..e3dbba4 100644 --- a/internal/engine/fixer.go +++ b/internal/engine/fixer.go @@ -16,7 +16,7 @@ import ( "github.com/cdnutter/playlink/internal/store" ) -// The fixer takes each library through the same steps, the master first and +// The tune-up takes each library through the same steps, the master first and // then the slave: // // 1. structure: every album in its own folder at the right depth (Artist/ @@ -62,7 +62,7 @@ type FixerState struct { Link StepStatus `json:"link"` } -// FixerStateOf reads a pair's fixer progress. +// FixerStateOf reads a pair's tune-up progress. func FixerStateOf(p store.Pair) FixerState { var s FixerState if len(p.FixerState) > 0 { @@ -204,7 +204,7 @@ func (e *Engine) StartFixCheck(pairID int64, c FixCheck) (*Job, error) { if c.Step == StepTags && e.meta == nil { return nil, errors.New("online lookup is not available") } - return e.startJob(pairID, "fix-"+c.Step, func(ctx context.Context, j *Job) error { + return e.startJob(pairID, "tune-up "+c.Step, func(ctx context.Context, j *Job) error { l, err := e.loadPair(ctx, pairID) if err != nil { return err @@ -292,7 +292,7 @@ func (e *Engine) StartFixApply(pairID int64, req FixApply) (*Job, error) { return nil, fmt.Errorf("%w: nothing selected", ErrInvalidBulk) } side, step := plan.Side, plan.Step - return e.startJob(pairID, "fix-"+step+"-apply", func(ctx context.Context, j *Job) error { + return e.startJob(pairID, "tune-up "+step+" (apply)", func(ctx context.Context, j *Job) error { l, err := e.loadPair(ctx, pairID) if err != nil { return err @@ -300,7 +300,7 @@ func (e *Engine) StartFixApply(pairID int64, req FixApply) (*Job, error) { e.fixMu.Lock() plan.Applied = true // paths and tags change: the plan is stale e.fixMu.Unlock() - trigger := "fixer" + trigger := "tune-up" edited := map[string]bool{} if n := len(tagItems) + songsIn(albums); n > 0 { j.phase("Updating tags", int64(n)) @@ -369,7 +369,7 @@ func (e *Engine) StartFixApply(pairID int64, req FixApply) (*Job, error) { // Done when every fix was applied and nothing needs a person. if !dry && skipped == 0 && applied >= plan.Fixable && plan.Blocked == 0 { if err := e.SetStep(ctx, pairID, side, step, "done", fmt.Sprintf("%d fixed", applied)); err != nil { - e.log.Warn("fixer step not recorded", "err", err) + e.log.Warn("tune-up step not recorded", "err", err) } } return nil diff --git a/web/src/App.tsx b/web/src/App.tsx index 9b64400..5373f44 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -65,7 +65,7 @@ function Shell({ username }: { username: string }) { ]; let page; - if (route[0] === "pair" && route[1]) page = ; + if (route[0] === "pair" && route[1]) page = ; else if (route[0] === "activity") page = ; else if (route[0] === "settings") page = ; else page = ; diff --git a/web/src/api.ts b/web/src/api.ts index a4ce75c..c1340f4 100644 --- a/web/src/api.ts +++ b/web/src/api.ts @@ -107,7 +107,7 @@ export type PairInput = Pick< export type LibraryType = "music" | "soundtracks"; -// ---- step-by-step fixer ---- +// ---- tune-up (step-by-step library fixing) ---- export type FixStep = "structure" | "tags" | "folders" | "files"; export type StepStatus = { state: "" | "done" | "skipped"; at?: number; note?: string }; diff --git a/web/src/pages/PairForm.tsx b/web/src/pages/PairForm.tsx index 887807d..d59e517 100644 --- a/web/src/pages/PairForm.tsx +++ b/web/src/pages/PairForm.tsx @@ -78,7 +78,7 @@ export function PairForm({ pair, open, onClose }: { pair?: Pair; open: boolean; onSuccess: () => { qc.invalidateQueries({ queryKey: ["pairs"] }); qc.invalidateQueries({ queryKey: ["pair"] }); - toast("ok", pair ? "Library pair updated" : "Library pair created. Start with the Fixer, master library first."); + toast("ok", pair ? "Library pair updated" : "Library pair created. Start with the Tune-up, master library first."); onClose(); }, }); diff --git a/web/src/pages/PairPage.tsx b/web/src/pages/PairPage.tsx index d990eb1..4343825 100644 --- a/web/src/pages/PairPage.tsx +++ b/web/src/pages/PairPage.tsx @@ -12,7 +12,7 @@ import { OpsTable } from "./Activity"; import { WarningList } from "./Warnings"; import { TagsTab } from "./Tags"; import { OrganizeTab } from "./Organize"; -import { FixerTab } from "./Fixer"; +import { TuneUpTab } from "./TuneUp"; export function usePair(id: number) { return useQuery({ @@ -125,7 +125,7 @@ function LastJob({ job }: { job: Job }) { } const tabs = [ - { id: "fixer", label: "Fixer" }, + { id: "tune-up", label: "Tune-up" }, { id: "review", label: "Review" }, { id: "build", label: "Build" }, { id: "tags", label: "Tags" }, @@ -208,7 +208,7 @@ export function PairPage({ id, tab }: { id: number; tab: string }) { ))} - {tab === "fixer" && } + {tab === "tune-up" && } {tab === "review" && } {tab === "build" && } {tab === "tags" && } diff --git a/web/src/pages/Fixer.tsx b/web/src/pages/TuneUp.tsx similarity index 99% rename from web/src/pages/Fixer.tsx rename to web/src/pages/TuneUp.tsx index 230e32d..315000e 100644 --- a/web/src/pages/Fixer.tsx +++ b/web/src/pages/TuneUp.tsx @@ -26,7 +26,7 @@ import { JobBar, ScanButton } from "./PairPage"; import { WarningList } from "./Warnings"; import { AlbumEditor } from "./Album"; -// The fixer takes each library through the same steps, master first, then +// The tune-up takes each library through the same steps, master first, then // links the two. Each step is checked, reviewed and applied here. type Sel = { side: Side; step: FixStep } | "link"; @@ -69,12 +69,12 @@ function firstOpen(pair: Pair): Sel { return "link"; } -export function FixerTab({ pair }: { pair: Pair }) { +export function TuneUpTab({ pair }: { pair: Pair }) { const info = useInfo(); const [sel, setSel] = useState(() => firstOpen(pair)); const fixer = useQuery({ queryKey: ["fixer", pair.id], queryFn: () => api.get(`/pairs/${pair.id}/fixer`) }); if (info.data && !info.data.tag_editing) - return The fixer reads and writes tags with ffmpeg and ffprobe, which were not found. The Docker image includes both.; + return The tune-up reads and writes tags with ffmpeg and ffprobe, which were not found. The Docker image includes both.; const isSel = (s: Side, st: FixStep) => sel !== "link" && sel.side === s && sel.step === st; return ( @@ -217,7 +217,7 @@ function StepPanel({ pair, side, step, fixer, onNext }: { pair: Pair; side: Side const status = pair.fixer[side][step]; const plan = fixer?.plan && fixer.plan.side === side && fixer.plan.step === step ? fixer.plan : null; const running = pair.job?.state === "running"; - const ourJob = running && pair.job!.kind.startsWith("fix-"); + const ourJob = running && pair.job!.kind.startsWith("tune-up"); const refresh = () => { qc.invalidateQueries({ queryKey: ["pair", pair.id] }); qc.invalidateQueries({ queryKey: ["fixer", pair.id] });