Files
Aniworld/docs/DEVELOPMENT.md
Lukas 3551838887 Add startup health checks and /health/ready endpoint
- 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>
2026-05-23 22:12:03 +02:00

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

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

Adding Queue Deduplication

The download queue prevents duplicate entries at two levels:

In-Memory Deduplication (src/server/services/download_service.py):

  • _pending_by_episode dict tracks pending episodes: key = (serie_id, season, episode)
  • _add_to_pending_queue() updates the dict when adding items
  • add_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):

  • DownloadQueueItem has a unique index on episode_id via __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_time tracks 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,
    )
  1. 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.ClientSession holds 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__, use async with
  • ImageDownloader — has __aenter__/__aexit__, use async with
  • NFOService — wraps both above, use async 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 checks
  • timestamp: ISO timestamp of the check
  • series_app_initialized: Whether the series app is loaded
  • anime_directory_configured: Whether anime_directory is set
  • scheduler_next_run / scheduler_last_run: Scheduler times
  • checks: 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

  1. Server was down at scheduled time (03:00 UTC by default).
  2. Check data/scheduler.db exists — if not, jobs are not persisted.
  3. If server was down >1 hour, missed job is dropped (misfire window exceeded).
  4. Trigger manually: POST /api/scheduler/trigger-rescan
  5. Monitor next run: GET /healthscheduler_next_run
  6. If problem repeats, increase misfire_grace_time in scheduler_service.py.

Startup health check failures

If /health returns unhealthy status:

  1. anime_directory error: Directory not configured or not writable

    • Check ANIME_DIRECTORY environment variable
    • Verify directory exists and permissions allow write access
    • Download service will not initialize until resolved
  2. ffmpeg warning: ffmpeg not found in PATH

    • HLS stream downloads will fail
    • Install ffmpeg: apt install ffmpeg or brew install ffmpeg
  3. DNS warnings: Domain resolution failed

    • Check network connectivity
    • DNS failures are transient — warnings don't block startup
    • Retry later to verify: GET /health