From 3c3102aac699b05d539be1ba93e33add05072ea1 Mon Sep 17 00:00:00 2001 From: Caleb Allen Date: Sat, 28 Mar 2026 10:25:04 -0400 Subject: [PATCH] fix: start index build only after ALL library scans complete MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FullRescan scans the first library directly, then queues the rest. The PostScan hook was restarting the index build after the FIRST library, which starved the queued libraries for DB access — they never scanned, leaving the library with only 6 tracks. Fix: move StartIndexBuild to the OnAllScansComplete hook, which fires when drainQueue finds no more libraries to scan. This ensures ALL libraries finish scanning before the index build starts. For startup soft scans: if no scans were queued (library unchanged), start the index build directly. If scans WERE queued, the hook handles it. Added OnAllScansComplete callback to ScanHooks. Called from drainQueue when the scan pipeline goes idle. --- backend/app.go | 18 +++++++++++++----- backend/library/library.go | 3 +++ backend/library/scan_queue.go | 5 +++++ 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/backend/app.go b/backend/app.go index bb032ce..66f5911 100644 --- a/backend/app.go +++ b/backend/app.go @@ -206,8 +206,10 @@ func (yj *YellowJacketApp) OnStartup(ctx context.Context) { }, PostScan: func() { yj.playlist.RestoreAllPlaylists() - // Restart the index build now that the scan is done. - yj.explore.StartIndexBuild() + // DON'T restart the index build here — queued + // library scans may still be running. The index + // build starts after ALL scans complete (via the + // scan hooks below). }, }) @@ -215,6 +217,9 @@ func (yj *YellowJacketApp) OnStartup(ctx context.Context) { // phantom tracks after each library scan completes. yj.library.SetScanHooks(library.ScanHooks{ ResolvePhantoms: yj.playlist.ResolvePhantomTracksAfterScan, + OnAllScansComplete: func() { + yj.explore.StartIndexBuild() + }, }) // Wire removal hooks so the library can stop playback and @@ -329,8 +334,11 @@ func (yj *YellowJacketApp) OnDomReady(ctx context.Context) { yj.logger.Error("soft scan failed", "err", err) } - // Start the explore search index build AFTER the library - // scan completes so they don't fight for DB access. - yj.explore.StartIndexBuild() + // If no scans were queued (library unchanged), start the + // index build directly. If scans WERE queued, the + // OnAllScansComplete hook starts it after they finish. + if yj.library.GetScanQueueLength() == 0 && !yj.library.IsScanActive() { + yj.explore.StartIndexBuild() + } }() } diff --git a/backend/library/library.go b/backend/library/library.go index dc4b5cc..e30b015 100644 --- a/backend/library/library.go +++ b/backend/library/library.go @@ -79,6 +79,9 @@ type ScanHooks struct { // ResolvePhantoms re-links phantom playlist tracks whose // files now exist in the library after scanning. ResolvePhantoms func() + // OnAllScansComplete runs after ALL queued scans finish + // (queue drained). + OnAllScansComplete func() } // Library manages scanning and querying the music collection. diff --git a/backend/library/scan_queue.go b/backend/library/scan_queue.go index fc46018..3c3e3b4 100644 --- a/backend/library/scan_queue.go +++ b/backend/library/scan_queue.go @@ -254,7 +254,12 @@ func (l *Library) drainQueue() { l.currentScanLibraryID = 0 l.currentScanLibraryName = "" l.scanActive = false + hooks := l.scanHooks l.mu.Unlock() runtime.EventsEmit(l.ctx, events.LibraryScanQueueDrained) + + if hooks.OnAllScansComplete != nil { + hooks.OnAllScansComplete() + } }