#!/usr/bin/env python3 """ 自动化脚本:为3D手办功能生成新的示例结果图片 """ import asyncio import base64 import httpx import json import os import time from PIL import Image from io import BytesIO # 配置 API_BASE_URL = "https://1251722089-fabl8rzpod.ap-singapore.tencentscf.com" INPUT_IMAGE_PATH = "examples/figure_3d_input_example.jpeg" OUTPUT_DIR = "examples/results" TIMEOUT_SECONDS = 600 # 10分钟 # 认证配置 AUTH_HEADERS = { "Content-Type": "application/json", "X-API-Auth": "local_development_token_12345678" } # 3D手办风格配置 STYLES = [ ("professional_lighting", "💡 专业灯光场景"), ("collector_shelf", "📚 爱好者收藏架场景"), ("desktop_display", "💻 电脑桌展示") ] def load_and_encode_image(image_path): """加载图片并转换为Base64编码""" try: with open(image_path, 'rb') as f: image_data = f.read() # 转换为Base64 base64_data = base64.b64encode(image_data).decode('utf-8') print(f"✅ 成功加载图片: {image_path}") print(f"📏 图片大小: {len(image_data)} bytes") return base64_data except Exception as e: print(f"❌ 加载图片失败: {e}") return None async def submit_task(style_key, image_data): """提交3D手办生成任务""" url = f"{API_BASE_URL}/api/v1/tasks/figure-3d-generation" payload = { "image_data": image_data, "figure_style": style_key, "resolution": "square - 1024x1024 (1:1)" } headers = AUTH_HEADERS try: async with httpx.AsyncClient(timeout=30.0) as client: print(f"🚀 提交任务: {style_key}") response = await client.post(url, json=payload, headers=headers) if response.status_code in [200, 201]: result = response.json() task_id = result.get("task_id") print(f"✅ 任务提交成功: {task_id}") return task_id else: print(f"❌ 任务提交失败: {response.status_code} - {response.text}") return None except Exception as e: print(f"❌ 提交任务异常: {e}") return None async def wait_for_completion(task_id): """等待任务完成""" url = f"{API_BASE_URL}/api/v1/tasks/{task_id}" start_time = time.time() async with httpx.AsyncClient(timeout=30.0) as client: while True: try: response = await client.get(url, headers=AUTH_HEADERS) if response.status_code == 200: result = response.json() status = result.get("status") print(f"📊 任务状态: {status}") if status == "completed": print("✅ 任务完成!") return result elif status == "failed": print(f"❌ 任务失败: {result.get('error', 'Unknown error')}") return None elif status in ["pending", "running", "processing"]: # 检查超时 elapsed = time.time() - start_time if elapsed > TIMEOUT_SECONDS: print(f"⏰ 任务超时 ({TIMEOUT_SECONDS}秒)") return None print(f"⏳ 等待中... (已等待 {elapsed:.0f}秒)") await asyncio.sleep(10) # 每10秒检查一次 else: print(f"❓ 未知状态: {status}") await asyncio.sleep(5) else: print(f"❌ 状态查询失败: {response.status_code}") await asyncio.sleep(5) except Exception as e: print(f"❌ 状态查询异常: {e}") await asyncio.sleep(5) async def download_result(task_id, style_key): """下载任务结果""" url = f"{API_BASE_URL}/api/v1/tasks/{task_id}/result" try: async with httpx.AsyncClient(timeout=60.0) as client: response = await client.get(url, headers=AUTH_HEADERS) if response.status_code == 200: result = response.json() result_url = result.get("result_url") if result_url: # 下载图片 (图片URL通常不需要认证,但为了保险起见也加上) img_response = await client.get(result_url) if img_response.status_code == 200: # 保存图片 output_path = os.path.join(OUTPUT_DIR, f"figure_3d_{style_key}.png") # 确保输出目录存在 os.makedirs(OUTPUT_DIR, exist_ok=True) with open(output_path, 'wb') as f: f.write(img_response.content) print(f"✅ 结果已保存: {output_path}") return output_path else: print(f"❌ 下载图片失败: {img_response.status_code}") else: print("❌ 结果中没有图片URL") else: print(f"❌ 获取结果失败: {response.status_code}") except Exception as e: print(f"❌ 下载结果异常: {e}") return None async def generate_example(style_key, style_name, image_data): """生成单个示例""" print(f"\n{'='*50}") print(f"🎨 开始生成示例: {style_name}") print(f"{'='*50}") # 1. 提交任务 task_id = await submit_task(style_key, image_data) if not task_id: return False # 2. 等待完成 result = await wait_for_completion(task_id) if not result: return False # 3. 下载结果 output_path = await download_result(task_id, style_key) if output_path: print(f"🎉 示例生成成功: {style_name}") return True else: print(f"💥 示例生成失败: {style_name}") return False async def main(): """主函数""" print("🚀 开始生成3D手办示例...") # 加载输入图片 image_data = load_and_encode_image(INPUT_IMAGE_PATH) if not image_data: print("❌ 无法加载输入图片,退出") return success_count = 0 # 逐个生成示例(每次只运行一个) for style_key, style_name in STYLES: success = await generate_example(style_key, style_name, image_data) if success: success_count += 1 # 在任务之间稍作休息 if style_key != STYLES[-1][0]: # 不是最后一个 print("\n⏸️ 休息5秒后继续下一个...") await asyncio.sleep(5) print(f"\n{'='*50}") print(f"🏁 生成完成! 成功: {success_count}/{len(STYLES)}") print(f"{'='*50}") if __name__ == "__main__": asyncio.run(main())