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