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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +98 -15
app.py CHANGED
@@ -301,14 +301,20 @@ async def text_to_speech_edge_async(text, language_code_persian, rate, volume, p
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,37 +327,114 @@ def text_to_speech_edge_sync_wrapper(text, language_code_persian, rate, volume,
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=["*"])
 
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"خطا: مدل صدای انتخاب شده ('{language_code_persian}') یافت نشد.", 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 edge_tts.exceptions.NoAudioReceived:
311
+ error_msg = f"خطا: صدایی برای متن و صدای انتخاب شده دریافت نشد (صدا: {voice_id})."
312
+ return error_msg, None
313
+ except ValueError as ve:
314
+ error_msg = f"خطا در پارامترهای ورودی: {ve}"
315
+ return error_msg, None
316
  except Exception as e:
317
+ return f"خطای غیرمنتظره در سرور: {type(e).__name__}", None
318
 
319
  _event_loops_by_thread = {}
320
  def _get_or_create_event_loop():
 
327
  try:
328
  loop = _get_or_create_event_loop(); asyncio.set_event_loop(loop)
329
  result = loop.run_until_complete(text_to_speech_edge_async(text, language_code_persian, rate, volume, pitch))
330
+ except RuntimeError as e:
331
+ if "no current event loop" in str(e).lower() or "cannot be called from a running event loop" in str(e).lower():
332
+ new_loop = asyncio.new_event_loop(); asyncio.set_event_loop(new_loop)
333
+ try: result = new_loop.run_until_complete(text_to_speech_edge_async(text, language_code_persian, rate, volume, pitch))
334
+ finally: new_loop.close()
335
+ else: return f"خطای اجرایی: {e}", None
336
+ except Exception as e: return f"خطای غیرمنتظره: {type(e).__name__}", None
337
  return result
338
 
339
  app_theme = gr.themes.Soft(
340
  primary_hue=gr.themes.colors.blue,
341
+ secondary_hue=gr.themes.colors.sky,
342
+ neutral_hue=gr.themes.colors.slate,
343
  font=[gr.themes.GoogleFont("Vazirmatn"), "Arial", "sans-serif"],
344
+ ).set(
345
+ body_background_fill="#f4f7f6",
346
  )
347
 
348
  custom_css = """
349
  body { font-family: 'Vazirmatn', 'Arial', sans-serif; direction: rtl; }
350
+ .gradio-container {
351
+ max-width: 95% !important; margin: 1rem auto !important; padding: 1rem !important;
352
+ border-radius: 16px !important; box-shadow: 0 6px 20px rgba(0, 0, 0, 0.07) !important;
353
+ background-color: #ffffff !important;
354
+ }
355
+ .app-header {
356
+ text-align: center; padding: 20px 10px; background: #34495e; color: white;
357
+ border-radius: 12px; margin-bottom: 1.5rem;
358
+ }
359
+ .app-header h1 {
360
+ color: white !important; font-size: 1.6em !important; font-weight: 600 !important;
361
+ margin: 5px 0;
362
+ }
363
+ .app-header p { color: #bdc3c7 !important; font-size: 0.9em !important; margin-top: 5px; }
364
+ .main-content-row > .gr-column { margin-bottom: 1rem; }
365
+
366
+ .gr-button.lg.primary {
367
+ background: #3498db !important; color: white !important; font-weight: 500 !important;
368
+ border-radius: 8px !important; padding: 12px 15px !important; width: 100% !important;
369
+ font-size: 1em !important; transition: all 0.2s ease !important;
370
+ box-shadow: 0 3px 6px rgba(52, 152, 219, 0.25) !important; border: none !important;
371
+ }
372
+ .gr-button.lg.primary:hover {
373
+ background: #2980b9 !important; transform: translateY(-2px) !important;
374
+ box-shadow: 0 5px 10px rgba(52, 152, 219, 0.35) !important;
375
+ }
376
+ .gr-input, .gr-dropdown, .gr-textbox, .gr-slider {
377
+ border-radius: 8px !important; border: 1px solid #ced4da !important;
378
+ font-size: 0.95em !important;
379
+ }
380
+ .gr-panel {
381
+ border-radius: 10px !important; border: 1px solid #e9ecef !important;
382
+ background-color: #f8f9fa !important; padding: 0.75rem !important;
383
+ }
384
+ label > span {
385
+ font-weight: 500 !important; color: #495057 !important; font-size: 0.9em !important;
386
+ margin-bottom: 3px !important; display: block;
387
+ }
388
+ footer { display: none !important; visibility: hidden !important; }
389
+ .gradio-footer { display: none !important; visibility: hidden !important; }
390
+ @media (min-width: 768px) {
391
+ .gradio-container { max-width: 800px !important; padding: 1.5rem !important;}
392
+ .app-header h1 { font-size: 2em !important; }
393
+ .main-content-row { display: flex; flex-direction: row; gap: 1.5rem; }
394
+ .main-content-row > .gr-column { flex: 1; margin-bottom: 0; }
395
+ .main-content-row > .gr-column:nth-child(1) { flex-basis: 60%; }
396
+ .main-content-row > .gr-column:nth-child(2) { flex-basis: 40%; }
397
+ }
398
  """
399
 
400
  default_voice_key_persian = 'فارسی (ایران) - فرید (مرد)'
401
+ if default_voice_key_persian not in language_dict_persian_keys:
402
+ default_voice_key_persian = list(language_dict_persian_keys.keys())[0] if language_dict_persian_keys else None
403
 
404
  with gr.Blocks(theme=app_theme, css=custom_css) as demo:
405
+ with gr.Row():
406
+ gr.HTML(f"""
407
+ <div class="app-header">
408
+ <h1>مبدل هوشمند متن به گفتار</h1>
409
+ <p>با کیفیت صدای طبیعی و روان، متن خود را زنده کنید</p>
410
+ </div>
411
+ """)
412
+
413
+ with gr.Row(elem_classes="main-content-row"):
414
+ with gr.Column(scale=3):
415
+ input_text = gr.Textbox(
416
+ lines=5, label="📝 متن خود را برای تبدیل وارد نمایید", placeholder="اینجا بنویسید...", value=""
417
+ )
418
+ language_dropdown = gr.Dropdown(
419
+ choices=list(language_dict_persian_keys.keys()), value=default_voice_key_persian, label="🗣️ زبان و گوینده را انتخاب کنید"
420
+ )
421
+ with gr.Accordion("⚙️ تنظیمات پیشرفته صدا (اختیاری)", open=False):
422
+ with gr.Row():
423
+ rate_slider = gr.Slider(minimum=-100, maximum=100, step=1, value=0, label="سرعت (%)", scale=1)
424
+ volume_slider = gr.Slider(minimum=-100, maximum=100, step=1, value=0, label="حجم (%)", scale=1)
425
+ pitch_slider = gr.Slider(minimum=-50, maximum=50, step=1, value=0, label="گام (Hz)", scale=2)
426
+
427
+ submit_button = gr.Button("🔊 تولید و پخش صدا", variant="primary")
428
+
429
+ with gr.Column(scale=2):
430
+ output_text_status = gr.Textbox(label="📊 وضعیت عملیات", interactive=False, lines=1, placeholder="نتیجه اینجا نمایش داده می‌شود...")
431
+ output_audio = gr.Audio(type="filepath", label="🎧 فایل صوتی خروجی", interactive=False)
432
 
433
  submit_button.click(
434
  fn=text_to_speech_edge_sync_wrapper,
435
  inputs=[input_text, language_dropdown, rate_slider, volume_slider, pitch_slider],
436
  outputs=[output_text_status, output_audio],
437
+ api_name="predict"
438
  )
439
 
440
+ demo.launch()