tts-api commited on
Commit
09484ef
·
verified ·
1 Parent(s): ee0ad63

Update api.py

Browse files
Files changed (1) hide show
  1. api.py +80 -105
api.py CHANGED
@@ -1,106 +1,81 @@
1
- import os
2
- import uuid
3
- import asyncio
4
- import unicodedata
5
- from fastapi import FastAPI, HTTPException
6
- from fastapi.responses import FileResponse
7
- from pydantic import BaseModel
8
- import edge_tts
9
-
10
- app = FastAPI(title="Edge TTS API", description="Microsoft Edge Neural TTS API")
11
-
12
- os.makedirs("tts_outputs", exist_ok=True)
13
-
14
- VOICES = {
15
- "ar": {
16
- "male": "ar-EG-ShakirNeural",
17
- "female": "ar-EG-SalmaNeural"
18
- },
19
- "ar-sa": {
20
- "female": "ar-SA-ZariyahNeural"
21
- },
22
- "en": {
23
- "male": "en-US-GuyNeural",
24
- "female": "en-US-AvaNeural"
25
- },
26
- "en-gb": {
27
- "female": "en-GB-SoniaNeural"
28
- },
29
- "fr": {
30
- "male": "fr-FR-HenriNeural",
31
- "female": "fr-FR-DeniseNeural"
32
- },
33
- "de": {
34
- "male": "de-DE-ConradNeural",
35
- "female": "de-DE-KatjaNeural"
36
- },
37
- "es": {
38
- "male": "es-ES-AlvaroNeural",
39
- "female": "es-ES-ElviraNeural"
40
- },
41
- "it": {
42
- "male": "it-IT-DiegoNeural",
43
- "female": "it-IT-ElsaNeural"
44
- },
45
- "tr": {
46
- "male": "tr-TR-AhmetNeural",
47
- "female": "tr-TR-EmelNeural"
48
- },
49
- "ru": {
50
- "male": "ru-RU-DmitryNeural",
51
- "female": "ru-RU-SvetlanaNeural"
52
- },
53
- "hi": {
54
- "male": "hi-IN-MadhurNeural",
55
- "female": "hi-IN-SwaraNeural"
56
- }
57
- }
58
-
59
- SUPPORTED_LANGUAGES = list(VOICES.keys())
60
-
61
- class TTSRequest(BaseModel):
62
- text: str
63
- language: str = "en"
64
- gender: str = "female"
65
- rate: str = "+0%" # speed: -50%, -25%, +0%, +25%, +50%
66
-
67
- @app.get("/")
68
- def root():
69
- return {"message": "Edge TTS API is running ✅"}
70
-
71
- @app.post("/tts")
72
- async def synthesize(req: TTSRequest):
73
- text = unicodedata.normalize("NFC", req.text)
74
-
75
- if not text.strip():
76
- raise HTTPException(status_code=400, detail="Text cannot be empty.")
77
- if req.language not in VOICES:
78
- raise HTTPException(status_code=400, detail=f"Unsupported language. Choose from: {SUPPORTED_LANGUAGES}")
79
- if req.gender not in ["male", "female"]:
80
- raise HTTPException(status_code=400, detail="Gender must be 'male' or 'female'.")
81
-
82
- lang_voices = VOICES[req.language]
83
- if req.gender not in lang_voices:
84
- gender = list(lang_voices.keys())[0]
85
- else:
86
- gender = req.gender
87
-
88
- voice = lang_voices[gender]
89
- output_path = os.path.join("tts_outputs", f"{uuid.uuid4()}.mp3")
90
-
91
- try:
92
- communicate = edge_tts.Communicate(text, voice, rate=req.rate)
93
- await communicate.save(output_path)
94
- except Exception as e:
95
- raise HTTPException(status_code=500, detail=str(e))
96
-
97
- return FileResponse(
98
- path=output_path,
99
- media_type="audio/mpeg",
100
- filename="output.mp3",
101
- headers={"X-Voice": voice}
102
- )
103
-
104
- @app.get("/voices")
105
- def get_voices():
106
  return VOICES
 
