JakgritB Claude Sonnet 4.6 commited on
Commit ·
8e8e9d6
1
Parent(s): cf17161
fix(hf-space): replace full app with minimal landing page
Browse filesHF Space now shows a branded landing page only — no live demo,
no app routes exposed. The actual app stays on localhost/AMD Cloud.
Reverts main.py SPA catch-all added in previous commits.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Dockerfile +3 -29
- backend/app/main.py +0 -13
- landing.py +75 -0
Dockerfile
CHANGED
|
@@ -1,35 +1,9 @@
|
|
| 1 |
-
# Stage 1: build React frontend
|
| 2 |
-
FROM node:22-alpine AS frontend-builder
|
| 3 |
-
WORKDIR /frontend
|
| 4 |
-
COPY frontend/package*.json ./
|
| 5 |
-
RUN npm ci
|
| 6 |
-
COPY frontend/ ./
|
| 7 |
-
# Empty string = same-origin API calls (FastAPI serves both)
|
| 8 |
-
ENV VITE_API_BASE_URL=""
|
| 9 |
-
RUN npm run build
|
| 10 |
-
|
| 11 |
-
# Stage 2: FastAPI backend (CPU, demo mode for HF Space)
|
| 12 |
FROM python:3.11-slim
|
| 13 |
WORKDIR /app
|
| 14 |
|
| 15 |
-
RUN
|
| 16 |
-
apt-get install -y --no-install-recommends ffmpeg git curl && \
|
| 17 |
-
rm -rf /var/lib/apt/lists/*
|
| 18 |
-
|
| 19 |
-
COPY backend/pyproject.toml ./
|
| 20 |
-
RUN pip install --no-cache-dir -e "."
|
| 21 |
-
|
| 22 |
-
COPY backend/app ./app
|
| 23 |
|
| 24 |
-
|
| 25 |
-
COPY --from=frontend-builder /frontend/dist ./static
|
| 26 |
-
|
| 27 |
-
RUN mkdir -p /app/data
|
| 28 |
-
|
| 29 |
-
ENV DEMO_MODE=true
|
| 30 |
-
ENV STORAGE_DIR=/app/data
|
| 31 |
-
ENV FRONTEND_ORIGIN=https://elevenclip-ai.hf.space
|
| 32 |
|
| 33 |
EXPOSE 7860
|
| 34 |
-
|
| 35 |
-
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
FROM python:3.11-slim
|
| 2 |
WORKDIR /app
|
| 3 |
|
| 4 |
+
RUN pip install --no-cache-dir fastapi uvicorn
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
|
| 6 |
+
COPY landing.py ./landing.py
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
EXPOSE 7860
|
| 9 |
+
CMD ["uvicorn", "landing:app", "--host", "0.0.0.0", "--port", "7860"]
|
|
|
backend/app/main.py
CHANGED
|
@@ -1,5 +1,3 @@
|
|
| 1 |
-
from pathlib import Path
|
| 2 |
-
|
| 3 |
from fastapi import BackgroundTasks, FastAPI, File, Form, HTTPException, UploadFile
|
| 4 |
from fastapi.middleware.cors import CORSMiddleware
|
| 5 |
from fastapi.responses import FileResponse
|
|
@@ -34,17 +32,6 @@ app.add_middleware(
|
|
| 34 |
)
|
| 35 |
app.mount("/media", StaticFiles(directory=settings.storage_dir), name="media")
|
| 36 |
|
| 37 |
-
_STATIC_DIR = Path(__file__).parent.parent / "static"
|
| 38 |
-
if _STATIC_DIR.is_dir():
|
| 39 |
-
app.mount("/assets", StaticFiles(directory=_STATIC_DIR / "assets"), name="frontend-assets")
|
| 40 |
-
|
| 41 |
-
@app.get("/{full_path:path}", include_in_schema=False)
|
| 42 |
-
async def serve_spa(full_path: str) -> FileResponse:
|
| 43 |
-
candidate = _STATIC_DIR / full_path
|
| 44 |
-
if candidate.is_file():
|
| 45 |
-
return FileResponse(candidate)
|
| 46 |
-
return FileResponse(_STATIC_DIR / "index.html")
|
| 47 |
-
|
| 48 |
|
| 49 |
@app.get("/health", response_model=HealthResponse)
|
| 50 |
async def health() -> HealthResponse:
|
|
|
|
|
|
|
|
|
|
| 1 |
from fastapi import BackgroundTasks, FastAPI, File, Form, HTTPException, UploadFile
|
| 2 |
from fastapi.middleware.cors import CORSMiddleware
|
| 3 |
from fastapi.responses import FileResponse
|
|
|
|
| 32 |
)
|
| 33 |
app.mount("/media", StaticFiles(directory=settings.storage_dir), name="media")
|
| 34 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
|
| 36 |
@app.get("/health", response_model=HealthResponse)
|
| 37 |
async def health() -> HealthResponse:
|
landing.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import FastAPI
|
| 2 |
+
from fastapi.responses import HTMLResponse
|
| 3 |
+
|
| 4 |
+
app = FastAPI()
|
| 5 |
+
|
| 6 |
+
_HTML = """<!DOCTYPE html>
|
| 7 |
+
<html lang="en">
|
| 8 |
+
<head>
|
| 9 |
+
<meta charset="UTF-8">
|
| 10 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 11 |
+
<title>ElevenClip.AI</title>
|
| 12 |
+
<style>
|
| 13 |
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
| 14 |
+
body {
|
| 15 |
+
min-height: 100vh;
|
| 16 |
+
display: flex;
|
| 17 |
+
align-items: center;
|
| 18 |
+
justify-content: center;
|
| 19 |
+
background: #0f0f13;
|
| 20 |
+
color: #f0f0f0;
|
| 21 |
+
font-family: system-ui, -apple-system, sans-serif;
|
| 22 |
+
}
|
| 23 |
+
.card { text-align: center; max-width: 560px; padding: 3rem 2rem; }
|
| 24 |
+
.emoji { font-size: 4rem; margin-bottom: 1.5rem; }
|
| 25 |
+
h1 { font-size: 2.4rem; font-weight: 700; margin-bottom: 0.5rem; }
|
| 26 |
+
.tagline { font-size: 1.1rem; color: #a0a0b8; margin-bottom: 2rem; line-height: 1.6; }
|
| 27 |
+
.badge {
|
| 28 |
+
display: inline-block;
|
| 29 |
+
background: linear-gradient(135deg, #7c3aed, #db2777);
|
| 30 |
+
border-radius: 999px;
|
| 31 |
+
padding: 0.4rem 1.2rem;
|
| 32 |
+
font-size: 0.85rem;
|
| 33 |
+
font-weight: 600;
|
| 34 |
+
letter-spacing: 0.03em;
|
| 35 |
+
margin-bottom: 2.5rem;
|
| 36 |
+
}
|
| 37 |
+
.stack { display: flex; flex-wrap: wrap; gap: 0.5rem; justify-content: center; margin-bottom: 2rem; }
|
| 38 |
+
.tag {
|
| 39 |
+
background: #1e1e2e;
|
| 40 |
+
border: 1px solid #2e2e42;
|
| 41 |
+
border-radius: 6px;
|
| 42 |
+
padding: 0.3rem 0.75rem;
|
| 43 |
+
font-size: 0.8rem;
|
| 44 |
+
color: #c0c0d8;
|
| 45 |
+
}
|
| 46 |
+
.footer { font-size: 0.8rem; color: #555; margin-top: 1rem; }
|
| 47 |
+
</style>
|
| 48 |
+
</head>
|
| 49 |
+
<body>
|
| 50 |
+
<div class="card">
|
| 51 |
+
<div class="emoji">🎬</div>
|
| 52 |
+
<h1>ElevenClip.AI</h1>
|
| 53 |
+
<p class="tagline">
|
| 54 |
+
AI-powered short-clip studio for creators.<br>
|
| 55 |
+
Long video in → ready-to-post clips out.
|
| 56 |
+
</p>
|
| 57 |
+
<div class="badge">AMD Developer Hackathon 2026</div>
|
| 58 |
+
<div class="stack">
|
| 59 |
+
<span class="tag">AMD MI300X</span>
|
| 60 |
+
<span class="tag">ROCm</span>
|
| 61 |
+
<span class="tag">Whisper Large V3</span>
|
| 62 |
+
<span class="tag">Qwen2.5-7B</span>
|
| 63 |
+
<span class="tag">Qwen2-VL-7B</span>
|
| 64 |
+
<span class="tag">FastAPI</span>
|
| 65 |
+
<span class="tag">React</span>
|
| 66 |
+
</div>
|
| 67 |
+
<p class="footer">Demo coming soon — May 4–10, 2026</p>
|
| 68 |
+
</div>
|
| 69 |
+
</body>
|
| 70 |
+
</html>"""
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
@app.get("/", response_class=HTMLResponse)
|
| 74 |
+
async def root() -> HTMLResponse:
|
| 75 |
+
return HTMLResponse(_HTML)
|