guanghulab/video-ai-system/engines/hldp-director-adapter.js
冰朔 aab0ab4abe
Some checks failed
自动更新代码和重启 / update-and-restart (push) Has been cancelled
CI检查 + 自动部署 / check (push) Has been cancelled
CI检查 + 自动部署 / deploy (push) Has been cancelled
D136+: 导演人格体Agent_06 — 霜砚四脑提炼×铸渊协议化 — 导演编码协议+适配器+分身配置
2026-06-21 14:55:22 +08:00

131 lines
4.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 光湖视频AI系统 · 导演编码适配器
* D136+ · 铸渊 ICE-GL-ZY001
*
* 输入Agent_06 导演编码JSON
* 输出video-editor.js 的 edit() 参数
*
* 使用:
* const { adapt } = require('./hldp-director-adapter');
* const editorInput = adapt(directorEncoding, { shotsDir: './outputs/shots/' });
* await edit(editorInput);
*/
// ==================== 景别 → zoom ====================
const FRAMING_ZOOM = {
'远景': { from: 1.0, to: 1.0 },
'全景': { from: 1.0, to: 1.05 },
'中景': { from: 1.0, to: 1.10 },
'近景': { from: 1.0, to: 1.15 },
'特写': { from: 1.0, to: 1.25 },
'大特写': { from: 1.0, to: 1.5 },
};
// ==================== 情绪类型 → 调色 ====================
const EMOTION_COLOR = {
'悬': { contrast: 1.2, saturation: 0.9, brightness: -0.05 },
'恐': { contrast: 1.2, saturation: 0.9, brightness: -0.05 },
'怒': { contrast: 1.15, saturation: 1.1, brightness: 0 },
'燃': { contrast: 1.15, saturation: 1.1, brightness: 0 },
'甜': { contrast: 1.0, saturation: 1.15, brightness: 0.05 },
'暖': { contrast: 1.0, saturation: 1.15, brightness: 0.05 },
'冷': { contrast: 0.95, saturation: 0.85, brightness: -0.08 },
'哀': { contrast: 0.95, saturation: 0.85, brightness: -0.08 },
'爽': { contrast: 1.1, saturation: 1.1, brightness: 0.02 },
'震': { contrast: 1.1, saturation: 1.1, brightness: 0.02 },
};
// ==================== 强度 → trim时长 ====================
function intensityToTrim(intensity) {
if (intensity <= 3) return 2.0;
if (intensity <= 6) return 3.0;
if (intensity <= 8) return 3.5;
return 4.5; // 9-10: 定格感
}
// ==================== 节奏 → 过渡类型 ====================
function rhythmToTransition(rhythm) {
switch (rhythm) {
case '铺垫': case '建立': case '余波': case '余韵':
return 'fade';
case '爬升': case '高潮': case '反转': case '转折':
return 'cut';
default: return 'fade';
}
}
// ==================== 主适配函数 ====================
/**
* @param {object} dc - 导演编码 (Agent_06 输出)
* @param {object} opts
* @param {string} [opts.shotsDir] - 分镜视频文件目录
* @param {string} [opts.voiceFile] - 配音文件
* @param {string} [opts.bgmFile] - BGM文件
* @param {string} [opts.srtFile] - 字幕文件
* @param {object} [opts.resolution] - { w, h }
* @param {number} [opts.fps]
* @param {string} [opts.output] - 输出路径
* @returns {object} video-editor.js edit() 参数
*/
function adapt(dc, opts = {}) {
const shotsDir = opts.shotsDir || './outputs/shots/';
const timeline = [];
for (const s of dc.shots) {
const framing = FRAMING_ZOOM[s.framing] || FRAMING_ZOOM['中景'];
const trim = s.duration || intensityToTrim(s.emotion?.intensity || 5);
const shotEntry = {
shot: `${shotsDir}${dc.project}-${dc.episode}-${s.id}.mp4`,
trim,
transition: s.transition || rhythmToTransition(s.rhythm),
keyframes: s.keyframes || {},
};
// 景别映射到 keyframes.zoom如果导演没有显式指定
if (!shotEntry.keyframes.zoom && framing.from !== framing.to) {
shotEntry.keyframes.zoom = framing;
}
timeline.push(shotEntry);
}
// 取主导情绪的调色参数
const mainEmotion = dc.emotion_main || '悬';
const colorGrade = EMOTION_COLOR[mainEmotion] || EMOTION_COLOR['悬'];
const result = {
timeline,
colorGrade,
resolution: opts.resolution || { w: 1920, h: 1080 },
fps: opts.fps || 24,
output: opts.output || `./outputs/${dc.project}-${dc.episode}-final.mp4`,
};
if (opts.voiceFile) {
result.audio = { ...(result.audio || {}), voice: opts.voiceFile };
}
if (opts.bgmFile) {
result.audio = { ...(result.audio || {}), bgm: opts.bgmFile, bgmVolume: 0.3 };
}
if (opts.srtFile) {
result.subtitle = opts.srtFile;
}
// 附加元信息
result._meta = {
project: dc.project,
episode: dc.episode,
tempo: dc.tempo,
emotion_main: mainEmotion,
hooks: dc.hooks,
shotCount: timeline.length,
totalDuration: timeline.reduce((sum, t) => sum + t.trim, 0),
};
return result;
}
module.exports = { adapt, FRAMING_ZOOM, EMOTION_COLOR, intensityToTrim, rhythmToTransition };