Hamed744 commited on
Commit
6371ad1
·
verified ·
1 Parent(s): da89421

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +66 -113
app.py CHANGED
@@ -6,62 +6,54 @@ import traceback
6
  import threading
7
  import os
8
 
9
- # --- دیکشنری زبان‌ها و صداها با اضافه کردن نام فارسی (نمونه) ---
10
- # شما باید این لیست را برای همه زبان ها کامل کنید
11
- # ساختار جدید کلید: 'نام نمایشی فارسی (نام انگلیسی - جنسیت)'
12
- # یا: 'نام نمایشی فارسی (نام انگلیسی)' اگر جنسیت مشخص نیست یا برای همه است
13
- original_language_dict = {
14
- 'English-Jenny (Female)': ('en-US-JennyNeural', 'زن'),
15
- 'English-Guy (Male)': ('en-US-GuyNeural', 'مرد'),
16
- 'English-Ana (Female)': ('en-US-AnaNeural', 'زن'),
17
- 'English-Aria (Female)': ('en-US-AriaNeural', 'زن'),
18
- 'English-Christopher (Male)': ('en-US-ChristopherNeural', 'مرد'),
19
- 'English-Eric (Male)': ('en-US-EricNeural', 'مرد'),
20
- 'English-Michelle (Female)': ('en-US-MichelleNeural', 'زن'),
21
- 'English-Roger (Male)': ('en-US-RogerNeural', 'مرد'),
22
- 'Spanish (Mexican)-Dalia (Female)': ('es-MX-DaliaNeural', 'زن'),
23
- 'Spanish (Mexican)-Jorge- (Male)': ('es-MX-JorgeNeural', 'مرد'),
24
- 'Korean-Sun-Hi- (Female)': ('ko-KR-SunHiNeural', 'زن'),
25
- 'Korean-InJoon- (Male)': ('ko-KR-InJoonNeural', 'مرد'),
26
- 'Persian (Iran)-Dilara- (Female)': ('fa-IR-DilaraNeural', 'زن'), # فارسی اضافه شده
27
- 'Persian (Iran)-Farid- (Male)': ('fa-IR-FaridNeural', 'مرد'), # فارسی اضافه شده
28
- # ... بقیه زبان‌ها را به همین ترتیب با (voice_id, gender_tag) تکمیل کنید ...
29
- # مثال برای زبانی که فقط یک مدل دارد یا جنسیت نامشخص است
30
- # 'Some Language-Model': ('xx-XX-ModelNeural', 'همه'),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  }
32
 
33
- # ساخت دیکشنری جدید برای نمایش و استفاده در برنامه
34
- # کلیدها نام های نمایشی جدید خواهند بود
35
- language_display_dict = {}
36
- for display_key_english, (voice_id, gender_tag) in original_language_dict.items():
37
- # استخراج زبان و نام از کلید انگلیسی
38
- lang_name_part = display_key_english.split('-')[0]
39
- model_name_part = display_key_english.split('-', 1)[1] if '-' in display_key_english else display_key_english
40
-
41
- persian_lang_name = lang_name_part # به طور پیش فرض، اگر ترجمه نداشتیم
42
- if "English" in lang_name_part:
43
- persian_lang_name = "انگلیسی"
44
- elif "Spanish (Mexican)" in lang_name_part:
45
- persian_lang_name = "اسپانیایی (مکزیک)"
46
- elif "Korean" in lang_name_part:
47
- persian_lang_name = "کره‌ای"
48
- elif "Persian (Iran)" in lang_name_part:
49
- persian_lang_name = "فارسی (ایران)"
50
- # ... اضافه کردن ترجمه برای سایر زبان ها ...
51
-
52
- new_display_key = f"{persian_lang_name} - {model_name_part}"
53
- language_display_dict[new_display_key] = {'id': voice_id, 'gender': gender_tag.lower()}
54
-
55
-
56
- # --- توابع تبدیل متن به گفتار و wrapper (همانند قبل، اما از language_display_dict استفاده می کند) ---
57
- async def text_to_speech_edge_async(text, language_display_key, rate, volume, pitch): # ورودی به language_display_key تغییر کرد
58
  try:
59
  if not text: return "خطا: لطفاً متنی را برای تبدیل وارد کنید.", None
60
-
61
- voice_info = language_display_dict.get(language_display_key)
62
- if not voice_info: return f"خطا: مدل صدای انتخاب شده ('{language_display_key}') یافت نشد.", None
63
- voice_id = voice_info['id']
64
-
65
  rate_str, volume_str, pitch_str = f"{int(rate):+g}%", f"{int(volume):+g}%", f"{int(pitch):+g}Hz"
66
  communicate = edge_tts.Communicate(text, voice_id, rate=rate_str, volume=volume_str, pitch=pitch_str)
67
  with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as tmp_file: tmp_path = tmp_file.name
@@ -83,20 +75,20 @@ def _get_or_create_event_loop():
83
  _event_loops_by_thread[thread_id] = asyncio.new_event_loop()
84
  return _event_loops_by_thread[thread_id]
85
 
86
- def text_to_speech_edge_sync_wrapper(text, language_display_key, rate, volume, pitch):
87
  try:
88
  loop = _get_or_create_event_loop(); asyncio.set_event_loop(loop)
89
- result = loop.run_until_complete(text_to_speech_edge_async(text, language_display_key, rate, volume, pitch))
90
  except RuntimeError as e:
91
  if "no current event loop" in str(e).lower() or "cannot be called from a running event loop" in str(e).lower():
92
  new_loop = asyncio.new_event_loop(); asyncio.set_event_loop(new_loop)
93
- try: result = new_loop.run_until_complete(text_to_speech_edge_async(text, language_display_key, rate, volume, pitch))
94
  finally: new_loop.close()
95
  else: return f"خطای اجرایی: {e}", None
96
  except Exception as e: return f"خطای غیرمنتظره: {type(e).__name__}", None
97
  return result
98
 
99
- # --- تعریف تم و CSS (همانند قبل، با تغییرات جزئی در صورت نیاز) ---
100
  app_theme = gr.themes.Soft(
101
  primary_hue=gr.themes.colors.blue,
102
  secondary_hue=gr.themes.colors.sky,
@@ -138,13 +130,10 @@ body { font-family: 'Vazirmatn', 'Arial', sans-serif; direction: rtl; }
138
  background: #2980b9 !important; transform: translateY(-2px) !important;
139
  box-shadow: 0 5px 10px rgba(52, 152, 219, 0.35) !important;
140
  }
141
- .gr-input, .gr-dropdown, .gr-textbox, .gr-slider, .gr-radio label span { /* استایل برای رادیو هم اضافه شد */
142
  border-radius: 8px !important; border: 1px solid #ced4da !important;
143
  font-size: 0.95em !important;
144
  }
145
- .gr-radio label input { margin-left: 5px; margin-right: 2px; } /* تنظیم فاصله دکمه های رادیو */
146
- .gr-radio { padding: 5px; border:none !important; } /* حذف حاشیه اضافی از رادیو */
147
-
148
  .gr-input:focus, .gr-dropdown:focus, .gr-textbox:focus {
149
  box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.15) !important;
150
  border-color: #5dade2 !important;
@@ -153,7 +142,7 @@ body { font-family: 'Vazirmatn', 'Arial', sans-serif; direction: rtl; }
153
  border-radius: 10px !important; border: 1px solid #e9ecef !important;
154
  background-color: #f8f9fa !important; padding: 0.75rem !important;
155
  }
