# 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 term | **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": "" } ] ``` 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. 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) --- ## 7. 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) --- ## 8. 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) --- ## 9. 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) --- ## 10. 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 | --- ## 11. 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 --- ## 12. 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)