From cf004986c95732d00208e83467267904ea3f2ef6 Mon Sep 17 00:00:00 2001 From: Caleb Allen Date: Fri, 13 Mar 2026 11:17:37 -0400 Subject: [PATCH] fix(12-02): wait for scan to stop before library removal, surface errors in UI RemoveLibrary now polls until the cancelled scan goroutine finishes before proceeding with the removal transaction. Also show removal errors as toast messages instead of only logging to console, and explicitly reload library list after successful removal. --- backend/library/crud.go | 24 ++++++++++++++++++- .../src/components/config-page/config-page.ts | 4 ++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/backend/library/crud.go b/backend/library/crud.go index 10b1454..523d4cd 100644 --- a/backend/library/crud.go +++ b/backend/library/crud.go @@ -7,6 +7,7 @@ import ( "os" "path/filepath" "strings" + "time" "github.com/wailsapp/wails/v2/pkg/runtime" @@ -200,8 +201,9 @@ func (l *Library) GetRemovalImpact(libraryID int64) (*RemovalImpact, error) { // performing orphan cleanup, phantom metadata conversion, FTS5 // rebuild, and queue compaction. Returns a summary of what was removed. func (l *Library) RemoveLibrary(id int64) (*RemovalSummary, error) { - // 1. Cancel active scan for this library. + // 1. Cancel active scan for this library and wait for it to stop. l.cancelLibraryScan(id) + l.waitForScanIdle(id) // 2. Stop playback if the currently-playing track belongs to this library. if l.currentTrackBelongsToLibrary(id) { @@ -465,6 +467,26 @@ func (l *Library) RemoveLibrary(id int64) (*RemovalSummary, error) { return summary, nil } +// waitForScanIdle polls until the specified library is no longer the +// active scan target. Called after cancelLibraryScan to ensure the +// scan goroutine has finished before proceeding with removal. +func (l *Library) waitForScanIdle(id int64) { + for range 100 { // up to ~5 seconds + l.mu.Lock() + active := l.currentScanLibraryID == id + l.mu.Unlock() + + if !active { + return + } + + time.Sleep(50 * time.Millisecond) + } + + l.logger.Warn("timed out waiting for scan to stop", + "libraryID", id) +} + // cancelLibraryScan cancels an active scan for the specified library // and removes it from the scan queue. func (l *Library) cancelLibraryScan(id int64) { diff --git a/frontend/src/components/config-page/config-page.ts b/frontend/src/components/config-page/config-page.ts index 4ad671d..6157c31 100644 --- a/frontend/src/components/config-page/config-page.ts +++ b/frontend/src/components/config-page/config-page.ts @@ -1396,9 +1396,13 @@ export class ConfigPage extends LitElement { this.showToast( `Removed '${libName}' (${summary?.tracksDeleted ?? 0} tracks deleted)`, ); + void this.loadLibraries(); } catch (err) { this.isRemoving = false; + this.removingLibraryId = null; + this.removalImpact = null; console.error('Failed to remove library:', err); + this.showToast(`Failed to remove '${libName}': ${String(err)}`); } };