feat(video-from-script): 重构工作流为子Agent分步执行并新增提示词模板系统
将视频制作工作流拆分为独立子步骤:分镜 → 图片提示词 → 生图 → 视频提示词 → 生视频 → 成片,每步由子Agent独立执行。引入prompts/目录统一管理提示词模板(分镜.md、图片提示词.md、视频提示词.md),通过account.json的storyboardPrompt/imageStylePrompt/videoStylePrompt字段引用。 变更内容: - 新增confirmed机制和pipeline.js confirm命令,生图后必须人工确认才能继续 - manifest schema改用shotDesc/narration/duration/directorRef替代旧字段 - 文件命名规则从keyword改为slug(从shotDesc/narration派生) - 删除旧的storyboard-rules.md和prompt-rules.md - pipeline.js脚本拆分为lib/目录下的独立模块(cmd-init/cmd-confirm/cmd-validate/phase-*) - 新增cmd-create-account支持一键创建带prompts目录的账号 - capcut_assemble支持narration字段替代text作为字幕源 - 新增.gitclaude/settings.json权限配置
This commit is contained in:
104
.claude/skills/video-from-script/scripts/lib/phase-videos.js
Normal file
104
.claude/skills/video-from-script/scripts/lib/phase-videos.js
Normal file
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* Phase: videos — 视频生成(VEO / Grok / Kling)
|
||||
*
|
||||
* 图生视频,批量提交,生成后自动上传 OSS
|
||||
*/
|
||||
|
||||
const path = require('path')
|
||||
const { saveManifest, ensureDir, log, getManifestDir } = require('./pipeline-utils')
|
||||
|
||||
async function phaseVideos(manifest, manifestPath, options) {
|
||||
const dir = getManifestDir(manifestPath)
|
||||
const videosDir = path.join(dir, 'videos')
|
||||
ensureDir(videosDir)
|
||||
|
||||
const accountConfig = options.accountConfig || {}
|
||||
const videoModel = manifest.videoModel || accountConfig.videoModel || 'veo3-fast-frames'
|
||||
|
||||
const items = manifest.items.filter(it =>
|
||||
it.status === 'done' && it.confirmed !== false && it.url && it.videoPrompt && !it.video
|
||||
)
|
||||
if (items.length === 0) { log('videos', '无待处理 item,跳过'); return }
|
||||
|
||||
// 选择生成器
|
||||
let generator
|
||||
const modelLower = videoModel.toLowerCase()
|
||||
if (modelLower.includes('grok')) {
|
||||
generator = require('../grok-video-generator')
|
||||
} else if (modelLower.includes('kling')) {
|
||||
generator = require('../kling-video-generator')
|
||||
} else {
|
||||
generator = require('../veo-video-generator')
|
||||
}
|
||||
|
||||
log('videos', `共 ${items.length} 个, 模型: ${videoModel}`)
|
||||
|
||||
const tasks = items.map((item, i) => {
|
||||
const task = {
|
||||
id: item.id || i + 1,
|
||||
prompt: item.videoPrompt,
|
||||
image: item.url,
|
||||
outputDir: videosDir,
|
||||
}
|
||||
if (item.lastFrameUrl) {
|
||||
task.images = [item.url, item.lastFrameUrl]
|
||||
task.lastFrameUrl = item.lastFrameUrl
|
||||
} else {
|
||||
task.images = [item.url]
|
||||
}
|
||||
return task
|
||||
})
|
||||
|
||||
try {
|
||||
const results = await generator.batchGenerate(tasks, {
|
||||
videoModel,
|
||||
aspectRatio: manifest.format || '9:16',
|
||||
outputDir: videosDir,
|
||||
skipManifestWrite: true,
|
||||
})
|
||||
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
const result = results[i]
|
||||
const item = items[i]
|
||||
if (!item) continue
|
||||
if (result.success && result.file) {
|
||||
item.video = path.relative(dir, result.file).replace(/\\/g, '/')
|
||||
item.videoDuration = result.duration
|
||||
} else {
|
||||
item.status = 'failed'
|
||||
item.error = result.error || '视频生成失败'
|
||||
log('videos', ` item ${(item.id || '?')} 失败: ${item.error}`)
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
log('videos', `批量生成失败: ${err.message}`)
|
||||
for (const item of items) {
|
||||
if (!item.video) {
|
||||
item.status = 'failed'
|
||||
item.error = `批量生成异常: ${err.message}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 上传视频到 OSS
|
||||
const { uploadFile } = require('../oss-upload')
|
||||
const videoItems = manifest.items.filter(it => it.video && !it.videoUrl)
|
||||
if (videoItems.length > 0) {
|
||||
log('videos', `上传 ${videoItems.length} 个视频到 OSS...`)
|
||||
for (const item of videoItems) {
|
||||
const videoPath = path.resolve(dir, item.video)
|
||||
try {
|
||||
const { url } = await uploadFile(videoPath)
|
||||
item.videoUrl = url
|
||||
log('videos', ` ${item.video} → OK`)
|
||||
} catch (err) {
|
||||
log('videos', ` ${item.video} 上传失败: ${err.message}`)
|
||||
}
|
||||
saveManifest(manifestPath, manifest)
|
||||
}
|
||||
}
|
||||
|
||||
saveManifest(manifestPath, manifest)
|
||||
}
|
||||
|
||||
module.exports = { phaseVideos }
|
||||
Reference in New Issue
Block a user