diff --git a/frontend/src/components/config-page/config-page.ts b/frontend/src/components/config-page/config-page.ts index 1816ae0..ad2025a 100644 --- a/frontend/src/components/config-page/config-page.ts +++ b/frontend/src/components/config-page/config-page.ts @@ -5,7 +5,9 @@ import { EventsOn } from '@runtime/runtime'; import { Scan, FullRescan, - CancelScan, + CancelCurrentScan, + CancelAllScans, + ScanAllLibraries, PauseScan, ResumeScan, } from '@go/library/Library'; @@ -52,6 +54,9 @@ interface ScanProgress { added: number; skipped: number; updated: number; + libraryId: number; + libraryName: string; + queuedCount: number; } interface ScanMetrics { @@ -345,6 +350,7 @@ export class ConfigPage extends LitElement { @state() private scanPaused = false; @state() private showCancelDialog = false; @state() private cancelMetrics: { added: number } | null = null; + @state() private scanQueuedCount = 0; @state() private shortcutConflict: { newAction: string; newKey: string; @@ -357,6 +363,8 @@ export class ConfigPage extends LitElement { private cancelScanPaused?: () => void; private cancelScanResumed?: () => void; private cancelScanCancelled?: () => void; + private cancelScanQueued?: () => void; + private cancelScanQueueDrained?: () => void; static override styles = css` :host { @@ -901,6 +909,14 @@ export class ConfigPage extends LitElement { Events.LibraryScanCancelled, this.handleScanCancelled, ); + this.cancelScanQueued = EventsOn( + Events.LibraryScanQueued, + this.handleScanQueued, + ); + this.cancelScanQueueDrained = EventsOn( + Events.LibraryScanQueueDrained, + this.handleScanQueueDrained, + ); } override disconnectedCallback(): void { @@ -911,6 +927,8 @@ export class ConfigPage extends LitElement { this.cancelScanPaused?.(); this.cancelScanResumed?.(); this.cancelScanCancelled?.(); + this.cancelScanQueued?.(); + this.cancelScanQueueDrained?.(); } private async loadLibraryConfig(): Promise { @@ -950,15 +968,26 @@ export class ConfigPage extends LitElement { ): void => { if (progress) { this.scanProgress = progress; + this.scanQueuedCount = + progress.queuedCount ?? 0; } }; private handleScanComplete = ( metrics?: ScanMetrics, ): void => { + this.scanProgress = null; + + // If queue still has entries, don't fully reset — next scan will fire ScanStarted + if (this.scanQueuedCount > 0) { + if (metrics) { + this.metrics = metrics; + } + return; + } + this.scanning = false; this.scanPaused = false; - this.scanProgress = null; this.statusMessage = 'Scan complete.'; if (metrics) { @@ -966,6 +995,16 @@ export class ConfigPage extends LitElement { } }; + private handleScanQueued = (): void => { + this.scanQueuedCount++; + }; + + private handleScanQueueDrained = (): void => { + this.scanQueuedCount = 0; + this.scanning = false; + this.scanPaused = false; + }; + private handleScanPaused = (): void => { this.scanPaused = true; }; @@ -1010,17 +1049,24 @@ export class ConfigPage extends LitElement { private handleCancelKeep = (): void => { this.showCancelDialog = false; this.cancelMetrics = null; - CancelScan(); + CancelCurrentScan(); }; private handleCancelDiscard = (): void => { this.showCancelDialog = false; this.cancelMetrics = null; - CancelScan(); + CancelCurrentScan(); this.statusMessage = 'Scan cancelled. Partial results discarded \u2014 run Full Rescan for a clean library.'; }; + private handleCancelAll = (): void => { + this.showCancelDialog = false; + this.cancelMetrics = null; + CancelAllScans(); + this.statusMessage = 'All scanning cancelled.'; + }; + private handleCancelDialogDismiss = (): void => { this.showCancelDialog = false; this.cancelMetrics = null; @@ -1073,6 +1119,16 @@ export class ConfigPage extends LitElement { }); }; + private handleScanAll = async (): Promise => { + try { + await ScanAllLibraries(); + } catch (err) { + this.statusMessage = + 'Scan all libraries completed with errors.'; + this.scanErrors = String(err); + } + }; + private handleSoftScan = async (): Promise => { try { await Scan(); @@ -1938,6 +1994,12 @@ export class ConfigPage extends LitElement { > Full Rescan + `} @@ -1985,45 +2047,98 @@ export class ConfigPage extends LitElement { class="cancel-dialog" @click=${(e: Event) => e.stopPropagation()} > -
- Cancel Scan -
-
- ${this.cancelMetrics - ?.added - ? `Keep ${this.cancelMetrics.added} tracks found so far, or discard?` - : 'Cancel the current scan?'} -
-
- - - -
+ ${this.scanQueuedCount > 0 + ? html` +
+ Cancel Scanning +
+
+ A scan is in + progress with + ${this + .scanQueuedCount} + ${this + .scanQueuedCount === + 1 + ? 'library' + : 'libraries'} + still queued. +
+
+ + + +
+ ` + : html` +
+ Cancel Scan +
+
+ ${this + .cancelMetrics + ?.added + ? `Keep ${this.cancelMetrics.added} tracks found so far, or discard?` + : 'Cancel the current scan?'} +
+
+ + + +
+ `} ` @@ -2055,11 +2170,25 @@ export class ConfigPage extends LitElement { if (!p) return nothing; + const libraryPrefix = p.libraryName + ? `Scanning: ${p.libraryName}` + : ''; + if (p.phase === 'counting') { return html`
+ ${libraryPrefix + ? html`${libraryPrefix} \u2014 ` + : ''} Counting files\u2026
+ ${p.queuedCount > 0 + ? html`
+ ${p.queuedCount} + ${p.queuedCount === 1 ? 'library' : 'libraries'} + queued +
` + : nothing} `; } @@ -2079,7 +2208,11 @@ export class ConfigPage extends LitElement { thumbnails: 'Generating thumbnails', }; - const label = phaseLabel[p.phase] ?? 'Scanning'; + const baseLabel = + phaseLabel[p.phase] ?? 'Scanning'; + const label = p.libraryName + ? `${baseLabel}: ${p.libraryName}` + : baseLabel; // Build detail string: "1,247 / 2,013 files (891 new, 23 updated, 356 skipped)" const parts: string[] = []; @@ -2121,6 +2254,13 @@ export class ConfigPage extends LitElement { style="width: ${percent}%" > + ${p.queuedCount > 0 + ? html`
+ ${p.queuedCount} + ${p.queuedCount === 1 ? 'library' : 'libraries'} + queued +
` + : nothing} `; }