Spaces:
Sleeping
Sleeping
Upload 2 files
Browse files- app.py +180 -0
- requirements.txt +1 -0
app.py
ADDED
@@ -0,0 +1,180 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import edge_tts
|
3 |
+
import asyncio
|
4 |
+
from flask import Flask, render_template, request, send_from_directory, jsonify
|
5 |
+
app = Flask(__name__)
|
6 |
+
AUDIO_DIR = os.path.join(os.getcwd(), 'generated_audio')
|
7 |
+
os.makedirs(AUDIO_DIR, exist_ok=True)
|
8 |
+
|
9 |
+
voices = {
|
10 |
+
'Multilingual': {
|
11 |
+
'Andrew': {'voice_id': 'en-US-AndrewMultilingualNeural', 'styles': ['default']},
|
12 |
+
'Ava': {'voice_id': 'en-US-AvaMultilingualNeural', 'styles': ['default']},
|
13 |
+
'Brian': {'voice_id': 'en-US-BrianMultilingualNeural', 'styles': ['default']},
|
14 |
+
'Emma': {'voice_id': 'en-US-EmmaMultilingualNeural', 'styles': ['default']},
|
15 |
+
'Florian': {'voice_id': 'de-DE-FlorianMultilingualNeural', 'styles': ['default']},
|
16 |
+
'Giuseppe': {'voice_id': 'it-IT-GiuseppeMultilingualNeural', 'styles': ['default']},
|
17 |
+
'Hyunsu': {'voice_id': 'ko-KR-HyunsuMultilingualNeural', 'styles': ['default']},
|
18 |
+
'Remy': {'voice_id': 'fr-FR-RemyMultilingualNeural', 'styles': ['default']},
|
19 |
+
'Seraphina': {'voice_id': 'de-DE-SeraphinaMultilingualNeural', 'styles': ['default']},
|
20 |
+
'Thalita': {'voice_id': 'pt-BR-ThalitaMultilingualNeural', 'styles': ['default']},
|
21 |
+
'Vivienne': {'voice_id': 'fr-FR-VivienneMultilingualNeural', 'styles': ['default']}
|
22 |
+
},
|
23 |
+
'Arabic': {
|
24 |
+
'Fatima': {'voice_id': 'ar-AE-FatimaNeural', 'styles': ['default']},
|
25 |
+
'Hamdan': {'voice_id': 'ar-AE-HamdanNeural', 'styles': ['default']},
|
26 |
+
'Ali': {'voice_id': 'ar-BH-AliNeural', 'styles': ['default']},
|
27 |
+
'Laila': {'voice_id': 'ar-BH-LailaNeural', 'styles': ['default']},
|
28 |
+
'Salma': {'voice_id': 'ar-EG-SalmaNeural', 'styles': ['default']},
|
29 |
+
'Shakir': {'voice_id': 'ar-EG-ShakirNeural', 'styles': ['default']}
|
30 |
+
},
|
31 |
+
'Chinese': {
|
32 |
+
'Xiaoxiao': {'voice_id': 'zh-CN-XiaoxiaoNeural', 'styles': ['default']},
|
33 |
+
'Xiaoyi': {'voice_id': 'zh-CN-XiaoyiNeural', 'styles': ['default']},
|
34 |
+
'Yunjian': {'voice_id': 'zh-CN-YunjianNeural', 'styles': ['default']},
|
35 |
+
'Yunxi': {'voice_id': 'zh-CN-YunxiNeural', 'styles': ['default']},
|
36 |
+
'Yunxia': {'voice_id': 'zh-CN-YunxiaNeural', 'styles': ['default']},
|
37 |
+
'Yunyang': {'voice_id': 'zh-CN-YunyangNeural', 'styles': ['default']}
|
38 |
+
},
|
39 |
+
'English': {
|
40 |
+
'Ana': {'voice_id': 'en-US-AnaNeural', 'styles': ['default']},
|
41 |
+
'Andrew': {'voice_id': 'en-US-AndrewNeural', 'styles': ['default']},
|
42 |
+
'Aria': {'voice_id': 'en-US-AriaNeural', 'styles': ['default']},
|
43 |
+
'Ava': {'voice_id': 'en-US-AvaNeural', 'styles': ['default']},
|
44 |
+
'Brian': {'voice_id': 'en-US-BrianNeural', 'styles': ['default']},
|
45 |
+
'Christopher': {'voice_id': 'en-US-ChristopherNeural', 'styles': ['default']},
|
46 |
+
'Emma': {'voice_id': 'en-US-EmmaNeural', 'styles': ['default']},
|
47 |
+
'Eric': {'voice_id': 'en-US-EricNeural', 'styles': ['default']},
|
48 |
+
'Guy': {'voice_id': 'en-US-GuyNeural', 'styles': ['default']},
|
49 |
+
'Jenny': {'voice_id': 'en-US-JennyNeural', 'styles': ['default']},
|
50 |
+
'Michelle': {'voice_id': 'en-US-MichelleNeural', 'styles': ['default']},
|
51 |
+
'Roger': {'voice_id': 'en-US-RogerNeural', 'styles': ['default']},
|
52 |
+
'Steffan': {'voice_id': 'en-US-SteffanNeural', 'styles': ['default']}
|
53 |
+
},
|
54 |
+
'French': {
|
55 |
+
'Denise': {'voice_id': 'fr-FR-DeniseNeural', 'styles': ['default']},
|
56 |
+
'Eloise': {'voice_id': 'fr-FR-EloiseNeural', 'styles': ['default']},
|
57 |
+
'Henri': {'voice_id': 'fr-FR-HenriNeural', 'styles': ['default']},
|
58 |
+
'Charline': {'voice_id': 'fr-BE-CharlineNeural', 'styles': ['default']},
|
59 |
+
'Gerard': {'voice_id': 'fr-BE-GerardNeural', 'styles': ['default']},
|
60 |
+
'Antoine': {'voice_id': 'fr-CA-AntoineNeural', 'styles': ['default']},
|
61 |
+
'Jean': {'voice_id': 'fr-CA-JeanNeural', 'styles': ['default']},
|
62 |
+
'Sylvie': {'voice_id': 'fr-CA-SylvieNeural', 'styles': ['default']},
|
63 |
+
'Thierry': {'voice_id': 'fr-CA-ThierryNeural', 'styles': ['default']}
|
64 |
+
},
|
65 |
+
'German': {
|
66 |
+
'Amala': {'voice_id': 'de-DE-AmalaNeural', 'styles': ['default']},
|
67 |
+
'Conrad': {'voice_id': 'de-DE-ConradNeural', 'styles': ['default']},
|
68 |
+
'Katja': {'voice_id': 'de-DE-KatjaNeural', 'styles': ['default']},
|
69 |
+
'Killian': {'voice_id': 'de-DE-KillianNeural', 'styles': ['default']}
|
70 |
+
},
|
71 |
+
'Hindi': {
|
72 |
+
'Madhur': {'voice_id': 'hi-IN-MadhurNeural', 'styles': ['default']},
|
73 |
+
'Swara': {'voice_id': 'hi-IN-SwaraNeural', 'styles': ['default']}
|
74 |
+
},
|
75 |
+
'Italian': {
|
76 |
+
'Diego': {'voice_id': 'it-IT-DiegoNeural', 'styles': ['default']},
|
77 |
+
'Elsa': {'voice_id': 'it-IT-ElsaNeural', 'styles': ['default']},
|
78 |
+
'Isabella': {'voice_id': 'it-IT-IsabellaNeural', 'styles': ['default']}
|
79 |
+
},
|
80 |
+
'Japanese': {
|
81 |
+
'Keita': {'voice_id': 'ja-JP-KeitaNeural', 'styles': ['default']},
|
82 |
+
'Nanami': {'voice_id': 'ja-JP-NanamiNeural', 'styles': ['default']}
|
83 |
+
},
|
84 |
+
'Korean': {
|
85 |
+
'InJoon': {'voice_id': 'ko-KR-InJoonNeural', 'styles': ['default']},
|
86 |
+
'SunHi': {'voice_id': 'ko-KR-SunHiNeural', 'styles': ['default']}
|
87 |
+
},
|
88 |
+
'Portuguese': {
|
89 |
+
'Antonio': {'voice_id': 'pt-BR-AntonioNeural', 'styles': ['default']},
|
90 |
+
'Francisca': {'voice_id': 'pt-BR-FranciscaNeural', 'styles': ['default']},
|
91 |
+
'Duarte': {'voice_id': 'pt-PT-DuarteNeural', 'styles': ['default']},
|
92 |
+
'Raquel': {'voice_id': 'pt-PT-RaquelNeural', 'styles': ['default']}
|
93 |
+
},
|
94 |
+
'Russian': {
|
95 |
+
'Dmitry': {'voice_id': 'ru-RU-DmitryNeural', 'styles': ['default']},
|
96 |
+
'Svetlana': {'voice_id': 'ru-RU-SvetlanaNeural', 'styles': ['default']}
|
97 |
+
},
|
98 |
+
'Spanish': {
|
99 |
+
'Alvaro': {'voice_id': 'es-ES-AlvaroNeural', 'styles': ['default']},
|
100 |
+
'Elvira': {'voice_id': 'es-ES-ElviraNeural', 'styles': ['default']},
|
101 |
+
'Ximena': {'voice_id': 'es-ES-XimenaNeural', 'styles': ['default']},
|
102 |
+
'Dalia': {'voice_id': 'es-MX-DaliaNeural', 'styles': ['default']},
|
103 |
+
'Jorge': {'voice_id': 'es-MX-JorgeNeural', 'styles': ['default']},
|
104 |
+
'Alonso': {'voice_id': 'es-US-AlonsoNeural', 'styles': ['default']},
|
105 |
+
'Paloma': {'voice_id': 'es-US-PalomaNeural', 'styles': ['default']}
|
106 |
+
}
|
107 |
+
}
|
108 |
+
|
109 |
+
@app.route('/')
|
110 |
+
def index():
|
111 |
+
return render_template('index.html', voices=voices)
|
112 |
+
|
113 |
+
@app.route('/get_voice_info', methods=['GET'])
|
114 |
+
def get_voice_info():
|
115 |
+
voice_id = request.args.get('voice_id')
|
116 |
+
language = request.args.get('language')
|
117 |
+
if language in voices and voice_id in voices[language]:
|
118 |
+
return jsonify(voices[language][voice_id])
|
119 |
+
return jsonify({'error': 'Voice not found'})
|
120 |
+
|
121 |
+
@app.route('/generate_audio', methods=['POST'])
|
122 |
+
async def generate_audio():
|
123 |
+
text = request.form.get('text')
|
124 |
+
language = request.form.get('language')
|
125 |
+
voice = request.form.get('voice')
|
126 |
+
style = request.form.get('style', 'default')
|
127 |
+
|
128 |
+
# Format rate properly with % symbol
|
129 |
+
rate_value = request.form.get('rate', '0')
|
130 |
+
rate = f"{'+' if int(rate_value) >= 0 else ''}{rate_value}%"
|
131 |
+
|
132 |
+
# Format volume properly with % symbol
|
133 |
+
volume_value = request.form.get('volume', '0')
|
134 |
+
volume = f"{'+' if int(volume_value) >= 0 else ''}{volume_value}%"
|
135 |
+
|
136 |
+
# Format pitch properly with Hz
|
137 |
+
pitch_value = request.form.get('pitch', '0')
|
138 |
+
pitch = f"{'+' if int(pitch_value) >= 0 else ''}{pitch_value}Hz"
|
139 |
+
|
140 |
+
if not all([text, language, voice]):
|
141 |
+
return jsonify({'error': 'Missing required parameters', 'success': False})
|
142 |
+
|
143 |
+
try:
|
144 |
+
voice_info = voices[language][voice]
|
145 |
+
voice_id = voice_info['voice_id']
|
146 |
+
|
147 |
+
if style != 'default':
|
148 |
+
voice_id = f"{voice_id}(Style={style})"
|
149 |
+
|
150 |
+
communicate = edge_tts.Communicate(
|
151 |
+
text,
|
152 |
+
voice_id,
|
153 |
+
rate=rate,
|
154 |
+
volume=volume,
|
155 |
+
pitch=pitch
|
156 |
+
)
|
157 |
+
|
158 |
+
audio_filename = f"{language}_{voice}_{hash(text)}.mp3"
|
159 |
+
audio_filepath = os.path.join(AUDIO_DIR, audio_filename)
|
160 |
+
|
161 |
+
await communicate.save(audio_filepath)
|
162 |
+
|
163 |
+
return jsonify({
|
164 |
+
'success': True,
|
165 |
+
'audio_path': f'/generated_audio/{audio_filename}',
|
166 |
+
'filename': audio_filename
|
167 |
+
})
|
168 |
+
except Exception as e:
|
169 |
+
return jsonify({'error': str(e), 'success': False})
|
170 |
+
|
171 |
+
@app.route('/generated_audio/<filename>')
|
172 |
+
def serve_audio(filename):
|
173 |
+
return send_from_directory(AUDIO_DIR, filename)
|
174 |
+
|
175 |
+
@app.route('/download_audio/<filename>')
|
176 |
+
def download_audio(filename):
|
177 |
+
return send_from_directory(AUDIO_DIR, filename, as_attachment=True)
|
178 |
+
|
179 |
+
if __name__ == '__main__':
|
180 |
+
app.run(debug=False, host='0.0.0.0')
|
requirements.txt
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
telethon
|