Task 4.2: Update Download API Endpoints to Use Key

- Updated DownloadRequest and DownloadItem models with comprehensive
  docstrings explaining serie_id (key as primary identifier) vs
  serie_folder (filesystem metadata)
- Updated add_to_queue() endpoint docstring to document request parameters
- Updated all test files to include required serie_folder field:
  - tests/api/test_download_endpoints.py
  - tests/api/test_queue_features.py
  - tests/frontend/test_existing_ui_integration.py
  - tests/integration/test_download_flow.py
- Updated infrastructure.md with Download Queue request/response models
- All 869 tests pass

This is part of the Series Identifier Standardization effort (Phase 4.2)
to ensure key is used as the primary identifier throughout the codebase.
This commit is contained in:
Lukas 2025-11-27 19:33:06 +01:00
parent da4973829e
commit 589141e9aa
8 changed files with 80 additions and 46 deletions

View File

@ -86,6 +86,15 @@ All lookups, events, and API operations use `key`. The `folder` is metadata for
- `POST /retry` - Retry failed downloads
- `DELETE /completed` - Clear completed items
**Request Models:**
- `DownloadRequest`: `serie_id` (key, primary identifier), `serie_folder` (filesystem path), `serie_name` (display), `episodes`, `priority`
**Response Models:**
- `DownloadItem`: `id`, `serie_id` (key), `serie_folder` (metadata), `serie_name`, `episode`, `status`, `progress`
- `QueueStatus`: `is_running`, `is_paused`, `active_downloads`, `pending_queue`, `completed_downloads`, `failed_downloads`
### WebSocket (`/ws/connect`)
Real-time updates for downloads, scans, and queue operations.

View File

@ -198,37 +198,20 @@ Updated `src/server/api/anime.py` to standardize all endpoints to use `key` as t
---
#### Task 4.2: Update Download API Endpoints to Use Key
#### Task 4.2: Update Download API Endpoints to Use Key
**File:** [`src/server/api/download.py`](src/server/api/download.py)
**Completed:** November 27, 2025
**Objective:** Ensure download API uses `key` for series identification.
Updated `src/server/api/download.py` and `src/server/models/download.py` to standardize identifier documentation:
**Steps:**
- Updated `DownloadRequest` model with comprehensive docstrings explaining `serie_id` (key as primary identifier) vs `serie_folder` (filesystem metadata)
- Updated `DownloadItem` model with same clarification
- Updated `add_to_queue()` endpoint docstring to document request parameters
- Updated all test files to include required `serie_folder` field
- All download API tests pass (23/23)
- All integration tests pass (869/869)
1. Open [`src/server/api/download.py`](src/server/api/download.py)
2. Update `DownloadRequest` model:
- Rename or document `serie_id` as `series_key`
- Keep `serie_folder` for filesystem operations
- Keep `serie_name` for display
3. Update `add_to_queue()` endpoint:
- Use `series_key` for series identification
- Pass `serie_folder` to download service
4. Update all docstrings
**Success Criteria:**
- [ ] Request model uses `key` as identifier
- [ ] Endpoint passes correct identifiers to service
- [ ] All download API tests pass
**Test Command:**
```bash
conda run -n AniWorld python -m pytest tests/api/ -k "download" -v
```
---
------
#### Task 4.3: Update Queue API Endpoints to Use Key
@ -918,7 +901,7 @@ conda run -n AniWorld python -m pytest tests/integration/test_identifier_consist
- [x] Phase 3: Service Layer ✅
- [ ] Phase 4: API Layer
- [x] Task 4.1: Update Anime API Endpoints ✅ **Completed November 27, 2025**
- [ ] Task 4.2: Update Download API Endpoints
- [x] Task 4.2: Update Download API Endpoints ✅ **Completed November 27, 2025**
- [ ] Task 4.3: Update Queue API Endpoints
- [ ] Task 4.4: Update WebSocket API Endpoints
- [ ] Task 4.5: Update Pydantic Models

View File

