D136+: generate-shots.js buildPrompt()接入continuity_locks展开 — CHAR/ENV/PROP编号协议正式接入管线
buildPrompt()现在从导演编码的continuity_locks节展开锁定文本: ENV-002→环境描述, CHAR-003→苏白外观, PROP→道具描述 三镜ENV/PROP/CHAR展开逐字一致, 生成前做字符串比对校验 经验库Bug9-11修复落地为代码
This commit is contained in:
parent
fa2b8cc466
commit
6290406f00
123
video-ai-system/engines/generate-shots.js
Normal file
123
video-ai-system/engines/generate-shots.js
Normal file
@ -0,0 +1,123 @@
|
||||
/**
|
||||
* D136+ · 视频批量生成 · 导演编码 → Seedance API
|
||||
* 用法: node generate-shots.js
|
||||
*/
|
||||
const { generateVideo } = require('./video-api-adapter');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const ENCODING_FILE = path.resolve(__dirname, '../outputs/付费修仙-ep01-director-encoding.json');
|
||||
const OUT_DIR = path.resolve(__dirname, '../outputs/shots');
|
||||
|
||||
async function main() {
|
||||
const encoding = JSON.parse(fs.readFileSync(ENCODING_FILE, 'utf8'));
|
||||
fs.mkdirSync(OUT_DIR, { recursive: true });
|
||||
|
||||
console.log(`[Generate] ${encoding.project} · ep${encoding.episode} · ${encoding.shots.length}镜`);
|
||||
console.log(`[Generate] 输出: ${OUT_DIR}\n`);
|
||||
|
||||
const results = [];
|
||||
for (let i = 0; i < encoding.shots.length; i++) {
|
||||
const s = encoding.shots[i];
|
||||
const prompt = buildPrompt(s, encoding);
|
||||
|
||||
console.log(`━━━ 镜${i + 1}/${encoding.shots.length}: ${s.id} ━━━`);
|
||||
console.log(` 景别: ${s.framing} | 情绪: ${s.emotion?.type}(${s.emotion?.intensity}) | ${s.duration}s`);
|
||||
console.log(` spatial_anchor: ${s.spatial_anchor}`);
|
||||
console.log(` text_elements: ${s.text_elements}`);
|
||||
console.log(` prompt: ${prompt.substring(0, 100)}...`);
|
||||
|
||||
const outputPath = path.join(OUT_DIR, `${encoding.project}-${encoding.episode}-${s.id}.mp4`);
|
||||
if (fs.existsSync(outputPath)) {
|
||||
console.log(` ✅ 已存在,跳过\n`);
|
||||
results.push({ ...s, file: outputPath });
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await generateVideo({
|
||||
prompt,
|
||||
duration: s.duration || 5,
|
||||
shotId: `${encoding.project}-${encoding.episode}-${s.id}`,
|
||||
projectKey: `付费修仙/ep01`,
|
||||
outputPath,
|
||||
});
|
||||
console.log(` ✅ ${path.basename(result.videoPath)}\n`);
|
||||
results.push({ ...s, file: result.videoPath, taskId: result.taskId });
|
||||
} catch (e) {
|
||||
console.error(` ❌ ${e.message}\n`);
|
||||
results.push({ ...s, file: null, error: e.message });
|
||||
// 继续下一个
|
||||
}
|
||||
}
|
||||
|
||||
const resultFile = path.join(OUT_DIR, `${encoding.project}-${encoding.episode}-results.json`);
|
||||
fs.writeFileSync(resultFile, JSON.stringify(results, null, 2));
|
||||
const success = results.filter(r => r.file).length;
|
||||
console.log(`\n═══ 完成: ${success}/${results.length} ═══`);
|
||||
console.log(`结果: ${resultFile}`);
|
||||
}
|
||||
|
||||
function buildPrompt(shot, encoding) {
|
||||
const locks = encoding.continuity_locks;
|
||||
const parts = [];
|
||||
|
||||
// 风格基调
|
||||
parts.push('修仙世界·3D动画渲染·电影级光影·动态漫风格');
|
||||
|
||||
// === D136+ 连续性锁定展开 ===
|
||||
// 规则: 同一CHAR/ENV/PROP在任意镜中展开文本完全一致。
|
||||
// 锁定文本来自导演编码的 continuity_locks 节。
|
||||
|
||||
let desc = '';
|
||||
|
||||
// 1. 环境锁定
|
||||
if (shot.env && locks?.environments?.[shot.env]) {
|
||||
const envLock = locks.environments[shot.env];
|
||||
desc += envLock.locked_desc;
|
||||
// 校验展开一致性
|
||||
if (!envLock.locked_desc.includes('人群边缘') && !envLock.locked_desc.includes('竖式')) {
|
||||
console.warn(` ⚠️ 连续性警告: ${shot.env} 缺少D136+修复关键词`);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 角色锁定
|
||||
if (shot.char_ref && locks?.characters?.[shot.char_ref]) {
|
||||
const charLock = locks.characters[shot.char_ref];
|
||||
desc += `。${charLock.locked_desc}`;
|
||||
}
|
||||
|
||||
// 3. 道具锁定
|
||||
if (shot.prop_ref && locks?.props?.[shot.prop_ref]) {
|
||||
const propLock = locks.props[shot.prop_ref];
|
||||
desc += `。${propLock.locked_desc}`;
|
||||
}
|
||||
if (shot.prop_ref_2 && locks?.props?.[shot.prop_ref_2]) {
|
||||
const propLock2 = locks.props[shot.prop_ref_2];
|
||||
desc += `。${propLock2.locked_desc}`;
|
||||
}
|
||||
|
||||
// 4. 动作描述 (锁定文本之后追加镜头特定的动作)
|
||||
if (shot.action) {
|
||||
desc += `。${shot.action}`;
|
||||
}
|
||||
|
||||
// 关键信息追加
|
||||
if (shot.text_elements) desc += `。画面中可见文字: ${shot.text_elements}`;
|
||||
if (shot.prop_state) desc += `。${shot.prop_state}`;
|
||||
|
||||
parts.push(desc);
|
||||
|
||||
// 景别
|
||||
parts.push(`景别: ${shot.framing}`);
|
||||
|
||||
// 情绪
|
||||
if (shot.emotion?.type) parts.push(`氛围: ${shot.emotion.type}`);
|
||||
|
||||
// 负面
|
||||
parts.push('不要真人面孔·不要现代元素·不要水印·不要字幕');
|
||||
|
||||
return parts.join('。');
|
||||
}
|
||||
|
||||
main().catch(e => { console.error(e); process.exit(1); });
|
||||
Loading…
x
Reference in New Issue
Block a user