Edge_TTS / api.py
alaaah25's picture
Update api.py
86095ef verified
Raw
History Blame
4.06 kB
import os
import uuid
import unicodedata
import re
import pathlib
from fastapi import FastAPI, HTTPException, Query
from fastapi.responses import FileResponse, HTMLResponse
from pydantic import BaseModel
import edge_tts
app = FastAPI(title="Edge TTS API", description="Microsoft Edge Neural TTS API")
os.makedirs("tts_outputs", exist_ok=True)
VOICES = {
"ar": {"male": "ar-SA-HamedNeural", "female": "ar-EG-SalmaNeural"},
"en": {"male": "en-US-GuyNeural", "female": "en-US-AvaNeural"},
"en-gb": {"male": "en-GB-RyanNeural", "female": "en-GB-SoniaNeural"},
"fr": {"male": "fr-FR-HenriNeural", "female": "fr-FR-DeniseNeural"},
"de": {"male": "de-DE-ConradNeural", "female": "de-DE-KatjaNeural"},
"es": {"male": "es-ES-AlvaroNeural", "female": "es-ES-ElviraNeural"},
"it": {"male": "it-IT-DiegoNeural", "female": "it-IT-ElsaNeural"},
"tr": {"male": "tr-TR-AhmetNeural", "female": "tr-TR-EmelNeural"},
"ru": {"male": "ru-RU-DmitryNeural", "female": "ru-RU-SvetlanaNeural"},
"hi": {"male": "hi-IN-MadhurNeural", "female": "hi-IN-SwaraNeural"}
}
SUPPORTED_LANGUAGES = list(VOICES.keys())
def clean_text(text: str) -> str:
text = unicodedata.normalize("NFC", text)
text = re.sub(r'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]', '', text)
text = re.sub(r'\s+', ' ', text)
return text.strip()
class TTSRequest(BaseModel):
text: str
language: str = "en"
gender: str = "female"
rate: str = "+0%"
@app.get("/", response_class=HTMLResponse)
def get_app():
html_path = pathlib.Path("index.html")
if html_path.exists():
return html_path.read_text(encoding="utf-8")
return "<h1>TTS API is running</h1>"
@app.post("/tts")
async def synthesize(req: TTSRequest):
text = clean_text(req.text)
if not text:
raise HTTPException(status_code=400, detail="Text cannot be empty.")
if req.language not in VOICES:
raise HTTPException(status_code=400, detail=f"Unsupported language. Choose from: {SUPPORTED_LANGUAGES}")
if req.gender not in ["male", "female"]:
raise HTTPException(status_code=400, detail="Gender must be 'male' or 'female'.")
lang_voices = VOICES[req.language]
gender = req.gender if req.gender in lang_voices else list(lang_voices.keys())[0]
voice = lang_voices[gender]
output_path = os.path.join("tts_outputs", f"{uuid.uuid4()}.mp3")
try:
communicate = edge_tts.Communicate(text, voice, rate=req.rate)
await communicate.save(output_path)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
return FileResponse(path=output_path, media_type="audio/mpeg", filename="output.mp3", headers={"X-Voice": voice})
@app.post("/tts-text")
async def synthesize_text(
text: str = Query(..., description="النص المراد تحويله"),
language: str = Query("en", description="ar, en, fr, es, de, it, tr, ru, hi"),
gender: str = Query("female", description="male or female"),
rate: str = Query("+0%", description="السرعة: -50%, -25%, +0%, +25%, +50%")
):
text = clean_text(text)
if not text:
raise HTTPException(status_code=400, detail="Text cannot be empty.")
if language not in VOICES:
raise HTTPException(status_code=400, detail=f"Unsupported language. Choose from: {SUPPORTED_LANGUAGES}")
if gender not in ["male", "female"]:
raise HTTPException(status_code=400, detail="Gender must be 'male' or 'female'.")
lang_voices = VOICES[language]
gender = gender if gender in lang_voices else list(lang_voices.keys())[0]
voice = lang_voices[gender]
output_path = os.path.join("tts_outputs", f"{uuid.uuid4()}.mp3")
try:
communicate = edge_tts.Communicate(text, voice, rate=rate)
await communicate.save(output_path)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
return FileResponse(path=output_path, media_type="audio/mpeg", filename="output.mp3", headers={"X-Voice": voice})
@app.get("/voices")
def get_voices():
return VOICES