diff --git a/infrastructure.md b/infrastructure.md index 1648ab7..34a0835 100644 --- a/infrastructure.md +++ b/infrastructure.md @@ -103,6 +103,23 @@ Real-time updates for downloads, scans, and queue operations. **Message Types**: `download_progress`, `download_complete`, `download_failed`, `queue_status`, `scan_progress`, `scan_complete`, `scan_failed` +**Series Identifier in Messages:** +All series-related WebSocket events include `key` as the primary identifier in their data payload: +```json +{ + "type": "download_progress", + "timestamp": "2025-10-17T10:30:00.000Z", + "data": { + "download_id": "abc123", + "key": "attack-on-titan", + "folder": "Attack on Titan (2013)", + "percent": 45.2, + "speed_mbps": 2.5, + "eta_seconds": 180 + } +} +``` + ## Database Models | Model | Purpose | diff --git a/instructions.md b/instructions.md index 5e430d3..3e4b8b9 100644 --- a/instructions.md +++ b/instructions.md @@ -166,34 +166,7 @@ For each task completed: #### Task 4.3: Update Queue API Endpoints to Use Key ✅ (November 27, 2025) ---- - -#### Task 4.4: Update WebSocket API Endpoints to Use Key - -**File:** `src/server/api/websocket.py` - -**Objective:** Ensure WebSocket API endpoint handlers use `key` for series identification. - -**Steps:** - -1. Open `src/server/api/websocket.py` -2. Review all WebSocket message handlers -3. Ensure messages use `key` for series identification -4. Update message schemas to include `key` field -5. Keep `folder` for display purposes -6. Update docstrings - -**Success Criteria:** - -- [ ] All WebSocket handlers use `key` -- [ ] Message schemas include `key` -- [ ] All WebSocket API tests pass - -**Test Command:** - -```bash -conda run -n AniWorld python -m pytest tests/api/ -k "websocket" -v -``` +#### Task 4.4: Update WebSocket API Endpoints to Use Key ✅ (November 27, 2025) --- @@ -828,7 +801,7 @@ conda run -n AniWorld python -m pytest tests/integration/test_identifier_consist - [x] Task 4.1: Update Anime API Endpoints ✅ **Completed November 27, 2025** - [x] Task 4.2: Update Download API Endpoints ✅ **Completed November 27, 2025** - [x] Task 4.3: Update Queue API Endpoints ✅ **Completed November 27, 2025** - - [ ] Task 4.4: Update WebSocket API Endpoints + - [x] Task 4.4: Update WebSocket API Endpoints ✅ **Completed November 27, 2025** - [ ] Task 4.5: Update Pydantic Models - [ ] Task 4.6: Update Validators - [ ] Task 4.7: Update Template Helpers diff --git a/src/server/api/websocket.py b/src/server/api/websocket.py index c3cf46f..aa550d6 100644 --- a/src/server/api/websocket.py +++ b/src/server/api/websocket.py @@ -2,6 +2,14 @@ This module provides WebSocket endpoints for clients to connect and receive real-time updates about downloads, queue status, and system events. + +Series Identifier Convention: + - `key`: Primary identifier for series (provider-assigned, URL-safe) + e.g., "attack-on-titan" + - `folder`: Display metadata only (e.g., "Attack on Titan (2013)") + +All series-related WebSocket events include `key` as the primary identifier +in their data payload. The `folder` field is optional for display purposes. """ from __future__ import annotations @@ -58,19 +66,25 @@ async def websocket_endpoint( } ``` - Server message format: + Server message format (series-related events include 'key' identifier): ```json { "type": "download_progress", "timestamp": "2025-10-17T10:30:00.000Z", "data": { "download_id": "abc123", + "key": "attack-on-titan", + "folder": "Attack on Titan (2013)", "percent": 45.2, "speed_mbps": 2.5, "eta_seconds": 180 } } ``` + + Note: + - `key` is the primary series identifier (provider-assigned, URL-safe) + - `folder` is optional display metadata """ connection_id = str(uuid.uuid4()) user_id: Optional[str] = None diff --git a/src/server/models/websocket.py b/src/server/models/websocket.py index 961e85b..5bce34d 100644 --- a/src/server/models/websocket.py +++ b/src/server/models/websocket.py @@ -3,6 +3,15 @@ This module defines message models for WebSocket communication between the server and clients. Models ensure type safety and provide validation for real-time updates. + +Series Identifier Convention: + - `key`: Primary identifier for series (provider-assigned, URL-safe) + e.g., "attack-on-titan" + - `folder`: Display metadata only (e.g., "Attack on Titan (2013)") + +All series-related WebSocket events should include `key` as the primary +identifier in their data payload. The `folder` field is optional and +used for display purposes only. """ from __future__ import annotations @@ -65,7 +74,16 @@ class WebSocketMessage(BaseModel): class DownloadProgressMessage(BaseModel): - """Download progress update message.""" + """Download progress update message. + + Data payload should include: + - download_id: Unique download identifier + - key: Series identifier (primary, e.g., 'attack-on-titan') + - folder: Series folder name (optional, display only) + - percent: Download progress percentage + - speed_mbps: Download speed + - eta_seconds: Estimated time remaining + """ type: WebSocketMessageType = Field( default=WebSocketMessageType.DOWNLOAD_PROGRESS, @@ -77,12 +95,22 @@ class DownloadProgressMessage(BaseModel): ) data: Dict[str, Any] = Field( ..., - description="Progress data including download_id, percent, speed, eta", + description=( + "Progress data including download_id, key (series identifier), " + "folder (display), percent, speed_mbps, eta_seconds" + ), ) class DownloadCompleteMessage(BaseModel): - """Download completion message.""" + """Download completion message. + + Data payload should include: + - download_id: Unique download identifier + - key: Series identifier (primary, e.g., 'attack-on-titan') + - folder: Series folder name (optional, display only) + - file_path: Path to downloaded file + """ type: WebSocketMessageType = Field( default=WebSocketMessageType.DOWNLOAD_COMPLETE, @@ -93,12 +121,23 @@ class DownloadCompleteMessage(BaseModel): description="ISO 8601 timestamp", ) data: Dict[str, Any] = Field( - ..., description="Completion data including download_id, file_path" + ..., + description=( + "Completion data including download_id, key (series identifier), " + "folder (display), file_path" + ), ) class DownloadFailedMessage(BaseModel): - """Download failure message.""" + """Download failure message. + + Data payload should include: + - download_id: Unique download identifier + - key: Series identifier (primary, e.g., 'attack-on-titan') + - folder: Series folder name (optional, display only) + - error_message: Description of the failure + """ type: WebSocketMessageType = Field( default=WebSocketMessageType.DOWNLOAD_FAILED, @@ -109,7 +148,11 @@ class DownloadFailedMessage(BaseModel): description="ISO 8601 timestamp", ) data: Dict[str, Any] = Field( - ..., description="Error data including download_id, error_message" + ..., + description=( + "Error data including download_id, key (series identifier), " + "folder (display), error_message" + ), ) diff --git a/src/server/services/websocket_service.py b/src/server/services/websocket_service.py index cf764e9..5a78130 100644 --- a/src/server/services/websocket_service.py +++ b/src/server/services/websocket_service.py @@ -3,6 +3,15 @@ This module provides a comprehensive WebSocket manager for handling real-time updates, connection management, room-based messaging, and broadcast functionality for the Aniworld web application. + +Series Identifier Convention: + - `key`: Primary identifier for series (provider-assigned, URL-safe) + e.g., "attack-on-titan" + - `folder`: Display metadata only (e.g., "Attack on Titan (2013)") + +All broadcast methods that handle series-related data should include `key` +as the primary identifier in the message payload. The `folder` field is +optional and used for display purposes only. """ from __future__ import annotations @@ -363,6 +372,16 @@ class WebSocketService: Args: download_id: The download item identifier progress_data: Progress information (percent, speed, etc.) + Should include 'key' (series identifier) and + optionally 'folder' (display name) + + Note: + The progress_data should include: + - key: Series identifier (primary, e.g., 'attack-on-titan') + - folder: Series folder name (optional, display only) + - percent: Download progress percentage + - speed_mbps: Download speed + - eta_seconds: Estimated time remaining """ message = { "type": "download_progress", @@ -382,6 +401,14 @@ class WebSocketService: Args: download_id: The download item identifier result_data: Download result information + Should include 'key' (series identifier) and + optionally 'folder' (display name) + + Note: + The result_data should include: + - key: Series identifier (primary, e.g., 'attack-on-titan') + - folder: Series folder name (optional, display only) + - file_path: Path to the downloaded file """ message = { "type": "download_complete", @@ -401,6 +428,14 @@ class WebSocketService: Args: download_id: The download item identifier error_data: Error information + Should include 'key' (series identifier) and + optionally 'folder' (display name) + + Note: + The error_data should include: + - key: Series identifier (primary, e.g., 'attack-on-titan') + - folder: Series folder name (optional, display only) + - error_message: Description of the failure """ message = { "type": "download_failed", @@ -412,7 +447,9 @@ class WebSocketService: } await self._manager.broadcast_to_room(message, "downloads") - async def broadcast_queue_status(self, status_data: Dict[str, Any]) -> None: + async def broadcast_queue_status( + self, status_data: Dict[str, Any] + ) -> None: """Broadcast queue status update to all clients. Args: