diff --git a/media.db b/media.db index 6e59859..d6cbc9c 100644 Binary files a/media.db and b/media.db differ diff --git a/src/app/api/scan/route.ts b/src/app/api/scan/route.ts index c37671d..dfcfb06 100644 --- a/src/app/api/scan/route.ts +++ b/src/app/api/scan/route.ts @@ -1,11 +1,21 @@ import { NextResponse } from "next/server"; -import { scanAllLibraries } from "@/lib/scanner"; +import { scanAllLibraries, scanSelectedLibrary } from "@/lib/scanner"; -export async function POST() { +export async function POST(request: Request) { try { - await scanAllLibraries(); - return NextResponse.json({ message: "Scan complete" }); + const body = await request.json(); + const { libraryId } = body; + + if (libraryId) { + // Scan specific library + await scanSelectedLibrary(libraryId); + return NextResponse.json({ message: `Library ${libraryId} scan complete` }); + } else { + // Scan all libraries + await scanAllLibraries(); + return NextResponse.json({ message: "All libraries scan complete" }); + } } catch (error: any) { return NextResponse.json({ error: error.message }, { status: 500 }); } diff --git a/src/app/settings/page.tsx b/src/app/settings/page.tsx index 9e015fa..2de3e33 100644 --- a/src/app/settings/page.tsx +++ b/src/app/settings/page.tsx @@ -8,7 +8,7 @@ import { Input } from "@/components/ui/input"; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; import { Header } from "@/components/ui/header"; import { Alert, AlertDescription } from "@/components/ui/alert"; -import { Trash2, Plus, Folder, HardDrive } from "lucide-react"; +import { Trash2, Plus, Folder, HardDrive, Scan, CheckSquare, Square, RefreshCw } from "lucide-react"; interface Library { id: number; @@ -20,6 +20,8 @@ const SettingsPage = () => { const [newLibraryPath, setNewLibraryPath] = useState(""); const [error, setError] = useState(null); const [scanStatus, setScanStatus] = useState(""); + const [selectedLibraries, setSelectedLibraries] = useState([]); + const [scanProgress, setScanProgress] = useState>({}); useEffect(() => { fetchLibraries(); @@ -82,17 +84,37 @@ const SettingsPage = () => { const [isScanning, setIsScanning] = useState(false); + const toggleLibrarySelection = (libraryId: number) => { + setSelectedLibraries(prev => + prev.includes(libraryId) + ? prev.filter(id => id !== libraryId) + : [...prev, libraryId] + ); + }; + + const selectAllLibraries = () => { + if (selectedLibraries.length === libraries.length) { + setSelectedLibraries([]); + } else { + setSelectedLibraries(libraries.map(lib => lib.id)); + } + }; + const scanLibraries = async () => { setIsScanning(true); - setScanStatus("Scanning libraries..."); + setScanStatus("Scanning all libraries..."); try { const res = await fetch("/api/scan", { method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({}), }); if (res.ok) { - setScanStatus("Scan completed successfully!"); + setScanStatus("All libraries scan completed successfully!"); setTimeout(() => setScanStatus(""), 3000); } else { setScanStatus("Scan failed. Please try again."); @@ -104,6 +126,80 @@ const SettingsPage = () => { } }; + const scanSelectedLibraries = async () => { + if (selectedLibraries.length === 0) return; + + setIsScanning(true); + const libraryMap = new Map(libraries.map(lib => [lib.id, lib.path])); + + try { + setScanStatus(`Scanning ${selectedLibraries.length} selected library${selectedLibraries.length > 1 ? 'ies' : ''}...`); + + // Set all selected libraries as scanning + const progressMap: Record = {}; + selectedLibraries.forEach(id => progressMap[id] = true); + setScanProgress(progressMap); + + // Scan each library sequentially to avoid overwhelming the server + for (const libraryId of selectedLibraries) { + const res = await fetch("/api/scan", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ libraryId }), + }); + + // Update progress for this library + setScanProgress(prev => ({ ...prev, [libraryId]: false })); + + if (!res.ok) { + const data = await res.json(); + setScanStatus(`Error scanning library ${libraryMap.get(libraryId)}: ${data.error || 'Unknown error'}`); + break; + } + } + + setScanStatus(`${selectedLibraries.length} library${selectedLibraries.length > 1 ? 'ies' : ''} scan completed successfully!`); + setSelectedLibraries([]); // Clear selection after scan + setTimeout(() => setScanStatus(""), 3000); + } catch (error) { + setScanStatus("Network error during scan"); + } finally { + setIsScanning(false); + setScanProgress({}); + } + }; + + const scanSpecificLibrary = async (libraryId: number, libraryPath: string) => { + setIsScanning(true); + setScanProgress(prev => ({ ...prev, [libraryId]: true })); + setScanStatus(`Scanning library: ${libraryPath}...`); + + try { + const res = await fetch("/api/scan", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ libraryId }), + }); + + if (res.ok) { + setScanStatus(`Library "${libraryPath}" scan completed successfully!`); + setTimeout(() => setScanStatus(""), 3000); + } else { + const data = await res.json(); + setScanStatus(`Scan failed: ${data.error || 'Please try again.'}`); + } + } catch (error) { + setScanStatus("Network error during scan"); + } finally { + setIsScanning(false); + setScanProgress(prev => ({ ...prev, [libraryId]: false })); + } + }; + const getTotalStorage = () => { return libraries.reduce((total, lib) => { // Rough estimation based on path length @@ -167,27 +263,74 @@ const SettingsPage = () => { {libraries.length > 0 && (
-

- {libraries.length} {libraries.length === 1 ? 'Library' : 'Libraries'} -

+
+

+ {libraries.length} {libraries.length === 1 ? 'Library' : 'Libraries'} +

+ {libraries.length > 1 && ( + + )} +
{libraries.map((lib) => (
+

{lib.path}

-

Ready to scan

+

+ {scanProgress[lib.id] ? 'Scanning...' : 'Ready to scan'} +

- +
+ + +
))}
@@ -215,25 +358,56 @@ const SettingsPage = () => {

Media Scanner

-

Discover new media files

+

Discover new media files in your libraries

- + {libraries.length > 0 && ( +
+ {selectedLibraries.length > 0 && ( + + )} + + +
+ )} {scanStatus && (
@@ -246,7 +420,7 @@ const SettingsPage = () => {

{libraries.length === 0 ? "Add at least one library to enable scanning" - : "Scan will discover new videos and photos"} + : `Select libraries above or use individual scan buttons`}

diff --git a/src/lib/scanner.ts b/src/lib/scanner.ts index 8be9884..b16fccf 100644 --- a/src/lib/scanner.ts +++ b/src/lib/scanner.ts @@ -114,4 +114,12 @@ export const scanAllLibraries = async () => { for (const library of libraries) { await scanLibrary(library); } +}; + +export const scanSelectedLibrary = async (libraryId: number) => { + const library = db.prepare("SELECT * FROM libraries WHERE id = ?").get(libraryId) as { id: number; path: string } | undefined; + if (!library) { + throw new Error(`Library with ID ${libraryId} not found`); + } + await scanLibrary(library); }; \ No newline at end of file