Commit ·
0af7cf6
1
Parent(s): 2f9de1b
- .dockerignore +15 -0
- Dockerfile +25 -0
- README.md +46 -5
- backend/main.py +14 -0
- backend/utils/public_url.py +4 -1
.dockerignore
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.git
|
| 2 |
+
**/.venv
|
| 3 |
+
**/__pycache__
|
| 4 |
+
**/*.pyc
|
| 5 |
+
**/node_modules
|
| 6 |
+
frontend/dist
|
| 7 |
+
.env
|
| 8 |
+
.env.*
|
| 9 |
+
**/.env.local
|
| 10 |
+
storage
|
| 11 |
+
showcase_output
|
| 12 |
+
reference_videos
|
| 13 |
+
*.mp4
|
| 14 |
+
*.png
|
| 15 |
+
**/.DS_Store
|
Dockerfile
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# syntax=docker/dockerfile:1
|
| 2 |
+
# Hugging Face Spaces (Docker) — FastAPI + Vite build. Listens on $PORT (default 7860).
|
| 3 |
+
|
| 4 |
+
FROM node:22-bookworm-slim AS frontend-build
|
| 5 |
+
WORKDIR /app/frontend
|
| 6 |
+
COPY frontend/package.json frontend/package-lock.json ./
|
| 7 |
+
RUN npm ci
|
| 8 |
+
COPY frontend/ ./
|
| 9 |
+
RUN npm run build
|
| 10 |
+
|
| 11 |
+
FROM python:3.12-slim-bookworm
|
| 12 |
+
RUN apt-get update && apt-get install -y --no-install-recommends ffmpeg \
|
| 13 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 14 |
+
|
| 15 |
+
WORKDIR /app
|
| 16 |
+
COPY backend/requirements.txt /app/backend/requirements.txt
|
| 17 |
+
RUN pip install --no-cache-dir -r /app/backend/requirements.txt
|
| 18 |
+
COPY backend/ /app/backend/
|
| 19 |
+
COPY --from=frontend-build /app/frontend/dist /app/frontend/dist
|
| 20 |
+
|
| 21 |
+
ENV PYTHONPATH=/app/backend
|
| 22 |
+
ENV PORT=7860
|
| 23 |
+
|
| 24 |
+
WORKDIR /app/backend
|
| 25 |
+
CMD ["sh", "-c", "exec uvicorn main:app --host 0.0.0.0 --port ${PORT:-7860}"]
|
README.md
CHANGED
|
@@ -1,10 +1,51 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
colorTo: indigo
|
| 6 |
sdk: docker
|
| 7 |
-
|
| 8 |
---
|
| 9 |
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: Product Showcase Studio
|
| 3 |
+
emoji: 🎬
|
| 4 |
+
colorFrom: slate
|
| 5 |
colorTo: indigo
|
| 6 |
sdk: docker
|
| 7 |
+
app_port: 7860
|
| 8 |
---
|
| 9 |
|
| 10 |
+
# Product Showcase Studio
|
| 11 |
+
|
| 12 |
+
Cinematic product video pipeline: **shot planning** → **KIE Veo / Seedance** → **FFmpeg merge**, with optional **GPT Image** keyframes and **product URL** import.
|
| 13 |
+
|
| 14 |
+
## Deploy on Hugging Face Spaces
|
| 15 |
+
|
| 16 |
+
1. **Create a Space** → **Docker** → link this repository (or push these files to a new repo under your account).
|
| 17 |
+
2. **Settings → Repository secrets** (or **Variables and secrets**): add at least:
|
| 18 |
+
- **`VITE_API_BASE_URL`** — your Space’s public app URL, same as the address in the browser when the app loads, with no trailing slash (for example `https://YOUR-USERNAME-YOUR-SPACE-NAME.hf.space`). This is required so **callback URLs** and **hosted images** use the correct origin.
|
| 19 |
+
- **`KIE_API_KEY`** — if you use KIE Veo.
|
| 20 |
+
- **`OPENAI_API_KEY`** — optional, for LLM shot plans and GPT Image keyframes.
|
| 21 |
+
- **`REPLICATE_API_TOKEN`** — optional Seedance fallback when KIE is not used.
|
| 22 |
+
3. Wait for the build (Node build + Python image). Open the Space URL when the build is **Running**.
|
| 23 |
+
|
| 24 |
+
The container runs **uvicorn** on **`PORT`** (Spaces set this; default **7860**). **FFmpeg** is installed for the merge step.
|
| 25 |
+
|
| 26 |
+
### Local Docker (optional)
|
| 27 |
+
|
| 28 |
+
```bash
|
| 29 |
+
docker build -t product-showcase .
|
| 30 |
+
docker run --rm -p 7860:7860 \
|
| 31 |
+
-e VITE_API_BASE_URL=http://127.0.0.1:7860 \
|
| 32 |
+
-e KIE_API_KEY=... \
|
| 33 |
+
product-showcase
|
| 34 |
+
```
|
| 35 |
+
|
| 36 |
+
Then open `http://127.0.0.1:7860`.
|
| 37 |
+
|
| 38 |
+
## Local development
|
| 39 |
+
|
| 40 |
+
See [FLOW.md](FLOW.md) for architecture and env details.
|
| 41 |
+
|
| 42 |
+
```bash
|
| 43 |
+
# API
|
| 44 |
+
cd backend && python -m venv .venv && source .venv/bin/activate
|
| 45 |
+
pip install -r requirements.txt && python main.py
|
| 46 |
+
|
| 47 |
+
# UI (separate terminal)
|
| 48 |
+
cd frontend && npm install && npm run dev
|
| 49 |
+
```
|
| 50 |
+
|
| 51 |
+
With a built `frontend/dist`, `python main.py` also serves the SPA from the same origin.
|
backend/main.py
CHANGED
|
@@ -4,10 +4,12 @@ Video pipeline aligned with python-backend (KIE Veo, image hosting, merge).
|
|
| 4 |
"""
|
| 5 |
|
| 6 |
from contextlib import asynccontextmanager
|
|
|
|
| 7 |
import os
|
| 8 |
import shutil
|
| 9 |
|
| 10 |
from fastapi import FastAPI
|
|
|
|
| 11 |
from fastapi.middleware.cors import CORSMiddleware
|
| 12 |
|
| 13 |
from api.video_generation import router as video_router
|
|
@@ -78,6 +80,10 @@ app.include_router(image_router, prefix="/api")
|
|
| 78 |
app.include_router(export_router, prefix="/api")
|
| 79 |
|
| 80 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
@app.get("/health")
|
| 82 |
def health():
|
| 83 |
return {
|
|
@@ -96,6 +102,14 @@ def health():
|
|
| 96 |
}
|
| 97 |
|
| 98 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
if __name__ == "__main__":
|
| 100 |
import uvicorn
|
| 101 |
|
|
|
|
| 4 |
"""
|
| 5 |
|
| 6 |
from contextlib import asynccontextmanager
|
| 7 |
+
from pathlib import Path
|
| 8 |
import os
|
| 9 |
import shutil
|
| 10 |
|
| 11 |
from fastapi import FastAPI
|
| 12 |
+
from fastapi.staticfiles import StaticFiles
|
| 13 |
from fastapi.middleware.cors import CORSMiddleware
|
| 14 |
|
| 15 |
from api.video_generation import router as video_router
|
|
|
|
| 80 |
app.include_router(export_router, prefix="/api")
|
| 81 |
|
| 82 |
|
| 83 |
+
_REPO_ROOT = Path(__file__).resolve().parent.parent
|
| 84 |
+
_FRONTEND_DIST = _REPO_ROOT / "frontend" / "dist"
|
| 85 |
+
|
| 86 |
+
|
| 87 |
@app.get("/health")
|
| 88 |
def health():
|
| 89 |
return {
|
|
|
|
| 102 |
}
|
| 103 |
|
| 104 |
|
| 105 |
+
if _FRONTEND_DIST.is_dir():
|
| 106 |
+
app.mount(
|
| 107 |
+
"/",
|
| 108 |
+
StaticFiles(directory=str(_FRONTEND_DIST), html=True),
|
| 109 |
+
name="frontend",
|
| 110 |
+
)
|
| 111 |
+
|
| 112 |
+
|
| 113 |
if __name__ == "__main__":
|
| 114 |
import uvicorn
|
| 115 |
|
backend/utils/public_url.py
CHANGED
|
@@ -10,7 +10,10 @@ def get_server_port_str() -> str:
|
|
| 10 |
|
| 11 |
|
| 12 |
def get_public_base_url() -> str:
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
| 14 |
if explicit:
|
| 15 |
return explicit
|
| 16 |
return f"http://127.0.0.1:{get_server_port_str()}"
|
|
|
|
| 10 |
|
| 11 |
|
| 12 |
def get_public_base_url() -> str:
|
| 13 |
+
"""Public origin for callbacks and hosted URLs. Set VITE_API_BASE_URL or PUBLIC_BASE_URL on deploy (e.g. https://YOUR-SPACE.hf.space)."""
|
| 14 |
+
explicit = (
|
| 15 |
+
(os.getenv("VITE_API_BASE_URL") or os.getenv("PUBLIC_BASE_URL") or "").strip().rstrip("/")
|
| 16 |
+
)
|
| 17 |
if explicit:
|
| 18 |
return explicit
|
| 19 |
return f"http://127.0.0.1:{get_server_port_str()}"
|