File size: 6,559 Bytes
91d209c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a54c80e
91d209c
87bfb33
91d209c
a54c80e
 
91d209c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
"""
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"
    )