Hamed744 commited on
Commit
c35c314
·
verified ·
1 Parent(s): 3c9c265

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +215 -89
app.py CHANGED
@@ -2,10 +2,12 @@ import gradio as gr
2
  import edge_tts
3
  import tempfile
4
  import asyncio
5
- import traceback # برای چاپ کامل traceback
 
 
6
 
7
- # --- language_dict همانند قبل باقی می ماند ---
8
- # (مطمئن شوید لیست صداهای شما معتبر است)
9
  language_dict = {
10
  'English-Jenny (Female)': 'en-US-JennyNeural',
11
  'English-Guy (Male)': 'en-US-GuyNeural',
@@ -297,7 +299,7 @@ language_dict = {
297
  'Zulu (South Africa)-Themba- (Male)': 'zu-ZA-ThembaNeural',
298
  }
299
 
300
- # --- تابع اصلی با مدیریت خطای بهتر و اجرای async ---
301
  async def text_to_speech_edge_async(text, language_code, rate, volume, pitch):
302
  try:
303
  if not text:
@@ -305,17 +307,13 @@ async def text_to_speech_edge_async(text, language_code, rate, volume, pitch):
305
 
306
  voice_id = language_dict.get(language_code)
307
  if voice_id is None:
308
- return f"خطا: مدل صدای انتخاب شده ('{language_code}') در دیکشنری یافت نشد یا نامعتبر است.", None
309
 
310
- # اصلاح فرمت rate, volume, pitch برای edge-tts
311
- # اگر مقدار 0 باشد، باید از فرمت "+0%" یا "-0%" استفاده شود.
312
- # یا اینکه اگر کتابخانه اجازه می دهد، None یا رشته خالی ارسال کنیم.
313
- # با توجه به مستندات edge-tts، باید همیشه با % یا Hz باشند.
314
- rate_str = f"{int(rate):+g}%" # :+g علامت را برای صفر هم می گذارد
315
  volume_str = f"{int(volume):+g}%"
316
  pitch_str = f"{int(pitch):+g}Hz"
317
 
318
- print(f"Requesting TTS with: Text='{text[:30]}...', Voice='{voice_id}', Rate='{rate_str}', Volume='{volume_str}', Pitch='{pitch_str}'")
319
 
320
  communicate = edge_tts.Communicate(
321
  text, voice_id, rate=rate_str, volume=volume_str, pitch=pitch_str
@@ -325,122 +323,250 @@ async def text_to_speech_edge_async(text, language_code, rate, volume, pitch):
325
  tmp_path = tmp_file.name
326
 
327
  await communicate.save(tmp_path)
328
- print(f"TTS successful. Audio saved to: {tmp_path}")
329
- return "تبدیل متن به گفتار با موفقیت انجام شد.", tmp_path
330
 
331
  except edge_tts.exceptions.NoAudioReceived:
332
- error_msg = f"خطا: هیچ صدایی از سرویس برای متن و صدای انتخاب شده دریافت نشد. (صدا: {voice_id})"
333
- print(f"NoAudioReceived: {error_msg}")
334
  return error_msg, None
335
- except ValueError as ve: # برای خطاهای مربوط به پارامترهای نامعتبر
336
- error_msg = f"خطا در پارامترهای ورودی برای edge-tts: {ve}. (صدا: {voice_id}, نرخ: {rate_str}, حجم: {volume_str}, زیروبمی: {pitch_str})"
337
  print(f"ValueError in edge-tts: {error_msg}")
338
  traceback.print_exc()
339
  return error_msg, None
340
  except Exception as e:
341
- print(f"An unexpected error occurred in text_to_speech_edge_async (PID: {os.getpid()}, Thread: {threading.get_ident()}):")
342
  traceback.print_exc()
343
- user_error_msg = f"یک خطای غیرمنتظره در سرور رخ داد: {type(e).__name__}"
344
  return user_error_msg, None
345
 
346
- # --- Wrapper برای اجرای تابع async در محیط Gradio ---
347
- # این wrapper حالا robust تر است
348
- import threading
349
- import os
350
-
351
- _event_loops = {} # برای ذخیره event loop ها به ازای هر thread
352
 
353
- def get_or_create_event_loop_for_thread():
354
  thread_id = threading.get_ident()
355
- if thread_id not in _event_loops or _event_loops[thread_id].is_closed():
356
- loop = asyncio.new_event_loop()
357
- asyncio.set_event_loop(loop)
358
- _event_loops[thread_id] = loop
359
- # print(f"Created new event loop for thread {thread_id} (PID: {os.getpid()})")
360
  # else:
361
- # print(f"Reusing event loop for thread {thread_id} (PID: {os.getpid()})")
362
- return _event_loops[thread_id]
363
-
364
 
365
  def text_to_speech_edge_sync_wrapper(text, language_code, rate, volume, pitch):
366
- # print(f"Sync wrapper called in PID: {os.getpid()}, Thread: {threading.get_ident()}")
367
  try:
368
- # هر thread در Gradio/FastAPI ممکن است event loop خود را نیاز داشته باشد
369
- loop = get_or_create_event_loop_for_thread()
370
- # اطمینان از اینکه این لوپ برای این thread به عنوان لوپ فعلی تنظیم شده
371
  asyncio.set_event_loop(loop)
372
- # اجرای کوروتین در لوپ
373
  result = loop.run_until_complete(
374
  text_to_speech_edge_async(text, language_code, rate, volume, pitch)
375
  )
376
  except RuntimeError as e:
377
- print(f"RuntimeError in sync_wrapper (PID: {os.getpid()}, Thread: {threading.get_ident()}): {e}")
378
- traceback.print_exc()
379
- # اگر هنوز با خطای "no current event loop" مواجه شدیم، یکبار دیگر با لوپ جدید تلاش می کنیم
380
- # این بخش شاید دیگر لازم نباشد با get_or_create_event_loop_for_thread
381
  if "no current event loop" in str(e).lower() or "cannot be called from a running event loop" in str(e).lower():
382
- print("Attempting with a completely new loop as fallback...")
383
  new_loop = asyncio.new_event_loop()
384
- asyncio.set_event_loop(new_loop) # این خط مهم است
385
  try:
386
  result = new_loop.run_until_complete(
387
  text_to_speech_edge_async(text, language_code, rate, volume, pitch)
388
  )
389
  finally:
390
- new_loop.close() # مهم است که لوپ های موقت را ببندیم
391
  else:
392
- return f"خطای اجرایی: {e}", None
393
  except Exception as e:
394
- print(f"Unexpected Exception in sync_wrapper (PID: {os.getpid()}, Thread: {threading.get_ident()}):")
395
- traceback.print_exc()
396
- return f"خطای غیرمنتظره در wrapper: {type(e).__name__}", None
397
  return result
398
 
 
 
 
 
 
 
 
 
 
 
 
 
399
 
400
- # --- تعریف رابط کاربری Gradio ---
401
- input_text = gr.Textbox(lines=5, label="متن ورودی (Input Text)", value="سلام من یک ربات فارسی زبان هستم.") # یک مقدار پیش فرض برای تست اولیه
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
402
  default_voice_key = 'Persian (Iran)-Farid- (Male)'
403
  if default_voice_key not in language_dict:
404
  default_voice_key = list(language_dict.keys())[0] if language_dict else None
405
 
406
- language = gr.Dropdown(
407
- choices=list(language_dict.keys()),
408
- value=default_voice_key,
409
- label="انتخاب مدل صدا (Choose the Voice Model)"
410
- )
411
- rate = gr.Slider(
412
- minimum=-100, maximum=100, step=1, value=0, label="سرعت (Rate)"
413
- )
414
- volume = gr.Slider(
415
- minimum=-100, maximum=100, step=1, value=0, label="حجم صدا (Volume)"
416
- )
417
- pitch = gr.Slider(
418
- minimum=-50, maximum=50, step=1, value=0, label="زیر و بمی (Pitch)", info="مقدار بر حسب هرتز (Hz)"
419
- )
420
 
421
- output_text_status = gr.Textbox(label="وضعیت (Status)")
422
- output_audio = gr.Audio(type="filepath", label="فایل صوتی خروجی (Exported Audio)")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
423
 
 
424
 
425
- iface = gr.Interface(
426
- fn=text_to_speech_edge_sync_wrapper,
427
- inputs=[input_text, language, rate, volume, pitch],
428
- outputs=[output_text_status, output_audio],
429
- title="Edge-TTS فارسی (اصلاح شده)",
430
- description="تبدیل متن به گفتار با استفاده از سرویس Microsoft Edge",
431
- allow_flagging="never",
432
- # مثال ها را فعلا غیرفعال می کنیم تا مشکل caching حل شود، یا مقادیر را تغییر می دهیم
433
- # examples=[
434
- # ["سلام دنیا، حال شما چطور است؟", 'Persian (Iran)-Dilara- (Female)', 0, 0, 0], # این باعث خطا میشد
435
- # ["Hello world, how are you?", 'English-Jenny (Female)', 10, 0, -10], # این هم
436
- # ]
437
- # مثال های اصلاح شده:
438
- examples=[
439
- ["سلام دنیا، حال شما چطور است؟", 'Persian (Iran)-Dilara- (Female)', 0, 0, 0], # مقادیر صفر هنوز ممکن است مشکل ساز باشند اگر فرمت اشتباه باشد
440
- ["Hello world, how are you?", 'English-Jenny (Female)', +10, 0, -10], # rate مثبت
441
- ]
442
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
443
 
444
- # if __name__ == "__main__": # در Hugging Face Spaces این بخش لازم نیست و خودش app.py را اجرا می کند
445
- # iface.launch(share=True) # share=True در HF Spaces کار نمی کند و خودش لینک عمومی می دهد
446
- iface.launch() # در HF Spaces فقط launch() کافی است
 
2
  import edge_tts
3
  import tempfile
4
  import asyncio
5
+ import traceback
6
+ import threading
7
+ import os
8
 
9
+ # --- دیکشنری زبان‌ها و صداها ---
10
+ # لطفاً مطمئن شوید این لیست کامل و صحیح است.
11
  language_dict = {
12
  'English-Jenny (Female)': 'en-US-JennyNeural',
13
  'English-Guy (Male)': 'en-US-GuyNeural',
 
299
  'Zulu (South Africa)-Themba- (Male)': 'zu-ZA-ThembaNeural',
300
  }
301
 
302
+ # --- تابع اصلی تبدیل متن به گفتار (ناهمزمان) ---
303
  async def text_to_speech_edge_async(text, language_code, rate, volume, pitch):
304
  try:
305
  if not text:
 
307
 
308
  voice_id = language_dict.get(language_code)
309
  if voice_id is None:
310
+ return f"خطا: مدل صدای انتخاب شده ('{language_code}') یافت نشد.", None
311
 
312
+ rate_str = f"{int(rate):+g}%"
 
 
 
 
313
  volume_str = f"{int(volume):+g}%"
314
  pitch_str = f"{int(pitch):+g}Hz"
315
 
316
+ # print(f"TTS Request: Voice='{voice_id}', Rate='{rate_str}', Volume='{volume_str}', Pitch='{pitch_str}'")
317
 
318
  communicate = edge_tts.Communicate(
319
  text, voice_id, rate=rate_str, volume=volume_str, pitch=pitch_str
 
323
  tmp_path = tmp_file.name
324
 
325
  await communicate.save(tmp_path)
326
+ # print(f"TTS Success. Audio: {tmp_path}")
327
+ return "تبدیل با موفقیت انجام شد.", tmp_path
328
 
329
  except edge_tts.exceptions.NoAudioReceived:
330
+ error_msg = f"خطا: صدایی برای متن و صدای انتخاب شده دریافت نشد (صدا: {voice_id})."
331
+ print(f"NoAudioReceived Error: {error_msg}")
332
  return error_msg, None
333
+ except ValueError as ve:
334
+ error_msg = f"خطا در پارامترهای ورودی edge-tts: {ve} (صدا: {voice_id}, نرخ: {rate_str}, حجم: {volume_str}, زیروبمی: {pitch_str})"
335
  print(f"ValueError in edge-tts: {error_msg}")
336
  traceback.print_exc()
337
  return error_msg, None
338
  except Exception as e:
339
+ print(f"Unexpected error in text_to_speech_edge_async (PID: {os.getpid()}, Thread: {threading.get_ident()}):")
340
  traceback.print_exc()
341
+ user_error_msg = f"خطای غیرمنتظره در سرور: {type(e).__name__}"
342
  return user_error_msg, None
343
 
344
+ # --- Wrapper برای اجرای تابع ناهمزمان در محیط همزمان Gradio ---
345
+ _event_loops_by_thread = {}
 
 
 
 
346
 
347
+ def _get_or_create_event_loop():
348
  thread_id = threading.get_ident()
349
+ if thread_id not in _event_loops_by_thread or _event_loops_by_thread[thread_id].is_closed():
350
+ # print(f"Creating new event loop for thread {thread_id}")
351
+ _event_loops_by_thread[thread_id] = asyncio.new_event_loop()
 
 
352
  # else:
353
+ # print(f"Reusing event loop for thread {thread_id}")
354
+ return _event_loops_by_thread[thread_id]
 
355
 
356
  def text_to_speech_edge_sync_wrapper(text, language_code, rate, volume, pitch):
357
+ # print(f"Sync wrapper called by PID: {os.getpid()}, Thread: {threading.get_ident()}")
358
  try:
359
+ loop = _get_or_create_event_loop()
 
 
360
  asyncio.set_event_loop(loop)
 
361
  result = loop.run_until_complete(
362
  text_to_speech_edge_async(text, language_code, rate, volume, pitch)
363
  )
364
  except RuntimeError as e:
365
+ # print(f"RuntimeError in sync_wrapper (PID: {os.getpid()}, Thread: {threading.get_ident()}): {e}")
366
+ # traceback.print_exc()
367
+ # Fallback for "cannot be called from a running event loop" or similar issues
 
368
  if "no current event loop" in str(e).lower() or "cannot be called from a running event loop" in str(e).lower():
369
+ # print("Fallback: Attempting with a completely new, temporary loop...")
370
  new_loop = asyncio.new_event_loop()
371
+ asyncio.set_event_loop(new_loop)
372
  try:
373
  result = new_loop.run_until_complete(
374
  text_to_speech_edge_async(text, language_code, rate, volume, pitch)
375
  )
376
  finally:
377
+ new_loop.close()
378
  else:
379
+ return f"خطای اجرایی (RuntimeError): {e}", None
380
  except Exception as e:
381
+ # print(f"Unexpected Exception in sync_wrapper (PID: {os.getpid()}, Thread: {threading.get_ident()}):")
382
+ # traceback.print_exc()
383
+ return f"خطای غیرمنتظره در Wrapper: {type(e).__name__}", None
384
  return result
385
 
386
+ # --- تعریف تم و رابط کاربری با Gradio Blocks ---
387
+ # انتخاب یک تم زیبا
388
+ app_theme = gr.themes.Soft(
389
+ primary_hue=gr.themes.colors.blue,
390
+ secondary_hue=gr.themes.colors.sky,
391
+ neutral_hue=gr.themes.colors.slate,
392
+ font=[gr.themes.GoogleFont("Vazirmatn"), "Arial", "sans-serif"], # اضافه کردن فونت فارسی وزیرمتن
393
+ ).set(
394
+ # اینجا می‌توانید مقادیر بیشتری از تم را سفارشی کنید
395
+ # مثال: button_primary_background_fill="linear-gradient(to right, #0072ff, #00c6ff)",
396
+ # body_background_fill="#f0f9ff",
397
+ )
398
 
399
+ # CSS سفارشی برای انیمیشن ها و ظاهر بهتر
400
+ custom_css = """
401
+ body {
402
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); /* پس زمینه گرادیانت نرم */
403
+ overflow-x: hidden; /* جلوگیری از اسکرول افقی ناخواسته */
404
+ }
405
+ .gradio-container {
406
+ max-width: 900px !important; /* کمی عریض تر */
407
+ margin: 2rem auto !important;
408
+ border-radius: 20px !important;
409
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1) !important;
410
+ overflow: hidden; /* برای اینکه border-radius روی همه چیز اعمال شود */
411
+ background-color: white !important;
412
+ }
413
+ .app-title-container {
414
+ text-align: center;
415
+ padding: 20px 0;
416
+ background: linear-gradient(to right, #6a11cb 0%, #2575fc 100%); /* یک گرادیانت جذاب برای هدر */
417
+ border-top-left-radius: 20px;
418
+ border-top-right-radius: 20px;
419
+ margin-bottom: 15px; /* کمی فاصله از پایین هدر */
420
+ }
421
+ .app-title-container img {
422
+ width: 70px;
423
+ height: auto;
424
+ margin-bottom: 0px;
425
+ animation: float 3s ease-in-out infinite; /* انیمیشن شناور برای لوگو */
426
+ }
427
+ .app-title-container h1 {
428
+ color: white !important; /* رنگ متن عنوان */
429
+ font-size: 2.2em !important;
430
+ font-weight: 700 !important;
431
+ margin-top: 5px;
432
+ text-shadow: 1px 1px 3px rgba(0,0,0,0.2);
433
+ }
434
+ .app-title-container p {
435
+ color: #e0e0e0 !important; /* رنگ متن توضیحات زیر عنوان */
436
+ font-size: 1.1em !important;
437
+ margin-top: 5px;
438
+ }
439
+
440
+ .gr-button.lg.primary { /* استایل برای دکمه اصلی */
441
+ background: linear-gradient(to right, #0072ff, #00c6ff) !important;
442
+ color: white !important;
443
+ font-weight: bold !important;
444
+ border-radius: 10px !important;
445
+ padding: 12px 20px !important;
446
+ transition: transform 0.2s ease, box-shadow 0.2s ease !important;
447
+ box-shadow: 0 4px 6px rgba(0, 114, 255, 0.2) !important;
448
+ }
449
+ .gr-button.lg.primary:hover {
450
+ transform: translateY(-2px) !important;
451
+ box-shadow: 0 6px 12px rgba(0, 114, 255, 0.3) !important;
452
+ }
453
+ .gr-input, .gr-dropdown, .gr-textbox, .gr-slider { /* استایل برای اجزای ورودی */
454
+ border-radius: 8px !important;
455
+ border: 1px solid #dcdcdc !important;
456
+ transition: box-shadow 0.2s ease;
457
+ }
458
+ .gr-input:focus, .gr-dropdown:focus, .gr-textbox:focus {
459
+ box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25) !important;
460
+ border-color: #80bdff !important;
461
+ }
462
+ .gr-panel { /* برای بخش Accordion */
463
+ border-radius: 10px !important;
464
+ border: 1px solid #e0e0e0 !important;
465
+ background-color: #fafafa !important;
466
+ }
467
+ label span { /* برای برچسب ها */
468
+ font-weight: 500 !important;
469
+ color: #333 !important;
470
+ }
471
+ .footer-markdown a {
472
+ color: #007bff;
473
+ text-decoration: none;
474
+ }
475
+ .footer-markdown a:hover {
476
+ text-decoration: underline;
477
+ }
478
+
479
+ /* انیمیشن برای لوگو */
480
+ @keyframes float {
481
+ 0% { transform: translatey(0px); }
482
+ 50% { transform: translatey(-8px); }
483
+ 100% { transform: translatey(0px); }
484
+ }
485
+ /* انیمیشن برای بارگذاری (می توان برای بخش وضعیت استفاده کرد) */
486
+ .status-processing {
487
+ animation: pulse 1.5s infinite;
488
+ }
489
+ @keyframes pulse {
490
+ 0% { opacity: 1; }
491
+ 50% { opacity: 0.5; }
492
+ 100% { opacity: 1; }
493
+ }
494
+ """
495
+
496
+ # انتخاب صدای پیش فرض (مطمئن شوید در language_dict وجود دارد)
497
  default_voice_key = 'Persian (Iran)-Farid- (Male)'
498
  if default_voice_key not in language_dict:
499
  default_voice_key = list(language_dict.keys())[0] if language_dict else None
500
 
501
+ # ساخت رابط کاربری با Blocks
502
+ with gr.Blocks(theme=app_theme, css=custom_css) as demo:
503
+ # بخش عنوان با HTML و CSS سفارشی
504
+ with gr.Row():
505
+ gr.HTML("""
506
+ <div class="app-title-container">
507
+ <img src="https://raw.githubusercontent.com/gradio-app/gradio/main/guides/assets/gradio-logo.svg" alt="Edge TTS Logo">
508
+ <h1>مبدل متن به گفتار Edge-TTS</h1>
509
+ <p>کیفیت صدای طبیعی با قدرت مایکروسافت Edge</p>
510
+ </div>
511
+ """)
 
 
 
512
 
513
+ with gr.Row(equal_height=False): # equal_height=False برای کنترل بهتر ارتفاع ستون ها
514
+ with gr.Column(scale=3): # ستون ورودی ها
515
+ input_text = gr.Textbox(
516
+ lines=7,
517
+ label="📝 متن ورودی خود را اینجا بنویسید",
518
+ placeholder="مثال: سلام، به دنیای تبدیل متن به گفتار خوش آمدید...",
519
+ value="سلام، این یک نمونه متن برای آزمایش است." # مقدار پیش فرض برای تست
520
+ )
521
+ language = gr.Dropdown(
522
+ choices=list(language_dict.keys()),
523
+ value=default_voice_key,
524
+ label="🗣️ مدل و زبان صدا را انتخاب کنید"
525
+ )
526
+ with gr.Accordion("⚙️ تنظیمات پیشرفته صدا (اختیاری)", open=False, elem_classes="gr-panel"):
527
+ with gr.Row():
528
+ rate = gr.Slider(minimum=-100, maximum=100, step=1, value=0, label="💨 سرعت گفتار (%)", scale=1)
529
+ volume = gr.Slider(minimum=-100, maximum=100, step=1, value=0, label="🔊 حجم صدا (%)", scale=1)
530
+ pitch = gr.Slider(minimum=-50, maximum=50, step=1, value=0, label="🎶 زیر و بمی صدا (Hz)", scale=2) # scale=2 برای پهن تر شدن
531
 
532
+ submit_button = gr.Button("🎤 تبدیل به گفتار و پخش", variant="primary", icon="▶️")
533
 
534
+ with gr.Column(scale=2): # ستون خروجی ها
535
+ output_text_status = gr.Textbox(label="📊 وضعیت عملیات", interactive=False, lines=1)
536
+ output_audio = gr.Audio(type="filepath", label="🎧 فایل صوتی تولید شده", interactive=False)
537
+ gr.Markdown("---", css="margin-top: 20px; margin-bottom: 20px;")
538
+ gr.Markdown(
539
+ """
540
+ <div class='footer-markdown' style='text-align: center; font-size: 0.9em; color: #555;'>
541
+ توسعه داده شده با Gradio
542
+ <br>
543
+ <a href='https://github.com/rany2/edge-tts' target='_blank' rel='noopener noreferrer'>مشاهده کتابخانه edge-tts در گیت‌هاب</a>
544
+ </div>
545
+ """,
546
+ elem_classes="footer-markdown"
547
+ )
548
+
549
+ # تعریف عملکرد دکمه
550
+ submit_button.click(
551
+ fn=text_to_speech_edge_sync_wrapper,
552
+ inputs=[input_text, language, rate, volume, pitch],
553
+ outputs=[output_text_status, output_audio],
554
+ # api_name="text_to_speech" # برای فراخوانی از طریق API (اختیاری)
555
+ )
556
+
557
+ # بخش مثال ها
558
+ gr.Examples(
559
+ examples=[
560
+ ["به سرویس تبدیل متن به گفتار خوش آمدید. امیدوارم از کیفیت صدا لذت ببرید.", 'Persian (Iran)-Dilara- (Female)', +5, 0, -5],
561
+ ["Gradio makes it easy to build and share machine learning apps.", 'English-Jenny (Female)', 0, +10, 0],
562
+ ["こんにちは、これは音声合成のテストです。", 'Japanese-Nanami- (Female)', -10, 0, +5],
563
+ ],
564
+ inputs=[input_text, language, rate, volume, pitch],
565
+ outputs=[output_text_status, output_audio], # خروجی ها باید با تابع اصلی مطابقت داشته باشند
566
+ fn=text_to_speech_edge_sync_wrapper, # تابع باید برای examples هم تعریف شود
567
+ cache_examples=True, # اگر می خواهید مثال ها کش شوند ( مطمئن شوید با مقادیر صفر مشکلی نیست)
568
+ label="💡 چند نمونه برای شروع"
569
+ )
570
 
571
+ # اجرای برنامه
572
+ demo.launch()