import os import socket from dotenv import load_dotenv load_dotenv() from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from routers import video from cleanup import start_cleanup_scheduler def write_cookies_from_secret(): """Decode YT_COOKIES_B64 secret and write to disk at startup.""" b64 = os.environ.get("YT_COOKIES_B64", "") if not b64: return import base64 cookies_path = os.environ.get("YT_COOKIES_PATH", "/home/user/app/cookies.txt") with open(cookies_path, "wb") as f: f.write(base64.b64decode(b64)) print(f"[startup] cookies.txt written to {cookies_path}") @asynccontextmanager async def lifespan(app: FastAPI): """Start background cleanup on startup, cleanup on shutdown.""" write_cookies_from_secret() start_cleanup_scheduler() yield app = FastAPI( title="ClipForge API", version="2.0.0", description="AI-powered video clip generator. Turns long videos into short viral clips with karaoke-style captions.", lifespan=lifespan, ) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=False, allow_methods=["*"], allow_headers=["*"], ) # API routes app.include_router(video.router, prefix="/api/video", tags=["video"]) # Create working directories os.makedirs("uploads", exist_ok=True) os.makedirs("outputs", exist_ok=True) # Serve output clips as static files app.mount("/outputs", StaticFiles(directory="outputs"), name="outputs") @app.get("/", tags=["root"]) def root(): return { "app": "ClipForge API", "version": "2.0.0", "docs": "/docs", "health": "/health", } @app.get("/health", tags=["health"]) def health_check(): import requests as req def http_check(url): try: r = req.get(url, timeout=8, headers={"User-Agent": "Mozilla/5.0"}, allow_redirects=True) return r.status_code except Exception as e: return str(e)[:60] candidates = { "cobalt.privacyredirect.com": "https://cobalt.privacyredirect.com/", "inv.nadeko.net": "https://inv.nadeko.net/api/v1/videos/dQw4w9WgXcQ", "yewtu.be": "https://yewtu.be/api/v1/videos/dQw4w9WgXcQ", "invidious.fdn.fr": "https://invidious.fdn.fr/api/v1/videos/dQw4w9WgXcQ", "iv.datura.network": "https://iv.datura.network/api/v1/videos/dQw4w9WgXcQ", "pipedapi.kavin.rocks": "https://pipedapi.kavin.rocks/streams/dQw4w9WgXcQ", "piped-api.garudalinux.org": "https://piped-api.garudalinux.org/streams/dQw4w9WgXcQ", } return { "status": "ok", "connectivity": {name: http_check(url) for name, url in candidates.items()}, "cookies": "present" if os.path.exists(os.environ.get("YT_COOKIES_PATH", "/home/user/app/cookies.txt")) else "missing", "environment": "huggingface" }