Hamed744 commited on
Commit
d6e8044
·
verified ·
1 Parent(s): 4548339

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +185 -87
app.py CHANGED
@@ -1,4 +1,5 @@
1
  import gradio as gr
 
2
  import tempfile
3
  import asyncio
4
  import traceback
@@ -6,132 +7,229 @@ import os
6
  import logging
7
  import time
8
  import threading
9
- from gtts import gTTS
 
 
10
  from deep_translator import GoogleTranslator
11
 
12
- # --- تنظیمات لاگینگ ---
13
- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
 
14
 
15
- # --- تابع ری‌استارت خودکار ---
16
  def auto_restart_service():
17
  RESTART_INTERVAL_SECONDS = 24 * 60 * 60
 
18
  time.sleep(RESTART_INTERVAL_SECONDS)
 
19
  os._exit(1)
20
-
21
- # --- دیکشنری لهجه‌های گوگل (جایگزین صداهای قبلی) ---
22
- # gTTS از لهجه‌های مختلف پشتیبانی می‌کند
23
- language_dict_google = {
24
- 'انگلیسی (آمریکا)': 'com',
25
- 'انگلیسی (بریتانیا)': 'co.uk',
26
- 'انگلیسی (استرالیا)': 'com.au',
27
- 'انگلیسی (کانادا)': 'ca',
28
- 'انگلیسی (هند)': 'co.in',
29
- 'انگلیسی (ایرلند)': 'ie',
30
- 'انگلیسی (آفریقای جنوبی)': 'co.za'
 
 
 
 
 
31
  }
32
 
33
- # --- تابع ترجمه ---
 
34
  async def translate_text_google_async(text_to_translate, target_language="en"):
 
 
 
 
35
  if not text_to_translate or not text_to_translate.strip():
36
  return "خطا: متنی برای ترجمه وارد نشده است.", None
37
 
38
  def _translate():
 
39
  try:
40
- logging.info(f"شروع ترجمه: {text_to_translate[:30]}...")
 
41
  translated = GoogleTranslator(source='fa', target=target_language).translate(text_to_translate)
 
42
  return translated
43
  except Exception as e:
44
- logging.error(f"خطا در ترجمه: {e}")
45
  return None
46
 
47
- translated_text = await asyncio.to_thread(_translate)
48
- if translated_text:
49
- return "ترجمه موفق", translated_text
50
- return "خطا در فرآیند ترجمه", None
51
-
52
- # --- تابع تولید صدا با gTTS (جایگزین Edge-TTS) ---
53
- async def text_to_speech_gtts_async(text_to_speak, accent_key, slow_mode=False):
54
- tld = language_dict_google.get(accent_key, 'com')
55
-
 
 
 
 
 
 
 
 
 
 
 
56
  if not text_to_speak or not text_to_speak.strip():
57
- return "خطای TTS: متن خالی است.", None
58
-
59
- logging.info(f"TTS: شروع تولید صدا با لهجه {accent_key}...")
60
-
61
- def _generate_audio():
62
- try:
63
- tts = gTTS(text=text_to_speak, lang='en', tld=tld, slow=slow_mode)
64
- with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp_file:
65
- tmp_path = tmp_file.name
66
- tts.save(tmp_path)
67
- return tmp_path
68
- except Exception as e:
69
- logging.error(f"خطای تولید صدا: {e}")
70
- return None
71
-
72
- audio_path = await asyncio.to_thread(_generate_audio)
73
- if audio_path:
74
- return "TTS موفق", audio_path
75
- return "خطا در تولید صدا", None
76
-
77
- # --- تابع اصلی Wrapper ---
78
- async def translate_and_speak_async_wrapper(persian_text, accent_key, speed_mode):
79
  if not persian_text or not persian_text.strip():
80
- return "لطفاً متن فارسی را وارد کنید.", None
81
 
82
- # ۱. ترجمه
83
- status_msg, translated_text = await translate_text_google_async(persian_text)
 
84
  if not translated_text:
