feat(skills): 完善视频生产 pipeline 及新增健身跟练账号
- SKILL.md: 新增工作流阶段定义、质量卡点、分镜规则 - manifest-schema.md: 补充完整字段规范及类型定义 - phase-tts.js: 优化 TTS 合成长逻辑,添加进度追踪 - capcut-tracks.js: 扩展轨道构建能力,支持更多元素类型 - capcut-timeline.js: 改进时间线生成,支持淡入淡出 - capcut_assemble.js: 新增 assemble 阶段完整实现 - cmd-init.js: 完善 init 命令逻辑 - qwen-tts.js: 调整超时配置 - accounts/禁忌帝王学: 更新拆分/图像/台词提示词 - accounts/健身跟练: 新增账号含 account.json 及全套提示词模板 - 新增 workflow-issues-20260501.md 参考文档 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -34,6 +34,7 @@ node scripts/pipeline.js validate --manifest <path>
|
||||
| `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 自动** |
|
||||
| `estimatedVideoDuration` | 视频模型固定时长(秒),顶层冗余字段 | videoModel 查表 | **init 自动**,assemble 直接读 |
|
||||
| `mode` | `single` 单图 / `framePair` 首尾帧 | CLI 参数 | **init 自动** |
|
||||
| `references` | 参考图数组,从 account.json styles.*.references 搬入 | account.json | **init 自动** |
|
||||
| `items` | 素材数组(AI 提供创意内容) | CLI --items | **AI → init** |
|
||||
@@ -58,18 +59,29 @@ node scripts/pipeline.js validate --manifest <path>
|
||||
|------|------|
|
||||
| `status` | 固定写 `"pending"` |
|
||||
| `shotDesc` | 英文分镜描述(含隐性动势,40-80词) |
|
||||
| `script` | **该段的完整原文案**(不提炼,保留论证、例子、细节)|
|
||||
| `duration` | 计划视频时长(秒),来自分镜阶段 |
|
||||
| `script` | **该 shot 的语义子句原文**(完整句拆分后的子段,一字不差)|
|
||||
| `duration` | **TTS 估算秒数(= script字数÷5)**,必须 ≤ 6s |
|
||||
| `estimatedAudioDuration` | 同 duration,备选别名 |
|
||||
| `estimatedVideoDuration` | 视频模型固定时长(Kling=6s, VEO=8s, Grok=6s),pipeline init 时自动填入 |
|
||||
| `imagePrompt` | 英文画面描述(给 Gemini/MJ),Step 2-A 生成 |
|
||||
| `directorRef` | 导演构图参考(tarantino / kitano / fincher),三层透传 |
|
||||
| `keyword` | 关键字氛围词(2-6 字),assemble 时以花字效果叠加在画面中央。可选 |
|
||||
| `confirmed` | 人工确认状态,默认 `false` |
|
||||
|
||||
**强制约束:**
|
||||
- **每个 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` 选择适配策略
|
||||
|
||||
### Agent 后续回写(Step 3-A 视频提示词)
|
||||
|
||||
| 字段 | 说明 | 写入时机 |
|
||||
|------|------|---------|
|
||||
| `videoPrompt` | 英文运动描述(给 Grok/VEO),描述镜头运动而非内容 | Step 3-A 由 Agent 回写 |
|
||||
| `videoPrompt` | 英文运动描述(给 Grok/VEO/Kling),描述镜头运动而非内容 | Step 3-A 由 Agent 回写 |
|
||||
|
||||
### Pipeline 回写(执行后)
|
||||
|
||||
@@ -81,10 +93,10 @@ node scripts/pipeline.js validate --manifest <path>
|
||||
| `url` | 图片 OSS 公网 URL | upload |
|
||||
| `confirmed` | 人工确认后设为 `true` | confirm |
|
||||
| `video` | 生成的视频路径 | videos |
|
||||
| `videoDuration` | 视频时长(秒),Grok=6, VEO=8 | videos |
|
||||
| `videoDuration` | 视频实测时长(秒),Kling=6, VEO=8, Grok=6 | videos |
|
||||
| `videoUrl` | 视频 OSS 公网 URL | videos |
|
||||
| `audio` | TTS 音频路径(多句时为合并后的完整音频) | tts |
|
||||
| `audioDuration` | 音频时长(秒) | tts |
|
||||
| `audio` | TTS 音频路径 | tts |
|
||||
| `audioDuration` | 音频实测时长(秒) | tts |
|
||||
| `segments` | 分句音频数组(仅多句时存在),见下方 | tts |
|
||||
|
||||
### Agent 审查时可操作
|
||||
@@ -220,20 +232,42 @@ TTS 阶段统一生成,单句时数组仅 1 个元素,多句时 N 个元素
|
||||
|
||||
## 成片时间线规则
|
||||
|
||||
> **核心原则**:
|
||||
> - 文案是时间轴唯一锚点
|
||||
> - 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 阶段真实值 |
|
||||
|
||||
### 图片模式(images)
|
||||
|
||||
图片没有独立时长。TTS 音频时长 = 画面时长。无 TTS 音频的 item 时长为 0(跳过,不显示)。
|
||||
|
||||
### 视频模式(videos)
|
||||
|
||||
TTS 音频为主轴,视频通过以下策略适配音频时长:
|
||||
**铁律:视频片段必须 ≥ 音频片段。**
|
||||
|
||||
| ratio = videoDur/audioDur | 策略 | 说明 |
|
||||
|---------------------------|------|------|
|
||||
TTS 音频为主轴,视频通过以下策略适配音频实测时长:
|
||||
|
||||
| ratio = estimatedVideoDuration / estimatedAudioDuration | 策略 | 说明 |
|
||||
|---------------------------------------------------|------|------|
|
||||
| 0.9 ~ 1.1 | none | 接近匹配,无需调整 |
|
||||
| > 1.1, ≤ 2 | speed_up | 加速(setpts 压缩时间) |
|
||||
| > 2 | trim | 裁剪(截断到音频时长) |
|
||||
| < 0.9, ≥ 0.5 | slow_down | 放缓(setpts 拉长时间) |
|
||||
| < 0.5 | freeze | 画面停顿(视频原速 + 最后一帧冻结补时长) |
|
||||
| > 1.1, ≤ 2 | **speed_up**(最优) | 视频加速追上音频,音频速率不变 |
|
||||
| > 2 | **trim**(次选) | 视频截断至音频时长,损失尾部 |
|
||||
| < 0.9 | **禁止 / 打回分镜** | audioDur > videoDur 的 shot 在分镜阶段必须拆分,不允许慢放/冻结 |
|
||||
|
||||
**禁止的策略(已删除):**
|
||||
- `slow_down`:音频时长超过视频时不允许慢放
|
||||
- `freeze`:不允许冻结帧补齐
|
||||
- 音频调速:CapCut 导入音频时无 speed 字段,1.15x 速率固定
|
||||
|
||||
所有策略失败后兜底:截断到目标时长。
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
# 图文成片工作流问题记录
|
||||
|
||||
## 问题一:manifest 初始化缺少 file 字段
|
||||
|
||||
**现象:**
|
||||
```
|
||||
[assemble] 成片失败: The "path" argument must be of type string. Received undefined
|
||||
```
|
||||
|
||||
**根因:**
|
||||
`pipeline.js init` 生成的 manifest.json 中,每个 item 只有 `shotDesc`、`script`、`duration` 等字段,**缺少 `file` 字段**。
|
||||
|
||||
`capcut_assemble.js` 依赖 `item.file` 来定位本地图片:
|
||||
```js
|
||||
const filePath = path.join(inputDir, item.file)
|
||||
return fs.existsSync(filePath)
|
||||
```
|
||||
|
||||
没有 `file` 字段时,`path.join(inputDir, undefined)` → `undefined` → 报错。
|
||||
|
||||
**修复:**
|
||||
手动给每个 item 补上 `file` 字段:
|
||||
```js
|
||||
m.items.forEach((item, i) => {
|
||||
item.file = 'images/scene_' + String(i+1).padStart(2,'0') + '_' + slug + '.jpeg'
|
||||
})
|
||||
```
|
||||
|
||||
**建议改进:**
|
||||
`pipeline.js init` 应自动根据 items 索引和 slug 生成 `file` 字段,与后续 assemble 阶段无缝衔接。
|
||||
|
||||
---
|
||||
|
||||
## 问题二:并行生图命令的 cwd 问题
|
||||
|
||||
**现象:**
|
||||
```bash
|
||||
# 6 个并行命令,其中 5 个报错
|
||||
cd .claude/skills/video-from-script/scripts && node gemini-image-generator.js ...
|
||||
# Exit code 1: no such file or directory
|
||||
```
|
||||
|
||||
**根因:**
|
||||
5 个并行命令在解析时,zsh 把 `.claude/...` 路径中 `.` 视为当前目录的相对路径,而并行任务可能在不同 cwd 下执行,导致路径解析失败。
|
||||
|
||||
**修复:**
|
||||
改用绝对路径:
|
||||
```bash
|
||||
SCRIPTS="/Users/lc/Desktop/CLAUDE/video-create/.claude/skills/video-from-script/scripts"
|
||||
node "$SCRIPTS/gemini-image-generator.js" generate "..." -o "$OUT" -r 9:16
|
||||
```
|
||||
|
||||
**建议改进:**
|
||||
CLI 命令始终使用绝对路径,避免相对路径在并行环境下的歧义。
|
||||
|
||||
---
|
||||
|
||||
## 问题三:shot 6 在 mv 重命名时被遗漏
|
||||
|
||||
**现象:**
|
||||
6 张图生完后,重命名时只有 5 张被正确命名为 `scene_0X_xxx.jpeg`,缺少 `scene_06_跪着.jpeg`。
|
||||
|
||||
**根因:**
|
||||
mv 命令基于修改时间排序,zsh glob `generated_*.jpeg` 只匹配当时存在的文件。6张图的生成时间戳不同,重命名脚本从最旧的文件开始取(tail -1),但脚本顺序与实际时间顺序可能不匹配。
|
||||
|
||||
**修复:**
|
||||
直接对生成的临时文件重命名为固定名称,不依赖时间排序逻辑。
|
||||
|
||||
**建议改进:**
|
||||
pipeline.js 的生图阶段应直接输出为 `scene_{NN}_{slug}.jpeg`,而非先生成 `generated_*.jpeg` 再重命名。
|
||||
|
||||
---
|
||||
|
||||
## 问题四:pipeline.js assemble 阶段的路径解析 Bug
|
||||
|
||||
**现象:**
|
||||
`pipeline.js run --phase tts,assemble` 时 tts 正常,但 assemble 阶段找不到文件而报错。直接调用 `capcut_assemble.js --input <dir>` 则正常。
|
||||
|
||||
**根因:**
|
||||
pipeline.js 在调用 assemble 时,传递的 input 路径为相对路径,且未正确设置 `item.file` 字段,导致 assemble 内部 `path.join(inputDir, item.file)` 得到 undefined。
|
||||
|
||||
**建议改进:**
|
||||
`pipeline.js run --phase assemble` 前应先检查 items 是否都有 `file` 字段,缺失时自动补充。
|
||||
|
||||
---
|
||||
|
||||
## 建议的改进方向
|
||||
|
||||
1. **`pipeline.js init`** 自动生成 `file` 字段,与图片命名规范一致
|
||||
2. **CLI 命令** 统一使用绝对路径,避免 cwd 歧义
|
||||
3. **生图脚本** 直接输出为 `scene_XX_xxx.jpeg`,消除重命名步骤
|
||||
4. **`pipeline.js validate`** 增加 assemble 阶段的前置检查(items.file + items.audio 完整性)
|
||||
Reference in New Issue
Block a user