suprimedev commited on
Commit
ad2dfc2
·
verified ·
1 Parent(s): 1c4f949

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +79 -41
app.py CHANGED
@@ -2,19 +2,24 @@ import gradio as gr
2
  from pydub import AudioSegment
3
  import requests
4
  import os
5
- import uuid # برای تولید نام فایل منحصر به فرد
6
- import re # برای تجزیه متن
 
7
 
8
  # مسیر ذخیره فایل‌های موقت
9
  TEMP_DIR = "temp_audio"
10
  if not os.path.exists(TEMP_DIR):
11
  os.makedirs(TEMP_DIR)
12
 
 
 
 
 
13
  def download_file(url, output_path):
14
  """فایل را از یک URL دانلود می‌کند."""
15
  try:
16
  response = requests.get(url, stream=True)
17
- response.raise_for_status() # خطایی رخ داد، آن را بالا ببر
18
  with open(output_path, 'wb') as f:
19
  for chunk in response.iter_content(chunk_size=8192):
20
  f.write(chunk)
@@ -34,17 +39,14 @@ def get_audio_from_input(input_source):
34
  unique_filename = os.path.join(TEMP_DIR, str(uuid.uuid4()))
35
 
36
  if input_source.startswith("http://") or input_source.startswith("https://"):
37
- # این یک URL است، دانلودش کن
38
- # سعی می کنیم پسوند فایل را از URL تشخیص دهیم، در غیر این صورت از .mp3 استفاده می کنیم.
39
  file_extension = os.path.splitext(input_source.split('?')[0])[1]
40
- if not file_extension: # اگر پسوندی در URL نباشد (مثلا برای APIها)
41
  file_extension = ".mp3"
42
  temp_filepath = unique_filename + "_downloaded" + file_extension
43
  if not download_file(input_source, temp_filepath):
44
  return None, f"خطا در دانلود فایل از لینک: {input_source}"
45
  audio_path = temp_filepath
46
  else:
47
- # فرض می‌شود یک مسیر فایل محلی است
48
  audio_path = input_source
49
 
50
  try:
@@ -53,10 +55,7 @@ def get_audio_from_input(input_source):
53
  except Exception as e:
54
  return None, f"خطا در بارگذاری فایل صوتی ({audio_path}): {e}. مطمئن شوید فایل MP3 یا WAV معتبر است."
55
  finally:
56
- # اگر فایل از URL دانلود شده بود، آن را پاک کن
57
  if 'temp_filepath' in locals() and os.path.exists(temp_filepath):
58
- # اگر فایل بعداً توسط pydub پردازش و لود شده باشد، می‌توانیم آن را حذف کنیم.
59
- # در غیر این صورت، ممکن است در حال استفاده باشد.
60
  try:
61
  os.remove(temp_filepath)
62
  except OSError as e:
@@ -99,14 +98,15 @@ def tts_and_merge(text_input):
99
  if not text_input.strip():
100
  return None, "لطفاً متنی برای پردازش وارد کنید."
101
 
102
- # الگو برای تشخیص شماره و متن پرانتزی: (شماره)متن
103
- # این الگو همچنین newline را برای پردازش خط به خط در نظر می‌گیرد.
104
  lines = text_input.strip().split('\n')
105
 
106
  audio_urls_to_merge = []
107
  errors = []
 
 
 
108
 
109
- for line in lines:
110
  match = re.match(r'^\s*\((\d+)\)(.*)$', line)
111
  if match:
112
  speaker_number = match.group(1)
@@ -116,47 +116,87 @@ def tts_and_merge(text_input):
116
  errors.append(f"خطا: متن خالی برای گوینده {speaker_number} در خط '{line}'")
117
  continue
118
 
119
- # ساخت URL برای Talkbot API
120
- # متن باید URL-encoded شود، اما requests.get این کار را به طور خودکار برای پارامترها انجام می‌دهد.
121
- tts_url = f"https://talkbot.ir/api/TTS-S{speaker_number}?text={text_for_tts}"
122
 
