feat(server,web/m7-cover-sources): scan-orchestrator 4th stage

Adds an artist-art-enrich stage after cover-enrich in RunScan. The
stage drains EnrichArtistBatch with a configurable cap and the
operator's data_dir, persists processed/succeeded/failed tallies to
the scan_runs.artist_art_enrich jsonb column added in migration
0018.

The admin /admin Library Scan card grid expands from 3 to 4 columns
with the new "Artist art" card mirroring the cover-enrichment one
(Processed / Succeeded / Failed). The TS ScanStatus type gains an
optional artist_art_enrich field. The Go scanStatusResp gains the
matching ArtistArtEnrich json.RawMessage field.

cmd/minstrel/main.go (and admin scan-trigger handler) wire the new
RunScanConfig fields: ArtistEnrichCap reuses cfg.Library.CoverArtBackfillCap
for now (can be split if separate budgets are useful), DataDir is
cfg.Storage.DataDir.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-06 13:03:36 -04:00
parent 295d9da361
commit 00cd28e79b
7 changed files with 108 additions and 26 deletions
+7
View File
@@ -205,6 +205,12 @@ export type ScanStageCoverEnrich = {
failed: number;
};
export type ScanStageArtistArtEnrich = {
processed: number;
succeeded: number;
failed: number;
};
export type ScanStatus = {
id: string;
started_at: string;
@@ -212,6 +218,7 @@ export type ScanStatus = {
library?: ScanStageLibrary;
mbid_backfill?: ScanStageMbidBackfill;
cover_enrich?: ScanStageCoverEnrich;
artist_art_enrich?: ScanStageArtistArtEnrich;
error_message?: string;
in_flight: boolean;
};
+15 -1
View File
@@ -343,7 +343,7 @@
{#if !scan || !scan.id}
<p class="mt-3 text-sm text-text-muted">No scan has run yet.</p>
{:else}
<div class="mt-3 grid gap-3 sm:grid-cols-3">
<div class="mt-3 grid gap-3 sm:grid-cols-4">
<!-- Library stage -->
<div class="rounded border border-border bg-bg p-3">
<div class="text-xs font-medium uppercase tracking-wide text-text-muted">Library walk</div>
@@ -388,6 +388,20 @@
<p class="mt-2 text-sm text-text-muted">{scanInFlight ? 'Pending…' : 'Skipped.'}</p>
{/if}
</div>
<!-- Artist art enrichment stage -->
<div class="rounded border border-border bg-bg p-3">
<div class="text-xs font-medium uppercase tracking-wide text-text-muted">Artist art</div>
{#if scan.artist_art_enrich}
<dl class="mt-2 space-y-0.5 text-sm">
<div class="flex justify-between"><dt class="text-text-secondary">Processed</dt><dd>{scan.artist_art_enrich.processed}</dd></div>
<div class="flex justify-between"><dt class="text-text-secondary">Succeeded</dt><dd>{scan.artist_art_enrich.succeeded}</dd></div>
<div class="flex justify-between"><dt class="text-text-secondary">Failed</dt><dd>{scan.artist_art_enrich.failed}</dd></div>
</dl>
{:else}
<p class="mt-2 text-sm text-text-muted">{scanInFlight ? 'Pending…' : 'Skipped.'}</p>
{/if}
</div>
</div>
<div class="mt-3 text-xs text-text-muted">