download re implemented

This commit is contained in:
2025-10-30 22:06:41 +01:00
parent 6ebc2ed2ea
commit 3be175522f
16 changed files with 359 additions and 1335 deletions

View File

@@ -10,7 +10,6 @@ from fastapi.responses import JSONResponse
from src.server.models.download import (
DownloadRequest,
QueueOperationRequest,
QueueReorderRequest,
QueueStatusResponse,
)
from src.server.services.download_service import DownloadService, DownloadServiceError
@@ -283,39 +282,41 @@ async def remove_from_queue(
)
@router.delete("/", status_code=status.HTTP_204_NO_CONTENT)
async def remove_multiple_from_queue(
request: QueueOperationRequest,
@router.post("/start", status_code=status.HTTP_200_OK)
async def start_queue(
_: dict = Depends(require_auth),
download_service: DownloadService = Depends(get_download_service),
):
"""Remove multiple items from the download queue.
"""Start the next download from pending queue.
Batch removal of multiple download items. Each item is processed
individually, and the operation continues even if some items are not
found.
Manually starts the first pending download in the queue. Only one download
can be active at a time. If the queue is empty or a download is already
active, an error is returned.
Requires authentication.
Args:
request: List of download item IDs to remove
Returns:
dict: Status message with started item ID
Raises:
HTTPException: 401 if not authenticated, 400 for invalid request,
500 on service error
HTTPException: 401 if not authenticated, 400 if queue is empty or
download already active, 500 on service error
"""
try:
if not request.item_ids:
item_id = await download_service.start_next_download()
if item_id is None:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="At least one item ID must be specified",
detail="No pending downloads in queue",
)
await download_service.remove_from_queue(request.item_ids)
# Note: We don't raise 404 if some items weren't found, as this is
# a batch operation and partial success is acceptable
return {
"status": "success",
"message": "Download started",
"item_id": item_id,
}
except DownloadServiceError as e:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
@@ -326,41 +327,7 @@ async def remove_multiple_from_queue(
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to remove items from queue: {str(e)}",
)
@router.post("/start", status_code=status.HTTP_200_OK)
async def start_queue(
_: dict = Depends(require_auth),
download_service: DownloadService = Depends(get_download_service),
):
"""Start the download queue processor.
Starts processing the download queue. Downloads will be processed according
to priority and concurrency limits. If the queue is already running, this
operation is idempotent.
Requires authentication.
Returns:
dict: Status message indicating queue has been started
Raises:
HTTPException: 401 if not authenticated, 500 on service error
"""
try:
await download_service.start()
return {
"status": "success",
"message": "Download queue processing started",
}
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to start download queue: {str(e)}",
detail=f"Failed to start download: {str(e)}",
)
@@ -369,208 +336,34 @@ async def stop_queue(
_: dict = Depends(require_auth),
download_service: DownloadService = Depends(get_download_service),
):
"""Stop the download queue processor.
"""Stop processing new downloads from queue.
Stops processing the download queue. Active downloads will be allowed to
complete (with a timeout), then the queue processor will shut down.
Queue state is persisted before shutdown.
Prevents new downloads from starting. The current active download will
continue to completion, but no new downloads will be started from the
pending queue.
Requires authentication.
Returns:
dict: Status message indicating queue has been stopped
dict: Status message indicating queue processing has been stopped
Raises:
HTTPException: 401 if not authenticated, 500 on service error
"""
try:
await download_service.stop()
await download_service.stop_downloads()
return {
"status": "success",
"message": "Download queue processing stopped",
"message": (
"Queue processing stopped (current download will continue)"
),
}
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to stop download queue: {str(e)}",
)
@router.post("/pause", status_code=status.HTTP_200_OK)
async def pause_queue(
_: dict = Depends(require_auth),
download_service: DownloadService = Depends(get_download_service),
):
"""Pause the download queue processor.
Pauses download processing. Active downloads will continue, but no new
downloads will be started until the queue is resumed.
Requires authentication.
Returns:
dict: Status message indicating queue has been paused
Raises:
HTTPException: 401 if not authenticated, 500 on service error
"""
try:
await download_service.pause_queue()
return {
"status": "success",
"message": "Download queue paused",
}
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to pause download queue: {str(e)}",
)
@router.post("/resume", status_code=status.HTTP_200_OK)
async def resume_queue(
_: dict = Depends(require_auth),
download_service: DownloadService = Depends(get_download_service),
):
"""Resume the download queue processor.
Resumes download processing after being paused. The queue will continue
processing pending items according to priority.
Requires authentication.
Returns:
dict: Status message indicating queue has been resumed
Raises:
HTTPException: 401 if not authenticated, 500 on service error
"""
try:
await download_service.resume_queue()
return {
"status": "success",
"message": "Download queue resumed",
}
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to resume download queue: {str(e)}",
)
@router.post("/reorder", status_code=status.HTTP_200_OK)
async def reorder_queue(
request: dict,
_: dict = Depends(require_auth),
download_service: DownloadService = Depends(get_download_service),
):
"""Reorder items in the pending queue.
Changes the order of pending download items in the queue. This only
affects items that haven't started downloading yet. Supports both
bulk reordering with item_ids array and single item reorder.
Requires authentication.
Args:
request: Either {"item_ids": ["id1", "id2", ...]} for bulk reorder
or {"item_id": "id", "new_position": 0} for single item
Returns:
dict: Status message indicating items have been reordered
Raises:
HTTPException: 401 if not authenticated, 404 if item not found,
400 for invalid request, 500 on service error
"""
try:
# Support new bulk reorder payload: {"item_ids": ["id1", "id2", ...]}
if "item_ids" in request:
item_order = request.get("item_ids", [])
if not isinstance(item_order, list):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="item_ids must be a list of item IDs",
)
success = await download_service.reorder_queue_bulk(item_order)
if not success:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="One or more items in item_ids were not found in pending queue",
)
return {
"status": "success",
"message": "Queue reordered successfully",
}
# Support legacy bulk reorder payload: {"item_order": ["id1", "id2", ...]}
elif "item_order" in request:
item_order = request.get("item_order", [])
if not isinstance(item_order, list):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="item_order must be a list of item IDs",
)
success = await download_service.reorder_queue_bulk(item_order)
if not success:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="One or more items in item_order were not found in pending queue",
)
return {
"status": "success",
"message": "Queue item reordered successfully",
}
else:
# Fallback to single-item reorder shape
# Validate request
try:
req = QueueReorderRequest(**request)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail=str(e),
)
success = await download_service.reorder_queue(
item_id=req.item_id,
new_position=req.new_position,
)
if not success:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Item {req.item_id} not found in pending queue",
)
return {
"status": "success",
"message": "Queue item reordered successfully",
}
except DownloadServiceError as e:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=str(e),
)
except HTTPException:
raise
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to reorder queue item: {str(e)}",
detail=f"Failed to stop queue processing: {str(e)}",
)