From 8078d9a4cc38eb8399eafb5c2034ed3f6bee3dd1 Mon Sep 17 00:00:00 2001
From: grimsi <9295182+grimsi@users.noreply.github.com>
Date: Fri, 18 Jul 2025 23:46:08 +0200
Subject: [PATCH] Handle scan failure correctly
---
.../general/ScanProgressPopover.tsx | 39 +--
.../app/libraries/LibraryScanService.kt | 251 ++++++++++--------
2 files changed, 156 insertions(+), 134 deletions(-)
diff --git a/app/src/main/frontend/components/general/ScanProgressPopover.tsx b/app/src/main/frontend/components/general/ScanProgressPopover.tsx
index 368b289..7898658 100644
--- a/app/src/main/frontend/components/general/ScanProgressPopover.tsx
+++ b/app/src/main/frontend/components/general/ScanProgressPopover.tsx
@@ -13,7 +13,7 @@ import {useSnapshot} from "valtio/react";
import {scanState} from "Frontend/state/ScanState";
import LibraryScanProgress from "Frontend/generated/org/gameyfin/app/libraries/dto/LibraryScanProgress";
import {libraryState} from "Frontend/state/LibraryState";
-import {Target} from "@phosphor-icons/react";
+import {Target, Warning} from "@phosphor-icons/react";
import {timeBetween, timeUntil, toTitleCase} from "Frontend/util/utils";
import LibraryScanStatus from "Frontend/generated/org/gameyfin/app/libraries/dto/LibraryScanStatus";
import {useEffect, useState} from "react";
@@ -77,21 +77,23 @@ export default function ScanProgressPopover() {
}
- {scan.status === LibraryScanStatus.IN_PROGRESS ?
- scan.currentStep.current && scan.currentStep.total ?
-
-
- {`${scan.currentStep.description} (${scan.currentStep.current}/${scan.currentStep.total})`}
-
-
-
:
-
-
{scan.currentStep.description}
-
-
- :
+ {scan.status === LibraryScanStatus.IN_PROGRESS &&
+ (scan.currentStep.current && scan.currentStep.total ?
+
+
+ {`${scan.currentStep.description} (${scan.currentStep.current}/${scan.currentStep.total})`}
+
+
+
:
+
+
{scan.currentStep.description}
+
+
+ )
+ }
+ {scan.status === LibraryScanStatus.COMPLETED &&
{scan.result?.new} new /
{(scan as any).result?.updated != null && `${(scan as any).result.updated} updated / `}
@@ -100,6 +102,11 @@ export default function ScanProgressPopover() {
(in {timeBetween(scan.startedAt, scan.finishedAt!)})
}
+ {scan.status === LibraryScanStatus.FAILED &&
+
+ Scan failed (check logs for details)
+
+ }
{scans.length > 1 && index < (scans.length - 1) && }
)}
diff --git a/app/src/main/kotlin/org/gameyfin/app/libraries/LibraryScanService.kt b/app/src/main/kotlin/org/gameyfin/app/libraries/LibraryScanService.kt
index 502f6cd..2087619 100644
--- a/app/src/main/kotlin/org/gameyfin/app/libraries/LibraryScanService.kt
+++ b/app/src/main/kotlin/org/gameyfin/app/libraries/LibraryScanService.kt
@@ -117,70 +117,77 @@ class LibraryScanService(
)
emit(progress)
- val scanResult = filesystemService.scanLibraryForGamefiles(library)
- val newPaths = scanResult.newPaths
- val removedGamePaths = scanResult.removedGamePaths.map { it.toString() }
- val removedUnmatchedPaths = scanResult.removedUnmatchedPaths.map { it.toString() }
+ try {
+ val scanResult = filesystemService.scanLibraryForGamefiles(library)
+ val newPaths = scanResult.newPaths
+ val removedGamePaths = scanResult.removedGamePaths.map { it.toString() }
+ val removedUnmatchedPaths = scanResult.removedUnmatchedPaths.map { it.toString() }
- progress.currentStep = LibraryScanStep(
- description = "Matching new games",
- current = 0,
- total = newPaths.size
- )
- emit(progress)
+ progress.currentStep = LibraryScanStep(
+ description = "Matching new games",
+ current = 0,
+ total = newPaths.size
+ )
+ emit(progress)
- // 1. Match new games
- val (newUnmatchedPaths, matchedGames) = matchNewGames(library, newPaths, progress)
+ // 1. Match new games
+ val (newUnmatchedPaths, matchedGames) = matchNewGames(library, newPaths, progress)
- val (removedGames) = updateLibrary(
- library,
- removedUnmatchedPaths,
- newUnmatchedPaths,
- removedGamePaths
- )
+ val (removedGames) = updateLibrary(
+ library,
+ removedUnmatchedPaths,
+ newUnmatchedPaths,
+ removedGamePaths
+ )
- // 2. Download all images
- val totalImages = matchedGames.count { it.coverImage != null } +
- matchedGames.count { it.headerImage !== null } +
- matchedGames.sumOf { it.images.size }
+ // 2. Download all images
+ val totalImages = matchedGames.count { it.coverImage != null } +
+ matchedGames.count { it.headerImage !== null } +
+ matchedGames.sumOf { it.images.size }
- progress.currentStep = LibraryScanStep(
- description = "Downloading images",
- current = 0,
- total = totalImages
- )
- emit(progress)
+ progress.currentStep = LibraryScanStep(
+ description = "Downloading images",
+ current = 0,
+ total = totalImages
+ )
+ emit(progress)
- val (gamesWithImages) = downloadImages(matchedGames, progress)
+ val (gamesWithImages) = downloadImages(matchedGames, progress)
- // 3. Calculate game file sizes
- progress.currentStep = LibraryScanStep(
- description = "Calculating file sizes",
- current = 0,
- total = gamesWithImages.size
- )
- emit(progress)
+ // 3. Calculate game file sizes
+ progress.currentStep = LibraryScanStep(
+ description = "Calculating file sizes",
+ current = 0,
+ total = gamesWithImages.size
+ )
+ emit(progress)
- val (gamesWithFileSizes) = calculateFileSizes(gamesWithImages, progress)
+ val (gamesWithFileSizes) = calculateFileSizes(gamesWithImages, progress)
- progress.currentStep = LibraryScanStep(
- description = "Finishing up",
- current = 0,
- total = gamesWithFileSizes.size
- )
- emit(progress)
+ progress.currentStep = LibraryScanStep(
+ description = "Finishing up",
+ current = 0,
+ total = gamesWithFileSizes.size
+ )
+ emit(progress)
- val (persistedGames) = finishScan(gamesWithFileSizes, library, progress)
+ val (persistedGames) = finishScan(gamesWithFileSizes, library, progress)
- progress.currentStep = LibraryScanStep(description = "Finished")
- progress.finishedAt = Instant.now()
- progress.status = LibraryScanStatus.COMPLETED
- progress.result = QuickScanResult(
- new = persistedGames.size,
- removed = removedGames.size,
- unmatched = newUnmatchedPaths.size
- )
- emit(progress)
+ progress.currentStep = LibraryScanStep(description = "Finished")
+ progress.finishedAt = Instant.now()
+ progress.status = LibraryScanStatus.COMPLETED
+ progress.result = QuickScanResult(
+ new = persistedGames.size,
+ removed = removedGames.size,
+ unmatched = newUnmatchedPaths.size
+ )
+ emit(progress)
+ } catch (e: Exception) {
+ log.error(e) { "Error during quick scan for library ${library.id}: ${e.message}" }
+ progress.status = LibraryScanStatus.FAILED
+ progress.finishedAt = Instant.now()
+ emit(progress)
+ }
}
private fun fullScan(library: Library) {
@@ -193,86 +200,94 @@ class LibraryScanService(
)
emit(progress)
- val scanResult = filesystemService.scanLibraryForGamefiles(library)
- val newPaths = scanResult.newPaths
- val removedGamePaths = scanResult.removedGamePaths.map { it.toString() }
- val removedUnmatchedPaths = scanResult.removedUnmatchedPaths.map { it.toString() }
+ try {
+ val scanResult = filesystemService.scanLibraryForGamefiles(library)
+ val newPaths = scanResult.newPaths
+ val removedGamePaths = scanResult.removedGamePaths.map { it.toString() }
+ val removedUnmatchedPaths = scanResult.removedUnmatchedPaths.map { it.toString() }
- // 1. Update existing games
- progress.currentStep = LibraryScanStep(
- description = "Updating existing games",
- current = 0,
- total = library.games.size
- )
- emit(progress)
+ // 1. Update existing games
+ progress.currentStep = LibraryScanStep(
+ description = "Updating existing games",
+ current = 0,
+ total = library.games.size
+ )
+ emit(progress)
- val (updatedGames) = updateExistingGames(library.games, progress)
+ val (updatedGames) = updateExistingGames(library.games, progress)
- // 2. Match new games
- progress.currentStep = LibraryScanStep(
- description = "Matching new games",
- current = 0,
- total = newPaths.size
- )
- emit(progress)
+ // 2. Match new games
+ progress.currentStep = LibraryScanStep(
+ description = "Matching new games",
+ current = 0,
+ total = newPaths.size
+ )
+ emit(progress)
- val (newUnmatchedPaths, newMatchedGames) = matchNewGames(library, newPaths, progress)
+ val (newUnmatchedPaths, newMatchedGames) = matchNewGames(library, newPaths, progress)
- val (removedGames) = updateLibrary(
- library,
- removedUnmatchedPaths,
- newUnmatchedPaths,
- removedGamePaths
- )
+ val (removedGames) = updateLibrary(
+ library,
+ removedUnmatchedPaths,
+ newUnmatchedPaths,
+ removedGamePaths
+ )
- // 3. Download all images
- val newAndUpdatedGames = newMatchedGames + updatedGames
+ // 3. Download all images
+ val newAndUpdatedGames = newMatchedGames + updatedGames
- val totalImages = newAndUpdatedGames.count { it.coverImage != null } +
- newAndUpdatedGames.count { it.headerImage !== null } +
- newAndUpdatedGames.sumOf { it.images.size }
+ val totalImages = newAndUpdatedGames.count { it.coverImage != null } +
+ newAndUpdatedGames.count { it.headerImage !== null } +
+ newAndUpdatedGames.sumOf { it.images.size }
- progress.currentStep = LibraryScanStep(
- description = "Downloading images",
- current = 0,
- total = totalImages
- )
- emit(progress)
+ progress.currentStep = LibraryScanStep(
+ description = "Downloading images",
+ current = 0,
+ total = totalImages
+ )
+ emit(progress)
- val (gamesWithImages) = downloadImages(newAndUpdatedGames, progress)
+ val (gamesWithImages) = downloadImages(newAndUpdatedGames, progress)
- // 4. Calculate game file sizes
- progress.currentStep = LibraryScanStep(
- description = "Calculating file sizes",
- current = 0,
- total = gamesWithImages.size
- )
- emit(progress)
+ // 4. Calculate game file sizes
+ progress.currentStep = LibraryScanStep(
+ description = "Calculating file sizes",
+ current = 0,
+ total = gamesWithImages.size
+ )
+ emit(progress)
- val (gamesWithFileSizes) = calculateFileSizes(gamesWithImages, progress)
+ val (gamesWithFileSizes) = calculateFileSizes(gamesWithImages, progress)
- // 5. Finish scan
- progress.currentStep = LibraryScanStep(
- description = "Finishing up",
- current = 0,
- total = gamesWithFileSizes.size
- )
- emit(progress)
+ // 5. Finish scan
+ progress.currentStep = LibraryScanStep(
+ description = "Finishing up",
+ current = 0,
+ total = gamesWithFileSizes.size
+ )
+ emit(progress)
- val (persistedGames) = finishScan(gamesWithFileSizes, library, progress)
+ val (persistedGames) = finishScan(gamesWithFileSizes, library, progress)
- // 6. Send final progress update
- progress.currentStep = LibraryScanStep(description = "Finished")
- progress.finishedAt = Instant.now()
- progress.status = LibraryScanStatus.COMPLETED
- progress.result = FullScanResult(
- new = persistedGames.size,
- removed = removedGames.size,
- unmatched = newUnmatchedPaths.size,
- updated = updatedGames.size
- )
- emit(progress)
+ // 6. Send final progress update
+ progress.currentStep = LibraryScanStep(description = "Finished")
+ progress.finishedAt = Instant.now()
+ progress.status = LibraryScanStatus.COMPLETED
+ progress.result = FullScanResult(
+ new = persistedGames.size,
+ removed = removedGames.size,
+ unmatched = newUnmatchedPaths.size,
+ updated = updatedGames.size
+ )
+ emit(progress)
+ } catch (e: Exception) {
+ log.error(e) { "Error during full scan for library ${library.id}: ${e.message}" }
+ progress.status = LibraryScanStatus.FAILED
+ progress.finishedAt = Instant.now()
+ emit(progress)
+ return
+ }
}
private fun matchNewGames(