""" Ohm Stream Downloader - FastAPI Application Main application file with startup configuration and middleware. All API routes have been migrated to app/routers/ for better maintainability. """ import asyncio import logging import uuid from datetime import datetime from pathlib import Path from fastapi import FastAPI, Request, Response from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates from app.download_manager import DownloadManager from app.models import DownloadTask, DownloadStatus # Configure logging logger = logging.getLogger(__name__) PERMISSIONS_POLICY_VALUE = ( "accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), " "camera=(), display-capture=(), document-domain=(), encrypted-media=(), " "fullscreen=*, gamepad=(), geolocation=(), gyroscope=(), " "magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=*, " "publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), " "usb=(), web-share=(), xr-spatial-tracking=()" ) # Initialize FastAPI app app = FastAPI(title="Ohm Stream Downloader") app.add_middleware( CORSMiddleware, allow_origins=[ "http://localhost:3000", "http://127.0.0.1:3000", "http://192.168.1.204:3000", "http://192.168.1.204", "http://192.168.1.200:3000", "http://192.168.1.200", "http://192.168.5.127:3000", "http://192.168.5.127", ], allow_credentials=True, allow_methods=["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"], allow_headers=["*"], ) @app.middleware("http") async def permissions_policy_middleware(request: Request, call_next): response: Response = await call_next(request) response.headers["Permissions-Policy"] = PERMISSIONS_POLICY_VALUE return response # Initialize download manager download_manager = DownloadManager(download_dir="downloads", max_parallel=3) # Initialize episode checker with download manager from app.episode_checker import episode_checker episode_checker.set_download_manager(download_manager) @app.on_event("startup") async def startup_event(): """Initialize services on application startup""" # Create database tables if they don't exist from app.database import create_db_and_tables create_db_and_tables() logger.info("Database tables initialized") from app.sonarr_handler import get_sonarr_handler sonarr_handler = get_sonarr_handler() sonarr_handler.set_download_manager(download_manager) from app.auto_download_scheduler import auto_download_scheduler auto_download_scheduler.start() # Run initial provider health check in background from app.providers_manager import providers_manager asyncio.create_task(providers_manager.check_all_health()) logger.info("Application started: Sonarr handler and scheduler initialized") def restore_completed_downloads(): """Scan downloads directory and restore completed download tasks""" download_dir = Path("downloads") if not download_dir.exists(): return video_extensions = {".mp4", ".mkv", ".avi", ".mov", ".wmv", ".flv", ".webm"} for file_path in download_dir.iterdir(): if file_path.is_file() and file_path.suffix.lower() in video_extensions: if file_path.stat().st_size < 1024 * 1024: continue filename = file_path.name file_size = file_path.stat().st_size task_id = str(uuid.uuid4()) task = DownloadTask( id=task_id, url="", filename=filename, host="other", status=DownloadStatus.COMPLETED, progress=100.0, downloaded_bytes=file_size, total_bytes=file_size, speed=0.0, file_path=str(file_path), created_at=datetime.fromtimestamp(file_path.stat().st_ctime), completed_at=datetime.fromtimestamp(file_path.stat().st_mtime), ) download_manager.tasks[task_id] = task logger.info(f"Restored completed download: {filename}") # Restore completed downloads on startup restore_completed_downloads() # Mount static files and templates app.mount("/static", StaticFiles(directory="static"), name="static") app.mount("/downloads", StaticFiles(directory="downloads"), name="downloads") templates = Jinja2Templates(directory="templates") # ==================== INCLUDE ROUTERS ==================== from app.routers import ( auth_router, downloads_router, anime_router, favorites_router, recommendations_router, watchlist_router, sonarr_router, player_router, static_router, root_router, settings_router, admin_router, ) # Include routers app.include_router(root_router) app.include_router(auth_router) app.include_router(downloads_router) app.include_router(anime_router) app.include_router(favorites_router) app.include_router(recommendations_router) app.include_router(watchlist_router) app.include_router(sonarr_router) app.include_router(player_router) app.include_router(static_router) app.include_router(settings_router) app.include_router(admin_router) if __name__ == "__main__": import uvicorn uvicorn.run("main:app", host="0.0.0.0", port=3000, reload=True)