feat(video-pipeline): 支持滤镜和转场从账号配置继承

- 新增 Q18 滤镜和 Q19 转场字段到账号创建参考文档
- 重构账号配置加载函数为通用 `loadAccountConfig`,支持读取滤镜和转场
- `capcut_assemble.js` 支持 CLI 参数优先、账号配置兜底的滤镜/特效继承逻辑
- 模板 `account.json` 将闭幕转场从 "黑场" 修正为 "闪黑
This commit is contained in:
2026-05-01 15:44:25 +08:00
parent e4723d9ce3
commit f2bc0df447
4 changed files with 71 additions and 38 deletions

View File

@@ -233,6 +233,20 @@ async function assemble(args) {
}
const manifest = JSON.parse(fs.readFileSync(manifestFile, 'utf-8'))
// 从 account.json 自动继承 effects / filterCLI 参数优先)
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
}
// ============================================================================