@ -74,7 +74,12 @@ async def add_to_queue(
Requires authentication.
Args:
request: Download request with serie info, episodes, and priority
request: Download request containing:
- serie_id: Series key (primary identifier, 'attack-on-titan')
- serie_folder: Filesystem folder name for storing downloads
- serie_name: Display name for the series
- episodes: List of episodes to download
- priority: Queue priority level
Returns:
DownloadResponse: Status and list of created download item IDs

View File

@ -63,24 +63,33 @@ class DownloadProgress(BaseModel):
class DownloadItem(BaseModel):
"""Represents a single download item in the queue."""
"""Represents a single download item in the queue.
Note on identifiers:
- serie_id: The provider-assigned key (e.g., 'attack-on-titan') used for
all lookups and identification. This is the primary identifier.
- serie_folder: The filesystem folder name (e.g., 'Attack on Titan (2013)')
used only for filesystem operations. This is metadata, not an identifier.
"""
id: str = Field(..., description="Unique download item identifier")
serie_id: str = Field(
...,
description=(
"Series identifier - provider key "
"(e.g., 'attack-on-titan')"
"Series key (primary identifier) - provider-assigned URL-safe "
"key (e.g., 'attack-on-titan'). Used for lookups/identification."
)
)
serie_folder: str = Field(
...,
description=(
"Series folder name on disk "
"(e.g., 'Attack on Titan (2013)')"
"Series folder name on disk (metadata only) "
"(e.g., 'Attack on Titan (2013)'). For filesystem ops only."
)
)
serie_name: str = Field(..., min_length=1, description="Series name")
serie_name: str = Field(
..., min_length=1, description="Series display name"
)
episode: EpisodeIdentifier = Field(
..., description="Episode identification"
)
@ -168,24 +177,31 @@ class QueueStats(BaseModel):
class DownloadRequest(BaseModel):
"""Request to add episode(s) to the download queue."""
"""Request to add episode(s) to the download queue.
Note on identifiers:
- serie_id: The provider-assigned key (e.g., 'attack-on-titan') used as
the primary identifier for all operations. This is the unique key.
- serie_folder: The filesystem folder name (e.g., 'Attack on Titan (2013)')
used only for storing downloaded files. This is metadata.
"""
serie_id: str = Field(
...,
description=(
"Series identifier - provider key "
"(e.g., 'attack-on-titan')"
"Series key (primary identifier) - provider-assigned URL-safe "
"key (e.g., 'attack-on-titan'). Used for lookups/identification."
)
)
serie_folder: str = Field(
...,
description=(
"Series folder name on disk "
"(e.g., 'Attack on Titan (2013)')"
"Series folder name on disk (metadata only) "
"(e.g., 'Attack on Titan (2013)'). For filesystem ops only."
)
)
serie_name: str = Field(
..., min_length=1, description="Series name for display"
..., min_length=1, description="Series display name"
)
episodes: List[EpisodeIdentifier] = Field(
..., description="List of episodes to download"

View File

@ -146,7 +146,8 @@ async def test_get_queue_status_unauthorized(mock_download_service):
async def test_add_to_queue(authenticated_client, mock_download_service):
"""Test POST /api/queue/add endpoint."""
request_data = {
"serie_id": "series-1",
"serie_id": "test-anime",
"serie_folder": "Test Anime (2024)",
"serie_name": "Test Anime",
"episodes": [
{"season": 1, "episode": 1},
@ -175,7 +176,8 @@ async def test_add_to_queue_with_high_priority(
):
"""Test adding items with HIGH priority."""
request_data = {
"serie_id": "series-1",
"serie_id": "test-anime",
"serie_folder": "Test Anime (2024)",
"serie_name": "Test Anime",
"episodes": [{"season": 1, "episode": 1}],
"priority": "high",
@ -198,7 +200,8 @@ async def test_add_to_queue_empty_episodes(
):
"""Test adding empty episodes list returns 400."""
request_data = {
"serie_id": "series-1",
"serie_id": "test-anime",
"serie_folder": "Test Anime (2024)",
"serie_name": "Test Anime",
"episodes": [],
"priority": "normal",
@ -221,7 +224,8 @@ async def test_add_to_queue_service_error(
)
request_data = {
"serie_id": "series-1",
"serie_id": "test-anime",
"serie_folder": "Test Anime (2024)",
"serie_name": "Test Anime",
"episodes": [{"season": 1, "episode": 1}],
"priority": "normal",

View File

@ -48,6 +48,7 @@ def sample_download_request():
"""Sample download request for testing."""
return {
"serie_id": "test-series",
"serie_folder": "Test Series (2024)",
"serie_name": "Test Series",
"episodes": [
{"season": 1, "episode": 1},
@ -159,6 +160,7 @@ class TestQueueReordering:
"/api/queue/add",
json={
"serie_id": f"test-{i}",
"serie_folder": f"Test Series {i} (2024)",
"serie_name": f"Test Series {i}",
"episodes": [{"season": 1, "episode": i+1}],
"priority": "normal"

View File

@ -252,7 +252,8 @@ class TestFrontendDownloadAPI:
response = await authenticated_client.post(
"/api/queue/add",
json={
"serie_id": "test_anime",
"serie_id": "test-anime",
"serie_folder": "Test Anime (2024)",
"serie_name": "Test Anime",
"episodes": [{"season": 1, "episode": 1}],
"priority": "normal"

View File

@ -133,6 +133,7 @@ class TestDownloadFlowEndToEnd:
"/api/queue/add",
json={
"serie_id": "test-series-1",
"serie_folder": "Test Anime Series (2024)",
"serie_name": "Test Anime Series",
"episodes": [
{"season": 1, "episode": 1, "title": "Episode 1"},
@ -158,6 +159,7 @@ class TestDownloadFlowEndToEnd:
"/api/queue/add",
json={
"serie_id": "test-series-2",
"serie_folder": "Another Series (2024)",
"serie_name": "Another Series",
"episodes": [{"season": 1, "episode": 1}],
"priority": "high"
@ -194,6 +196,7 @@ class TestDownloadFlowEndToEnd:
"/api/queue/add",
json={
"serie_id": f"series-{priority}",
"serie_folder": f"Series {priority.title()} (2024)",
"serie_name": f"Series {priority.title()}",
"episodes": [{"season": 1, "episode": 1}],
"priority": priority
@ -208,6 +211,7 @@ class TestDownloadFlowEndToEnd:
"/api/queue/add",
json={
"serie_id": "test-series",
"serie_folder": "Test Series (2024)",
"serie_name": "Test Series",
"episodes": [],
"priority": "normal"
@ -224,6 +228,7 @@ class TestDownloadFlowEndToEnd:
"/api/queue/add",
json={
"serie_id": "test-series",
"serie_folder": "Test Series (2024)",
"serie_name": "Test Series",
"episodes": [{"season": 1, "episode": 1}],
"priority": "invalid"
@ -267,6 +272,7 @@ class TestQueueItemOperations:
"/api/queue/add",
json={
"serie_id": "test-series",
"serie_folder": "Test Series (2024)",
"serie_name": "Test Series",
"episodes": [{"season": 1, "episode": 1}],
"priority": "normal"
@ -301,6 +307,7 @@ class TestDownloadProgressTracking:
"/api/queue/add",
json={
"serie_id": "test-series",
"serie_folder": "Test Series (2024)",
"serie_name": "Test Series",
"episodes": [{"season": 1, "episode": 1}],
"priority": "normal"
@ -358,6 +365,7 @@ class TestErrorHandlingAndRetries:
"/api/queue/add",
json={
"serie_id": "invalid-series",
"serie_folder": "Invalid Series (2024)",
"serie_name": "Invalid Series",
"episodes": [{"season": 99, "episode": 99}],
"priority": "normal"
@ -374,6 +382,7 @@ class TestErrorHandlingAndRetries:
"/api/queue/add",
json={
"serie_id": "test-series",
"serie_folder": "Test Series (2024)",
"serie_name": "Test Series",
"episodes": [{"season": 1, "episode": 1}],
"priority": "normal"
@ -412,6 +421,7 @@ class TestAuthenticationRequirements:
"/api/queue/add",
json={
"serie_id": "test-series",
"serie_folder": "Test Series (2024)",
"serie_name": "Test Series",
"episodes": [{"season": 1, "episode": 1}],
"priority": "normal"
@ -444,6 +454,7 @@ class TestConcurrentOperations:
"/api/queue/add",
json={
"serie_id": f"series-{i}",
"serie_folder": f"Series {i} (2024)",
"serie_name": f"Series {i}",
"episodes": [{"season": 1, "episode": 1}],
"priority": "normal"
@ -488,6 +499,7 @@ class TestQueuePersistence:
"/api/queue/add",
json={
"serie_id": "persistent-series",
"serie_folder": "Persistent Series (2024)",
"serie_name": "Persistent Series",
"episodes": [{"season": 1, "episode": 1}],
"priority": "normal"
@ -524,6 +536,7 @@ class TestWebSocketIntegrationWithDownloads:
"/api/queue/add",
json={
"serie_id": "ws-series",
"serie_folder": "WebSocket Series (2024)",
"serie_name": "WebSocket Series",
"episodes": [{"season": 1, "episode": 1}],
"priority": "normal"
@ -546,6 +559,7 @@ class TestCompleteDownloadWorkflow:
"/api/queue/add",
json={
"serie_id": "workflow-series",
"serie_folder": "Workflow Test Series (2024)",
"serie_name": "Workflow Test Series",
"episodes": [{"season": 1, "episode": 1}],
"priority": "high"