Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -31,28 +31,60 @@ SUPPORTED_LANGUAGES = {
|
|
31 |
'de': 'Deutsch'
|
32 |
}
|
33 |
|
34 |
-
# تعيين أصوات لكل لغة
|
35 |
VOICE_MAPPINGS = {
|
36 |
-
'ar':
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
41 |
}
|
42 |
|
43 |
# تحديد اللغات RTL
|
44 |
RTL_LANGUAGES = ['ar']
|
45 |
|
46 |
-
async def generate_speech(text, lang):
|
47 |
-
"""توليد الصوت باستخدام edge-tts"""
|
48 |
try:
|
49 |
-
|
50 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
51 |
|
52 |
audio_path = tempfile.mktemp(suffix='.mp3')
|
53 |
await communicate.save(audio_path)
|
54 |
|
55 |
-
# التحقق من وجود الملف وحجمه
|
56 |
if os.path.exists(audio_path) and os.path.getsize(audio_path) > 0:
|
57 |
logger.info(f"تم إنشاء ملف صوتي: {audio_path}")
|
58 |
return audio_path
|
@@ -64,8 +96,8 @@ async def generate_speech(text, lang):
|
|
64 |
logger.error(f"خطأ في توليد الصوت: {str(e)}")
|
65 |
return None
|
66 |
|
67 |
-
def text_to_speech(text, lang, progress=gr.Progress()):
|
68 |
-
"""واجهة لتحويل النص إلى صوت"""
|
69 |
if not text:
|
70 |
logger.warning("لم يتم تقديم نص للتحويل إلى صوت")
|
71 |
return None
|
@@ -82,7 +114,7 @@ def text_to_speech(text, lang, progress=gr.Progress()):
|
|
82 |
audio_files = []
|
83 |
for i, part in enumerate(text_parts):
|
84 |
progress((i + 1) / len(text_parts), desc=f"معالجة الجزء {i+1} من {len(text_parts)}...")
|
85 |
-
audio_path = asyncio.run(generate_speech(part, lang))
|
86 |
if audio_path:
|
87 |
audio_files.append(audio_path)
|
88 |
|
@@ -276,8 +308,22 @@ def create_ui():
|
|
276 |
elem_classes=["ltr"]
|
277 |
)
|
278 |
with gr.Row():
|
279 |
-
|
280 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
281 |
|
282 |
with gr.TabItem("🔄 النص المترجم"):
|
283 |
translated_text = gr.Textbox(
|
@@ -286,8 +332,22 @@ def create_ui():
|
|
286 |
elem_classes=["rtl"]
|
287 |
)
|
288 |
with gr.Row():
|
289 |
-
|
290 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
291 |
|
292 |
with gr.Row():
|
293 |
download_btn = gr.File(
|
@@ -318,13 +378,13 @@ def create_ui():
|
|
318 |
|
319 |
generate_original_audio.click(
|
320 |
fn=text_to_speech,
|
321 |
-
inputs=[original_text, source_lang],
|
322 |
outputs=[original_audio]
|
323 |
)
|
324 |
|
325 |
generate_translated_audio.click(
|
326 |
fn=text_to_speech,
|
327 |
-
inputs=[translated_text, target_lang],
|
328 |
outputs=[translated_audio]
|
329 |
)
|
330 |
|
@@ -336,4 +396,4 @@ if __name__ == "__main__":
|
|
336 |
demo = create_ui()
|
337 |
demo.launch()
|
338 |
except Exception as e:
|
339 |
-
logger.error(f"خطأ في تشغيل التطبيق: {str(e)}")
|
|
|
31 |
'de': 'Deutsch'
|
32 |
}
|
33 |
|
34 |
+
# تعيين أصوات لكل لغة مع خيارات الجنس
|
35 |
VOICE_MAPPINGS = {
|
36 |
+
'ar': {
|
37 |
+
'male': 'ar-EG-ShakirNeural',
|
38 |
+
'female': 'ar-EG-SalmaNeural'
|
39 |
+
},
|
40 |
+
'en': {
|
41 |
+
'male': 'en-US-EricNeural',
|
42 |
+
'female': 'en-US-JennyNeural'
|
43 |
+
},
|
44 |
+
'fr': {
|
45 |
+
'male': 'fr-FR-HenriNeural',
|
46 |
+
'female': 'fr-FR-DeniseNeural'
|
47 |
+
},
|
48 |
+
'es': {
|
49 |
+
'male': 'es-ES-AlvaroNeural',
|
50 |
+
'female': 'es-ES-ElviraNeural'
|
51 |
+
},
|
52 |
+
'de': {
|
53 |
+
'male': 'de-DE-ConradNeural',
|
54 |
+
'female': 'de-DE-KatjaNeural'
|
55 |
+
}
|
56 |
+
}
|
57 |
+
|
58 |
+
# تعريف أنماط الصوت
|
59 |
+
VOICE_STYLES = {
|
60 |
+
'normal': {'rate': '1.0', 'volume': '1.0', 'pitch': '1.0'},
|
61 |
+
'excited': {'rate': '1.2', 'volume': '1.5', 'pitch': '1.3'},
|
62 |
+
'calm': {'rate': '0.9', 'volume': '0.8', 'pitch': '0.9'},
|
63 |
+
'angry': {'rate': '1.1', 'volume': '1.4', 'pitch': '0.8'},
|
64 |
+
'broadcaster': {'rate': '1.1', 'volume': '1.2', 'pitch': '1.1'}
|
65 |
}
|
66 |
|
67 |
# تحديد اللغات RTL
|
68 |
RTL_LANGUAGES = ['ar']
|
69 |
|
70 |
+
async def generate_speech(text, lang, gender='male', style='normal'):
|
71 |
+
"""توليد الصوت باستخدام edge-tts مع خيارات متقدمة"""
|
72 |
try:
|
73 |
+
if not text:
|
74 |
+
logger.warning("لم يتم تقديم نص للتحويل إلى صوت")
|
75 |
+
return None
|
76 |
+
|
77 |
+
voice = VOICE_MAPPINGS[lang][gender]
|
78 |
+
style_params = VOICE_STYLES[style]
|
79 |
+
|
80 |
+
# إنشاء سلسلة الخيارات للصوت
|
81 |
+
voice_options = f"rate={style_params['rate']} volume={style_params['volume']} pitch={style_params['pitch']}"
|
82 |
+
|
83 |
+
communicate = edge_tts.Communicate(text, voice, options=voice_options)
|
84 |
|
85 |
audio_path = tempfile.mktemp(suffix='.mp3')
|
86 |
await communicate.save(audio_path)
|
87 |
|
|
|
88 |
if os.path.exists(audio_path) and os.path.getsize(audio_path) > 0:
|
89 |
logger.info(f"تم إنشاء ملف صوتي: {audio_path}")
|
90 |
return audio_path
|
|
|
96 |
logger.error(f"خطأ في توليد الصوت: {str(e)}")
|
97 |
return None
|
98 |
|
99 |
+
def text_to_speech(text, lang, gender='male', style='normal', progress=gr.Progress()):
|
100 |
+
"""واجهة لتحويل النص إلى صوت مع خيارات متقدمة"""
|
101 |
if not text:
|
102 |
logger.warning("لم يتم تقديم نص للتحويل إلى صوت")
|
103 |
return None
|
|
|
114 |
audio_files = []
|
115 |
for i, part in enumerate(text_parts):
|
116 |
progress((i + 1) / len(text_parts), desc=f"معالجة الجزء {i+1} من {len(text_parts)}...")
|
117 |
+
audio_path = asyncio.run(generate_speech(part, lang, gender, style))
|
118 |
if audio_path:
|
119 |
audio_files.append(audio_path)
|
120 |
|
|
|
308 |
elem_classes=["ltr"]
|
309 |
)
|
310 |
with gr.Row():
|
311 |
+
with gr.Column():
|
312 |
+
original_gender = gr.Radio(
|
313 |
+
choices=["male", "female"],
|
314 |
+
value="male",
|
315 |
+
label="🧑 جنس المتحدث",
|
316 |
+
info="اختر جنس المتحدث"
|
317 |
+
)
|
318 |
+
original_style = gr.Dropdown(
|
319 |
+
choices=list(VOICE_STYLES.keys()),
|
320 |
+
value="normal",
|
321 |
+
label="🎭 نمط الصوت",
|
322 |
+
info="اختر نمط الصوت المناسب"
|
323 |
+
)
|
324 |
+
with gr.Column():
|
325 |
+
generate_original_audio = gr.Button("🔊 توليد الصوت", variant="secondary")
|
326 |
+
original_audio = gr.Audio(label="الصوت", visible=True)
|
327 |
|
328 |
with gr.TabItem("🔄 النص المترجم"):
|
329 |
translated_text = gr.Textbox(
|
|
|
332 |
elem_classes=["rtl"]
|
333 |
)
|
334 |
with gr.Row():
|
335 |
+
with gr.Column():
|
336 |
+
translated_gender = gr.Radio(
|
337 |
+
choices=["male", "female"],
|
338 |
+
value="male",
|
339 |
+
label="🧑 جنس المتحدث",
|
340 |
+
info="اختر جنس المتحدث"
|
341 |
+
)
|
342 |
+
translated_style = gr.Dropdown(
|
343 |
+
choices=list(VOICE_STYLES.keys()),
|
344 |
+
value="normal",
|
345 |
+
label="🎭 نمط الصوت",
|
346 |
+
info="اختر نمط الصوت المناسب"
|
347 |
+
)
|
348 |
+
with gr.Column():
|
349 |
+
generate_translated_audio = gr.Button("🔊 توليد الصوت", variant="secondary")
|
350 |
+
translated_audio = gr.Audio(label="الصوت", visible=True)
|
351 |
|
352 |
with gr.Row():
|
353 |
download_btn = gr.File(
|
|
|
378 |
|
379 |
generate_original_audio.click(
|
380 |
fn=text_to_speech,
|
381 |
+
inputs=[original_text, source_lang, original_gender, original_style],
|
382 |
outputs=[original_audio]
|
383 |
)
|
384 |
|
385 |
generate_translated_audio.click(
|
386 |
fn=text_to_speech,
|
387 |
+
inputs=[translated_text, target_lang, translated_gender, translated_style],
|
388 |
outputs=[translated_audio]
|
389 |
)
|
390 |
|
|
|
396 |
demo = create_ui()
|
397 |
demo.launch()
|
398 |
except Exception as e:
|
399 |
+
logger.error(f"خطأ في تشغيل التطبيق: {str(e)}")
|