feat: add Docker support for containerized deployment and serve static frontend from backend
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
# OmniVoice Docker Ignore
|
||||
|
||||
# Virtual environments
|
||||
.venv/
|
||||
venv/
|
||||
env/
|
||||
|
||||
# Node modules
|
||||
node_modules/
|
||||
bun.lockb
|
||||
.turbo/
|
||||
|
||||
# Python caches
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
.pytest_cache/
|
||||
.ruff_cache/
|
||||
.mypy_cache/
|
||||
|
||||
# Databases and user data
|
||||
# We don't want to copy existing local data into the image layout!
|
||||
*.db
|
||||
*.sqlite
|
||||
*.sqlite3
|
||||
omnivoice_data/
|
||||
frontend/dist/
|
||||
|
||||
# Git internals
|
||||
.git/
|
||||
.github/
|
||||
|
||||
# IDE files
|
||||
.DS_Store
|
||||
.vscode/
|
||||
.idea/
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
# ==========================================
|
||||
# Builder Stage: Compile React Frontend
|
||||
# ==========================================
|
||||
FROM oven/bun:1-alpine AS frontend-builder
|
||||
WORKDIR /app/frontend
|
||||
|
||||
# Copy frontend specifications
|
||||
COPY frontend/package.json ./
|
||||
COPY frontend/bun.lock ./
|
||||
|
||||
# Install dependencies fast
|
||||
RUN bun install --frozen-lockfile
|
||||
|
||||
# Copy frontend source and build static files
|
||||
COPY frontend/ ./
|
||||
# Output goes to /app/frontend/dist
|
||||
RUN bun run build
|
||||
|
||||
# ==========================================
|
||||
# Runtime Stage: Python & PyTorch Backend
|
||||
# ==========================================
|
||||
FROM pytorch/pytorch:2.4.0-cuda12.1-cudnn9-runtime AS runtime
|
||||
WORKDIR /app
|
||||
|
||||
# Enable unbuffered logs and optimizations
|
||||
ENV PYTHONDONTWRITEBYTECODE=1
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
ENV HF_HOME=/app/omnivoice_data/huggingface
|
||||
|
||||
# Install system dependencies (FFmpeg is critical for torchaudio/scene splitting)
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
ffmpeg \
|
||||
libsndfile1 \
|
||||
curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install `uv` for blazing-fast reliable pip resolution
|
||||
RUN pip install --no-cache-dir uv
|
||||
|
||||
# Copy python packaging specs
|
||||
COPY pyproject.toml uv.lock ./
|
||||
|
||||
# Native wheels from PyPI embed CUDA matching `torch >= 2.4` standard index
|
||||
# By installing via `uv`, the process completes exponentially faster
|
||||
RUN uv pip install --system --no-cache -e .
|
||||
|
||||
# Copy application source
|
||||
COPY backend/ ./backend/
|
||||
COPY omnivoice/ ./omnivoice/
|
||||
|
||||
# Copy the pre-built React frontend from the builder stage
|
||||
COPY --from=frontend-builder /app/frontend/dist ./frontend/dist
|
||||
|
||||
# Expose the single unified API and UI port
|
||||
EXPOSE 8000
|
||||
|
||||
# Mount points for persistent data (sqlite db, user voices, huggingface cache)
|
||||
VOLUME ["/app/omnivoice_data"]
|
||||
|
||||
# Bind to 0.0.0.0 for external access
|
||||
ENTRYPOINT ["uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
@@ -98,7 +98,7 @@ The studio is highly functional today, but we are aggressively expanding. Watch
|
||||
- [x] **Advanced Export Suite** — VTT subtitles, per-segment WAV ZIP, compressed MP3, and stem export (vocals + background separate).
|
||||
- [x] **Streaming TTS** — Chunked WAV streaming with progressive download and auto-playback.
|
||||
- [ ] **Native Desktop Applications** — Dedicated client apps for macOS, Windows, and Linux.
|
||||
- [ ] **One-Click Deployment** — Docker image packages engineered for zero-config GPU passthrough.
|
||||
- [x] **One-Click Deployment** — Docker image packages engineered for zero-config GPU passthrough.
|
||||
|
||||
---
|
||||
|
||||
|
||||
+10
-1
@@ -19,7 +19,7 @@ import soundfile as sf
|
||||
import torch
|
||||
import torchaudio
|
||||
from fastapi import FastAPI, File, Form, UploadFile, HTTPException, Query
|
||||
from fastapi.responses import FileResponse, Response, StreamingResponse
|
||||
from fastapi.responses import FileResponse, Response, StreamingResponse, JSONResponse, RedirectResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -1887,6 +1887,15 @@ async def delete_project(project_id: str):
|
||||
conn.close()
|
||||
return {"deleted": project_id}
|
||||
|
||||
# Mount frontend at root. Placed last so it doesn't shadow API routes.
|
||||
frontend_path = os.path.join(os.path.dirname(__file__), "..", "frontend", "dist")
|
||||
if os.path.exists(frontend_path):
|
||||
app.mount("/", StaticFiles(directory=frontend_path, html=True), name="frontend")
|
||||
else:
|
||||
@app.get("/")
|
||||
def _dev_fallback():
|
||||
return RedirectResponse(url="http://localhost:5173")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
omnivoice:
|
||||
build: .
|
||||
container_name: omnivoice-studio
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8000:8000"
|
||||
volumes:
|
||||
# Map the backend data directory to host for persistent SQLite, voices, and history
|
||||
- ./omnivoice_data:/app/omnivoice_data
|
||||
environment:
|
||||
# Optional: set this parameter to use Pyannote Speaker Diarization
|
||||
- HF_TOKEN=${HF_TOKEN:-}
|
||||
# Zero-config GPU Passthrough (Requires NVIDIA Container Toolkit on host)
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
devices:
|
||||
- driver: nvidia
|
||||
count: all
|
||||
capabilities: [gpu]
|
||||
@@ -94,7 +94,7 @@ const LANG_CODES = [
|
||||
{code: 'yo', label: 'Yoruba'}, {code: 'zu', label: 'Zulu'}
|
||||
];
|
||||
|
||||
const API = "http://localhost:8000";
|
||||
const API = import.meta.env.DEV ? "http://localhost:8000" : "";
|
||||
|
||||
function formatTime(s) {
|
||||
const m = Math.floor(s / 60);
|
||||
|
||||
Reference in New Issue
Block a user