cosoese / server.py
workitoscinco's picture
Upload 2 files
c9bec77 verified
Raw History Blame Contribute Delete
2.65 kB
from fastapi import FastAPI, Response
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from kokoro import KPipeline
import soundfile as sf
import numpy as np
import io
app = FastAPI()
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"])
pipe = KPipeline(lang_code='e')
class Req(BaseModel):
input: str
voice: str = "ef_dora"
speed: float = 1.0
INDEX_HTML = """<!DOCTYPE html><html><head><meta charset="utf-8"><title>Kokoro TTS</title></head>
<body style="font-family:sans-serif;text-align:center">
<h1>Kokoro TTS (espanol)</h1>
<textarea id="t" rows="3" style="width:min(600px,90vw)">Hola, soy una voz de Kokoro generada con IA abierta.</textarea><br><br>
<select id="v">
<option value="ef_dora">Dora (femenina)</option>
<option value="em_alex">Alex (masculina)</option>
<option value="em_santa">Santa (masculina)</option>
</select>
<label>Velocidad <input id="s2" type="range" min="0.5" max="2" step="0.1" value="1"><span id="sv">1</span></label><br><br>
<button id="b">Hablar</button>
<p id="s">Listo.</p>
<audio id="a" controls style="width:min(600px,90vw)"></audio><br>
<a id="d" hidden>Descargar wav</a>
<script>
const s2=document.querySelector("#s2");
s2.oninput=()=>document.querySelector("#sv").textContent=s2.value;
document.querySelector("#b").onclick=async()=>{
const s=document.querySelector("#s"); s.textContent="Generando...";
try{
const r=await fetch("/v1/audio/speech",{method:"POST",headers:{"Content-Type":"application/json"},
body:JSON.stringify({input:document.querySelector("#t").value.slice(0,1000),
voice:document.querySelector("#v").value,speed:parseFloat(s2.value)})});
if(!r.ok) throw new Error("servidor "+r.status);
const buf=await r.arrayBuffer();
const url=URL.createObjectURL(new Blob([buf],{type:"audio/wav"}));
const au=document.querySelector("#a"); au.src=url; au.play();
const dl=document.querySelector("#d"); dl.href=url; dl.download="kokoro.wav"; dl.hidden=false;
s.textContent="Listo ("+(buf.byteLength/1024).toFixed(1)+" KB).";
}catch(e){s.textContent="Error: "+e.message;}
};
</script></body></html>"""
@app.get("/", response_class=None)
def index():
from fastapi.responses import HTMLResponse
return HTMLResponse(INDEX_HTML)
@app.post("/v1/audio/speech")
def speech(r: Req):
parts = [a for _, _, a in pipe(r.input[:1000], voice=r.voice, speed=r.speed)]
wav = np.concatenate(parts) if len(parts) > 1 else parts[0]
buf = io.BytesIO()
sf.write(buf, wav, 24000, format="WAV")
return Response(content=buf.getvalue(), media_type="audio/wav")