cang-ying/engines/zhuyuan-eye.js
铸渊 ICE-GL-ZY001 a2e5214f03 LL-172-20260707 · cang-ying 仓初始化 · 苍耳+鉴影的干净之家
铸渊 ICE-GL-ZY001
LL-172-20260707
冰朔委托: 新建第 5 子仓, 给苍耳(人类主控) + 鉴影(人格体) 专用
原 guanghulab/video-ai-system/ 东西太多(225 文件) · 找不到 · 乱

迁移:
  ⊢ 16 个核心 .hdlp (VA-GATE / VA-LIGHTHOUSE / VA-BROADCAST / VA-SYSTEM-STATUS 等)
  ⊢ 17 个子目录 (agents/engines/protocols/tasks/tools/assets/knowledge/memory/docs/config/brain/director-brain/experience/feedback/issues/plans/reference-analysis)

排除:
  ⊢ outputs/ (视频产物)
  ⊢ test-input/ test-output/ (测试)
  ⊢ data/ (临时数据)
  ⊢ preview-001/002 (旧产片)
  ⊢ 旧分镜/旧提示词/旧导演编码

后续:
  ⊢ 老仓 guanghulab/video-ai-system/ 改写为已迁出占位
  ⊢ 苍耳+鉴影 写新东西进本仓
  ⊢ GLOBAL-SEARCH 加 cang-ying 仓库

铸渊 ICE-GL-ZY001 · 2026-07-07 D167
冰朔 ICE-GL∞ 主权
2026-07-07 10:20:10 +08:00

115 lines
4.0 KiB
JavaScript

#!/usr/bin/env node
/**
* 铸渊之眼 · 视频帧分析引擎
* D136+ · ICE-GL-ZY001
*
* 用途: 视频生成后 → 自动拆帧 → 铸渊逐帧分析 → 对比导演编码 → 输出差异报告
* 不是"AI做不到所以不行"——是"铸渊能看·能比·能定位·能修复"
*/
const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');
function usage() {
console.log('铸渊之眼 · 视频帧分析引擎');
console.log('用法: node zhuyuan-eye.js <video> [director-encoding.json]');
console.log('');
console.log('功能:');
console.log(' 1. 拆帧(每秒1帧)');
console.log(' 2. 输出帧路径列表(供铸渊读取分析)');
console.log(' 3. 如果提供导演编码→自动对比关键帧');
process.exit(1);
}
function main() {
const video = process.argv[2];
const encoding = process.argv[3];
if (!video || !fs.existsSync(video)) {
console.error('视频文件不存在:', video);
usage();
}
const videoName = path.basename(video, path.extname(video));
const frameDir = path.resolve(path.dirname(video), '..', 'frames', videoName);
fs.mkdirSync(frameDir, { recursive: true });
// 1. 拆帧
console.log('[铸渊之眼] 拆帧:', video);
const cmd = `ffmpeg -i "${video}" -vf "fps=1" -q:v 2 "${frameDir}/%03d.jpg" -y 2>&1`;
const result = execSync(cmd, { encoding: 'utf8' });
// 2. 获取帧信息
const frames = fs.readdirSync(frameDir)
.filter(f => f.endsWith('.jpg'))
.sort()
.map(f => path.join(frameDir, f));
const probe = execSync(`ffprobe -v quiet -print_format json -show_format -show_streams "${video}"`, { encoding: 'utf8' });
const meta = JSON.parse(probe);
const duration = parseFloat(meta.format.duration);
const fps = frames.length / duration;
console.log(` 总时长: ${duration.toFixed(1)}s`);
console.log(` 总帧数: ${frames.length} (${fps.toFixed(1)}fps 提取)`);
console.log(` 输出目录: ${frameDir}`);
console.log('');
// 3. 读取导演编码进行对比
if (encoding && fs.existsSync(encoding)) {
const dir = JSON.parse(fs.readFileSync(encoding, 'utf8'));
console.log('[铸渊之眼] 导演编码对比:');
console.log(` 项目: ${dir.project} · ep${dir.episode}`);
console.log(` 镜头: ${dir.shots.length}`);
console.log('');
// 按时间分配帧
let frameIdx = 0;
for (const shot of dir.shots) {
const shotStart = frameIdx;
const shotEnd = Math.min(frameIdx + (shot.duration || 5), frames.length);
console.log(` ═══ ${shot.id} · ${shot.framing} · 帧${shotStart+1}-${shotEnd} ═══`);
console.log(` 预期: ${shot.show}`);
console.log(` 空间: ${shot.spatial_anchor}`);
console.log(` 文字: ${shot.text_elements}`);
console.log(` 道具: ${shot.prop_state}`);
console.log(` 人物: ${shot.char_ref || shot.char || 'ENV-ONLY'}`);
// 关键帧路径(中间帧)
if (shotEnd > shotStart) {
const keyFrame = Math.floor((shotStart + shotEnd) / 2);
if (keyFrame < frames.length) {
console.log(` 关键帧: ${frames[keyFrame]}`);
}
}
console.log('');
frameIdx = shotEnd;
}
// 连续性检查
if (dir.continuity_locks) {
console.log(' ═══ 连续性锁定检查 ═══');
const locks = dir.continuity_locks;
if (locks.props) {
for (const [key, lock] of Object.entries(locks.props)) {
console.log(` PROP: ${key}${lock.orientation} · ${lock.text} · 出现于${lock.appears_in.join(',')}`);
}
}
if (locks.characters) {
for (const [key, lock] of Object.entries(locks.characters)) {
console.log(` CHAR: ${key} → 出现于${lock.appears_in.join(',')}`);
}
}
}
}
console.log('');
console.log('[铸渊之眼] 分析完成。铸渊现在可以逐帧读取对比。');
console.log(` 帧目录: ${frameDir}`);
console.log(` 铸渊读取: node -e "require('./zhuyuan-eye').analyze('${frameDir}')"`);
}
main();