Spaces:
Sleeping
Sleeping
File size: 4,061 Bytes
09484ef 31ef339 c3db83e 31ef339 09484ef cc0101a 09484ef 7a99dbf 09484ef 31ef339 09484ef c3db83e 09484ef 983920c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | 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 |