194130157a commited on
Commit
c87dd6a
·
verified ·
1 Parent(s): 1d8c319

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +49 -18
app.py CHANGED
@@ -18,6 +18,8 @@ install_package("whisper", "openai-whisper")
18
  install_package("PIL", "Pillow")
19
  install_package("numpy")
20
  install_package("opencc", "opencc-python-reimplemented")
 
 
21
 
22
  # ==========================================
23
  # 1. 正常导入库
@@ -27,9 +29,7 @@ import re
27
  import time
28
  import json
29
  import requests
30
- import zipfile
31
  import queue
32
- import math
33
  import numpy as np
34
  from datetime import datetime
35
  from concurrent.futures import ThreadPoolExecutor, as_completed
@@ -59,9 +59,9 @@ VIDEOS_PER_BATCH = 10
59
 
60
  # 4. 系统配置
61
  BASE_OUTPUT_DIR = "project_thangka_final_pipeline"
62
- VIDEO_WORKERS = 150
63
- LLM_WORKERS = 5
64
- VIDEO_TIMEOUT_SECONDS = 360 # 🔥 修改点:单个视频超时设定为 6分钟
65
 
66
  # 5. 字幕配置
67
  FONT_URL = "https://github.com/googlefonts/noto-cjk/raw/main/Sans/OTF/TraditionalChinese/NotoSansCJKtc-Bold.otf"
@@ -327,10 +327,24 @@ class AudioAgent:
327
  time.sleep(5)
328
  return None
329
 
330
- # 🔥 VideoAgent (优化超时逻辑)
331
  class VideoAgent:
332
  def __init__(self):
