LFM2.5_8B / Dockerfile
OrbitMC's picture
Update Dockerfile
0d89398 verified
Raw
History Blame Contribute Delete
4.97 kB
FROM python:3.11-slim
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends ffmpeg && \
rm -rf /var/lib/apt/lists/*
RUN pip install --no-cache-dir fastapi uvicorn edge-tts
RUN cat <<'PYEOF' > /app/main.py
from fastapi import FastAPI, Query
from fastapi.responses import StreamingResponse, HTMLResponse
from fastapi.middleware.cors import CORSMiddleware
import edge_tts
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
VOICES = [
"en-US-AvaNeural", "en-US-AndrewNeural", "en-GB-SoniaNeural",
"en-GB-RyanNeural", "ja-JP-NanamiNeural", "ja-JP-KeitaNeural",
"de-DE-KatjaNeural", "fr-FR-DeniseNeural", "es-ES-ElviraNeural",
"zh-CN-XiaoxiaoNeural", "zh-CN-YunjianNeural", "ko-KR-SunHiNeural"
]
@app.get("/")
async def root():
return {"message": "Edge TTS API", "voices": VOICES}
@app.get("/tts")
async def tts(
text: str = Query(..., description="Text to speak"),
voice: str = Query(default="en-US-AvaNeural", description="Voice ID"),
rate: str = Query(default="+0%", description="Speed: +10% or -10%"),
pitch: str = Query(default="+0Hz", description="Pitch: +10Hz or -10Hz")
):
# edge-tts regex validation requires a leading "+" or "-" sign
if not rate.startswith(("+", "-")):
rate = f"+{rate}"
if not pitch.startswith(("+", "-")):
pitch = f"+{pitch}"
communicate = edge_tts.Communicate(text, voice, rate=rate, pitch=pitch)
async def audio_generator():
async for chunk in communicate.stream():
if chunk["type"] == "audio":
yield chunk["data"]
return StreamingResponse(audio_generator(), media_type="audio/mpeg")
@app.get("/ui", response_class=HTMLResponse)
async def ui():
return """<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Edge TTS</title>
<style>
body{font-family:system-ui;max-width:600px;margin:40px auto;padding:20px;background:#111;color:#eee}
textarea,select,button{width:100%;padding:12px;margin:8px 0;border-radius:8px;border:none;font-size:16px}
textarea{height:120px;background:#222;color:#fff;resize:vertical}
select{background:#222;color:#fff}
button{background:#3b82f6;color:#fff;cursor:pointer;font-weight:bold}
button:hover{background:#2563eb}
button:disabled{opacity:.5;cursor:not-allowed}
.row{display:flex;gap:10px}
.row>*{flex:1}
audio{width:100%;margin-top:15px}
#status{text-align:center;margin-top:10px;color:#aaa}
</style>
</head>
<body>
<h2>Edge TTS API</h2>
<textarea id="text" placeholder="Enter text to speak...">Hello! This is Microsoft Edge TTS running inside Docker.</textarea>
<div class="row">
<select id="voice">
<option value="en-US-AvaNeural">Ava (Female)</option>
<option value="en-US-AndrewNeural">Andrew (Male)</option>
<option value="en-GB-SoniaNeural">Sonia (Female)</option>
<option value="en-GB-RyanNeural">Ryan (Male)</option>
<option value="ja-JP-NanamiNeural">Nanami (Female)</option>
<option value="ja-JP-KeitaNeural">Keita (Male)</option>
<option value="de-DE-KatjaNeural">Katja (Female)</option>
<option value="fr-FR-DeniseNeural">Denise (Female)</option>
<option value="es-ES-ElviraNeural">Elvira (Female)</option>
<option value="zh-CN-XiaoxiaoNeural">Xiaoxiao (Female)</option>
<option value="zh-CN-YunjianNeural">Yunjian (Male)</option>
<option value="ko-KR-SunHiNeural">Sun-Hi (Female)</option>
</select>
<select id="rate">
<option value="-50%">Very Slow</option>
<option value="-20%">Slow</option>
<option value="+0%" selected>Normal</option>
<option value="+20%">Fast</option>
<option value="+50%">Very Fast</option>
</select>
<select id="pitch">
<option value="-20Hz">Low</option>
<option value="+0Hz" selected>Normal</option>
<option value="+20Hz">High</option>
</select>
</div>
<button id="btn" onclick="generate()">Generate Speech</button>
<div id="status"></div>
<audio id="player" controls style="display:none"></audio>
<script>
async function generate(){
const btn=document.getElementById("btn"),status=document.getElementById("status"),player=document.getElementById("player");
const text=document.getElementById("text").value.trim();
if(!text){status.textContent="Please enter text.";return}
btn.disabled=true;status.textContent="Generating...";
const p=new URLSearchParams({text,voice:document.getElementById("voice").value,rate:document.getElementById("rate").value,pitch:document.getElementById("pitch").value});
try{
const res=await fetch("/tts?"+p.toString());
if(!res.ok)throw new Error(await res.text());
const blob=await res.blob(),url=URL.createObjectURL(blob);
player.src=url;player.style.display="block";player.play();status.textContent="Done!";
}catch(err){status.textContent="Error: "+err.message}
finally{btn.disabled=false}
}
</script>
</body>
</html>"""
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=7860)
PYEOF
EXPOSE 7860
CMD ["python", "/app/main.py"]