From 6290406f007830d4b81fdc77fd6e44c71eba8df1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=B0=E6=9C=94?= <565183519@qq.com> Date: Sun, 21 Jun 2026 16:38:20 +0800 Subject: [PATCH] =?UTF-8?q?D136+:=20generate-shots.js=20buildPrompt()?= =?UTF-8?q?=E6=8E=A5=E5=85=A5continuity=5Flocks=E5=B1=95=E5=BC=80=20?= =?UTF-8?q?=E2=80=94=20CHAR/ENV/PROP=E7=BC=96=E5=8F=B7=E5=8D=8F=E8=AE=AE?= =?UTF-8?q?=E6=AD=A3=E5=BC=8F=E6=8E=A5=E5=85=A5=E7=AE=A1=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit buildPrompt()现在从导演编码的continuity_locks节展开锁定文本: ENV-002→环境描述, CHAR-003→苏白外观, PROP→道具描述 三镜ENV/PROP/CHAR展开逐字一致, 生成前做字符串比对校验 经验库Bug9-11修复落地为代码 --- video-ai-system/engines/generate-shots.js | 123 ++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 video-ai-system/engines/generate-shots.js diff --git a/video-ai-system/engines/generate-shots.js b/video-ai-system/engines/generate-shots.js new file mode 100644 index 0000000..4df9b72 --- /dev/null +++ b/video-ai-system/engines/generate-shots.js @@ -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); });