""" FastAPI application for Aniworld anime download manager. This module provides the main FastAPI application with proper CORS configuration, middleware setup, static file serving, and Jinja2 template integration. """ from pathlib import Path from typing import Optional import uvicorn from fastapi import FastAPI, HTTPException, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from src.config.settings import settings # Import core functionality from src.core.SeriesApp import SeriesApp from src.server.api.anime import router as anime_router from src.server.api.auth import router as auth_router from src.server.api.config import router as config_router from src.server.api.download import router as download_router from src.server.api.websocket import router as websocket_router from src.server.controllers.error_controller import ( not_found_handler, server_error_handler, ) # Import controllers from src.server.controllers.health_controller import router as health_router from src.server.controllers.page_controller import router as page_router from src.server.middleware.auth import AuthMiddleware from src.server.middleware.error_handler import register_exception_handlers from src.server.services.progress_service import get_progress_service from src.server.services.websocket_service import get_websocket_service # Initialize FastAPI app app = FastAPI( title="Aniworld Download Manager", description="Modern web interface for Aniworld anime download management", version="1.0.0", docs_url="/api/docs", redoc_url="/api/redoc" ) # Configure CORS app.add_middleware( CORSMiddleware, allow_origins=["*"], # Configure appropriately for production allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Configure static files STATIC_DIR = Path(__file__).parent / "web" / "static" app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static") # Attach authentication middleware (token parsing + simple rate limiter) app.add_middleware(AuthMiddleware, rate_limit_per_minute=5) # Include routers app.include_router(health_router) app.include_router(page_router) app.include_router(auth_router) app.include_router(config_router) app.include_router(anime_router) app.include_router(download_router) app.include_router(websocket_router) # Register exception handlers register_exception_handlers(app) # Global variables for application state series_app: Optional[SeriesApp] = None @app.on_event("startup") async def startup_event(): """Initialize application on startup.""" global series_app try: # Initialize SeriesApp with configured directory if settings.anime_directory: series_app = SeriesApp(settings.anime_directory) # Initialize progress service with websocket callback progress_service = get_progress_service() ws_service = get_websocket_service() async def broadcast_callback( message_type: str, data: dict, room: str ): """Broadcast progress updates via WebSocket.""" message = { "type": message_type, "data": data, } await ws_service.manager.broadcast_to_room(message, room) progress_service.set_broadcast_callback(broadcast_callback) print("FastAPI application started successfully") except Exception as e: print(f"Error during startup: {e}") @app.on_event("shutdown") async def shutdown_event(): """Cleanup on application shutdown.""" print("FastAPI application shutting down") @app.exception_handler(404) async def handle_not_found(request: Request, exc: HTTPException): """Custom 404 handler.""" return await not_found_handler(request, exc) @app.exception_handler(500) async def handle_server_error(request: Request, exc: Exception): """Custom 500 handler.""" return await server_error_handler(request, exc) if __name__ == "__main__": uvicorn.run( "fastapi_app:app", host="127.0.0.1", port=8000, reload=True, log_level="info" )