diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index 1274b72..cd6aee5 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -1,114 +1,116 @@ - + {#if isMobile && sidebarOpen} {/if} - {#if !apiConfigured && currentView === 'scanner'} + {#if !apiConfigured && currentView === "scanner"} - Configure a metadata source in Settings to get started + + Configure a metadata source in Settings to get started + {/if} - {#if currentView === 'settings'} + {#if currentView === "settings"} - {:else if currentView === 'history'} + {:else if currentView === "history"} - {:else if currentView === 'scanner'} + {:else if currentView === "scanner"} {#key scanPanelKey} navigateTo('settings')} - onOpenHistory={() => navigateTo('history')} + {apiConfigured} + onOpenSettings={() => navigateTo("settings")} + onOpenHistory={() => navigateTo("history")} /> {/key} - {:else if currentView === 'library'} + {:else if currentView === "library"} {/if} diff --git a/frontend/src/components/AppSidebar.svelte b/frontend/src/components/AppSidebar.svelte index 2faa5db..0822ab8 100644 --- a/frontend/src/components/AppSidebar.svelte +++ b/frontend/src/components/AppSidebar.svelte @@ -154,7 +154,7 @@ > {#if !collapsed} v1.0.7 Release Candiatev1.0.8 Release Candiate {:else} v diff --git a/frontend/src/components/LibraryPanel.svelte b/frontend/src/components/LibraryPanel.svelte index 3e5c81b..41d7cda 100644 --- a/frontend/src/components/LibraryPanel.svelte +++ b/frontend/src/components/LibraryPanel.svelte @@ -8,13 +8,23 @@ let loading = false; let error = null; let expanded = {}; + let page = 0; + const pageSize = 200; - async function loadLibrary() { + async function loadLibrary(reset = true) { loading = true; error = null; try { - const response = await getLibraryReport(); - items = response.items || []; + if (reset) { + page = 0; + items = []; + } + const response = await getLibraryReport(pageSize, page * pageSize); + const nextItems = response.items || []; + items = reset ? nextItems : [...items, ...nextItems]; + if (nextItems.length > 0) { + page += 1; + } } catch (err) { error = `Failed to load library report: ${err.message}`; } finally { @@ -26,7 +36,7 @@ expanded = { ...expanded, [key]: !expanded[key] }; } - onMount(loadLibrary); + onMount(() => loadLibrary(true)); @@ -41,7 +51,7 @@ variant="outline" size="sm" className="border-white/15 text-text-secondary hover:bg-white/10" - on:click={loadLibrary} + on:click={() => loadLibrary(true)} disabled={loading} > @@ -148,6 +158,17 @@ {/if} {/each} + + loadLibrary(false)} + disabled={loading} + > + Load more + + {/if} diff --git a/frontend/src/components/ScanPanel.svelte b/frontend/src/components/ScanPanel.svelte index 1b1db4f..165dc5e 100644 --- a/frontend/src/components/ScanPanel.svelte +++ b/frontend/src/components/ScanPanel.svelte @@ -7,6 +7,7 @@ processFiles, clearAllSuggestedMatches, getFolderRules, + getScanHistory, } from "../lib/api.js"; import ResultsList from "./ResultsList.svelte"; import TypewriterQuote from "./TypewriterQuote.svelte"; @@ -118,7 +119,11 @@ filesFound: 0, message: "", scanning: false, + startedAt: null, + estimatedFinishAt: null, }; + let scanHistory = []; + let expectedTotalFiles = null; // Scan cancellation let scanAbortController = null; @@ -166,6 +171,12 @@ } catch (err) { console.error("Failed to load folder rules:", err); } + try { + const historyResponse = await getScanHistory(10); + scanHistory = historyResponse.scans || []; + } catch (err) { + console.error("Failed to load scan history:", err); + } } catch (err) { console.error("Failed to load initial data:", err); } @@ -190,7 +201,19 @@ filesFound: 0, message: "Starting scan...", scanning: true, + startedAt: new Date(), + estimatedFinishAt: null, }; + expectedTotalFiles = null; + if (scanHistory.length > 0 && directory) { + const normalizedDir = directory.toLowerCase(); + const lastMatch = scanHistory.find( + (scan) => (scan.directory || "").toLowerCase() === normalizedDir, + ); + if (lastMatch && lastMatch.files_found) { + expectedTotalFiles = lastMatch.files_found; + } + } addToast({ message: "Scan started.", tone: "info" }); // Reset files array before starting new scan @@ -219,6 +242,23 @@ message: data.message, filesFound: data.filesFound, }; + if ( + scanProgress.startedAt && + expectedTotalFiles && + data.filesFound > 0 + ) { + const elapsedMs = + new Date().getTime() - scanProgress.startedAt.getTime(); + const estimatedTotalMs = + (elapsedMs * expectedTotalFiles) / data.filesFound; + const estimatedFinish = new Date( + scanProgress.startedAt.getTime() + estimatedTotalMs, + ); + scanProgress = { + ...scanProgress, + estimatedFinishAt: estimatedFinish, + }; + } // Incrementally add files as they're found const previousLength = files.length; @@ -244,6 +284,9 @@ } lastScan = new Date().toISOString(); + if (data.count != null) { + expectedTotalFiles = data.count; + } // Save to store scanResults.setScanResults(files, directory); @@ -343,6 +386,7 @@ } finally { scanning = false; scanProgress.scanning = false; + scanProgress.estimatedFinishAt = null; scanAbortController = null; } } @@ -715,6 +759,14 @@ > Scanning in progress... + {#if scanProgress.startedAt} + + Started at {scanProgress.startedAt.toLocaleTimeString()} + {#if scanProgress.estimatedFinishAt} + ยท Estimated finish {scanProgress.estimatedFinishAt.toLocaleTimeString()} + {/if} + + {/if} None: + """Configure application logging.""" + logging.basicConfig(level=level, format=fmt) + + +def get_logger(name: Optional[str] = None) -> logging.Logger: + """Get a logger by name.""" + return logging.getLogger(name or __name__)
Configure a metadata source in Settings to get started
+ Configure a metadata source in Settings to get started +