/** * 🤖 300 个 bottts 机器人 → 白底方形 PNG(1024×1024) * 与 preview-300.html 的 seed 序列一一对应(共享 bot-common 逻辑,过滤负面表情), * 白底 + bottts 居中放大,Chrome headless 并发截图。 * * 用法:node scripts/gen-300-bot-png.js */ 'use strict'; const fs = require('fs'); const path = require('path'); const vm = require('vm'); const { spawn } = require('child_process'); const { generateSeeds } = require('./bot-common'); const ROOT = path.resolve(__dirname, '..'); const OUT_DIR = path.join(ROOT, 'output', 'agent-avatars-png', 'bot-png'); const TMP_DIR = path.join(ROOT, 'output', '.tmp-bot'); const CHROME = 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe'; const SIZE = 1024; const COUNT = 300; // —— 1. 加载 bottts —— global.window = global; vm.runInThisContext(fs.readFileSync(path.join(ROOT, 'js', 'data', 'sources', 'dicebear-bottts.js'), 'utf8'), { filename: 'dicebear-bottts.js' }); vm.runInThisContext(fs.readFileSync(path.join(ROOT, 'js', 'core', 'dicebear.js'), 'utf8'), { filename: 'dicebear.js' }); // —— 2. 生成 300 个 seed(与预览页一致)—— const cards = generateSeeds(seed => HGB_Dicebear.renderAvatar('bottts', seed), COUNT); // —— 3. 生成 HTML(白底 + bottts 居中,SVG 放大到 880)—— fs.mkdirSync(TMP_DIR, { recursive: true }); const htmlFiles = []; for (let i = 0; i < cards.length; i++) { const { svg } = cards[i]; const html = `
${svg.replace('`; const f = path.join(TMP_DIR, `a${i + 1}.html`); fs.writeFileSync(f, html, 'utf8'); htmlFiles.push(f); } // —— 4. 并发截图(8 进程)—— function shot(file, out) { return new Promise((res) => { const p = spawn(CHROME, [ '--headless=new', '--disable-gpu', '--hide-scrollbars', '--force-device-scale-factor=1', '--window-size=' + SIZE + ',' + SIZE, '--virtual-time-budget=800', '--screenshot=' + out, 'file:///' + file.replace(/\\/g, '/') ], { stdio: 'ignore' }); p.on('close', () => res()); p.on('error', () => res()); }); } (async () => { fs.mkdirSync(OUT_DIR, { recursive: true }); const jobs = htmlFiles.map((f, i) => ({ f, out: path.join(OUT_DIR, 'agent-' + String(i + 1).padStart(2, '0') + '.png') })); let done = 0, t0 = Date.now(); const CONC = 8; for (let s = 0; s < jobs.length; s += CONC) { const batch = jobs.slice(s, s + CONC); await Promise.all(batch.map(j => shot(j.f, j.out))); done += batch.length; console.log(`⏳ ${done}/${jobs.length} ${((Date.now() - t0) / 1000).toFixed(1)}s`); } fs.rmSync(TMP_DIR, { recursive: true, force: true }); console.log('🎉 完成 → output/agent-avatars-png/bot-png/ (' + jobs.length + ' 张,白底 1024×1024)'); })();