Show scan overlay after page reload

- Add is_scanning state tracking in AnimeService
- Add get_scan_status method to AnimeService
- Add /api/anime/scan/status endpoint to check scan state
- Add checkActiveScanStatus in JS to restore overlay on reconnect
- All 1024 tests passing
This commit is contained in:
Lukas 2025-12-24 21:06:22 +01:00
parent 72ac201153
commit 19cb8c11a0
7 changed files with 98 additions and 2 deletions

BIN
data/aniworld.db-shm Normal file

Binary file not shown.

BIN
data/aniworld.db-wal Normal file

Binary file not shown.

View File

@ -17,7 +17,8 @@
"keep_days": 30
},
"other": {
"master_password_hash": "$pbkdf2-sha256$29000$bY3xHiPkPCckJMT4H8PY2w$s7wlQnFrLpXdGE4GhX5hgZGSHka4SsuAchcFN5qBx3k"
"master_password_hash": "$pbkdf2-sha256$29000$NkbIuZfSGiMEAGDMWau1tg$pMLf3tp4uH36YeG3KBgkX3F00dwbN2dGPQCWNvrjhnU",
"anime_directory": "/mnt/server/serien/Serien/"
},
"version": "1.0.0"
}

View File

@ -17,7 +17,8 @@
"keep_days": 30
},
"other": {
"master_password_hash": "$pbkdf2-sha256$29000$CMG4t9a6t9Y6J2TMOYfQ2g$bUIhqeewMMSj2Heh07vIhAGjvzDVijHb62aes4JuJhw"
"master_password_hash": "$pbkdf2-sha256$29000$GKP0fo.RspaScm5trRWiFA$cEeWdNCea5O7PAF21LyHVK.Xxj8sw3/9bbEdapRbCrw",
"anime_directory": "/mnt/server/serien/Serien/"
},
"version": "1.0.0"
}

View File

@ -366,6 +366,31 @@ async def trigger_rescan(
) from exc
@router.get("/scan/status")
async def get_scan_status(
_auth: dict = Depends(require_auth),
anime_service: AnimeService = Depends(get_anime_service),
) -> dict:
"""Get the current scan status.
Returns the current state of any ongoing library scan,
useful for restoring UI state after page reload.
Args:
_auth: Ensures the caller is authenticated (value unused)
anime_service: AnimeService instance provided via dependency.
Returns:
Dict[str, Any]: Current scan status including:
- is_scanning: Whether a scan is in progress
- total_items: Total items to scan
- directories_scanned: Items scanned so far
- current_directory: Current item being scanned
- directory: Root scan directory
"""
return anime_service.get_scan_status()
class AddSeriesRequest(BaseModel):
"""Request model for adding a new series."""

View File

@ -54,6 +54,8 @@ class AnimeService:
self._scan_directories_count: int = 0
self._scan_files_count: int = 0
self._scan_total_items: int = 0
self._is_scanning: bool = False
self._scan_current_directory: str = ""
# Subscribe to SeriesApp events
# Note: Events library uses assignment (=), not += operator
try:
@ -219,6 +221,8 @@ class AnimeService:
self._scan_directories_count = 0
self._scan_files_count = 0
self._scan_total_items = args.total
self._is_scanning = True
self._scan_current_directory = ""
asyncio.run_coroutine_threadsafe(
self._progress_service.start_progress(
@ -237,6 +241,7 @@ class AnimeService:
elif args.status == "progress":
# Update scan counters
self._scan_directories_count = args.current
self._scan_current_directory = args.folder or ""
# Estimate files found (use current as proxy since detailed
# file count isn't available from SerieScanner)
@ -265,6 +270,9 @@ class AnimeService:
if self._scan_start_time:
elapsed = time.time() - self._scan_start_time
# Mark scan as complete
self._is_scanning = False
asyncio.run_coroutine_threadsafe(
self._progress_service.complete_progress(
progress_id=scan_id,
@ -282,6 +290,7 @@ class AnimeService:
loop
)
elif args.status == "failed":
self._is_scanning = False
asyncio.run_coroutine_threadsafe(
self._progress_service.fail_progress(
progress_id=scan_id,
@ -290,6 +299,7 @@ class AnimeService:
loop
)
elif args.status == "cancelled":
self._is_scanning = False
asyncio.run_coroutine_threadsafe(
self._progress_service.fail_progress(
progress_id=scan_id,
@ -385,6 +395,25 @@ class AnimeService:
error=str(exc)
)
def get_scan_status(self) -> dict:
"""Get the current scan status.
Returns:
Dictionary with scan status information including:
- is_scanning: Whether a scan is currently in progress
- total_items: Total number of items to scan
- directories_scanned: Number of directories scanned so far
- current_directory: Current directory being scanned
- directory: Root directory being scanned
"""
return {
"is_scanning": self._is_scanning,
"total_items": self._scan_total_items,
"directories_scanned": self._scan_directories_count,
"current_directory": self._scan_current_directory,
"directory": self._directory,
}
@lru_cache(maxsize=128)
def _cached_list_missing(self) -> list[dict]:
# Synchronous cached call - SeriesApp.series_list is populated

View File

@ -193,6 +193,9 @@ class AniWorldApp {
this.showToast(this.localization.getText('connected-server'), 'success');
this.updateConnectionStatus();
// Check if a scan is currently in progress (e.g., after page reload)
this.checkActiveScanStatus();
});
this.socket.on('disconnect', () => {
@ -1278,6 +1281,43 @@ class AniWorldApp {
}
}
/**
* Check if a scan is currently in progress (useful after page reload)
* and show the progress overlay if so
*/
async checkActiveScanStatus() {
try {
const response = await this.makeAuthenticatedRequest('/api/anime/scan/status');
if (!response || !response.ok) {
console.log('Could not fetch scan status');
return;
}
const data = await response.json();
console.log('Scan status:', data);
if (data.is_scanning) {
// A scan is in progress, show the overlay
this.showScanProgressOverlay({
directory: data.directory,
total_items: data.total_items
});
// Update with current progress
this.updateScanProgressOverlay({
directories_scanned: data.directories_scanned,
files_found: data.directories_scanned,
current_directory: data.current_directory,
total_items: data.total_items
});
this.updateProcessStatus('rescan', true);
}
} catch (error) {
console.error('Error checking scan status:', error);
}
}
showLoading() {
document.getElementById('loading-overlay').classList.remove('hidden');
}