156
- label > span { /* شامل برچسب های رادیو هم می شود */
157
  font-weight: 500 !important; color: #495057 !important; font-size: 0.9em !important;
158
  margin-bottom: 3px !important; display: block;
159
  }
@@ -182,38 +171,17 @@ button[title="Flag"], button[aria-label="Flag"] {display: none !important; }
182
  .main-content-row > .gr-column:nth-child(1) { flex-basis: 60%; }
183
  .main-content-row > .gr-column:nth-child(2) { flex-basis: 40%; }
184
  .gr-button.lg.primary { width: auto !important; }
185
- .gr-radio { display: flex; justify-content: center; gap: 15px; margin-bottom: 10px;} /* چیدمان بهتر رادیو در دسکتاپ */
186
- .gr-radio label { margin-bottom: 0 !important; } /* جلوگیری از پرش برچسب رادیو */
187
-
188
  }
189
  """
190
 
191
- # --- تابع برای آپدیت لیست زبان ها بر اساس جنسیت ---
192
- def update_language_dropdown(gender_filter):
193
- filtered_choices = []
194
- first_choice = None
195
- if gender_filter == "همه":
196
- filtered_choices = list(language_display_dict.keys())
197
- else: # "مرد" یا "زن"
198
- for display_key, voice_info in language_display_dict.items():
199
- if voice_info['gender'] == gender_filter:
200
- filtered_choices.append(display_key)
201
 
202
- if filtered_choices:
203
- first_choice = filtered_choices[0]
204
- # برگرداندن آپدیت برای Dropdown
205
- return gr.Dropdown(choices=filtered_choices, value=first_choice, label="🗣️ زبان و مدل صدا (فیلتر شده)")
206
-
207
-
208
- default_voice_key_display = "فارسی (ایران) - Farid- (Male)" # نام نمایشی جدید برای صدای پیش فرض
209
- if default_voice_key_display not in language_display_dict:
210
- # اگر صدای پیش فرض فارسی در لیست نبود (مثلا به خاطر فیلتر اولیه)، اولین مورد موجود را انتخاب کن
211
- if list(language_display_dict.keys()):
212
- default_voice_key_display = list(language_display_dict.keys())[0]
213
- else:
214
- default_voice_key_display = None # اگر لیست کلا خالی است
215
-
216
- LOGO_URL = "https://raw.githubusercontent.com/gradio-app/gradio/main/guides/assets/gradio-logo.svg"
217
 
218
  with gr.Blocks(theme=app_theme, css=custom_css) as demo:
219
  with gr.Row():
@@ -225,14 +193,6 @@ with gr.Blocks(theme=app_theme, css=custom_css) as demo:
225
  </div>
226
  """)
