| 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=["*"], |
| ) |
|
|
| |
| app.include_router(video.router, prefix="/api/video", tags=["video"]) |
|
|
| |
| os.makedirs("uploads", exist_ok=True) |
| os.makedirs("outputs", exist_ok=True) |
|
|
| |
| 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" |
| } |
|
|