85
- return status_msg, None
 
 
 
 
 
 
 
 
 
86
 
87
- # ۲. تبدیل به صدا (سرعت کم یا عادی)
88
- is_slow = True if speed_mode == "آهسته" else False
89
- tts_status, audio_path = await text_to_speech_gtts_async(translated_text, accent_key, is_slow)
90
 
91
  if not audio_path:
92
- return f"{translated_text}\n\n({tts_status})", None
93
 
94
- return translated_text, audio_path
95
 
96
- # --- رابط کاربری Gradio ---
97
  FLY_PRIMARY_COLOR_HEX = "#4F46E5"
 
 
 
 
 
 
 
 
 
 
 
98
  custom_css = f"""
99
- @import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@400;700&display=swap');
100
- body {{font-family:'Vazirmatn', sans-serif; direction:rtl; background-color:#F9FAFB;}}
101
- .app-title-card {{text-align:center; padding:2rem; background:linear-gradient(135deg,{FLY_PRIMARY_COLOR_HEX} 0%,#10B981 100%); color:white; border-radius:1rem; margin-bottom:1rem;}}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  """
103
 
104
- with gr.Blocks(css=custom_css, title="Alpha Translator") as demo:
105
  gr.HTML(f"""
106
  <div class="app-title-card">
107
- <h1>🚀 Alpha Translator (نسخه پایدار)</h1>
108
- <p>ترجمه هوشمند فارسی به انگلیسی با صدای زنده</p>
109
  </div>
110
  """)
111
 
112
- with gr.Row():
113
- with gr.Column():
114
- input_text = gr.Textbox(lines=5, label="📝 متن فارسی", placeholder="متن خود را اینجا بنویسید...")
115
- accent_dropdown = gr.Dropdown(choices=list(language_dict_google.keys()), value='انگلیسی (آمریکا)', label="🗣️ انتخاب لهجه")
116
- speed_radio = gr.Radio(["عادی", "آهسته"], value="عادی", label="🐢 سرعت خواندن")
117
- submit_btn = gr.Button("🚀 ترجمه و تولید صدا", variant="primary")
118
-
119
- with gr.Column():
120
- output_text = gr.Textbox(label="📜 ترجمه انگلیسی", interactive=False, lines=5)
121
- output_audio = gr.Audio(label="🎧 پخش صدا", autoplay=True)
122
-
123
- submit_btn.click(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  fn=translate_and_speak_async_wrapper,
125
- inputs=[input_text, accent_dropdown, speed_radio],
126
- outputs=[output_text, output_audio]
127
- )
128
-
129
- gr.Examples(
130
- examples=[["چطور می‌توانم به ایستگاه مترو بروم؟", "انگلیسی (آمریکا)", "عادی"],
131
- ["این غذا بسیار لذیذ به نظر می‌رسد.", "انگلیسی (بریتانیا)", "آهسته"]],
132
- inputs=[input_text, accent_dropdown, speed_radio]
133
  )
134
 
135
  if __name__ == "__main__":
136
- threading.Thread(target=auto_restart_service, daemon=True).start()
137
- demo.launch(server_name="0.0.0.0", server_port=7860)
 
1
  import gradio as gr
2
+ import edge_tts
3
  import tempfile
4
  import asyncio
5
  import traceback
 
7
  import logging
8
  import time
9
  import threading
10
+ import sys
11
+
12
+ # --- کتابخانه جدید برای ترجمه با گوگل ---
13
  from deep_translator import GoogleTranslator
14
 
15
+ # --- START: پیکربندی لاگینگ ---
16
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
17
+ # --- END: پیکربندی لاگینگ ---
18
 
19
+ # --- START: تابع ری‌استارت خودکار (بدون تغییر) ---
20
  def auto_restart_service():
21
  RESTART_INTERVAL_SECONDS = 24 * 60 * 60
22
+ logging.info(f"سرویس برای ری‌استارت خودکار پس از {RESTART_INTERVAL_SECONDS / 3600:.0f} ساعت زمان‌بندی شده است.")
23
  time.sleep(RESTART_INTERVAL_SECONDS)