227
 
228
- # --- دکمه های رادیویی برای انتخاب جنسیت ---
229
- gender_radio = gr.Radio(
230
- choices=["همه", "مرد", "زن"],
231
- value="همه",
232
- label="انتخاب جنسیت صدا",
233
- elem_classes="gr-radio" # برای استایل دهی بهتر
234
- )
235
-
236
  with gr.Row(elem_classes="main-content-row"):
237
  with gr.Column(scale=3):
238
  input_text = gr.Textbox(
@@ -241,10 +201,9 @@ with gr.Blocks(theme=app_theme, css=custom_css) as demo:
241
  placeholder="متن برای تبدیل...",
242
  value=""
243
  )
244
- # --- Dropdown زبان با قابلیت آپدیت ---
245
- language_dropdown = gr.Dropdown(
246
- choices=list(language_display_dict.keys()), # لیست اولیه
247
- value=default_voice_key_display,
248
  label="🗣️ زبان و مدل صدا"
249
  )
250
  with gr.Accordion("⚙️ تنظیمات بیشتر (اختیاری)", open=False):
@@ -261,29 +220,23 @@ with gr.Blocks(theme=app_theme, css=custom_css) as demo:
261
 
262
  gr.HTML("<hr style='margin-top: 1rem; margin-bottom: 1rem; border: 0; border-top: 1px solid #dee2e6;'>")
263
 
 
264
  gr.Examples(
265
  examples=[
266
- ["سلام بر شما، این یک آزمایش تبدیل متن به گفتار با صدای فارسی است.", "فارسی (ایران) - Dilara- (Female)", 0, 0, 0],
267
- ["Gradio is a fantastic library for building ML demos quickly and easily.", "انگلیسی - Jenny (Female)", +10, -5, 0],
268
- ["株式会社グラディオへようこそ。", "ژاپنی - Keita- (Male)", -5, 0, +5],
269
  ],
270
- inputs=[input_text, language_dropdown, rate, volume, pitch], # ورودی language_dropdown است
271
  outputs=[output_text_status, output_audio],
272
  fn=text_to_speech_edge_sync_wrapper,
273
  cache_examples=False,
274
  label="💡 نمونه‌های آماده برای تست"
275
  )
276
 
277
- # --- اتصال دکمه رادیو به تابع آپدیت Dropdown ---
278
- gender_radio.change(
279
- fn=update_language_dropdown,
280
- inputs=gender_radio,
281
- outputs=language_dropdown
282
- )
283
-
284
  submit_button.click(
285
  fn=text_to_speech_edge_sync_wrapper,
286
- inputs=[input_text, language_dropdown, rate, volume, pitch], # ورودی language_dropdown است
287
  outputs=[output_text_status, output_audio],
288
  )
289
 
 
6
  import threading
7
  import os
8
 
