Hamed744 commited on
Commit
b30822c
·
verified ·
1 Parent(s): fb10263

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +70 -52
app.py CHANGED
@@ -9,18 +9,19 @@ import re
9
  import struct
10
  import time
11
  import zipfile
12
- import google.generativeai as genai # اصلاح شد: این خط اکنون صحیح است
13
- from google.generativeai import types # این خط از قبل هم صحیح بود
14
  import threading
15
  import logging
16
- import io # اضافه شد: برای عملیات بایت در حافظه
 
17
 
18
  try:
19
  from pydub import AudioSegment
20
  PYDUB_AVAILABLE = True
21
  except ImportError:
22
  PYDUB_AVAILABLE = False
23
- logging.warning("⚠️ pydub نصب نشده است. قابلیت ادغام فایل‌های صوتی غیرفعال خواهد بود.")
24
 
25
  # --- START: پیکربندی لاگینگ ---
26
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
@@ -126,32 +127,30 @@ def smart_text_split(text, max_size=3800):
126
  final_chunks = [c for c in chunks if c]
127
  return final_chunks
128
 
129
- def merge_audio_bytes(audio_data_list: list[bytes], original_mime_type: str) -> io.BytesIO | None:
130
  """
131
- لیستی از بایت‌های صوتی را ادغام کرده و یک شیء io.BytesIO حاوی فایل WAV نهایی برمی‌گرداند.
132
  """
133
  if not PYDUB_AVAILABLE:
134
- logging.warning("⚠️ pydub برای ادغام در دسترس نیست. ادغام انجام نخواهد شد.")
135
- if audio_data_list:
136
- return io.BytesIO(audio_data_list[0])
137
- return None
138
-
139
  try:
140
- combined = AudioSegment.empty()
141
  for i, audio_bytes in enumerate(audio_data_list):
142
  audio_segment = AudioSegment.from_file(io.BytesIO(audio_bytes), format="wav")
143
- combined += audio_segment
144
  if i < len(audio_data_list) - 1:
145
- combined += AudioSegment.silent(duration=150) # 150 میلی‌ثانیه سکوت
146
 
147
- output_buffer = io.BytesIO()
148
- combined.export(output_buffer, format="wav")
149
- output_buffer.seek(0)
150
- return output_buffer
 
 
151
  except Exception as e:
152
- logging.error(f"❌ خطا در ادغام بایت‌های صوتی: {e}")
153
- if audio_data_list:
154
- return io.BytesIO(audio_data_list[0])
155
  return None
156
 
157
  # --- START: منطق تولید صدا با قابلیت تلاش مجدد با کلیدهای چرخشی ---
@@ -186,7 +185,12 @@ def generate_audio_chunk_with_retry(chunk_text, prompt_text, voice, temp):
186
 
187
  if response.candidates and response.candidates[0].content and response.candidates[0].content.parts and response.candidates[0].content.parts[0].inline_data:
188
  logging.info(f"✅ قطعه با موفقیت توسط کلید شماره {key_idx_display} تولید شد.")
189
- return response.candidates[0].content.parts[0].inline_data
 
 
 
 
 
190
  else:
191
  logging.warning(f"⚠️ پاسخ API برای قطعه با کلید شماره {key_idx_display} بدون داده صوتی بود. تلاش با کلید بعدی...")
192
 
