166 lines
4.7 KiB
Python
166 lines
4.7 KiB
Python
import os
|
|
import time
|
|
from datetime import datetime
|
|
from typing import List, Optional
|
|
|
|
from fastapi import FastAPI, Request, Form, HTTPException, Depends
|
|
from fastapi.responses import HTMLResponse, FileResponse, JSONResponse
|
|
from fastapi.staticfiles import StaticFiles
|
|
from fastapi.templating import Jinja2Templates
|
|
from pydantic import BaseModel
|
|
|
|
from tts_engine import generate_speech_from_api, AVAILABLE_VOICES, DEFAULT_VOICE
|
|
|
|
# Create FastAPI app
|
|
app = FastAPI(
|
|
title="Orpheus-FASTAPI",
|
|
description="High-performance Text-to-Speech server using Orpheus-FASTAPI",
|
|
version="1.0.0"
|
|
)
|
|
|
|
# Ensure directories exist
|
|
os.makedirs("outputs", exist_ok=True)
|
|
os.makedirs("static", exist_ok=True)
|
|
|
|
# Mount directories for serving files
|
|
app.mount("/outputs", StaticFiles(directory="outputs"), name="outputs")
|
|
app.mount("/static", StaticFiles(directory="static"), name="static")
|
|
|
|
# Setup templates
|
|
templates = Jinja2Templates(directory="templates")
|
|
|
|
# API models
|
|
class SpeechRequest(BaseModel):
|
|
input: str
|
|
model: str = "orpheus"
|
|
voice: str = DEFAULT_VOICE
|
|
response_format: str = "wav"
|
|
speed: float = 1.0
|
|
|
|
class APIResponse(BaseModel):
|
|
status: str
|
|
voice: str
|
|
output_file: str
|
|
generation_time: float
|
|
|
|
# OpenAI-compatible API endpoint
|
|
@app.post("/v1/audio/speech")
|
|
async def create_speech_api(request: SpeechRequest):
|
|
"""
|
|
Generate speech from text using the Orpheus TTS model.
|
|
Compatible with OpenAI's /v1/audio/speech endpoint.
|
|
"""
|
|
if not request.input:
|
|
raise HTTPException(status_code=400, detail="Missing input text")
|
|
|
|
# Generate unique filename
|
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
output_path = f"outputs/{request.voice}_{timestamp}.wav"
|
|
|
|
# Generate speech
|
|
start = time.time()
|
|
generate_speech_from_api(
|
|
prompt=request.input,
|
|
voice=request.voice,
|
|
output_file=output_path
|
|
)
|
|
end = time.time()
|
|
generation_time = round(end - start, 2)
|
|
|
|
# Return audio file
|
|
return FileResponse(
|
|
path=output_path,
|
|
media_type="audio/wav",
|
|
filename=f"{request.voice}_{timestamp}.wav"
|
|
)
|
|
|
|
# Legacy API endpoint for compatibility
|
|
@app.post("/speak")
|
|
async def speak(request: Request):
|
|
"""Legacy endpoint for compatibility with existing clients"""
|
|
data = await request.json()
|
|
text = data.get("text", "")
|
|
voice = data.get("voice", DEFAULT_VOICE)
|
|
|
|
if not text:
|
|
return JSONResponse(
|
|
status_code=400,
|
|
content={"error": "Missing 'text'"}
|
|
)
|
|
|
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
output_path = f"outputs/{voice}_{timestamp}.wav"
|
|
|
|
# Generate speech
|
|
start = time.time()
|
|
generate_speech_from_api(prompt=text, voice=voice, output_file=output_path)
|
|
end = time.time()
|
|
generation_time = round(end - start, 2)
|
|
|
|
return JSONResponse(content={
|
|
"status": "ok",
|
|
"voice": voice,
|
|
"output_file": output_path,
|
|
"generation_time": generation_time
|
|
})
|
|
|
|
# Web UI routes
|
|
@app.get("/", response_class=HTMLResponse)
|
|
async def root(request: Request):
|
|
"""Redirect to web UI"""
|
|
return templates.TemplateResponse(
|
|
"tts.html",
|
|
{"request": request, "voices": AVAILABLE_VOICES}
|
|
)
|
|
|
|
@app.get("/web/", response_class=HTMLResponse)
|
|
async def web_ui(request: Request):
|
|
"""Main web UI for TTS generation"""
|
|
return templates.TemplateResponse(
|
|
"tts.html",
|
|
{"request": request, "voices": AVAILABLE_VOICES}
|
|
)
|
|
|
|
@app.post("/web/", response_class=HTMLResponse)
|
|
async def generate_from_web(
|
|
request: Request,
|
|
text: str = Form(...),
|
|
voice: str = Form(DEFAULT_VOICE)
|
|
):
|
|
"""Handle form submission from web UI"""
|
|
if not text:
|
|
return templates.TemplateResponse(
|
|
"tts.html",
|
|
{
|
|
"request": request,
|
|
"error": "Please enter some text.",
|
|
"voices": AVAILABLE_VOICES
|
|
}
|
|
)
|
|
|
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
output_path = f"outputs/{voice}_{timestamp}.wav"
|
|
|
|
# Generate speech
|
|
start = time.time()
|
|
generate_speech_from_api(prompt=text, voice=voice, output_file=output_path)
|
|
end = time.time()
|
|
generation_time = round(end - start, 2)
|
|
|
|
return templates.TemplateResponse(
|
|
"tts.html",
|
|
{
|
|
"request": request,
|
|
"success": True,
|
|
"text": text,
|
|
"voice": voice,
|
|
"output_file": output_path,
|
|
"generation_time": generation_time,
|
|
"voices": AVAILABLE_VOICES
|
|
}
|
|
)
|
|
|
|
if __name__ == "__main__":
|
|
import uvicorn
|
|
print("🔥 Starting Orpheus-FASTAPI Server (CUDA)")
|
|
uvicorn.run("app:app", host="0.0.0.0", port=5005, reload=True)
|