suprimedev commited on
Commit
f25fdc1
·
verified ·
1 Parent(s): 4881f89

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +110 -128
app.py CHANGED
@@ -11,17 +11,22 @@ import json
11
  from datetime import datetime, timedelta
12
  import threading
13
  import hashlib
 
 
14
 
15
  warnings.filterwarnings("ignore")
16
 
17
- # ذخیره‌سازی نتایج برای جلوگیری از مشکل session
18
  results_cache = {}
19
  cache_lock = threading.Lock()
20
 
21
- # تابع برای پاکسازی cache قدیمی (بیش از 24 ساعت)
 
 
 
22
  def cleanup_old_cache():
23
  while True:
24
- time.sleep(3600) # هر ساعت چک کن
25
  with cache_lock:
26
  current_time = datetime.now()
27
  keys_to_remove = []
@@ -33,24 +38,20 @@ def cleanup_old_cache():
33
  if keys_to_remove:
34
  print(f"[CACHE] {len(keys_to_remove)} نتیجه قدیمی پاک شد.")
35
 
36
- # شروع thread پاکسازی
37
  cleanup_thread = threading.Thread(target=cleanup_old_cache, daemon=True)
38
  cleanup_thread.start()
39
 
40
  def get_cache_key(video_url, language):
41
- """ایجاد کلید یکتا برای cache بر اساس URL و زبان"""
42
  return hashlib.md5(f"{video_url}_{language}".encode()).hexdigest()
43
 
44
- def save_result_to_cache(video_url, language, mp3_path, text, status_msg):
45
  """ذخیره نتیجه در cache"""
46
- cache_key = get_cache_key(video_url, language)
47
-
48
  # کپی فایل MP3 به مکان دائمی
 
49
  if mp3_path and os.path.exists(mp3_path):
50
  cache_mp3_path = f"cache_{cache_key}.mp3"
51
  shutil.copy2(mp3_path, cache_mp3_path)
52
- else:
53
- cache_mp3_path = None
54
 
55
  with cache_lock:
56
  results_cache[cache_key] = {
@@ -59,11 +60,12 @@ def save_result_to_cache(video_url, language, mp3_path, text, status_msg):
59
  'text': text,
60
  'status_msg': status_msg,
61
  'video_url': video_url,
62
- 'language': language
 
 
63
  }
64
 
65
- print(f"[CACHE] نتیجه ذخیره شد: {cache_key}")
66
- return cache_key
67
 
68
  def get_result_from_cache(cache_key):
69
  """دریافت نتیجه از cache"""
@@ -71,28 +73,53 @@ def get_result_from_cache(cache_key):
71
  if cache_key in results_cache:
72
  result = results_cache[cache_key]
73
  print(f"[CACHE] نتیجه یافت شد: {cache_key}")
74
- return result['mp3_path'], result['text'], result['status_msg']
75
- return None, None, None
76
 
77
- def convert_to_mp3_and_transcribe(video_url, language, use_cache=True):
78
- """
79
- دانلود ویدیو/صوت از لینک، تبدیل به MP3، و استخراج متن با Google Speech Recognition.
80
- """
81
  if not video_url:
82
  return None, None, "لینک ویدیو را وارد کنید.", None
83
 
84
- # چک کردن cache
85
  cache_key = get_cache_key(video_url, language)
86
- if use_cache:
87
- cached_mp3, cached_text, cached_status = get_result_from_cache(cache_key)
88
- if cached_mp3 is not None or cached_text is not None:
89
- return cached_mp3, cached_text, f"[از حافظه] {cached_status}", cache_key
90
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  progress = gr.Progress(track_tqdm=False)
92
 
93
  try:
94
  print(f"[DEBUG] شروع پردازش لینک: {video_url} (زبان: {language})")
95
-
96
  progress(0, desc="شروع دانلود...")
97
 
98
  # دانلود و تبدیل به MP3
@@ -120,10 +147,7 @@ def convert_to_mp3_and_transcribe(video_url, language, use_cache=True):
120
  break
121
 
122
  if not mp3_file:
123
- print("[DEBUG] هیچ MP3 پیدا نشد.")
124
- status_msg = "خطا در دانلود یا تبدیل. مطمئن شوید لینک معتبر است."
125
- save_result_to_cache(video_url, language, None, None, status_msg)
126
- return None, None, status_msg, cache_key
127
 