@@ -210,22 +214,16 @@ def core_generate_audio(text_input, prompt_input, selected_voice, temperature_va
210
  logging.error("❌ متن قابل پردازش به قطعات کوچکتر نیست.")
211
  return None
212
 
213
- generated_audio_data_list = []
214
- last_mime_type = None
215
 
216
  for i, chunk in enumerate(text_chunks):
217
  logging.info(f"🔊 پردازش قطعه {i+1}/{len(text_chunks)}...")
218
 
219
- inline_data = generate_audio_chunk_with_retry(chunk, prompt_input, selected_voice, temperature_val)
220
-
221
- if inline_data:
222
- data_buffer = inline_data.data
223
- last_mime_type = inline_data.mime_type
224
-
225
- if "audio/L" in inline_data.mime_type:
226
- data_buffer = convert_to_wav(data_buffer, inline_data.mime_type)
227
-
228
- generated_audio_data_list.append(data_buffer)
229
  else:
230
  logging.error(f"🛑 فرآیند متوقف شد زیرا تولید قطعه {i+1} با تمام کلیدهای موجود ناموفق بود.")
231
  break
@@ -233,27 +231,46 @@ def core_generate_audio(text_input, prompt_input, selected_voice, temperature_va
233
  if i < len(text_chunks) - 1 and len(text_chunks) > 1:
234
  time.sleep(sleep_time)
235
 
236
- if not generated_audio_data_list:
237
  logging.error(f"❌ هیچ داده صوتی تولید نشد.")
238
  return None
239
 
240
- final_audio_bytes_io = None
241
 
242
- if len(generated_audio_data_list) > 1:
243
- logging.info("♻️ ادغام قطعات صوتی...")
244
- final_audio_bytes_io = merge_audio_bytes(generated_audio_data_list, last_mime_type)
245
- if final_audio_bytes_io:
246
- logging.info("✅ ادغام با موفقیت انجام شد.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  else:
248
- logging.warning("⚠️ ادغام ناموفق بود یا pydub در دسترس نیست. اولین قطعه بازگردانده می‌شود.")
249
- final_audio_bytes_io = io.BytesIO(generated_audio_data_list[0]) if generated_audio_data_list else None
250
- elif len(generated_audio_data_list) == 1:
251
- logging.info("✅ تنها یک قطعه صوتی تولید شد. نیازی به ادغام نیست.")
252
- final_audio_bytes_io = io.BytesIO(generated_audio_data_list[0])
253
 
254
- if final_audio_bytes_io:
255
  logging.info("✅ عملیات تولید صدا با موفقیت کامل شد.")
256
- return final_audio_bytes_io
257
  else:
258
  logging.error("❓ وضعیت نامشخص برای خروجی نهایی صدا.")
259
  return None
@@ -273,8 +290,9 @@ def gradio_tts_interface(use_file_input, uploaded_file, text_to_speak, speech_pr
273
  actual_text = text_to_speak
274
  if not actual_text or not actual_text.strip(): logging.warning("❌ متن ورودی برای تبدیل خالی است."); return None
275
 
276
- output_audio_data = core_generate_audio(actual_text, speech_prompt, speaker_voice, temperature)
277
- return output_audio_data
 
278
 
279
  # --- تابع جدید برای ریست خودکار هر 24 ساعت ---
280
  def auto_restart_service():
@@ -382,8 +400,8 @@ with gr.Blocks(theme=gr.themes.Base(font=[gr.themes.GoogleFont("Vazirmatn")]), c
382
 
383
  generate_button = gr.Button("🚀 تولید و پخش صدا", elem_classes=["generate-button-final"], elem_id="generate_button_alpha_v3")
384
 
385
- # مهم: type="bytes" را برای خروجی صوتی تنظیم کنید (نیاز به Gradio 4.0.0+ دارد)
386
- output_audio = gr.Audio(label=" ", type="bytes", elem_id="output_audio_player_alpha_v3")
387
 
388
  generate_button.click(
389
  fn=gradio_tts_interface,
 
9
  import struct
10
  import time
11
  import zipfile
12
+ import google.generativeai as genai
13
+ from google.generativeai import types
14
  import threading
15
  import logging
16
+ import io
17
+ import numpy as np # جدید: این خط را اضافه کنید
18
 
19
  try:
20
  from pydub import AudioSegment
21
  PYDUB_AVAILABLE = True
22
  except ImportError:
23
  PYDUB_AVAILABLE = False
24
+ logging.warning("⚠️ pydub نصب نشده است. قابلیت ادغام فایل‌های صوتی و تبدیل به NumPy غیرفعال خواهد بود.")
25
 
26
  # --- START: پیکربندی لاگینگ ---
27
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
 
127
  final_chunks = [c for c in chunks if c]
128
  return final_chunks
129
 
130
+ def merge_audio_bytes_to_numpy(audio_data_list: list[bytes]) -> tuple[int, np.ndarray] | None:
131
  """
132
+ لیستی از بایت‌های صوتی WAV را ادغام کرده و یک تاپل (sample_rate, numpy_array) برمی‌گرداند.
133
  """
134
  if not PYDUB_AVAILABLE:
135
+ logging.warning("⚠️ pydub برای ادغام و تبدیل به NumPy در دسترس نیست.")
136
+ return None # در این حالت نمی‌توانیم خروجی NumPy بدهیم
137
+
 
 
138
  try:
139
+ combined_audio_segment = AudioSegment.empty()
140
  for i, audio_bytes in enumerate(audio_data_list):
141
  audio_segment = AudioSegment.from_file(io.BytesIO(audio_bytes), format="wav")
142
+ combined_audio_segment += audio_segment
143
  if i < len(audio_data_list) - 1:
144
+ combined_audio_segment += AudioSegment.silent(duration=150) # 150 میلی‌ثانیه سکوت
145
 
146
+ # استخراج نرخ نمونه و داده‌های صوتی به عنوان آرایه NumPy
147
+ sample_rate = combined_audio_segment.frame_rate
148
+ # pydub به صورت پیش‌فرض داده‌ها را به int16 تبدیل می‌کند، مناسب برای NumPy
149
+ audio_array = np.array(combined_audio_segment.get_array_of_samples())
150
+
151
+ return (sample_rate, audio_array)
152
  except Exception as e:
153
+ logging.error(f"❌ خطا در ادغام بایت‌های صوتی و تبدیل به NumPy: {e}")
 
 
154
  return None
155
 
156
  # --- START: منطق تولید صدا با قابلیت تلاش مجدد با کلیدهای چرخشی ---
 
185
 
186
  if response.candidates and response.candidates[0].content and response.candidates[0].content.parts and response.candidates[0].content.parts[0].inline_data:
187
  logging.info(f"✅ قطعه با موفقیت توسط کلید شماره {key_idx_display} تولید شد.")
188
+ # همیشه داده را به صورت بایت WAV برمی‌گرداند.
189
+ data_buffer = response.candidates[0].content.parts[0].inline_data.data
190
+ mime_type = response.candidates[0].content.parts[0].inline_data.mime_type
191
+ if "audio/L" in mime_type:
192
+ data_buffer = convert_to_wav(data_buffer, mime_type)
193
+ return data_buffer
194
  else:
195
  logging.warning(f"⚠️ پاسخ API برای قطعه با کلید شماره {key_idx_display} بدون داده صوتی بود. تلاش با کلید بعدی...")
196
 
 
214
  logging.error("❌ متن قابل پردازش به قطعات کوچکتر نیست.")
215
  return None
216
 
217
+ generated_wav_bytes_list = [] # لیست حاوی داده‌های صوتی هر قطعه (بایت WAV)
 
218
 
219
  for i, chunk in enumerate(text_chunks):
220
  logging.info(f"🔊 پردازش قطعه {i+1}/{len(text_chunks)}...")
221
 
222
+ # generate_audio_chunk_with_retry اکنون مستقیماً بایت‌های WAV را برمی‌گرداند
223
+ wav_data_for_chunk = generate_audio_chunk_with_retry(chunk, prompt_input, selected_voice, temperature_val)
224
+
225
+ if wav_data_for_chunk:
226
+ generated_wav_bytes_list.append(wav_data_for_for_chunk)
 
 
 
 
 
227
  else:
228
  logging.error(f"🛑 فرآیند متوقف شد زیرا تولید قطعه {i+1} با تمام کلیدهای موجود ناموفق بود.")
229
  break
 
231
  if i < len(text_chunks) - 1 and len(text_chunks) > 1:
232
  time.sleep(sleep_time)
233
 
234
+ if not generated_wav_bytes_list:
235
  logging.error(f"❌ هیچ داده صوتی تولید نشد.")
236
  return None
237
 
238
+ final_audio_output = None
239
 
240
+ if len(generated_wav_bytes_list) > 1:
241
+ logging.info("♻️ ادغام قطعات صوتی و تبدیل به NumPy...")
242
+ final_audio_output = merge_audio_bytes_to_numpy(generated_wav_bytes_list)
243
+ if final_audio_output:
244
+ logging.info("✅ ادغام و تبدیل به NumPy با موفقیت انجام شد.")
245
+ else:
246
+ logging.warning("⚠️ ادغام ناموفق بود یا pydub در دسترس نیست. تلاش برای بازگرداندن اولین قطعه به عنوان NumPy...")
247
+ if generated_wav_bytes_list and PYDUB_AVAILABLE:
248
+ try:
249
+ # اگر ادغام به مشکل خورد، سعی می‌کنیم حداقل اولین قطعه را به NumPy تبدیل کنیم
250
+ single_audio_segment = AudioSegment.from_file(io.BytesIO(generated_wav_bytes_list[0]), format="wav")
251
+ final_audio_output = (single_audio_segment.frame_rate, np.array(single_audio_segment.get_array_of_samples()))
252
+ except Exception as e:
253
+ logging.error(f"❌ خطا در تبدیل اولین قطعه به NumPy: {e}")
254
+ return None
255
+ else:
256
+ return None # هیچ راهی برای بازگرداندن NumPy بدون pydub/داده وجود ندارد
257
+
258
+ elif len(generated_wav_bytes_list) == 1:
259
+ logging.info("✅ تنها یک قطعه صوتی تولید شد. تبدیل مستقیم به NumPy.")
260
+ if PYDUB_AVAILABLE:
261
+ try:
262
+ single_audio_segment = AudioSegment.from_file(io.BytesIO(generated_wav_bytes_list[0]), format="wav")
263
+ final_audio_output = (single_audio_segment.frame_rate, np.array(single_audio_segment.get_array_of_samples()))
264
+ except Exception as e:
265
+ logging.error(f"❌ خطا در تبدیل قطعه تکی به NumPy: {e}")
266
+ return None
267
  else:
268
+ logging.error(" pydub برای تبدیل قطعه تکی به NumPy در دسترس نیست.")
269
+ return None # نمی‌توانیم خروجی numpy بدهیم
 
 
 
270
 
271
+ if final_audio_output:
272
  logging.info("✅ عملیات تولید صدا با موفقیت کامل شد.")
273
+ return final_audio_output
274
  else:
275
  logging.error("❓ وضعیت نامشخص برای خروجی نهایی صدا.")
276
  return None
 
290
  actual_text = text_to_speak
291
  if not actual_text or not actual_text.strip(): logging.warning("❌ متن ورودی برای تبدیل خالی است."); return None
292
 
293
+ # core_generate_audio اکنون یک تاپل (sample_rate, numpy_array) برمی‌گرداند
294
+ output_audio_data_numpy = core_generate_audio(actual_text, speech_prompt, speaker_voice, temperature)
295
+ return output_audio_data_numpy
296
 
297
  # --- تابع جدید برای ریست خودکار هر 24 ساعت ---
298
  def auto_restart_service():
 
400
 
401
  generate_button = gr.Button("🚀 تولید و پخش صدا", elem_classes=["generate-button-final"], elem_id="generate_button_alpha_v3")
402
 
403
+ # مهم: type="numpy" را برای خروجی صوتی تنظیم کنید
404
+ output_audio = gr.Audio(label=" ", type="numpy", elem_id="output_audio_player_alpha_v3")
405
 
406
  generate_button.click(
407
  fn=gradio_tts_interface,