cang-ying/engines/hldp-director-adapter.js
Guanghu Domestic Migration a2fa7d57d8 chore: import sanitized domestic snapshot for REPO-005
Source snapshot: 23037fa3a2e9b0597162735755e92fc763bf4d94
2026-07-17 15:55:48 +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 };