fix queue error

This commit is contained in:
2025-12-10 20:55:09 +01:00
parent 798461a1ea
commit 99f79e4c29
6 changed files with 263 additions and 683 deletions

View File

@@ -120,6 +120,9 @@ class DownloadService:
"""Initialize the service by loading queue state from database.
Should be called after database is initialized during app startup.
Note: With the simplified model, status/priority/progress are now
managed in-memory only. The database stores the queue items
for persistence across restarts.
"""
if self._db_initialized:
return
@@ -127,44 +130,22 @@ class DownloadService:
try:
repository = self._get_repository()
# Load pending items from database
pending_items = await repository.get_pending_items()
for item in pending_items:
# Reset status if was downloading when saved
if item.status == DownloadStatus.DOWNLOADING:
item.status = DownloadStatus.PENDING
await repository.update_status(
item.id, DownloadStatus.PENDING
)
# Load all items from database - they all start as PENDING
# since status is now managed in-memory only
all_items = await repository.get_all_items()
for item in all_items:
# All items from database are treated as pending
item.status = DownloadStatus.PENDING
self._add_to_pending_queue(item)
# Load failed items from database
failed_items = await repository.get_failed_items()
for item in failed_items:
if item.retry_count < self._max_retries:
item.status = DownloadStatus.PENDING
await repository.update_status(
item.id, DownloadStatus.PENDING
)
self._add_to_pending_queue(item)
else:
self._failed_items.append(item)
# Load completed items for history
completed_items = await repository.get_completed_items(limit=100)
for item in completed_items:
self._completed_items.append(item)
self._db_initialized = True
logger.info(
"Queue restored from database",
pending_count=len(self._pending_queue),
failed_count=len(self._failed_items),
completed_count=len(self._completed_items),
"Queue restored from database: pending_count=%d",
len(self._pending_queue),
)
except Exception as e:
logger.error("Failed to load queue from database", error=str(e))
logger.error("Failed to load queue from database: %s", e, exc_info=True)
# Continue without persistence - queue will work in memory only
self._db_initialized = True
@@ -181,59 +162,28 @@ class DownloadService:
repository = self._get_repository()
return await repository.save_item(item)
except Exception as e:
logger.error("Failed to save item to database", error=str(e))
logger.error("Failed to save item to database: %s", e)
return item
async def _update_status_in_database(
async def _set_error_in_database(
self,
item_id: str,
status: DownloadStatus,
error: Optional[str] = None,
error: str,
) -> bool:
"""Update item status in the database.
"""Set error message on an item in the database.
Args:
item_id: Download item ID
status: New status
error: Optional error message
error: Error message
Returns:
True if update succeeded
"""
try:
repository = self._get_repository()
return await repository.update_status(item_id, status, error)
return await repository.set_error(item_id, error)
except Exception as e:
logger.error("Failed to update status in database", error=str(e))
return False
async def _update_progress_in_database(
self,
item_id: str,
progress: float,
downloaded: int,
total: Optional[int],
speed: Optional[float],
) -> bool:
"""Update download progress in the database.
Args:
item_id: Download item ID
progress: Progress percentage
downloaded: Downloaded bytes
total: Total bytes
speed: Download speed in bytes/sec
Returns:
True if update succeeded
"""
try:
repository = self._get_repository()
return await repository.update_progress(
item_id, progress, downloaded, total, speed
)
except Exception as e:
logger.error("Failed to update progress in database", error=str(e))
logger.error("Failed to set error in database: %s", e)
return False
async def _delete_from_database(self, item_id: str) -> bool:
@@ -249,7 +199,7 @@ class DownloadService:
repository = self._get_repository()
return await repository.delete_item(item_id)
except Exception as e:
logger.error("Failed to delete from database", error=str(e))
logger.error("Failed to delete from database: %s", e)
return False
async def _init_queue_progress(self) -> None:
@@ -271,7 +221,7 @@ class DownloadService:
)
self._queue_progress_initialized = True
except Exception as e:
logger.error("Failed to initialize queue progress", error=str(e))
logger.error("Failed to initialize queue progress: %s", e)
def _add_to_pending_queue(
self, item: DownloadItem, front: bool = False
@@ -396,7 +346,7 @@ class DownloadService:
return created_ids
except Exception as e:
logger.error("Failed to add items to queue", error=str(e))
logger.error("Failed to add items to queue: %s", e)
raise DownloadServiceError(f"Failed to add items: {str(e)}") from e
async def remove_from_queue(self, item_ids: List[str]) -> List[str]:
@@ -423,12 +373,10 @@ class DownloadService:
item.completed_at = datetime.now(timezone.utc)
self._failed_items.append(item)
self._active_download = None
# Update status in database
await self._update_status_in_database(
item_id, DownloadStatus.CANCELLED
)
# Delete cancelled item from database
await self._delete_from_database(item_id)
removed_ids.append(item_id)
logger.info("Cancelled active download", item_id=item_id)
logger.info("Cancelled active download: item_id=%s", item_id)
continue
# Check pending queue - O(1) lookup using helper dict
@@ -460,7 +408,7 @@ class DownloadService:
return removed_ids
except Exception as e:
logger.error("Failed to remove items", error=str(e))
logger.error("Failed to remove items: %s", e)
raise DownloadServiceError(
f"Failed to remove items: {str(e)}"
) from e
@@ -514,7 +462,7 @@ class DownloadService:
logger.info("Queue reordered", reordered_count=len(item_ids))
except Exception as e:
logger.error("Failed to reorder queue", error=str(e))
logger.error("Failed to reorder queue: %s", e)
raise DownloadServiceError(
f"Failed to reorder queue: {str(e)}"
) from e
@@ -558,7 +506,7 @@ class DownloadService:
return "queue_started"
except Exception as e:
logger.error("Failed to start queue processing", error=str(e))
logger.error("Failed to start queue processing: %s", e)
raise DownloadServiceError(
f"Failed to start queue processing: {str(e)}"
) from e
@@ -847,15 +795,12 @@ class DownloadService:
self._add_to_pending_queue(item)
retried_ids.append(item.id)
# Update status in database
await self._update_status_in_database(
item.id, DownloadStatus.PENDING
)
# Status is now managed in-memory only
logger.info(
"Retrying failed item",
item_id=item.id,
retry_count=item.retry_count
"Retrying failed item: item_id=%s, retry_count=%d",
item.id,
item.retry_count,
)
if retried_ids:
@@ -875,7 +820,7 @@ class DownloadService:
return retried_ids
except Exception as e:
logger.error("Failed to retry items", error=str(e))
logger.error("Failed to retry items: %s", e)
raise DownloadServiceError(
f"Failed to retry: {str(e)}"
) from e
@@ -892,21 +837,17 @@ class DownloadService:
logger.info("Skipping download due to shutdown")
return
# Update status in memory and database
# Update status in memory (status is now in-memory only)
item.status = DownloadStatus.DOWNLOADING
item.started_at = datetime.now(timezone.utc)
self._active_download = item
await self._update_status_in_database(
item.id, DownloadStatus.DOWNLOADING
)
logger.info(
"Starting download",
item_id=item.id,
serie_key=item.serie_id,
serie_name=item.serie_name,
season=item.episode.season,
episode=item.episode.episode,
"Starting download: item_id=%s, serie_key=%s, S%02dE%02d",
item.id,
item.serie_id,
item.episode.season,
item.episode.episode,
)
# Execute download via anime service
@@ -941,13 +882,11 @@ class DownloadService:
self._completed_items.append(item)
# Update database
await self._update_status_in_database(
item.id, DownloadStatus.COMPLETED
)
# Delete completed item from database (status is in-memory)
await self._delete_from_database(item.id)
logger.info(
"Download completed successfully", item_id=item.id
"Download completed successfully: item_id=%s", item.id
)
else:
raise AnimeServiceError("Download returned False")
@@ -955,20 +894,18 @@ class DownloadService:
except asyncio.CancelledError:
# Handle task cancellation during shutdown
logger.info(
"Download cancelled during shutdown",
item_id=item.id,
"Download cancelled during shutdown: item_id=%s",
item.id,
)
item.status = DownloadStatus.CANCELLED
item.completed_at = datetime.now(timezone.utc)
await self._update_status_in_database(
item.id, DownloadStatus.CANCELLED
)
# Delete cancelled item from database
await self._delete_from_database(item.id)
# Return item to pending queue if not shutting down
if not self._is_shutting_down:
self._add_to_pending_queue(item, front=True)
await self._update_status_in_database(
item.id, DownloadStatus.PENDING
)
# Re-save to database as pending
await self._save_to_database(item)
raise # Re-raise to properly cancel the task
except Exception as e:
@@ -978,16 +915,14 @@ class DownloadService:
item.error = str(e)
self._failed_items.append(item)
# Update database with error
await self._update_status_in_database(
item.id, DownloadStatus.FAILED, str(e)
)
# Set error in database
await self._set_error_in_database(item.id, str(e))
logger.error(
"Download failed",
item_id=item.id,
error=str(e),
retry_count=item.retry_count,
"Download failed: item_id=%s, error=%s, retry_count=%d",
item.id,
str(e),
item.retry_count,
)
# Note: Failure is already broadcast by AnimeService
# via ProgressService when SeriesApp fires failed event