feat: add Docker support for containerized deployment and serve static frontend from backend

This commit is contained in:
debpalash
2026-04-14 04:35:04 +05:30
parent 3a8adf5dd4
commit 5668aff528
6 changed files with 132 additions and 3 deletions
+36
View File
@@ -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
View File
@@ -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"]
+1 -1
View File
@@ -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] **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. - [x] **Streaming TTS** — Chunked WAV streaming with progressive download and auto-playback.
- [ ] **Native Desktop Applications** — Dedicated client apps for macOS, Windows, and Linux. - [ ] **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
View File
@@ -19,7 +19,7 @@ import soundfile as sf
import torch import torch
import torchaudio import torchaudio
from fastapi import FastAPI, File, Form, UploadFile, HTTPException, Query 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 fastapi.staticfiles import StaticFiles
from pydantic import BaseModel from pydantic import BaseModel
@@ -1887,6 +1887,15 @@ async def delete_project(project_id: str):
conn.close() conn.close()
return {"deleted": project_id} 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__": if __name__ == "__main__":
import uvicorn import uvicorn
+23
View File
@@ -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]
+1 -1
View File
@@ -94,7 +94,7 @@ const LANG_CODES = [
{code: 'yo', label: 'Yoruba'}, {code: 'zu', label: 'Zulu'} {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) { function formatTime(s) {
const m = Math.floor(s / 60); const m = Math.floor(s / 60);