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] **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
@@ -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
|
||||||
|
|||||||
@@ -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'}
|
{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);
|
||||||
|
|||||||
Reference in New Issue
Block a user