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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +40 -79
app.py CHANGED
@@ -2,24 +2,19 @@ import gradio as gr
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,14 +34,17 @@ def get_audio_from_input(input_source):
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,7 +53,10 @@ def get_audio_from_input(input_source):
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,15 +99,14 @@ def tts_and_merge(text_input):
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,87 +116,47 @@ def tts_and_merge(text_input):
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,7 +184,7 @@ with gr.Blocks() as demo:
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,6 +192,8 @@ with gr.Blocks() as demo:
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,6 +227,5 @@ with gr.Blocks() as demo:
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)
 
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
  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
  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
  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
  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
  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
  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
 
228
 
229
  if __name__ == "__main__":
230
+ demo.launch() # برای اجرا در لوکال
231
+ # demo.launch(share=True) # برای اشتراک‌گذاری موقت در یک لینک عمومی (برای هوش مصنوعی)