Files
video-create/.claude/skills/video-from-script/references/manifest-schema.md
sion123 0998fd6ae1 feat(video-pipeline): 重构视频流水线,优化成片时间线规则和状态管理
- 引入 manifest.json 作为唯一状态源,所有子 Agent 操作回写 manifest
- 重构 timebuilder 逻辑,支持四种视频适配策略(加速/裁剪/放缓/画面停顿)
- 统一 TTS 阶段输出结构,单句和多句均写入 segments[]
- 重写字幕和配音生成,基于 segments 精确时长实现音画同步
- 新增 confirm 命令支持按 id 范围确认,上传阶段分离图片和视频
- 添加中间产物写入 output/ 目录的约束,清理废弃配置参数
2026-05-02 00:14:40 +08:00

240 lines
8.5 KiB
Markdown
Raw 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.
# manifest.json 规范
> `pipeline.js init` 创建Pipeline 执行Agent 审查。
>
> **禁止 AI 手写 manifest.json**,必须通过 `pipeline.js init` 初始化。脚本从 account.json 自动继承结构字段AI 只提供创意内容items 的 shotDesc/script/imagePrompt 等)。
---
## 创建方式
```bash
# Step 2-0分镜确认后立即初始化imagePrompt/videoPrompt 后续补充)
node scripts/pipeline.js init --account 军事账号 --mode single \
--items '[{"shotDesc":"英文画面描述","script":"中文口播文案","duration":5,"directorRef":"tarantino","keyword":"权力"}]'
# 或从文件读取
node scripts/pipeline.js init --account 军事账号 --mode single --items-file ./items.json
# Step 2-C 人工确认
node scripts/pipeline.js confirm --manifest <path> --all
node scripts/pipeline.js confirm --manifest <path> --items 1,3,5
# 校验已有 manifest
node scripts/pipeline.js validate --manifest <path>
```
---
## 顶层字段
| 字段 | 说明 | 来源 | 谁填充 |
|------|------|------|--------|
| `account` | 账号 ID | account.json | **init 自动** |
| `imageModel` | `gemini` / `mj` | account.json | **init 自动** |
| `videoModel` | `veo3-fast-frames` / `grok-video-3` / `kling` 等 | account.json | **init 自动** |
| `format` | 画幅:`9:16` / `16:9` | account.json | **init 自动** |
| `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"` |
| `shotDesc` | 英文分镜描述含隐性动势40-80词 |
| `script` | **该段的完整原文案**(不提炼,保留论证、例子、细节)|
| `duration` | 计划视频时长(秒),来自分镜阶段 |
| `imagePrompt` | 英文画面描述(给 Gemini/MJStep 2-A 生成 |
| `directorRef` | 导演构图参考tarantino / kitano / fincher三层透传 |
| `keyword` | 关键字氛围词2-6 字assemble 时以花字效果叠加在画面中央。可选 |
| `confirmed` | 人工确认状态,默认 `false` |
### Agent 后续回写Step 3-A 视频提示词)
| 字段 | 说明 | 写入时机 |
|------|------|---------|
| `videoPrompt` | 英文运动描述(给 Grok/VEO描述镜头运动而非内容 | Step 3-A 由 Agent 回写 |
### Pipeline 回写(执行后)
| 字段 | 说明 | 写入阶段 |
|------|------|---------|
| `status` | `pending``generating``done` / `failed` | images |
| `file` | 生成的图片路径(相对 manifest | images |
| `candidates` | MJ 拆分的 4 张候选图路径Gemini 无此字段) | images |
| `url` | 图片 OSS 公网 URL | upload |
| `confirmed` | 人工确认后设为 `true` | confirm |
| `video` | 生成的视频路径 | videos |
| `videoDuration` | 视频时长Grok=6, VEO=8 | videos |
| `videoUrl` | 视频 OSS 公网 URL | videos |
| `audio` | TTS 音频路径(多句时为合并后的完整音频) | tts |
| `audioDuration` | 音频时长(秒) | tts |
| `segments` | 分句音频数组(仅多句时存在),见下方 | tts |
### Agent 审查时可操作
- MJ 换选:`item.file = item.candidates[2]`
- 删除不合格 item直接从 items 数组移除,重新跑 `--phase images`
- 调整 prompt 重跑:改 `imagePrompt`status 改回 `pending`
- 人工确认:`node scripts/pipeline.js confirm --manifest <path> --all`
---
## 状态机
### item 生命周期
```
pending → [images] → done → [confirm] → confirmed=true → [upload: url填入] → [videos] → done → [tts] → done
↓ ↓
failed failed + error
```
status 一旦进入 `done` 就不再回退。后续阶段通过检查"有前置字段 + 无后置字段"来识别待处理 item不依赖 status 变化。
### 各阶段拾取条件
Agent **不需要记住这些条件**pipeline 内部自动匹配。仅供理解原理:
| 阶段 | item 被拾取的条件 |
|------|------------------|
| images | `status=pending` + 有 `imagePrompt` |
| upload | `status=done` + 有 `file` + 无 `url` |
| videos | `status=done` + `confirmed=true` + 有 `url` + 有 `videoPrompt` + 无 `video` |
| tts | `status=done` + 有 `script`(回退 `text` + 无 `audio` |
### pipeline.phases 整体状态
每个阶段有独立状态:`pending``running``done` / `partial` / `failed`
- `done` — 全部 item 成功
- `partial` — 部分 item 失败(其他成功)
- `failed` — 阶段整体异常中断
---
## 失败处理
`--retry-failed` 一条命令搞定。
### 根据失败阶段选择操作
**图片生成失败**images 阶段 partial
```bash
# 只改 prompt 不改图片风格 → 重试即可
node scripts/pipeline.js run --manifest <path> --phase images --retry-failed
# 需要换 prompt → 先改 item.imagePrompt再重试
# (改完后跑上面同一条命令)
```
**视频生成失败**videos 阶段 partial
```bash
# API 临时故障、网络超时 → 直接重试
node scripts/pipeline.js run --manifest <path> --phase videos --retry-failed
# 提示词问题 → 先改 item.videoPrompt再重试
# (改完后跑上面同一条命令)
# 视频模型不可用 → 改 manifest.videoModel 或 account.json再重试
```
**全阶段重试**
```bash
node scripts/pipeline.js run --manifest <path> --retry-failed
```
### `--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. 正常执行指定阶段
---
## 首尾帧模式
`mode: "framePair"` 时,`imagePrompt` 作为起始帧,每个 item 额外字段:
| 字段 | 说明 | 谁填充 |
|------|------|--------|
| `imagePrompt` | 起始帧画面描述(与 single 模式复用同一字段) | AI |
| `lastFramePrompt` | 结束帧画面描述 | AI |
| `lastFrame` | 结束帧图片路径 | **pipeline images 回写** |
| `lastFrameUrl` | 结束帧 OSS URL | **pipeline upload 回写** |
**首尾帧规则**同一场景、视角一致、状态对比。VEO 检测到 `lastFrameUrl` 自动启用双图模式。
---
## 目录结构
```
output/{name}_{YYYYMMDD}_{NNN}/
├── manifest.json # 主清单
├── images/ # scene_{NN}_{slug}.jpeg首尾帧加 _lastMJ 候选加 _cand{1-4}
├── videos/ # scene_{NN}_{slug}.mp4
└── audio/ # seg_001.mp3
```
slug 从 `shotDesc` 派生slugify: 保留中文和字母数字,最多 20 字符)。
---
## segments[] 字段TTS 分句)
TTS 阶段统一生成,单句时数组仅 1 个元素,多句时 N 个元素。assemble 阶段直接使用各 segment 的实际音频时长对齐字幕。
| 字段 | 说明 |
|------|------|
| `text` | 分句文本(已去除标点) |
| `audio` | 该句音频路径(相对 manifest |
| `duration` | 该句音频时长(秒) |
`item.audio` 指向 `segments[0].audio``item.audioDuration` 为各段累计时长。assemble 阶段遍历 segments 逐一添加音频和字幕,使用实际文件时长(非比例分配),确保音频与字幕精确同步,消除留白。
---
## 成片时间线规则
### 图片模式images
图片没有独立时长。TTS 音频时长 = 画面时长。无 TTS 音频的 item 时长为 0跳过不显示
### 视频模式videos
TTS 音频为主轴,视频通过以下策略适配音频时长:
| ratio = videoDur/audioDur | 策略 | 说明 |
|---------------------------|------|------|
| 0.9 ~ 1.1 | none | 接近匹配,无需调整 |
| > 1.1, ≤ 2 | speed_up | 加速setpts 压缩时间) |
| > 2 | trim | 裁剪(截断到音频时长) |
| < 0.9, ≥ 0.5 | slow_down | 放缓setpts 拉长时间) |
| < 0.5 | freeze | 画面停顿(视频原速 + 最后一帧冻结补时长) |
所有策略失败后兜底:截断到目标时长。