docu
This commit is contained in:
1176
docs/API.md
Normal file
1176
docs/API.md
Normal file
File diff suppressed because it is too large
Load Diff
452
docs/ARCHITECTURE.md
Normal file
452
docs/ARCHITECTURE.md
Normal file
@@ -0,0 +1,452 @@
|
||||
# Architecture Documentation
|
||||
|
||||
## Document Purpose
|
||||
|
||||
This document describes the system architecture of the Aniworld anime download manager.
|
||||
|
||||
---
|
||||
|
||||
## 1. System Overview
|
||||
|
||||
Aniworld is a web-based anime download manager built with Python, FastAPI, and SQLite. It provides a REST API and WebSocket interface for managing anime libraries, downloading episodes, and tracking progress.
|
||||
|
||||
### High-Level Architecture
|
||||
|
||||
```
|
||||
+------------------+ +------------------+ +------------------+
|
||||
| Web Browser | | CLI Client | | External |
|
||||
| (Frontend) | | (Main.py) | | Providers |
|
||||
+--------+---------+ +--------+---------+ +--------+---------+
|
||||
| | |
|
||||
| HTTP/WebSocket | Direct | HTTP
|
||||
| | |
|
||||
+--------v---------+ +--------v---------+ +--------v---------+
|
||||
| | | | | |
|
||||
| FastAPI <-----> Core Layer <-----> Provider |
|
||||
| Server Layer | | (SeriesApp) | | Adapters |
|
||||
| | | | | |
|
||||
+--------+---------+ +--------+---------+ +------------------+
|
||||
| |
|
||||
| |
|
||||
+--------v---------+ +--------v---------+
|
||||
| | | |
|
||||
| SQLite DB | | File System |
|
||||
| (aniworld.db) | | (data/*.json) |
|
||||
| | | |
|
||||
+------------------+ +------------------+
|
||||
```
|
||||
|
||||
Source: [src/server/fastapi_app.py](../src/server/fastapi_app.py#L1-L252)
|
||||
|
||||
---
|
||||
|
||||
## 2. Architectural Layers
|
||||
|
||||
### 2.1 CLI Layer (`src/cli/`)
|
||||
|
||||
Legacy command-line interface for direct interaction with the core layer.
|
||||
|
||||
| Component | File | Purpose |
|
||||
| --------- | ----------------------------- | --------------- |
|
||||
| Main | [Main.py](../src/cli/Main.py) | CLI entry point |
|
||||
|
||||
### 2.2 Server Layer (`src/server/`)
|
||||
|
||||
FastAPI-based REST API and WebSocket server.
|
||||
|
||||
```
|
||||
src/server/
|
||||
+-- fastapi_app.py # Application entry point, lifespan management
|
||||
+-- api/ # API route handlers
|
||||
| +-- anime.py # /api/anime/* endpoints
|
||||
| +-- auth.py # /api/auth/* endpoints
|
||||
| +-- config.py # /api/config/* endpoints
|
||||
| +-- download.py # /api/queue/* endpoints
|
||||
| +-- scheduler.py # /api/scheduler/* endpoints
|
||||
| +-- websocket.py # /ws/* WebSocket handlers
|
||||
| +-- health.py # /health/* endpoints
|
||||
+-- controllers/ # Page controllers for HTML rendering
|
||||
| +-- page_controller.py # UI page routes
|
||||
| +-- health_controller.py# Health check route
|
||||
| +-- error_controller.py # Error pages (404, 500)
|
||||
+-- services/ # Business logic
|
||||
| +-- anime_service.py # Anime operations
|
||||
| +-- auth_service.py # Authentication
|
||||
| +-- config_service.py # Configuration management
|
||||
| +-- download_service.py # Download queue management
|
||||
| +-- progress_service.py # Progress tracking
|
||||
| +-- websocket_service.py# WebSocket broadcasting
|
||||
| +-- queue_repository.py # Database persistence
|
||||
+-- models/ # Pydantic models
|
||||
| +-- auth.py # Auth request/response models
|
||||
| +-- config.py # Configuration models
|
||||
| +-- download.py # Download queue models
|
||||
| +-- websocket.py # WebSocket message models
|
||||
+-- middleware/ # Request processing
|
||||
| +-- auth.py # JWT validation, rate limiting
|
||||
| +-- error_handler.py # Exception handlers
|
||||
| +-- setup_redirect.py # Setup flow redirect
|
||||
+-- database/ # SQLAlchemy ORM
|
||||
| +-- connection.py # Database connection
|
||||
| +-- models.py # ORM models
|
||||
| +-- service.py # Database service
|
||||
+-- web/ # Static files and templates
|
||||
+-- static/ # CSS, JS, images
|
||||
+-- templates/ # Jinja2 templates
|
||||
```
|
||||
|
||||
Source: [src/server/](../src/server/)
|
||||
|
||||
### 2.3 Core Layer (`src/core/`)
|
||||
|
||||
Domain logic for anime series management.
|
||||
|
||||
```
|
||||
src/core/
|
||||
+-- SeriesApp.py # Main application facade
|
||||
+-- SerieScanner.py # Directory scanning
|
||||
+-- entities/ # Domain entities
|
||||
| +-- series.py # Serie class
|
||||
| +-- SerieList.py # SerieList collection
|
||||
+-- providers/ # External provider adapters
|
||||
| +-- base_provider.py # Loader interface
|
||||
| +-- provider_factory.py # Provider registry
|
||||
+-- interfaces/ # Abstract interfaces
|
||||
| +-- callbacks.py # Progress callback system
|
||||
+-- exceptions/ # Domain exceptions
|
||||
+-- Exceptions.py # Custom exceptions
|
||||
```
|
||||
|
||||
Source: [src/core/](../src/core/)
|
||||
|
||||
### 2.4 Infrastructure Layer (`src/infrastructure/`)
|
||||
|
||||
Cross-cutting concerns.
|
||||
|
||||
```
|
||||
src/infrastructure/
|
||||
+-- logging/ # Structured logging setup
|
||||
+-- security/ # Security utilities
|
||||
```
|
||||
|
||||
### 2.5 Configuration Layer (`src/config/`)
|
||||
|
||||
Application settings management.
|
||||
|
||||
| Component | File | Purpose |
|
||||
| --------- | ---------------------------------------- | ------------------------------- |
|
||||
| Settings | [settings.py](../src/config/settings.py) | Environment-based configuration |
|
||||
|
||||
Source: [src/config/settings.py](../src/config/settings.py#L1-L96)
|
||||
|
||||
---
|
||||
|
||||
## 3. Component Interactions
|
||||
|
||||
### 3.1 Request Flow (REST API)
|
||||
|
||||
```
|
||||
1. Client sends HTTP request
|
||||
2. AuthMiddleware validates JWT token (if required)
|
||||
3. Rate limiter checks request frequency
|
||||
4. FastAPI router dispatches to endpoint handler
|
||||
5. Endpoint calls service layer
|
||||
6. Service layer uses core layer or database
|
||||
7. Response returned as JSON
|
||||
```
|
||||
|
||||
Source: [src/server/middleware/auth.py](../src/server/middleware/auth.py#L1-L209)
|
||||
|
||||
### 3.2 Download Flow
|
||||
|
||||
```
|
||||
1. POST /api/queue/add
|
||||
+-- DownloadService.add_to_queue()
|
||||
+-- QueueRepository.save_item() -> SQLite
|
||||
|
||||
2. POST /api/queue/start
|
||||
+-- DownloadService.start_queue_processing()
|
||||
+-- Process pending items sequentially
|
||||
+-- ProgressService emits events
|
||||
+-- WebSocketService broadcasts to clients
|
||||
|
||||
3. During download:
|
||||
+-- ProgressService.emit("progress_updated")
|
||||
+-- WebSocketService.broadcast_to_room()
|
||||
+-- Client receives WebSocket message
|
||||
```
|
||||
|
||||
Source: [src/server/services/download_service.py](../src/server/services/download_service.py#L1-L150)
|
||||
|
||||
### 3.3 WebSocket Event Flow
|
||||
|
||||
```
|
||||
1. Client connects to /ws/connect
|
||||
2. Server sends "connected" message
|
||||
3. Client joins room: {"action": "join", "data": {"room": "downloads"}}
|
||||
4. ProgressService emits events
|
||||
5. WebSocketService broadcasts to room subscribers
|
||||
6. Client receives real-time updates
|
||||
```
|
||||
|
||||
Source: [src/server/api/websocket.py](../src/server/api/websocket.py#L1-L260)
|
||||
|
||||
---
|
||||
|
||||
## 4. Design Patterns
|
||||
|
||||
### 4.1 Repository Pattern
|
||||
|
||||
Database access is abstracted through repository classes.
|
||||
|
||||
```python
|
||||
# QueueRepository provides CRUD for download items
|
||||
class QueueRepository:
|
||||
async def save_item(self, item: DownloadItem) -> None: ...
|
||||
async def get_all_items(self) -> List[DownloadItem]: ...
|
||||
async def delete_item(self, item_id: str) -> bool: ...
|
||||
```
|
||||
|
||||
Source: [src/server/services/queue_repository.py](../src/server/services/queue_repository.py)
|
||||
|
||||
### 4.2 Dependency Injection
|
||||
|
||||
FastAPI's `Depends()` provides constructor injection.
|
||||
|
||||
```python
|
||||
@router.get("/status")
|
||||
async def get_status(
|
||||
download_service: DownloadService = Depends(get_download_service),
|
||||
):
|
||||
...
|
||||
```
|
||||
|
||||
Source: [src/server/utils/dependencies.py](../src/server/utils/dependencies.py)
|
||||
|
||||
### 4.3 Event-Driven Architecture
|
||||
|
||||
Progress updates use an event subscription model.
|
||||
|
||||
```python
|
||||
# ProgressService publishes events
|
||||
progress_service.emit("progress_updated", event)
|
||||
|
||||
# WebSocketService subscribes
|
||||
progress_service.subscribe("progress_updated", ws_handler)
|
||||
```
|
||||
|
||||
Source: [src/server/fastapi_app.py](../src/server/fastapi_app.py#L98-L108)
|
||||
|
||||
### 4.4 Singleton Pattern
|
||||
|
||||
Services use module-level singletons for shared state.
|
||||
|
||||
```python
|
||||
# In download_service.py
|
||||
_download_service_instance: Optional[DownloadService] = None
|
||||
|
||||
def get_download_service() -> DownloadService:
|
||||
global _download_service_instance
|
||||
if _download_service_instance is None:
|
||||
_download_service_instance = DownloadService(...)
|
||||
return _download_service_instance
|
||||
```
|
||||
|
||||
Source: [src/server/services/download_service.py](../src/server/services/download_service.py)
|
||||
|
||||
---
|
||||
|
||||
## 5. Data Flow
|
||||
|
||||
### 5.1 Series Identifier Convention
|
||||
|
||||
The system uses two identifier fields:
|
||||
|
||||
| Field | Type | Purpose | Example |
|
||||
| -------- | -------- | -------------------------------------- | -------------------------- |
|
||||
| `key` | Primary | Provider-assigned, URL-safe identifier | `"attack-on-titan"` |
|
||||
| `folder` | Metadata | Filesystem folder name (display only) | `"Attack on Titan (2013)"` |
|
||||
|
||||
All API operations use `key`. The `folder` is for filesystem operations only.
|
||||
|
||||
Source: [src/server/database/models.py](../src/server/database/models.py#L26-L50)
|
||||
|
||||
### 5.2 Database Schema
|
||||
|
||||
```
|
||||
+----------------+ +----------------+ +--------------------+
|
||||
| anime_series | | episodes | | download_queue_item|
|
||||
+----------------+ +----------------+ +--------------------+
|
||||
| id (PK) |<--+ | id (PK) | +-->| id (PK) |
|
||||
| key (unique) | | | series_id (FK) |---+ | series_id (FK) |
|
||||
| name | +---| season | | status |
|
||||
| site | | episode_number | | priority |
|
||||
| folder | | title | | progress_percent |
|
||||
| created_at | | is_downloaded | | added_at |
|
||||
| updated_at | | file_path | | started_at |
|
||||
+----------------+ +----------------+ +--------------------+
|
||||
```
|
||||
|
||||
Source: [src/server/database/models.py](../src/server/database/models.py#L1-L200)
|
||||
|
||||
### 5.3 Configuration Storage
|
||||
|
||||
Configuration is stored in `data/config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Aniworld",
|
||||
"data_dir": "data",
|
||||
"scheduler": { "enabled": true, "interval_minutes": 60 },
|
||||
"logging": { "level": "INFO" },
|
||||
"backup": { "enabled": false, "path": "data/backups" },
|
||||
"other": {
|
||||
"master_password_hash": "$pbkdf2-sha256$...",
|
||||
"anime_directory": "/path/to/anime"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Source: [data/config.json](../data/config.json)
|
||||
|
||||
---
|
||||
|
||||
## 6. Technology Stack
|
||||
|
||||
| Layer | Technology | Version | Purpose |
|
||||
| ------------- | ------------------- | ------- | ---------------------- |
|
||||
| Web Framework | FastAPI | 0.104.1 | REST API, WebSocket |
|
||||
| ASGI Server | Uvicorn | 0.24.0 | HTTP server |
|
||||
| Database | SQLite + SQLAlchemy | 2.0.35 | Persistence |
|
||||
| Auth | python-jose | 3.3.0 | JWT tokens |
|
||||
| Password | passlib | 1.7.4 | bcrypt hashing |
|
||||
| Validation | Pydantic | 2.5.0 | Data models |
|
||||
| Templates | Jinja2 | 3.1.2 | HTML rendering |
|
||||
| Logging | structlog | 24.1.0 | Structured logging |
|
||||
| Testing | pytest | 7.4.3 | Unit/integration tests |
|
||||
|
||||
Source: [requirements.txt](../requirements.txt)
|
||||
|
||||
---
|
||||
|
||||
## 7. Scalability Considerations
|
||||
|
||||
### Current Limitations
|
||||
|
||||
1. **Single-process deployment**: In-memory rate limiting and session state are not shared across processes.
|
||||
|
||||
2. **SQLite database**: Not suitable for high concurrency. Consider PostgreSQL for production.
|
||||
|
||||
3. **Sequential downloads**: Only one download processes at a time by design.
|
||||
|
||||
### Recommended Improvements for Scale
|
||||
|
||||
| Concern | Current | Recommended |
|
||||
| -------------- | --------------- | ----------------- |
|
||||
| Rate limiting | In-memory dict | Redis |
|
||||
| Session store | In-memory | Redis or database |
|
||||
| Database | SQLite | PostgreSQL |
|
||||
| Task queue | In-memory deque | Celery + Redis |
|
||||
| Load balancing | None | Nginx/HAProxy |
|
||||
|
||||
---
|
||||
|
||||
## 8. Integration Points
|
||||
|
||||
### 8.1 External Providers
|
||||
|
||||
The system integrates with anime streaming providers via the Loader interface.
|
||||
|
||||
```python
|
||||
class Loader(ABC):
|
||||
@abstractmethod
|
||||
def search(self, query: str) -> List[Serie]: ...
|
||||
|
||||
@abstractmethod
|
||||
def get_episodes(self, serie: Serie) -> Dict[int, List[int]]: ...
|
||||
```
|
||||
|
||||
Source: [src/core/providers/base_provider.py](../src/core/providers/base_provider.py)
|
||||
|
||||
### 8.2 Filesystem Integration
|
||||
|
||||
The scanner reads anime directories to detect downloaded episodes.
|
||||
|
||||
```python
|
||||
SerieScanner(
|
||||
basePath="/path/to/anime", # Anime library directory
|
||||
loader=provider, # Provider for metadata
|
||||
db_session=session # Optional database
|
||||
)
|
||||
```
|
||||
|
||||
Source: [src/core/SerieScanner.py](../src/core/SerieScanner.py#L59-L96)
|
||||
|
||||
---
|
||||
|
||||
## 9. Security Architecture
|
||||
|
||||
### 9.1 Authentication Flow
|
||||
|
||||
```
|
||||
1. User sets master password via POST /api/auth/setup
|
||||
2. Password hashed with pbkdf2_sha256 (via passlib)
|
||||
3. Hash stored in config.json
|
||||
4. Login validates password, returns JWT token
|
||||
5. JWT contains: session_id, user, created_at, expires_at
|
||||
6. Subsequent requests include: Authorization: Bearer <token>
|
||||
```
|
||||
|
||||
Source: [src/server/services/auth_service.py](../src/server/services/auth_service.py#L1-L150)
|
||||
|
||||
### 9.2 Password Requirements
|
||||
|
||||
- Minimum 8 characters
|
||||
- Mixed case (upper and lower)
|
||||
- At least one number
|
||||
- At least one special character
|
||||
|
||||
Source: [src/server/services/auth_service.py](../src/server/services/auth_service.py#L97-L125)
|
||||
|
||||
### 9.3 Rate Limiting
|
||||
|
||||
| Endpoint | Limit | Window |
|
||||
| ----------------- | ----------- | ---------- |
|
||||
| `/api/auth/login` | 5 requests | 60 seconds |
|
||||
| `/api/auth/setup` | 5 requests | 60 seconds |
|
||||
| All origins | 60 requests | 60 seconds |
|
||||
|
||||
Source: [src/server/middleware/auth.py](../src/server/middleware/auth.py#L54-L68)
|
||||
|
||||
---
|
||||
|
||||
## 10. Deployment Modes
|
||||
|
||||
### 10.1 Development
|
||||
|
||||
```bash
|
||||
# Run with hot reload
|
||||
python -m uvicorn src.server.fastapi_app:app --reload
|
||||
```
|
||||
|
||||
### 10.2 Production
|
||||
|
||||
```bash
|
||||
# Via conda environment
|
||||
conda run -n AniWorld python -m uvicorn src.server.fastapi_app:app \
|
||||
--host 127.0.0.1 --port 8000
|
||||
```
|
||||
|
||||
### 10.3 Configuration
|
||||
|
||||
Environment variables (via `.env` or shell):
|
||||
|
||||
| Variable | Default | Description |
|
||||
| ----------------- | ------------------------------ | ---------------------- |
|
||||
| `JWT_SECRET_KEY` | Random | Secret for JWT signing |
|
||||
| `DATABASE_URL` | `sqlite:///./data/aniworld.db` | Database connection |
|
||||
| `ANIME_DIRECTORY` | (empty) | Path to anime library |
|
||||
| `LOG_LEVEL` | `INFO` | Logging level |
|
||||
| `CORS_ORIGINS` | `localhost:3000,8000` | Allowed CORS origins |
|
||||
|
||||
Source: [src/config/settings.py](../src/config/settings.py#L1-L96)
|
||||
78
docs/CHANGELOG.md
Normal file
78
docs/CHANGELOG.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# Changelog
|
||||
|
||||
## Document Purpose
|
||||
|
||||
This document tracks all notable changes to the Aniworld project.
|
||||
|
||||
### What This Document Contains
|
||||
|
||||
- **Version History**: All released versions with dates
|
||||
- **Added Features**: New functionality in each release
|
||||
- **Changed Features**: Modifications to existing features
|
||||
- **Deprecated Features**: Features marked for removal
|
||||
- **Removed Features**: Features removed from the codebase
|
||||
- **Fixed Bugs**: Bug fixes with issue references
|
||||
- **Security Fixes**: Security-related changes
|
||||
- **Breaking Changes**: Changes requiring user action
|
||||
|
||||
### What This Document Does NOT Contain
|
||||
|
||||
- Internal refactoring details (unless user-facing)
|
||||
- Commit-level changes
|
||||
- Work-in-progress features
|
||||
- Roadmap or planned features
|
||||
|
||||
### Target Audience
|
||||
|
||||
- All users and stakeholders
|
||||
- Operators planning upgrades
|
||||
- Developers tracking changes
|
||||
- Support personnel
|
||||
|
||||
---
|
||||
|
||||
## Format
|
||||
|
||||
This changelog follows [Keep a Changelog](https://keepachangelog.com/) principles and adheres to [Semantic Versioning](https://semver.org/).
|
||||
|
||||
## Sections for Each Release
|
||||
|
||||
```markdown
|
||||
## [Version] - YYYY-MM-DD
|
||||
|
||||
### Added
|
||||
|
||||
- New features
|
||||
|
||||
### Changed
|
||||
|
||||
- Changes to existing functionality
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Features that will be removed in future versions
|
||||
|
||||
### Removed
|
||||
|
||||
- Features removed in this release
|
||||
|
||||
### Fixed
|
||||
|
||||
- Bug fixes
|
||||
|
||||
### Security
|
||||
|
||||
- Security-related fixes
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Unreleased
|
||||
|
||||
_Changes that are in development but not yet released._
|
||||
|
||||
---
|
||||
|
||||
## Version History
|
||||
|
||||
_To be documented as versions are released._
|
||||
298
docs/CONFIGURATION.md
Normal file
298
docs/CONFIGURATION.md
Normal file
@@ -0,0 +1,298 @@
|
||||
# Configuration Reference
|
||||
|
||||
## Document Purpose
|
||||
|
||||
This document provides a comprehensive reference for all configuration options in the Aniworld application.
|
||||
|
||||
---
|
||||
|
||||
## 1. Configuration Overview
|
||||
|
||||
### Configuration Sources
|
||||
|
||||
Aniworld uses a layered configuration system:
|
||||
|
||||
1. **Environment Variables** (highest priority)
|
||||
2. **`.env` file** in project root
|
||||
3. **`data/config.json`** file
|
||||
4. **Default values** (lowest priority)
|
||||
|
||||
### Loading Mechanism
|
||||
|
||||
Configuration is loaded at application startup via Pydantic Settings.
|
||||
|
||||
```python
|
||||
# src/config/settings.py
|
||||
class Settings(BaseSettings):
|
||||
model_config = SettingsConfigDict(env_file=".env", extra="ignore")
|
||||
```
|
||||
|
||||
Source: [src/config/settings.py](../src/config/settings.py#L1-L96)
|
||||
|
||||
---
|
||||
|
||||
## 2. Environment Variables
|
||||
|
||||
### Authentication Settings
|
||||
|
||||
| Variable | Type | Default | Description |
|
||||
| ----------------------- | ------ | ---------------- | ------------------------------------------------------------------- |
|
||||
| `JWT_SECRET_KEY` | string | (random) | Secret key for JWT token signing. Auto-generated if not set. |
|
||||
| `PASSWORD_SALT` | string | `"default-salt"` | Salt for password hashing. |
|
||||
| `MASTER_PASSWORD_HASH` | string | (none) | Pre-hashed master password. Loaded from config.json if not set. |
|
||||
| `MASTER_PASSWORD` | string | (none) | **DEVELOPMENT ONLY** - Plaintext password. Never use in production. |
|
||||
| `SESSION_TIMEOUT_HOURS` | int | `24` | JWT token expiry time in hours. |
|
||||
|
||||
Source: [src/config/settings.py](../src/config/settings.py#L13-L42)
|
||||
|
||||
### Server Settings
|
||||
|
||||
| Variable | Type | Default | Description |
|
||||
| ----------------- | ------ | -------------------------------- | --------------------------------------------------------------------- |
|
||||
| `ANIME_DIRECTORY` | string | `""` | Path to anime library directory. |
|
||||
| `LOG_LEVEL` | string | `"INFO"` | Logging level: DEBUG, INFO, WARNING, ERROR, CRITICAL. |
|
||||
| `DATABASE_URL` | string | `"sqlite:///./data/aniworld.db"` | Database connection string. |
|
||||
| `CORS_ORIGINS` | string | `"http://localhost:3000"` | Comma-separated allowed CORS origins. Use `*` for localhost defaults. |
|
||||
| `API_RATE_LIMIT` | int | `100` | Maximum API requests per minute. |
|
||||
|
||||
Source: [src/config/settings.py](../src/config/settings.py#L43-L68)
|
||||
|
||||
### Provider Settings
|
||||
|
||||
| Variable | Type | Default | Description |
|
||||
| ------------------ | ------ | --------------- | --------------------------------------------- |
|
||||
| `DEFAULT_PROVIDER` | string | `"aniworld.to"` | Default anime provider. |
|
||||
| `PROVIDER_TIMEOUT` | int | `30` | HTTP timeout for provider requests (seconds). |
|
||||
| `RETRY_ATTEMPTS` | int | `3` | Number of retry attempts for failed requests. |
|
||||
|
||||
Source: [src/config/settings.py](../src/config/settings.py#L69-L79)
|
||||
|
||||
---
|
||||
|
||||
## 3. Configuration File (config.json)
|
||||
|
||||
Location: `data/config.json`
|
||||
|
||||
### File Structure
|
||||
|
||||
```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": {
|
||||
"master_password_hash": "$pbkdf2-sha256$...",
|
||||
"anime_directory": "/path/to/anime"
|
||||
},
|
||||
"version": "1.0.0"
|
||||
}
|
||||
```
|
||||
|
||||
Source: [data/config.json](../data/config.json)
|
||||
|
||||
---
|
||||
|
||||
## 4. Configuration Sections
|
||||
|
||||
### 4.1 General Settings
|
||||
|
||||
| Field | Type | Default | Description |
|
||||
| ---------- | ------ | ------------ | ------------------------------ |
|
||||
| `name` | string | `"Aniworld"` | Application name. |
|
||||
| `data_dir` | string | `"data"` | Base directory for data files. |
|
||||
|
||||
Source: [src/server/models/config.py](../src/server/models/config.py#L62-L66)
|
||||
|
||||
### 4.2 Scheduler Settings
|
||||
|
||||
Controls automatic library rescanning.
|
||||
|
||||
| Field | Type | Default | Description |
|
||||
| ---------------------------- | ---- | ------- | -------------------------------------------- |
|
||||
| `scheduler.enabled` | bool | `true` | Enable/disable automatic scans. |
|
||||
| `scheduler.interval_minutes` | int | `60` | Minutes between automatic scans. Minimum: 1. |
|
||||
|
||||
Source: [src/server/models/config.py](../src/server/models/config.py#L5-L12)
|
||||
|
||||
### 4.3 Logging Settings
|
||||
|
||||
| Field | Type | Default | Description |
|
||||
| ---------------------- | ------ | -------- | ------------------------------------------------- |
|
||||
| `logging.level` | string | `"INFO"` | Log level: DEBUG, INFO, WARNING, ERROR, CRITICAL. |
|
||||
| `logging.file` | string | `null` | Optional log file path. |
|
||||
| `logging.max_bytes` | int | `null` | Maximum log file size for rotation. |
|
||||
| `logging.backup_count` | int | `3` | Number of rotated log files to keep. |
|
||||
|
||||
Source: [src/server/models/config.py](../src/server/models/config.py#L27-L46)
|
||||
|
||||
### 4.4 Backup Settings
|
||||
|
||||
| Field | Type | Default | Description |
|
||||
| ------------------ | ------ | ---------------- | -------------------------------- |
|
||||
| `backup.enabled` | bool | `false` | Enable automatic config backups. |
|
||||
| `backup.path` | string | `"data/backups"` | Directory for backup files. |
|
||||
| `backup.keep_days` | int | `30` | Days to retain backups. |
|
||||
|
||||
Source: [src/server/models/config.py](../src/server/models/config.py#L15-L24)
|
||||
|
||||
### 4.5 Other Settings (Dynamic)
|
||||
|
||||
The `other` field stores arbitrary settings.
|
||||
|
||||
| Key | Type | Description |
|
||||
| ---------------------- | ------ | --------------------------------------- |
|
||||
| `master_password_hash` | string | Hashed master password (pbkdf2-sha256). |
|
||||
| `anime_directory` | string | Path to anime library. |
|
||||
| `advanced` | object | Advanced configuration options. |
|
||||
|
||||
---
|
||||
|
||||
## 5. Configuration Precedence
|
||||
|
||||
Settings are resolved in this order (first match wins):
|
||||
|
||||
1. Environment variable (e.g., `ANIME_DIRECTORY`)
|
||||
2. `.env` file in project root
|
||||
3. `data/config.json` (for dynamic settings)
|
||||
4. Code defaults in `Settings` class
|
||||
|
||||
---
|
||||
|
||||
## 6. Validation Rules
|
||||
|
||||
### Password Requirements
|
||||
|
||||
Master password must meet all criteria:
|
||||
|
||||
- Minimum 8 characters
|
||||
- At least one uppercase letter
|
||||
- At least one lowercase letter
|
||||
- At least one digit
|
||||
- At least one special character
|
||||
|
||||
Source: [src/server/services/auth_service.py](../src/server/services/auth_service.py#L97-L125)
|
||||
|
||||
### Logging Level Validation
|
||||
|
||||
Must be one of: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`
|
||||
|
||||
Source: [src/server/models/config.py](../src/server/models/config.py#L43-L47)
|
||||
|
||||
### Backup Path Validation
|
||||
|
||||
If `backup.enabled` is `true`, `backup.path` must be set.
|
||||
|
||||
Source: [src/server/models/config.py](../src/server/models/config.py#L87-L91)
|
||||
|
||||
---
|
||||
|
||||
## 7. Example Configurations
|
||||
|
||||
### Minimal Development Setup
|
||||
|
||||
**.env file:**
|
||||
|
||||
```
|
||||
LOG_LEVEL=DEBUG
|
||||
ANIME_DIRECTORY=/home/user/anime
|
||||
```
|
||||
|
||||
### Production Setup
|
||||
|
||||
**.env file:**
|
||||
|
||||
```
|
||||
JWT_SECRET_KEY=your-secure-random-key-here
|
||||
DATABASE_URL=postgresql+asyncpg://user:pass@localhost/aniworld
|
||||
LOG_LEVEL=WARNING
|
||||
CORS_ORIGINS=https://your-domain.com
|
||||
API_RATE_LIMIT=60
|
||||
```
|
||||
|
||||
### Docker Setup
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml
|
||||
environment:
|
||||
- JWT_SECRET_KEY=${JWT_SECRET_KEY}
|
||||
- DATABASE_URL=sqlite:///./data/aniworld.db
|
||||
- ANIME_DIRECTORY=/media/anime
|
||||
- LOG_LEVEL=INFO
|
||||
volumes:
|
||||
- ./data:/app/data
|
||||
- /media/anime:/media/anime:ro
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Configuration Backup Management
|
||||
|
||||
### Automatic Backups
|
||||
|
||||
Backups are created automatically before config changes when `backup.enabled` is `true`.
|
||||
|
||||
Location: `data/config_backups/`
|
||||
|
||||
Naming: `config_backup_YYYYMMDD_HHMMSS.json`
|
||||
|
||||
### Manual Backup via API
|
||||
|
||||
```bash
|
||||
# Create backup
|
||||
curl -X POST http://localhost:8000/api/config/backups \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
|
||||
# List backups
|
||||
curl http://localhost:8000/api/config/backups \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
|
||||
# Restore backup
|
||||
curl -X POST http://localhost:8000/api/config/backups/config_backup_20251213.json/restore \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
Source: [src/server/api/config.py](../src/server/api/config.py#L67-L142)
|
||||
|
||||
---
|
||||
|
||||
## 9. Troubleshooting
|
||||
|
||||
### Configuration Not Loading
|
||||
|
||||
1. Check file permissions on `data/config.json`
|
||||
2. Verify JSON syntax with a validator
|
||||
3. Check logs for Pydantic validation errors
|
||||
|
||||
### Environment Variable Not Working
|
||||
|
||||
1. Ensure variable name matches exactly (case-sensitive)
|
||||
2. Check `.env` file location (project root)
|
||||
3. Restart application after changes
|
||||
|
||||
### Master Password Issues
|
||||
|
||||
1. Password hash is stored in `config.json` under `other.master_password_hash`
|
||||
2. Delete this field to reset (requires re-setup)
|
||||
3. Check hash format starts with `$pbkdf2-sha256$`
|
||||
|
||||
---
|
||||
|
||||
## 10. Related Documentation
|
||||
|
||||
- [API.md](API.md) - Configuration API endpoints
|
||||
- [DEVELOPMENT.md](DEVELOPMENT.md) - Development environment setup
|
||||
- [ARCHITECTURE.md](ARCHITECTURE.md) - Configuration service architecture
|
||||
326
docs/DATABASE.md
Normal file
326
docs/DATABASE.md
Normal file
@@ -0,0 +1,326 @@
|
||||
# Database Documentation
|
||||
|
||||
## Document Purpose
|
||||
|
||||
This document describes the database schema, models, and data layer of the Aniworld application.
|
||||
|
||||
---
|
||||
|
||||
## 1. Database Overview
|
||||
|
||||
### Technology
|
||||
|
||||
- **Database Engine**: SQLite 3 (default), PostgreSQL supported
|
||||
- **ORM**: SQLAlchemy 2.0 with async support (aiosqlite)
|
||||
- **Location**: `data/aniworld.db` (configurable via `DATABASE_URL`)
|
||||
|
||||
Source: [src/config/settings.py](../src/config/settings.py#L53-L55)
|
||||
|
||||
### Connection Configuration
|
||||
|
||||
```python
|
||||
# Default connection string
|
||||
DATABASE_URL = "sqlite+aiosqlite:///./data/aniworld.db"
|
||||
|
||||
# PostgreSQL alternative
|
||||
DATABASE_URL = "postgresql+asyncpg://user:pass@localhost/aniworld"
|
||||
```
|
||||
|
||||
Source: [src/server/database/connection.py](../src/server/database/connection.py)
|
||||
|
||||
---
|
||||
|
||||
## 2. Entity Relationship Diagram
|
||||
|
||||
```
|
||||
+-------------------+ +-------------------+ +------------------------+
|
||||
| anime_series | | episodes | | download_queue_item |
|
||||
+-------------------+ +-------------------+ +------------------------+
|
||||
| id (PK) |<--+ | id (PK) | +-->| id (PK, VARCHAR) |
|
||||
| key (UNIQUE) | | | series_id (FK)----+---+ | series_id (FK)---------+
|
||||
| name | +---| | | status |
|
||||
| site | | season | | priority |
|
||||
| folder | | episode_number | | season |
|
||||
| created_at | | title | | episode |
|
||||
| updated_at | | file_path | | progress_percent |
|
||||
+-------------------+ | is_downloaded | | error_message |
|
||||
| created_at | | retry_count |
|
||||
| updated_at | | added_at |
|
||||
+-------------------+ | started_at |
|
||||
| completed_at |
|
||||
| created_at |
|
||||
| updated_at |
|
||||
+------------------------+
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Table Schemas
|
||||
|
||||
### 3.1 anime_series
|
||||
|
||||
Stores anime series metadata.
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
| ------------ | ------------- | -------------------------- | ------------------------------------------------------- |
|
||||
| `id` | INTEGER | PRIMARY KEY, AUTOINCREMENT | Internal database ID |
|
||||
| `key` | VARCHAR(255) | UNIQUE, NOT NULL, INDEX | **Primary identifier** - provider-assigned URL-safe key |
|
||||
| `name` | VARCHAR(500) | NOT NULL, INDEX | Display name of the series |
|
||||
| `site` | VARCHAR(500) | NOT NULL | Provider site URL |
|
||||
| `folder` | VARCHAR(1000) | NOT NULL | Filesystem folder name (metadata only) |
|
||||
| `created_at` | DATETIME | NOT NULL, DEFAULT NOW | Record creation timestamp |
|
||||
| `updated_at` | DATETIME | NOT NULL, ON UPDATE NOW | Last update timestamp |
|
||||
|
||||
**Identifier Convention:**
|
||||
|
||||
- `key` is the **primary identifier** for all operations (e.g., `"attack-on-titan"`)
|
||||
- `folder` is **metadata only** for filesystem operations (e.g., `"Attack on Titan (2013)"`)
|
||||
- `id` is used only for database relationships
|
||||
|
||||
Source: [src/server/database/models.py](../src/server/database/models.py#L23-L87)
|
||||
|
||||
### 3.2 episodes
|
||||
|
||||
Stores individual episode information.
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
| ---------------- | ------------- | ---------------------------- | ----------------------------- |
|
||||
| `id` | INTEGER | PRIMARY KEY, AUTOINCREMENT | Internal database ID |
|
||||
| `series_id` | INTEGER | FOREIGN KEY, NOT NULL, INDEX | Reference to anime_series.id |
|
||||
| `season` | INTEGER | NOT NULL | Season number (1-based) |
|
||||
| `episode_number` | INTEGER | NOT NULL | Episode number within season |
|
||||
| `title` | VARCHAR(500) | NULLABLE | Episode title if known |
|
||||
| `file_path` | VARCHAR(1000) | NULLABLE | Local file path if downloaded |
|
||||
| `is_downloaded` | BOOLEAN | NOT NULL, DEFAULT FALSE | Download status flag |
|
||||
| `created_at` | DATETIME | NOT NULL, DEFAULT NOW | Record creation timestamp |
|
||||
| `updated_at` | DATETIME | NOT NULL, ON UPDATE NOW | Last update timestamp |
|
||||
|
||||
**Foreign Key:**
|
||||
|
||||
- `series_id` -> `anime_series.id` (ON DELETE CASCADE)
|
||||
|
||||
Source: [src/server/database/models.py](../src/server/database/models.py#L122-L181)
|
||||
|
||||
### 3.3 download_queue_item
|
||||
|
||||
Stores download queue items with status tracking.
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
| ------------------ | ------------- | --------------------------- | ------------------------------ |
|
||||
| `id` | VARCHAR(36) | PRIMARY KEY | UUID identifier |
|
||||
| `series_id` | INTEGER | FOREIGN KEY, NOT NULL | Reference to anime_series.id |
|
||||
| `season` | INTEGER | NOT NULL | Season number |
|
||||
| `episode` | INTEGER | NOT NULL | Episode number |
|
||||
| `status` | VARCHAR(20) | NOT NULL, DEFAULT 'pending' | Download status |
|
||||
| `priority` | VARCHAR(10) | NOT NULL, DEFAULT 'NORMAL' | Queue priority |
|
||||
| `progress_percent` | FLOAT | NULLABLE | Download progress (0-100) |
|
||||
| `error_message` | TEXT | NULLABLE | Error description if failed |
|
||||
| `retry_count` | INTEGER | NOT NULL, DEFAULT 0 | Number of retry attempts |
|
||||
| `source_url` | VARCHAR(2000) | NULLABLE | Download source URL |
|
||||
| `added_at` | DATETIME | NOT NULL, DEFAULT NOW | When added to queue |
|
||||
| `started_at` | DATETIME | NULLABLE | When download started |
|
||||
| `completed_at` | DATETIME | NULLABLE | When download completed/failed |
|
||||
| `created_at` | DATETIME | NOT NULL, DEFAULT NOW | Record creation timestamp |
|
||||
| `updated_at` | DATETIME | NOT NULL, ON UPDATE NOW | Last update timestamp |
|
||||
|
||||
**Status Values:** `pending`, `downloading`, `paused`, `completed`, `failed`, `cancelled`
|
||||
|
||||
**Priority Values:** `LOW`, `NORMAL`, `HIGH`
|
||||
|
||||
**Foreign Key:**
|
||||
|
||||
- `series_id` -> `anime_series.id` (ON DELETE CASCADE)
|
||||
|
||||
Source: [src/server/database/models.py](../src/server/database/models.py#L200-L300)
|
||||
|
||||
---
|
||||
|
||||
## 4. Indexes
|
||||
|
||||
| Table | Index Name | Columns | Purpose |
|
||||
| --------------------- | ----------------------- | ----------- | --------------------------------- |
|
||||
| `anime_series` | `ix_anime_series_key` | `key` | Fast lookup by primary identifier |
|
||||
| `anime_series` | `ix_anime_series_name` | `name` | Search by name |
|
||||
| `episodes` | `ix_episodes_series_id` | `series_id` | Join with series |
|
||||
| `download_queue_item` | `ix_download_series_id` | `series_id` | Filter by series |
|
||||
| `download_queue_item` | `ix_download_status` | `status` | Filter by status |
|
||||
|
||||
---
|
||||
|
||||
## 5. Model Layer
|
||||
|
||||
### 5.1 SQLAlchemy ORM Models
|
||||
|
||||
```python
|
||||
# src/server/database/models.py
|
||||
|
||||
class AnimeSeries(Base, TimestampMixin):
|
||||
__tablename__ = "anime_series"
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||
key: Mapped[str] = mapped_column(String(255), unique=True, index=True)
|
||||
name: Mapped[str] = mapped_column(String(500), index=True)
|
||||
site: Mapped[str] = mapped_column(String(500))
|
||||
folder: Mapped[str] = mapped_column(String(1000))
|
||||
|
||||
episodes: Mapped[List["Episode"]] = relationship(
|
||||
"Episode", back_populates="series", cascade="all, delete-orphan"
|
||||
)
|
||||
```
|
||||
|
||||
Source: [src/server/database/models.py](../src/server/database/models.py#L23-L87)
|
||||
|
||||
### 5.2 Pydantic API Models
|
||||
|
||||
```python
|
||||
# src/server/models/download.py
|
||||
|
||||
class DownloadItem(BaseModel):
|
||||
id: str
|
||||
serie_id: str # Maps to anime_series.key
|
||||
serie_folder: str # Metadata only
|
||||
serie_name: str
|
||||
episode: EpisodeIdentifier
|
||||
status: DownloadStatus
|
||||
priority: DownloadPriority
|
||||
```
|
||||
|
||||
Source: [src/server/models/download.py](../src/server/models/download.py#L63-L118)
|
||||
|
||||
### 5.3 Model Mapping
|
||||
|
||||
| API Field | Database Column | Notes |
|
||||
| -------------- | --------------------- | ------------------ |
|
||||
| `serie_id` | `anime_series.key` | Primary identifier |
|
||||
| `serie_folder` | `anime_series.folder` | Metadata only |
|
||||
| `serie_name` | `anime_series.name` | Display name |
|
||||
|
||||
---
|
||||
|
||||
## 6. Repository Pattern
|
||||
|
||||
The `QueueRepository` class provides data access abstraction.
|
||||
|
||||
```python
|
||||
class QueueRepository:
|
||||
async def save_item(self, item: DownloadItem) -> None:
|
||||
"""Save or update a download item."""
|
||||
|
||||
async def get_all_items(self) -> List[DownloadItem]:
|
||||
"""Get all items from database."""
|
||||
|
||||
async def delete_item(self, item_id: str) -> bool:
|
||||
"""Delete item by ID."""
|
||||
|
||||
async def get_items_by_status(
|
||||
self, status: DownloadStatus
|
||||
) -> List[DownloadItem]:
|
||||
"""Get items filtered by status."""
|
||||
```
|
||||
|
||||
Source: [src/server/services/queue_repository.py](../src/server/services/queue_repository.py)
|
||||
|
||||
---
|
||||
|
||||
## 7. Database Service
|
||||
|
||||
The `AnimeSeriesService` provides async CRUD operations.
|
||||
|
||||
```python
|
||||
class AnimeSeriesService:
|
||||
@staticmethod
|
||||
async def create(
|
||||
db: AsyncSession,
|
||||
key: str,
|
||||
name: str,
|
||||
site: str,
|
||||
folder: str
|
||||
) -> AnimeSeries:
|
||||
"""Create a new anime series."""
|
||||
|
||||
@staticmethod
|
||||
async def get_by_key(
|
||||
db: AsyncSession,
|
||||
key: str
|
||||
) -> Optional[AnimeSeries]:
|
||||
"""Get series by primary key identifier."""
|
||||
```
|
||||
|
||||
Source: [src/server/database/service.py](../src/server/database/service.py)
|
||||
|
||||
---
|
||||
|
||||
## 8. Data Integrity Rules
|
||||
|
||||
### Validation Constraints
|
||||
|
||||
| Field | Rule | Error Message |
|
||||
| ------------------------- | ------------------------ | ------------------------------------- |
|
||||
| `anime_series.key` | Non-empty, max 255 chars | "Series key cannot be empty" |
|
||||
| `anime_series.name` | Non-empty, max 500 chars | "Series name cannot be empty" |
|
||||
| `episodes.season` | 0-1000 | "Season number must be non-negative" |
|
||||
| `episodes.episode_number` | 0-10000 | "Episode number must be non-negative" |
|
||||
|
||||
Source: [src/server/database/models.py](../src/server/database/models.py#L89-L119)
|
||||
|
||||
### Cascade Rules
|
||||
|
||||
- Deleting `anime_series` deletes all related `episodes` and `download_queue_item`
|
||||
|
||||
---
|
||||
|
||||
## 9. Migration Strategy
|
||||
|
||||
Currently, SQLAlchemy's `create_all()` is used for schema creation.
|
||||
|
||||
```python
|
||||
# src/server/database/connection.py
|
||||
async def init_db():
|
||||
async with engine.begin() as conn:
|
||||
await conn.run_sync(Base.metadata.create_all)
|
||||
```
|
||||
|
||||
For production migrations, Alembic is recommended but not yet implemented.
|
||||
|
||||
Source: [src/server/database/connection.py](../src/server/database/connection.py)
|
||||
|
||||
---
|
||||
|
||||
## 10. Common Query Patterns
|
||||
|
||||
### Get all series with missing episodes
|
||||
|
||||
```python
|
||||
series = await db.execute(
|
||||
select(AnimeSeries).options(selectinload(AnimeSeries.episodes))
|
||||
)
|
||||
for serie in series.scalars():
|
||||
downloaded = [e for e in serie.episodes if e.is_downloaded]
|
||||
```
|
||||
|
||||
### Get pending downloads ordered by priority
|
||||
|
||||
```python
|
||||
items = await db.execute(
|
||||
select(DownloadQueueItem)
|
||||
.where(DownloadQueueItem.status == "pending")
|
||||
.order_by(
|
||||
case(
|
||||
(DownloadQueueItem.priority == "HIGH", 1),
|
||||
(DownloadQueueItem.priority == "NORMAL", 2),
|
||||
(DownloadQueueItem.priority == "LOW", 3),
|
||||
),
|
||||
DownloadQueueItem.added_at
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. Database Location
|
||||
|
||||
| Environment | Default Location |
|
||||
| ----------- | ------------------------------------------------- |
|
||||
| Development | `./data/aniworld.db` |
|
||||
| Production | Via `DATABASE_URL` environment variable |
|
||||
| Testing | In-memory SQLite (`sqlite+aiosqlite:///:memory:`) |
|
||||
64
docs/DEVELOPMENT.md
Normal file
64
docs/DEVELOPMENT.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# Development Guide
|
||||
|
||||
## Document Purpose
|
||||
|
||||
This document provides guidance for developers working on the Aniworld project.
|
||||
|
||||
### What This Document Contains
|
||||
|
||||
- **Prerequisites**: Required software and tools
|
||||
- **Environment Setup**: Step-by-step local development setup
|
||||
- **Project Structure**: Source code organization explanation
|
||||
- **Development Workflow**: Branch strategy, commit conventions
|
||||
- **Coding Standards**: Style guide, linting, formatting
|
||||
- **Running the Application**: Development server, CLI usage
|
||||
- **Debugging Tips**: Common debugging approaches
|
||||
- **IDE Configuration**: VS Code settings, recommended extensions
|
||||
- **Contributing Guidelines**: How to submit changes
|
||||
- **Code Review Process**: Review checklist and expectations
|
||||
|
||||
### What This Document Does NOT Contain
|
||||
|
||||
- Production deployment (see [DEPLOYMENT.md](DEPLOYMENT.md))
|
||||
- API reference (see [API.md](API.md))
|
||||
- Architecture decisions (see [ARCHITECTURE.md](ARCHITECTURE.md))
|
||||
- Test writing guides (see [TESTING.md](TESTING.md))
|
||||
- Security guidelines (see [SECURITY.md](SECURITY.md))
|
||||
|
||||
### Target Audience
|
||||
|
||||
- New Developers joining the project
|
||||
- Contributors (internal and external)
|
||||
- Anyone setting up a development environment
|
||||
|
||||
---
|
||||
|
||||
## Sections to Document
|
||||
|
||||
1. Prerequisites
|
||||
- Python version
|
||||
- Conda environment
|
||||
- Node.js (if applicable)
|
||||
- Git
|
||||
2. Getting Started
|
||||
- Clone repository
|
||||
- Setup conda environment
|
||||
- Install dependencies
|
||||
- Configuration setup
|
||||
3. Project Structure Overview
|
||||
4. Development Server
|
||||
- Starting FastAPI server
|
||||
- Hot reload configuration
|
||||
- Debug mode
|
||||
5. CLI Development
|
||||
6. Code Style
|
||||
- PEP 8 compliance
|
||||
- Type hints requirements
|
||||
- Docstring format
|
||||
- Import organization
|
||||
7. Git Workflow
|
||||
- Branch naming
|
||||
- Commit message format
|
||||
- Pull request process
|
||||
8. Common Development Tasks
|
||||
9. Troubleshooting Development Issues
|
||||
39
docs/README.md
Normal file
39
docs/README.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Aniworld Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
This directory contains all documentation for the Aniworld anime download manager project.
|
||||
|
||||
## Documentation Structure
|
||||
|
||||
| Document | Purpose | Target Audience |
|
||||
| ---------------------------------------- | ---------------------------------------------- | ---------------------------------- |
|
||||
| [ARCHITECTURE.md](ARCHITECTURE.md) | System architecture and design decisions | Architects, Senior Developers |
|
||||
| [API.md](API.md) | REST API reference and WebSocket documentation | Frontend Developers, API Consumers |
|
||||
| [DEVELOPMENT.md](DEVELOPMENT.md) | Developer setup and contribution guide | All Developers |
|
||||
| [DEPLOYMENT.md](DEPLOYMENT.md) | Deployment and operations guide | DevOps, System Administrators |
|
||||
| [DATABASE.md](DATABASE.md) | Database schema and data models | Backend Developers |
|
||||
| [TESTING.md](TESTING.md) | Testing strategy and guidelines | QA Engineers, Developers |
|
||||
| [SECURITY.md](SECURITY.md) | Security considerations and guidelines | Security Engineers, All Developers |
|
||||
| [CONFIGURATION.md](CONFIGURATION.md) | Configuration options reference | Operators, Developers |
|
||||
| [CHANGELOG.md](CHANGELOG.md) | Version history and changes | All Stakeholders |
|
||||
| [TROUBLESHOOTING.md](TROUBLESHOOTING.md) | Common issues and solutions | Support, Operators |
|
||||
| [features.md](features.md) | Feature list and capabilities | Product Owners, Users |
|
||||
| [instructions.md](instructions.md) | AI agent development instructions | AI Agents, Developers |
|
||||
|
||||
## Documentation Standards
|
||||
|
||||
- All documentation uses Markdown format
|
||||
- Keep documentation up-to-date with code changes
|
||||
- Include code examples where applicable
|
||||
- Use clear, concise language
|
||||
- Include diagrams for complex concepts (use Mermaid syntax)
|
||||
|
||||
## Contributing to Documentation
|
||||
|
||||
When adding or updating documentation:
|
||||
|
||||
1. Follow the established format in each document
|
||||
2. Update the README.md if adding new documents
|
||||
3. Ensure cross-references are valid
|
||||
4. Review for spelling and grammar
|
||||
71
docs/TESTING.md
Normal file
71
docs/TESTING.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# Testing Documentation
|
||||
|
||||
## Document Purpose
|
||||
|
||||
This document describes the testing strategy, guidelines, and practices for the Aniworld project.
|
||||
|
||||
### What This Document Contains
|
||||
|
||||
- **Testing Strategy**: Overall approach to quality assurance
|
||||
- **Test Categories**: Unit, integration, API, performance, security tests
|
||||
- **Test Structure**: Organization of test files and directories
|
||||
- **Writing Tests**: Guidelines for writing effective tests
|
||||
- **Fixtures and Mocking**: Shared test utilities and mock patterns
|
||||
- **Running Tests**: Commands and configurations
|
||||
- **Coverage Requirements**: Minimum coverage thresholds
|
||||
- **CI/CD Integration**: How tests run in automation
|
||||
- **Test Data Management**: Managing test fixtures and data
|
||||
- **Best Practices**: Do's and don'ts for testing
|
||||
|
||||
### What This Document Does NOT Contain
|
||||
|
||||
- Production deployment (see [DEPLOYMENT.md](DEPLOYMENT.md))
|
||||
- Security audit procedures (see [SECURITY.md](SECURITY.md))
|
||||
- Bug tracking and issue management
|
||||
- Performance benchmarking results
|
||||
|
||||
### Target Audience
|
||||
|
||||
- Developers writing tests
|
||||
- QA Engineers
|
||||
- CI/CD Engineers
|
||||
- Code reviewers
|
||||
|
||||
---
|
||||
|
||||
## Sections to Document
|
||||
|
||||
1. Testing Philosophy
|
||||
- Test pyramid approach
|
||||
- Quality gates
|
||||
2. Test Categories
|
||||
- Unit Tests (`tests/unit/`)
|
||||
- Integration Tests (`tests/integration/`)
|
||||
- API Tests (`tests/api/`)
|
||||
- Frontend Tests (`tests/frontend/`)
|
||||
- Performance Tests (`tests/performance/`)
|
||||
- Security Tests (`tests/security/`)
|
||||
3. Test Structure and Naming
|
||||
- File naming conventions
|
||||
- Test function naming
|
||||
- Test class organization
|
||||
4. Running Tests
|
||||
- pytest commands
|
||||
- Running specific tests
|
||||
- Verbose output
|
||||
- Coverage reports
|
||||
5. Fixtures and Conftest
|
||||
- Shared fixtures
|
||||
- Database fixtures
|
||||
- Mock services
|
||||
6. Mocking Guidelines
|
||||
- What to mock
|
||||
- Mock patterns
|
||||
- External service mocks
|
||||
7. Coverage Requirements
|
||||
8. CI/CD Integration
|
||||
9. Writing Good Tests
|
||||
- Arrange-Act-Assert pattern
|
||||
- Test isolation
|
||||
- Edge cases
|
||||
10. Common Pitfalls to Avoid
|
||||
53
docs/features.md
Normal file
53
docs/features.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# Aniworld Web Application Features
|
||||
|
||||
## Authentication & Security
|
||||
|
||||
- **Master Password Login**: Secure access to the application with a master password system
|
||||
- **JWT Token Sessions**: Stateless authentication with JSON Web Tokens
|
||||
- **Rate Limiting**: Built-in protection against brute force attacks
|
||||
|
||||
## Configuration Management
|
||||
|
||||
- **Setup Page**: Initial configuration interface for server setup and basic settings
|
||||
- **Config Page**: View and modify application configuration settings
|
||||
- **Scheduler Configuration**: Configure automated rescan schedules
|
||||
- **Backup Management**: Create, restore, and manage configuration backups
|
||||
|
||||
## User Interface
|
||||
|
||||
- **Dark Mode**: Toggle between light and dark themes for better user experience
|
||||
- **Responsive Design**: Mobile-friendly interface with touch support
|
||||
- **Real-time Updates**: WebSocket-based live notifications and progress tracking
|
||||
|
||||
## Anime Management
|
||||
|
||||
- **Anime Library Page**: Display list of anime series with missing episodes
|
||||
- **Series Selection**: Select individual anime series and add episodes to download queue
|
||||
- **Anime Search**: Search for anime series using integrated providers
|
||||
- **Library Scanning**: Automated scanning for missing episodes
|
||||
|
||||
## Download Management
|
||||
|
||||
- **Download Queue Page**: View and manage the current download queue with organized sections
|
||||
- **Queue Organization**: Displays downloads organized by status (pending, active, completed, failed)
|
||||
- **Manual Start/Stop Control**: User manually starts downloads one at a time with Start/Stop buttons
|
||||
- **FIFO Queue Processing**: First-in, first-out queue order (no priority or reordering)
|
||||
- **Single Download Mode**: Only one download active at a time, new downloads must be manually started
|
||||
- **Download Status Display**: Real-time status updates and progress of current download
|
||||
- **Queue Operations**: Add and remove items from the pending queue
|
||||
- **Completed Downloads List**: Separate section for completed downloads with clear button
|
||||
- **Failed Downloads List**: Separate section for failed downloads with retry and clear options
|
||||
- **Retry Failed Downloads**: Automatically retry failed downloads with configurable limits
|
||||
- **Clear Completed**: Remove completed downloads from the queue
|
||||
- **Clear Failed**: Remove failed downloads from the queue
|
||||
- **Queue Statistics**: Real-time counters for pending, active, completed, and failed items
|
||||
|
||||
## Real-time Communication
|
||||
|
||||
- **WebSocket Support**: Real-time notifications for download progress and queue updates
|
||||
- **Progress Tracking**: Live progress updates for downloads and scans
|
||||
- **System Notifications**: Real-time system messages and alerts
|
||||
|
||||
## Core Functionality Overview
|
||||
|
||||
The web application provides a complete interface for managing anime downloads with user-friendly pages for configuration, library management, search capabilities, and download monitoring. All operations are tracked in real-time with comprehensive progress reporting and error handling.
|
||||
@@ -1,422 +0,0 @@
|
||||
# Series Identifier Standardization - Validation Instructions
|
||||
|
||||
## Overview
|
||||
|
||||
This document provides comprehensive instructions for AI agents to validate the **Series Identifier Standardization** change across the Aniworld codebase. The change standardizes `key` as the primary identifier for series and relegates `folder` to metadata-only status.
|
||||
|
||||
## Summary of the Change
|
||||
|
||||
| Field | Purpose | Usage |
|
||||
| -------- | ------------------------------------------------------------------------------ | --------------------------------------------------------------- |
|
||||
| `key` | **Primary Identifier** - Provider-assigned, URL-safe (e.g., `attack-on-titan`) | All lookups, API operations, database queries, WebSocket events |
|
||||
| `folder` | **Metadata Only** - Filesystem folder name (e.g., `Attack on Titan (2013)`) | Display purposes, filesystem operations only |
|
||||
| `id` | **Database Primary Key** - Internal auto-increment integer | Database relationships only |
|
||||
|
||||
---
|
||||
|
||||
## Validation Checklist
|
||||
|
||||
### Phase 2: Application Layer Services
|
||||
|
||||
**Files to validate:**
|
||||
|
||||
1. **`src/server/services/anime_service.py`**
|
||||
|
||||
- [ ] Class docstring explains `key` vs `folder` convention
|
||||
- [ ] All public methods accept `key` parameter for series identification
|
||||
- [ ] No methods accept `folder` as an identifier parameter
|
||||
- [ ] Event handler methods document key/folder convention
|
||||
- [ ] Progress tracking uses `key` in progress IDs where possible
|
||||
|
||||
2. **`src/server/services/download_service.py`**
|
||||
|
||||
- [ ] `DownloadItem` uses `serie_id` (which should be the `key`)
|
||||
- [ ] `serie_folder` is documented as metadata only
|
||||
- [ ] Queue operations look up series by `key` not `folder`
|
||||
- [ ] Persistence format includes `serie_id` as the key identifier
|
||||
|
||||
3. **`src/server/services/websocket_service.py`**
|
||||
|
||||
- [ ] Module docstring explains key/folder convention
|
||||
- [ ] Broadcast methods include `key` in message payloads
|
||||
- [ ] `folder` is documented as optional/display only
|
||||
- [ ] Event broadcasts use `key` as primary identifier
|
||||
|
||||
4. **`src/server/services/scan_service.py`**
|
||||
|
||||
- [ ] Scan operations use `key` for identification
|
||||
- [ ] Progress events include `key` field
|
||||
|
||||
5. **`src/server/services/progress_service.py`**
|
||||
- [ ] Progress tracking includes `key` in metadata where applicable
|
||||
|
||||
**Validation Commands:**
|
||||
|
||||
```bash
|
||||
# Check service layer for folder-based lookups
|
||||
grep -rn "by_folder\|folder.*=.*identifier\|folder.*lookup" src/server/services/ --include="*.py"
|
||||
|
||||
# Verify key is used in services
|
||||
grep -rn "serie_id\|series_key\|key.*identifier" src/server/services/ --include="*.py"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: API Endpoints and Responses
|
||||
|
||||
**Files to validate:**
|
||||
|
||||
1. **`src/server/api/anime.py`**
|
||||
|
||||
- [ ] `AnimeSummary` model has `key` field with proper description
|
||||
- [ ] `AnimeDetail` model has `key` field with proper description
|
||||
- [ ] API docstrings explain `key` is the primary identifier
|
||||
- [ ] `folder` field descriptions state "metadata only"
|
||||
- [ ] Endpoint paths use `key` parameter (e.g., `/api/anime/{key}`)
|
||||
- [ ] No endpoints use `folder` as path parameter for lookups
|
||||
|
||||
2. **`src/server/api/download.py`**
|
||||
|
||||
- [ ] Download endpoints use `serie_id` (key) for operations
|
||||
- [ ] Request models document key/folder convention
|
||||
- [ ] Response models include `key` as primary identifier
|
||||
|
||||
3. **`src/server/models/anime.py`**
|
||||
|
||||
- [ ] Module docstring explains identifier convention
|
||||
- [ ] `AnimeSeriesResponse` has `key` field properly documented
|
||||
- [ ] `SearchResult` has `key` field properly documented
|
||||
- [ ] Field validators normalize `key` to lowercase
|
||||
- [ ] `folder` fields document metadata-only purpose
|
||||
|
||||
4. **`src/server/models/download.py`**
|
||||
|
||||
- [ ] `DownloadItem` has `serie_id` documented as the key
|
||||
- [ ] `serie_folder` documented as metadata only
|
||||
- [ ] Field descriptions are clear about primary vs metadata
|
||||
|
||||
5. **`src/server/models/websocket.py`**
|
||||
- [ ] Module docstring explains key/folder convention
|
||||
- [ ] Message models document `key` as primary identifier
|
||||
- [ ] `folder` documented as optional display metadata
|
||||
|
||||
**Validation Commands:**
|
||||
|
||||
```bash
|
||||
# Check API endpoints for folder-based paths
|
||||
grep -rn "folder.*Path\|/{folder}" src/server/api/ --include="*.py"
|
||||
|
||||
# Verify key is used in endpoints
|
||||
grep -rn "/{key}\|series_key\|serie_id" src/server/api/ --include="*.py"
|
||||
|
||||
# Check model field descriptions
|
||||
grep -rn "Field.*description.*identifier\|Field.*description.*key\|Field.*description.*folder" src/server/models/ --include="*.py"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 4: Frontend Integration
|
||||
|
||||
**Files to validate:**
|
||||
|
||||
1. **`src/server/web/static/js/app.js`**
|
||||
|
||||
- [ ] `selectedSeries` Set uses `key` values, not `folder`
|
||||
- [ ] `seriesData` array comments indicate `key` as primary identifier
|
||||
- [ ] Selection operations use `key` property
|
||||
- [ ] API calls pass `key` for series identification
|
||||
- [ ] WebSocket message handlers extract `key` from data
|
||||
- [ ] No code uses `folder` for series lookups
|
||||
|
||||
2. **`src/server/web/static/js/queue.js`**
|
||||
|
||||
- [ ] Queue items reference series by `key` or `serie_id`
|
||||
- [ ] WebSocket handlers extract `key` from messages
|
||||
- [ ] UI operations use `key` for identification
|
||||
- [ ] `serie_folder` used only for display
|
||||
|
||||
3. **`src/server/web/static/js/websocket_client.js`**
|
||||
|
||||
- [ ] Message handling preserves `key` field
|
||||
- [ ] No transformation that loses `key` information
|
||||
|
||||
4. **HTML Templates** (`src/server/web/templates/`)
|
||||
- [ ] Data attributes use `key` for identification (e.g., `data-key`)
|
||||
- [ ] No `data-folder` used for identification purposes
|
||||
- [ ] Display uses `folder` or `name` appropriately
|
||||
|
||||
**Validation Commands:**
|
||||
|
||||
```bash
|
||||
# Check JavaScript for folder-based lookups
|
||||
grep -rn "\.folder\s*==\|folder.*identifier\|getByFolder" src/server/web/static/js/ --include="*.js"
|
||||
|
||||
# Check data attributes in templates
|
||||
grep -rn "data-key\|data-folder\|data-series" src/server/web/templates/ --include="*.html"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 5: Database Operations
|
||||
|
||||
**Files to validate:**
|
||||
|
||||
1. **`src/server/database/models.py`**
|
||||
|
||||
- [ ] `AnimeSeries` model has `key` column with unique constraint
|
||||
- [ ] `key` column is indexed
|
||||
- [ ] Model docstring explains identifier convention
|
||||
- [ ] `folder` column docstring states "metadata only"
|
||||
- [ ] Validators check `key` is not empty
|
||||
- [ ] No `folder` uniqueness constraint (unless intentional)
|
||||
|
||||
2. **`src/server/database/service.py`**
|
||||
|
||||
- [ ] `AnimeSeriesService` has `get_by_key()` method
|
||||
- [ ] Class docstring explains lookup convention
|
||||
- [ ] No `get_by_folder()` without deprecation
|
||||
- [ ] All CRUD operations use `key` for identification
|
||||
- [ ] Logging uses `key` in messages
|
||||
|
||||
**Validation Commands:**
|
||||
|
||||
```bash
|
||||
# Check database models
|
||||
grep -rn "unique=True\|index=True" src/server/database/models.py
|
||||
|
||||
# Check service lookups
|
||||
grep -rn "get_by_key\|get_by_folder\|filter.*key\|filter.*folder" src/server/database/service.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 6: WebSocket Events
|
||||
|
||||
**Files to validate:**
|
||||
|
||||
1. **All WebSocket broadcast calls** should include `key` in payload:
|
||||
|
||||
- `download_progress` → includes `key`
|
||||
- `download_complete` → includes `key`
|
||||
- `download_failed` → includes `key`
|
||||
- `scan_progress` → includes `key` (where applicable)
|
||||
- `queue_status` → items include `key`
|
||||
|
||||
2. **Message format validation:**
|
||||
```json
|
||||
{
|
||||
"type": "download_progress",
|
||||
"data": {
|
||||
"key": "attack-on-titan", // PRIMARY - always present
|
||||
"folder": "Attack on Titan (2013)", // OPTIONAL - display only
|
||||
"progress": 45.5,
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Validation Commands:**
|
||||
|
||||
```bash
|
||||
# Check WebSocket broadcast calls
|
||||
grep -rn "broadcast.*key\|send_json.*key" src/server/services/ --include="*.py"
|
||||
|
||||
# Check message construction
|
||||
grep -rn '"key":\|"folder":' src/server/services/ --include="*.py"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 7: Test Coverage
|
||||
|
||||
**Test files to validate:**
|
||||
|
||||
1. **`tests/unit/test_serie_class.py`**
|
||||
|
||||
- [ ] Tests for key validation (empty, whitespace, None)
|
||||
- [ ] Tests for key as primary identifier
|
||||
- [ ] Tests for folder as metadata only
|
||||
|
||||
2. **`tests/unit/test_anime_service.py`**
|
||||
|
||||
- [ ] Service tests use `key` for operations
|
||||
- [ ] Mock objects have proper `key` attributes
|
||||
|
||||
3. **`tests/unit/test_database_models.py`**
|
||||
|
||||
- [ ] Tests for `key` uniqueness constraint
|
||||
- [ ] Tests for `key` validation
|
||||
|
||||
4. **`tests/unit/test_database_service.py`**
|
||||
|
||||
- [ ] Tests for `get_by_key()` method
|
||||
- [ ] No tests for deprecated folder lookups
|
||||
|
||||
5. **`tests/api/test_anime_endpoints.py`**
|
||||
|
||||
- [ ] API tests use `key` in requests
|
||||
- [ ] Mock `FakeSerie` has proper `key` attribute
|
||||
- [ ] Comments explain key/folder convention
|
||||
|
||||
6. **`tests/unit/test_websocket_service.py`**
|
||||
- [ ] WebSocket tests verify `key` in messages
|
||||
- [ ] Broadcast tests include `key` in payload
|
||||
|
||||
**Validation Commands:**
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
conda run -n AniWorld python -m pytest tests/ -v --tb=short
|
||||
|
||||
# Run specific test files
|
||||
conda run -n AniWorld python -m pytest tests/unit/test_serie_class.py -v
|
||||
conda run -n AniWorld python -m pytest tests/unit/test_database_models.py -v
|
||||
conda run -n AniWorld python -m pytest tests/api/test_anime_endpoints.py -v
|
||||
|
||||
# Search tests for identifier usage
|
||||
grep -rn "key.*identifier\|folder.*metadata" tests/ --include="*.py"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Issues to Check
|
||||
|
||||
### 1. Inconsistent Naming
|
||||
|
||||
Look for inconsistent parameter names:
|
||||
|
||||
- `serie_key` vs `series_key` vs `key`
|
||||
- `serie_id` should refer to `key`, not database `id`
|
||||
- `serie_folder` vs `folder`
|
||||
|
||||
### 2. Missing Documentation
|
||||
|
||||
Check that ALL models, services, and APIs document:
|
||||
|
||||
- What `key` is and how to use it
|
||||
- That `folder` is metadata only
|
||||
|
||||
### 3. Legacy Code Patterns
|
||||
|
||||
Search for deprecated patterns:
|
||||
|
||||
```python
|
||||
# Bad - using folder for lookup
|
||||
series = get_by_folder(folder_name)
|
||||
|
||||
# Good - using key for lookup
|
||||
series = get_by_key(series_key)
|
||||
```
|
||||
|
||||
### 4. API Response Consistency
|
||||
|
||||
Verify all API responses include:
|
||||
|
||||
- `key` field (primary identifier)
|
||||
- `folder` field (optional, for display)
|
||||
|
||||
### 5. Frontend Data Flow
|
||||
|
||||
Verify the frontend:
|
||||
|
||||
- Stores `key` in selection sets
|
||||
- Passes `key` to API calls
|
||||
- Uses `folder` only for display
|
||||
|
||||
---
|
||||
|
||||
## Deprecation Warnings
|
||||
|
||||
The following should have deprecation warnings (for removal in v3.0.0):
|
||||
|
||||
1. Any `get_by_folder()` or `GetByFolder()` methods
|
||||
2. Any API endpoints that accept `folder` as a lookup parameter
|
||||
3. Any frontend code that uses `folder` for identification
|
||||
|
||||
**Example deprecation:**
|
||||
|
||||
```python
|
||||
import warnings
|
||||
|
||||
def get_by_folder(self, folder: str):
|
||||
"""DEPRECATED: Use get_by_key() instead."""
|
||||
warnings.warn(
|
||||
"get_by_folder() is deprecated, use get_by_key(). "
|
||||
"Will be removed in v3.0.0",
|
||||
DeprecationWarning,
|
||||
stacklevel=2
|
||||
)
|
||||
# ... implementation
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Automated Validation Script
|
||||
|
||||
Run this script to perform automated checks:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# identifier_validation.sh
|
||||
|
||||
echo "=== Series Identifier Standardization Validation ==="
|
||||
echo ""
|
||||
|
||||
echo "1. Checking core entities..."
|
||||
grep -rn "PRIMARY IDENTIFIER\|metadata only" src/core/entities/ --include="*.py" | head -20
|
||||
|
||||
echo ""
|
||||
echo "2. Checking for deprecated folder lookups..."
|
||||
grep -rn "get_by_folder\|GetByFolder" src/ --include="*.py"
|
||||
|
||||
echo ""
|
||||
echo "3. Checking API models for key field..."
|
||||
grep -rn 'key.*Field\|Field.*key' src/server/models/ --include="*.py" | head -20
|
||||
|
||||
echo ""
|
||||
echo "4. Checking database models..."
|
||||
grep -rn "key.*unique\|key.*index" src/server/database/models.py
|
||||
|
||||
echo ""
|
||||
echo "5. Checking frontend key usage..."
|
||||
grep -rn "selectedSeries\|\.key\|data-key" src/server/web/static/js/ --include="*.js" | head -20
|
||||
|
||||
echo ""
|
||||
echo "6. Running tests..."
|
||||
conda run -n AniWorld python -m pytest tests/unit/test_serie_class.py -v --tb=short
|
||||
|
||||
echo ""
|
||||
echo "=== Validation Complete ==="
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Expected Results
|
||||
|
||||
After validation, you should confirm:
|
||||
|
||||
1. ✅ All core entities use `key` as primary identifier
|
||||
2. ✅ All services look up series by `key`
|
||||
3. ✅ All API endpoints use `key` for operations
|
||||
4. ✅ All database queries use `key` for lookups
|
||||
5. ✅ Frontend uses `key` for selection and API calls
|
||||
6. ✅ WebSocket events include `key` in payload
|
||||
7. ✅ All tests pass
|
||||
8. ✅ Documentation clearly explains the convention
|
||||
9. ✅ Deprecation warnings exist for legacy patterns
|
||||
|
||||
---
|
||||
|
||||
## Sign-off
|
||||
|
||||
Once validation is complete, update this section:
|
||||
|
||||
- [x] Phase 1: Core Entities - Validated by: **AI Agent** Date: **28 Nov 2025**
|
||||
- [x] Phase 2: Services - Validated by: **AI Agent** Date: **28 Nov 2025**
|
||||
- [ ] Phase 3: API - Validated by: **\_\_\_** Date: **\_\_\_**
|
||||
- [ ] Phase 4: Frontend - Validated by: **\_\_\_** Date: **\_\_\_**
|
||||
- [ ] Phase 5: Database - Validated by: **\_\_\_** Date: **\_\_\_**
|
||||
- [ ] Phase 6: WebSocket - Validated by: **\_\_\_** Date: **\_\_\_**
|
||||
- [ ] Phase 7: Tests - Validated by: **\_\_\_** Date: **\_\_\_**
|
||||
|
||||
**Final Approval:** \***\*\*\*\*\***\_\_\_\***\*\*\*\*\*** Date: **\*\***\_**\*\***
|
||||
@@ -1,440 +0,0 @@
|
||||
# Aniworld Web Application Infrastructure
|
||||
|
||||
```bash
|
||||
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, three identifiers are used for anime series:
|
||||
|
||||
| Identifier | Type | Purpose | Example |
|
||||
| ---------- | --------------- | ----------------------------------------------------------- | -------------------------- |
|
||||
| `key` | Unique, Indexed | **PRIMARY** - All lookups, API operations, WebSocket events | `"attack-on-titan"` |
|
||||
| `folder` | String | Display/filesystem metadata only (never for lookups) | `"Attack on Titan (2013)"` |
|
||||
| `id` | Primary Key | Internal database key for relationships | `1`, `42` |
|
||||
|
||||
### Key Format Requirements
|
||||
|
||||
- **Lowercase only**: No uppercase letters allowed
|
||||
- **URL-safe**: Only alphanumeric characters and hyphens
|
||||
- **Hyphen-separated**: Words separated by single hyphens
|
||||
- **No leading/trailing hyphens**: Must start and end with alphanumeric
|
||||
- **No consecutive hyphens**: `attack--titan` is invalid
|
||||
|
||||
**Valid examples**: `"attack-on-titan"`, `"one-piece"`, `"86-eighty-six"`, `"re-zero"`
|
||||
**Invalid examples**: `"Attack On Titan"`, `"attack_on_titan"`, `"attack on titan"`
|
||||
|
||||
### Notes
|
||||
|
||||
- **Backward Compatibility**: API endpoints accepting `anime_id` will check `key` first, then fall back to `folder` lookup
|
||||
- **New Code**: Always use `key` for identification; `folder` is metadata only
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Authentication (`/api/auth`)
|
||||
|
||||
- `POST /login` - Master password authentication (returns JWT)
|
||||
- `POST /logout` - Invalidate session
|
||||
- `GET /status` - Check authentication status
|
||||
|
||||
### Configuration (`/api/config`)
|
||||
|
||||
- `GET /` - Get configuration
|
||||
- `PUT /` - Update configuration
|
||||
- `POST /validate` - Validate without applying
|
||||
- `GET /backups` - List backups
|
||||
- `POST /backups/{name}/restore` - Restore backup
|
||||
|
||||
### Anime (`/api/anime`)
|
||||
|
||||
- `GET /` - List anime with missing episodes (returns `key` as identifier)
|
||||
- `GET /{anime_id}` - Get anime details (accepts `key` or `folder` for backward compatibility)
|
||||
- `POST /search` - Search for anime (returns `key` as identifier)
|
||||
- `POST /add` - Add new series (extracts `key` from link URL)
|
||||
- `POST /rescan` - Trigger library rescan
|
||||
|
||||
**Response Models:**
|
||||
|
||||
- `AnimeSummary`: `key` (primary identifier), `name`, `site`, `folder` (metadata), `missing_episodes`, `link`
|
||||
- `AnimeDetail`: `key` (primary identifier), `title`, `folder` (metadata), `episodes`, `description`
|
||||
|
||||
### Download Queue (`/api/queue`)
|
||||
|
||||
- `GET /status` - Queue status and statistics
|
||||
- `POST /add` - Add episodes to queue
|
||||
- `DELETE /{item_id}` - Remove item
|
||||
- `POST /start` | `/stop` | `/pause` | `/resume` - Queue control
|
||||
- `POST /retry` - Retry failed downloads
|
||||
- `DELETE /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`, `progress`
|
||||
- `QueueStatus`: `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:
|
||||
|
||||
```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
|
||||
|
||||
| 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 method**
|
||||
- `AnimeSeriesService.get_by_id(id)` - Internal lookup by database ID
|
||||
- No `get_by_folder()` method exists - folder is never used for lookups
|
||||
|
||||
### DownloadQueueItem Fields
|
||||
|
||||
| Field | Type | Purpose |
|
||||
| -------------- | ----------- | ----------------------------------------- |
|
||||
| `id` | String (PK) | UUID for the queue item |
|
||||
| `serie_id` | String | Series key for identification |
|
||||
| `serie_folder` | String | Filesystem folder path |
|
||||
| `serie_name` | String | Display name for the series |
|
||||
| `season` | Integer | Season number |
|
||||
| `episode` | Integer | Episode number |
|
||||
| `status` | Enum | pending, downloading, completed, failed |
|
||||
| `priority` | Enum | low, normal, high |
|
||||
| `progress` | Float | Download progress percentage (0.0-100.0) |
|
||||
| `error` | String | Error message if failed |
|
||||
| `retry_count` | Integer | Number of retry attempts |
|
||||
| `added_at` | DateTime | When item was added to queue |
|
||||
| `started_at` | DateTime | When download started (nullable) |
|
||||
| `completed_at` | DateTime | When download completed/failed (nullable) |
|
||||
|
||||
## Data Storage
|
||||
|
||||
### Storage Architecture
|
||||
|
||||
The application uses **SQLite database** as the primary storage for all application data.
|
||||
|
||||
| Data Type | Storage Location | Service |
|
||||
| -------------- | ------------------ | --------------------------------------- |
|
||||
| Anime Series | `data/aniworld.db` | `AnimeSeriesService` |
|
||||
| Episodes | `data/aniworld.db` | `AnimeSeriesService` |
|
||||
| Download Queue | `data/aniworld.db` | `DownloadService` via `QueueRepository` |
|
||||
| User Sessions | `data/aniworld.db` | `AuthService` |
|
||||
| Configuration | `data/config.json` | `ConfigService` |
|
||||
|
||||
### Download Queue Storage
|
||||
|
||||
The download queue is stored in SQLite via `QueueRepository`, which wraps `DownloadQueueService`:
|
||||
|
||||
```python
|
||||
# QueueRepository provides async operations for queue items
|
||||
repository = QueueRepository(session_factory)
|
||||
|
||||
# Save item to database
|
||||
saved_item = await repository.save_item(download_item)
|
||||
|
||||
# Get pending items (ordered by priority and add time)
|
||||
pending = await repository.get_pending_items()
|
||||
|
||||
# Update item status
|
||||
await repository.update_status(item_id, DownloadStatus.COMPLETED)
|
||||
|
||||
# Update download progress
|
||||
await repository.update_progress(item_id, progress=45.5, downloaded=450, total=1000, speed=2.5)
|
||||
```
|
||||
|
||||
**Queue Persistence Features:**
|
||||
|
||||
- Queue state survives server restarts
|
||||
- Items in `downloading` status are reset to `pending` on startup
|
||||
- Failed items within retry limit are automatically re-queued
|
||||
- Completed and failed history is preserved (with limits)
|
||||
- Real-time progress updates are persisted to database
|
||||
|
||||
### Anime Series Database Storage
|
||||
|
||||
```python
|
||||
# Add series to database
|
||||
await AnimeSeriesService.create(db_session, series_data)
|
||||
|
||||
# Query series by key
|
||||
series = await AnimeSeriesService.get_by_key(db_session, "attack-on-titan")
|
||||
|
||||
# Update series
|
||||
await AnimeSeriesService.update(db_session, series_id, update_data)
|
||||
```
|
||||
|
||||
### Legacy File Storage (Deprecated)
|
||||
|
||||
The legacy file-based storage is **deprecated** and will be removed in v3.0.0:
|
||||
|
||||
- `Serie.save_to_file()` - Deprecated, use `AnimeSeriesService.create()`
|
||||
- `Serie.load_from_file()` - Deprecated, use `AnimeSeriesService.get_by_key()`
|
||||
- `SerieList.add()` - Deprecated, use `SerieList.add_to_db()`
|
||||
|
||||
Deprecation warnings are raised when using these methods.
|
||||
|
||||
## Core Services
|
||||
|
||||
### SeriesApp (`src/core/SeriesApp.py`)
|
||||
|
||||
Main engine for anime series management with async support, progress callbacks, and cancellation.
|
||||
|
||||
**Key Methods:**
|
||||
|
||||
- `search(words)` - Search for anime series
|
||||
- `download(serie_folder, season, episode, key, language)` - Download an episode
|
||||
- `rescan()` - Rescan directory for missing episodes
|
||||
- `get_all_series_from_data_files()` - Load all series from data files in the anime directory (used for database sync on startup)
|
||||
|
||||
### Data File to Database Sync
|
||||
|
||||
On application startup, the system automatically syncs series from data files to the database:
|
||||
|
||||
1. After `download_service.initialize()` succeeds
|
||||
2. `SeriesApp.get_all_series_from_data_files()` loads all series from `data` files
|
||||
3. Each series is added to the database via `SerieList.add_to_db()`
|
||||
4. Existing series are skipped (no duplicates)
|
||||
5. Sync continues silently even if individual series fail
|
||||
|
||||
This ensures that series metadata stored in filesystem data files is available in the database for the web application.
|
||||
|
||||
### Callback System (`src/core/interfaces/callbacks.py`)
|
||||
|
||||
- `ProgressCallback`, `ErrorCallback`, `CompletionCallback`
|
||||
- Context classes include `key` + optional `folder` fields
|
||||
- Thread-safe `CallbackManager` for 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"`
|
||||
- **`validate_series_key_or_folder(identifier, allow_folder=True)`**: Backward-compatible validation
|
||||
- Returns tuple `(identifier, is_key)` where `is_key` indicates if it's a valid key format
|
||||
- Set `allow_folder=False` to require strict key format
|
||||
|
||||
### 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:**
|
||||
|
||||
```python
|
||||
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:
|
||||
|
||||
```javascript
|
||||
const socket = io();
|
||||
socket.join("download_progress");
|
||||
socket.on("download_progress", (data) => {
|
||||
/* ... */
|
||||
});
|
||||
```
|
||||
|
||||
### Authentication
|
||||
|
||||
JWT tokens stored in localStorage, included as `Authorization: Bearer <token>`.
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
# 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
|
||||
|
||||
## Code Examples
|
||||
|
||||
### API Usage with Key Identifier
|
||||
|
||||
```python
|
||||
# Fetching anime list - response includes 'key' as identifier
|
||||
response = requests.get("/api/anime", headers={"Authorization": f"Bearer {token}"})
|
||||
anime_list = response.json()
|
||||
# Each item has: key="attack-on-titan", folder="Attack on Titan (2013)", ...
|
||||
|
||||
# Fetching specific anime by key (preferred)
|
||||
response = requests.get("/api/anime/attack-on-titan", headers={"Authorization": f"Bearer {token}"})
|
||||
|
||||
# Adding to download queue using key
|
||||
download_request = {
|
||||
"serie_id": "attack-on-titan", # Use key, not folder
|
||||
"serie_folder": "Attack on Titan (2013)", # Metadata for filesystem
|
||||
"serie_name": "Attack on Titan",
|
||||
"episodes": ["S01E01", "S01E02"],
|
||||
"priority": 1
|
||||
}
|
||||
response = requests.post("/api/queue/add", json=download_request, headers=headers)
|
||||
```
|
||||
|
||||
### WebSocket Event Handling
|
||||
|
||||
```javascript
|
||||
// WebSocket events always include 'key' as identifier
|
||||
socket.on("download_progress", (data) => {
|
||||
const key = data.key; // Primary identifier: "attack-on-titan"
|
||||
const folder = data.folder; // Metadata: "Attack on Titan (2013)"
|
||||
updateProgressBar(key, data.percent);
|
||||
});
|
||||
```
|
||||
122
docs/instructions.md
Normal file
122
docs/instructions.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# Aniworld Web Application Development Instructions
|
||||
|
||||
This document provides detailed tasks for AI agents to implement a modern web application for the Aniworld anime download manager. All tasks should follow the coding guidelines specified in the project's copilot instructions.
|
||||
|
||||
## Project Overview
|
||||
|
||||
The goal is to create a FastAPI-based web application that provides a modern interface for the existing Aniworld anime download functionality. The core anime logic should remain in `SeriesApp.py` while the web layer provides REST API endpoints and a responsive UI.
|
||||
|
||||
## Architecture Principles
|
||||
|
||||
- **Single Responsibility**: Each file/class has one clear purpose
|
||||
- **Dependency Injection**: Use FastAPI's dependency system
|
||||
- **Clean Separation**: Web layer calls core logic, never the reverse
|
||||
- **File Size Limit**: Maximum 500 lines per file
|
||||
- **Type Hints**: Use comprehensive type annotations
|
||||
- **Error Handling**: Proper exception handling and logging
|
||||
|
||||
## Additional Implementation Guidelines
|
||||
|
||||
### Code Style and Standards
|
||||
|
||||
- **Type Hints**: Use comprehensive type annotations throughout all modules
|
||||
- **Docstrings**: Follow PEP 257 for function and class documentation
|
||||
- **Error Handling**: Implement custom exception classes with meaningful messages
|
||||
- **Logging**: Use structured logging with appropriate log levels
|
||||
- **Security**: Validate all inputs and sanitize outputs
|
||||
- **Performance**: Use async/await patterns for I/O operations
|
||||
|
||||
## 📞 Escalation
|
||||
|
||||
If you encounter:
|
||||
|
||||
- Architecture issues requiring design decisions
|
||||
- Tests that conflict with documented requirements
|
||||
- Breaking changes needed
|
||||
- Unclear requirements or expectations
|
||||
|
||||
**Document the issue and escalate rather than guessing.**
|
||||
|
||||
---
|
||||
|
||||
## 📚 Helpful Commands
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
conda run -n AniWorld python -m pytest tests/ -v --tb=short
|
||||
|
||||
# Run specific test file
|
||||
conda run -n AniWorld python -m pytest tests/unit/test_websocket_service.py -v
|
||||
|
||||
# Run specific test class
|
||||
conda run -n AniWorld python -m pytest tests/unit/test_websocket_service.py::TestWebSocketService -v
|
||||
|
||||
# Run specific test
|
||||
conda run -n AniWorld python -m pytest tests/unit/test_websocket_service.py::TestWebSocketService::test_broadcast_download_progress -v
|
||||
|
||||
# Run with extra verbosity
|
||||
conda run -n AniWorld python -m pytest tests/ -vv
|
||||
|
||||
# Run with full traceback
|
||||
conda run -n AniWorld python -m pytest tests/ -v --tb=long
|
||||
|
||||
# Run and stop at first failure
|
||||
conda run -n AniWorld python -m pytest tests/ -v -x
|
||||
|
||||
# Run tests matching pattern
|
||||
conda run -n AniWorld python -m pytest tests/ -v -k "auth"
|
||||
|
||||
# Show all print statements
|
||||
conda run -n AniWorld python -m pytest tests/ -v -s
|
||||
|
||||
#Run app
|
||||
conda run -n AniWorld python -m uvicorn src.server.fastapi_app:app --host 127.0.0.1 --port 8000 --reload
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
1. **Incremental Development**: Implement features incrementally, testing each component thoroughly before moving to the next
|
||||
2. **Code Review**: Review all generated code for adherence to project standards
|
||||
3. **Documentation**: Document all public APIs and complex logic
|
||||
4. **Testing**: Maintain test coverage above 80% for all new code
|
||||
5. **Performance**: Profile and optimize critical paths, especially download and streaming operations
|
||||
6. **Security**: Regular security audits and dependency updates
|
||||
7. **Monitoring**: Implement comprehensive monitoring and alerting
|
||||
8. **Maintenance**: Plan for regular maintenance and updates
|
||||
|
||||
## Task Completion Checklist
|
||||
|
||||
For each task completed:
|
||||
|
||||
- [ ] Implementation follows coding standards
|
||||
- [ ] Unit tests written and passing
|
||||
- [ ] Integration tests passing
|
||||
- [ ] Documentation updated
|
||||
- [ ] Error handling implemented
|
||||
- [ ] Logging added
|
||||
- [ ] Security considerations addressed
|
||||
- [ ] Performance validated
|
||||
- [ ] Code reviewed
|
||||
- [ ] Task marked as complete in instructions.md
|
||||
- [ ] Infrastructure.md updated
|
||||
- [ ] Changes committed to git; keep your messages in git short and clear
|
||||
- [ ] Take the next task
|
||||
|
||||
---
|
||||
|
||||
### Prerequisites
|
||||
|
||||
1. Server is running: `conda run -n AniWorld python -m uvicorn src.server.fastapi_app:app --host 127.0.0.1 --port 8000 --reload`
|
||||
2. Password: `Hallo123!`
|
||||
3. Login via browser at `http://127.0.0.1:8000/login`
|
||||
|
||||
### Notes
|
||||
|
||||
- This is a simplification that removes complexity while maintaining core functionality
|
||||
- Improves user experience with explicit manual control
|
||||
- Easier to understand, test, and maintain
|
||||
- Good foundation for future enhancements if needed
|
||||
|
||||
---
|
||||
Reference in New Issue
Block a user