From 8980cfe717e6b6d7dbf68b57706bb0108d08e89f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Mon, 27 Jul 2026 22:38:18 +0000 Subject: [PATCH] =?UTF-8?q?music:=20POST=20/reindex=3Ffull=3D1=20=E2=80=94?= =?UTF-8?q?=20on-demand=20full=20staged=20rebuild?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The default POST /reindex is incremental (skips unchanged albums by version stamp), so it can't backfill a meta-format change like the new track `disc` field. Add ?full=1 to run reindexFull() instead — a from-scratch rebuild into a fresh slot, atomically swapped in (safe, never disrupts the live index). Same 30-min per-request timeout applies. The nightly 3am run still does this automatically; this is the on-demand trigger. Co-Authored-By: Claude Opus 4.8 --- src/servers/sidecar/music/index.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/servers/sidecar/music/index.ts b/src/servers/sidecar/music/index.ts index 7342b3fa..8454c358 100644 --- a/src/servers/sidecar/music/index.ts +++ b/src/servers/sidecar/music/index.ts @@ -7,6 +7,7 @@ import { startNightlyReindex, stopNightlyReindex } from './nightly-reindex'; import { startMusicWatcher, stopMusicWatcher } from './watcher'; import { reindexNow, + reindexFull, ensureCacheSetup, MUSIC_ROOT, getIndexStatus, @@ -49,7 +50,9 @@ const DATA_PATH = process.env.DATA_PATH ?? join(process.cwd(), 'data'); // "" } }, Type ∈ Studio/Live/Compilation/Single/EP/… // ETag: ; If-None-Match → 304. Source: each artist folder's // _discography.md (normalized; the md itself is never modified). -// POST /reindex run the build to COMPLETION, then return the final IndexStatus. +// POST /reindex[?full=1] run the build to COMPLETION, then return the final IndexStatus. +// default = incremental (skips unchanged); ?full=1 = full staged +// rebuild + atomic swap (backfill a meta-format change). // GET /reindex/status IndexStatus snapshot. // GET /reindex/stream SSE. Triggers a build if idle (`?trigger=0` = watch-only). // `event: progress` (IndexStatus) throttled ~200ms, then one @@ -112,10 +115,13 @@ const server = Bun.serve({ // ── Index build ── if (url.pathname === '/reindex') { if (req.method !== 'POST') return new Response('Method not allowed', { status: 405 }); - // Run the resync to completion, THEN respond — so the caller's manifest read right after is fresh. - // Incremental builds are near-instant (unchanged albums skip by version stamp). reindexNow joins - // an in-flight build rather than starting a second. - const result = await reindexNow(); + // Run to completion, THEN respond — so the caller's manifest read right after is fresh. Both join an + // in-flight build rather than starting a second. + // default : incremental — near-instant, skips unchanged albums by version stamp. + // ?full=1 : full from-scratch rebuild into a fresh slot, swapped in atomically (staged + safe) — + // use to backfill a meta-format change (e.g. a new track field) across the WHOLE library. + const full = url.searchParams.get('full') === '1' || url.searchParams.get('full') === 'true'; + const result = await (full ? reindexFull() : reindexNow()); return json(result); } if (url.pathname === '/reindex/status') return json(getIndexStatus());