Opera8 commited on
Commit
0e07998
·
verified ·
1 Parent(s): d63f389

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +41 -180
app.py CHANGED
@@ -6,11 +6,6 @@ import traceback
6
  import threading
7
  import os
8
 
9
- # --- دیکشنری زبان‌ها و صداها با کلیدهای فارسی (نمونه) ---
10
- # توجه: این فقط یک نمونه کوچک است. شما باید کل دیکشنری را به این شکل فارسی کنید.
11
- # برای سادگی، من فقط چند مورد اول را تغییر می دهم و بقیه را انگلیسی نگه می دارم.
12
- # شما باید برای هر کلید، نام زبان و جنسیت را به فارسی ترجمه کنید.
13
-
14
  language_dict_persian_keys = {
15
  'انگلیسی - جنی (زن)': 'en-US-JennyNeural',
16
  'انگلیسی - گای (مرد)': 'en-US-GuyNeural',
@@ -21,7 +16,7 @@ language_dict_persian_keys = {
21
  'انگلیسی - میشل (زن)': 'en-US-MichelleNeural',
22
  'انگلیسی - راجر (مرد)': 'en-US-RogerNeural',
23
  'اسپانیایی (مکزیک) - دالیا (زن)': 'es-MX-DaliaNeural',
24
- 'اسپانیایی (مکزیک) - خورخه (مرد)': 'es-MX-JorgeNeural', # نام خورخه ممکن است دقیق نباشد
25
  'کره‌ای - سان-هی (زن)': 'ko-KR-SunHiNeural',
26
  'کره‌ای - این‌جون (مرد)': 'ko-KR-InJoonNeural',
27
  'تایلندی - پرموادی (زن)': 'th-TH-PremwadeeNeural',
@@ -43,13 +38,13 @@ language_dict_persian_keys = {
43
  'هلندی - کولت (زن)': 'nl-NL-ColetteNeural',
44
  'هلندی - فنا (زن)': 'nl-NL-FennaNeural',
45
  'هلندی - مارتن (مرد)': 'nl-NL-MaartenNeural',
46
- 'مالایی - عثمان (مرد)': 'ms-MY-OsmanNeural', # "Malese" به "مالایی"
47
  'مالایی - یاسمین (زن)': 'ms-MY-YasminNeural',
48
  'نروژی - پرنیل (زن)': 'nb-NO-PernilleNeural',
49
  'نروژی - فین (مرد)': 'nb-NO-FinnNeural',
50
  'سوئدی - سوفی (زن)': 'sv-SE-SofieNeural',
51
  'سوئدی - ماتیاس (مرد)': 'sv-SE-MattiasNeural',
52
- 'عربی (عربستان) - حامد (مرد)': 'ar-SA-HamedNeural', # "عربی" به "عربی (عربستان)"
53
  'عربی (عربستان) - زاریا (زن)': 'ar-SA-ZariyahNeural',
54
  'یونانی - آتنا (زن)': 'el-GR-AthinaNeural',
55
  'یونانی - نستوراس (مرد)': 'el-GR-NestorasNeural',
@@ -57,9 +52,9 @@ language_dict_persian_keys = {
57
  'آلمانی - آمالا (زن)': 'de-DE-AmalaNeural',
58
  'آلمانی - کنراد (مرد)': 'de-DE-ConradNeural',
59
  'آلمانی - کیلیان (مرد)': 'de-DE-KillianNeural',
60
- 'آفریقایی - آدری (زن)': 'af-ZA-AdriNeural', # "Afrikaans" به "آفریقایی"
61
  'آفریقایی - ویلم (مرد)': 'af-ZA-WillemNeural',
62
- 'اتیوپیایی - آمه‌ها (مرد)': 'am-ET-AmehaNeural', # "Ethiopian" به "اتیوپیایی"
63
  'اتیوپیایی - مکدس (زن)': 'am-ET-MekdesNeural',
64
  'عربی (امارات) - فاطمه (زن)': 'ar-AE-FatimaNeural',
65
  'عربی (امارات) - حمدان (مرد)': 'ar-AE-HamdanNeural',
@@ -69,9 +64,9 @@ language_dict_persian_keys = {
69
  'عربی (مصر) - سلما (زن)': 'ar-EG-SalmaNeural',
70
  'عربی (مصر) - شاکر (مرد)': 'ar-EG-ShakirNeural',
71
  'عربی (عراق) - باسل (مرد)': 'ar-IQ-BasselNeural',
72
- 'عربی (عراق) - رعنا (زن)': 'ar-IQ-RanaNeural', # "Rana" به "رعنا"
73
  'عربی (اردن) - سانا (زن)': 'ar-JO-SanaNeural',
74
- 'عربی (اردن) - تایم (مرد)': 'ar-JO-TaimNeural', # "Taim"
75
  'عربی (کویت) - فهد (مرد)': 'ar-KW-FahedNeural',
76
  'عربی (کویت) - نورا (زن)': 'ar-KW-NouraNeural',
77
  'عربی (لبنان) - لیلا (زن)': 'ar-LB-LaylaNeural',
@@ -82,11 +77,11 @@ language_dict_persian_keys = {
82
  'عربی (مراکش) - مونا (زن)': 'ar-MA-MounaNeural',
83
  'عربی (عمان) - عبدالله (مرد)': 'ar-OM-AbdullahNeural',
84
  'عربی (عمان) - عایشه (زن)': 'ar-OM-AyshaNeural',
85
- 'عربی (قطر) - امل (زن)': 'ar-QA-AmalNeural', # "Amal"
86
  'عربی (قطر) - معاذ (مرد)': 'ar-QA-MoazNeural',
87
  'عربی (سوریه) - امانی (زن)': 'ar-SY-AmanyNeural',
88
  'عربی (سوریه) - لیث (مرد)': 'ar-SY-LaithNeural',
89
- 'عربی (تونس) - هادی (مرد)': 'ar-TN-HediNeural', # "Hedi"
90
  'عربی (تونس) - ریم (زن)': 'ar-TN-ReemNeural',
91
  'عربی (یمن) - مریم (زن)': 'ar-YE-MaryamNeural',
92
  'عربی (یمن) - صالح (مرد)': 'ar-YE-SalehNeural',
@@ -97,14 +92,14 @@ language_dict_persian_keys = {
97
  'بنگالی (بنگلادش) - نابانیتا (زن)': 'bn-BD-NabanitaNeural',
98
  'بنگالی (بنگلادش) - پرادیپ (مرد)': 'bn-BD-PradeepNeural',
99
  'بنگالی (هند) - باشکار (مرد)': 'bn-IN-BashkarNeural',
100
- 'بنگالی (هند) - تانیشا (زن)': 'bn-IN-TanishaaNeural', # "Tanishaa"
101
- 'بوسنیایی - گوران (مرد)': 'bs-BA-GoranNeural', # "Bosnian" به "بوسنیایی"
102
  'بوسنیایی - وسنا (زن)': 'bs-BA-VesnaNeural',
103
- 'کاتالان (اسپانیا) - جوآنا (زن)': 'ca-ES-JoanaNeural', # "Catalan"
104
  'کاتالان (اسپانیا) - انریک (مرد)': 'ca-ES-EnricNeural',
105
- 'چکی - آنتونین (مرد)': 'cs-CZ-AntoninNeural', # "Czech" به "چکی"
106
  'چکی - ولاستا (زن)': 'cs-CZ-VlastaNeural',
107
- 'ولزی (بریتانیا) - آلد (مرد)': 'cy-GB-AledNeural', # "Welsh"
108
  'ولزی (بریتانیا) - نیا (زن)': 'cy-GB-NiaNeural',
109
  'دانمارکی - کریستل (زن)': 'da-DK-ChristelNeural',
110
  'دانمارکی - یپه (مرد)': 'da-DK-JeppeNeural',
@@ -175,13 +170,13 @@ language_dict_persian_keys = {
175
  'اسپانیایی (پاراگوئه) - تانیا (زن)': 'es-PY-TaniaNeural',
176
  'اسپانیایی (السالوادور) - لورنا (زن)': 'es-SV-LorenaNeural',
177
  'اسپانیایی (السالوادور) - رودریگو (مرد)': 'es-SV-RodrigoNeural',
178
- 'اسپانیایی (آمریکا) - آلونسو (مرد)': 'es-US-AlonsoNeural', # "United States" به "آمریکا"
179
  'اسپانیایی (آمریکا) - پالوما (زن)': 'es-US-PalomaNeural',
180
  'اسپانیایی (اروگوئه) - ماتئو (مرد)': 'es-UY-MateoNeural',
181
  'اسپانیایی (اروگوئه) - والنتینا (زن)': 'es-UY-ValentinaNeural',
182
  'اسپانیایی (ونزوئلا) - پائولا (زن)': 'es-VE-PaolaNeural',
183
  'اسپانیایی (ونزوئلا) - سباستین (مرد)': 'es-VE-SebastianNeural',
184
- 'استونیایی - آنو (زن)': 'et-EE-AnuNeural', # "Estonian"
185
  'استونیایی - کرت (مرد)': 'et-EE-KertNeural',
186
  'فارسی (ایران) - دل‌آرا (زن)': 'fa-IR-DilaraNeural',
187
  'فارسی (ایران) - فرید (مرد)': 'fa-IR-FaridNeural',
@@ -196,7 +191,7 @@ language_dict_persian_keys = {
196
  'فرانسوی (سوئیس) - فابریس (مرد)': 'fr-CH-FabriceNeural',
197
  'ایرلندی - کلم (مرد)': 'ga-IE-ColmNeural',
198
  'ایرلندی - اورلا (زن)': 'ga-IE-OrlaNeural',
199
- 'گالیسی (اسپانیا) - روی (مرد)': 'gl-ES-RoiNeural', # "Galician"
200
  'گالیسی (اسپانیا) - سابلا (زن)': 'gl-ES-SabelaNeural',
201
  'گجراتی (هند) - دوانی (زن)': 'gu-IN-DhwaniNeural',
202
  'گجراتی (هند) - نیرانجان (مرد)': 'gu-IN-NiranjanNeural',
@@ -204,7 +199,7 @@ language_dict_persian_keys = {
204
  'عبری (اسرائیل) - هیلا (زن)': 'he-IL-HilaNeural',
205
  'هندی (هند) - مادور (مرد)': 'hi-IN-MadhurNeural',
206
  'هندی (هند) - سوارا (زن)': 'hi-IN-SwaraNeural',
207
- 'کروات - گابریلا (زن)': 'hr-HR-GabrijelaNeural', # "Croatian"
208
  'کروات - سرچکو (مرد)': 'hr-HR-SreckoNeural',
209
  'مجاری - نوئمی (زن)': 'hu-HU-NoemiNeural',
210
  'مجاری - تاماش (مرد)': 'hu-HU-TamasNeural',
@@ -212,15 +207,15 @@ language_dict_persian_keys = {
212
  'ارمنی - هایک (مرد)': 'hy-AM-HaykNeural',
213
  'ایسلندی - گودرون (زن)': 'is-IS-GudrunNeural',
214
  'ایسلندی - گونار (مرد)': 'is-IS-GunnarNeural',
215
- 'جاوه‌ای (اندونزی) - دیماس (مرد)': 'jv-ID-DimasNeural', # "Javanese"
216
  'جاوه‌ای (اندونزی) - سیتی (زن)': 'jv-ID-SitiNeural',
217
  'گرجی - اکا (زن)': 'ka-GE-EkaNeural',
218
  'گرجی - گیورگی (مرد)': 'ka-GE-GiorgiNeural',
219
  'قزاقی - آیگول (زن)': 'kk-KZ-AigulNeural',
220
  'قزاقی - دولت (مرد)': 'kk-KZ-DauletNeural',
221
- 'خمر (کامبوج) - پیست (مرد)': 'km-KH-PisethNeural', # "Khmer"
222
  'خمر (کامبوج) - سری‌مم (زن)': 'km-KH-SreymomNeural',
223
- 'کانادایی (هند) - گاگان (مرد)': 'kn-IN-GaganNeural', # "Kannada"
224
  'کانادایی (هند) - ساپنا (زن)': 'kn-IN-SapnaNeural',
225
  'لائوسی - چانتاونگ (مرد)': 'lo-LA-ChanthavongNeural',
226
  'لائوسی - کئومانی (زن)': 'lo-LA-KeomanyNeural',
@@ -236,7 +231,7 @@ language_dict_persian_keys = {
236
  'مغولی - یسوی (زن)': 'mn-MN-YesuiNeural',
237
  'مراتی (هند) - آروهی (زن)': 'mr-IN-AarohiNeural',
238
  'مراتی (هند) - مانوهار (مرد)': 'mr-IN-ManoharNeural',
239
- 'مالتی (مالت) - گریس (زن)': 'mt-MT-GraceNeural', # "Maltese"
240
  'مالتی (مالت) - جوزف (مرد)': 'mt-MT-JosephNeural',
241
  'برمه‌ای (میانمار) - نیلار (زن)': 'my-MM-NilarNeural',
242
  'برمه‌ای (میانمار) - تیها (مرد)': 'my-MM-ThihaNeural',
@@ -254,7 +249,7 @@ language_dict_persian_keys = {
254
  'رومانیایی - امیل (مرد)': 'ro-RO-EmilNeural',
255
  'روسی - دیمیتری (مرد)': 'ru-RU-DmitryNeural',
256
  'روسی - سوتلانا (زن)': 'ru-RU-SvetlanaNeural',
257
- 'سینهالی (سریلانکا) - دینوکا (مرد)': 'si-LK-DinukaNeural', # "Sinhala"
258
  'سینهالی (سریلانکا) - تیلینی (زن)': 'si-LK-ThiliniNeural',
259
  'اسلواک - لوکاش (مرد)': 'sk-SK-LukasNeural',
260
  'اسلواک - ویکتوریا (زن)': 'sk-SK-ViktoriaNeural',
@@ -266,7 +261,7 @@ language_dict_persian_keys = {
266
  'آلبانیایی - ایلیر (مرد)': 'sq-AL-IlirNeural',
267
  'صربی - نیکولا (مرد)': 'sr-RS-NikolaNeural',
268
  'صربی - سوفی (زن)': 'sr-RS-SophieNeural',
269
- 'سوندانی (اندونزی) - جاجانگ (مرد)': 'su-ID-JajangNeural', # "Sundanese"
270
  'سوندانی (اندونزی) - توتی (زن)': 'su-ID-TutiNeural',
271
  'سواحیلی (کنیا) - رفیقی (مرد)': 'sw-KE-RafikiNeural',
272
  'سواحیلی (کنیا) - زوری (زن)': 'sw-KE-ZuriNeural',
@@ -302,26 +297,18 @@ language_dict_persian_keys = {
302
  'زولو (آفریقای جنوبی) - تمبا (مرد)': 'zu-ZA-ThembaNeural',
303
  }
304
 
305
- # --- توابع تبدیل متن به گفتار و wrapper (همانند قبل) ---
306
- async def text_to_speech_edge_async(text, language_code_persian, rate, volume, pitch): # language_code_persian نام پارامتر تغییر کرد
307
  try:
308
  if not text: return "خطا: لطفاً متنی را برای تبدیل وارد کنید.", None
309
- # دریافت voice_id از دیکشنری با کلیدهای فارسی
310
  voice_id = language_dict_persian_keys.get(language_code_persian)
311
- if voice_id is None: return f"خطا: مدل صدای انتخاب شده ('{language_code_persian}') یافت نشد.", None
312
  rate_str, volume_str, pitch_str = f"{int(rate):+g}%", f"{int(volume):+g}%", f"{int(pitch):+g}Hz"
313
  communicate = edge_tts.Communicate(text, voice_id, rate=rate_str, volume=volume_str, pitch=pitch_str)
314
  with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as tmp_file: tmp_path = tmp_file.name
315
  await communicate.save(tmp_path)
316
  return "تبدیل با موفقیت انجام شد.", tmp_path
317
- except edge_tts.exceptions.NoAudioReceived:
318
- error_msg = f"خطا: صدایی برای متن و صدای انتخاب شده دریافت نشد (صدا: {voice_id})."
319
- return error_msg, None
320
- except ValueError as ve:
321
- error_msg = f"خطا در پارامترهای ورودی: {ve}" # حذف اشاره به edge-tts
322
- return error_msg, None
323
  except Exception as e:
324
- return f"خطای غیرمنتظره در سرور: {type(e).__name__}", None
325
 
326
  _event_loops_by_thread = {}
327
  def _get_or_create_event_loop():
@@ -334,163 +321,37 @@ def text_to_speech_edge_sync_wrapper(text, language_code_persian, rate, volume,
334
  try:
335
  loop = _get_or_create_event_loop(); asyncio.set_event_loop(loop)
336
  result = loop.run_until_complete(text_to_speech_edge_async(text, language_code_persian, rate, volume, pitch))
337
- except RuntimeError as e:
338
- if "no current event loop" in str(e).lower() or "cannot be called from a running event loop" in str(e).lower():
339
- new_loop = asyncio.new_event_loop(); asyncio.set_event_loop(new_loop)
340
- try: result = new_loop.run_until_complete(text_to_speech_edge_async(text, language_code_persian, rate, volume, pitch))
341
- finally: new_loop.close()
342
- else: return f"خطای اجرایی: {e}", None
343
- except Exception as e: return f"خطای غیرمنتظره: {type(e).__name__}", None
344
  return result
345
 
346
- # --- تعریف تم و CSS ---
347
  app_theme = gr.themes.Soft(
348
  primary_hue=gr.themes.colors.blue,
349
- secondary_hue=gr.themes.colors.sky,
350
- neutral_hue=gr.themes.colors.slate,
351
  font=[gr.themes.GoogleFont("Vazirmatn"), "Arial", "sans-serif"],
352
- ).set(
353
- body_background_fill="#f4f7f6",
354
  )
355
 
356
  custom_css = """
357
  body { font-family: 'Vazirmatn', 'Arial', sans-serif; direction: rtl; }
358
- .gradio-container {
359
- max-width: 95% !important; margin: 1rem auto !important; padding: 1rem !important;
360
- border-radius: 16px !important; box-shadow: 0 6px 20px rgba(0, 0, 0, 0.07) !important;
361
- background-color: #ffffff !important;
362
- }
363
- .app-header {
364
- text-align: center; padding: 20px 10px; background: #34495e; color: white;
365
- border-radius: 12px; margin-bottom: 1.5rem;
366
- }
367
- .app-header img.logo {
368
- width: 50px; height: auto; margin-bottom: 5px;
369
- animation: float_soft 4s ease-in-out infinite alternate;
370
- }
371
- .app-header h1 {
372
- color: white !important; font-size: 1.6em !important; font-weight: 600 !important;
373
- margin: 5px 0;
374
- }
375
- .app-header p { color: #bdc3c7 !important; font-size: 0.9em !important; margin-top: 5px; }
376
- .main-content-row > .gr-column { margin-bottom: 1rem; }
377
-
378
- .gr-button.lg.primary {
379
- background: #3498db !important; color: white !important; font-weight: 500 !important;
380
- border-radius: 8px !important; padding: 12px 15px !important; width: 100% !important;
381
- font-size: 1em !important; transition: all 0.2s ease !important;
382
- box-shadow: 0 3px 6px rgba(52, 152, 219, 0.25) !important; border: none !important;
383
- }
384
- .gr-button.lg.primary:hover {
385
- background: #2980b9 !important; transform: translateY(-2px) !important;
386
- box-shadow: 0 5px 10px rgba(52, 152, 219, 0.35) !important;
387
- }
388
- .gr-input, .gr-dropdown, .gr-textbox, .gr-slider {
389
- border-radius: 8px !important; border: 1px solid #ced4da !important;
390
- font-size: 0.95em !important;
391
- }
392
- .gr-input:focus, .gr-dropdown:focus, .gr-textbox:focus {
393
- box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.15) !important;
394
- border-color: #5dade2 !important;
395
- }
396
- .gr-panel {
397
- border-radius: 10px !important; border: 1px solid #e9ecef !important;
398
- background-color: #f8f9fa !important; padding: 0.75rem !important;
399
- }
400
- label > span {
401
- font-weight: 500 !important; color: #495057 !important; font-size: 0.9em !important;
402
- margin-bottom: 3px !important; display: block;
403
- }
404
- .gr-examples table { font-size: 0.85em; }
405
- .gr-examples th, .gr-examples td { padding: 6px 8px !important; }
406
-
407
- footer { display: none !important; visibility: hidden !important; }
408
- .gradio-footer { display: none !important; visibility: hidden !important; }
409
- .flagging-container { display: none !important; visibility: hidden !important; }
410
- .flex.row.gap-2.absolute.bottom-2.right-2.gr-compact.gr-box.gr-text-gray-500 { display: none !important; visibility: hidden !important; }
411
- div[data-testid="flag"] { display: none !important; }
412
- button[title="Flag"], button[aria-label="Flag"] {display: none !important; }
413
- .footer-utils { display: none !important; visibility: hidden !important; }
414
-
415
- @keyframes float_soft {
416
- 0% { transform: translatey(0px) scale(1); }
417
- 50% { transform: translatey(-5px) scale(1.05); }
418
- 100% { transform: translatey(0px) scale(1); }
419
- }
420
- @media (min-width: 768px) {
421
- .gradio-container { max-width: 800px !important; padding: 1.5rem !important;}
422
- .app-header h1 { font-size: 2em !important; }
423
- .app-header p { font-size: 1em !important; }
424
- .main-content-row { display: flex; flex-direction: row; gap: 1.5rem; }
425
- .main-content-row > .gr-column { flex: 1; margin-bottom: 0; }
426
- .main-content-row > .gr-column:nth-child(1) { flex-basis: 60%; }
427
- .main-content-row > .gr-column:nth-child(2) { flex-basis: 40%; }
428
- .gr-button.lg.primary { width: auto !important; }
429
- }
430
  """
431
 
432
- # انتخاب صدای پیش فرض فارسی
433
  default_voice_key_persian = 'فارسی (ایران) - فرید (مرد)'
434
- if default_voice_key_persian not in language_dict_persian_keys:
435
- # اگر به هر دلیلی صدای پیش فرض فارسی ما در لیست نبود، اولین مورد لیست را انتخاب کن
436
- default_voice_key_persian = list(language_dict_persian_keys.keys())[0] if language_dict_persian_keys else None
437
-
438
- # LOGO_URL دیگر استفاده نمی‌شود چون لوگو حذف شده است، اما برای حفظ سایر بخش‌های کد دست نخورده، آن را نگه می‌داریم
439
- LOGO_URL = "https://www.gstatic.com/lamda/images/gemini/google_bard_logo_150_v2_dark_color_1x.png"
440
 
441
  with gr.Blocks(theme=app_theme, css=custom_css) as demo:
442
- with gr.Row():
443
- gr.HTML(f"""
444
- <div class="app-header">
445
- <h1>مبدل هوشمند متن به گفتار</h1>
446
- <p>با کیفیت صدای طبیعی و روان، متن خود را زنده کنید</p>
447
- </div>
448
- """)
449
-
450
- with gr.Row(elem_classes="main-content-row"):
451
- with gr.Column(scale=3):
452
- input_text = gr.Textbox(
453
- lines=5,
454
- label="📝 متن خود را برای تبدیل وارد نمایید",
455
- placeholder="اینجا بنویسید...",
456
- value=""
457
- )
458
- language_dropdown = gr.Dropdown( # نام متغیر برای وضوح بیشتر
459
- choices=list(language_dict_persian_keys.keys()),
460
- value=default_voice_key_persian,
461
- label="🗣️ زبان و گوینده را انتخاب کنید"
462
- )
463
- with gr.Accordion("⚙️ تنظیمات پیشرفته صدا (اختیاری)", open=False):
464
- with gr.Row():
465
- rate_slider = gr.Slider(minimum=-100, maximum=100, step=1, value=0, label="سرعت (%)", scale=1)
466
- volume_slider = gr.Slider(minimum=-100, maximum=100, step=1, value=0, label="حجم (%)", scale=1)
467
- pitch_slider = gr.Slider(minimum=-50, maximum=50, step=1, value=0, label="گام (Hz)", scale=2)
468
-
469
- submit_button = gr.Button("🔊 تولید و پخش صدا", variant="primary")
470
-
471
- with gr.Column(scale=2):
472
- output_text_status = gr.Textbox(label="📊 وضعیت عملیات", interactive=False, lines=1, placeholder="نتیجه اینجا نمایش داده می‌شود...")
473
- output_audio = gr.Audio(type="filepath", label="🎧 فایل صوتی خروجی", interactive=False)
474
-
475
- gr.HTML("<hr style='margin-top: 1rem; margin-bottom: 1rem; border: 0; border-top: 1px solid #dee2e6;'>")
476
-
477
- gr.Examples(
478
- examples=[
479
- ["سلام بر شما، روز خوبی داشته باشید.", 'فارسی (ایران) - دل‌آرا (زن)', 0, 0, 0],
480
- ["This is a test of the speech synthesis system.", 'انگلیسی - جنی (زن)', +5, 0, 0],
481
- ["تجربه کاربری در این سامانه بسیار روان است.", 'فارسی (ایران) - فرید (مرد)', -5, 0, 0],
482
- ],
483
- inputs=[input_text, language_dropdown, rate_slider, volume_slider, pitch_slider], # استفاده از نام متغیرهای جدید
484
- outputs=[output_text_status, output_audio],
485
- fn=text_to_speech_edge_sync_wrapper,
486
- cache_examples=False,
487
- label="💡 چند نمونه برای شروع"
488
- )
489
 
490
  submit_button.click(
491
  fn=text_to_speech_edge_sync_wrapper,
492
- inputs=[input_text, language_dropdown, rate_slider, volume_slider, pitch_slider], # استفاده از نام متغیرهای جدید
493
  outputs=[output_text_status, output_audio],
 
494
  )
495
 
496
- demo.launch()
 
6
  import threading
7
  import os
8
 
 
 
 
 
 
9
  language_dict_persian_keys = {
10
  'انگلیسی - جنی (زن)': 'en-US-JennyNeural',
11
  'انگلیسی - گای (مرد)': 'en-US-GuyNeural',
 
16
  'انگلیسی - میشل (زن)': 'en-US-MichelleNeural',
17
  'انگلیسی - راجر (مرد)': 'en-US-RogerNeural',
18
  'اسپانیایی (مکزیک) - دالیا (زن)': 'es-MX-DaliaNeural',
19
+ 'اسپانیایی (مکزیک) - خورخه (مرد)': 'es-MX-JorgeNeural',
20
  'کره‌ای - سان-هی (زن)': 'ko-KR-SunHiNeural',
21
  'کره‌ای - این‌جون (مرد)': 'ko-KR-InJoonNeural',
22
  'تایلندی - پرموادی (زن)': 'th-TH-PremwadeeNeural',
 
38
  'هلندی - کولت (زن)': 'nl-NL-ColetteNeural',
39
  'هلندی - فنا (زن)': 'nl-NL-FennaNeural',
40
  'هلندی - مارتن (مرد)': 'nl-NL-MaartenNeural',
41
+ 'مالایی - عثمان (مرد)': 'ms-MY-OsmanNeural',
42
  'مالایی - یاسمین (زن)': 'ms-MY-YasminNeural',
43
  'نروژی - پرنیل (زن)': 'nb-NO-PernilleNeural',
44
  'نروژی - فین (مرد)': 'nb-NO-FinnNeural',
45
  'سوئدی - سوفی (زن)': 'sv-SE-SofieNeural',
46
  'سوئدی - ماتیاس (مرد)': 'sv-SE-MattiasNeural',
47
+ 'عربی (عربستان) - حامد (مرد)': 'ar-SA-HamedNeural',
48
  'عربی (عربستان) - زاریا (زن)': 'ar-SA-ZariyahNeural',
49
  'یونانی - آتنا (زن)': 'el-GR-AthinaNeural',
50
  'یونانی - نستوراس (مرد)': 'el-GR-NestorasNeural',
 
52
  'آلمانی - آمالا (زن)': 'de-DE-AmalaNeural',
53
  'آلمانی - کنراد (مرد)': 'de-DE-ConradNeural',
54
  'آلمانی - کیلیان (مرد)': 'de-DE-KillianNeural',
55
+ 'آفریقایی - آدری (زن)': 'af-ZA-AdriNeural',
56
  'آفریقایی - ویلم (مرد)': 'af-ZA-WillemNeural',
57
+ 'اتیوپیایی - آمه‌ها (مرد)': 'am-ET-AmehaNeural',
58
  'اتیوپیایی - مکدس (زن)': 'am-ET-MekdesNeural',
59
  'عربی (امارات) - فاطمه (زن)': 'ar-AE-FatimaNeural',
60
  'عربی (امارات) - حمدان (مرد)': 'ar-AE-HamdanNeural',
 
64
  'عربی (مصر) - سلما (زن)': 'ar-EG-SalmaNeural',
65
  'عربی (مصر) - شاکر (مرد)': 'ar-EG-ShakirNeural',
66
  'عربی (عراق) - باسل (مرد)': 'ar-IQ-BasselNeural',
67
+ 'عربی (عراق) - رعنا (زن)': 'ar-IQ-RanaNeural',
68
  'عربی (اردن) - سانا (زن)': 'ar-JO-SanaNeural',
69
+ 'عربی (اردن) - تایم (مرد)': 'ar-JO-TaimNeural',
70
  'عربی (کویت) - فهد (مرد)': 'ar-KW-FahedNeural',
71
  'عربی (کویت) - نورا (زن)': 'ar-KW-NouraNeural',
72
  'عربی (لبنان) - لیلا (زن)': 'ar-LB-LaylaNeural',
 
77
  'عربی (مراکش) - مونا (زن)': 'ar-MA-MounaNeural',
78
  'عربی (عمان) - عبدالله (مرد)': 'ar-OM-AbdullahNeural',
79
  'عربی (عمان) - عایشه (زن)': 'ar-OM-AyshaNeural',
80
+ 'عربی (قطر) - امل (زن)': 'ar-QA-AmalNeural',
81
  'عربی (قطر) - معاذ (مرد)': 'ar-QA-MoazNeural',
82
  'عربی (سوریه) - امانی (زن)': 'ar-SY-AmanyNeural',
83
  'عربی (سوریه) - لیث (مرد)': 'ar-SY-LaithNeural',
84
+ 'عربی (تونس) - هادی (مرد)': 'ar-TN-HediNeural',
85
  'عربی (تونس) - ریم (زن)': 'ar-TN-ReemNeural',
86
  'عربی (یمن) - مریم (زن)': 'ar-YE-MaryamNeural',
87
  'عربی (یمن) - صالح (مرد)': 'ar-YE-SalehNeural',
 
92
  'بنگالی (بنگلادش) - نابانیتا (زن)': 'bn-BD-NabanitaNeural',
93
  'بنگالی (بنگلادش) - پرادیپ (مرد)': 'bn-BD-PradeepNeural',
94
  'بنگالی (هند) - باشکار (مرد)': 'bn-IN-BashkarNeural',
95
+ 'بنگالی (هند) - تانیشا (زن)': 'bn-IN-TanishaaNeural',
96
+ 'بوسنیایی - گوران (مرد)': 'bs-BA-GoranNeural',
97
  'بوسنیایی - وسنا (زن)': 'bs-BA-VesnaNeural',
98
+ 'کاتالان (اسپانیا) - جوآنا (زن)': 'ca-ES-JoanaNeural',
99
  'کاتالان (اسپانیا) - انریک (مرد)': 'ca-ES-EnricNeural',
100
+ 'چکی - آنتونین (مرد)': 'cs-CZ-AntoninNeural',
101
  'چکی - ولاستا (زن)': 'cs-CZ-VlastaNeural',
102
+ 'ولزی (بریتانیا) - آلد (مرد)': 'cy-GB-AledNeural',
103
  'ولزی (بریتانیا) - نیا (زن)': 'cy-GB-NiaNeural',
104
  'دانمارکی - کریستل (زن)': 'da-DK-ChristelNeural',
105
  'دانمارکی - یپه (مرد)': 'da-DK-JeppeNeural',
 
170
  'اسپانیایی (پاراگوئه) - تانیا (زن)': 'es-PY-TaniaNeural',
171
  'اسپانیایی (السالوادور) - لورنا (زن)': 'es-SV-LorenaNeural',
172
  'اسپانیایی (السالوادور) - رودریگو (مرد)': 'es-SV-RodrigoNeural',
173
+ 'اسپانیایی (آمریکا) - آلونسو (مرد)': 'es-US-AlonsoNeural',
174
  'اسپانیایی (آمریکا) - پالوما (زن)': 'es-US-PalomaNeural',
175
  'اسپانیایی (اروگوئه) - ماتئو (مرد)': 'es-UY-MateoNeural',
176
  'اسپانیایی (اروگوئه) - والنتینا (زن)': 'es-UY-ValentinaNeural',
177
  'اسپانیایی (ونزوئلا) - پائولا (زن)': 'es-VE-PaolaNeural',
178
  'اسپانیایی (ونزوئلا) - سباستین (مرد)': 'es-VE-SebastianNeural',
179
+ 'استونیایی - آنو (زن)': 'et-EE-AnuNeural',
180
  'استونیایی - کرت (مرد)': 'et-EE-KertNeural',
181
  'فارسی (ایران) - دل‌آرا (زن)': 'fa-IR-DilaraNeural',
182
  'فارسی (ایران) - فرید (مرد)': 'fa-IR-FaridNeural',
 
191
  'فرانسوی (سوئیس) - فابریس (مرد)': 'fr-CH-FabriceNeural',
192
  'ایرلندی - کلم (مرد)': 'ga-IE-ColmNeural',
193
  'ایرلندی - اورلا (زن)': 'ga-IE-OrlaNeural',
194
+ 'گالیسی (اسپانیا) - روی (مرد)': 'gl-ES-RoiNeural',
195
  'گالیسی (اسپانیا) - سابلا (زن)': 'gl-ES-SabelaNeural',
196
  'گجراتی (هند) - دوانی (زن)': 'gu-IN-DhwaniNeural',
197
  'گجراتی (هند) - نیرانجان (مرد)': 'gu-IN-NiranjanNeural',
 
199
  'عبری (اسرائیل) - هیلا (زن)': 'he-IL-HilaNeural',
200
  'هندی (هند) - مادور (مرد)': 'hi-IN-MadhurNeural',
201
  'هندی (هند) - سوارا (زن)': 'hi-IN-SwaraNeural',
202
+ 'کروات - گابریلا (زن)': 'hr-HR-GabrijelaNeural',
203
  'کروات - سرچکو (مرد)': 'hr-HR-SreckoNeural',
204
  'مجاری - نوئمی (زن)': 'hu-HU-NoemiNeural',
205
  'مجاری - تاماش (مرد)': 'hu-HU-TamasNeural',
 
207
  'ارمنی - هایک (مرد)': 'hy-AM-HaykNeural',
208
  'ایسلندی - گودرون (زن)': 'is-IS-GudrunNeural',
209
  'ایسلندی - گونار (مرد)': 'is-IS-GunnarNeural',
210
+ 'جاوه‌ای (اندونزی) - دیماس (مرد)': 'jv-ID-DimasNeural',
211
  'جاوه‌ای (اندونزی) - سیتی (زن)': 'jv-ID-SitiNeural',
212
  'گرجی - اکا (زن)': 'ka-GE-EkaNeural',
213
  'گرجی - گیورگی (مرد)': 'ka-GE-GiorgiNeural',
214
  'قزاقی - آیگول (زن)': 'kk-KZ-AigulNeural',
215
  'قزاقی - دولت (مرد)': 'kk-KZ-DauletNeural',
216
+ 'خمر (کامبوج) - پیست (مرد)': 'km-KH-PisethNeural',
217
  'خمر (کامبوج) - سری‌مم (زن)': 'km-KH-SreymomNeural',
218
+ 'کانادایی (هند) - گاگان (مرد)': 'kn-IN-GaganNeural',
219
  'کانادایی (هند) - ساپنا (زن)': 'kn-IN-SapnaNeural',
220
  'لائوسی - چانتاونگ (مرد)': 'lo-LA-ChanthavongNeural',
221
  'لائوسی - کئومانی (زن)': 'lo-LA-KeomanyNeural',
 
231
  'مغولی - یسوی (زن)': 'mn-MN-YesuiNeural',
232
  'مراتی (هند) - آروهی (زن)': 'mr-IN-AarohiNeural',
233
  'مراتی (هند) - مانوهار (مرد)': 'mr-IN-ManoharNeural',
234
+ 'مالتی (مالت) - گریس (زن)': 'mt-MT-GraceNeural',
235
  'مالتی (مالت) - جوزف (مرد)': 'mt-MT-JosephNeural',
236
  'برمه‌ای (میانمار) - نیلار (زن)': 'my-MM-NilarNeural',
237
  'برمه‌ای (میانمار) - تیها (مرد)': 'my-MM-ThihaNeural',
 
249
  'رومانیایی - امیل (مرد)': 'ro-RO-EmilNeural',
250
  'روسی - دیمیتری (مرد)': 'ru-RU-DmitryNeural',
251
  'روسی - سوتلانا (زن)': 'ru-RU-SvetlanaNeural',
252
+ 'سینهالی (سریلانکا) - دینوکا (مرد)': 'si-LK-DinukaNeural',
253
  'سینهالی (سریلانکا) - تیلینی (زن)': 'si-LK-ThiliniNeural',
254
  'اسلواک - لوکاش (مرد)': 'sk-SK-LukasNeural',
255
  'اسلواک - ویکتوریا (زن)': 'sk-SK-ViktoriaNeural',
 
261
  'آلبانیایی - ایلیر (مرد)': 'sq-AL-IlirNeural',
262
  'صربی - نیکولا (مرد)': 'sr-RS-NikolaNeural',
263
  'صربی - سوفی (زن)': 'sr-RS-SophieNeural',
264
+ 'سوندانی (اندونزی) - جاجانگ (مرد)': 'su-ID-JajangNeural',
265
  'سوندانی (اندونزی) - توتی (زن)': 'su-ID-TutiNeural',
266
  'سواحیلی (کنیا) - رفیقی (مرد)': 'sw-KE-RafikiNeural',
267
  'سواحیلی (کنیا) - زوری (زن)': 'sw-KE-ZuriNeural',
 
297
  'زولو (آفریقای جنوبی) - تمبا (مرد)': 'zu-ZA-ThembaNeural',
298
  }
299
 
300
+ async def text_to_speech_edge_async(text, language_code_persian, rate, volume, pitch):
 
301
  try:
302
  if not text: return "خطا: لطفاً متنی را برای تبدیل وارد کنید.", None
 
303
  voice_id = language_dict_persian_keys.get(language_code_persian)
304
+ if voice_id is None: return f"خطا: مدل صدای انتخاب شده یافت نشد.", None
305
  rate_str, volume_str, pitch_str = f"{int(rate):+g}%", f"{int(volume):+g}%", f"{int(pitch):+g}Hz"
306
  communicate = edge_tts.Communicate(text, voice_id, rate=rate_str, volume=volume_str, pitch=pitch_str)
307
  with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as tmp_file: tmp_path = tmp_file.name
308
  await communicate.save(tmp_path)
309
  return "تبدیل با موفقیت انجام شد.", tmp_path
 
 
 
 
 
 
310
  except Exception as e:
311
+ return f"خطا: {type(e).__name__}", None
312
 
313
  _event_loops_by_thread = {}
314
  def _get_or_create_event_loop():
 
321
  try:
322
  loop = _get_or_create_event_loop(); asyncio.set_event_loop(loop)
323
  result = loop.run_until_complete(text_to_speech_edge_async(text, language_code_persian, rate, volume, pitch))
324
+ except Exception as e: return f"خطا: {type(e).__name__}", None
 
 
 
 
 
 
325
  return result
326
 
 
327
  app_theme = gr.themes.Soft(
328
  primary_hue=gr.themes.colors.blue,
 
 
329
  font=[gr.themes.GoogleFont("Vazirmatn"), "Arial", "sans-serif"],
 
 
330
  )
331
 
332
  custom_css = """
333
  body { font-family: 'Vazirmatn', 'Arial', sans-serif; direction: rtl; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
  """
335
 
 
336
  default_voice_key_persian = 'فارسی (ایران) - فرید (مرد)'
 
 
 
 
 
 
337
 
338
  with gr.Blocks(theme=app_theme, css=custom_css) as demo:
339
+ input_text = gr.Textbox(lines=5, label="متن")
340
+ language_dropdown = gr.Dropdown(choices=list(language_dict_persian_keys.keys()), value=default_voice_key_persian, label="زبان")
341
+ rate_slider = gr.Slider(minimum=-100, maximum=100, step=1, value=0, label="سرعت")
342
+ volume_slider = gr.Slider(minimum=-100, maximum=100, step=1, value=0, label="حجم")
343
+ pitch_slider = gr.Slider(minimum=-50, maximum=50, step=1, value=0, label="گام")
344
+
345
+ submit_button = gr.Button("تولید صدا")
346
+
347
+ output_text_status = gr.Textbox(label="وضعیت")
348
+ output_audio = gr.Audio(type="filepath", label="صدا")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
 
350
  submit_button.click(
351
  fn=text_to_speech_edge_sync_wrapper,
352
+ inputs=[input_text, language_dropdown, rate_slider, volume_slider, pitch_slider],
353
  outputs=[output_text_status, output_audio],
354
+ api_name="predict"
355
  )
356
 
357
+ demo.launch(server_name="0.0.0.0", server_port=7860, cors_allowed_origins=["*"])