1195 lines
25 KiB
Markdown
1195 lines
25 KiB
Markdown
# 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 <jwt_token>
|
|
```
|
|
|
|
**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)
|