From 350f05fc3f396dacbb9cd93580f3d31756c7c5e4 Mon Sep 17 00:00:00 2001 From: sion123 <450702724@qq.com> Date: Fri, 1 May 2026 16:00:01 +0800 Subject: [PATCH] =?UTF-8?q?feat(video-pipeline):=20=E5=A2=9E=E5=BC=BA?= =?UTF-8?q?=E8=B4=A6=E5=8F=B7=E9=85=8D=E7=BD=AE=E5=92=8C=E5=88=86=E9=95=9C?= =?UTF-8?q?=E5=85=B3=E9=94=AE=E8=AF=8D=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 account.json 中新增 styles、ttsVoice、ttsInstruction、keywordStyle、transitions 等配置字段 - 分镜表 items 添加 keyword 字段,用于渲染关键词花字动画 - 重构 capcut_assemble.js 转场策略:前两段不加转场,rhythm 模式优化为只在关键位置添加 - 动画系统区分循环动画和入场动画,支持更丰富的动画组合(缩放、回弹伸缩等) - 更新 SKILL.md 和 manifest-schema.md 文档以反映新增字段和流程变更 --- .claude/skills/video-from-script/SKILL.md | 10 ++- .../references/account-system.md | 71 +++++++++++++++---- .../references/manifest-schema.md | 2 +- .../scripts/capcut_assemble.js | 26 +++---- 4 files changed, 76 insertions(+), 33 deletions(-) diff --git a/.claude/skills/video-from-script/SKILL.md b/.claude/skills/video-from-script/SKILL.md index 3781c6b..af08db7 100644 --- a/.claude/skills/video-from-script/SKILL.md +++ b/.claude/skills/video-from-script/SKILL.md @@ -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 --mode \ - --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 # 初始化 manifest(Step 2-B 使用,AI 只提供创意内容,不含 videoPrompt) node scripts/pipeline.js init --account --mode \ - --items '[{"shotDesc":"...","script":"...","duration":5,"imagePrompt":"...","directorRef":"tarantino"}]' + --items '[{"shotDesc":"...","script":"...","duration":5,"imagePrompt":"...","directorRef":"tarantino","keyword":"权力"}]' # 也可从文件读取 items(适合大量数据) node scripts/pipeline.js init --account --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` — 账号系统说明 diff --git a/.claude/skills/video-from-script/references/account-system.md b/.claude/skills/video-from-script/references/account-system.md index d27ae0d..121d62e 100644 --- a/.claude/skills/video-from-script/references/account-system.md +++ b/.claude/skills/video-from-script/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 | --- diff --git a/.claude/skills/video-from-script/references/manifest-schema.md b/.claude/skills/video-from-script/references/manifest-schema.md index 3721bbf..b03d9a8 100644 --- a/.claude/skills/video-from-script/references/manifest-schema.md +++ b/.claude/skills/video-from-script/references/manifest-schema.md @@ -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 diff --git a/.claude/skills/video-from-script/scripts/capcut_assemble.js b/.claude/skills/video-from-script/scripts/capcut_assemble.js index ea15ced..bbfa06c 100644 --- a/.claude/skills/video-from-script/scripts/capcut_assemble.js +++ b/.claude/skills/video-from-script/scripts/capcut_assemble.js @@ -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('|') }