Task 4.4: Update WebSocket API Endpoints to use key identifier

- Updated src/server/api/websocket.py docstrings to document key as primary series identifier
- Updated src/server/models/websocket.py with detailed docstrings explaining key and folder fields in message payloads
- Updated src/server/services/websocket_service.py broadcast method docstrings to document key field usage
- Added WebSocket message example with key in infrastructure.md
- All 83 WebSocket tests pass
- Task 4.4 marked as complete in instructions.md
This commit is contained in:
Lukas 2025-11-27 19:52:53 +01:00
parent f4d14cf17e
commit 3c8ba1d48c
5 changed files with 121 additions and 37 deletions

View File

@ -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` **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 ## Database Models
| Model | Purpose | | Model | Purpose |

View File

@ -166,34 +166,7 @@ For each task completed:
#### Task 4.3: Update Queue API Endpoints to Use Key ✅ (November 27, 2025) #### Task 4.3: Update Queue API Endpoints to Use Key ✅ (November 27, 2025)
--- #### Task 4.4: Update WebSocket 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
```
--- ---
@ -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.1: Update Anime API Endpoints ✅ **Completed November 27, 2025**
- [x] Task 4.2: Update Download 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** - [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.5: Update Pydantic Models
- [ ] Task 4.6: Update Validators - [ ] Task 4.6: Update Validators
- [ ] Task 4.7: Update Template Helpers - [ ] Task 4.7: Update Template Helpers

View File

@ -2,6 +2,14 @@
This module provides WebSocket endpoints for clients to connect and receive This module provides WebSocket endpoints for clients to connect and receive
real-time updates about downloads, queue status, and system events. 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 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 ```json
{ {
"type": "download_progress", "type": "download_progress",
"timestamp": "2025-10-17T10:30:00.000Z", "timestamp": "2025-10-17T10:30:00.000Z",
"data": { "data": {
"download_id": "abc123", "download_id": "abc123",
"key": "attack-on-titan",
"folder": "Attack on Titan (2013)",
"percent": 45.2, "percent": 45.2,
"speed_mbps": 2.5, "speed_mbps": 2.5,
"eta_seconds": 180 "eta_seconds": 180
} }
} }
``` ```
Note:
- `key` is the primary series identifier (provider-assigned, URL-safe)
- `folder` is optional display metadata
""" """
connection_id = str(uuid.uuid4()) connection_id = str(uuid.uuid4())
user_id: Optional[str] = None user_id: Optional[str] = None

View File

@ -3,6 +3,15 @@
This module defines message models for WebSocket communication between This module defines message models for WebSocket communication between
the server and clients. Models ensure type safety and provide validation the server and clients. Models ensure type safety and provide validation
for real-time updates. 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 from __future__ import annotations
@ -65,7 +74,16 @@ class WebSocketMessage(BaseModel):
class DownloadProgressMessage(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( type: WebSocketMessageType = Field(
default=WebSocketMessageType.DOWNLOAD_PROGRESS, default=WebSocketMessageType.DOWNLOAD_PROGRESS,
@ -77,12 +95,22 @@ class DownloadProgressMessage(BaseModel):
) )
data: Dict[str, Any] = Field( 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): 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( type: WebSocketMessageType = Field(
default=WebSocketMessageType.DOWNLOAD_COMPLETE, default=WebSocketMessageType.DOWNLOAD_COMPLETE,
@ -93,12 +121,23 @@ class DownloadCompleteMessage(BaseModel):
description="ISO 8601 timestamp", description="ISO 8601 timestamp",
) )
data: Dict[str, Any] = Field( 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): 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( type: WebSocketMessageType = Field(
default=WebSocketMessageType.DOWNLOAD_FAILED, default=WebSocketMessageType.DOWNLOAD_FAILED,
@ -109,7 +148,11 @@ class DownloadFailedMessage(BaseModel):
description="ISO 8601 timestamp", description="ISO 8601 timestamp",
) )
data: Dict[str, Any] = Field( 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"
),
) )

View File

@ -3,6 +3,15 @@
This module provides a comprehensive WebSocket manager for handling This module provides a comprehensive WebSocket manager for handling
real-time updates, connection management, room-based messaging, and real-time updates, connection management, room-based messaging, and
broadcast functionality for the Aniworld web application. 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 from __future__ import annotations
@ -363,6 +372,16 @@ class WebSocketService:
Args: Args:
download_id: The download item identifier download_id: The download item identifier
progress_data: Progress information (percent, speed, etc.) 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 = { message = {
"type": "download_progress", "type": "download_progress",
@ -382,6 +401,14 @@ class WebSocketService:
Args: Args:
download_id: The download item identifier download_id: The download item identifier
result_data: Download result information 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 = { message = {
"type": "download_complete", "type": "download_complete",
@ -401,6 +428,14 @@ class WebSocketService:
Args: Args:
download_id: The download item identifier download_id: The download item identifier
error_data: Error information 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 = { message = {
"type": "download_failed", "type": "download_failed",
@ -412,7 +447,9 @@ class WebSocketService:
} }
await self._manager.broadcast_to_room(message, "downloads") 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. """Broadcast queue status update to all clients.
Args: Args: