Opera8 commited on
Commit
53b65bd
·
verified ·
1 Parent(s): 774c7f2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +48 -38
app.py CHANGED
@@ -115,17 +115,18 @@ def get_user_ip():
115
  return request.headers.getlist("X-Forwarded-For")[0].split(',')[0].strip()
116
  return request.remote_addr
117
 
118
- # --- TTS HELPER FUNCTIONS (بدون حذف دیالوگ‌ها) ---
119
  def call_worker(index, chunk_payload):
120
  raw_text = chunk_payload.get("text", "")
121
- clean_text = raw_text.strip()
122
 
123
- # فقط اگر خود هوش مصنوعی متن خالی داده باشد (یعنی حرفی برای گفتن نباشد)، سکوت می‌گذاریم
 
 
 
 
124
  if not clean_text:
125
- logging.warning(f"⚠️ [قطعه {index+1}] متن کاملا خالی بود.")
126
  return index, AudioSegment.silent(duration=500)
127
 
128
- # زنده بودن مدل برای متون زیر 600 حرف
129
  use_live = True if len(clean_text) <= 600 else False
130
 
131
  target_speaker = chunk_payload.get("speaker")
@@ -141,7 +142,7 @@ def call_worker(index, chunk_payload):
141
  "fallback_to_live": True
142
  }
143
 
144
- # افزایش تلاش به 50 بار برای اطمینان از ساخته شدن قطعی
145
  max_attempts = 50
146
 
147
  for attempt in range(max_attempts):
@@ -150,7 +151,7 @@ def call_worker(index, chunk_payload):
150
 
151
  for worker_url in workers:
152
  try:
153
- logging.info(f"▶️ [قطعه {index+1}] ارسال درخواست به {worker_url} (تلاش {attempt+1}/{max_attempts})")
154
  response = requests.post(worker_url, json=worker_payload, headers=STANDARD_HEADERS, timeout=120)
155
 
156
  if response.status_code == 200:
@@ -164,16 +165,15 @@ def call_worker(index, chunk_payload):
164
  continue
165
 
166
  except Exception as e:
167
- logging.error(f"❌ [قطعه {index+1}] قطعی ارتباط با کارگر: {str(e)[:40]}")
168
  continue
169
 
170
  sleep_time = 3 + (attempt * 1.5) + random.uniform(0.5, 2.0)
171
- logging.warning(f"🔄 [قطعه {index+1}] تمام کارگرها شلوغ/خطا دادند. استراحت به مدت {sleep_time:.1f} ثانیه...")
172
  time.sleep(sleep_time)
173
 
174
- # اگر پس از 50 بار تلاش ساخته نشد، ارور برمی‌گرداند تا فرآیند به صورت اصولی متوقف شود
175
- logging.error(f"🚫 [قطعه {index+1}] پس از {max_attempts} بار تلاش ساخته نشد.")
176
- return index, None
177
 
178
  # --- AI PODCAST SCRIPT LOGIC (Cohere Labs Space) ---
179
  def generate_podcast_in_background(task_id, system_prompt, safety_settings):
@@ -246,7 +246,7 @@ def generate_podcast_in_background(task_id, system_prompt, safety_settings):
246
  except Exception as e:
247
  with tasks_lock: tasks[task_id].update({'status': 'failed', 'error': str(e)})
248
 
249
- # --- FULL AUTO PODCAST LOGIC ---
250
  def generate_full_podcast_audio_background(task_id, prompt, speakers):
251
  try:
252
  logging.info(f"🚀 [پروژه {task_id}] عملیات ساخت پادکست آغاز شد.")
@@ -255,20 +255,21 @@ def generate_full_podcast_audio_background(task_id, prompt, speakers):
255
 
256
  spk_text = "\n".join([f"- {s['id']}: {s['name']}" for s in speakers])
257
 