24
+ logging.info(f"زمان ری‌استارت خودکار فرا رسیده است. برنامه برای ری‌استارت خارج می‌شود...")
25
  os._exit(1)
26
+ # --- END: تابع ری‌استارت خودکار ---
27
+
28
+ # --- دیکشنری صداهای انگلیسی (بدون تغییر) ---
29
+ language_dict_persian_keys = {
30
+ 'انگلیسی (آمریکا) - جنی (زن)': 'en-US-JennyNeural', 'انگلیسی (آمریکا) - گای (مرد)': 'en-US-GuyNeural',
31
+ 'انگلیسی (آمریکا) - آنا (زن، صدای کودک)': 'en-US-AnaNeural', 'انگلیسی (آمریکا) - آریا (زن)': 'en-US-AriaNeural',
32
+ 'انگلیسی (آمریکا) - کریستوفر (مرد)': 'en-US-ChristopherNeural', 'انگلیسی (آمریکا) - اریک (مرد)': 'en-US-EricNeural',
33
+ 'انگلیسی (آمریکا) - میشل (زن)': 'en-US-MichelleNeural', 'انگلیسی (آمریکا) - راجر (مرد)': 'en-US-RogerNeural',
34
+ 'انگلیسی (بریتانیا) - لیبی (زن)': 'en-GB-LibbyNeural', 'انگلیسی (بریتانیا) - میزی (زن)': 'en-GB-MaisieNeural',
35
+ 'انگلیسی (بریتانیا) - رایان (مرد)': 'en-GB-RyanNeural', 'انگلیسی (بریتانیا) - سونیا (زن)': 'en-GB-SoniaNeural',
36
+ 'انگلیسی (بریتانیا) - توماس (مرد)': 'en-GB-ThomasNeural', 'انگلیسی (بریتانیا) - میا (زن، جدید)': 'en-GB-MiaNeural',
37
+ 'انگلیسی (استرالیا) - ناتاشا (زن)': 'en-AU-NatashaNeural', 'انگلیسی (استرالیا) - ویلیام (مرد)': 'en-AU-WilliamNeural',
38
+ 'انگلیسی (کانادا) - کلارا (زن)': 'en-CA-ClaraNeural', 'انگلیسی (کانادا) - لیام (مرد)': 'en-CA-LiamNeural',
39
+ 'انگلیسی (ایرلند) - امیلی (زن)': 'en-IE-EmilyNeural', 'انگلیسی (ایرلند) - کانر (مرد)': 'en-IE-ConnorNeural',
40
+ 'انگلیسی (هند) - نیرجا (زن)': 'en-IN-NeerjaNeural', 'انگلیسی (هند) - پرابهات (مرد)': 'en-IN-PrabhatNeural',
41
+ 'انگلیسی (آفریقای جنوبی) - لیا (زن)': 'en-ZA-LeahNeural', 'انگلیسی (آفریقای جنوبی) - لوک (مرد)': 'en-ZA-LukeNeural',
42
  }
43
 
44
+
45
+ # --- START: تابع جدید ترجمه با Google Translate ---
46
  async def translate_text_google_async(text_to_translate, target_language="en"):
47
+ """
48
+ متن را با استفاده از Google Translate (از طریق کتابخانه deep-translator) ترجمه می‌کند.
49
+ این تابع به صورت آسنکرون اجرا می‌شود تا برنامه اصلی را مسدود نکند.
50
+ """
51
  if not text_to_translate or not text_to_translate.strip():
52
  return "خطا: متنی برای ترجمه وارد نشده است.", None
53
 
54
  def _translate():
55
+ # این تابع همزمان (synchronous) در یک ترد جداگانه اجرا می‌شود
56
  try:
57
+ logging.info(f"شروع ترجمه متن: '{text_to_translate[:30]}...'")
58
+ # ترجمه از فارسی ('fa') به زبان مقصد (پیش‌فرض 'en')
59
  translated = GoogleTranslator(source='fa', target=target_language).translate(text_to_translate)
