- Add _run_startup_health_checks() function in fastapi_app.py - Check ffmpeg availability (warning) - Check DNS resolution for aniworld.to and api.themoviedb.org (warning) - Check anime_directory configuration and writability (error) - Store startup checks in app.state for health endpoint access - Add /health/ready endpoint for container orchestrators - Returns not_ready with 503 when critical failures present - Includes critical_failures list for debugging - Update /health endpoint to include startup check results - Status reflects worst check (error > warning > ok) - Document health check endpoints in DEVELOPMENT.md - Add unit tests for startup health checks - Add unit tests for /health/ready endpoint Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
8.3 KiB
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)
- API reference (see API.md)
- Architecture decisions (see ARCHITECTURE.md)
- Test writing guides (see TESTING.md)
- Security guidelines (see SECURITY.md)
Target Audience
- New Developers joining the project
- Contributors (internal and external)
- Anyone setting up a development environment
Sections to Document
- Prerequisites
- Python version
- Conda environment
- Node.js (if applicable)
- Git
- Getting Started
- Clone repository
- Setup conda environment
- Install dependencies
- Configuration setup
- Project Structure Overview
- Development Server
- Starting FastAPI server
- Hot reload configuration
- Debug mode
- CLI Development
- Code Style
- PEP 8 compliance
- Type hints requirements
- Docstring format
- Import organization
- Git Workflow
- Branch naming
- Commit message format
- Pull request process
- Common Development Tasks
Adding Queue Deduplication
The download queue prevents duplicate entries at two levels:
In-Memory Deduplication (src/server/services/download_service.py):
_pending_by_episodedict tracks pending episodes: key =(serie_id, season, episode)_add_to_pending_queue()updates the dict when adding itemsadd_to_queue()checks this dict before adding episodes (includes batch-local dedup)_remove_from_pending_queue()cleans up the dict when items are removed
Database Constraint (src/server/models.py):
DownloadQueueItemhas a unique index onepisode_idvia__table_args__- Prevents duplicate queue entries at the database level
- Unique constraint:
Index("ix_download_queue_episode_pending", "episode_id", unique=True)
Scheduler Cooldown (src/server/services/scheduler_service.py):
_last_auto_download_timetracks when auto-download last ran- 5-minute cooldown prevents rapid re-triggers
- Checked at start of
_auto_download_missing()
Mocking the Download Queue
When testing components that use the download queue:
# Mock repository for unit tests
class MockQueueRepository:
def __init__(self):
self._items: Dict[str, DownloadItem] = {}
async def save_item(self, item: DownloadItem) -> DownloadItem:
self._items[item.id] = item
return item
async def get_all_items(self) -> List[DownloadItem]:
return list(self._items.values())
# Use in fixture
@pytest.fixture
def mock_queue_repository():
return MockQueueRepository()
@pytest.fixture
def download_service(mock_anime_service, mock_queue_repository):
return DownloadService(
anime_service=mock_anime_service,
queue_repository=mock_queue_repository,
max_retries=3,
)
- Troubleshooting Development Issues
Async Context Managers for aiohttp
All aiohttp.ClientSession usages must be wrapped in async with:
# Correct — session properly closed on exit
async with TMDBClient(api_key="key") as client:
result = await client.search_tv_show("Show")
# Wrong — session may leak if exception occurs
client = TMDBClient(api_key="key")
result = await client.search_tv_show("Show")
await client.close() # May not be called if exception raised earlier
Why:
aiohttp.ClientSessionholds TCP connections that must be explicitly closed- If exception occurs before
close(), session leaks - Context manager guarantees
__aexit__runs even on exceptions
Services that use aiohttp:
TMDBClient— has__aenter__/__aexit__, useasync withImageDownloader— has__aenter__/__aexit__, useasync withNFOService— wraps both above, useasync with
Verification:
- Missing context manager usage triggers
__del__warning on garbage collection - Integration tests verify no "Unclosed client session" errors in logs
Scheduler Persistence and Recovery
APScheduler stores jobs in data/scheduler.db (SQLite) so they survive process restarts:
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
jobstores = {
"default": SQLAlchemyJobStore(url="sqlite:///./data/scheduler.db"),
}
scheduler = AsyncIOScheduler(jobstores=jobstores)
Grace period: misfire_grace_time=3600 (1 hour). If server is down at scheduled time and restarts within 1 hour, missed job runs automatically via APScheduler coalesce behavior.
Startup recovery: On start(), scheduler loads persisted jobs from DB. APScheduler handles missed jobs internally when coalesce=True.
Health endpoint: GET /health returns scheduler_next_run and scheduler_last_run for external monitors (Uptime Kuma, Prometheus, etc.).
If server is down >1 hour: No automatic recovery. Manual trigger via POST /api/scheduler/trigger-rescan or wait for next scheduled run.
Health Check Endpoints
The application provides health check endpoints for monitoring and container orchestration:
GET /health
Basic health check returning service status and startup health check results.
Response fields:
status: "healthy", "degraded", or "unhealthy" based on startup checkstimestamp: ISO timestamp of the checkseries_app_initialized: Whether the series app is loadedanime_directory_configured: Whether anime_directory is setscheduler_next_run/scheduler_last_run: Scheduler timeschecks: Detailed startup check results (ffmpeg, DNS, anime_directory)
GET /health/ready
Readiness check for container orchestrators (Kubernetes, Docker Swarm).
Response when ready:
{
"status": "ready",
"ready": true,
"timestamp": "2024-01-01T00:00:00",
"checks": {...}
}
Response when not ready (503):
{
"status": "not_ready",
"ready": false,
"timestamp": "2024-01-01T00:00:00",
"critical_failures": ["anime_directory: not configured"],
"checks": {...}
}
GET /health/detailed
Comprehensive health check including database, filesystem, and system metrics.
Startup Health Checks
On application startup, the following checks are performed:
| Check | Failure Status | Impact |
|---|---|---|
ffmpeg |
warning | HLS downloads may fail |
dns_aniworld |
warning | Provider requests may fail |
dns_tmdb |
warning | TMDB API calls may fail |
anime_directory |
error | Download service disabled |
DNS checks are warnings because failures can be transient. anime_directory errors disable the download service to prevent failures.
Troubleshooting Development Issues
Scheduler missed a run
- Server was down at scheduled time (03:00 UTC by default).
- Check
data/scheduler.dbexists — if not, jobs are not persisted. - If server was down >1 hour, missed job is dropped (misfire window exceeded).
- Trigger manually:
POST /api/scheduler/trigger-rescan - Monitor next run:
GET /health→scheduler_next_run - If problem repeats, increase
misfire_grace_timeinscheduler_service.py.
Startup health check failures
If /health returns unhealthy status:
-
anime_directory error: Directory not configured or not writable
- Check
ANIME_DIRECTORYenvironment variable - Verify directory exists and permissions allow write access
- Download service will not initialize until resolved
- Check
-
ffmpeg warning: ffmpeg not found in PATH
- HLS stream downloads will fail
- Install ffmpeg:
apt install ffmpegorbrew install ffmpeg
-
DNS warnings: Domain resolution failed
- Check network connectivity
- DNS failures are transient — warnings don't block startup
- Retry later to verify:
GET /health