- Migrated monolithic main.py to feature-scoped routers in app/routers/ - Added GEMINI.md for project context and AI instructional guidelines - Updated README.md with a comprehensive modernization plan (SQL migration, robust scraping DSL, frontend modernization) - Improved authentication with cookie support and modular JS - Updated test suite and documentation
35 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
Ohm Stream Downloader is a FastAPI-based web application for downloading anime episodes and media files from various file hosting services (1fichier, Doodstream, Rapidfile, Uptobox, VidMoly, SendVid, Sibnet, Lpayer, Vidzy, LuLuvid, Uqload) and streaming platforms (Anime-Sama, Neko-Sama, Anime-Ultime, Vostfree, French-Manga, FS7). It features a modern web interface, parallel downloads, pause/resume support, video streaming, personalized recommendations, JWT authentication, and Sonarr webhook integration for automated downloads.
Development Commands
# Create and activate virtual environment
python3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
# Install JavaScript test dependencies (optional, for frontend tests)
npm install
# Run development server (auto-reload)
uvicorn main:app --reload --host 0.0.0.0 --port 3000
# Access web interface
# Open http://localhost:3000/web in browser
# --- Python Tests (pytest) ---
# Run all tests
pytest
# Run tests with coverage report
pytest --cov=app --cov-report=html
# Run only unit tests (fast, isolated)
pytest -m "unit"
# Run only integration tests
pytest -m "integration"
# Exclude slow tests
pytest -m "not slow"
# Verbose output
pytest -v
# Show print debugging
pytest -s
# Run specific test file
pytest tests/test_sonarr.py -v
# Run specific test class
pytest tests/test_sonarr.py::TestSonarrHandler -v
# Run specific test
pytest tests/test_sonarr.py::TestSonarrHandler::test_add_mapping -v
# --- JavaScript Tests (vitest) ---
# Run all JavaScript tests
npm test
# Run JavaScript tests in watch mode
npm run test:watch
# Run specific JavaScript test file
npx vitest run static/js/__tests__/auth-api.test.js
Architecture
Directory Structure:
Ohm_streaming/
├── main.py # FastAPI application startup & middleware
├── app/
│ ├── routers/ # FastAPI routers (API endpoints organized by feature)
│ │ ├── __init__.py # Exports all routers
│ │ ├── router_auth.py # /api/auth/* routes (user authentication)
│ │ ├── router_anime.py # /api/anime/* and /api/series/* routes
│ │ ├── router_downloads.py # /api/download/* routes
│ │ ├── router_favorites.py # /api/favorites/* routes
│ │ ├── router_player.py # /player/* and /watch/* routes
│ │ ├── router_recommendations.py # /api/recommendations and /api/releases routes
│ │ ├── router_root.py # / and /web routes
│ │ ├── router_sonarr.py # /api/sonarr/* and /api/webhook/sonarr routes
│ │ ├── router_static.py # /static/* and /video/* routes
│ │ └── router_watchlist.py # /api/watchlist/* routes
│ ├── models/ # Pydantic models (DownloadTask, AnimeMetadata, Sonarr, etc.)
│ ├── downloaders/ # Host-specific downloaders (organized structure)
│ │ ├── base.py # BaseDownloader abstract class (legacy, kept for compatibility)
│ │ ├── __init__.py # Factory function (three-tier: anime sites → series sites → video players)
│ │ ├── anime_sites/ # Anime streaming sites (catalogs)
│ │ │ ├── base.py # BaseAnimeSite abstract class
│ │ │ ├── __init__.py # Anime site factory
│ │ │ ├── animesama.py # Anime-Sama (anime provider)
│ │ │ ├── animeultime.py # Anime-Ultime (anime provider)
│ │ │ ├── nekosama.py # Neko-Sama (anime provider)
│ │ │ ├── vostfree.py # Vostfree (anime provider)
│ │ │ └── frenchmanga.py # French-Manga (anime provider)
│ │ ├── series_sites/ # TV series streaming sites (catalogs)
│ │ │ ├── base.py # BaseSeriesSite abstract class
│ │ │ ├── __init__.py # Series site factory
│ │ │ └── fs7.py # FS7 (French Stream)
│ │ └── video_players/ # File hosting services (players)
│ │ ├── base.py # BaseVideoPlayer abstract class
│ │ ├── __init__.py # Video player factory
│ │ ├── unfichier.py # 1fichier.com handler
│ │ ├── doodstream.py # Doodstream handler
│ │ ├── rapidfile.py # Rapidfile handler
│ │ ├── uptobox.py # Uptobox handler
│ │ ├── vidmoly.py # VidMoly handler
│ │ ├── sendvid.py # SendVid handler
│ │ ├── sibnet.py # Sibnet handler
│ │ ├── lpayer.py # Lpayer handler
│ │ ├── vidzy.py # Vidzy handler
│ │ ├── luluv.py # LuLuvid handler
│ │ └── uqload.py # Uqload handler
│ ├── providers.py # Provider configuration (domains, icons, colors)
│ ├── config.py # Environment-based configuration (Pydantic Settings)
│ ├── utils.py # Security utilities (sanitize_filename, is_safe_filename)
│ ├── download_manager.py # Manages download queue, progress, parallel downloads
│ ├── favorites.py # Favorites management system (JSON-based)
│ ├── recommendation_engine.py # Analyzes download history for personalized recommendations
│ ├── recommendations.py # Fetches latest releases from anime sources
│ ├── kitsu_api.py # Kitsu API integration for anime metadata
│ ├── sonarr_handler.py # Sonarr webhook integration handler
│ ├── auth.py # JWT authentication system
│ └── models/
│ ├── __init__.py # Core models (DownloadTask, AnimeMetadata, etc.)
│ └── sonarr.py # Sonarr Pydantic models
├── downloads/ # Downloaded files storage
├── templates/
│ ├── index.html # Main web interface
│ ├── player.html # Video player page
│ └── base.html # Base template
├── static/ # Static assets (CSS, JS, images)
│ ├── js/
│ │ ├── __tests__/ # JavaScript tests (vitest)
│ │ │ ├── auth-api.test.js
│ │ │ ├── auth-utils.test.js
│ │ │ └── smoke.test.js
│ │ ├── auth.js # Authentication UI logic
│ │ ├── auth-api.js # Authentication API client
│ │ ├── auth-ui.js # Authentication UI components
│ │ └── auth-utils.js # Authentication utilities
├── tests/ # Python test suite with fixtures
│ ├── e2e/ # End-to-end tests (Playwright)
└── vitest.config.js # Vitest configuration for JS tests
Core Components:
0. Configuration (app/config.py)
Settingsclass using Pydantic Settings for environment-based configuration- Loads from
.envfile with sensible defaults - Provides
get_settings()function for accessing configuration globally
1. DownloadManager (app/download_manager.py)
- Manages all download tasks with parallel download limit (default: 3 concurrent)
- Handles pause/resume/cancel operations
- Tracks progress, speed, and file chunks for resume support
- Uses
asyncio.Semaphoreto limit concurrent downloads - Auto-restores completed downloads from disk on server startup
2. Downloaders (app/downloaders/)
Architecture: The downloaders are organized into three categories with separate base classes:
Anime Sites (app/downloaders/anime_sites/):
- Provide anime catalogs, metadata, and episode listings
- Link to video players for actual file hosting
- Inherit from
BaseAnimeSiteabstract class - Factory:
get_anime_site(url)inanime_sites/__init__.py - Implement:
search_anime(),get_episodes(),get_anime_metadata(),get_download_link()
Series Sites (app/downloaders/series_sites/):
- Provide TV series catalogs, metadata, and episode listings
- Similar to anime sites but for general TV series content
- Inherit from
BaseSeriesSiteabstract class - Factory:
get_series_site(url)inseries_sites/__init__.py - Implement:
search_anime(),get_episodes(),get_anime_metadata(),get_download_link()
Video Players (app/downloaders/video_players/):
- Host actual video files and provide direct download links
- Extract URLs from embedded players and handle file downloads
- Inherit from
BaseVideoPlayerabstract class - Factory:
get_video_player(url)invideo_players/__init__.py - Implement:
get_download_link(url, target_filename=None)
Three-Tier Factory Pattern:
get_downloader(url)in main__init__.pychecks: anime sites → series sites → video players- Falls back to
GenericDownloaderif no match - This separation allows anime/series sites to delegate to video players for actual downloads
BaseAnimeSite Interface:
can_handle(url)- Check if this anime site can handle the URLsearch_anime(query, lang)- Search for anime, returns list with title, url, cover_imageget_episodes(anime_url, lang)- Get episode list with episode_number, url, title, hostget_anime_metadata(anime_url)- Get metadata dict (synopsis, genres, rating, release_year, studio, poster_image, total_episodes, status)get_download_link(url)- Get video player URL from episode page (NOT direct download link)
BaseSeriesSite Interface:
can_handle(url)- Check if this series site can handle the URLsearch_anime(query, lang)- Search for series, returns list with title, url, cover_image, langget_episodes(anime_url, lang)- Get episode list with episode_number, url, title, hostget_anime_metadata(anime_url)- Get metadata dict (title, synopsis, genres, rating, release_year, studio, poster_image, total_episodes, status, languages)get_download_link(url)- Get video player URL from episode page (NOT direct download link)
BaseVideoPlayer Interface:
can_handle(url)- Check if this player can handle the URLget_download_link(url, target_filename=None)- Extract direct download link and filename- Note:
target_filenameparameter is optional but MUST be supported for VidMoly/SendVid compatibility - Always use
sanitize_filename()on extracted filenames!
- Note:
Key Patterns:
- All downloaders use httpx.AsyncClient for HTTP requests
- BeautifulSoup with lxml for HTML parsing
- Async/await throughout for non-blocking I/O
- Fuzzy search using jieba for Chinese text segmentation and typo tolerance
- Security: Filename sanitization enforced via
app.utilsfunctions
URL Format Convention:
- Pipe-separated format:
video_url|anime_page_url|episode_title- Preserves metadata through the download process
- Example:
https://vidmoly.to/abc123|https://anime-sama.si/catalogue/naruto/s1/vostfr/|Episode+1 target_filenameparameter allows anime/series sites to suggest filenames- Video players extract the final download link and filename
3. Provider Configuration (app/providers.py)
ANIME_PROVIDERS- Anime streaming sites configurationFILE_HOSTS- File hosting services configuration- Each provider has: name, domains, icon, color, url_pattern
detect_provider_from_url(url)- Identify provider from URL
4. Router Architecture (app/routers/)
Overview:
- API endpoints have been migrated from a monolithic
main.py(2200+ lines) to modular routers - Each router is responsible for a specific feature domain
- Routers are imported and registered in
main.pyusing FastAPI's APIRouter - This improves maintainability, testability, and code organization
Router Organization:
router_auth.py-/api/auth/*- User registration, login, token refresh, profile managementrouter_anime.py-/api/anime/*and/api/series/*- Search, metadata, episodes, downloadsrouter_downloads.py-/api/download/*- Download task management (pause, resume, cancel, delete)router_favorites.py-/api/favorites/*- Favorites CRUD operationsrouter_player.py-/player/*and/watch/*- Video player endpointsrouter_recommendations.py-/api/recommendationsand/api/releases/latest- Personalization and latest releasesrouter_root.py-/and/web- Root and main web interface routesrouter_sonarr.py-/api/sonarr/*and/api/webhook/sonarr- Sonarr integration and webhooksrouter_static.py-/static/*and/video/*- Static file serving and video streamingrouter_watchlist.py-/api/watchlist/*- Watchlist and auto-download scheduler management
Key Benefits:
- Clear separation of concerns - each router handles one feature area
- Easier testing - routers can be tested independently
- Better navigation - smaller files focused on specific functionality
- Shared dependencies via FastAPI's dependency injection (e.g.,
download_manager,get_current_user_from_token) - No URL changes - frontend remains fully compatible
When Adding New Endpoints:
- Identify which router the endpoint belongs to based on its URL prefix
- Add the endpoint function to the appropriate router file in
app/routers/ - Use FastAPI dependencies for shared services (
download_manager,templates, authentication) - Follow existing patterns for error handling and response models
5. API Endpoints
Download Management:
POST /api/download- Create new download taskGET /api/downloads- List all download tasksGET /api/download/{task_id}- Get task detailsPOST /api/download/{task_id}/pause- Pause downloadPOST /api/download/{task_id}/resume- Resume downloadDELETE /api/download/{task_id}- Delete task (keeps completed files)GET /api/download/{task_id}/file- Download completed file
Anime Features:
GET /api/anime/search- Unified search across all providersGET /api/anime/metadata- Get anime metadataGET /api/anime/episodes- Get episode listPOST /api/anime/download- Download single episodePOST /api/anime/download-season- Download entire season
Video Streaming:
GET /video/{task_id}- Stream video with Range supportGET /stream/{filename}- Stream by filenameGET /player/{task_id}- Video player pageGET /watch/{filename}- Player by filename
Recommendations & Favorites:
GET /api/recommendations- Personalized recommendationsGET /api/releases/latest- Latest anime releasesGET /api/favorites- List favoritesPOST /api/favorites- Add favoriteDELETE /api/favorites/{anime_id}- Remove favorite
Sonarr Integration:
POST /api/webhook/sonarr- Receive Sonarr webhooksGET /api/sonarr/config- Get Sonarr configurationPUT /api/sonarr/config- Update Sonarr configurationGET /api/sonarr/mappings- List Sonarr to anime mappingsPOST /api/sonarr/mappings- Create/update mappingDELETE /api/sonarr/mappings/{series_id}- Delete mappingGET /api/sonarr/search- Search anime for mappingGET /api/sonarr/episodes- Get episode listGET /api/sonarr/suggest- Suggest anime matchesPOST /api/sonarr/download- Manually trigger download
6. Web Interface
- Single-page app at
/web(templates/index.html) - Auto-refreshes every second to show progress
- Video player with seeking support (HTTP Range headers)
- Dark theme with gradients and animations
7. Security Utilities (app/utils.py)
sanitize_filename(filename, max_length=255)- Sanitize filenames to prevent path traversal- Removes dangerous characters:
\ / : * ? " < > | - Strips path separators and leading dots/dashes
- Limits filename length while preserving extension
- Removes dangerous characters:
is_safe_filename(filename)- Validate filename safety- Checks for path traversal patterns (
..,/,\) - Detects absolute paths and drive letters
- Used throughout the codebase for file operations
- Checks for path traversal patterns (
8. Authentication System (app/auth.py)
- UserManager - JSON-based user storage in
config/users.json- User registration with bcrypt password hashing
- Password truncated to 72 bytes (bcrypt limitation)
- User authentication and last login tracking
- JWT Tokens - Stateless authentication with refresh token support
- Access tokens: 24-hour expiration (configurable via
ACCESS_TOKEN_EXPIRE_MINUTES) - Refresh tokens: 30-day expiration (stored in
config/refresh_tokens.json) - HS256 algorithm with JWT_SECRET_KEY (change in production!)
- Token verification and user extraction
- Access tokens: 24-hour expiration (configurable via
- Password Security
- bcrypt hashing with passlib
- Automatic deprecated scheme migration
- JWT Secret Validation (in
app/config.py)- Default secret is rejected at startup (security enforcement)
- Minimum 32 characters required
- Use
Settings.generate_secret()to generate secure secrets
- Configuration
JWT_SECRET_KEYenvironment variable (MUST be changed from default)- Users stored in
config/users.json - Refresh tokens stored in
config/refresh_tokens.json
Authentication Endpoints:
POST /api/auth/register- User registrationPOST /api/auth/login- Login and receive JWT tokenGET /api/auth/me- Get current user profilePUT /api/auth/me- Update user profile
9. Recommendation Engine (app/recommendation_engine.py)
- Analyzes download history to generate personalized recommendations
- Tracks genre preferences and viewing patterns
- Scores anime based on user's download history
- Used by
/api/recommendationsendpoint
10. Kitsu API (app/kitsu_api.py)
- Integrates with Kitsu anime database for metadata
- Fetches anime information by title or ID
- Provides enriched metadata (synopsis, genres, ratings, poster images)
- Used as fallback when provider metadata is incomplete
11. Watchlist & Auto-Download System
WatchlistManager (app/watchlist.py):
- JSON-based storage in
config/watchlist.json - Per-user watchlist management (multi-tenant)
- CRUD operations for tracked anime
- Statistics and queries
- Settings management in
config/watchlist_settings.json
EpisodeChecker (app/episode_checker.py):
- Checks for new episodes for anime in watchlist
- Downloads episodes automatically when detected
- Integrates with existing downloaders
- Handles errors and retries
- Lazy initialization to avoid circular imports
AutoDownloadScheduler (app/auto_download_scheduler.py):
- APScheduler-based periodic checking
- Configurable intervals (1-168 hours)
- Start/stop control via API
- Next run tracking
- Background task execution
Watchlist Models (app/models/watchlist.py):
WatchlistItem- Tracked anime with settingsWatchlistStatus- ACTIVE, PAUSED, COMPLETED, ARCHIVEDQualityPreference- AUTO, 1080p, 720p, 480pWatchlistSettings- Global configurationAutoDownloadResult- Operation results
Watchlist Endpoints:
GET /api/watchlist- List user's watchlist (with status filter)POST /api/watchlist- Add anime to watchlistGET /api/watchlist/{item_id}- Get specific itemPUT /api/watchlist/{item_id}- Update watchlist itemDELETE /api/watchlist/{item_id}- Remove from watchlistPOST /api/watchlist/{item_id}/check- Check specific animePOST /api/watchlist/check-all- Check all due itemsPOST /api/watchlist/{item_id}/pause- Pause trackingPOST /api/watchlist/{item_id}/resume- Resume trackingGET /api/watchlist/settings- Get global settingsPUT /api/watchlist/settings- Update settingsGET /api/watchlist/stats- Get watchlist statisticsGET /api/watchlist/scheduler/status- Get scheduler statusPOST /api/watchlist/scheduler/start- Start schedulerPOST /api/watchlist/scheduler/stop- Stop scheduler
12. Pydantic Models (app/models/)
__init__.py- Core models:DownloadStatus- Enum for task states (PENDING, DOWNLOADING, PAUSED, COMPLETED, FAILED, CANCELLED)HostType- Enum for file host types (RAPIDFILE, UNFICHIER, DOODSTREAM, OTHER)DownloadTask- Main task model with progress trackingDownloadRequest- Request model for creating downloadsAnimeMetadata- Anime information (synopsis, genres, rating, release_year, studio, etc.)AnimeSearchResult- Enhanced search result with metadata
sonarr.py- Sonarr-specific models:SonarrWebhookPayload- Complete webhook payload schemaSonarrEventType- Enum for event types (Grab, Download, Rename, Delete, Test)SonarrMapping- Mapping between Sonarr series and anime providersSonarrConfig- Webhook configuration (enabled, secret, auto-download, etc.)
auth.py- Authentication models:UserCreate- User registration requestUserLogin- Login requestUser- User profileToken- JWT token response
watchlist.py- Watchlist models:WatchlistItem- Tracked anime itemWatchlistItemCreate- Create requestWatchlistItemUpdate- Update requestWatchlistStatus- Status enumWatchlistSettings- Global settings
Test Structure
Python Test Organization (tests/):
conftest.py- Pytest configuration and fixturestest_models.py- Pydantic model teststest_downloaders.py- Downloader teststest_download_manager.py- DownloadManager teststest_favorites.py- Favorites system teststest_api.py- FastAPI endpoint teststest_sonarr.py- Sonarr integration teststest_anime_sama_seasons.py- Anime-Sama season handling teststest_translate_api.py- Translation API teststest_delete_and_restore.py- Delete and restore functionality teststest_french_manga.py- French-Manga provider teststest_jwt_secret_validation.py- JWT secret key validation teststest_token_refresh.py- Token refresh functionality tests
JavaScript Test Organization (static/js/tests/):
smoke.test.js- Basic smoke testsauth-api.test.js- Authentication API client testsauth-utils.test.js- Authentication utility function tests- Uses Vitest with jsdom environment
- Coverage reports generated in
htmlcov/(shared with Python tests)
Fixtures in conftest.py:
temp_dir- Temporary directorytemp_download_dir- Temporary download directorydownload_manager- DownloadManager instancefavorites_manager- FavoritesManager instancemock_httpx_client- Mock for httpx.AsyncClientsample_download_task- Sample task datasample_anime_metadata- Sample metadata
Test Markers:
unit- Unit tests (isolated, fast) - auto-appliedintegration- Integration tests (API endpoints) - auto-appliedasyncio- Async tests - auto-appliedslow- Slow tests - manualnetwork- Requires network - manual
pytest.ini Configuration:
- Auto-applies markers for async and integration tests
- Coverage enabled by default (
--cov=app) - HTML coverage report generated in
htmlcov/ - Verbose output with local variables in tracebacks
- 300-second timeout for tests
asyncio_mode = autofor async test support
Running Single Test:
# Run specific test file
pytest tests/test_sonarr.py -v
# Run specific test class
pytest tests/test_sonarr.py::TestSonarrHandler -v
# Run specific test
pytest tests/test_sonarr.py::TestSonarrHandler::test_add_mapping -v
Adding New Host Support
To add support for a new file hosting service:
- Create new file in
app/downloaders/video_players/(e.g.,myhost.py) - Inherit from
BaseVideoPlayer - Implement required methods (
can_handle,get_download_link) - Add to imports in
app/downloaders/video_players/__init__.py - Add to
playerslist inget_video_player() - Add configuration to
FILE_HOSTSinapp/providers.py
Example:
from .base import BaseVideoPlayer
from bs4 import BeautifulSoup
class MyHostDownloader(BaseVideoPlayer):
def can_handle(self, url: str) -> bool:
return "myhost.com" in url.lower()
async def get_download_link(self, url: str, target_filename: Optional[str] = None) -> tuple[str, str]:
soup = BeautifulSoup(await self._fetch_page(url), 'lxml')
# ... extraction logic ...
# IMPORTANT: Always sanitize filenames!
from app.utils import sanitize_filename
filename = sanitize_filename(extracted_filename)
return download_url, filename
async def close(self):
# IMPORTANT: Always close the HTTP client
await self.client.aclose()
Important:
- Always close the HTTP client in your downloader to avoid resource leaks
- Use
sanitize_filename()fromapp.utilswhen extracting filenames from URLs - Use
is_safe_filename()to validate filenames before file operations - The
target_filenameparameter is required for compatibility with anime/series sites
Adding New Series Site
To add a new TV series streaming provider (similar to anime sites but for general TV series):
- Create new file in
app/downloaders/series_sites/(e.g.,mysite.py) - Inherit from
BaseSeriesSite - Implement series-specific methods:
search_anime(query, lang)- Return list of series with title, url, cover_image, langget_episodes(anime_url, lang)- Return list of episodesget_anime_metadata(anime_url)- Return metadata dict (should include languages field)get_download_link(url)- Return video player URL from episode page
- Add to imports in
app/downloaders/series_sites/__init__.py - Add to
siteslist inget_series_site()
BaseSeriesSite is nearly identical to BaseAnimeSite but designed for general TV series content rather than anime-specific content.
Sonarr Integration
The application includes full Sonarr webhook support for automated anime downloads.
Architecture
SonarrHandler (app/sonarr_handler.py):
- Processes incoming webhooks from Sonarr
- Manages series mappings (Sonarr TVDB ID → Anime Provider URL)
- Supports HMAC SHA256 signature verification for security
- Auto-triggers downloads on Grab events
- Provides search and suggestion APIs for mapping setup
Sonarr Models (app/models/sonarr.py):
SonarrWebhookPayload- Complete webhook payload schemaSonarrEventType- Enum for event types (Grab, Download, Rename, Delete, Test)SonarrMapping- Mapping between Sonarr series and anime providersSonarrConfig- Webhook configuration (enabled, secret, auto-download, etc.)
Workflow
-
Setup in Sonarr:
- Configure webhook: Settings > Connect > Sonarr > Webhook
- URL:
http://your-server:3000/api/webhook/sonarr - Enable "Grab" event
-
Create Mappings:
- Get Sonarr series TVDB ID from series details
- Search anime:
GET /api/sonarr/search?q={title} - Create mapping:
POST /api/sonarr/mappings
-
Automatic Download:
- Sonarr grabs new episode → Sends webhook
- Ohm Stream Downloader receives webhook
- Looks up mapping by TVDB ID
- Finds matching episode on anime provider
- Creates and starts download task
Configuration Files
config/sonarr.json- Webhook configurationconfig/sonarr_mappings.json- Series mappings
Example Mapping
{
"sonarr_series_id": 79644,
"sonarr_title": "Naruto Shippuden",
"anime_provider": "anime-sama",
"anime_url": "https://anime-sama.si/catalogue/naruto-shippuden/saison1/vostfr/",
"anime_title": "Naruto Shippuden",
"lang": "vostfr",
"quality_preference": "1080p",
"auto_download": true
}
Security
- Optional HMAC SHA256 signature verification
- Configure secret in both Sonarr and Ohm Stream Downloader
- Enable with
verify_hmac: truein config
Testing
- Test endpoint:
POST /api/webhook/test/sonarr - Manual trigger:
POST /api/sonarr/download - Get suggestions:
GET /api/sonarr/suggest?sonarr_title={title}
Documentation: See docs/SONARR_INTEGRATION.md for complete setup guide.
Adding New Anime Provider
To add a new anime streaming provider:
- Create new file in
app/downloaders/anime_sites/(e.g.,mysite.py) - Inherit from
BaseAnimeSite - Implement anime-specific methods:
search_anime(query, lang)- Return list of anime with title, url, cover_imageget_episodes(anime_url, lang)- Return list of episodesget_anime_metadata(anime_url)- Return metadata dictget_download_link(url)- Return video player URL from episode page
- Add to imports in
app/downloaders/anime_sites/__init__.py - Add to
siteslist inget_anime_site() - Add to
ANIME_PROVIDERSinapp/providers.py - Update
main.pyto include in unified search
Metadata should include:
- synopsis, genres, rating, release_year, studio, poster_image, total_episodes, status
Working with Routers
Adding New Endpoints:
- Identify which router handles the URL prefix you need
- Edit the appropriate router file in
app/routers/ - Use FastAPI's APIRouter pattern with proper dependencies
- Import the router in
app/routers/__init__.pyif creating a new router - Register the router in
main.py
Example - Adding a new endpoint to router_anime.py:
from fastapi import APIRouter, Depends
from app.download_manager import DownloadManager
router = APIRouter(prefix="/api/anime", tags=["anime"])
@router.get("/custom-endpoint")
async def custom_endpoint(
download_manager: DownloadManager = Depends(lambda: download_manager)
):
# Your logic here
return {"status": "success"}
Common Dependencies:
download_manager: DownloadManager = Depends(lambda: download_manager)- Access download queuecurrent_user: User = Depends(get_current_user_from_token)- Authenticated usertemplates: Jinja2Templates = Depends(lambda: templates)- Template rendering
Router Organization Principles:
- Group related endpoints by URL prefix
- Keep routers focused on a single feature area
- Use dependency injection for shared services
- Tag routers for OpenAPI documentation
Configuration
The application uses environment variables for configuration via app/config.py (Pydantic Settings).
Environment Variables (.env):
# Copy the example file
cp .env.example .env
# Edit .env to configure:
APP_NAME=Ohm Stream Downloader # Application name
DEBUG=false # Debug mode
HOST=0.0.0.0 # Server host
PORT=3000 # Server port
DOWNLOAD_DIR=downloads # Download storage location
MAX_PARALLEL_DOWNLOADS=3 # Maximum concurrent downloads
CHUNK_SIZE=1048576 # Download chunk size (1MB)
CORS_ORIGINS=... # Comma-separated allowed origins
HTTP_TIMEOUT=10.0 # HTTP request timeout (seconds)
DOWNLOAD_TIMEOUT=300 # Download timeout (seconds)
LOG_LEVEL=INFO # Logging level
JWT_SECRET_KEY=change-me-in-production # JWT signing key (MUST be changed, min 32 chars)
# Generate a secure key with: python -c "from app.config import Settings; print(Settings.generate_secret())"
Configuration Files:
.env- Environment configuration (create from .env.example)config/users.json- User authentication database (created automatically)config/refresh_tokens.json- Refresh token storage (created automatically)config/sonarr.json- Sonarr webhook configuration (created automatically)config/sonarr_mappings.json- Sonarr to anime provider mappings (created automatically)config/watchlist.json- User watchlist items (created automatically)config/watchlist_settings.json- Watchlist global settings (created automatically)config/.gitkeep- Ensures config directory is tracked in git- Example files:
config/sonarr.example.json,config/sonarr_mappings.example.json
Documentation:
README.md- User-facing features and roadmapCLAUDE.md- This file (developer guide)docs/SONARR_INTEGRATION.md- Complete Sonarr setup guidedocs/SONARR_IMPLEMENTATION.md- Technical implementation summarydocs/IMPROVEMENTS_2024-01-24.md- Recent security and quality improvementsdocs/WATCHLIST_AUTO_DOWNLOAD.md- Watchlist system documentation
Security
Filename Sanitization (app/utils.py):
sanitize_filename()- Removes dangerous characters (\ / : * ? " < > |)is_safe_filename()- Validates against path traversal patterns- Used throughout the codebase for all file operations
- Prevents
../../../etc/passwdstyle attacks - Limits filename length to 255 characters
CORS Configuration:
- Restricted origins (not
*) in production - Specific allowed methods (GET, POST, PUT, DELETE, PATCH, OPTIONS)
- Configured in
main.pyvia environment variables
Authentication:
- JWT token-based authentication with 24-hour access token expiration
- Refresh token support with 30-day expiration
- bcrypt password hashing with passlib
- Passwords truncated to 72 bytes (bcrypt limitation)
- JWT secret key validation (minimum 32 characters, default rejected)
- Credentials stored in
config/users.json - Refresh tokens stored in
config/refresh_tokens.json
Key Implementation Details
Resume Support:
- Downloads use HTTP Range headers to resume from last byte
- Files downloaded in 1MB chunks
- Partial files cleaned up on cancel
- Resume position tracked in
downloaded_bytesfield
Domain Handling:
- Anime providers use dynamic domain detection (e.g., Anime-Sama fetches current domain from anime-sama.pw)
- Multiple domains per provider supported in configuration
- Domain detection via
detect_provider_from_url(url)in providers.py
Task Lifecycle:
- PENDING → DOWNLOADING → PAUSED / COMPLETED / CANCELLED / FAILED
- Active downloads tracked in
active_downloadsdict - All tasks stored in
tasksdict with UUID keys - Completed files preserved when deleting tasks (only partial files removed)
Video Streaming:
- Range header support for seeking in video player
- Serves from
/downloadsdirectory via StaticFiles - Video extensions: .mp4, .mkv, .avi, .mov, .wmv, .flv, .webm
Error Handling:
- Graceful degradation with status tracking
- Network errors caught and reported in task status
- Automatic retry on resume
- Downloads > 1MB considered complete to skip small error files
Dependencies
Core:
- fastapi - Web framework
- uvicorn - ASGI server
- httpx - Async HTTP client
- beautifulsoup4, lxml - HTML parsing
- aiofiles - Async file operations
- jieba - Chinese text segmentation for fuzzy search
- passlib[bcrypt] - Password hashing
- python-jose[cryptography] - JWT token handling
- apscheduler - Task scheduling for auto-download
- pydantic-settings - Environment-based configuration
Python Testing:
- pytest - Test framework
- pytest-asyncio - Async test support
- pytest-cov - Coverage reporting
- pytest-mock - Mocking support
- pytest-timeout - Test timeout handling
- pytest-html - HTML test reports
JavaScript Testing (optional, for frontend):
- vitest - Fast JavaScript test runner
- jsdom - DOM implementation for tests
- @playwright/test - End-to-end browser testing