cang-ying/engines/image_api_adapter.py

115 lines
3.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
image_api_adapter.py · Python端图片API适配器
D144 · 铸渊 ICE-GL-ZY001
桥接到 Node.js image-api-bridge.js为 char-hero-design-packer.py 提供
generate_image() 函数。
本地密钥入口: VIDEO_AI_SECRETS_FILE 或 LOCAL-SECRETS-PATH.hdlp 登记路径
不提交仓库,不硬编码密钥。
"""
import json
import os
import subprocess
from pathlib import Path
ENGINES_DIR = Path(__file__).parent
BRIDGE_SCRIPT = ENGINES_DIR / "image-api-bridge.js"
NODE_BIN = "/Users/bingshuolingdianyuanhe/.workbuddy/binaries/node/versions/22.22.2/bin/node"
def _call_bridge(action: str, **kwargs) -> dict:
"""
调用 Node.js 桥接脚本
:param action: 操作名 (generate_character_ref | generate_image)
:param kwargs: 传给 JS 的参数
:return: {ok: bool, data: {...} | error: str}
"""
if not BRIDGE_SCRIPT.exists():
return {"ok": False, "error": f"桥接脚本不存在: {BRIDGE_SCRIPT}"}
payload = {"action": action, **kwargs}
try:
result = subprocess.run(
[NODE_BIN, str(BRIDGE_SCRIPT)],
input=json.dumps(payload, ensure_ascii=False),
capture_output=True,
text=True,
timeout=120,
cwd=str(ENGINES_DIR),
)
except FileNotFoundError:
return {"ok": False, "error": f"Node 未找到: {NODE_BIN}"}
except subprocess.TimeoutExpired:
return {"ok": False, "error": "桥接调用超时120秒"}
if result.returncode != 0:
stderr = result.stderr.strip()[:500] if result.stderr else "无输出"
return {"ok": False, "error": f"桥接退出码 {result.returncode}: {stderr}"}
try:
return json.loads(result.stdout.strip())
except json.JSONDecodeError:
return {"ok": False, "error": f"桥接输出非JSON: {result.stdout[:200]}"}
def generate_image(prompt: str, reference_image: str = None,
output_dir: str = None, output_name: str = None) -> str:
"""
生成图片(为 char-hero-design-packer.py 提供接口)
:param prompt: 提示词
:param reference_image: 参考图路径(当前桥接为文生图,参考图暂不支持)
:param output_dir: 输出目录
:param output_name: 输出文件名
:return: 生成的图片路径,失败返回 None
"""
kwargs = {"prompt": prompt, "size": "2048x2048"}
if output_dir and output_name:
kwargs["outputPath"] = os.path.join(output_dir, output_name)
elif output_name:
kwargs["filename"] = output_name
result = _call_bridge("generate_image", **kwargs)
if not result.get("ok"):
print(f" ⚠️ 图片生成失败: {result.get('error', '未知错误')}")
return None
data = result.get("data", {})
image_path = data.get("imagePath", "")
if image_path and os.path.isfile(image_path):
return image_path
return None
def generate_character_ref(char_id: str, l0_descriptor: str = None,
l1_clothing: str = None) -> str:
"""
生成角色参考图
:param char_id: 角色编号,如 CHAR-003
:param l0_descriptor: L0底层编码描述
:param l1_clothing: L1服装配置
:return: 生成的图片路径,失败返回 None
"""
kwargs = {"charId": char_id}
if l0_descriptor:
kwargs["l0"] = l0_descriptor
if l1_clothing:
kwargs["l1"] = l1_clothing
result = _call_bridge("generate_character_ref", **kwargs)
if not result.get("ok"):
print(f" ⚠️ 角色参考图生成失败: {result.get('error', '未知错误')}")
return None
data = result.get("data", {})
return data.get("imagePath", "")