2026-04-29 21:04:43 +08:00
|
|
|
|
# manifest.json 规范
|
|
|
|
|
|
|
|
|
|
|
|
> `pipeline.js init` 创建,Pipeline 执行,Agent 审查。
|
|
|
|
|
|
>
|
2026-05-01 02:48:37 +08:00
|
|
|
|
> **禁止 AI 手写 manifest.json**,必须通过 `pipeline.js init` 初始化。脚本从 account.json 自动继承结构字段,AI 只提供创意内容(items 的 shotDesc/script/imagePrompt 等)。
|
2026-04-29 21:04:43 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 创建方式
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
2026-05-02 00:14:40 +08:00
|
|
|
|
# Step 2-0:分镜确认后立即初始化(imagePrompt/videoPrompt 后续补充)
|
2026-05-01 02:48:37 +08:00
|
|
|
|
node scripts/pipeline.js init --account 军事账号 --mode single \
|
2026-05-02 00:14:40 +08:00
|
|
|
|
--items '[{"shotDesc":"英文画面描述","script":"中文口播文案","duration":5,"directorRef":"tarantino","keyword":"权力"}]'
|
2026-04-29 21:04:43 +08:00
|
|
|
|
|
|
|
|
|
|
# 或从文件读取
|
2026-05-01 02:48:37 +08:00
|
|
|
|
node scripts/pipeline.js init --account 军事账号 --mode single --items-file ./items.json
|
2026-04-30 21:18:31 +08:00
|
|
|
|
|
|
|
|
|
|
# Step 2-C 人工确认
|
2026-05-01 02:48:37 +08:00
|
|
|
|
node scripts/pipeline.js confirm --manifest <path> --all
|
|
|
|
|
|
node scripts/pipeline.js confirm --manifest <path> --items 1,3,5
|
2026-04-29 21:04:43 +08:00
|
|
|
|
|
|
|
|
|
|
# 校验已有 manifest
|
2026-05-01 02:48:37 +08:00
|
|
|
|
node scripts/pipeline.js validate --manifest <path>
|
2026-04-29 21:04:43 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 顶层字段
|
|
|
|
|
|
|
|
|
|
|
|
| 字段 | 说明 | 来源 | 谁填充 |
|
|
|
|
|
|
|------|------|------|--------|
|
|
|
|
|
|
| `account` | 账号 ID | account.json | **init 自动** |
|
|
|
|
|
|
| `imageModel` | `gemini` / `mj` | account.json | **init 自动** |
|
2026-04-29 21:56:47 +08:00
|
|
|
|
| `videoModel` | `veo3-fast-frames` / `grok-video-3` / `kling` 等 | account.json | **init 自动** |
|
2026-04-29 21:04:43 +08:00
|
|
|
|
| `format` | 画幅:`9:16` / `16:9` | account.json | **init 自动** |
|
2026-05-06 22:53:37 +08:00
|
|
|
|
| `estimatedVideoDuration` | 视频模型固定时长(秒),顶层冗余字段 | videoModel 查表 | **init 自动**,assemble 直接读 |
|
2026-04-29 21:04:43 +08:00
|
|
|
|
| `mode` | `single` 单图 / `framePair` 首尾帧 | CLI 参数 | **init 自动** |
|
|
|
|
|
|
| `references` | 参考图数组,从 account.json styles.*.references 搬入 | account.json | **init 自动** |
|
|
|
|
|
|
| `items` | 素材数组(AI 提供创意内容) | CLI --items | **AI → init** |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## references 字段
|
|
|
|
|
|
|
|
|
|
|
|
从 account.json 搬入,pipeline 直接使用,不再回读 account.json。
|
|
|
|
|
|
|
|
|
|
|
|
- **Gemini** → 读 `file`(本地路径,图生图用)
|
|
|
|
|
|
- **MJ** → 读 `url`(公网 URL,`--sref` 用)
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## items[] 字段
|
|
|
|
|
|
|
|
|
|
|
|
### Agent 写入(创建时)
|
|
|
|
|
|
|
|
|
|
|
|
| 字段 | 说明 |
|
|
|
|
|
|
|------|------|
|
|
|
|
|
|
| `status` | 固定写 `"pending"` |
|
2026-04-30 21:18:31 +08:00
|
|
|
|
| `shotDesc` | 英文分镜描述(含隐性动势,40-80词) |
|
2026-05-06 22:53:37 +08:00
|
|
|
|
| `script` | **该 shot 的语义子句原文**(完整句拆分后的子段,一字不差)|
|
|
|
|
|
|
| `duration` | **TTS 估算秒数(= script字数÷5)**,必须 ≤ 6s |
|
|
|
|
|
|
| `estimatedAudioDuration` | 同 duration,备选别名 |
|
|
|
|
|
|
| `estimatedVideoDuration` | 视频模型固定时长(Kling=6s, VEO=8s, Grok=6s),pipeline init 时自动填入 |
|
2026-04-30 21:18:31 +08:00
|
|
|
|
| `imagePrompt` | 英文画面描述(给 Gemini/MJ),Step 2-A 生成 |
|
|
|
|
|
|
| `directorRef` | 导演构图参考(tarantino / kitano / fincher),三层透传 |
|
2026-05-01 15:21:59 +08:00
|
|
|
|
| `keyword` | 关键字氛围词(2-6 字),assemble 时以花字效果叠加在画面中央。可选 |
|
2026-04-30 21:18:31 +08:00
|
|
|
|
| `confirmed` | 人工确认状态,默认 `false` |
|
|
|
|
|
|
|
2026-05-06 22:53:37 +08:00
|
|
|
|
**强制约束:**
|
|
|
|
|
|
- **每个 shot 的 `duration`(TTS估算)必须 ≤ 6s**,否则 pipeline 拒绝执行
|
|
|
|
|
|
- `script` 必须是语义子句,**完整句直接填入多个 shot 是严重错误**
|
|
|
|
|
|
- `estimatedVideoDuration` 在 manifest 初始化时由 `pipeline.js init` 从 videoModel 自动推算:
|
|
|
|
|
|
- `kling` → `6`
|
|
|
|
|
|
- `veo3-fast` / `veo3-fast-frames` → `8`
|
|
|
|
|
|
- `grok-video-3` → `6`
|
|
|
|
|
|
- assemble 阶段通过 `ratio = estimatedVideoDuration / realAudioDuration` 选择适配策略
|
|
|
|
|
|
|
2026-04-30 21:18:31 +08:00
|
|
|
|
### Agent 后续回写(Step 3-A 视频提示词)
|
|
|
|
|
|
|
|
|
|
|
|
| 字段 | 说明 | 写入时机 |
|
|
|
|
|
|
|------|------|---------|
|
2026-05-06 22:53:37 +08:00
|
|
|
|
| `videoPrompt` | 英文运动描述(给 Grok/VEO/Kling),描述镜头运动而非内容 | Step 3-A 由 Agent 回写 |
|
2026-04-29 21:04:43 +08:00
|
|
|
|
|
|
|
|
|
|
### Pipeline 回写(执行后)
|
|
|
|
|
|
|
|
|
|
|
|
| 字段 | 说明 | 写入阶段 |
|
|
|
|
|
|
|------|------|---------|
|
|
|
|
|
|
| `status` | `pending` → `generating` → `done` / `failed` | images |
|
|
|
|
|
|
| `file` | 生成的图片路径(相对 manifest) | images |
|
|
|
|
|
|
| `candidates` | MJ 拆分的 4 张候选图路径(Gemini 无此字段) | images |
|
|
|
|
|
|
| `url` | 图片 OSS 公网 URL | upload |
|
2026-04-30 21:18:31 +08:00
|
|
|
|
| `confirmed` | 人工确认后设为 `true` | confirm |
|
2026-04-29 21:04:43 +08:00
|
|
|
|
| `video` | 生成的视频路径 | videos |
|
2026-05-06 22:53:37 +08:00
|
|
|
|
| `videoDuration` | 视频实测时长(秒),Kling=6, VEO=8, Grok=6 | videos |
|
2026-04-29 21:04:43 +08:00
|
|
|
|
| `videoUrl` | 视频 OSS 公网 URL | videos |
|
2026-05-06 22:53:37 +08:00
|
|
|
|
| `audio` | TTS 音频路径 | tts |
|
|
|
|
|
|
| `audioDuration` | 音频实测时长(秒) | tts |
|
2026-05-01 14:41:28 +08:00
|
|
|
|
| `segments` | 分句音频数组(仅多句时存在),见下方 | tts |
|
2026-04-29 21:04:43 +08:00
|
|
|
|
|
|
|
|
|
|
### Agent 审查时可操作
|
|
|
|
|
|
|
|
|
|
|
|
- MJ 换选:`item.file = item.candidates[2]`
|
|
|
|
|
|
- 删除不合格 item:直接从 items 数组移除,重新跑 `--phase images`
|
|
|
|
|
|
- 调整 prompt 重跑:改 `imagePrompt`,status 改回 `pending`
|
2026-05-01 02:48:37 +08:00
|
|
|
|
- 人工确认:`node scripts/pipeline.js confirm --manifest <path> --all`
|
2026-04-29 21:04:43 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-04-29 21:56:47 +08:00
|
|
|
|
## 状态机
|
|
|
|
|
|
|
|
|
|
|
|
### item 生命周期
|
|
|
|
|
|
|
|
|
|
|
|
```
|
2026-04-30 21:18:31 +08:00
|
|
|
|
pending → [images] → done → [confirm] → confirmed=true → [upload: url填入] → [videos] → done → [tts] → done
|
|
|
|
|
|
↓ ↓
|
|
|
|
|
|
failed failed + error
|
2026-04-29 21:56:47 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
status 一旦进入 `done` 就不再回退。后续阶段通过检查"有前置字段 + 无后置字段"来识别待处理 item,不依赖 status 变化。
|
|
|
|
|
|
|
|
|
|
|
|
### 各阶段拾取条件
|
|
|
|
|
|
|
|
|
|
|
|
Agent **不需要记住这些条件**,pipeline 内部自动匹配。仅供理解原理:
|
|
|
|
|
|
|
|
|
|
|
|
| 阶段 | item 被拾取的条件 |
|
|
|
|
|
|
|------|------------------|
|
|
|
|
|
|
| images | `status=pending` + 有 `imagePrompt` |
|
|
|
|
|
|
| upload | `status=done` + 有 `file` + 无 `url` |
|
2026-04-30 21:18:31 +08:00
|
|
|
|
| videos | `status=done` + `confirmed=true` + 有 `url` + 有 `videoPrompt` + 无 `video` |
|
2026-05-01 02:48:37 +08:00
|
|
|
|
| tts | `status=done` + 有 `script`(回退 `text`) + 无 `audio` |
|
2026-04-29 21:56:47 +08:00
|
|
|
|
|
|
|
|
|
|
### pipeline.phases 整体状态
|
|
|
|
|
|
|
|
|
|
|
|
每个阶段有独立状态:`pending` → `running` → `done` / `partial` / `failed`
|
|
|
|
|
|
|
|
|
|
|
|
- `done` — 全部 item 成功
|
|
|
|
|
|
- `partial` — 部分 item 失败(其他成功)
|
|
|
|
|
|
- `failed` — 阶段整体异常中断
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 失败处理
|
|
|
|
|
|
|
|
|
|
|
|
用 `--retry-failed` 一条命令搞定。
|
|
|
|
|
|
|
|
|
|
|
|
### 根据失败阶段选择操作
|
|
|
|
|
|
|
|
|
|
|
|
**图片生成失败**(images 阶段 partial):
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# 只改 prompt 不改图片风格 → 重试即可
|
2026-05-01 02:48:37 +08:00
|
|
|
|
node scripts/pipeline.js run --manifest <path> --phase images --retry-failed
|
2026-04-29 21:56:47 +08:00
|
|
|
|
|
|
|
|
|
|
# 需要换 prompt → 先改 item.imagePrompt,再重试
|
|
|
|
|
|
# (改完后跑上面同一条命令)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**视频生成失败**(videos 阶段 partial):
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# API 临时故障、网络超时 → 直接重试
|
2026-05-01 02:48:37 +08:00
|
|
|
|
node scripts/pipeline.js run --manifest <path> --phase videos --retry-failed
|
2026-04-29 21:56:47 +08:00
|
|
|
|
|
|
|
|
|
|
# 提示词问题 → 先改 item.videoPrompt,再重试
|
|
|
|
|
|
# (改完后跑上面同一条命令)
|
|
|
|
|
|
|
|
|
|
|
|
# 视频模型不可用 → 改 manifest.videoModel 或 account.json,再重试
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**全阶段重试**:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
2026-05-01 02:48:37 +08:00
|
|
|
|
node scripts/pipeline.js run --manifest <path> --retry-failed
|
2026-04-29 21:56:47 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### `--retry-failed` 内部行为
|
|
|
|
|
|
|
|
|
|
|
|
1. 扫描所有 `status=failed` 或 `status=partial` 的 item
|
|
|
|
|
|
2. 根据已有字段自动判断应重置到哪个阶段:
|
|
|
|
|
|
- 有 `url` + `videoPrompt` + 无 `video` → 重置为可生视频(`status=done`)
|
|
|
|
|
|
- 无 `url` + 有 `imagePrompt` → 重置为可生图(`status=pending`)
|
|
|
|
|
|
3. 对应 `pipeline.phases` 重置为 `pending`
|
|
|
|
|
|
4. 清除 `error` 字段
|
|
|
|
|
|
5. 正常执行指定阶段
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-04-29 21:04:43 +08:00
|
|
|
|
## 首尾帧模式
|
|
|
|
|
|
|
|
|
|
|
|
`mode: "framePair"` 时,`imagePrompt` 作为起始帧,每个 item 额外字段:
|
|
|
|
|
|
|
|
|
|
|
|
| 字段 | 说明 | 谁填充 |
|
|
|
|
|
|
|------|------|--------|
|
|
|
|
|
|
| `imagePrompt` | 起始帧画面描述(与 single 模式复用同一字段) | AI |
|
|
|
|
|
|
| `lastFramePrompt` | 结束帧画面描述 | AI |
|
|
|
|
|
|
| `lastFrame` | 结束帧图片路径 | **pipeline images 回写** |
|
|
|
|
|
|
| `lastFrameUrl` | 结束帧 OSS URL | **pipeline upload 回写** |
|
|
|
|
|
|
|
|
|
|
|
|
**首尾帧规则**:同一场景、视角一致、状态对比。VEO 检测到 `lastFrameUrl` 自动启用双图模式。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 目录结构
|
|
|
|
|
|
|
|
|
|
|
|
```
|
2026-05-02 00:14:40 +08:00
|
|
|
|
output/{name}_{YYYYMMDD}_{NNN}/
|
2026-04-29 21:04:43 +08:00
|
|
|
|
├── manifest.json # 主清单
|
2026-04-30 21:18:31 +08:00
|
|
|
|
├── images/ # scene_{NN}_{slug}.jpeg(首尾帧加 _last,MJ 候选加 _cand{1-4})
|
|
|
|
|
|
├── videos/ # scene_{NN}_{slug}.mp4
|
2026-04-29 21:04:43 +08:00
|
|
|
|
└── audio/ # seg_001.mp3
|
|
|
|
|
|
```
|
2026-04-30 21:18:31 +08:00
|
|
|
|
|
|
|
|
|
|
slug 从 `shotDesc` 派生(slugify: 保留中文和字母数字,最多 20 字符)。
|
2026-05-01 14:41:28 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## segments[] 字段(TTS 分句)
|
|
|
|
|
|
|
2026-05-02 00:14:40 +08:00
|
|
|
|
TTS 阶段统一生成,单句时数组仅 1 个元素,多句时 N 个元素。assemble 阶段直接使用各 segment 的实际音频时长对齐字幕。
|
2026-05-01 14:41:28 +08:00
|
|
|
|
|
|
|
|
|
|
| 字段 | 说明 |
|
|
|
|
|
|
|------|------|
|
|
|
|
|
|
| `text` | 分句文本(已去除标点) |
|
|
|
|
|
|
| `audio` | 该句音频路径(相对 manifest) |
|
|
|
|
|
|
| `duration` | 该句音频时长(秒) |
|
|
|
|
|
|
|
2026-05-02 00:14:40 +08:00
|
|
|
|
`item.audio` 指向 `segments[0].audio`,`item.audioDuration` 为各段累计时长。assemble 阶段遍历 segments 逐一添加音频和字幕,使用实际文件时长(非比例分配),确保音频与字幕精确同步,消除留白。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 成片时间线规则
|
|
|
|
|
|
|
2026-05-06 22:53:37 +08:00
|
|
|
|
> **核心原则**:
|
|
|
|
|
|
> - 文案是时间轴唯一锚点
|
|
|
|
|
|
> - TTS 语速固定 1.15x(写死在 qwen-tts.js),音频导入 CapCut 时不可调速
|
|
|
|
|
|
> - **音频时长是主时间线**:每个 shot 的 TTS 估算必须 ≤ 视频模型固定时长
|
|
|
|
|
|
> - **视频必须 ≥ 音频**:audioDur > videoDur 的 shot 在分镜阶段必须拆分,不允许慢放/冻结
|
|
|
|
|
|
|
|
|
|
|
|
### 时间线估算规则
|
|
|
|
|
|
|
|
|
|
|
|
| 字段 | 计算方式 | 来源 |
|
|
|
|
|
|
|------|---------|------|
|
|
|
|
|
|
| TTS 语速 | **固定 1.15x** | qwen-tts.js 参数 `rate: 1.15`,不可修改 |
|
|
|
|
|
|
| 单 shot TTS 估算 | `script.length ÷ 5`(字/秒) | AI 写入 duration 字段 |
|
|
|
|
|
|
| 视频模型固定时长 | Kling=6s, VEO=8s, Grok=6s | `pipeline.js init` 从 videoModel 推算 |
|
|
|
|
|
|
| ratio | `estimatedVideoDuration / estimatedAudioDuration` | 估算值,供分镜阶段检查 |
|
|
|
|
|
|
| ratio(实测) | `videoDuration / audioDuration` | assemble 阶段真实值 |
|
|
|
|
|
|
|
2026-05-02 00:14:40 +08:00
|
|
|
|
### 图片模式(images)
|
|
|
|
|
|
|
|
|
|
|
|
图片没有独立时长。TTS 音频时长 = 画面时长。无 TTS 音频的 item 时长为 0(跳过,不显示)。
|
|
|
|
|
|
|
|
|
|
|
|
### 视频模式(videos)
|
|
|
|
|
|
|
2026-05-06 22:53:37 +08:00
|
|
|
|
**铁律:视频片段必须 ≥ 音频片段。**
|
|
|
|
|
|
|
|
|
|
|
|
TTS 音频为主轴,视频通过以下策略适配音频实测时长:
|
2026-05-02 00:14:40 +08:00
|
|
|
|
|
2026-05-06 22:53:37 +08:00
|
|
|
|
| ratio = estimatedVideoDuration / estimatedAudioDuration | 策略 | 说明 |
|
|
|
|
|
|
|---------------------------------------------------|------|------|
|
2026-05-02 00:14:40 +08:00
|
|
|
|
| 0.9 ~ 1.1 | none | 接近匹配,无需调整 |
|
2026-05-06 22:53:37 +08:00
|
|
|
|
| > 1.1, ≤ 2 | **speed_up**(最优) | 视频加速追上音频,音频速率不变 |
|
|
|
|
|
|
| > 2 | **trim**(次选) | 视频截断至音频时长,损失尾部 |
|
|
|
|
|
|
| < 0.9 | **禁止 / 打回分镜** | audioDur > videoDur 的 shot 在分镜阶段必须拆分,不允许慢放/冻结 |
|
|
|
|
|
|
|
|
|
|
|
|
**禁止的策略(已删除):**
|
|
|
|
|
|
- `slow_down`:音频时长超过视频时不允许慢放
|
|
|
|
|
|
- `freeze`:不允许冻结帧补齐
|
|
|
|
|
|
- 音频调速:CapCut 导入音频时无 speed 字段,1.15x 速率固定
|
2026-05-02 00:14:40 +08:00
|
|
|
|
|
|
|
|
|
|
所有策略失败后兜底:截断到目标时长。
|