123
- print(f"درخواست TTS برای گوینده {speaker_number}: {tts_url}") # برای debugging
124
 
125
  try:
126
- # درخواست به Talkbot API
127
- response = requests.get(tts_url)
128
- response.raise_for_status() # برای خطاهای HTTP
129
 
130
- # فرض بر این است که پاسخ مستقیم لینک MP3 است
131
- # اگر API لینک را در JSON برمی‌گرداند، باید آن را parse کنید.
132
- # در مثال شما، فرض می‌شود پاسخ مستقیم لینک MP3 است.
133
- audio_link = response.text.strip()
 
 
 
 
 
134
 
135
- if audio_link.startswith("http"): # مطمئن شوید لینک معتبر است
136
- audio_urls_to_merge.append(audio_link)
137
- else:
138
- errors.append(f"API برای گوینده {speaker_number} لینک معتبری برنگرداند: '{audio_link}'")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
 
140
  except requests.exceptions.RequestException as e:
141
- errors.append(f"خطا در ارتباط با Talkbot API برای گوینده {speaker_number}: {e}")
142
  except Exception as e:
143
- errors.append(f"خطای غیرمنتظره در پردازش Talkbot API برای گوینده {speaker_number}: {e}")
144
  else:
145
- if line.strip(): # اگر خط خالی نباشد، اما مطابق الگو نباشد
146
  errors.append(f"فرمت نامعتبر در خط: '{line}'. انتظار می‌رود (شماره)متن.")
147
 
148
-
149
  if not audio_urls_to_merge:
150
- return None, "هیچ فایل صوتی برای ادغام تولید نشد." + "\n".join(errors) if errors else ""
 
 
 
 
151
 
152
- # حالا لینک‌های تولید شده را به تابع merge_audio_files اصلی می‌فرستیم
153
  merged_output_path, merge_message = merge_audio_files(audio_urls_to_merge)
154
 
155
  final_message = merge_message
156
  if errors:
157
  final_message += "\n\nخطاهای رخ داده:\n" + "\n".join(errors)
158
 
159
- return merged_output_path, final_message
160
 
161
 
162
  # ایجاد رابط کاربری Gradio
@@ -184,7 +224,7 @@ with gr.Blocks() as demo:
184
  merge_button = gr.Button("ادغام فایل‌های صوتی")
185
 
186
  merge_button.click(
187
- fn=lambda x: merge_audio_files([s.strip() for s in x.split('\n') if s.strip()]), # تبدیل ورودی رشته‌ای به لیست برای تابع
188
  inputs=[audio_links_input],
189
  outputs=[audio_merge_output_audio, audio_merge_output_message]
190
  )
