# Downloaders (app/downloaders/) ## OVERVIEW 3-tier scraper architecture: anime catalogs → series catalogs → video players. Factory pattern routes URLs through each tier. ## STRUCTURE ``` downloaders/ ├── __init__.py # get_downloader(url) — 3-tier factory + GenericDownloader ├── base.py # Legacy BaseDownloader (kept for compat) ├── anime_sites/ # Anime streaming catalogs (see anime_sites/AGENTS.md) │ ├── __init__.py # get_anime_site(url) factory │ ├── base.py # BaseAnimeSite abstract class │ └── *.py # 5 anime providers ├── series_sites/ # TV series catalogs (see series_sites/AGENTS.md) │ ├── __init__.py # get_series_site(url) factory │ ├── base.py # BaseSeriesSite abstract class │ └── fs7.py # 1 series provider └── video_players/ # File hosting extractors (see video_players/AGENTS.md) ├── __init__.py # get_video_player(url) factory ├── base.py # BaseVideoPlayer abstract class └── *.py # 13 video player handlers ``` ## WHERE TO LOOK | Need | File | Notes | |------|------|-------| | Route URL to downloader | `__init__.py:32` | `get_downloader(url)` tries anime→series→video→generic | | Add anime provider | `anime_sites/` | Inherit BaseAnimeSite, register in anime_sites/__init__.py | | Add series provider | `series_sites/` | Inherit BaseSeriesSite, register in series_sites/__init__.py | | Add video player | `video_players/` | Inherit BaseVideoPlayer, register in video_players/__init__.py | | Provider domains/icons | `app/providers.py` | Separate from downloader code | ## CONVENTIONS **URL pipe format**: `video_url|anime_page_url|episode_title` — metadata preserved through tiers. Anime/series sites return player URLs (not direct downloads). Video players extract final download links. **Factory chain**: `get_downloader()` → `get_anime_site()` → `get_series_site()` → `get_video_player()` → `GenericDownloader`. **New provider checklist**: 1) Create .py inheriting base class, 2) Implement required methods, 3) Add to `__init__.py` factory list, 4) Add to `app/providers.py`. ## ANTI-PATTERNS - Do NOT return None from `get_download_link()` — raise Exception - Do NOT use sync `requests` — always `httpx.AsyncClient` - Do NOT forget `await self.close()` — causes resource leaks - Do NOT skip `sanitize_filename()` on extracted filenames - Do NOT hardcode User-Agent per player — use base class headers