# AGENTS.md - Agentic Coding Guidelines This file provides guidance for AI agents working in this repository. ## Quick Start ```bash # Setup python3 -m venv venv && source venv/bin/activate pip install -r requirements.txt # Run dev server uvicorn main:app --reload --host 0.0.0.0 --port 3000 ``` ## Build, Lint & Test Commands ### Running Tests ```bash # All tests pytest # With coverage (HTML report in htmlcov/) pytest --cov=app --cov-report=html # Unit only (fast) pytest -m "unit" # Integration tests only pytest -m "integration" # Exclude slow tests pytest -m "not slow" # Exclude network tests (mocked only) pytest -m "not network" # Verbose with print debugging pytest -v -s # Generate HTML report pytest --html=report.html --self-contained-html # Timeout per test (seconds) pytest --timeout=30 ``` ### Running Single Tests ```bash # Specific file pytest tests/test_sonarr.py -v # Specific class pytest tests/test_sonarr.py::TestSonarrHandler -v # Specific test pytest tests/test_sonarr.py::TestSonarrHandler::test_add_mapping -v # Pattern match pytest -k "test_download" -v ``` ## Code Style ### Imports (PEP 8 order) 1. Standard library (`os`, `json`, `asyncio`) 2. Third-party (`httpx`, `beautifulsoup4`, `fastapi`) 3. Local app (`app.config`, `app.utils`) ```python import os import asyncio from typing import Optional import httpx from fastapi import APIRouter, HTTPException from app.config import get_settings from app.models import DownloadTask, DownloadStatus ``` ### Formatting - **Line length**: 120 chars max - **Indentation**: 4 spaces - **Blank lines**: 2 between top-level, 1 between inline ### Type Annotations - Use explicit types - Use `Optional[X]` not `X | None` - Use `list[X]`, `dict[X, Y]` ```python # Good async def get_download_link(url: str, target_filename: Optional[str] = None) -> tuple[str, str]: results: list[dict[str, str]] = [] # Avoid async def get_download_link(url, target_filename=None): results = [] ``` ### Naming Conventions | Element | Convention | Example | |---------|------------|---------| | Modules | snake_case | `download_manager.py` | | Classes | PascalCase | `DownloadManager` | | Functions | snake_case | `get_download_link()` | | Constants | UPPER_SNAKE | `MAX_PARALLEL_DOWNLOADS` | | Variables | snake_case | `download_task` | | Enums | PascalCase | `DownloadStatus` | | Enum values | UPPER_SNAKE | `DownloadStatus.PENDING` | ### Async/Await - Always use for I/O operations - Close clients properly to avoid leaks ```python async def close(self): await self.client.aclose() ``` ### Error Handling - Use try/except for recoverable errors - Raise specific exceptions (`HTTPException`, `ValueError`) - Never use empty except blocks - Log errors appropriately ```python try: result = await client.get(url) except httpx.TimeoutException: logger.warning(f"Request timeout for {url}") raise HTTPException(status_code=504, detail="Request timeout") ``` ### File Operations - Always sanitize filenames: `app.utils.sanitize_filename()` - Validate paths: `app.utils.is_safe_filename()` ### Testing - Use pytest with pytest-asyncio - Mark tests: `@pytest.mark.unit`, `@pytest.mark.integration`, `@pytest.mark.slow`, `@pytest.mark.network` - Tests in `test_api.py` are auto-marked as integration, others as unit - Use fixtures from `tests/conftest.py` ```python @pytest.mark.unit @pytest.mark.asyncio async def test_download_manager(): manager = DownloadManager(max_parallel=3) assert manager.max_parallel == 3 # Mark slow tests @pytest.mark.slow async def test_full_download_flow(): ... # Mark tests requiring network @pytest.mark.network async def test_external_api(): ... ``` ### Security - Never hardcode secrets - use environment variables - Validate all inputs (URLs, filenames) - Use HMAC for webhook verification when configured - Limit CORS origins - never use `*` in production ## Architecture Patterns ### Three-Tier Downloader Architecture The project uses a three-tier downloader system: 1. **Anime Catalogs** (`app/downloaders/anime_sites/`) - `animesama.py` - Anime-Sama (primary) - `animeultime.py` - Anime-Ultime - `nekosama.py` - Neko-Sama - `vostfree.py` - Vostfree - `frenchmanga.py` - French-Manga 2. **Series Catalogs** (`app/downloaders/series_sites/`) - `fs7.py` - French Stream 3. **Video Players** (`app/downloaders/video_players/`) - `sibnet.py`, `doodstream.py`, `vidmoly.py`, `uqload.py` - `uptobox.py`, `unfichier.py`, `rapidfile.py` - `sendvid.py`, `lpayer.py`, `vidzy.py`, `luluv.py` - `oneupload.py`, `smoothpre.py` Each tier has a base class and factory pattern. When adding providers: 1. Inherit from appropriate base class (`base.py`) 2. Implement required methods (`search_anime`, `get_episodes`, `get_download_link`) 3. Register in `app/providers.py` 4. Add URL detection patterns **URL Convention**: Pipe-separated format preserves metadata: ``` video_url|anime_page_url|episode_title ``` ### Core Modules | Module | Purpose | |--------|---------| | `app/watchlist.py` | Episode tracking & auto-download | | `app/auto_download_scheduler.py` | APScheduler for periodic checks | | `app/episode_checker.py` | New episode detection | | `app/sonarr_handler.py` | Sonarr webhook integration | | `app/recommendation_engine.py` | Personalized anime recommendations | | `app/favorites.py` | User favorites management | | `app/auth.py` | JWT authentication | | `app/download_manager.py` | Download queue management | ## Key Files | File | Purpose | |------|---------| | `main.py` | FastAPI app, all API endpoints | | `app/config.py` | Pydantic Settings configuration | | `app/download_manager.py` | Download queue & task management | | `app/utils.py` | `sanitize_filename`, `is_safe_filename` | | `app/auth.py` | JWT auth, user management | | `app/providers.py` | Provider definitions & URL detection | | `app/models/__init__.py` | Core Pydantic models | | `app/models/watchlist.py` | Watchlist models | | `app/models/sonarr.py` | Sonarr integration models | | `app/models/auth.py` | Authentication models | ## Frontend Architecture ### JavaScript Modules (`static/js/`) | Module | Purpose | |--------|---------| | `main.js` | Application entry point | | `api.js` | API client functions | | `auth.js` | Authentication handling | | `tabs.js` | Tab navigation | | `anime.js` | Anime search & display | | `anime-details.js` | Anime detail views | | `watchlist.js` | Watchlist API calls | | `watchlist-ui.js` | Watchlist UI rendering | | `downloads.js` | Download management UI | | `recommendations.js` | Recommendations display | | `series-search.js` | TV series search | | `utils.js` | Utility functions | ### Templates (`templates/`) | Template | Purpose | |----------|---------| | `base.html` | Base layout with CSS/JS imports | | `index.html` | Main SPA interface | | `login.html` | Login/register page | | `watchlist.html` | Watchlist management page | | `player.html` | Video player page | | `components/` | Reusable HTML components | ## Configuration - Use `.env` from `.env.example` - `JWT_SECRET_KEY` must change in production - Config files stored in `config/`: - `users.json` - User database - `watchlist.json` - Watchlist data - `watchlist_settings.json` - Auto-download settings - `sonarr.json` - Sonarr integration config - `sonarr_mappings.json` - Series to anime mappings ## API Endpoints Overview ### Authentication - `POST /api/auth/register` - Register new user - `POST /api/auth/login` - Login, get JWT token - `GET /api/auth/me` - Get current user info - `POST /api/auth/logout` - Logout (client-side) ### Downloads - `POST /api/download` - Create download task - `GET /api/downloads` - List all downloads - `GET /api/download/{task_id}` - Get download status - `POST /api/download/{task_id}/pause` - Pause download - `POST /api/download/{task_id}/resume` - Resume download - `DELETE /api/download/{task_id}` - Cancel/delete download - `GET /api/download/{task_id}/file` - Download completed file ### Anime Search & Metadata - `GET /api/anime/search` - Search across all anime providers - `GET /api/series/search` - Search TV series providers - `GET /api/anime/metadata` - Get detailed anime metadata - `GET /api/anime/episodes` - Get episode list - `GET /api/anime/seasons` - Get available seasons - `POST /api/anime/download-season` - Download all episodes ### Watchlist - `GET /api/watchlist` - List watchlist items - `POST /api/watchlist` - Add to watchlist - `PUT /api/watchlist/{item_id}` - Update watchlist item - `DELETE /api/watchlist/{item_id}` - Remove from watchlist - `GET /api/watchlist/settings` - Get auto-download settings - `PUT /api/watchlist/settings` - Update settings - `POST /api/watchlist/check` - Trigger manual episode check ### Favorites - `GET /api/favorites` - List favorites - `POST /api/favorites` - Add to favorites - `DELETE /api/favorites/{anime_id}` - Remove from favorites - `POST /api/favorites/toggle` - Toggle favorite status ### Recommendations - `GET /api/recommendations` - Get personalized recommendations - `GET /api/releases/latest` - Get latest releases - `GET /api/releases/seasonal` - Get seasonal anime ### Sonarr Integration - `POST /api/sonarr/webhook` - Receive Sonarr webhooks - `GET /api/sonarr/mappings` - List Sonarr mappings - `POST /api/sonarr/mappings` - Create mapping - `DELETE /api/sonarr/mappings/{series_id}` - Delete mapping ## Dependencies ### Core - `fastapi` - Web framework - `uvicorn` - ASGI server - `httpx` - Async HTTP client - `aiohttp` - Alternative HTTP client - `pydantic` / `pydantic-settings` - Data validation & settings ### Scraping & Parsing - `beautifulsoup4` - HTML parsing - `lxml` - XML/HTML parser - `jieba` - Chinese text segmentation ### Authentication - `python-jose` - JWT handling - `passlib[bcrypt]` - Password hashing ### Scheduler - `apscheduler` - Job scheduling for auto-downloads ### Cryptography - `pycryptodome` - AES decryption for video players ### Testing - `pytest` + `pytest-asyncio` - Async test support - `pytest-cov` - Coverage reporting - `pytest-mock` - Mocking utilities - `pytest-timeout` - Test timeout protection - `pytest-html` - HTML test reports ## CI/CD ### GitHub Actions This project uses GitHub Actions for continuous integration. The workflow is defined in `.github/workflows/ci.yml`. **Workflow Features:** - Runs on push and pull requests to `main` and `dev` branches - Tests on Python 3.11 and 3.12 - Excludes slow tests by default (`-m "not slow"`) - Generates HTML coverage reports - Runs linting with ruff - Runs type checking with mypy **Artifacts:** - Coverage reports are uploaded as artifacts after each run - Access via the "Actions" tab on GitHub **Running locally:** ```bash # Run tests (excluding slow tests) pytest -m "not slow" --cov=app --cov-report=html # Run all tests including slow ones pytest # Run only unit tests pytest -m "unit" # Run only integration tests pytest -m "integration" # Run linting pip install ruff && ruff check app/ # Run type checking pip install mypy && mypy app/ ```