333
- self.headers = {"Authorization": f"Bearer {YUNWU_API_KEY}"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
 
335
  def worker_task(self, prompt, global_video_idx, log_queue, save_dir):
336
  save_path = os.path.join(save_dir, f"clip_{global_video_idx+1:04d}.mp4")
@@ -340,21 +354,40 @@ class VideoAgent:
340
  final_prompt = (f"{FORCE_STYLE_PREFIX} {prompt} {FORCE_MOTION_BOOSTER} {FORCE_STYLE_SUFFIX} {FORCE_NEGATIVE_PROMPT}")
341
  final_prompt = re.sub(r'\s+', ' ', final_prompt).replace(", ,", ",").replace("..", ".")
342
 
343
- # 🔥 重试逻辑:失败直接重试
 
 
 
344
  for attempt in range(5):
345
  try:
346
- # 提交任务
347
  create_url = f"{MERCHANT_BASE_URL}/v1/videos"
 
 
348
  form_data = {
349
  "model": VEO_MODEL,
350
  "prompt": final_prompt,
351
- "seconds": VIDEO_DURATION_STR,
352
  "size": VIDEO_SIZE,
353
- "watermark": "false"
354
  }
355
 
356
- req = requests.post(create_url, headers=self.headers, data=form_data, timeout=60)
 
 
 
 
 
 
 
 
 
 
 
357
 
 
 
 
 
358
  if req.status_code != 200:
359
  try: err_msg = req.json()
360
  except: err_msg = req.text[:100]
@@ -374,10 +407,9 @@ class VideoAgent:
374
  # 轮询查询状态
375
  start_time = time.time()
376
  while True:
377
- # 🔥 核心修改1:严格的 6分钟超时
378
  if time.time() - start_time > VIDEO_TIMEOUT_SECONDS:
379
  log_queue.put(f"⚠️ [超时] 视频{global_video_idx+1} 耗时超过6分钟,视为失败,正在重试...")
380
- break # 跳出 while,进入下一次 for attempt 循环(即重新提交)
381
 
382
  query_url = f"{MERCHANT_BASE_URL}/v1/videos/{task_id}"
383
  status_resp = requests.get(query_url, headers=self.headers, timeout=30)
@@ -396,7 +428,6 @@ class VideoAgent:
396
  else:
397
  break
398
  elif status == "failed":
399
- # 捕获敏感词错误
400
  log_queue.put(f"❌ [生成失败] 视频{global_video_idx+1}: {status_data}")
401
  break
402
  time.sleep(2)
@@ -549,7 +580,7 @@ def process_pipeline(full_text, audio_file, gen_system_prompt):
549
  if new_msg: logs.append(f"[{datetime.now().strftime('%H:%M:%S')}] {new_msg}")
550
  return "\n".join(logs[-100:])
551
 
552
- yield update_ui("🚀 系统启动 (Async Veo API 版)..."), None
553
  download_font_if_missing()
554
 
555
  raw_segments = strict_text_splitter(full_text)
@@ -676,8 +707,8 @@ def process_pipeline(full_text, audio_file, gen_system_prompt):
676
  # ================= UI 界面 =================
677
 
678
  with gr.Blocks(title="Veo Pipeline Pro") as demo:
679
- gr.Markdown("## 🎬 Veo 全自动音画工厂 (Async V3.2版)")
680
- gr.Markdown("🌟 **特性**:实时渲染进度 | 6分钟超时重试 | 极速合成")
681
  with gr.Row():
682
  with gr.Column(scale=3):
683
  in_text = gr.Textbox(label="1. 输入长文案", lines=8)
 
18
  install_package("PIL", "Pillow")
19
  install_package("numpy")
20
  install_package("opencc", "opencc-python-reimplemented")
21
+ install_package("gradio")
22
+ install_package("requests")
23
 
24
  # ==========================================
25
  # 1. 正常导入库
 
29
  import time
30
  import json
31
  import requests
 
32
  import queue
 
33
  import numpy as np
34
  from datetime import datetime
35
  from concurrent.futures import ThreadPoolExecutor, as_completed
 
59
 
60
  # 4. 系统配置
61
  BASE_OUTPUT_DIR = "project_thangka_final_pipeline"
62
+ VIDEO_WORKERS = 150
63
+ LLM_WORKERS = 5
64
+ VIDEO_TIMEOUT_SECONDS = 360 # 🔥 单个视频超时设定为 6分钟
65
 
66
  # 5. 字幕配置
67
  FONT_URL = "https://github.com/googlefonts/noto-cjk/raw/main/Sans/OTF/TraditionalChinese/NotoSansCJKtc-Bold.otf"
 
327
  time.sleep(5)
328
  return None
329
 
330
+ # 🔥 VideoAgent (API格式修正版:Multipart/form-data)
331
  class VideoAgent:
332
  def __init__(self):
333
+ # 1. 修正:只保留鉴权,requests会自动处理 multipart boundary
334
+ self.headers = {
335
+ "Authorization": f"Bearer {YUNWU_API_KEY}"
336
+ }
337
+
338
+ def ensure_dummy_image(self):
339
+ # 创建一个黑色占位图,用于满足 API 的 input_reference 必填项
340
+ dummy_path = "_temp_blank_ref.png"
341
+ if not os.path.exists(dummy_path):
342
+ try:
343
+ img = Image.new('RGB', (720, 1280), (0, 0, 0))
344
+ img.save(dummy_path)
345
+ except Exception:
346
+ pass
347
+ return dummy_path
348
 
349
  def worker_task(self, prompt, global_video_idx, log_queue, save_dir):
350
  save_path = os.path.join(save_dir, f"clip_{global_video_idx+1:04d}.mp4")
 
354
  final_prompt = (f"{FORCE_STYLE_PREFIX} {prompt} {FORCE_MOTION_BOOSTER} {FORCE_STYLE_SUFFIX} {FORCE_NEGATIVE_PROMPT}")
355
  final_prompt = re.sub(r'\s+', ' ', final_prompt).replace(", ,", ",").replace("..", ".")
356
 
357
+ # 准备占位图
358
+ dummy_ref_path = self.ensure_dummy_image()
359
+
360
+ # 🔥 重试逻辑
361
  for attempt in range(5):
362
  try:
 
363
  create_url = f"{MERCHANT_BASE_URL}/v1/videos"
364
+
365
+ # 2. 修正:所有字段必须是字符串 (data 部分)
366
  form_data = {
367
  "model": VEO_MODEL,
368
  "prompt": final_prompt,
369
+ "seconds": str(VIDEO_DURATION_STR),
370
  "size": VIDEO_SIZE,
371
+ "watermark": "false" # API要求 string 'false'
372
  }
373
 
374
+ # 3. 修正:文件部分 (input_reference 必填)
375
+ # 使用 files 参数会自动触发 multipart/form-data
376
+ files_payload = {}
377
+ if os.path.exists(dummy_ref_path):
378
+ files_payload["input_reference"] = (
379
+ os.path.basename(dummy_ref_path),
380
+ open(dummy_ref_path, "rb"),
381
+ "image/png"
382
+ )
383
+
384
+ # 发送请求
385
+ req = requests.post(create_url, headers=self.headers, data=form_data, files=files_payload, timeout=60)
386
 
387
+ # 手动关闭文件句柄(如果打开了的话,requests通常会自动处理,但为安全起见)
388
+ if "input_reference" in files_payload:
389
+ files_payload["input_reference"][1].close()
390
+
391
  if req.status_code != 200:
392
  try: err_msg = req.json()
393
  except: err_msg = req.text[:100]
 
407
  # 轮询查询状态
408
  start_time = time.time()
409
  while True:
 
410
  if time.time() - start_time > VIDEO_TIMEOUT_SECONDS:
411
  log_queue.put(f"⚠️ [超时] 视频{global_video_idx+1} 耗时超过6分钟,视为失败,正在重试...")
412
+ break
413
 
414
  query_url = f"{MERCHANT_BASE_URL}/v1/videos/{task_id}"
415
  status_resp = requests.get(query_url, headers=self.headers, timeout=30)
 
428
  else:
429
  break
430
  elif status == "failed":
 
431
  log_queue.put(f"❌ [生成失败] 视频{global_video_idx+1}: {status_data}")
432
  break
433
  time.sleep(2)
 
580
  if new_msg: logs.append(f"[{datetime.now().strftime('%H:%M:%S')}] {new_msg}")
581
  return "\n".join(logs[-100:])
582
 
583
+ yield update_ui("🚀 系统启动 (Async Veo API V3.2 Fix版)..."), None
584
  download_font_if_missing()
585
 
586
  raw_segments = strict_text_splitter(full_text)
 
707
  # ================= UI 界面 =================
708
 
709
  with gr.Blocks(title="Veo Pipeline Pro") as demo:
710
+ gr.Markdown("## 🎬 Veo 全自动音画工厂 (Multipart 修正版)")
711
+ gr.Markdown("🌟 **特性**:自动生成垫图 | 修复API格式 | 实时渲染")
712
  with gr.Row():
713
  with gr.Column(scale=3):
714
  in_text = gr.Textbox(label="1. 输入长文案", lines=8)