60
+ logging.info("ترجمه با Google Translate موفق بود.")
61
  return translated
62
  except Exception as e:
63
+ logging.error(f"خطا در حین ترجمه با Google: {e}\n{traceback.format_exc()}")
64
  return None
65
 
66
+ try:
67
+ # اجرای تابع همزمان _translate در یک ترد جداگانه برای جلوگیری از بلاک شدن
68
+ translated_text = await asyncio.to_thread(_translate)
69
+
70
+ if translated_text:
71
+ return "ترجمه موفق", translated_text
72
+ else:
73
+ return "خطا: مشکلی در فرآیند ترجمه پیش آمد.", None
74
+
75
+ except Exception as e:
76
+ logging.error(f"خطای غیرمنتظره در فراخوانی ترد ترجمه: {e}\n{traceback.format_exc()}")
77
+ return "خطای غیرمنتظره: مشکلی در سیستم ترجمه رخ داد.", None
78
+ # --- END: تابع جدید ترجمه ---
79
+
80
+
81
+ # --- تابع تولید صدا (بدون تغییر) ---
82
+ async def text_to_speech_edge_async(text_to_speak, tts_voice_key, rate, volume, pitch):
83
+ voice_id = language_dict_persian_keys.get(tts_voice_key)
84
+ if not voice_id:
85
+ return f"خطای TTS: صدای '{tts_voice_key}' یافت نشد.", None
86
  if not text_to_speak or not text_to_speak.strip():
87
+ return "خطای TTS: متن ترجمه شده برای خواندن خالی است.", None
88
+
89
+ logging.info(f"TTS: شروع تولید صدا برای '{tts_voice_key}'...")
90
+ tmp_path = None
91
+ try:
92
+ rate_str, volume_str, pitch_str = f"{int(rate):+g}%", f"{int(volume):+g}%", f"{int(pitch):+g}Hz"
93
+ communicate = edge_tts.Communicate(text_to_speak, voice_id, rate=rate_str, volume=volume_str, pitch=pitch_str)
94
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp_file:
95
+ tmp_path = tmp_file.name
96
+ await communicate.save(tmp_path)
97
+ logging.info(f"TTS: صدا با موفقیت در '{os.path.basename(tmp_path)}' ذخیره شد.")
98
+ return "TTS موفق", tmp_path
99
+ except Exception as e:
100
+ if tmp_path and os.path.exists(tmp_path):
101
+ try: os.remove(tmp_path)
102
+ except Exception as e_rem: logging.warning(f"TTS: خطای پاک کردن فایل موقت: {e_rem}")
103
+ error_type = type(e).__name__
104
+ logging.error(f"TTS: خطای نامشخص برای '{voice_id}': {error_type} - {e}")
105
+ return f"خطای TTS ({error_type}): مشکلی در تولید صدا پیش آمد.", None
106
+
107
+ # --- تابع اصلی Wrapper (با تغییر جزئی برای فراخوانی تابع جدید) ---
108
+ async def translate_and_speak_async_wrapper(persian_text, english_tts_voice_key, rate, volume, pitch):
109
  if not persian_text or not persian_text.strip():
110
+ return "لطفاً متن فارسی را برای ترجمه وارد کنید.", None, None
111
 
112
+ # --- تغییر کلیدی: فراخوانی تابع جدید ترجمه گوگل ---
113
+ translation_status_msg, translated_text = await translate_text_google_async(persian_text, target_language="en")
114
+
115
  if not translated_text:
116
+ # اگر translated_text خالی است، translation_status_msg حاوی پیام خطا است
117
+ return translation_status_msg, None
118
+
119
+ translated_text_output = translated_text
120
+
121
+ if english_tts_voice_key not in language_dict_persian_keys:
122
+ if language_dict_persian_keys:
123
+ english_tts_voice_key = list(language_dict_persian_keys.keys())[0]
124
+ else:
125
+ return f"{translated_text_output}\n\n(خطای TTS: هیچ صدایی موجود نیست.)", None
126
 
