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:
2026-05-01 16:00:01 +08:00
parent f2bc0df447
commit 350f05fc3f
4 changed files with 76 additions and 33 deletions

View File

@@ -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('|')
}