128
  progress(0.3, desc="دانلود کامل. کپی MP3...")
129
 
@@ -134,46 +158,28 @@ def convert_to_mp3_and_transcribe(video_url, language, use_cache=True):
134
  if os.path.exists(mp3_file):
135
  os.remove(mp3_file)
136
 
137
- print(f"[DEBUG] MP3 آماده: {temp_mp3.name}, اندازه: {os.path.getsize(temp_mp3.name)/1024:.1f} KB")
138
-
139
- progress(0.5, desc="MP3 آماده. شروع استخراج متن با Google...")
140
 
141
  # استخراج متن
142
  text, error_msg = transcribe_audio(temp_mp3.name, progress, language)
143
 
144
  if text is None:
145
- status_msg = f"MP3 آماده است، اما استخراج متن fail شد. {error_msg}"
146
- save_result_to_cache(video_url, language, temp_mp3.name, None, status_msg)
147
- return temp_mp3.name, None, status_msg, cache_key
148
-
149
- progress(1.0, desc="استخراج متن کامل شد!")
150
 
151
- print(f"[DEBUG] متن نهایی (اولین 100 کاراکتر): {text[:100]}...")
152
 
153
- status_msg = f"موفق! زبان: {language_display(language)}. {len(text.split())} کلمه استخراج شد."
154
- save_result_to_cache(video_url, language, temp_mp3.name, text, status_msg)
155
-
156
- return temp_mp3.name, text, status_msg, cache_key
157
 
158
  except Exception as e:
159
  print(f"[ERROR] خطای کلی: {str(e)}")
160
- status_msg = f"خطا کلی: {str(e)}"
161
- save_result_to_cache(video_url, language, None, None, status_msg)
162
- return None, None, status_msg, cache_key
163
 
164
  def language_display(lang_code):
165
  """نمایش نام زبان"""
166
- if lang_code == 'fa-IR':
167
- return "پارسی"
168
- elif lang_code == 'en-US':
169
- return "انگلیسی"
170
- else:
171
- return lang_code
172
 
173
  def transcribe_audio(mp3_path, progress, language, chunk_length_ms=60000, overlap_ms=5000):
174
- """
175
- استخراج متن با Google STT + retry برای rate limit. chunk 60s.
176
- """
177
  recognizer = sr.Recognizer()
178
  recognizer.energy_threshold = 300
179
  recognizer.dynamic_energy_threshold = True
