Files
video-create/.claude/skills/video-from-script/scripts/lib/cmd-validate.js

99 lines
3.8 KiB
JavaScript
Raw Normal View History

/**
* Command: validate 校验 manifest.json 完整性
* Command: validate-account 校验账号目录完整性
*/
const fs = require('fs')
const path = require('path')
const { loadManifest, ACCOUNTS_DIR } = require('./pipeline-utils')
function validateManifest(manifestPath) {
const issues = []
if (!fs.existsSync(manifestPath)) {
console.error(`错误: manifest 不存在: ${manifestPath}`)
process.exit(1)
}
let manifest
try {
manifest = loadManifest(manifestPath)
} catch (e) {
console.error(`错误: JSON 解析失败: ${e.message}`)
process.exit(1)
}
if (!manifest.account) issues.push('缺少顶层 account')
if (!manifest.imageModel) issues.push('缺少顶层 imageModel可选: gemini, mj')
if (!manifest.format) issues.push('缺少顶层 format如 9:16')
if (!manifest.items || !Array.isArray(manifest.items)) issues.push('缺少顶层 items 数组')
if (!manifest.mode) issues.push('缺少顶层 modesingle 或 framePair')
if (manifest.items && Array.isArray(manifest.items)) {
manifest.items.forEach((item, i) => {
const prefix = `items[${i}]`
if (!item.narration && !item.text) issues.push(`${prefix} 缺少 narration 或 text中文旁白`)
if (!item.shotDesc) issues.push(`${prefix} 缺少 shotDesc分镜描述`)
if (!item.imagePrompt) issues.push(`${prefix} 缺少 imagePrompt`)
if (manifest.mode === 'framePair' && !item.lastFramePrompt) {
issues.push(`${prefix} 首尾帧模式缺少 lastFramePromptimagePrompt 作为第一帧)`)
}
if (item.status && !['pending', 'generating', 'done', 'failed'].includes(item.status)) {
issues.push(`${prefix} status 无效: ${item.status}`)
}
})
}
if (issues.length === 0) {
console.log(`✓ Manifest 校验通过: ${manifestPath}`)
console.log(` ${manifest.items?.length || 0} items, account=${manifest.account}, mode=${manifest.mode}`)
} else {
console.error(`✗ 发现 ${issues.length} 个问题:`)
issues.forEach(issue => console.error(` - ${issue}`))
process.exit(1)
}
}
function validateAccount(accountId) {
const issues = []
const accountDir = path.join(ACCOUNTS_DIR, accountId)
if (!fs.existsSync(accountDir)) { console.error(`错误: 账号不存在: ${accountDir}`); process.exit(1) }
const accountPath = path.join(accountDir, 'account.json')
if (!fs.existsSync(accountPath)) { console.error('错误: 缺少 account.json'); process.exit(1) }
let config
try { config = JSON.parse(fs.readFileSync(accountPath, 'utf-8')) }
catch (e) { console.error(`错误: JSON 解析失败: ${e.message}`); process.exit(1) }
if (config.id !== accountId) issues.push(`id 不匹配: json="${config.id}" vs 目录="${accountId}"`)
if (!config.name) issues.push('缺少 name')
if (!config.imageModel) issues.push('缺少 imageModel')
if (!config.defaultFormat) issues.push('缺少 defaultFormat')
const refDir = path.join(accountDir, 'references')
const localRefs = fs.existsSync(refDir)
? fs.readdirSync(refDir).filter(f => /\.(png|jpg|jpeg|webp)$/i.test(f))
: []
const topRefs = config.references || []
if (localRefs.length === 0 && topRefs.length === 0) {
issues.push('无参考图(建议至少 1 张)')
}
for (const ref of topRefs) {
if (!ref.url) issues.push(`参考图 ${ref.file} 缺少 url未上传 OSS`)
}
if (issues.length === 0) {
console.log(`✓ 账号校验通过: ${accountId}`)
console.log(` ${config.name}, 模型: ${config.imageModel}+${config.videoModel || '(未指定)'}`)
console.log(` 参考图: ${localRefs.length} 本地, ${topRefs.length} 已上传`)
} else {
console.error(`✗ 发现 ${issues.length} 个问题:`)
issues.forEach(i => console.error(` - ${i}`))
process.exit(1)
}
}
module.exports = { validateManifest, validateAccount }