1
+ import os
2
+ import uuid
3
+ import unicodedata
4
+ import re
5
+ from fastapi import FastAPI, HTTPException, UploadFile, File, Form
6
+ from fastapi.responses import FileResponse
7
+ from pydantic import BaseModel
8
+ import edge_tts
9
+
10
+ app = FastAPI(title="Edge TTS API", description="Microsoft Edge Neural TTS API")
11
+
12
+ os.makedirs("tts_outputs", exist_ok=True)
13
+
14
+ VOICES = {
15
+ "ar": {"male": "ar-EG-ShakirNeural", "female": "ar-EG-SalmaNeural"},
16
+ "ar-sa": {"female": "ar-SA-ZariyahNeural"},
17
+ "en": {"male": "en-US-GuyNeural", "female": "en-US-AvaNeural"},
18
+ "en-gb": {"female": "en-GB-SoniaNeural"},
19
+ "fr": {"male": "fr-FR-HenriNeural", "female": "fr-FR-DeniseNeural"},
20
+ "de": {"male": "de-DE-ConradNeural", "female": "de-DE-KatjaNeural"},
21
+ "es": {"male": "es-ES-AlvaroNeural", "female": "es-ES-ElviraNeural"},
22
+ "it": {"male": "it-IT-DiegoNeural", "female": "it-IT-ElsaNeural"},
23
+ "tr": {"male": "tr-TR-AhmetNeural", "female": "tr-TR-EmelNeural"},
24
+ "ru": {"male": "ru-RU-DmitryNeural", "female": "ru-RU-SvetlanaNeural"},
25
+ "hi": {"male": "hi-IN-MadhurNeural", "female": "hi-IN-SwaraNeural"}
26
+ }
27
+
28
+ SUPPORTED_LANGUAGES = list(VOICES.keys())
29
+
30
+ def clean_text(text: str) -> str:
31
+ # Normalize unicode (handles tashkeel and special chars)
32
+ text = unicodedata.normalize("NFC", text)
33
+ # Remove control characters except newlines and tabs
34
+ text = re.sub(r'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]', '', text)
35
+ # Replace multiple newlines/spaces with single space
36
+ text = re.sub(r'\s+', ' ', text)
37
+ return text.strip()
38
+
39
+ class TTSRequest(BaseModel):
40
+ text: str
41
+ language: str = "en"
42
+ gender: str = "female"
43
+ rate: str = "+0%"
44
+
45
+ @app.get("/")
46
+ def root():
47
+ return {"message": "Edge TTS API is running ✅"}
48
+
49
+ @app.post("/tts")
50
+ async def synthesize(req: TTSRequest):
51
+ text = clean_text(req.text)
52
+
53
+ if not text:
54
+ raise HTTPException(status_code=400, detail="Text cannot be empty.")
55
+ if req.language not in VOICES:
56
+ raise HTTPException(status_code=400, detail=f"Unsupported language. Choose from: {SUPPORTED_LANGUAGES}")
57
+ if req.gender not in ["male", "female"]:
58
+ raise HTTPException(status_code=400, detail="Gender must be 'male' or 'female'.")
59
+
60
+ lang_voices = VOICES[req.language]
61
+ gender = req.gender if req.gender in lang_voices else list(lang_voices.keys())[0]
62
+ voice = lang_voices[gender]
63
+
64
+ output_path = os.path.join("tts_outputs", f"{uuid.uuid4()}.mp3")
65
+
66
+ try:
67
+ communicate = edge_tts.Communicate(text, voice, rate=req.rate)
68
+ await communicate.save(output_path)
69
+ except Exception as e:
70
+ raise HTTPException(status_code=500, detail=str(e))
71
+
72
+ return FileResponse(
73
+ path=output_path,
74
+ media_type="audio/mpeg",
75
+ filename="output.mp3",
76
+ headers={"X-Voice": voice}
77
+ )
78
+
79
+ @app.get("/voices")
80
+ def get_voices():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  return VOICES