9
+ # --- دیکشنری زبان‌ها و صداها با کلیدهای فارسی ---
10
+ language_dict = {
11
+ 'انگلیسی - جنی (زن)': 'en-US-JennyNeural',
12
+ 'انگلیسی - گای (مرد)': 'en-US-GuyNeural',
13
+ 'انگلیسی - آنا (زن)': 'en-US-AnaNeural',
14
+ 'انگلیسی - آریا (زن)': 'en-US-AriaNeural',
15
+ 'انگلیسی - کریستوفر (مرد)': 'en-US-ChristopherNeural',
16
+ 'انگلیسی - اریک (مرد)': 'en-US-EricNeural',
17
+ 'انگلیسی - میشل (زن)': 'en-US-MichelleNeural',
18
+ 'انگلیسی - راجر (مرد)': 'en-US-RogerNeural',
19
+
20
+ 'فارسی (ایران) - دلارا (زن)': 'fa-IR-DilaraNeural',
21
+ 'فارسی (ایران) - فرید (مرد)': 'fa-IR-FaridNeural',
22
+
23
+ 'اسپانیایی (مکزیک) - دالیا (زن)': 'es-MX-DaliaNeural',
24
+ 'اسپانیایی (مکزیک) - خورخه (مرد)': 'es-MX-JorgeNeural', # "Jorge-" به "خورخه"
25
+
26
+ 'کره‌ای - سان-هی (زن)': 'ko-KR-SunHiNeural', # "Sun-Hi-" به "سان-هی"
27
+ 'کره‌ای - اینجون (مرد)': 'ko-KR-InJoonNeural', # "InJoon-" به "اینجون"
28
+
29
+ 'ژاپنی - نانامی (زن)': 'ja-JP-NanamiNeural', # "Nanami-" به "نانامی"
30
+ 'ژاپنی - کیتا (مرد)': 'ja-JP-KeitaNeural', # "Keita-" به "کیتا"
31
+
32
+ 'فرانسوی - دنیز (زن)': 'fr-FR-DeniseNeural', # "Denise-" به "دنیز"
33
+ 'فرانسوی - الوئیز (زن)': 'fr-FR-EloiseNeural',# "Eloise-" به "الوئیز"
34
+ 'فرانسوی - هانری (مرد)': 'fr-FR-HenriNeural', # "Henri-" به "هانری"
35
+
36
+ 'آلمانی - کاتیا (زن)': 'de-DE-KatjaNeural', # "Katja-" به "کاتیا"
37
+ 'آلمانی - آملا (زن)': 'de-DE-AmalaNeural', # "Amala-" به "آملا"
38
+ 'آلمانی - کنراد (مرد)': 'de-DE-ConradNeural', # "Conrad-" به "کنراد"
39
+ 'آلمانی - کیلیان (مرد)': 'de-DE-KillianNeural', # "Killian-" به "کیلیان"
40
+
41
+ # ... شما باید بقیه زبان‌ها را به همین ترتیب فارسی‌سازی کنید ...
42
+ # مثال های بیشتر:
43
+ 'عربی (عربستان) - حامد (مرد)': 'ar-SA-HamedNeural',
44
+ 'عربی (عربستان) - زاریا (زن)': 'ar-SA-ZariyahNeural',
45
+
46
+ 'چینی (ماندارین ساده) - شیائوشیاو (زن)': 'zh-CN-XiaoxiaoNeural',
47
+ 'چینی (ماندارین ساده) - یون‌یانگ (مرد)': 'zh-CN-YunyangNeural',
48
  }
49
 
50
+ # --- توابع تبدیل متن به گفتار و wrapper (بدون تغییر باقی می‌مانند) ---
51
+ async def text_to_speech_edge_async(text, language_code, rate, volume, pitch):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  try:
53
  if not text: return "خطا: لطفاً متنی را برای تبدیل وارد کنید.", None
54
+ # language_code در اینجا کلید فارسی از dropdown است
55
+ voice_id = language_dict.get(language_code) # مقدار voice_id همان کد انگلیسی مثل 'en-US-JennyNeural' خواهد بود
56
+ if voice_id is None: return f"خطا: مدل صدای انتخاب شده ('{language_code}') یافت نشد.", None
 
 
57
  rate_str, volume_str, pitch_str = f"{int(rate):+g}%", f"{int(volume):+g}%", f"{int(pitch):+g}Hz"
58
  communicate = edge_tts.Communicate(text, voice_id, rate=rate_str, volume=volume_str, pitch=pitch_str)
59
  with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as tmp_file: tmp_path = tmp_file.name
 
75
  _event_loops_by_thread[thread_id] = asyncio.new_event_loop()
76
  return _event_loops_by_thread[thread_id]
77
 
78
+ def text_to_speech_edge_sync_wrapper(text, language_code, rate, volume, pitch):
79
  try:
80
  loop = _get_or_create_event_loop(); asyncio.set_event_loop(loop)
81
+ result = loop.run_until_complete(text_to_speech_edge_async(text, language_code, rate, volume, pitch))
82
  except RuntimeError as e:
83
  if "no current event loop" in str(e).lower() or "cannot be called from a running event loop" in str(e).lower():
84
  new_loop = asyncio.new_event_loop(); asyncio.set_event_loop(new_loop)
85
+ try: result = new_loop.run_until_complete(text_to_speech_edge_async(text, language_code, rate, volume, pitch))
86
  finally: new_loop.close()