127
+ tts_status_msg, audio_path = await text_to_speech_edge_async(translated_text, english_tts_voice_key, rate, volume, pitch)
 
 
128
 
129
  if not audio_path:
130
+ return f"{translated_text_output}\n\n({tts_status_msg})", None
131
 
132
+ return translated_text_output, audio_path
133
 
134
+ # --- بخش UI و Gradio (کامل و بدون تغییر) ---
135
  FLY_PRIMARY_COLOR_HEX = "#4F46E5"
136
+ FLY_SECONDARY_COLOR_HEX = "#10B981"
137
+ FLY_ACCENT_COLOR_HEX = "#D97706"
138
+ FLY_TEXT_COLOR_HEX = "#1F2937"
139
+ FLY_SUBTLE_TEXT_HEX = "#6B7280"
140
+ FLY_LIGHT_BACKGROUND_HEX = "#F9FAFB"
141
+ FLY_WHITE_HEX = "#FFFFFF"
142
+ FLY_BORDER_COLOR_HEX = "#D1D5DB"
143
+ FLY_INPUT_BG_HEX_SIMPLE = "#F3F4F6"
144
+ FLY_PANEL_BG_SIMPLE = "#E0F2FE"
145
+
146
+ app_theme_outer = gr.themes.Base(font=[gr.themes.GoogleFont("Inter"), "system-ui", "sans-serif"]).set(body_background_fill=FLY_LIGHT_BACKGROUND_HEX)
147
  custom_css = f"""
148
+ @import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;600;700;800&display=swap');
149
+ @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700;800&display=swap');
150
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap');
151
+ :root {{
152
+ --fly-primary: {FLY_PRIMARY_COLOR_HEX}; --fly-secondary: {FLY_SECONDARY_COLOR_HEX};
153
+ --fly-accent: {FLY_ACCENT_COLOR_HEX}; --fly-text-primary: {FLY_TEXT_COLOR_HEX};
154
+ --fly-text-secondary: {FLY_SUBTLE_TEXT_HEX}; --fly-bg-light: {FLY_LIGHT_BACKGROUND_HEX};
155
+ --fly-bg-white: {FLY_WHITE_HEX}; --fly-border-color: {FLY_BORDER_COLOR_HEX};
156
+ --fly-input-bg-simple: {FLY_INPUT_BG_HEX_SIMPLE}; --fly-panel-bg-simple: {FLY_PANEL_BG_SIMPLE};
157
+ --font-global: 'Vazirmatn', 'Inter', 'Poppins', system-ui, sans-serif;
158
+ --font-english: 'Poppins', 'Inter', system-ui, sans-serif;
159
+ --radius-xl: 1rem; --shadow-xl: 0 20px 25px -5px rgba(0,0,0,0.1),0 8px 10px -6px rgba(0,0,0,0.1);
160
+ --fly-primary-rgb: 79,70,229; --fly-accent-rgb: 217,119,6;
161
+ }}
162
+ body {{font-family:var(--font-global);direction:rtl;background-color:var(--fly-bg-light);color:var(--fly-text-primary);line-height:1.7;-webkit-font-smoothing:antialiased;font-size:16px;}}
163
+ .gradio-container {{max-width:100% !important;width:100% !important;min-height:100vh;margin:0 auto !important;padding:0 !important;background:linear-gradient(170deg, #E0F2FE 0%, #F3E8FF 100%);display:flex;flex-direction:column;}}
164
+ .app-title-card {{text-align:center;padding:2.5rem 1rem;margin:0;background:linear-gradient(135deg,var(--fly-primary) 0%,var(--fly-secondary) 100%);color:var(--fly-bg-white);border-bottom-left-radius:var(--radius-xl);border-bottom-right-radius:var(--radius-xl);box-shadow:var(--shadow-xl);position:relative;overflow:hidden;}}
165
+ .app-title-card::before {{content:'';position:absolute;top:-50px;right:-50px;width:150px;height:150px;background:rgba(255,255,255,0.1);border-radius:9999px;opacity:0.5;transform:rotate(45deg);}}
166
+ .app-title-card h1 {{font-size:2.25em !important;font-weight:800 !important;margin:0 0 0.5rem 0;font-family:var(--font-english);letter-spacing:-0.5px;text-shadow:0 2px 4px rgba(0,0,0,0.1);}}
167
+ .app-title-card p {{font-size:1em !important;margin-top:0.25rem;font-weight:400;color:rgba(255,255,255,0.85) !important;}}
168
+ .app-footer-fly {{text-align:center;font-size:0.85em;color:var(--fly-text-secondary);margin-top:2.5rem;padding:1rem 0;background-color:rgba(255,255,255,0.3);backdrop-filter:blur(5px);border-top:1px solid var(--fly-border-color);}}
169
+ footer,.gradio-footer,.flagging-container,.flex.row.gap-2.absolute.bottom-2.right-2 {{display:none !important;visibility:hidden !important;}}
170
+ .main-content-area {{flex-grow:1;padding:0.75rem;width:100%;margin:0 auto;box-sizing:border-box;}}
171
+ .content-panel-simple {{background-color:var(--fly-bg-white);padding:1rem;border-radius:var(--radius-xl);box-shadow:var(--shadow-xl);margin-top:-2rem;position:relative;z-index:10;margin-bottom:2rem;width:100%;box-sizing:border-box;}}
172
+ .content-panel-simple .gr-button.lg.primary {{background:var(--fly-accent) !important;margin-top:1rem !important;padding:12px 20px !important;transition:all 0.25s ease-in-out !important;color:white !important;font-weight:600 !important;border-radius:10px !important;border:none !important;box-shadow:0 3px 8px -1px rgba(var(--fly-accent-rgb),0.3) !important;width:100% !important;}}
173
+ .content-panel-simple .gr-button.lg.primary:hover {{background:#B45309 !important;transform:translateY(-1px) !important;box-shadow:0 5px 10px -1px rgba(var(--fly-accent-rgb),0.4) !important;}}
174
+ .content-panel-simple .gr-input > label + div > textarea, .content-panel-simple .gr-dropdown > label + div > div > input, .content-panel-simple .gr-textbox > label + div > textarea {{border-radius:8px !important;border:1.5px solid var(--fly-border-color) !important;background-color:var(--fly-input-bg-simple) !important;padding:10px 12px !important;}}
175
+ .content-panel-simple .gr-input > label + div > textarea:focus, .content-panel-simple .gr-dropdown > label + div > div > input:focus, .content-panel-simple .gr-textbox > label + div > textarea:focus {{border-color:var(--fly-primary) !important;box-shadow:0 0 0 3px rgba(var(--fly-primary-rgb),0.12) !important;background-color:var(--fly-bg-white) !important;}}
176
+ .content-panel-simple .gr-textbox[label*="ترجمه شده"] > label + div > textarea {{background-color:var(--fly-panel-bg-simple) !important;border-color:#A5D5FE !important;font-family:var(--font-english);font-size:1em !important;}}
177
+ .content-panel-simple div[label*="تنظیمات پیشرفته"] .gr-panel {{border-radius:8px !important;border:1px solid var(--fly-border-color) !important;background-color:var(--fly-input-bg-simple) !important;}}
178
+ @media (min-width:768px) {{.main-content-area {{max-width:780px;}} .content-panel-simple {{padding:2rem;}} .content-panel-simple .main-content-row {{display:flex !important;gap:1.5rem !important;}} .content-panel-simple .gr-button.lg.primary {{width:auto !important;align-self:flex-start;}} }}
179
  """
