diff --git a/.claude/skills/video-from-script/references/account-creation.md b/.claude/skills/video-from-script/references/account-creation.md index 4206b24..f6b3ee4 100644 --- a/.claude/skills/video-from-script/references/account-creation.md +++ b/.claude/skills/video-from-script/references/account-creation.md @@ -93,6 +93,8 @@ Phase 4: 技术配置(有默认值,可跳过) | 16 | TTS 语气指令? | 无 | account.json 的 ttsInstruction,描述期望的语气风格 | | 17 | 关键字氛围词? | 开启(默认样式) | 画面中央大字叠加,增强冲击力。选项:关闭 / 默认样式 / 描述期望效果 | | | 期望的花字风格?(选填) | 按账号风格推荐 | 根据视觉基调推荐,见下方花字推荐表 | +| 18 | 滤镜? | 无 | account.json 的 capcut.filter,格式 `滤镜名:强度`(如 `质感电影:40`)。见下方滤镜推荐表 | +| 19 | 转场偏好? | 默认闪白 | account.json 的 capcut.transitions。见下方转场推荐表 | **花字效果推荐表**(92 种免费效果,按风格分类): @@ -109,6 +111,33 @@ Phase 4: 技术配置(有默认值,可跳过) > Agent 可调用 `get_text_effects` API 获取完整列表(92 种免费 + 更多 VIP)。 > Q17 选"默认样式"时,Agent 根据账号风格从上表自动匹配。 +**滤镜推荐表**(免费滤镜,格式 `滤镜名:强度`,强度 0-100): + +| 账号风格 | 推荐滤镜 | 备选 | +|---------|---------|------| +| 暗黑/军事/权谋 | 暗调氛围:40 | 暗夜:50、质感暗调:35 | +| 电影/叙事 | 质感电影:40 | 情感电影:35、情绪电影:30 | +| 复古/怀旧 | 暗调复古电影:40 | 复古工业:45 | +| 科技/赛博 | 质感暗调:30 | — | +| 清新/生活 | 质感电影:25 | 情感电影:20 | +| 纪录/人文 | 质感电影:30 | 情绪电影:25 | + +> 留空则不加滤镜。Agent 可调用 CapCut Mate API 获取完整滤镜列表。 + +**转场推荐表**(免费转场,`duration` 单位微秒,推荐 100000-300000): + +| 转场名 | 效果 | 适用场景 | +|--------|------|---------| +| 闪白 | 明闪过渡,节奏强 | 暗黑/军事/冲击类(默认推荐) | +| 闪白 II | 明闪变体,稍柔和 | 潮酷/街头 | +| 闪黑 | 暗闪过渡,压抑感 | 沉重/悬疑/结尾 | +| 溶解 | 画面缓慢交融 | 唯美/叙事/情感 | +| 叠化 | 经典淡入淡出 | 通用/纪录片 | +| 色彩溶解 | 带色彩过渡的溶解 | 艺术/创意 | + +> 默认配置使用「闪白」+ 按位置变体(hook 闪白、body 溶解、closing 闪黑),可直接采用默认。 +> Agent 可调用 CapCut Mate API 获取完整转场列表。 + **运动偏好 → 视频提示词映射**: | 用户选择 | 运动风格基调 | @@ -150,6 +179,8 @@ Phase 4: 技术配置(有默认值,可跳过) - TTS音色:{Q15} - TTS语气:{Q16} - 关键字氛围词:{Q17 开启/关闭,花字风格} +- 滤镜:{Q18} +- 转场:{Q19} 确认 "开始" → 创建账号 修改 → 调整后重新输出 @@ -178,6 +209,8 @@ Phase 4: 技术配置(有默认值,可跳过) - 从 `_template/account.json` 复制骨架 - 填入 id、name、description、模型、画幅等 - Q17 选关闭时删除 `keywordStyle` 节;选自定义花字时更新 `textEffect` 字段 + - Q18 填入 `capcut.filter`(格式 `滤镜名:强度`) + - Q19 填入 `capcut.transitions` 配置 3. **生成分镜.md** - 读取 `_template/prompts/通用分镜.md` diff --git a/.claude/skills/video-from-script/scripts/capcut_assemble.js b/.claude/skills/video-from-script/scripts/capcut_assemble.js index 0e03ff2..ea15ced 100644 --- a/.claude/skills/video-from-script/scripts/capcut_assemble.js +++ b/.claude/skills/video-from-script/scripts/capcut_assemble.js @@ -233,6 +233,20 @@ async function assemble(args) { } const manifest = JSON.parse(fs.readFileSync(manifestFile, 'utf-8')) + + // 从 account.json 自动继承 effects / filter(CLI 参数优先) + let finalEffects = effectsStr + let finalFilter = filterStr + if (!finalEffects || !finalFilter) { + const accountData = loadAccountConfig(manifest) + if (!finalEffects && accountData.capcut?.effects?.length) { + finalEffects = accountData.capcut.effects.join(',') + } + if (!finalFilter && accountData.capcut?.filter) { + finalFilter = accountData.capcut.filter + } + } + const { width, height } = getResolution(format) const defaultDurationUs = parseFloat(duration) * US @@ -284,6 +298,8 @@ async function assemble(args) { console.log(` 模式: ${mode} 画幅: ${format} (${width}x${height})`) console.log(` 时间线: ${hasTTS ? 'TTS音频驱动' : `固定${duration}s/段`} 总时长: ${(totalDurationUs / US).toFixed(1)}s`) console.log(` 字幕: ${subtitles} 配音: ${voiceover} 动画: ${animation}`) + if (finalEffects) console.log(` 特效: ${finalEffects}`) + if (finalFilter) console.log(` 滤镜: ${finalFilter}`) console.log(` 素材: ${items.length} 个可用\n`) const steps = [] @@ -397,7 +413,7 @@ async function assemble(args) { } } } - if (changed) saveManifest(manifestFile, manifest) + if (changed) fs.writeFileSync(manifestFile, JSON.stringify(manifest, null, 2)) } } catch (err) { console.log(` OSS 上传失败,将尝试本地路径: ${err.message}\n`) @@ -445,26 +461,26 @@ async function assemble(args) { // -- 添加特效 -- step++; console.log(`[${step}/${totalSteps}] 添加特效...`) - if (effectsStr) { + if (finalEffects) { try { - await addEffects(draftUrl, effectsStr, totalDurationUs) + await addEffects(draftUrl, finalEffects, totalDurationUs) } catch (e) { console.log(` 特效跳过: ${e.message}`) } } else { - console.log(' 跳过(未指定 --effects)') + console.log(' 跳过(未配置特效)') } // -- 添加滤镜 -- step++; console.log(`[${step}/${totalSteps}] 添加滤镜...`) - if (filterStr) { + if (finalFilter) { try { - await addFilter(draftUrl, filterStr, totalDurationUs) + await addFilter(draftUrl, finalFilter, totalDurationUs) } catch (e) { console.log(` 滤镜跳过: ${e.message}`) } } else { - console.log(' 跳过(未指定 --filter)') + console.log(' 跳过(未配置滤镜)') } // -- 保存草稿 -- @@ -809,31 +825,23 @@ async function addBGM(draftUrl, bgmUrl, totalDurationUs) { } // ============================================================================ -// 读取账号字幕风格配置 +// 读取账号配置 // ============================================================================ -function loadSubtitleStyle(manifest) { +function loadAccountConfig(manifest) { const account = manifest.account if (!account) return {} - const scriptDir = __dirname - const accountFile = path.join(scriptDir, '..', '..', '..', 'accounts', account, 'account.json') + const accountFile = path.join(__dirname, '..', '..', '..', '..', 'accounts', account, 'account.json') if (!fs.existsSync(accountFile)) return {} - try { - const accountData = JSON.parse(fs.readFileSync(accountFile, 'utf-8')) - return accountData.capcut?.subtitleStyle || {} - } catch { return {} } + try { return JSON.parse(fs.readFileSync(accountFile, 'utf-8')) } catch { return {} } +} + +function loadSubtitleStyle(manifest) { + return loadAccountConfig(manifest).capcut?.subtitleStyle || {} } function loadKeywordStyle(manifest) { - const account = manifest.account - if (!account) return {} - const scriptDir = __dirname - const accountFile = path.join(scriptDir, '..', '..', '..', 'accounts', account, 'account.json') - if (!fs.existsSync(accountFile)) return {} - try { - const accountData = JSON.parse(fs.readFileSync(accountFile, 'utf-8')) - return accountData.capcut?.keywordStyle || {} - } catch { return {} } + return loadAccountConfig(manifest).capcut?.keywordStyle || {} } // ============================================================================ @@ -894,15 +902,7 @@ async function addKeywordOverlays(draftUrl, items, timeline, style = {}) { } function loadTransitions(manifest) { - const account = manifest.account - if (!account) return null - const scriptDir = __dirname - const accountFile = path.join(scriptDir, '..', '..', '..', 'accounts', account, 'account.json') - if (!fs.existsSync(accountFile)) return null - try { - const accountData = JSON.parse(fs.readFileSync(accountFile, 'utf-8')) - return accountData.capcut?.transitions || null - } catch { return null } + return loadAccountConfig(manifest).capcut?.transitions || null } // ============================================================================ diff --git a/accounts/_template/account.json b/accounts/_template/account.json index 382968f..aca44dd 100644 --- a/accounts/_template/account.json +++ b/accounts/_template/account.json @@ -40,12 +40,12 @@ "hook": { "name": "闪白", "duration": 100000 }, "keypoint": { "name": "闪白", "duration": 120000 }, "body": { "name": "溶解", "duration": 300000 }, - "closing": { "name": "黑场", "duration": 200000 } + "closing": { "name": "闪黑", "duration": 200000 } }, "byDirector": { "tarantino": { "name": "闪白", "duration": 100000 }, "kitano": { "name": "溶解", "duration": 400000 }, - "fincher": { "name": "黑场", "duration": 200000 } + "fincher": { "name": "闪黑", "duration": 200000 } } } } diff --git a/accounts/军事账号/account.json b/accounts/军事账号/account.json index 75b5b69..16bb46e 100644 --- a/accounts/军事账号/account.json +++ b/accounts/军事账号/account.json @@ -21,7 +21,7 @@ "videoStylePrompt": "prompts/视频提示词.md", "capcut": { "effects": ["录制边框 III"], - "filter": "电影感:40", + "filter": "质感电影:40", "subtitleStyle": { "font": "思源黑体 Heavy", "fontSize": 24, @@ -55,12 +55,12 @@ "hook": { "name": "闪白", "duration": 100000 }, "keypoint": { "name": "闪白", "duration": 120000 }, "body": { "name": "溶解", "duration": 300000 }, - "closing": { "name": "黑场", "duration": 200000 } + "closing": { "name": "闪黑", "duration": 200000 } }, "byDirector": { "tarantino": { "name": "闪白", "duration": 100000 }, "kitano": { "name": "溶解", "duration": 400000 }, - "fincher": { "name": "黑场", "duration": 200000 } + "fincher": { "name": "闪黑", "duration": 200000 } } } }