feat(server/web/coverart): manual Re-search missing art button

POST /api/admin/cover-sources/research bumps cover_art_sources_meta.
current_version unconditionally; the next enrichment pass re-eligibles
every 'none' row. Existing positively-sourced rows are not affected —
the version-mismatch eligibility rule only kicks in for 'none'.

Admin Cover Art panel gains a "Re-search missing art" button that
calls the endpoint and shows a confirmation toast. Operator's escape
hatch when:

- They've added a Last.fm key and want to retry failed rows
  immediately rather than waiting for the next scheduled scan.
- An upstream provider's catalog has updated (e.g. Deezer added a
  back-catalog import).
- They want to verify a fix end-to-end before the next scheduled
  scan fires.

SettingsService.BumpVersion (the unconditional version of T5's
BumpVersionIfProvidersChanged) is the single-call DB writer; both
endpoints route through it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-07 07:59:40 -04:00
parent 8f5ec4c304
commit c53bcb6c99
9 changed files with 224 additions and 1 deletions
+24
View File
@@ -19,6 +19,7 @@
deleteQuarantineViaLidarr,
refetchMissingCovers,
triggerScan,
researchMissingArt,
type ScanSchedule,
type ScanScheduleMode
} from '$lib/api/admin';
@@ -315,6 +316,21 @@
let bulkBusy = $state(false);
let bulkResult = $state<string | null>(null);
// ---- Re-search missing art ----
let researchSaving = $state(false);
async function onResearchMissingArt() {
researchSaving = true;
try {
await researchMissingArt();
showToast('All previously-failed art will be re-attempted on the next scan.');
} catch (e) {
showToast(`Re-search failed: ${(e as { code?: string })?.code ?? 'unknown'}`);
} finally {
researchSaving = false;
}
}
async function onBulkRefetch() {
if (bulkBusy) return;
bulkBusy = true;
@@ -603,6 +619,14 @@
>
{bulkBusy ? 'Queueing…' : 'Refetch missing covers'}
</button>
<button
type="button"
onclick={onResearchMissingArt}
disabled={researchSaving}
class="rounded-md border border-border px-3 py-1.5 text-sm text-text-secondary hover:bg-surface-hover hover:text-text-primary disabled:opacity-50"
>
{researchSaving ? 'Bumping…' : 'Re-search missing art'}
</button>
{#if coverage}
{#if coverage.total === 0}