180
 
181
+ with gr.Blocks(theme=app_theme_outer, css=custom_css, title="Alpha Translator") as demo:
182
  gr.HTML(f"""
183
  <div class="app-title-card">
184
+ <h1>🚀 Alpha Translator</h1>
185
+ <p>جادوی ترجمه و تلفظ در دستان شما</p>
186
  </div>
187
  """)
188
 
189
+ with gr.Column(elem_classes=["main-content-area"]):
190
+ with gr.Group(elem_classes=["content-panel-simple"]):
191
+ with gr.Row(elem_classes=["main-content-row"]):
192
+ with gr.Column(scale=3, min_width=300):
193
+ input_text_persian = gr.Textbox(lines=4, label="📝 متن فارسی برای ترجمه", placeholder="مثال: سلام، فردا هوا چطور است؟")
194
+ dropdown_label = "🗣️ انتخاب گوینده و لهجه انگلیسی"
195
+ current_voice_list = list(language_dict_persian_keys.keys())
196
+ default_english_tts_voice = current_voice_list[0] if current_voice_list else "لیست صداها خالی است"
197
+ language_dropdown_tts_english = gr.Dropdown(choices=current_voice_list, value=default_english_tts_voice, label=dropdown_label, interactive=bool(current_voice_list))
198
+
199
+ with gr.Accordion("⚙️ تنظیمات پیشرفته صدا", open=False):
200
+ with gr.Row():
201
+ rate_slider = gr.Slider(-100, 100, 0, step=1, label="سرعت (%)", scale=1)
202
+ volume_slider = gr.Slider(-100, 100, 0, step=1, label="حجم (%)", scale=1)
203
+ pitch_slider = gr.Slider(-50, 50, 0, step=1, label="گام (Hz)")
204
+ submit_button = gr.Button("🚀 ترجمه و تلفظ", variant="primary", elem_classes=["lg"])
205
+
206
+ with gr.Column(scale=2, min_width=280):
207
+ output_text_translated = gr.Textbox(label="📜 متن ترجمه شده (انگلیسی)", interactive=False, lines=6, placeholder="متن انگلیسی ترجمه شده یا پیام‌های خطا...")
208
+ output_audio = gr.Audio(type="filepath", label="🎧 فایل صوتی", interactive=False, autoplay=True)
209
+
210
+ if language_dict_persian_keys:
211
+ gr.HTML("<hr class='custom-hr' style='margin: 1.5rem 0;'>")
212
+ gr.Examples(
213
+ examples=[
214
+ ["قیمت این لباس چقدر است؟", default_english_tts_voice, 0, 0, 0],
215
+ ["می‌توانید آدرس را روی نقشه به من نشان دهید؟", list(language_dict_persian_keys.keys())[min(1, len(language_dict_persian_keys)-1)], 0, 0, 0],
216
+ ["ببخشید، متوجه نشدم. امکان دارد تکرار کنید؟", list(language_dict_persian_keys.keys())[min(8, len(language_dict_persian_keys)-1)], -10, 0, 0],
217
+ ],
218
+ inputs=[input_text_persian, language_dropdown_tts_english, rate_slider, volume_slider, pitch_slider],
219
+ outputs=[output_text_translated, output_audio],
220
+ fn=translate_and_speak_async_wrapper,
221
+ cache_examples=os.getenv("GRADIO_CACHE_EXAMPLES", "False").lower() == "true",
222
+ label="💡 نمونه‌های کاربردی"
223
+ )
224
+
225
+ gr.Markdown("<p class='app-footer-fly'>Alpha Language Learning © 2025</p>")
226
+
227
+ submit_button.click(
228
  fn=translate_and_speak_async_wrapper,
229
+ inputs=[input_text_persian, language_dropdown_tts_english, rate_slider, volume_slider, pitch_slider],
230
+ outputs=[output_text_translated, output_audio]
 
 
 
 
 
 
231
  )
232
 
233
  if __name__ == "__main__":
234
+ threading.Thread(target=auto_restart_service, daemon=True, name="AutoRestartThread").start()
235
+ demo.launch(server_name="0.0.0.0", server_port=int(os.getenv("PORT", 7860)), show_error=True)