diff --git a/infrastructure.md b/infrastructure.md index d2e7fda..1648ab7 100644 --- a/infrastructure.md +++ b/infrastructure.md @@ -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. diff --git a/instructions.md b/instructions.md index a426567..ee82e40 100644 --- a/instructions.md +++ b/instructions.md @@ -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 diff --git a/src/server/api/download.py b/src/server/api/download.py index 7ceab62..a796b23 100644 --- a/src/server/api/download.py +++ b/src/server/api/download.py @@ -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 diff --git a/src/server/models/download.py b/src/server/models/download.py index b7f832f..89c169d 100644 --- a/src/server/models/download.py +++ b/src/server/models/download.py @@ -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" diff --git a/tests/api/test_download_endpoints.py b/tests/api/test_download_endpoints.py index b7001b9..6c8603e 100644 --- a/tests/api/test_download_endpoints.py +++ b/tests/api/test_download_endpoints.py @@ -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", diff --git a/tests/api/test_queue_features.py b/tests/api/test_queue_features.py index 823db84..0f7639c 100644 --- a/tests/api/test_queue_features.py +++ b/tests/api/test_queue_features.py @@ -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" diff --git a/tests/frontend/test_existing_ui_integration.py b/tests/frontend/test_existing_ui_integration.py index 7e8e46a..6c83b6b 100644 --- a/tests/frontend/test_existing_ui_integration.py +++ b/tests/frontend/test_existing_ui_integration.py @@ -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" diff --git a/tests/integration/test_download_flow.py b/tests/integration/test_download_flow.py index b19f81f..f951b6e 100644 --- a/tests/integration/test_download_flow.py +++ b/tests/integration/test_download_flow.py @@ -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"