11 KiB
Aniworld Web Application Infrastructure
conda activate AniWorld
Project Structure
src/
├── core/ # Core application logic
│ ├── SeriesApp.py # Main application class
│ ├── SerieScanner.py # Directory scanner
│ ├── entities/ # Domain entities (series.py, SerieList.py)
│ ├── interfaces/ # Abstract interfaces (providers.py, callbacks.py)
│ ├── providers/ # Content providers (aniworld, streaming)
│ └── exceptions/ # Custom exceptions
├── server/ # FastAPI web application
│ ├── fastapi_app.py # Main FastAPI application
│ ├── controllers/ # Route controllers (health, page, error)
│ ├── api/ # API routes (auth, config, anime, download, websocket)
│ ├── models/ # Pydantic models
│ ├── services/ # Business logic services
│ ├── database/ # SQLAlchemy ORM layer
│ ├── utils/ # Utilities (dependencies, templates, security)
│ └── web/ # Frontend (templates, static assets)
├── cli/ # CLI application
data/ # Config, database, queue state
logs/ # Application logs
tests/ # Test suites
Technology Stack
| Layer | Technology |
|---|---|
| Backend | FastAPI, Uvicorn, SQLAlchemy, SQLite, Pydantic |
| Frontend | HTML5, CSS3, Vanilla JS, Bootstrap 5, HTMX |
| Security | JWT (python-jose), bcrypt (passlib) |
| Real-time | Native WebSocket |
Series Identifier Convention
Throughout the codebase, two identifiers are used for anime series:
key: Primary identifier (provider-assigned, URL-safe, e.g.,"attack-on-titan")folder: Display/filesystem metadata only (e.g.,"Attack on Titan (2013)")
All lookups, events, and API operations use key. The folder is metadata for display purposes.
API Endpoints
Authentication (/api/auth)
POST /login- Master password authentication (returns JWT)POST /logout- Invalidate sessionGET /status- Check authentication status
Configuration (/api/config)
GET /- Get configurationPUT /- Update configurationPOST /validate- Validate without applyingGET /backups- List backupsPOST /backups/{name}/restore- Restore backup
Anime (/api/anime)
GET /- List anime with missing episodes (returnskeyas identifier)GET /{anime_id}- Get anime details (acceptskeyorfolderfor backward compatibility)POST /search- Search for anime (returnskeyas identifier)POST /add- Add new series (extractskeyfrom link URL)POST /rescan- Trigger library rescan
Response Models:
AnimeSummary:key(primary identifier),name,site,folder(metadata),missing_episodes,linkAnimeDetail:key(primary identifier),title,folder(metadata),episodes,description
Download Queue (/api/queue)
GET /status- Queue status and statisticsPOST /add- Add episodes to queueDELETE /{item_id}- Remove itemPOST /start|/stop|/pause|/resume- Queue controlPOST /retry- Retry failed downloadsDELETE /completed- Clear completed items
Request Models:
DownloadRequest:serie_id(key, primary identifier),serie_folder(filesystem path),serie_name(display),episodes,priority
Response Models:
DownloadItem:id,serie_id(key),serie_folder(metadata),serie_name,episode,status,progressQueueStatus:is_running,is_paused,active_downloads,pending_queue,completed_downloads,failed_downloads
WebSocket (/ws/connect)
Real-time updates for downloads, scans, and queue operations.
Rooms: downloads, download_progress, scan_progress
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:
{
"type": "download_progress",
"timestamp": "2025-10-17T10:30:00.000Z",
"data": {
"download_id": "abc123",
"key": "attack-on-titan",
"folder": "Attack on Titan (2013)",
"percent": 45.2,
"speed_mbps": 2.5,
"eta_seconds": 180
}
}
Database Models
| Model | Purpose |
|---|---|
| AnimeSeries | Series metadata (key, name, folder, etc) |
| Episode | Episodes linked to series |
| DownloadQueueItem | Queue items with status and progress |
| UserSession | JWT sessions with expiry |
Mixins: TimestampMixin (created_at, updated_at), SoftDeleteMixin
AnimeSeries Identifier Fields
| Field | Type | Purpose |
|---|---|---|
id |
Primary Key | Internal database key for relationships |
key |
Unique, Indexed | PRIMARY IDENTIFIER for all lookups |
folder |
String | Filesystem metadata only (not for identification) |
Database Service Methods:
AnimeSeriesService.get_by_key(key)- Primary lookup methodAnimeSeriesService.get_by_id(id)- Internal lookup by database ID- No
get_by_folder()method exists - folder is never used for lookups
Core Services
SeriesApp (src/core/SeriesApp.py)
Main engine for anime series management with async support, progress callbacks, and cancellation.
Callback System (src/core/interfaces/callbacks.py)
ProgressCallback,ErrorCallback,CompletionCallback- Context classes include
key+ optionalfolderfields - Thread-safe
CallbackManagerfor multiple callback registration
Services (src/server/services/)
| Service | Purpose |
|---|---|
| AnimeService | Series management, scans (uses SeriesApp) |
| DownloadService | Queue management, download execution |
| ScanService | Library scan operations with callbacks |
| ProgressService | Centralized progress tracking + WebSocket |
| WebSocketService | Real-time connection management |
| AuthService | JWT authentication, rate limiting |
| ConfigService | Configuration persistence with backups |
Validation Utilities (src/server/utils/validators.py)
Provides data validation functions for ensuring data integrity across the application.
Series Key Validation
validate_series_key(key): Validates key format (URL-safe, lowercase, hyphens only)- Valid:
"attack-on-titan","one-piece","86-eighty-six" - Invalid:
"Attack On Titan","attack_on_titan","attack on titan"
- Valid:
validate_series_key_or_folder(identifier, allow_folder=True): Backward-compatible validation- Returns tuple
(identifier, is_key)whereis_keyindicates if it's a valid key format - Set
allow_folder=Falseto require strict key format
- Returns tuple
Other Validators
| Function | Purpose |
|---|---|
validate_series_name |
Series display name validation |
validate_episode_range |
Episode range validation (1-1000) |
validate_download_quality |
Quality setting (360p-1080p, best, worst) |
validate_language |
Language codes (ger-sub, ger-dub, etc.) |
validate_anime_url |
Aniworld.to/s.to URL validation |
validate_backup_name |
Backup filename validation |
validate_config_data |
Configuration data structure validation |
sanitize_filename |
Sanitize filenames for safe filesystem use |
Template Helpers (src/server/utils/template_helpers.py)
Provides utilities for template rendering and series data preparation.
Core Functions
| Function | Purpose |
|---|---|
get_base_context |
Base context for all templates |
render_template |
Render template with context |
validate_template_exists |
Check if template file exists |
list_available_templates |
List all available template files |
Series Context Helpers
All series helpers use key as the primary identifier:
| Function | Purpose |
|---|---|
prepare_series_context |
Prepare series data for templates (uses key) |
get_series_by_key |
Find series by key (not folder) |
filter_series_by_missing_episodes |
Filter series with missing episodes |
Example Usage:
from src.server.utils.template_helpers import prepare_series_context
series_data = [
{"key": "attack-on-titan", "name": "Attack on Titan", "folder": "Attack on Titan (2013)"},
{"key": "one-piece", "name": "One Piece", "folder": "One Piece (1999)"}
]
prepared = prepare_series_context(series_data, sort_by="name")
# Returns sorted list using 'key' as identifier
Frontend
Static Files
- CSS:
styles.css(Fluent UI design),ux_features.css(accessibility) - JS:
app.js,queue.js,websocket_client.js, accessibility modules
WebSocket Client
Native WebSocket wrapper with Socket.IO-compatible API:
const socket = io();
socket.join("download_progress");
socket.on("download_progress", (data) => {
/* ... */
});
Authentication
JWT tokens stored in localStorage, included as Authorization: Bearer <token>.
Testing
# All tests
conda run -n AniWorld python -m pytest tests/ -v
# Unit tests only
conda run -n AniWorld python -m pytest tests/unit/ -v
# API tests
conda run -n AniWorld python -m pytest tests/api/ -v
Production Notes
Current (Single-Process)
- SQLite with WAL mode
- In-memory WebSocket connections
- File-based config and queue persistence
Multi-Process Deployment
- Switch to PostgreSQL/MySQL
- Move WebSocket registry to Redis
- Use distributed locking for queue operations
- Consider Redis for session/cache storage