feat(library): ignore a release (do-not-monitor)

Add MonitoredRelease.ignored (migration add_release_ignored). The monitor's
enqueue_due skips ignored releases, and they drop off the Wanted list —
independent of `monitored`, so auto-monitor-future can't silently re-grab
an album the user chose to ignore.

- Worker enqueue_due: AND NOT mr.ignored.
- /api/releases/[id] PATCH now accepts `ignored`; /api/wanted filters it out;
  artist detail exposes `ignored`.
- UI: Ignore button on Wanted rows; discography shows an "Ignored" badge with
  an Ignore/Un-ignore toggle (monitor + search disabled while ignored).

Worker + web tests added.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jonathan
2026-07-14 21:03:54 +02:00
parent 6ca39859fa
commit 855a0f15fe
10 changed files with 70 additions and 10 deletions
+17
View File
@@ -65,6 +65,20 @@ export function WantedClient() {
refresh();
}
async function ignore(w: Wanted) {
const res = await fetch(`/api/releases/${w.id}`, {
method: "PATCH",
headers: { "content-type": "application/json" },
body: JSON.stringify({ ignored: true }),
}).catch(() => null);
if (res?.ok) {
setRows((list) => list.filter((x) => x.id !== w.id));
toast(`Ignoring ${w.album} — un-ignore from the artists discography`);
} else {
toast(`Couldn't ignore ${w.album}`);
}
}
return (
<div>
<PageHead title="Wanted" eyebrow="Cutting list · acquire & upgrade" />
@@ -109,6 +123,9 @@ export function WantedClient() {
<button className="btn sm ghost" onClick={() => searchNow(w)}>
Search now
</button>
<button className="btn sm ghost" onClick={() => ignore(w)}>
Ignore
</button>
</div>
</li>
))}