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:
@@ -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()
|
||||
|
||||
@@ -498,27 +498,36 @@ class WebSocketService:
|
||||
}
|
||||
await self._manager.send_personal_message(message, connection_id)
|
||||
|
||||
async def broadcast_scan_started(self, directory: str) -> None:
|
||||
async def broadcast_scan_started(
|
||||
self, directory: str, total_items: int = 0
|
||||
) -> None:
|
||||
"""Broadcast that a library scan has started.
|
||||
|
||||
Args:
|
||||
directory: The root directory path being scanned
|
||||
total_items: Total number of items to scan (for progress display)
|
||||
"""
|
||||
message = {
|
||||
"type": "scan_started",
|
||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||
"data": {
|
||||
"directory": directory,
|
||||
"total_items": total_items,
|
||||
},
|
||||
}
|
||||
await self._manager.broadcast(message)
|
||||
logger.info("Broadcast scan_started", directory=directory)
|
||||
logger.info(
|
||||
"Broadcast scan_started",
|
||||
directory=directory,
|
||||
total_items=total_items,
|
||||
)
|
||||
|
||||
async def broadcast_scan_progress(
|
||||
self,
|
||||
directories_scanned: int,
|
||||
files_found: int,
|
||||
current_directory: str,
|
||||
total_items: int = 0,
|
||||
) -> None:
|
||||
"""Broadcast scan progress update to all clients.
|
||||
|
||||
@@ -526,6 +535,7 @@ class WebSocketService:
|
||||
directories_scanned: Number of directories scanned so far
|
||||
files_found: Number of MP4 files found so far
|
||||
current_directory: Current directory being scanned
|
||||
total_items: Total number of items to scan (for progress display)
|
||||
"""
|
||||
message = {
|
||||
"type": "scan_progress",
|
||||
@@ -534,6 +544,7 @@ class WebSocketService:
|
||||
"directories_scanned": directories_scanned,
|
||||
"files_found": files_found,
|
||||
"current_directory": current_directory,
|
||||
"total_items": total_items,
|
||||
},
|
||||
}
|
||||
await self._manager.broadcast(message)
|
||||
|
||||
Reference in New Issue
Block a user