87
  else: return f"خطای اجرایی: {e}", None
88
  except Exception as e: return f"خطای غیرمنتظره: {type(e).__name__}", None
89
  return result
90
 
91
+ # --- تعریف تم و CSS (بدون تغییر باقی می‌مانند) ---
92
  app_theme = gr.themes.Soft(
93
  primary_hue=gr.themes.colors.blue,
94
  secondary_hue=gr.themes.colors.sky,
 
130
  background: #2980b9 !important; transform: translateY(-2px) !important;
131
  box-shadow: 0 5px 10px rgba(52, 152, 219, 0.35) !important;
132
  }
133
+ .gr-input, .gr-dropdown, .gr-textbox, .gr-slider {
134
  border-radius: 8px !important; border: 1px solid #ced4da !important;
135
  font-size: 0.95em !important;
136
  }
 
 
 
137
  .gr-input:focus, .gr-dropdown:focus, .gr-textbox:focus {
138
  box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.15) !important;
139
  border-color: #5dade2 !important;
 
142
  border-radius: 10px !important; border: 1px solid #e9ecef !important;
143
  background-color: #f8f9fa !important; padding: 0.75rem !important;
144
  }
145
+ label > span {
146
  font-weight: 500 !important; color: #495057 !important; font-size: 0.9em !important;
147
  margin-bottom: 3px !important; display: block;
148
  }
 
171
  .main-content-row > .gr-column:nth-child(1) { flex-basis: 60%; }
172
  .main-content-row > .gr-column:nth-child(2) { flex-basis: 40%; }
173
  .gr-button.lg.primary { width: auto !important; }
 
 
 
174
  }
175
  """
176
 
177
+ # --- تعریف رابط کاربری با Blocks ---
178
+ # مقدار پیش فرض برای زبان، حالا باید کلید فارسی باشد
179
+ default_voice_key_fa = 'فارسی (ایران) - فرید (مرد)'
180
+ if default_voice_key_fa not in language_dict:
181
+ # اگر به هر دلیلی کلید فارسی بالا در لیست نبود، اولین مورد لیست را انتخاب کن
182
+ default_voice_key_fa = list(language_dict.keys())[0] if language_dict else None
 
 
 
 
183
 
184
+ LOGO_URL = "https://raw.githubusercontent.com/gradio-app/gradio/main/guides/assets/gradio-logo.svg" # یا URL لوگوی خودتان
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
 
186
  with gr.Blocks(theme=app_theme, css=custom_css) as demo:
187
  with gr.Row():
 
193
  </div>
194
  """)
195
 
 
 
 
 
 
 
 
 
196
  with gr.Row(elem_classes="main-content-row"):
197
  with gr.Column(scale=3):
198
  input_text = gr.Textbox(
 
201
  placeholder="متن برای تبدیل...",
202
  value=""
203
  )
204
+ language = gr.Dropdown(
205
+ choices=list(language_dict.keys()), # کلیدهای فارسی برای نمایش به کاربر
206
+ value=default_voice_key_fa, # استفاده از کلید فارسی برای مقدار پیش فرض
 
207
  label="🗣️ زبان و مدل صدا"
208
  )
209
  with gr.Accordion("⚙️ تنظیمات بیشتر (اختیاری)", open=False):
 
220
 
221
  gr.HTML("<hr style='margin-top: 1rem; margin-bottom: 1rem; border: 0; border-top: 1px solid #dee2e6;'>")
222
 
223
+ # مثال ها هم باید از کلیدهای فارسی استفاده کنند
224
  gr.Examples(
225
  examples=[
226
+ ["سلام بر شما، این یک آزمایش تبدیل متن به گفتار با صدای فارسی است.", 'فارسی (ایران) - دلارا (زن)', 0, 0, 0],
227
+ ["Gradio is a fantastic library for building ML demos quickly and easily.", 'انگلیسی - جنی (زن)', +10, -5, 0],
228
+ ["株式会社グラディオへようこそ。", 'ژاپنی - کیتا (مرد)', -5, 0, +5],
229
  ],
230
+ inputs=[input_text, language, rate, volume, pitch],
231
  outputs=[output_text_status, output_audio],
232
  fn=text_to_speech_edge_sync_wrapper,
233
  cache_examples=False,
234
  label="💡 نمونه‌های آماده برای تست"
235
  )
236
 
 
 
 
 
 
 
 
237
  submit_button.click(
238
  fn=text_to_speech_edge_sync_wrapper,
239
+ inputs=[input_text, language, rate, volume, pitch],
240
  outputs=[output_text_status, output_audio],
241
  )
242