feat(video-pipeline): 增强账号配置和分镜关键词支持
- 在 account.json 中新增 styles、ttsVoice、ttsInstruction、keywordStyle、transitions 等配置字段 - 分镜表 items 添加 keyword 字段,用于渲染关键词花字动画 - 重构 capcut_assemble.js 转场策略:前两段不加转场,rhythm 模式优化为只在关键位置添加 - 动画系统区分循环动画和入场动画,支持更丰富的动画组合(缩放、回弹伸缩等) - 更新 SKILL.md 和 manifest-schema.md 文档以反映新增字段和流程变更
This commit is contained in:
@@ -120,7 +120,7 @@ Step 1: 分镜脚本生成(子 Agent 执行)
|
||||
- 主 Agent 将用户文案 + 模板交给子 Agent
|
||||
- 子 Agent 按模板要求输出分镜表 JSON:
|
||||
```json
|
||||
[{"id":1,"shotDesc":"英文画面描述,40-80词","script":"中文口播文案,≤22字","duration":5,"directorRef":"tarantino"}]
|
||||
[{"id":1,"shotDesc":"英文画面描述,40-80词","script":"中文口播文案,≤22字","duration":5,"directorRef":"tarantino","keyword":"权力"}]
|
||||
```
|
||||
- 主 Agent 审查分镜表(时长合理、隐性动势完整、directorRef 已填)
|
||||
- 展示给用户确认,确认后进入 Step 2-A
|
||||
@@ -136,7 +136,7 @@ Step 2-B: 生成静态分镜图 + Manifest 初始化
|
||||
- 组装 items 并初始化 manifest(**不含 videoPrompt**):
|
||||
```bash
|
||||
node scripts/pipeline.js init --account <id> --mode <single|framePair> \
|
||||
--items '[{"shotDesc":"...","script":"...","duration":5,"imagePrompt":"...","directorRef":"tarantino"}]'
|
||||
--items '[{"shotDesc":"...","script":"...","duration":5,"imagePrompt":"...","directorRef":"tarantino","keyword":"权力"}]'
|
||||
```
|
||||
- 脚本自动从 account.json 继承:imageModel、videoModel、format、references
|
||||
- 所有 item.confirmed = false
|
||||
@@ -183,7 +183,7 @@ node scripts/pipeline.js validate-account --account <id>
|
||||
|
||||
# 初始化 manifest(Step 2-B 使用,AI 只提供创意内容,不含 videoPrompt)
|
||||
node scripts/pipeline.js init --account <id> --mode <single|framePair> \
|
||||
--items '[{"shotDesc":"...","script":"...","duration":5,"imagePrompt":"...","directorRef":"tarantino"}]'
|
||||
--items '[{"shotDesc":"...","script":"...","duration":5,"imagePrompt":"...","directorRef":"tarantino","keyword":"权力"}]'
|
||||
# 也可从文件读取 items(适合大量数据)
|
||||
node scripts/pipeline.js init --account <id> --mode single --items-file ./items.json
|
||||
|
||||
@@ -354,8 +354,6 @@ output/{name}_{YYYYMMDD}_{NNN}/
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## 质量卡点(Agent 可执行)
|
||||
|
||||
每个阶段完成后 Agent 自动校验,不通过的自动修复。需要人工视觉判断的(画质、动画、BGM等)由用户在人工审查步骤处理。
|
||||
@@ -395,7 +393,7 @@ output/{name}_{YYYYMMDD}_{NNN}/
|
||||
|
||||
所有子技能共享以下资源(位于本目录):
|
||||
|
||||
- `scripts/` — 共享脚本(gemini-image-generator.js, mj-image-generator.js, grok-video-generator.js, veo-video-generator.js, kling-video-generator.js, qwen-tts.js, capcut_assemble.js, sync-to-jianying.js, oss-upload.js)
|
||||
- `scripts/` — 共享脚本(生图、生视频、TTS、成片组装、同步剪映、OSS 上传等)
|
||||
- `accounts/` — 账号配置(项目根目录,详见 [account-system.md](references/account-system.md))
|
||||
- `references/account-system.md` — 账号系统说明
|
||||
|
||||
|
||||
@@ -37,25 +37,62 @@ accounts/ # 项目根目录下
|
||||
"id": "tech-talk",
|
||||
"name": "科技解说",
|
||||
"description": "科技类短视频账号,深色背景,赛博朋克风格",
|
||||
"pipeline": "image-video",
|
||||
"defaultFormat": "9:16",
|
||||
"imageModel": "gemini",
|
||||
"videoModel": "kling",
|
||||
"videoModel": "veo3-fast",
|
||||
"batchSize": 30,
|
||||
"styles": {
|
||||
"赛博风格": {
|
||||
"references": [
|
||||
{ "file": "cyber_ref.png", "url": "https://oss.../cyber_ref.png" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"ttsVoice": "cosyvoice-v3.5-plus-bailian-xxx",
|
||||
"ttsInstruction": "用冷静理性的男性声音朗读,语速适中",
|
||||
"storyboardPrompt": "prompts/分镜.md",
|
||||
"imageStylePrompt": "prompts/图片提示词.md",
|
||||
"videoStylePrompt": "prompts/视频提示词.md",
|
||||
"references": [
|
||||
{ "file": "ref_001.png", "url": "https://oss.../ref_001.png" }
|
||||
],
|
||||
"references": [],
|
||||
"capcut": {
|
||||
"effects": ["录制边框 III"],
|
||||
"filter": "电影感:40",
|
||||
"filter": "质感暗调:30",
|
||||
"subtitleStyle": {
|
||||
"fontSize": 36,
|
||||
"fontSize": 24,
|
||||
"color": "#FFFFFF",
|
||||
"highlightColor": "#FF6B35",
|
||||
"bold": true
|
||||
"bold": true,
|
||||
"hasShadow": true,
|
||||
"shadowColor": "#000000",
|
||||
"shadowAlpha": 0.8,
|
||||
"transformY": -380,
|
||||
"alignment": 1,
|
||||
"inAnimation": "淡入",
|
||||
"outAnimation": "淡出"
|
||||
},
|
||||
"defaultBGM": "https://example.com/bgm_tech.mp3"
|
||||
"keywordStyle": {
|
||||
"textEffect": "简约白色黑边花字",
|
||||
"fontSize": 60,
|
||||
"color": "#FFFFFF",
|
||||
"bold": true,
|
||||
"transformY": 0,
|
||||
"inAnimation": "打字机效果",
|
||||
"outAnimation": "模糊淡出",
|
||||
"inAnimDuration": 300000,
|
||||
"outAnimDuration": 300000
|
||||
},
|
||||
"transitions": {
|
||||
"strategy": "rhythm",
|
||||
"default": { "name": "闪白", "duration": 150000 },
|
||||
"byPosition": {
|
||||
"hook": { "name": "闪白", "duration": 100000 },
|
||||
"keypoint": { "name": "闪白", "duration": 120000 },
|
||||
"body": { "name": "溶解", "duration": 300000 },
|
||||
"closing": { "name": "闪黑", "duration": 200000 }
|
||||
}
|
||||
},
|
||||
"defaultBGM": ""
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -65,17 +102,23 @@ accounts/ # 项目根目录下
|
||||
| `id` | string | 账号唯一标识(与目录名一致) |
|
||||
| `name` | string | 账号显示名 |
|
||||
| `description` | string | 一句话描述 |
|
||||
| `defaultFormat` | string | 默认画幅(9:16 / 16:9 / 1:1 / 4:3) |
|
||||
| `imageModel` | string | 默认图片模型 |
|
||||
| `videoModel` | string | 默认视频模型 |
|
||||
| `pipeline` | string | 流水线类型:`image-video`(默认) |
|
||||
| `defaultFormat` | string | 默认画幅(`9:16` / `16:9` / `1:1` / `4:3`) |
|
||||
| `imageModel` | string | 默认图片模型(`gemini` / `mj`) |
|
||||
| `videoModel` | string | 默认视频模型(`veo3-fast` / `grok-video-3` / `kling`) |
|
||||
| `batchSize` | number | 默认批量生成数量 |
|
||||
| `styles` | object | 命名风格预设,每项含 `references` 数组 |
|
||||
| `ttsVoice` | string | TTS 音色 ID,留空用 config.json 全局默认 |
|
||||
| `ttsInstruction` | string | TTS 语气指令(描述期望的语气、语速、情感) |
|
||||
| `storyboardPrompt` | string | 分镜提示词模板路径(相对于账号目录) |
|
||||
| `imageStylePrompt` | string | 图片提示词模板路径(相对于账号目录) |
|
||||
| `videoStylePrompt` | string | 视频提示词模板路径(相对于账号目录) |
|
||||
| `references` | array | 参考图列表,每项含 file(本地文件名)和 url(OSS 公网地址) |
|
||||
| `references` | array | 默认参考图列表(每项含 `file` 和 `url`),已被 `styles` 取代 |
|
||||
| `capcut.effects` | string[] | CapCut 特效名称列表 |
|
||||
| `capcut.filter` | string | CapCut 滤镜,格式 "名称:强度" |
|
||||
| `capcut.subtitleStyle` | object | 字幕样式(字号、颜色、高亮色、加粗) |
|
||||
| `capcut.filter` | string | CapCut 滤镜,格式 `"名称:强度"`(如 `"质感电影:40"`) |
|
||||
| `capcut.subtitleStyle` | object | 字幕样式(font/字体、fontSize、color、highlightColor、bold、transformY、inAnimation/outAnimation) |
|
||||
| `capcut.keywordStyle` | object | 关键字氛围词样式(textEffect 花字、fontSize、color、动画),留空或删除则关闭 |
|
||||
| `capcut.transitions` | object | 转场配置(strategy + default + byPosition + byDirector) |
|
||||
| `capcut.defaultBGM` | string | 默认背景音乐 URL |
|
||||
|
||||
---
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
```bash
|
||||
# Step 2-A 生成 imagePrompt 后,通过脚本初始化(不含 videoPrompt)
|
||||
node scripts/pipeline.js init --account 军事账号 --mode single \
|
||||
--items '[{"shotDesc":"英文画面描述","script":"中文口播文案","duration":5,"imagePrompt":"English prompt","directorRef":"tarantino"}]'
|
||||
--items '[{"shotDesc":"英文画面描述","script":"中文口播文案","duration":5,"imagePrompt":"English prompt","directorRef":"tarantino","keyword":"权力"}]'
|
||||
|
||||
# 或从文件读取
|
||||
node scripts/pipeline.js init --account 军事账号 --mode single --items-file ./items.json
|
||||
|
||||
@@ -120,8 +120,8 @@ function getTransition(item, index, totalCount, transitionConfig) {
|
||||
const defaultT = transitionConfig.default || { name: '闪白', duration: 150000 }
|
||||
const strategy = transitionConfig.strategy || 'fixed'
|
||||
|
||||
// 第一个素材不加转场
|
||||
if (index === 0) return { name: '', duration: 0 }
|
||||
// 前两段不加转场(避免开头黑屏/闪烁)
|
||||
if (index <= 1) return { name: '', duration: 0 }
|
||||
|
||||
switch (strategy) {
|
||||
case 'director': {
|
||||
@@ -132,13 +132,11 @@ function getTransition(item, index, totalCount, transitionConfig) {
|
||||
}
|
||||
|
||||
case 'rhythm': {
|
||||
// 按位置选择转场(hook / body / keypoint / closing)
|
||||
// 按位置选择转场:只在 keypoint / closing 加转场,其余不加
|
||||
const rules = transitionConfig.byPosition || {}
|
||||
if (index === 1) return rules.hook || defaultT
|
||||
if (index >= totalCount - 2) return rules.closing || defaultT
|
||||
// 每隔3个 shot 用一个强调转场
|
||||
if (index % 3 === 0) return rules.keypoint || defaultT
|
||||
return rules.body || defaultT
|
||||
if (index % 4 === 0) return rules.keypoint || defaultT
|
||||
return { name: '', duration: 0 }
|
||||
}
|
||||
|
||||
case 'fixed':
|
||||
@@ -218,7 +216,7 @@ async function assemble(args) {
|
||||
format = '9:16',
|
||||
apiKey = '',
|
||||
duration = '4',
|
||||
animation = '渐显+放大',
|
||||
animation = '缩放',
|
||||
} = args
|
||||
|
||||
if (!input) throw new Error('缺少 --input 参数')
|
||||
@@ -533,12 +531,16 @@ async function addImages(draftUrl, items, imgUrls, timeline, width, height, anim
|
||||
transition_duration: t.duration,
|
||||
}
|
||||
|
||||
// animation 解析:循环动画 → loop_animation,其余 → in_animation
|
||||
// GroupAnimationType(循环):缩放, 缩放 II, 回弹伸缩, 旋转伸缩, ...
|
||||
// IntroType(入场):渐显, 放大, 缩小, 向右滑动, 轻微放大, ...
|
||||
// OutroType(出场):渐隐, 缩小, 放大, 向左滑动, ...
|
||||
if (animation) {
|
||||
const parts = animation.split('+').map(p => p.trim()).filter(Boolean)
|
||||
const groupNames = ['缩放', '缩放 II']
|
||||
const groupAnims = parts.filter(p => groupNames.includes(p))
|
||||
const inAnims = parts.filter(p => !groupNames.includes(p))
|
||||
if (groupAnims.length > 0) info.loop_animation = groupAnims.join('|')
|
||||
const loopNames = ['缩放', '缩放 II', '回弹伸缩', '旋转伸缩']
|
||||
const loopAnims = parts.filter(p => loopNames.includes(p))
|
||||
const inAnims = parts.filter(p => !loopNames.includes(p))
|
||||
if (loopAnims.length > 0) info.loop_animation = loopAnims.join('|')
|
||||
if (inAnims.length > 0) info.in_animation = inAnims.join('|')
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user