53f99affec
前端视频工作流选择器 → 后端转发 api_video_workflow → Pixelle comfy 注入 audio_path → 部署联调。视频入口可选、默认不启用、向后兼容。 Co-Authored-By: Claude <noreply@anthropic.com>
242 lines
10 KiB
Markdown
242 lines
10 KiB
Markdown
# 数字人接入 Comfy Cloud 视频工作流(完整视频入口)实现计划
|
||
|
||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||
|
||
**Goal:** 给 BoosAPI 数字人加「视频工作流」入口,让用户选 `comfy/video_digital_ltx2.json` 触发「肖像图+旁白音频→LTX2.3 说话视频」,贯穿前端→BoosAPI 后端→Pixelle 三层。
|
||
|
||
**Architecture:** 前端 `DigitalHuman.tsx` 加可选「视频工作流」选择器(列 `video_*`),提交 `api_video_workflow`;BoosAPI `digital_human.rs` 的 `pixelle_request_body` 透传该字段;Pixelle `asset_based.py` 对 `comfy/` 视频工作流也注入 `audio_path`。视频入口默认空=不启用,保持现有「图片+HTML 合成」向后兼容。
|
||
|
||
**Tech Stack:** React/TS(BoosAPI 前端)、Rust/axum(BoosAPI 后端)、Python(Pixelle,容器内)。
|
||
|
||
> **环境:** BoosAPI 仓库 `/root/metapi`,前端 `src/web`,后端 `src/routes`。Pixelle 在 Docker 容器 `pixelle-video-api`,源码 `/app/pixelle_video`(镜像内置,改后 `docker compose restart api`,备份到 `/opt/Pixelle-Video/patches/`)。构建:前端 `npm run typecheck:web`,后端 `cargo build -j 2`(-j 2 防 OOM)。分支 `feat/comfy-cloud-adapter`。
|
||
|
||
> **前置(已完成):** comfy-cloud 适配器、`comfy/video_digital_ltx2.json`(probe 通过)、media.py 转发 image_path。
|
||
|
||
---
|
||
|
||
## 文件结构
|
||
|
||
- Modify `src/web/pages/DigitalHuman.tsx` — 加 `apiVideoWorkflow` state + 「视频工作流」选择器 + payload 字段 + draft 持久化。
|
||
- Modify `src/routes/digital_human.rs` — `pixelle_request_body` 转发 `api_video_workflow`。
|
||
- Modify(容器内)`/app/pixelle_video/pipelines/asset_based.py` — comfy 视频工作流注入 audio_path。
|
||
|
||
---
|
||
|
||
<!-- TASKS -->
|
||
|
||
## Task 1: 前端加「视频工作流」选择器 + 提交字段
|
||
|
||
**Files:**
|
||
- Modify: `src/web/pages/DigitalHuman.tsx`
|
||
|
||
- [ ] **Step 1: `DigitalHumanDraft` type 加字段(~line 34 附近,imageWorkflow 旁)**
|
||
|
||
找到 `imageWorkflow?: string;`(约 line 34),其后加一行:
|
||
```ts
|
||
apiVideoWorkflow?: string;
|
||
```
|
||
|
||
- [ ] **Step 2: 加 state(~line 370,imageWorkflow useState 旁)**
|
||
|
||
找到 `const [imageWorkflow, setImageWorkflow] = useState(draftRef.current.imageWorkflow || '');`(约 line 370),其后加:
|
||
```ts
|
||
const [apiVideoWorkflow, setApiVideoWorkflow] = useState(draftRef.current.apiVideoWorkflow || '');
|
||
```
|
||
|
||
- [ ] **Step 3: draft 持久化加字段(~line 462 写入 + ~line 480 依赖数组)**
|
||
|
||
在 `writeDigitalHumanDraft({...})` 对象里(约 line 462),`imageWorkflow,` 后加 `apiVideoWorkflow,`;
|
||
在该 useEffect 的依赖数组里(约 line 480),`imageWorkflow,` 后同样加 `apiVideoWorkflow,`。
|
||
|
||
- [ ] **Step 4: 提交 payload 加字段(~line 570 payload 对象)**
|
||
|
||
找到 payload 对象里的 `media_workflow: imageWorkflow || undefined,`(约 line 575),其后加:
|
||
```ts
|
||
api_video_workflow: apiVideoWorkflow || undefined,
|
||
```
|
||
|
||
- [ ] **Step 5: 加「视频工作流」选择器 UI(图片工作流 select 之后,~line 1012)**
|
||
|
||
找到「图片工作流」那个 `<label className="digital-human-field">...select...</label>` 块结束处
|
||
(约 line 1012,`</label>` 后),插入新选择器:
|
||
```tsx
|
||
<label className="digital-human-field">
|
||
<span>视频工作流(可选,生成说话视频)</span>
|
||
<select value={apiVideoWorkflow} onChange={(event) => setApiVideoWorkflow(event.target.value)}>
|
||
<option value="">不生成视频(仅图片合成)</option>
|
||
{mediaWorkflows
|
||
.filter((wf) => wf.name.toLowerCase().includes('video'))
|
||
.map((wf) => (
|
||
<option key={workflowValue(wf)} value={workflowValue(wf)}>{pickItemLabel(wf)}{wf.source ? ` · ${wf.source}` : ''}</option>
|
||
))}
|
||
</select>
|
||
</label>
|
||
```
|
||
|
||
- [ ] **Step 6: typecheck**
|
||
|
||
Run: `cd /root/metapi && npm run typecheck:web 2>&1 | tail -5`
|
||
Expected: 无 TS 错误。
|
||
|
||
- [ ] **Step 7: Commit**
|
||
|
||
```bash
|
||
cd /root/metapi && git add src/web/pages/DigitalHuman.tsx
|
||
git commit -m "feat(digital-human): 前端加视频工作流选择器(api_video_workflow)
|
||
|
||
Co-Authored-By: Claude <noreply@anthropic.com>"
|
||
```
|
||
|
||
<!-- T2 -->
|
||
|
||
## Task 2: BoosAPI 后端转发 api_video_workflow 给 Pixelle
|
||
|
||
**Files:**
|
||
- Modify: `src/routes/digital_human.rs`
|
||
|
||
- [ ] **Step 1: 在 pixelle_request_body 加转发(~line 2077,tts_workflow 块之后)**
|
||
|
||
找到 `pixelle_request_body` 里转发 tts_workflow 的块(约 line 2073-2078):
|
||
```rust
|
||
if let Some(value) = optional_string(body, &["ttsWorkflow", "tts_workflow"])
|
||
.or_else(|| default_pixelle_tts_workflow(state, backend_provider))
|
||
{
|
||
request_body["tts_workflow"] = json!(value);
|
||
}
|
||
```
|
||
在它**之后**插入:
|
||
```rust
|
||
if let Some(value) = optional_string(body, &["apiVideoWorkflow", "api_video_workflow"]) {
|
||
request_body["api_video_workflow"] = json!(value);
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 2: 构建确认编译**
|
||
|
||
Run: `cd /root/metapi && cargo build -j 2 2>&1 | tail -3`
|
||
Expected: `Finished`。
|
||
|
||
- [ ] **Step 3: 确认 optional_string 接受多 key(它已在文件内大量使用,签名兼容)**
|
||
|
||
Run: `cd /root/metapi && rtk proxy grep -n "fn optional_string" src/routes/digital_human.rs | head`
|
||
Expected: 找到定义,签名为 `(body, keys: &[&str])`(与 Step 1 用法一致)。若签名不同,按实际调整调用形式。
|
||
|
||
- [ ] **Step 4: Commit**
|
||
|
||
```bash
|
||
cd /root/metapi && git add src/routes/digital_human.rs
|
||
git commit -m "feat(digital-human): 后端转发 api_video_workflow 给 Pixelle
|
||
|
||
Co-Authored-By: Claude <noreply@anthropic.com>"
|
||
```
|
||
|
||
<!-- T3 -->
|
||
|
||
## Task 3: Pixelle 给 comfy 视频工作流注入 audio_path
|
||
|
||
**Files:**
|
||
- Modify(容器内):`/app/pixelle_video/pipelines/asset_based.py`
|
||
|
||
- [ ] **Step 1: 定位音频注入块(~line 703)**
|
||
|
||
Run: `rtk proxy docker exec pixelle-video-api sh -lc "grep -n 'audio_driven_i2v' /app/pixelle_video/pipelines/asset_based.py"`
|
||
Expected: 命中 `if "audio_driven_i2v" in adapter_abilities:` 那行(约 703)。
|
||
|
||
- [ ] **Step 2: 在该判断旁加 comfy 前缀分支(用 python 精确改写)**
|
||
|
||
```bash
|
||
rtk proxy docker exec pixelle-video-api /app/.venv/bin/python -c "
|
||
p='/app/pixelle_video/pipelines/asset_based.py'
|
||
src=open(p).read()
|
||
anchor=' if \"audio_driven_i2v\" in adapter_abilities:\n api_video_params[\"audio_path\"] = frame.audio_path'
|
||
assert anchor in src, 'anchor not found'
|
||
add=anchor + '''
|
||
elif isinstance(api_video_workflow, str) and api_video_workflow.startswith(\"comfy/\"):
|
||
api_video_params[\"audio_path\"] = frame.audio_path'''
|
||
if 'api_video_workflow.startswith(\"comfy/\")' in src:
|
||
print('already patched')
|
||
else:
|
||
src=src.replace(anchor, add, 1)
|
||
open(p,'w').write(src)
|
||
print('patched')
|
||
"
|
||
```
|
||
Expected: `patched`(或 `already patched`)。
|
||
|
||
- [ ] **Step 3: 语法校验 + 重启 + 备份**
|
||
|
||
```bash
|
||
rtk proxy docker exec pixelle-video-api /app/.venv/bin/python -c "import ast; ast.parse(open('/app/pixelle_video/pipelines/asset_based.py').read()); print('syntax OK')"
|
||
cd /opt/Pixelle-Video && rtk proxy docker compose restart api && sleep 7 && rtk proxy docker ps --filter name=pixelle --format '{{.Names}} {{.Status}}'
|
||
mkdir -p /opt/Pixelle-Video/patches && rtk proxy docker cp pixelle-video-api:/app/pixelle_video/pipelines/asset_based.py /opt/Pixelle-Video/patches/asset_based.py.patched
|
||
```
|
||
Expected: `syntax OK`;容器 healthy;备份文件存在。
|
||
|
||
- [ ] **Step 4: 确认改动重启后仍在**
|
||
|
||
Run: `rtk proxy docker exec pixelle-video-api sh -lc "grep -c 'api_video_workflow.startswith(\"comfy/\")' /app/pixelle_video/pipelines/asset_based.py"`
|
||
Expected: `1`。无 git commit(Pixelle 非本仓库)。
|
||
|
||
<!-- T4 -->
|
||
|
||
## Task 4: 部署 BoosAPI + 端到端联调
|
||
|
||
**Files:** 无(部署 + 运行时验证)
|
||
|
||
- [ ] **Step 1: 重新构建并重启 BoosAPI(含前端改动)**
|
||
|
||
前端改了需重新 build 前端资源,后端改了需重新编译。用项目脚本:
|
||
```bash
|
||
cd /root/metapi && ./build-and-run.sh --fast-release 2>&1 | tail -15
|
||
```
|
||
> 若只想快速验证后端逻辑、前端单独 build,可 `npm run build:web` + `./build-and-run.sh --backend-only`。
|
||
> Expected: 构建成功,BoosAPI 起在 :4000。注意这是动线上服务,确认当前无关键流量再重启。
|
||
|
||
- [ ] **Step 2: 确认数字人 UI 出现「视频工作流」选择器**
|
||
|
||
打开管理控制台数字人页面,确认新增「视频工作流(可选)」下拉,选项含
|
||
`video_digital_ltx2`(comfy)+ `video_wan2.1_fusionx`(comfy)等 video_* 工作流。
|
||
|
||
- [ ] **Step 3: 发起数字人任务,视频工作流选 `comfy/video_digital_ltx2.json`**
|
||
|
||
给肖像图 + 文本 + 选 TTS,视频工作流选 `comfy/video_digital_ltx2.json`,提交。
|
||
|
||
- [ ] **Step 4: 监控流转(BoosAPI 日志 + Comfy Cloud job)**
|
||
|
||
```bash
|
||
cd /root/metapi && rtk proxy sqlite3 boosapi.db "SELECT id, created_at, client_family, http_status, substr(error_message,1,80) FROM proxy_logs ORDER BY id DESC LIMIT 5;"
|
||
```
|
||
Expected: 新日志(id 增长),数字人任务推进;Comfy Cloud 出现 LTX2.3 job;最终出说话视频。
|
||
|
||
- [ ] **Step 5: 出错归因**
|
||
|
||
- LoadImage 仍用默认图 → 查 media.py image_path 转发(Task 已完成,确认 `grep image_path /app/pixelle_video/services/media.py`)。
|
||
- LoadAudio 无音频 → 查 Task 3 注入(`grep comfy/ /app/pixelle_video/pipelines/asset_based.py`)+ workflow 标记 `$~audio_path!`。
|
||
- `api_video_workflow` 没传到 → 查 Task 2 后端转发 + Task 1 前端 payload 字段名(`api_video_workflow`)。
|
||
- 节点/模型错 → 不应发生(probe 已过);若发生看 Comfy Cloud 真实回包。
|
||
|
||
- [ ] **Step 6: 成功标准**
|
||
|
||
数字人任务选 comfy/video_digital_ltx2.json 后,Comfy Cloud 出真实 job,注入了
|
||
image_path+audio_path+prompt,返回带音频说话视频,数字人成片。
|
||
|
||
---
|
||
|
||
## 完成标准
|
||
|
||
- 前端:数字人页有「视频工作流」可选下拉,列 video_* 工作流,typecheck 干净。
|
||
- 后端:`api_video_workflow` 透传给 Pixelle,`cargo build -j 2` 通过。
|
||
- Pixelle:comfy 视频工作流注入 audio_path,重启生效,备份 patches/。
|
||
- 端到端:选 comfy/video_digital_ltx2.json 出说话视频。
|
||
- 向后兼容:不选视频工作流时,现有「图片+HTML 合成」行为不变。
|
||
|
||
## 已知风险 / 备注
|
||
|
||
- Pixelle asset_based.py 改动在镜像内置层,重建镜像会丢,已备份 patches/,长期应固化 Dockerfile。
|
||
- Task 4 动线上服务(重启 BoosAPI),确认时机。
|
||
- 单人肖像;多人不在范围。
|
||
- LTX2.3 执行时长/分辨率等真实生成参数,若出片质量/超时问题属调参,非本计划的接线问题。
|
||
|
||
|
||
|