# API Documentation ## Document Purpose This document provides comprehensive REST API and WebSocket reference for the Aniworld application. Source: [src/server/fastapi_app.py](../src/server/fastapi_app.py#L1-L252) --- ## 1. API Overview ### Base URL and Versioning | Environment | Base URL | | ------------------- | --------------------------------- | | Local Development | `http://127.0.0.1:8000` | | API Documentation | `http://127.0.0.1:8000/api/docs` | | ReDoc Documentation | `http://127.0.0.1:8000/api/redoc` | The API does not use versioning prefixes. All endpoints are available under `/api/*`. Source: [src/server/fastapi_app.py](../src/server/fastapi_app.py#L177-L184) ### Authentication The API uses JWT Bearer Token authentication. **Header Format:** ``` Authorization: Bearer ``` **Public Endpoints (no authentication required):** - `/api/auth/*` - Authentication endpoints - `/api/health` - Health check endpoints - `/api/docs`, `/api/redoc` - API documentation - `/static/*` - Static files - `/`, `/login`, `/setup`, `/queue` - UI pages Source: [src/server/middleware/auth.py](../src/server/middleware/auth.py#L39-L52) ### Content Types | Direction | Content-Type | | --------- | ----------------------------- | | Request | `application/json` | | Response | `application/json` | | WebSocket | `application/json` (messages) | ### Common Headers | Header | Required | Description | | --------------- | -------- | ------------------------------------ | | `Authorization` | Yes\* | Bearer token for protected endpoints | | `Content-Type` | Yes | `application/json` for POST/PUT | | `Origin` | No | Required for CORS preflight | \*Not required for public endpoints listed above. --- ## 2. Authentication Endpoints Prefix: `/api/auth` Source: [src/server/api/auth.py](../src/server/api/auth.py#L1-L180) ### POST /api/auth/setup Initial setup endpoint to configure the master password. Can only be called once. **Request Body:** ```json { "master_password": "string (min 8 chars, mixed case, number, special char)", "anime_directory": "string (optional, path to anime folder)" } ``` **Response (201 Created):** ```json { "status": "ok" } ``` **Errors:** - `400 Bad Request` - Master password already configured or invalid password Source: [src/server/api/auth.py](../src/server/api/auth.py#L28-L90) ### POST /api/auth/login Validate master password and return JWT token. **Request Body:** ```json { "password": "string", "remember": false } ``` **Response (200 OK):** ```json { "access_token": "eyJ...", "token_type": "bearer", "expires_at": "2025-12-14T10:30:00Z" } ``` **Errors:** - `401 Unauthorized` - Invalid credentials - `429 Too Many Requests` - Account locked due to failed attempts Source: [src/server/api/auth.py](../src/server/api/auth.py#L93-L124) ### POST /api/auth/logout Logout by revoking token. **Response (200 OK):** ```json { "status": "ok", "message": "Logged out successfully" } ``` Source: [src/server/api/auth.py](../src/server/api/auth.py#L127-L140) ### GET /api/auth/status Return whether master password is configured and if caller is authenticated. **Response (200 OK):** ```json { "configured": true, "authenticated": true } ``` Source: [src/server/api/auth.py](../src/server/api/auth.py#L157-L162) --- ## 3. Anime Endpoints Prefix: `/api/anime` Source: [src/server/api/anime.py](../src/server/api/anime.py#L1-L812) ### Series Identifier Convention The API uses two identifier fields: | Field | Purpose | Example | | -------- | ---------------------------------------------------- | -------------------------- | | `key` | **Primary identifier** - provider-assigned, URL-safe | `"attack-on-titan"` | | `folder` | Metadata only - filesystem folder name | `"Attack on Titan (2013)"` | Use `key` for all API operations. The `folder` field is for display purposes only. ### GET /api/anime/status Get anime library status information. **Authentication:** Required **Response (200 OK):** ```json { "directory": "/path/to/anime", "series_count": 42 } ``` Source: [src/server/api/anime.py](../src/server/api/anime.py#L28-L58) ### GET /api/anime List library series that have missing episodes. **Authentication:** Required **Query Parameters:** | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `page` | int | 1 | Page number (must be positive) | | `per_page` | int | 20 | Items per page (max 1000) | | `sort_by` | string | null | Sort field: `title`, `id`, `name`, `missing_episodes` | | `filter` | string | null | Filter: `no_episodes` (shows only series with no downloaded episodes in folder) | **Response (200 OK):** ```json [ { "key": "beheneko-the-elf-girls-cat", "name": "Beheneko", "site": "aniworld.to", "folder": "beheneko the elf girls cat (2025)", "missing_episodes": { "1": [1, 2, 3, 4] }, "link": "" } ] ``` **Example with filter:** ```bash GET /api/anime?filter=no_episodes ``` Source: [src/server/api/anime.py](../src/server/api/anime.py#L155-L303) ### GET /api/anime/search Search the provider for anime series matching a query. **Authentication:** Not required **Query Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `query` | string | Yes | Search term (max 200 chars) | **Response (200 OK):** ```json [ { "key": "attack-on-titan", "name": "Attack on Titan", "site": "aniworld.to", "folder": "Attack on Titan (2013)", "missing_episodes": {}, "link": "https://aniworld.to/anime/stream/attack-on-titan" } ] ``` Source: [src/server/api/anime.py](../src/server/api/anime.py#L431-L474) ### POST /api/anime/search Search via POST body. **Request Body:** ```json { "query": "attack on titan" } ``` **Response:** Same as GET /api/anime/search Source: [src/server/api/anime.py](../src/server/api/anime.py#L477-L495) ### POST /api/anime/add Add a new series to the library with automatic database persistence, folder creation, and episode scanning. **Authentication:** Required **Request Body:** ```json { "link": "https://aniworld.to/anime/stream/attack-on-titan", "name": "Attack on Titan" } ``` **Response (200 OK):** ```json { "status": "success", "message": "Successfully added series: Attack on Titan", "key": "attack-on-titan", "folder": "Attack on Titan", "db_id": 1, "missing_episodes": ["1-1", "1-2", "1-3"], "total_missing": 3 } ``` **Enhanced Flow:** 1. Validates the request (link format, name) 2. Creates Serie object with sanitized folder name 3. Saves to database via AnimeDBService 4. Creates folder using sanitized display name (not internal key) 5. Performs targeted episode scan for this anime only 6. Returns response with missing episodes count **Folder Name Sanitization:** - Removes invalid filesystem characters: `< > : " / \ | ? *` - Trims leading/trailing whitespace and dots - Preserves Unicode characters (for Japanese titles) - Example: `"Attack on Titan: Final Season"` → `"Attack on Titan Final Season"` Source: [src/server/api/anime.py](../src/server/api/anime.py#L604-L710) ### POST /api/anime/rescan Trigger a rescan of the local library. **Authentication:** Required **Response (200 OK):** ```json { "success": true, "message": "Rescan started successfully" } ``` Source: [src/server/api/anime.py](../src/server/api/anime.py#L306-L337) ### GET /api/anime/{anime_id} Return detailed information about a specific series. **Authentication:** Not required **Path Parameters:** | Parameter | Description | |-----------|-------------| | `anime_id` | Series `key` (primary) or `folder` (deprecated fallback) | **Response (200 OK):** ```json { "key": "attack-on-titan", "title": "Attack on Titan", "folder": "Attack on Titan (2013)", "episodes": ["1-1", "1-2", "1-3"], "description": null } ``` Source: [src/server/api/anime.py](../src/server/api/anime.py#L713-L793) --- ## 4. Download Queue Endpoints Prefix: `/api/queue` Source: [src/server/api/download.py](../src/server/api/download.py#L1-L529) ### GET /api/queue/status Get current download queue status and statistics. **Authentication:** Required **Response (200 OK):** ```json { "status": { "is_running": false, "is_paused": false, "active_downloads": [], "pending_queue": [], "completed_downloads": [], "failed_downloads": [] }, "statistics": { "total_items": 5, "pending_count": 3, "active_count": 1, "completed_count": 1, "failed_count": 0, "total_downloaded_mb": 1024.5, "average_speed_mbps": 2.5, "estimated_time_remaining": 3600 } } ``` Source: [src/server/api/download.py](../src/server/api/download.py#L21-L56) ### POST /api/queue/add Add episodes to the download queue. **Authentication:** Required **Request Body:** ```json { "serie_id": "attack-on-titan", "serie_folder": "Attack on Titan (2013)", "serie_name": "Attack on Titan", "episodes": [ { "season": 1, "episode": 1, "title": "Episode 1" }, { "season": 1, "episode": 2, "title": "Episode 2" } ], "priority": "NORMAL" } ``` **Priority Values:** `LOW`, `NORMAL`, `HIGH` **Response (201 Created):** ```json { "status": "success", "message": "Added 2 episode(s) to download queue", "added_items": ["uuid1", "uuid2"], "item_ids": ["uuid1", "uuid2"], "failed_items": [] } ``` Source: [src/server/api/download.py](../src/server/api/download.py#L59-L120) ### POST /api/queue/start Start automatic queue processing. **Authentication:** Required **Response (200 OK):** ```json { "status": "success", "message": "Queue processing started" } ``` Source: [src/server/api/download.py](../src/server/api/download.py#L293-L331) ### POST /api/queue/stop Stop processing new downloads from queue. **Authentication:** Required **Response (200 OK):** ```json { "status": "success", "message": "Queue processing stopped (current download will continue)" } ``` Source: [src/server/api/download.py](../src/server/api/download.py#L334-L387) ### POST /api/queue/pause Pause queue processing (alias for stop). **Authentication:** Required **Response (200 OK):** ```json { "status": "success", "message": "Queue processing paused" } ``` Source: [src/server/api/download.py](../src/server/api/download.py#L416-L445) ### DELETE /api/queue/{item_id} Remove a specific item from the download queue. **Authentication:** Required **Path Parameters:** | Parameter | Description | |-----------|-------------| | `item_id` | Download item UUID | **Response (204 No Content)** Source: [src/server/api/download.py](../src/server/api/download.py#L225-L256) ### DELETE /api/queue Remove multiple items from the download queue. **Authentication:** Required **Request Body:** ```json { "item_ids": ["uuid1", "uuid2"] } ``` **Response (204 No Content)** Source: [src/server/api/download.py](../src/server/api/download.py#L259-L290) ### DELETE /api/queue/completed Clear completed downloads from history. **Authentication:** Required **Response (200 OK):** ```json { "status": "success", "message": "Cleared 5 completed item(s)", "count": 5 } ``` Source: [src/server/api/download.py](../src/server/api/download.py#L123-L149) ### DELETE /api/queue/failed Clear failed downloads from history. **Authentication:** Required **Response (200 OK):** ```json { "status": "success", "message": "Cleared 2 failed item(s)", "count": 2 } ``` Source: [src/server/api/download.py](../src/server/api/download.py#L152-L178) ### DELETE /api/queue/pending Clear all pending downloads from the queue. **Authentication:** Required **Response (200 OK):** ```json { "status": "success", "message": "Removed 10 pending item(s)", "count": 10 } ``` Source: [src/server/api/download.py](../src/server/api/download.py#L181-L207) ### POST /api/queue/reorder Reorder items in the pending queue. **Authentication:** Required **Request Body:** ```json { "item_ids": ["uuid3", "uuid1", "uuid2"] } ``` **Response (200 OK):** ```json { "status": "success", "message": "Queue reordered with 3 items" } ``` Source: [src/server/api/download.py](../src/server/api/download.py#L448-L477) ### POST /api/queue/retry Retry failed downloads. **Authentication:** Required **Request Body:** ```json { "item_ids": ["uuid1", "uuid2"] } ``` Pass empty `item_ids` array to retry all failed items. **Response (200 OK):** ```json { "status": "success", "message": "Retrying 2 failed item(s)", "retried_count": 2, "retried_ids": ["uuid1", "uuid2"] } ``` Source: [src/server/api/download.py](../src/server/api/download.py#L480-L514) --- ## 5. Configuration Endpoints Prefix: `/api/config` Source: [src/server/api/config.py](../src/server/api/config.py#L1-L374) ### GET /api/config Return current application configuration. **Authentication:** Required **Response (200 OK):** ```json { "name": "Aniworld", "data_dir": "data", "scheduler": { "enabled": true, "interval_minutes": 60 }, "logging": { "level": "INFO", "file": null, "max_bytes": null, "backup_count": 3 }, "backup": { "enabled": false, "path": "data/backups", "keep_days": 30 }, "other": {} } ``` Source: [src/server/api/config.py](../src/server/api/config.py#L16-L27) ### PUT /api/config Apply an update to the configuration. **Authentication:** Required **Request Body:** ```json { "scheduler": { "enabled": true, "interval_minutes": 30 }, "logging": { "level": "DEBUG" } } ``` **Response (200 OK):** Updated configuration object Source: [src/server/api/config.py](../src/server/api/config.py#L30-L47) ### POST /api/config/validate Validate a configuration without applying it. **Authentication:** Required **Request Body:** Full `AppConfig` object **Response (200 OK):** ```json { "valid": true, "errors": [] } ``` Source: [src/server/api/config.py](../src/server/api/config.py#L50-L64) ### GET /api/config/backups List all available configuration backups. **Authentication:** Required **Response (200 OK):** ```json [ { "name": "config_backup_20251213_090130.json", "size": 1024, "created": "2025-12-13T09:01:30Z" } ] ``` Source: [src/server/api/config.py](../src/server/api/config.py#L67-L81) ### POST /api/config/backups Create a backup of the current configuration. **Authentication:** Required **Query Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `name` | string | No | Custom backup name | **Response (200 OK):** ```json { "name": "config_backup_20251213_090130.json", "message": "Backup created successfully" } ``` Source: [src/server/api/config.py](../src/server/api/config.py#L84-L102) ### POST /api/config/backups/{backup_name}/restore Restore configuration from a backup. **Authentication:** Required **Response (200 OK):** Restored configuration object Source: [src/server/api/config.py](../src/server/api/config.py#L105-L123) ### DELETE /api/config/backups/{backup_name} Delete a configuration backup. **Authentication:** Required **Response (200 OK):** ```json { "message": "Backup 'config_backup_20251213.json' deleted successfully" } ``` Source: [src/server/api/config.py](../src/server/api/config.py#L126-L142) ### POST /api/config/directory Update anime directory configuration. **Authentication:** Required **Request Body:** ```json { "directory": "/path/to/anime" } ``` **Response (200 OK):** ```json { "message": "Anime directory updated successfully", "synced_series": 15 } ``` Source: [src/server/api/config.py](../src/server/api/config.py#L189-L247) --- ## 6. NFO Management Endpoints Prefix: `/api/nfo` Source: [src/server/api/nfo.py](../src/server/api/nfo.py#L1-L684) These endpoints manage tvshow.nfo metadata files and associated media (poster, logo, fanart) for anime series. NFO files use Kodi/XBMC format and are scraped from TMDB API. **Prerequisites:** - TMDB API key must be configured in settings - NFO service returns 503 if API key not configured ### GET /api/nfo/{serie_id}/check Check if NFO file and media files exist for a series. **Authentication:** Required **Path Parameters:** - `serie_id` (string): Series identifier **Response (200 OK):** ```json { "serie_id": "one-piece", "serie_folder": "One Piece (1999)", "has_nfo": true, "nfo_path": "/path/to/anime/One Piece (1999)/tvshow.nfo", "media_files": { "has_poster": true, "has_logo": false, "has_fanart": true, "poster_path": "/path/to/anime/One Piece (1999)/poster.jpg", "logo_path": null, "fanart_path": "/path/to/anime/One Piece (1999)/fanart.jpg" } } ``` **Errors:** - `401 Unauthorized` - Not authenticated - `404 Not Found` - Series not found - `503 Service Unavailable` - TMDB API key not configured Source: [src/server/api/nfo.py](../src/server/api/nfo.py#L90-L147) ### POST /api/nfo/{serie_id}/create Create NFO file and download media for a series. **Authentication:** Required **Path Parameters:** - `serie_id` (string): Series identifier **Request Body:** ```json { "serie_name": "One Piece", "year": 1999, "download_poster": true, "download_logo": true, "download_fanart": true, "overwrite_existing": false } ``` **Fields:** - `serie_name` (string, optional): Series name for TMDB search (defaults to folder name) - `year` (integer, optional): Series year to help narrow TMDB search - `download_poster` (boolean, default: true): Download poster.jpg - `download_logo` (boolean, default: true): Download logo.png - `download_fanart` (boolean, default: true): Download fanart.jpg - `overwrite_existing` (boolean, default: false): Overwrite existing NFO **Response (200 OK):** ```json { "serie_id": "one-piece", "serie_folder": "One Piece (1999)", "nfo_path": "/path/to/anime/One Piece (1999)/tvshow.nfo", "media_files": { "has_poster": true, "has_logo": true, "has_fanart": true, "poster_path": "/path/to/anime/One Piece (1999)/poster.jpg", "logo_path": "/path/to/anime/One Piece (1999)/logo.png", "fanart_path": "/path/to/anime/One Piece (1999)/fanart.jpg" }, "message": "NFO and media files created successfully" } ``` **Errors:** - `401 Unauthorized` - Not authenticated - `404 Not Found` - Series not found - `409 Conflict` - NFO already exists (use `overwrite_existing: true`) - `503 Service Unavailable` - TMDB API error or key not configured Source: [src/server/api/nfo.py](../src/server/api/nfo.py#L150-L240) ### PUT /api/nfo/{serie_id}/update Update existing NFO file with fresh TMDB data. **Authentication:** Required **Path Parameters:** - `serie_id` (string): Series identifier **Query Parameters:** - `download_media` (boolean, default: true): Re-download media files **Response (200 OK):** ```json { "serie_id": "one-piece", "serie_folder": "One Piece (1999)", "nfo_path": "/path/to/anime/One Piece (1999)/tvshow.nfo", "media_files": { "has_poster": true, "has_logo": true, "has_fanart": true, "poster_path": "/path/to/anime/One Piece (1999)/poster.jpg", "logo_path": "/path/to/anime/One Piece (1999)/logo.png", "fanart_path": "/path/to/anime/One Piece (1999)/fanart.jpg" }, "message": "NFO updated successfully" } ``` **Errors:** - `401 Unauthorized` - Not authenticated - `404 Not Found` - Series or NFO not found (use create endpoint) - `503 Service Unavailable` - TMDB API error Source: [src/server/api/nfo.py](../src/server/api/nfo.py#L243-L325) ### GET /api/nfo/{serie_id}/content Get NFO file XML content for a series. **Authentication:** Required **Path Parameters:** - `serie_id` (string): Series identifier **Response (200 OK):** ```json { "serie_id": "one-piece", "serie_folder": "One Piece (1999)", "content": "\n...", "file_size": 2048, "last_modified": "2026-01-15T10:30:00" } ``` **Errors:** - `401 Unauthorized` - Not authenticated - `404 Not Found` - Series or NFO not found Source: [src/server/api/nfo.py](../src/server/api/nfo.py#L328-L397) ### GET /api/nfo/{serie_id}/media/status Get media files status for a series. **Authentication:** Required **Path Parameters:** - `serie_id` (string): Series identifier **Response (200 OK):** ```json { "has_poster": true, "has_logo": false, "has_fanart": true, "poster_path": "/path/to/anime/One Piece (1999)/poster.jpg", "logo_path": null, "fanart_path": "/path/to/anime/One Piece (1999)/fanart.jpg" } ``` **Errors:** - `401 Unauthorized` - Not authenticated - `404 Not Found` - Series not found Source: [src/server/api/nfo.py](../src/server/api/nfo.py#L400-L447) ### POST /api/nfo/{serie_id}/media/download Download missing media files for a series. **Authentication:** Required **Path Parameters:** - `serie_id` (string): Series identifier **Request Body:** ```json { "download_poster": true, "download_logo": true, "download_fanart": true } ``` **Response (200 OK):** ```json { "has_poster": true, "has_logo": true, "has_fanart": true, "poster_path": "/path/to/anime/One Piece (1999)/poster.jpg", "logo_path": "/path/to/anime/One Piece (1999)/logo.png", "fanart_path": "/path/to/anime/One Piece (1999)/fanart.jpg" } ``` **Errors:** - `401 Unauthorized` - Not authenticated - `404 Not Found` - Series or NFO not found (NFO required for TMDB ID) - `503 Service Unavailable` - TMDB API error Source: [src/server/api/nfo.py](../src/server/api/nfo.py#L450-L519) ### POST /api/nfo/batch/create Batch create NFO files for multiple series. **Authentication:** Required **Request Body:** ```json { "serie_ids": ["one-piece", "naruto", "bleach"], "download_media": true, "skip_existing": true, "max_concurrent": 3 } ``` **Fields:** - `serie_ids` (array of strings): Series identifiers to process - `download_media` (boolean, default: true): Download media files - `skip_existing` (boolean, default: true): Skip series with existing NFOs - `max_concurrent` (integer, 1-10, default: 3): Number of concurrent operations **Response (200 OK):** ```json { "total": 3, "successful": 2, "failed": 0, "skipped": 1, "results": [ { "serie_id": "one-piece", "serie_folder": "One Piece (1999)", "success": true, "message": "NFO created successfully", "nfo_path": "/path/to/anime/One Piece (1999)/tvshow.nfo" }, { "serie_id": "naruto", "serie_folder": "Naruto (2002)", "success": false, "message": "Skipped - NFO already exists", "nfo_path": null }, { "serie_id": "bleach", "serie_folder": "Bleach (2004)", "success": true, "message": "NFO created successfully", "nfo_path": "/path/to/anime/Bleach (2004)/tvshow.nfo" } ] } ``` **Errors:** - `401 Unauthorized` - Not authenticated - `503 Service Unavailable` - TMDB API key not configured Source: [src/server/api/nfo.py](../src/server/api/nfo.py#L522-L634) ### GET /api/nfo/missing Get list of series without NFO files. **Authentication:** Required **Response (200 OK):** ```json { "total_series": 150, "missing_nfo_count": 23, "series": [ { "serie_id": "dragon-ball", "serie_folder": "Dragon Ball (1986)", "serie_name": "Dragon Ball", "has_media": false, "media_files": { "has_poster": false, "has_logo": false, "has_fanart": false, "poster_path": null, "logo_path": null, "fanart_path": null } } ] } ``` **Errors:** - `401 Unauthorized` - Not authenticated - `503 Service Unavailable` - TMDB API key not configured Source: [src/server/api/nfo.py](../src/server/api/nfo.py#L637-L684) --- ## 7. Scheduler Endpoints Prefix: `/api/scheduler` Source: [src/server/api/scheduler.py](../src/server/api/scheduler.py#L1-L122) ### GET /api/scheduler/config Get current scheduler configuration. **Authentication:** Required **Response (200 OK):** ```json { "enabled": true, "interval_minutes": 60 } ``` Source: [src/server/api/scheduler.py](../src/server/api/scheduler.py#L22-L42) ### POST /api/scheduler/config Update scheduler configuration. **Authentication:** Required **Request Body:** ```json { "enabled": true, "interval_minutes": 30 } ``` **Response (200 OK):** Updated scheduler configuration Source: [src/server/api/scheduler.py](../src/server/api/scheduler.py#L45-L75) ### POST /api/scheduler/trigger-rescan Manually trigger a library rescan. **Authentication:** Required **Response (200 OK):** ```json { "success": true, "message": "Rescan started successfully" } ``` Source: [src/server/api/scheduler.py](../src/server/api/scheduler.py#L78-L122) --- ## 8. Health Check Endpoints Prefix: `/health` Source: [src/server/api/health.py](../src/server/api/health.py#L1-L267) ### GET /health Basic health check endpoint. **Authentication:** Not required **Response (200 OK):** ```json { "status": "healthy", "timestamp": "2025-12-13T10:30:00.000Z", "version": "1.0.0" } ``` Source: [src/server/api/health.py](../src/server/api/health.py#L151-L161) ### GET /health/detailed Comprehensive health check with database, filesystem, and system metrics. **Authentication:** Not required **Response (200 OK):** ```json { "status": "healthy", "timestamp": "2025-12-13T10:30:00.000Z", "version": "1.0.0", "dependencies": { "database": { "status": "healthy", "connection_time_ms": 1.5, "message": "Database connection successful" }, "filesystem": { "status": "healthy", "data_dir_writable": true, "logs_dir_writable": true }, "system": { "cpu_percent": 25.0, "memory_percent": 45.0, "memory_available_mb": 8192.0, "disk_percent": 60.0, "disk_free_mb": 102400.0, "uptime_seconds": 86400.0 } }, "startup_time": "2025-12-13T08:00:00.000Z" } ``` Source: [src/server/api/health.py](../src/server/api/health.py#L164-L200) --- ## 9. WebSocket Protocol Endpoint: `/ws/connect` Source: [src/server/api/websocket.py](../src/server/api/websocket.py#L1-L260) ### Connection **URL:** `ws://127.0.0.1:8000/ws/connect` **Query Parameters:** | Parameter | Required | Description | |-----------|----------|-------------| | `token` | No | JWT token for authenticated access | ### Message Types | Type | Direction | Description | | ------------------- | ---------------- | -------------------------- | | `connected` | Server -> Client | Connection confirmation | | `ping` | Client -> Server | Keepalive request | | `pong` | Server -> Client | Keepalive response | | `download_progress` | Server -> Client | Download progress update | | `download_complete` | Server -> Client | Download completed | | `download_failed` | Server -> Client | Download failed | | `download_added` | Server -> Client | Item added to queue | | `download_removed` | Server -> Client | Item removed from queue | | `queue_status` | Server -> Client | Queue status update | | `queue_started` | Server -> Client | Queue processing started | | `queue_stopped` | Server -> Client | Queue processing stopped | | `scan_progress` | Server -> Client | Library scan progress | | `scan_complete` | Server -> Client | Library scan completed | | `system_info` | Server -> Client | System information message | | `error` | Server -> Client | Error message | Source: [src/server/models/websocket.py](../src/server/models/websocket.py#L25-L57) ### Room Subscriptions Clients can join/leave rooms to receive specific updates. **Join Room:** ```json { "action": "join", "data": { "room": "downloads" } } ``` **Leave Room:** ```json { "action": "leave", "data": { "room": "downloads" } } ``` **Available Rooms:** - `downloads` - Download progress and status updates ### Server Message Format ```json { "type": "download_progress", "timestamp": "2025-12-13T10:30:00.000Z", "data": { "download_id": "uuid-here", "key": "attack-on-titan", "folder": "Attack on Titan (2013)", "percent": 45.2, "speed_mbps": 2.5, "eta_seconds": 180 } } ``` ### WebSocket Status Endpoint **GET /ws/status** Returns WebSocket service status. **Response (200 OK):** ```json { "status": "operational", "active_connections": 5, "supported_message_types": [ "download_progress", "download_complete", "download_failed", "queue_status", "connected", "ping", "pong", "error" ] } ``` Source: [src/server/api/websocket.py](../src/server/api/websocket.py#L238-L260) --- ## 10. Data Models ### Download Item ```json { "id": "uuid-string", "serie_id": "attack-on-titan", "serie_folder": "Attack on Titan (2013)", "serie_name": "Attack on Titan", "episode": { "season": 1, "episode": 1, "title": "To You, in 2000 Years" }, "status": "pending", "priority": "NORMAL", "added_at": "2025-12-13T10:00:00Z", "started_at": null, "completed_at": null, "progress": null, "error": null, "retry_count": 0, "source_url": null } ``` **Status Values:** `pending`, `downloading`, `paused`, `completed`, `failed`, `cancelled` **Priority Values:** `LOW`, `NORMAL`, `HIGH` Source: [src/server/models/download.py](../src/server/models/download.py#L63-L118) ### Episode Identifier ```json { "season": 1, "episode": 1, "title": "Episode Title" } ``` Source: [src/server/models/download.py](../src/server/models/download.py#L36-L41) ### Download Progress ```json { "percent": 45.2, "downloaded_mb": 256.0, "total_mb": 512.0, "speed_mbps": 2.5, "eta_seconds": 180 } ``` Source: [src/server/models/download.py](../src/server/models/download.py#L44-L60) --- ## 11. Error Handling ### HTTP Status Codes | Code | Meaning | When Used | | ---- | --------------------- | --------------------------------- | | 200 | OK | Successful request | | 201 | Created | Resource created | | 204 | No Content | Successful deletion | | 400 | Bad Request | Invalid request body/parameters | | 401 | Unauthorized | Missing or invalid authentication | | 403 | Forbidden | Insufficient permissions | | 404 | Not Found | Resource does not exist | | 422 | Unprocessable Entity | Validation error | | 429 | Too Many Requests | Rate limit exceeded | | 500 | Internal Server Error | Server-side error | ### Error Response Format ```json { "success": false, "error": "VALIDATION_ERROR", "message": "Human-readable error message", "details": { "field": "Additional context" }, "request_id": "uuid-for-tracking" } ``` Source: [src/server/middleware/error_handler.py](../src/server/middleware/error_handler.py#L26-L56) ### Common Error Codes | Error Code | HTTP Status | Description | | ---------------------- | ----------- | ------------------------------ | | `AUTHENTICATION_ERROR` | 401 | Invalid or missing credentials | | `AUTHORIZATION_ERROR` | 403 | Insufficient permissions | | `VALIDATION_ERROR` | 422 | Request validation failed | | `NOT_FOUND_ERROR` | 404 | Resource not found | | `CONFLICT_ERROR` | 409 | Resource conflict | | `RATE_LIMIT_ERROR` | 429 | Rate limit exceeded | --- ## 12. Rate Limiting ### Authentication Endpoints | Endpoint | Limit | Window | | ---------------------- | ---------- | ---------- | | `POST /api/auth/login` | 5 requests | 60 seconds | | `POST /api/auth/setup` | 5 requests | 60 seconds | Source: [src/server/middleware/auth.py](../src/server/middleware/auth.py#L143-L162) ### Origin-Based Limiting All endpoints from the same origin are limited to 60 requests per minute per origin. Source: [src/server/middleware/auth.py](../src/server/middleware/auth.py#L115-L133) ### Rate Limit Response ```json { "detail": "Too many authentication attempts, try again later" } ``` HTTP Status: 429 Too Many Requests --- ## 13. Pagination The anime list endpoint supports pagination. **Query Parameters:** | Parameter | Default | Max | Description | |-----------|---------|-----|-------------| | `page` | 1 | - | Page number (1-indexed) | | `per_page` | 20 | 1000 | Items per page | **Example:** ``` GET /api/anime?page=2&per_page=50 ``` Source: [src/server/api/anime.py](../src/server/api/anime.py#L180-L220)