@@ -192,8 +232,6 @@ with gr.Blocks() as demo:
192
  gr.Examples(
193
  examples=[
194
  ["https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3\nhttps://www.soundhelix.com/examples/mp3/SoundHelix-Song-2.mp3"],
195
- # اگر فایل‌های واقعی داشته باشید، می‌توانید این خط را فعال کنید:
196
- # ["./path/to/your/local_audio.mp3\n./path/to/another/local_audio.wav"]
197
  ],
198
  inputs=audio_links_input,
199
  label="نمونه‌ها"
@@ -227,6 +265,6 @@ with gr.Blocks() as demo:
227
 
228
 
229
  if __name__ == "__main__":
230
- demo.launch() # برای اجرا در لوکال
231
- # demo.launch(share=True) # برای اشتراک‌گذاری موقت در یک لینک عمومی (برای هوش مصنوعی)
232
-
 
2
  from pydub import AudioSegment
3
  import requests
4
  import os
5
+ import uuid
6
+ import re
7
+ import time # برای sleep در Polling
8
 
9
  # مسیر ذخیره فایل‌های موقت
10
  TEMP_DIR = "temp_audio"
11
  if not os.path.exists(TEMP_DIR):
12
  os.makedirs(TEMP_DIR)
13
 
14
+ # تنظیمات Polling
15
+ POLLING_INTERVAL_SECONDS = 2
16
+ MAX_POLLING_ATTEMPTS = 30 # مثلاً 30 * 2 = 60 ثانیه صبر می‌کنیم
17
+
18
  def download_file(url, output_path):
19
  """فایل را از یک URL دانلود می‌کند."""
20
  try:
21
  response = requests.get(url, stream=True)
22
+ response.raise_for_status()
23
  with open(output_path, 'wb') as f:
24
  for chunk in response.iter_content(chunk_size=8192):
25
  f.write(chunk)
 
39
  unique_filename = os.path.join(TEMP_DIR, str(uuid.uuid4()))
40
 
41
  if input_source.startswith("http://") or input_source.startswith("https://"):
 
 
42
  file_extension = os.path.splitext(input_source.split('?')[0])[1]
43
+ if not file_extension:
44
  file_extension = ".mp3"
45
  temp_filepath = unique_filename + "_downloaded" + file_extension
46
  if not download_file(input_source, temp_filepath):
47
  return None, f"خطا در دانلود فایل از لینک: {input_source}"
48
  audio_path = temp_filepath
49
  else:
 
50
  audio_path = input_source
51
 
52
  try:
 
55
  except Exception as e:
56
  return None, f"خطا در بارگذاری فایل صوتی ({audio_path}): {e}. مطمئن شوید فایل MP3 یا WAV معتبر است."
57
  finally:
 
58
  if 'temp_filepath' in locals() and os.path.exists(temp_filepath):
 
 
59
  try:
60
  os.remove(temp_filepath)
61
  except OSError as e:
 
98
  if not text_input.strip():
99
  return None, "لطفاً متنی برای پردازش وارد کنید."
100
 
 
 
101
  lines = text_input.strip().split('\n')
102
 
103
  audio_urls_to_merge = []
104
  errors = []
105
+
106
+ # برای Gradio که بتواند در حین Polling وضعیت را نشان دهد
107
+ yield None, "در حال شروع پردازش TTS..."
108
 
109
+ for line_idx, line in enumerate(lines):
110
  match = re.match(r'^\s*\((\d+)\)(.*)$', line)
111
  if match:
112
  speaker_number = match.group(1)
 
116
  errors.append(f"خطا: متن خالی برای گوینده {speaker_number} در خط '{line}'")
117
  continue
118
 
119
+ # فرض: این Endpoint برای درخواست TTS و دریافت event_id است
120
+ # (ممکن است نیاز به تغییر به POST و body JSON داشته باشد)
121
+ tts_request_url = f"https://talkbot.ir/api/TTS-S{speaker_number}/request?text={requests.utils.quote(text_for_tts)}" # URL Encode text
122
 
123
+ yield None, f"در حال تولید صدا برای خط {line_idx+1} (گوینده {speaker_number})..."
124
 
125
  try:
126
+ # مرحله 1: ارسال درخواست TTS و دریافت event_id
127
+ response = requests.get(tts_request_url) # یا requests.post(...)
128
+ response.raise_for_status()
129
 
130
+ # فرض: پاسخ یک JSON با event_id است
131
+ response_data = response.json()
132
+ event_id = response_data.get("event_id")
133
+
134
+ if not event_id:
135
+ errors.append(f"API برای گوینده {speaker_number} در خط {line_idx+1} یک event_id معتبر برنگرداند: {response.text}")
136
+ continue
137
+
138
+ print(f"درخواست TTS برای {speaker_number} (خط {line_idx+1}) ارسال شد، Event ID: {event_id}")
139
 
140
+ # مرحله 2: Polling برای وضعیت
141
+ audio_link = None
142
+ polling_attempts = 0
143
+ while polling_attempts < MAX_POLLING_ATTEMPTS:
144
+ polling_attempts += 1
145
+ status_url = f"https://talkbot.ir/api/TTS-S{speaker_number}/status/{event_id}" # یا یک Endpoint کلی تر مثل /api/tts/status/{event_id}
146
+
147
+ yield None, f"خط {line_idx+1} (گوینده {speaker_number}): در حال بررسی وضعیت (تلاش {polling_attempts}/{MAX_POLLING_ATTEMPTS})..."
148
+
149
+ status_response = requests.get(status_url)
150
+ status_response.raise_for_status()
151
+ status_data = status_response.json()
152
+
153
+ status = status_data.get("status")
154
+
155
+ if status == "completed":
156
+ audio_link = status_data.get("audio_url")
157
+ if audio_link and audio_link.startswith("http"):
158
+ audio_urls_to_merge.append(audio_link)
159
+ print(f"صدا برای {speaker_number} (خط {line_idx+1}) آماده شد: {audio_link}")
160
+ break # از حلقه Polling خارج می‌شویم
161
+ else:
162
+ errors.append(f"لینک صوتی نامعتبر در پاسخ وضعیت برای {speaker_number} (خط {line_idx+1}): {status_data}")
163
+ break
164
+ elif status == "failed":
165
+ error_msg = status_data.get("error", "خطای ناشناخته از API.")
166
+ errors.append(f"تولید صدا برای گوینده {speaker_number} (خط {line_idx+1}) با خطا مواجه شد: {error_msg}")
167
+ break
168
+ elif status == "processing":
169
+ time.sleep(POLLING_INTERVAL_SECONDS)
170
+ else:
171
+ errors.append(f"وضعیت ناشناخته از API برای {speaker_number} (خط {line_idx+1}): {status_data}")
172
+ break
173
+
174
+ if not audio_link: # اگر Polling به پایان رسید و لینکی دریافت نشد
175
+ errors.append(f"تولید صدا برای گوینده {speaker_number} (خط {line_idx+1}) در زمان تعیین شده به اتمام نرسید یا لینکی دریافت نشد.")
176
 
177
  except requests.exceptions.RequestException as e:
178
+ errors.append(f"خطا در ارتباط با Talkbot API برای گوینده {speaker_number} (خط {line_idx+1}): {e}")
179
  except Exception as e:
180
+ errors.append(f"خطای غیرمنتظره در پردازش Talkbot API برای گوینده {speaker_number} (خط {line_idx+1}): {e}")
181
  else:
182
+ if line.strip():
183
  errors.append(f"فرمت نامعتبر در خط: '{line}'. انتظار می‌رود (شماره)متن.")
184
 
 
185
  if not audio_urls_to_merge:
186
+ final_message = "هیچ فایل صوتی برای ادغام تولید نشد."
187
+ if errors:
188
+ final_message += "\n\nخطاهای رخ داده:\n" + "\n".join(errors)
189
+ yield None, final_message
190
+ return
191
 
192
+ yield None, "در حال ادغام فایل‌های صوتی..."
193
  merged_output_path, merge_message = merge_audio_files(audio_urls_to_merge)
194
 
195
  final_message = merge_message
196
  if errors:
197
  final_message += "\n\nخطاهای رخ داده:\n" + "\n".join(errors)
198
 
199
+ yield merged_output_path, final_message
200
 
201
 
202
  # ایجاد رابط کاربری Gradio
 
224
  merge_button = gr.Button("ادغام فایل‌های صوتی")
225
 
226
  merge_button.click(
227
+ fn=lambda x: merge_audio_files([s.strip() for s in x.split('\n') if s.strip()]),
228
  inputs=[audio_links_input],
229
  outputs=[audio_merge_output_audio, audio_merge_output_message]
230
  )
 
232
  gr.Examples(
233
  examples=[
234
  ["https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3\nhttps://www.soundhelix.com/examples/mp3/SoundHelix-Song-2.mp3"],
 
 
235
  ],
236
  inputs=audio_links_input,
237
  label="نمونه‌ها"
 
265
 
266
 
267
  if __name__ == "__main__":
268
+ demo.launch(debug=True, show_api=False, inline=False, share=True)
269
+ # برای اینکه Gradio بتواند خروجی‌های میانی (yield) را مدیریت کند، نیاز به queue دارید.
270
+ # demo.launch(share=True, enable_queue=True)