""" Product Showcase Studio — FastAPI backend. Video pipeline aligned with python-backend (KIE Veo, image hosting, merge). """ from contextlib import asynccontextmanager from pathlib import Path import os import shutil from fastapi import FastAPI from fastapi.staticfiles import StaticFiles from fastapi.middleware.cors import CORSMiddleware from api.video_generation import router as video_router from api.seedance_generation import router as seedance_router from api.image_service import router as image_router from api.video_export import router as export_router from api.showcase_prompts import router as showcase_router from api.scraper_routes import router as scraper_router from api.gpt_image_frames import router as gpt_image_router from utils.env_load import load_app_env from utils.public_url import get_public_base_url, get_server_port_str from utils.storage import cleanup_old_files load_app_env() @asynccontextmanager async def lifespan(app: FastAPI): print("Starting Product Showcase API…") os.makedirs("storage/images", exist_ok=True) os.makedirs("storage/videos", exist_ok=True) public = get_public_base_url() print(f"Public URL for callbacks/hosted images: {public}") if not os.getenv("KIE_API_KEY"): if os.getenv("REPLICATE_API_TOKEN") or os.getenv("REPLICATE_API_KEY"): print("Info: KIE_API_KEY not set — Seedance can use Replicate fallback when token is set.") else: print("Warning: KIE_API_KEY not set — KIE video generation will fail (set REPLICATE_API_TOKEN for Seedance fallback).") if not os.getenv("OPENAI_API_KEY"): print("Info: OPENAI_API_KEY not set — shot plan and GPT Image frames are unavailable.") else: print(f"GPT Image model for first frames: {os.getenv('GPT_IMAGE_MODEL', 'gpt-image-1.5')}") yield cleanup_old_files() app = FastAPI( title="Product Showcase API", description="Cinematic product videos: shot planning + KIE Veo / Seedance 2 + export", version="1.0.0", lifespan=lifespan, ) origins = [ "http://localhost:5173", "http://127.0.0.1:5173", "http://localhost:3000", "http://127.0.0.1:3000", ] extra = (os.getenv("CORS_ALLOWED_ORIGINS") or "").strip() if extra: origins = [o.strip().rstrip("/") for o in extra.split(",") if o.strip()] app.add_middleware( CORSMiddleware, allow_origins=origins, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) app.include_router(showcase_router, prefix="/api") app.include_router(scraper_router, prefix="/api") app.include_router(gpt_image_router, prefix="/api") app.include_router(video_router, prefix="/api") app.include_router(seedance_router, prefix="/api") app.include_router(image_router, prefix="/api") app.include_router(export_router, prefix="/api") _REPO_ROOT = Path(__file__).resolve().parent.parent _FRONTEND_DIST = _REPO_ROOT / "frontend" / "dist" @app.get("/health") def health(): return { "status": "healthy", "service": "product-showcase", "kie_configured": bool(os.getenv("KIE_API_KEY")), "replicate_configured": bool( (os.getenv("REPLICATE_API_TOKEN") or os.getenv("REPLICATE_API_KEY") or "").strip() ), "openai_configured": bool(os.getenv("OPENAI_API_KEY")), "gpt_image_model": os.getenv("GPT_IMAGE_MODEL", "gpt-image-1.5"), "ffmpeg_available": bool(shutil.which("ffmpeg")), "ffprobe_available": bool(shutil.which("ffprobe")), "public_base_url": get_public_base_url(), "server_port": int(get_server_port_str()), } if _FRONTEND_DIST.is_dir(): app.mount( "/", StaticFiles(directory=str(_FRONTEND_DIST), html=True), name="frontend", ) if __name__ == "__main__": import uvicorn port = int(get_server_port_str()) uvicorn.run("main:app", host="0.0.0.0", port=port, reload=True)