@@ -185,18 +191,16 @@ def transcribe_audio(mp3_path, progress, language, chunk_length_ms=60000, overla
185
  temp_wav_dir = tempfile.mkdtemp()
186
  audio = AudioSegment.from_mp3(mp3_path)
187
  duration_ms = len(audio)
 
188
  if duration_ms == 0:
189
- print("[DEBUG] فایل صوتی خالی!")
190
- return None, "فایل صوتی خالی یا بدون صدا."
191
 
192
  step_size = chunk_length_ms - overlap_ms
193
  if step_size <= 0:
194
  step_size = chunk_length_ms // 2
195
  num_chunks = max(1, (duration_ms // step_size) + 1)
196
 
197
- print(f"[DEBUG] مدت: {duration_ms/1000:.1f}s, chunkها: {num_chunks}, گام: {step_size/1000:.1f}s")
198
-
199
- progress(0.5, desc=f"تقسیم به {num_chunks} chunk 60s (زبان: {language_display(language)})...")
200
 
201
  i = 0
202
  chunk_idx = 1
@@ -205,14 +209,12 @@ def transcribe_audio(mp3_path, progress, language, chunk_length_ms=60000, overla
205
  chunk = audio[i:end_pos]
206
 
207
  if len(chunk) < 3000:
208
- print(f"[DEBUG] Chunk {chunk_idx} خیلی کوتاه ({len(chunk)/1000}s), رد شد.")
209
  break
210
 
211
  temp_wav = os.path.join(temp_wav_dir, f"chunk_{i}.wav")
212
 
213
  try:
214
  chunk.export(temp_wav, format="wav")
215
- print(f"[DEBUG] Chunk {chunk_idx} export شد: {temp_wav}")
216
 
217
  text_chunk = None
218
  retry_count = 0
@@ -220,36 +222,27 @@ def transcribe_audio(mp3_path, progress, language, chunk_length_ms=60000, overla
220
 
221
  while retry_count < max_retries:
222
  try:
223
- progress(0.5 + (i / duration_ms) * 0.5, desc=f"Chunk {chunk_idx}/{num_chunks} ({(i/1000):.0f}-{end_pos/1000:.0f}s, retry {retry_count+1})...")
224
 
225
  with sr.AudioFile(temp_wav) as source:
226
  recognizer.adjust_for_ambient_noise(source, duration=0.5)
227
- audio_data = recognizer.record(source, duration=None)
228
 
229
  text = recognizer.recognize_google(audio_data, language=language)
230
- if text.strip():
231
- text_chunk = text
232
- print(f"[DEBUG] Chunk {chunk_idx} موفق: {text[:50]}...")
233
- break
234
- else:
235
- text_chunk = "[سکوت]"
236
- print(f"[DEBUG] Chunk {chunk_idx} سکوت.")
237
- break
238
 
239
  except sr.UnknownValueError:
240
  text_chunk = "[نامشخص]"
241
- print(f"[DEBUG] Chunk {chunk_idx} نامشخص (نویز/سکوت).")
242
  break
243
 
244
  except sr.RequestError as e:
245
  retry_count += 1
246
- print(f"[DEBUG] Chunk {chunk_idx} RequestError (rate limit?): {str(e)}. Retry {retry_count}/{max_retries}")
247
  if retry_count < max_retries:
248
  time.sleep(2)
249
  else:
250
- text_chunk = f"[خطا rate limit: {str(e)[:30]}...]"
251
  bad_chunks += 1
252
- print(f"[DEBUG] Chunk {chunk_idx} fail پس از retryها.")
253
  break
254
 
255
  if text_chunk:
@@ -259,10 +252,8 @@ def transcribe_audio(mp3_path, progress, language, chunk_length_ms=60000, overla
259
 
260
  total_chunks += 1
261
 
262
- except Exception as chunk_e:
263
- print(f"[ERROR] Chunk {chunk_idx} (خطای کلی): {str(chunk_e)}")
264
- text_chunk = f"[خطا chunk: {str(chunk_e)[:30]}...]"
265
- full_text.append(text_chunk)
266
  bad_chunks += 1
267
  total_chunks += 1
268
 
@@ -275,78 +266,69 @@ def transcribe_audio(mp3_path, progress, language, chunk_length_ms=60000, overla
275
  shutil.rmtree(temp_wav_dir, ignore_errors=True)
276
 
277
  final_text = ' '.join(full_text).strip()
278
- error_msg = ""
279
 
280
  if not final_text:
281
- error_msg = "هیچ chunk موفقی نبود."
282
- return None, error_msg
283
 
284
  bad_ratio = bad_chunks / total_chunks if total_chunks > 0 else 1
285
  if bad_ratio > 0.7:
286
- error_msg = f"بیش از 70% chunkها fail ({bad_ratio*100:.0f}%). ممکن است نویز باشد، rate limit گوگل، یا زبان اشتباه انتخاب شده."
287
- return None, error_msg
288
-
289
- print(f"[DEBUG] {total_chunks} chunk پردازش شد, {bad_chunks} بد.")
290
 
291
- return final_text, error_msg
292
 
293
- # API endpoint برای دریافت نتیجه با cache key
294
- def get_cached_result(cache_key):
295
- """API برای دریافت نتیجه ذخیره شده"""
296
- mp3, text, status = get_result_from_cache(cache_key)
297
- return {
298
- "cache_key": cache_key,
299
- "mp3_available": mp3 is not None,
300
- "text": text,
301
- "status": status
302
- }
 
 
 
 
 
 
 
 
 
 
 
303
 
304
- # رابط Gradio با خروجی cache key
305
  iface = gr.Interface(
306
- fn=convert_to_mp3_and_transcribe,
307
  inputs=[
308
  gr.Textbox(
309
- label="لینک ویدیو (یوتیوب یا MP4 مستقیم)",
310
- placeholder="https://www.youtube.com/watch?v=... یا https://example.com/video.mp4"
311
  ),
312
  gr.Dropdown(
313
- choices=[
314
- ("پارسی", "fa-IR"),
315
- ("انگلیسی", "en-US"),
316
- ],
317
  value="fa-IR",
318
- label="زبان متن"
319
- ),
320
- gr.Checkbox(
321
- label="استفاده از حافظه cache",
322
- value=True,
323
- visible=False # مخفی کنیم چون همیشه فعال است
324
  )
325
  ],
326
  outputs=[
327
  gr.File(label="دانلود MP3"),
328
- gr.Textbox(label="متن استخراج‌شده (Google STT)", lines=10),
329
- gr.Textbox(label="پیام وضعیت"),
330
- gr.Textbox(label="Cache Key (برای دریافت مجدد نتیجه)", visible=True)
331
  ],
332
- title="تبدیل ویدیو به MP3 و استخراج متن (Google STT) - با Cache",
333
- description="لینک ویدیو را وارد کنید و زبان را انتخاب کنید. نتایج برای 24 ساعت ذخیره می‌شوند.",
334
  examples=[
335
  ["https://www.youtube.com/watch?v=5qap5aO4i9A", "fa-IR"],
336
  ["https://www.youtube.com/watch?v=dQw4w9WgXcQ", "en-US"]
337
- ],
338
- allow_flagging="never",
339
- cache_examples=False
340
  )
341
 
342
- # اضافه کردن API route برای دسترسی به cache
343
- with gr.Blocks() as demo:
344
- iface.render()
345
-
346
- # API endpoint مخفی
347
- @gr.route("/api/get_result/{cache_key}")
348
- def api_get_result(cache_key):
349
- return get_cached_result(cache_key)
350
 
351
  if __name__ == "__main__":
352
- demo.launch(server_name="0.0.0.0", server_port=7860, share=False)
 
 
11
  from datetime import datetime, timedelta
12
  import threading
13
  import hashlib
14
+ from fastapi import FastAPI
15
+ from fastapi.responses import JSONResponse
16
 
17
  warnings.filterwarnings("ignore")
18
 
19
+ # ذخیره‌سازی نتایج
20
  results_cache = {}
21
  cache_lock = threading.Lock()
22
 
23
+ # FastAPI instance برای API endpoints
24
+ app = FastAPI()
25
+
26
+ # تابع برای پاکسازی cache قدیمی
27
  def cleanup_old_cache():
28
  while True:
29
+ time.sleep(3600) # هر ساعت
30
  with cache_lock:
31
  current_time = datetime.now()
32
  keys_to_remove = []
 
38
  if keys_to_remove:
39
  print(f"[CACHE] {len(keys_to_remove)} نتیجه قدیمی پاک شد.")
40
 
 
41
  cleanup_thread = threading.Thread(target=cleanup_old_cache, daemon=True)
42
  cleanup_thread.start()
43
 
44
  def get_cache_key(video_url, language):
45
+ """ایجاد کلید یکتا برای cache"""
46
  return hashlib.md5(f"{video_url}_{language}".encode()).hexdigest()
47
 
48
+ def save_result_to_cache(cache_key, video_url, language, mp3_path, text, status_msg, processing=False):
49
  """ذخیره نتیجه در cache"""
 
 
50
  # کپی فایل MP3 به مکان دائمی
51
+ cache_mp3_path = None
52
  if mp3_path and os.path.exists(mp3_path):
53
  cache_mp3_path = f"cache_{cache_key}.mp3"
54
  shutil.copy2(mp3_path, cache_mp3_path)
 
 
55
 
56
  with cache_lock:
57
  results_cache[cache_key] = {
 
60
  'text': text,
61
  'status_msg': status_msg,
62
  'video_url': video_url,
63
+ 'language': language,
64
+ 'processing': processing,
65
+ 'completed': not processing and (text is not None or status_msg.startswith("خطا"))
66
  }
67
 
68
+ print(f"[CACHE] نتیجه ذخیره شد: {cache_key} (processing: {processing})")
 
69
 
70
  def get_result_from_cache(cache_key):
71
  """دریافت نتیجه از cache"""
 
73
  if cache_key in results_cache:
74
  result = results_cache[cache_key]
75
  print(f"[CACHE] نتیجه یافت شد: {cache_key}")
76
+ return result
77
+ return None
78
 
79
+ def convert_to_mp3_and_transcribe_wrapper(video_url, language):
80
+ """Wrapper function که cache_key را در ابتدا برمی‌گرداند"""
 
 
81
  if not video_url:
82
  return None, None, "لینک ویدیو را وارد کنید.", None
83
 
84
+ # ایجاد cache key
85
  cache_key = get_cache_key(video_url, language)
 
 
 
 
86
 
87
+ # چک کردن cache
88
+ cached_result = get_result_from_cache(cache_key)
89
+ if cached_result and cached_result['completed']:
90
+ return (cached_result['mp3_path'],
91
+ cached_result['text'],
92
+ f"[از حافظه] {cached_result['status_msg']}",
93
+ cache_key)
94
+
95
+ # اگر در حال پردازش است
96
+ if cached_result and cached_result['processing']:
97
+ return None, None, "در حال پردازش... لطفاً صبر کنید.", cache_key
98
+
99
+ # ثبت شروع پردازش
100
+ save_result_to_cache(cache_key, video_url, language, None, None, "در حال پردازش...", processing=True)
101
+
102
+ # پردازش در background
103
+ def process_async():
104
+ try:
105
+ mp3_path, text, status_msg = convert_to_mp3_and_transcribe(video_url, language)
106
+ save_result_to_cache(cache_key, video_url, language, mp3_path, text, status_msg, processing=False)
107
+ except Exception as e:
108
+ save_result_to_cache(cache_key, video_url, language, None, None, f"خطا: {str(e)}", processing=False)
109
+
110
+ # شروع پردازش (برای سادگی، همینجا انجام می‌ده��م - در production باید async باشد)
111
+ mp3_path, text, status_msg = convert_to_mp3_and_transcribe(video_url, language)
112
+ save_result_to_cache(cache_key, video_url, language, mp3_path, text, status_msg, processing=False)
113
+
114
+ return mp3_path, text, status_msg, cache_key
115
+
116
+ def convert_to_mp3_and_transcribe(video_url, language):
117
+ """تابع اصلی پردازش (بدون cache)"""
118
  progress = gr.Progress(track_tqdm=False)
119
 
120
  try:
121
  print(f"[DEBUG] شروع پردازش لینک: {video_url} (زبان: {language})")
122
+
123
  progress(0, desc="شروع دانلود...")
124
 
125
  # دانلود و تبدیل به MP3
 
147
  break
148
 
149
  if not mp3_file:
150
+ return None, None, "خطا در دانلود یا تبدیل."
 
 
 
151
 
152
  progress(0.3, desc="دانلود کامل. کپی MP3...")
153
 
 
158
  if os.path.exists(mp3_file):
159
  os.remove(mp3_file)
160
 
161
+ progress(0.5, desc="MP3 آماده. شروع استخراج متن...")
 
 
162
 
163
  # استخراج متن
164
  text, error_msg = transcribe_audio(temp_mp3.name, progress, language)
165
 
166
  if text is None:
167
+ return temp_mp3.name, None, f"MP3 آماده، اما استخراج متن fail شد. {error_msg}"
 
 
 
 
168
 
169
+ progress(1.0, desc="کامل شد!")
170
 
171
+ return temp_mp3.name, text, f"موفق! {len(text.split())} کلمه استخراج شد."
 
 
 
172
 
173
  except Exception as e:
174
  print(f"[ERROR] خطای کلی: {str(e)}")
175
+ return None, None, f"خطا: {str(e)}"
 
 
176
 
177
  def language_display(lang_code):
178
  """نمایش نام زبان"""
179
+ return "پارسی" if lang_code == 'fa-IR' else "انگلیسی" if lang_code == 'en-US' else lang_code
 
 
 
 
 
180
 
181
  def transcribe_audio(mp3_path, progress, language, chunk_length_ms=60000, overlap_ms=5000):
182
+ """استخراج متن - کد قبلی شما"""
 
 
183
  recognizer = sr.Recognizer()
184
  recognizer.energy_threshold = 300
185
  recognizer.dynamic_energy_threshold = True
 
191
  temp_wav_dir = tempfile.mkdtemp()
192
  audio = AudioSegment.from_mp3(mp3_path)
193
  duration_ms = len(audio)
194
+
195
  if duration_ms == 0:
196
+ return None, "فایل صوتی خالی"
 
197
 
198
  step_size = chunk_length_ms - overlap_ms
199
  if step_size <= 0:
200
  step_size = chunk_length_ms // 2
201
  num_chunks = max(1, (duration_ms // step_size) + 1)
202
 
203
+ print(f"[DEBUG] مدت: {duration_ms/1000:.1f}s, chunks: {num_chunks}")
 
 
204
 
205
  i = 0
206
  chunk_idx = 1
 
209
  chunk = audio[i:end_pos]
210
 
211
  if len(chunk) < 3000:
 
212
  break
213
 
214
  temp_wav = os.path.join(temp_wav_dir, f"chunk_{i}.wav")
215
 
216
  try:
217
  chunk.export(temp_wav, format="wav")
 
218
 
219
  text_chunk = None
220
  retry_count = 0
 
222
 
223
  while retry_count < max_retries:
224
  try:
225
+ progress(0.5 + (i / duration_ms) * 0.5, desc=f"Chunk {chunk_idx}/{num_chunks}")
226
 
227
  with sr.AudioFile(temp_wav) as source:
228
  recognizer.adjust_for_ambient_noise(source, duration=0.5)
229
+ audio_data = recognizer.record(source)
230
 
231
  text = recognizer.recognize_google(audio_data, language=language)
232
+ text_chunk = text if text.strip() else "[سکوت]"
233
+ break
 
 
 
 
 
 
234
 
235
  except sr.UnknownValueError:
236
  text_chunk = "[نامشخص]"
 
237
  break
238
 
239
  except sr.RequestError as e:
240
  retry_count += 1
 
241
  if retry_count < max_retries:
242
  time.sleep(2)
243
  else:
244
+ text_chunk = f"[خطا: {str(e)[:20]}]"
245
  bad_chunks += 1
 
246
  break
247
 
248
  if text_chunk:
 
252
 
253
  total_chunks += 1
254
 
255
+ except Exception as e:
256
+ print(f"[ERROR] Chunk {chunk_idx}: {str(e)}")
 
 
257
  bad_chunks += 1
258
  total_chunks += 1
259
 
 
266
  shutil.rmtree(temp_wav_dir, ignore_errors=True)
267
 
268
  final_text = ' '.join(full_text).strip()
 
269
 
270
  if not final_text:
271
+ return None, "هیچ متنی استخراج نشد"
 
272
 
273
  bad_ratio = bad_chunks / total_chunks if total_chunks > 0 else 1
274
  if bad_ratio > 0.7:
275
+ return None, f"بیش از {bad_ratio*100:.0f}% خطا"
 
 
 
276
 
277
+ return final_text, ""
278
 
279
+ # API endpoint
280
+ @app.get("/api/check_result/{cache_key}")
281
+ async def check_result_api(cache_key: str):
282
+ """API endpoint برای بررسی وضعیت نتیجه"""
283
+ result = get_result_from_cache(cache_key)
284
+
285
+ if not result:
286
+ return JSONResponse({"status": 0, "message": "Not found"})
287
+
288
+ if result['processing']:
289
+ return JSONResponse({"status": 0, "message": "Processing"})
290
+
291
+ if result['completed']:
292
+ return JSONResponse({
293
+ "status": 1,
294
+ "text": result['text'],
295
+ "mp3_available": result['mp3_path'] is not None,
296
+ "status_message": result['status_msg']
297
+ })
298
+
299
+ return JSONResponse({"status": 0, "message": "Unknown status"})
300
 
301
+ # رابط Gradio
302
  iface = gr.Interface(
303
+ fn=convert_to_mp3_and_transcribe_wrapper,
304
  inputs=[
305
  gr.Textbox(
306
+ label="لینک ویدیو",
307
+ placeholder="https://www.youtube.com/watch?v=..."
308
  ),
309
  gr.Dropdown(
310
+ choices=[("پارسی", "fa-IR"), ("انگلیسی", "en-US")],
 
 
 
311
  value="fa-IR",
312
+ label="زبان"
 
 
 
 
 
313
  )
314
  ],
315
  outputs=[
316
  gr.File(label="دانلود MP3"),
317
+ gr.Textbox(label="متن استخراج‌شده", lines=10),
318
+ gr.Textbox(label="وضعیت"),
319
+ gr.Textbox(label="Cache Key", visible=True)
320
  ],
321
+ title="تبدیل ویدیو به MP3 و متن",
 
322
  examples=[
323
  ["https://www.youtube.com/watch?v=5qap5aO4i9A", "fa-IR"],
324
  ["https://www.youtube.com/watch?v=dQw4w9WgXcQ", "en-US"]
325
+ ]
 
 
326
  )
327
 
328
+ # Mount FastAPI to Gradio
329
+ from gradio import mount_gradio_app
330
+ mount_gradio_app(app, iface, path="/")
 
 
 
 
 
331
 
332
  if __name__ == "__main__":
333
+ import uvicorn
334
+ uvicorn.run(app, host="0.0.0.0", port=7860)