Show total items to scan in progress overlay

- Add total_items parameter to broadcast_scan_started and broadcast_scan_progress
- Pass total from SeriesApp to WebSocket broadcasts in AnimeService
- Update JS overlay to show progress bar and current/total count
- Add CSS for progress bar styling
- Add unit tests for new total_items parameter
- All 1024 tests passing
This commit is contained in:
2025-12-24 20:54:27 +01:00
parent a24f07a36e
commit 72ac201153
10 changed files with 212 additions and 16 deletions

View File

@@ -53,12 +53,17 @@ class AnimeService:
self._scan_start_time: Optional[float] = None
self._scan_directories_count: int = 0
self._scan_files_count: int = 0
self._scan_total_items: int = 0
# Subscribe to SeriesApp events
# Note: Events library uses assignment (=), not += operator
try:
self._app.download_status = self._on_download_status
self._app.scan_status = self._on_scan_status
logger.debug("Successfully subscribed to SeriesApp events")
logger.info(
"Subscribed to SeriesApp events",
scan_status_handler=str(self._app.scan_status),
series_app_id=id(self._app),
)
except Exception as e:
logger.exception("Failed to subscribe to SeriesApp events")
raise AnimeServiceError("Initialization failed") from e
@@ -173,20 +178,39 @@ class AnimeService:
try:
scan_id = "library_scan"
logger.info(
"Scan status event received",
status=args.status,
current=args.current,
total=args.total,
folder=args.folder,
)
# Get event loop - try running loop first, then stored loop
loop = None
try:
loop = asyncio.get_running_loop()
logger.debug("Using running event loop for scan status")
except RuntimeError:
# No running loop in this thread - use stored loop
loop = self._event_loop
logger.debug(
"Using stored event loop for scan status",
has_loop=loop is not None
)
if not loop:
logger.debug(
logger.warning(
"No event loop available for scan status event",
status=args.status
)
return
logger.info(
"Processing scan status event",
status=args.status,
loop_id=id(loop),
)
# Map SeriesApp scan events to progress service
if args.status == "started":
@@ -194,6 +218,7 @@ class AnimeService:
self._scan_start_time = time.time()
self._scan_directories_count = 0
self._scan_files_count = 0
self._scan_total_items = args.total
asyncio.run_coroutine_threadsafe(
self._progress_service.start_progress(
@@ -204,9 +229,9 @@ class AnimeService:
),
loop
)
# Broadcast scan started via WebSocket
# Broadcast scan started via WebSocket with total items
asyncio.run_coroutine_threadsafe(
self._broadcast_scan_started_safe(),
self._broadcast_scan_started_safe(total_items=args.total),
loop
)
elif args.status == "progress":
@@ -224,12 +249,13 @@ class AnimeService:
),
loop
)
# Broadcast scan progress via WebSocket (throttled - every update)
# Broadcast scan progress via WebSocket
asyncio.run_coroutine_threadsafe(
self._broadcast_scan_progress_safe(
directories_scanned=args.current,
files_found=args.current, # Use folder count as proxy
current_directory=args.folder or "",
total_items=args.total,
),
loop
)
@@ -274,16 +300,26 @@ class AnimeService:
except Exception as exc: # pylint: disable=broad-except
logger.error("Error handling scan status event: %s", exc)
async def _broadcast_scan_started_safe(self) -> None:
async def _broadcast_scan_started_safe(self, total_items: int = 0) -> None:
"""Safely broadcast scan started event via WebSocket.
Wraps the WebSocket broadcast in try/except to ensure scan
continues even if WebSocket fails.
Args:
total_items: Total number of items to scan
"""
try:
await self._websocket_service.broadcast_scan_started(
directory=self._directory
logger.info(
"Broadcasting scan_started via WebSocket",
directory=self._directory,
total_items=total_items,
)
await self._websocket_service.broadcast_scan_started(
directory=self._directory,
total_items=total_items,
)
logger.info("scan_started broadcast sent successfully")
except Exception as exc:
logger.warning(
"Failed to broadcast scan_started via WebSocket",
@@ -295,6 +331,7 @@ class AnimeService:
directories_scanned: int,
files_found: int,
current_directory: str,
total_items: int = 0,
) -> None:
"""Safely broadcast scan progress event via WebSocket.
@@ -305,12 +342,14 @@ class AnimeService:
directories_scanned: Number of directories scanned so far
files_found: Number of files found so far
current_directory: Current directory being scanned
total_items: Total number of items to scan
"""
try:
await self._websocket_service.broadcast_scan_progress(
directories_scanned=directories_scanned,
files_found=files_found,
current_directory=current_directory,
total_items=total_items,
)
except Exception as exc:
logger.warning(
@@ -418,6 +457,12 @@ class AnimeService:
try:
# Store event loop for event handlers
self._event_loop = asyncio.get_running_loop()
logger.info(
"Rescan started, event loop stored",
loop_id=id(self._event_loop),
series_app_id=id(self._app),
scan_handler=str(self._app.scan_status),
)
# SeriesApp.rescan returns scanned series list
scanned_series = await self._app.rescan()