258
- # حفظ پرامپت قدرتمند برای کوتاه نوشتن و جلوگیری از ارور 500 در ریشه
259
  system_prompt = f"""Act as a Professional Podcast Producer.
260
  Topic: "{prompt}"
261
  Speakers Available:
262
  {spk_text}
263
 
264
- CRITICAL INSTRUCTIONS (MUST FOLLOW STRICTLY):
265
- 1. Create a VERY LONG, in-depth, and highly detailed podcast script.
266
- 2. STRICT RULE: Keep EVERY SINGLE dialogue line SHORT (Under 250 characters).
267
- 3. If a speaker has a lot to say, BREAK their speech into multiple separate, consecutive turns for that SAME speaker. Do NOT write long paragraphs.
268
- 4. NO stage directions, NO emojis, NO brackets like [laugh] or (sigh). Plain spoken text ONLY.
269
 
270
  Output ONLY valid JSON.
271
- Format: {{"selected_speakers": ["id1", "id2"], "script": [{{"speaker_id": "id1", "dialogue": "..."}}]}}"""
 
272
 
273
  AYA_SPACE_URL = "https://coherelabs-aya-expanse.hf.space/gradio_api"
274
  MAX_ATTEMPTS = 50
@@ -319,13 +320,14 @@ Format: {{"selected_speakers": ["id1", "id2"], "script": [{{"speaker_id": "id1",
319
  for t in data["script"]:
320
  if "dialogue" in t: t["dialogue"] = re.sub(r'\[.*?\]|\(.*?\)', '', t["dialogue"]).strip()
321
 
322
- script_turns = data["script"]
 
323
  total_turns = len(script_turns)
324
 
325
- logging.info(f"✅ سناریو با موفقیت استخراج شد! شامل {total_turns} نوبت کوتاه و استاندارد.")
326
 
327
  with tasks_lock:
328
- tasks[task_id] = {'status': 'generating_audio', 'progress': f'در حال تولید همزمان صداها (0 از {total_turns} تکمیل شده)'}
329
 
330
  audio_results = [None] * total_turns
331
  completed_count = 0
@@ -339,22 +341,29 @@ Format: {{"selected_speakers": ["id1", "id2"], "script": [{{"speaker_id": "id1",
339
  raise ValueError(f"خطا در تولید صدای نوبت {index+1}")
340
  return idx, audio_seg
341
 
342
- logging.info("⚡ شروع پروسه ساخت موازی (ارسال تمام قطعات به صورت همزمان)...")
343
- max_concurrent_workers = min(3, total_turns)
344
- with concurrent.futures.ThreadPoolExecutor(max_workers=max_concurrent_workers) as executor:
345
- future_to_index = {executor.submit(process_single_chunk, i, turn): i for i, turn in enumerate(script_turns)}
 
 
346
 
347
- for future in concurrent.futures.as_completed(future_to_index):
348
- try:
349
- idx, audio_seg = future.result()
350
- if audio_seg is not None:
 
 
 
351
  audio_results[idx] = audio_seg
352
- completed_count += 1
353
- with tasks_lock:
354
- tasks[task_id]['progress'] = f'در حال تولید صداها ({completed_count} از {total_turns} تکمیل شده)'
355
- except Exception as exc:
356
- logging.error(f"❌ خطای بحرانی در پردازش قطعه: {str(exc)}")
357
- raise Exception(str(exc))
 
 
358
 
359
  logging.info("✂️ تمام قطعات ساخته شد. در حال ادغام (میکس) فایل‌ها...")
360
  combined_audio = AudioSegment.empty()
@@ -492,7 +501,8 @@ def create_full_podcast():
492
 
493
  safety = [{"category": c, "threshold": "BLOCK_NONE"} for c in ["HARM_CATEGORY_HARASSMENT", "HARM_CATEGORY_HATE_SPEECH", "HARM_CATEGORY_SEXUALLY_EXPLICIT", "HARM_CATEGORY_DANGEROUS_CONTENT"]]
494
  spk_text = "\n".join([f"- {s['id']}: {s['name']}" for s in speakers])
495
- sys_prompt = f"""Act as a Professional Podcast Producer.\nTopic: "{prompt}"\nSpeakers Available:\n{spk_text}\nCRITICAL INSTRUCTION: You must create a VERY LONG, in-depth, and highly detailed podcast script.\n- Do NOT write a short summary.\n- The conversation must deeply explore the topic.\n- Make the dialogue engaging and informative.\n\nOutput ONLY valid JSON.\nFormat: {{"selected_speakers": ["id1", "id2"], "script": [{{"speaker_id": "id1", "dialogue": "..."}}]}}\nDialogue rules: No stage directions like [laugh], (sigh). Just spoken words."""
 
496
 
497
  threading.Thread(target=generate_podcast_in_background, args=(task_id, sys_prompt, safety)).start()
498
  return jsonify({"task_id": task_id}), 202
 
115
  return request.headers.getlist("X-Forwarded-For")[0].split(',')[0].strip()
116
  return request.remote_addr
117
 
118
+ # --- TTS HELPER FUNCTIONS (تمیزکننده و سازنده پرقدرت) ---
119
  def call_worker(index, chunk_payload):
120
  raw_text = chunk_payload.get("text", "")
 
121
 
122
+ # 🔴 پاکسازی متن از تمام کاراکترهای سمی ارک‌داون، براکت و ...)
123
+ clean_text = re.sub(r'[*_`~#]', '', raw_text)
124
+ clean_text = re.sub(r'\[.*?\]|\(.*?\)', '', clean_text)
125
+ clean_text = clean_text.strip()
126
+
127
  if not clean_text:
 
128
  return index, AudioSegment.silent(duration=500)
129
 
 
130
  use_live = True if len(clean_text) <= 600 else False
131
 
132
  target_speaker = chunk_payload.get("speaker")
 
142
  "fallback_to_live": True
143
  }
144
 
145
+ # تلاش مستمر و بدون جا انداختن حتی یک کلمه
146
  max_attempts = 50
147
 
148
  for attempt in range(max_attempts):
 
151
 
152
  for worker_url in workers:
153
  try:
154
+ logging.info(f"▶️ [قطعه {index+1}] ارسال به {worker_url} (تلاش {attempt+1}/{max_attempts})")
155
  response = requests.post(worker_url, json=worker_payload, headers=STANDARD_HEADERS, timeout=120)
156
 
157
  if response.status_code == 200:
 
165
  continue
166
 
167
  except Exception as e:
168
+ logging.error(f"❌ [قطعه {index+1}] قطعی ارتباط: {str(e)[:40]}")
169
  continue
170
 
171
  sleep_time = 3 + (attempt * 1.5) + random.uniform(0.5, 2.0)
172
+ logging.warning(f"🔄 [قطعه {index+1}] کارگرها شلوغ/خطا دادند. استراحت به مدت {sleep_time:.1f} ثانیه...")
173
  time.sleep(sleep_time)
174
 
175
+ # اگر بعد از 50 بار ساخته نشد، ارور صادر میکنه تا پادکست ناقص نمونه
176
+ raise ValueError(f"قطعه {index+1} پس از 50 بار تلاش ساخته نشد.")
 
177
 
178
  # --- AI PODCAST SCRIPT LOGIC (Cohere Labs Space) ---
179
  def generate_podcast_in_background(task_id, system_prompt, safety_settings):
 
246
  except Exception as e:
247
  with tasks_lock: tasks[task_id].update({'status': 'failed', 'error': str(e)})
248
 
249
+ # --- FULL AUTO PODCAST LOGIC (پردازش دسته‌ای 7تایی) ---
250
  def generate_full_podcast_audio_background(task_id, prompt, speakers):
251
  try:
252
  logging.info(f"🚀 [پروژه {task_id}] عملیات ساخت پادکست آغاز شد.")
 
255
 
256
  spk_text = "\n".join([f"- {s['id']}: {s['name']}" for s in speakers])
257
 
258
+ # 🔴 پرامپت دقیقاً منطبق با سایت شد + دستور اکید برای نوشتن فقط به زبان فارسی تا کارگر ارور نده
259
  system_prompt = f"""Act as a Professional Podcast Producer.
260
  Topic: "{prompt}"
261
  Speakers Available:
262
  {spk_text}
263
 
264
+ CRITICAL INSTRUCTION: You must create a VERY LONG, in-depth, and highly detailed podcast script.
265
+ - Do NOT write a short summary.
266
+ - The conversation must deeply explore the topic. Make it a very long and detailed conversation, as long as necessary to fully cover the topic.
267
+ - Make the dialogue engaging and informative.
268
+ - 🔴 IMPORTANT: Write the ENTIRE script ONLY in Persian (Farsi) language. Do NOT use English characters.
269
 
270
  Output ONLY valid JSON.
271
+ Format: {{"selected_speakers": ["id1", "id2"], "script": [{{"speaker_id": "id1", "dialogue": "..."}}]}}
272
+ Dialogue rules: No stage directions like [laugh], (sigh). Just spoken words."""
273
 
274
  AYA_SPACE_URL = "https://coherelabs-aya-expanse.hf.space/gradio_api"
275
  MAX_ATTEMPTS = 50
 
320
  for t in data["script"]:
321
  if "dialogue" in t: t["dialogue"] = re.sub(r'\[.*?\]|\(.*?\)', '', t["dialogue"]).strip()
322
 
323
+ # فقط نوبت‌هایی که متن دارن رو پردازش می‌کنیم
324
+ script_turns = [t for t in data.get("script", []) if str(t.get("dialogue", "")).strip()]
325
  total_turns = len(script_turns)
326
 
327
+ logging.info(f"✅ سناریو استخراج شد! شامل {total_turns} نوبت گفتگو.")
328
 
329
  with tasks_lock:
330
+ tasks[task_id] = {'status': 'generating_audio', 'progress': f'در حال تولید صداها (0 از {total_turns} تکمیل شده)'}
331
 
332
  audio_results = [None] * total_turns
333
  completed_count = 0
 
341
  raise ValueError(f"خطا در تولید صدای نوبت {index+1}")
342
  return idx, audio_seg
343
 
344
+ # 🌟 استراتژی طلایی دسته‌بندی (7 تا 7 تا) که پیشنهاد خودت بود 🌟
345
+ batch_size = 7
346
+ batches = [script_turns[i:i + batch_size] for i in range(0, total_turns, batch_size)]
347
+
348
+ for batch_index, batch in enumerate(batches):
349
+ logging.info(f"🔄 شروع پردازش دسته‌ی {batch_index+1} از {len(batches)} (شامل {len(batch)} دیالوگ)...")
350
 
351
+ with concurrent.futures.ThreadPoolExecutor(max_workers=len(batch)) as executor:
352
+ start_idx = batch_index * batch_size
353
+ future_to_index = {executor.submit(process_single_chunk, start_idx + i, turn): start_idx + i for i, turn in enumerate(batch)}
354
+
355
+ for future in concurrent.futures.as_completed(future_to_index):
356
+ try:
357
+ idx, audio_seg = future.result()
358
  audio_results[idx] = audio_seg
359
+ completed_count += 1
360
+ with tasks_lock:
361
+ tasks[task_id]['progress'] = f'در حال تولید صداها ({completed_count} از {total_turns} تکمیل شده)'
362
+ except Exception as exc:
363
+ logging.error(f"❌ خطای بحرانی: {str(exc)}")
364
+ raise Exception(str(exc))
365
+
366
+ logging.info(f"✔️ دسته‌ی {batch_index+1} با موفقیت تمام شد.")
367
 
368
  logging.info("✂️ تمام قطعات ساخته شد. در حال ادغام (میکس) فایل‌ها...")
369
  combined_audio = AudioSegment.empty()
 
501
 
502
  safety = [{"category": c, "threshold": "BLOCK_NONE"} for c in ["HARM_CATEGORY_HARASSMENT", "HARM_CATEGORY_HATE_SPEECH", "HARM_CATEGORY_SEXUALLY_EXPLICIT", "HARM_CATEGORY_DANGEROUS_CONTENT"]]
503
  spk_text = "\n".join([f"- {s['id']}: {s['name']}" for s in speakers])
504
+
505
+ sys_prompt = f"""Act as a Professional Podcast Producer.\nTopic: "{prompt}"\nSpeakers Available:\n{spk_text}\nCRITICAL INSTRUCTION: You must create a VERY LONG, in-depth, and highly detailed podcast script.\n- Do NOT write a short summary.\n- The conversation must deeply explore the topic.\n- Make the dialogue engaging and informative.\n- 🔴 IMPORTANT: Write the ENTIRE script ONLY in Persian (Farsi) language. Do NOT use English characters.\n\nOutput ONLY valid JSON.\nFormat: {{"selected_speakers": ["id1", "id2"], "script": [{{"speaker_id": "id1", "dialogue": "..."}}]}}\nDialogue rules: No stage directions like [laugh], (sigh). Just spoken words."""
506
 
507
  threading.Thread(target=generate_podcast_in_background, args=(task_id, sys_prompt, safety)).start()
508
  return jsonify({"task_id": task_id}), 202