""" FastAPI Backend for React Video Editor Handles video generation, image processing, and API integrations """ from fastapi import FastAPI, HTTPException, Request, Response from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import StreamingResponse, JSONResponse from fastapi.staticfiles import StaticFiles from contextlib import asynccontextmanager import uvicorn import os from dotenv import load_dotenv from api.video_generation import router as video_router from api.image_service import router as image_router from api.frame_extraction import router as frame_router from api.prompt_generation import router as prompt_router from api.video_export import router as export_router from api.replicate_service import router as replicate_router from api.whisper_service import router as whisper_router from api.auth import router as auth_router from utils.storage import cleanup_old_files # Load environment variables load_dotenv('.env.local') # Lifespan context manager for startup/shutdown @asynccontextmanager async def lifespan(app: FastAPI): """Handle startup and shutdown events""" # Startup print("๐Ÿš€ Starting FastAPI server...") print(f"๐Ÿ“ Storage directory: {os.path.join(os.getcwd(), 'storage')}") # Create storage directory if it doesn't exist os.makedirs('storage/images', exist_ok=True) os.makedirs('storage/videos', exist_ok=True) # Check for API keys kie_api_key = os.getenv('KIE_API_KEY') if not kie_api_key: print("โš ๏ธ Warning: KIE_API_KEY not set. Video generation will fail.") print(" Get your API key at: https://kie.ai/api-key") else: print("โœ… KIE_API_KEY configured") gemini_api_key = os.getenv('VITE_GEMINI_API_KEY') if gemini_api_key: print("โœ… GEMINI_API_KEY configured") openai_api_key = os.getenv('OPENAI_API_KEY') if openai_api_key: print("โœ… OPENAI_API_KEY configured (GPT-4o prompt generation)") replicate_api_token = os.getenv('REPLICATE_API_TOKEN') if replicate_api_token: print("โœ… REPLICATE_API_TOKEN configured") else: print("โš ๏ธ Warning: REPLICATE_API_TOKEN not set. Replicate video generation will fail.") # Check environment mode environment = os.getenv('ENVIRONMENT', 'dev').lower() is_dev_mode = environment == 'dev' or environment == 'development' if is_dev_mode: print("๐Ÿงช DEV MODE: Segment generation limited to 2 segments") else: print("๐Ÿš€ PROD MODE: Generating all segments") yield # Shutdown print("๐Ÿ›‘ Shutting down FastAPI server...") cleanup_old_files() # Create FastAPI app app = FastAPI( title="React Video Editor API", description="Python backend for video generation and processing", version="1.0.0", lifespan=lifespan ) # CORS middleware app.add_middleware( CORSMiddleware, allow_origins=["*"], # In production, specify exact origins allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Include routers app.include_router(auth_router, prefix="/api") app.include_router(video_router, prefix="/api") app.include_router(image_router, prefix="/api") app.include_router(frame_router, prefix="/api") app.include_router(prompt_router, prefix="/api") app.include_router(export_router, prefix="/api") app.include_router(replicate_router, prefix="/api") app.include_router(whisper_router, prefix="/api") # Health check endpoints (must be before catch-all route) @app.get("/health") async def health_check(): """Health check endpoint""" environment = os.getenv('ENVIRONMENT', 'dev').lower() is_dev_mode = environment == 'dev' or environment == 'development' return { "status": "healthy", "environment": environment, "is_dev_mode": is_dev_mode, "max_segments": 2 if is_dev_mode else None, "kie_api_configured": bool(os.getenv('KIE_API_KEY')), "replicate_api_configured": bool(os.getenv('REPLICATE_API_TOKEN')), "gemini_api_configured": bool(os.getenv('VITE_GEMINI_API_KEY')), "openai_api_configured": bool(os.getenv('OPENAI_API_KEY')) } # Serve static files (frontend) in production frontend_dist_path = os.path.join(os.getcwd(), "frontend", "dist") if os.path.exists(frontend_dist_path): # Serve static files app.mount("/assets", StaticFiles(directory=os.path.join(frontend_dist_path, "assets")), name="assets") # Root endpoint - serve frontend or API info @app.get("/") async def root(): """Root endpoint - serve frontend in production, API info in dev""" index_path = os.path.join(frontend_dist_path, "index.html") if os.path.exists(index_path): from fastapi.responses import FileResponse return FileResponse(index_path) # Fallback if frontend not built return { "status": "healthy", "service": "React Video Editor API", "version": "1.0.0" } # Serve frontend index.html for all non-API routes # This must be last to not interfere with API routes @app.get("/{full_path:path}") async def serve_frontend(full_path: str): """Serve frontend for all non-API routes""" # Don't serve frontend for API routes or docs - let FastAPI handle these if (full_path.startswith("api/") or full_path.startswith("docs") or full_path.startswith("redoc") or full_path.startswith("openapi.json") or full_path == "health"): raise HTTPException(status_code=404, detail="Not found") # Serve index.html for frontend routes index_path = os.path.join(frontend_dist_path, "index.html") if os.path.exists(index_path): from fastapi.responses import FileResponse return FileResponse(index_path) raise HTTPException(status_code=404, detail="Frontend not found") else: # Root endpoint when frontend not built @app.get("/") async def root(): """Root endpoint - health check""" return { "status": "healthy", "service": "React Video Editor API", "version": "1.0.0" } if __name__ == "__main__": # Support both SERVER_PORT and PORT (Hugging Face uses PORT) port = int(os.getenv('PORT') or os.getenv('SERVER_PORT', 4000)) uvicorn.run( "main:app", host="0.0.0.0", port=port, reload=True, log_level="info" )