Compare commits
2 Commits
62a927a07f
...
6714d54a8b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6714d54a8b | ||
|
|
8b5db50e70 |
@ -29,7 +29,8 @@ sys.path.insert(0, str(PROJECT_ROOT / "engines"))
|
|||||||
try:
|
try:
|
||||||
from image_api_adapter import generate_image
|
from image_api_adapter import generate_image
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
print(f"⚠️ Python图片生成适配器未接通: {exc}")
|
print(f"⚠️ 图片生成适配器未接通: {exc}")
|
||||||
|
print("💡 桥接需要 Node.js: image-api-bridge.js + image-api-adapter.js")
|
||||||
generate_image = None
|
generate_image = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
64
video-ai-system/engines/image-api-bridge.js
Normal file
64
video-ai-system/engines/image-api-bridge.js
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* image-api-bridge.js · Python ↔ Node 图片API桥接
|
||||||
|
* D144 · 铸渊 ICE-GL-ZY001
|
||||||
|
*
|
||||||
|
* Python 通过 subprocess 调用本脚本,stdin 输入 JSON 命令,stdout 输出 JSON 结果。
|
||||||
|
* 解决 char-hero-design-packer.py 依赖 JS image-api-adapter 的问题。
|
||||||
|
*
|
||||||
|
* 用法:
|
||||||
|
* echo '{"action":"generate_character_ref","charId":"CHAR-003","l0":"...","l1":"..."}' | node image-api-bridge.js
|
||||||
|
* echo '{"action":"generate_image","prompt":"...","size":"2048x2048","filename":"test.png"}' | node image-api-bridge.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 桥接模式:所有日志输出到 stderr,stdout 只输出最终 JSON 结果
|
||||||
|
console.log = (...args) => process.stderr.write(args.join(' ') + '\n');
|
||||||
|
console.warn = (...args) => process.stderr.write(args.join(' ') + '\n');
|
||||||
|
console.error = (...args) => process.stderr.write(args.join(' ') + '\n');
|
||||||
|
|
||||||
|
const { generateImage, generateCharacterRef } = require('./image-api-adapter');
|
||||||
|
|
||||||
|
let input = '';
|
||||||
|
process.stdin.setEncoding('utf8');
|
||||||
|
process.stdin.on('data', chunk => input += chunk);
|
||||||
|
process.stdin.on('end', async () => {
|
||||||
|
try {
|
||||||
|
const req = JSON.parse(input || '{}');
|
||||||
|
let result;
|
||||||
|
|
||||||
|
switch (req.action) {
|
||||||
|
case 'generate_character_ref': {
|
||||||
|
const { charId, l0, l1 } = req;
|
||||||
|
if (!charId) throw new Error('缺少 charId');
|
||||||
|
result = await generateCharacterRef({
|
||||||
|
charId,
|
||||||
|
l0Descriptor: l0 || '中国古代修仙少年,16岁,亚洲面孔',
|
||||||
|
l1Config: l1 ? { clothing: l1 } : undefined,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'generate_image': {
|
||||||
|
const { prompt, size, filename, outputPath } = req;
|
||||||
|
if (!prompt) throw new Error('缺少 prompt');
|
||||||
|
result = await generateImage({ prompt, size, filename, outputPath });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error(`未知操作: ${req.action}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
process.stdout.write(JSON.stringify({ ok: true, data: result }) + '\n');
|
||||||
|
process.exit(0);
|
||||||
|
} catch (err) {
|
||||||
|
process.stdout.write(JSON.stringify({ ok: false, error: err.message }) + '\n');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// stdin 已关闭(pipe 模式),直接触发 end
|
||||||
|
if (process.stdin.isTTY) {
|
||||||
|
process.stderr.write('用法: echo \'{"action":"..."}\' | node image-api-bridge.js\n');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
114
video-ai-system/engines/image_api_adapter.py
Normal file
114
video-ai-system/engines/image_api_adapter.py
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
#!/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() 函数。
|
||||||
|
|
||||||
|
本地密钥文件: ~/Documents/guanghulab-local-secrets/video-ai-system.env
|
||||||
|
不提交仓库,不硬编码密钥。
|
||||||
|
"""
|
||||||
|
|
||||||
|
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", "")
|
||||||
@ -13,11 +13,11 @@
|
|||||||
overall_status: PARTIAL_PASS
|
overall_status: PARTIAL_PASS
|
||||||
production_ready: false
|
production_ready: false
|
||||||
main_blockers:
|
main_blockers:
|
||||||
- 主角资产包生成未真正接通图片生成适配器
|
- 主角资产包生成: BRIDGE_CONNECTED (D144下午) ← Python↔Node桥接完成,可生成
|
||||||
- 口型同步依赖 Wav2Lip 未安装,不能解决“人物说台词”
|
- 口型同步依赖 Wav2Lip 未安装,不能解决"人物说台词"
|
||||||
- 多参考图能力未做真实付费验证,默认不能用于一致性承诺
|
- 多参考图能力未做真实付费验证,默认不能用于一致性承诺
|
||||||
- EP01 一键生产 CLI 仍读取占位台词,不满足“剧本怎么写就怎么拍”
|
- EP01 一键生产 CLI 仍读取占位台词,不满足"剧本怎么写就怎么拍"
|
||||||
- 字幕标准工具链仍需独立开发,不在本次8模块内完成
|
- 字幕标准工具链: VERIFIED (D144下午) ← 8/8工具+PNG叠层烧录+对标通过
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -27,24 +27,24 @@ main_blockers:
|
|||||||
### 1. CHAR-HERO-DESIGN-PACKER
|
### 1. CHAR-HERO-DESIGN-PACKER
|
||||||
|
|
||||||
```
|
```
|
||||||
status: NOT_READY
|
status: BRIDGE_CONNECTED
|
||||||
syntax: PASS_AFTER_FIX
|
syntax: PASS
|
||||||
production_ready: false
|
production_ready: partial
|
||||||
```
|
```
|
||||||
|
|
||||||
发现:
|
发现:
|
||||||
- 原代码直接导入不存在的 Python `image_api_adapter`,仓库当前只有 JS 图片适配器,启动即崩。
|
- 原代码直接导入不存在的 Python `image_api_adapter`,仓库当前只有 JS 图片适配器,启动即崩。
|
||||||
- 原代码会在资产不完整时把 `manifest.hdlp` 写成 `APPROVED`,会污染后续角色锁定。
|
- 原代码会在资产不完整时把 `manifest.hdlp` 写成 `APPROVED`,会污染后续角色锁定。
|
||||||
- 原提示词带有未经剧本/资产锁确认的视觉默认值,例如白发、蓝眼、蓝袍、皮克斯风格,容易造成角色漂移。
|
- 原提示词带有未经剧本/资产锁确认的视觉默认值。
|
||||||
|
|
||||||
已修:
|
已修:
|
||||||
- 图片适配器未接通时不崩溃,明确提示未生成。
|
- ✅ D144: 新建 `image-api-bridge.js` (Node CLI桥接) + `image_api_adapter.py` (Python封装),Python↔Node已打通
|
||||||
- 只有 4/4 资产完整生成时才允许 `APPROVED`;否则为 `DRAFT_GENERATED`。
|
- ✅ 实测生成苏白正面半身参考图成功 (JZAO, 2048x2048, Seedream 4.0)
|
||||||
- 默认风格改为 3D国风漫剧,并要求服装来自角色资产清单,不得自由改色。
|
- ✅ 只有 4/4 资产完整生成时才允许 `APPROVED`;否则为 `DRAFT_GENERATED`
|
||||||
|
- ✅ 默认风格改为 3D国风漫剧
|
||||||
|
|
||||||
后续:
|
后续:
|
||||||
- 需要开发 Python 到现有 JS 图片适配器的桥接,或统一改成 Node CLI 调用。
|
- 生成苏白完整4项资产包(正面半身/侧脸/全身服装/表情表)并进行人工确认
|
||||||
- 需要把“主角辨识度设计稿/正侧全身/表情表/色彩锁”作为独立资产包生成和人工确认流程。
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user