64 lines
2.8 KiB
Python
64 lines
2.8 KiB
Python
from contextlib import asynccontextmanager
|
|
|
|
from fastapi import FastAPI
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from fastapi.responses import HTMLResponse
|
|
|
|
from app.api.routes import jobs, clips, renders
|
|
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI):
|
|
yield
|
|
|
|
|
|
app = FastAPI(
|
|
title="ClipForge",
|
|
description="Self-hosted AI video clipper",
|
|
version="0.1.0",
|
|
lifespan=lifespan,
|
|
)
|
|
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=["*"],
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
app.include_router(jobs.router, prefix="/api")
|
|
app.include_router(clips.router, prefix="/api")
|
|
app.include_router(renders.router, prefix="/api")
|
|
|
|
|
|
@app.get("/", response_class=HTMLResponse)
|
|
async def root():
|
|
return """<!DOCTYPE html><html><head><title>ClipForge</title>
|
|
<style>*{margin:0;padding:0;box-sizing:border-box}body{font-family:system-ui,-apple-system,sans-serif;background:#0a0a0a;color:#e0e0e0;min-height:100vh;display:flex;justify-content:center;align-items:center}
|
|
main{max-width:640px;padding:2rem;text-align:center}h1{font-size:2.5rem;margin-bottom:.5rem}
|
|
.sub{color:#888;margin-bottom:2rem}.card{background:#141414;border:1px solid #2a2a2a;border-radius:12px;padding:1.5rem;text-align:left;margin-bottom:1rem}
|
|
.card h3{color:#fff;margin-bottom:.75rem}code{background:#1a1a1a;padding:2px 6px;border-radius:4px;font-size:.85rem;color:#4ade80}
|
|
.endpoint{margin:.5rem 0;line-height:1.6}.method{color:#f59e0b;font-weight:700;margin-right:.5rem}
|
|
.status{display:inline-block;width:8px;height:8px;border-radius:50%;background:#4ade80;margin-right:6px}
|
|
</style></head><body><main>
|
|
<h1>ClipForge</h1><p class="sub">Self-hosted AI video clipper</p>
|
|
<div class="card"><h3><span class="status"></span>API Endpoints</h3>
|
|
<div class="endpoint"><span class="method">POST</span><code>/api/jobs</code> Create job (YouTube URL)</div>
|
|
<div class="endpoint"><span class="method">POST</span><code>/api/jobs/upload</code> Upload video file</div>
|
|
<div class="endpoint"><span class="method">GET</span><code>/api/jobs/{id}</code> Job status</div>
|
|
<div class="endpoint"><span class="method">GET</span><code>/api/jobs/{id}/clips</code> Get clips</div>
|
|
<div class="endpoint"><span class="method">GET</span><code>/api/jobs/{id}/progress</code> SSE progress stream</div>
|
|
<div class="endpoint"><span class="method">POST</span><code>/api/clips/{id}/render</code> Render clip</div>
|
|
<div class="endpoint"><span class="method">GET</span><code>/api/renders/{id}/download</code> Download render</div>
|
|
</div>
|
|
<div class="card"><h3>Interactive Docs</h3>
|
|
<p style="line-height:1.6"><a href="/docs" style="color:#60a5fa">/docs</a> — Swagger UI<br>
|
|
<a href="/redoc" style="color:#60a5fa">/redoc</a> — ReDoc</p>
|
|
</div></main></body></html>"""
|
|
|
|
|
|
@app.get("/health")
|
|
async def health():
|
|
return {"status": "ok", "service": "clipforge"}
|