79 lines
2.5 KiB
JavaScript
79 lines
2.5 KiB
JavaScript
#!/usr/bin/env node
|
|
import puppeteer from 'puppeteer';
|
|
import { execSync } from 'child_process';
|
|
import path from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
import fs from 'fs';
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
const outputDir = path.join(__dirname, 'output/v5-frames');
|
|
const htmlFile = path.join(__dirname, 'output/stripe-v5.html');
|
|
|
|
// Clean and create output dir
|
|
fs.rmSync(outputDir, { recursive: true, force: true });
|
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
|
|
async function captureFrames() {
|
|
const browser = await puppeteer.launch({
|
|
headless: true,
|
|
executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
|
args: ['--no-sandbox']
|
|
});
|
|
|
|
const page = await browser.newPage();
|
|
await page.setViewport({ width: 1920, height: 1080 });
|
|
|
|
// 10 seconds at 30fps = 300 frames
|
|
const fps = 30;
|
|
const duration = 10;
|
|
const totalFrames = fps * duration;
|
|
const frameInterval = 1000 / fps; // ~33.33ms per frame
|
|
|
|
console.log(`Capturing ${totalFrames} frames at ${fps}fps...`);
|
|
|
|
// Load the page fresh for each capture run
|
|
await page.goto(`file://${htmlFile}`, { waitUntil: 'domcontentloaded' });
|
|
|
|
// Let fonts load
|
|
await new Promise(r => setTimeout(r, 500));
|
|
|
|
// Reload to restart animation from 0
|
|
await page.goto(`file://${htmlFile}`, { waitUntil: 'domcontentloaded' });
|
|
|
|
const captureStart = Date.now();
|
|
|
|
for (let i = 0; i < totalFrames; i++) {
|
|
// Calculate when this frame should be captured (in real time from start)
|
|
const targetTime = i * frameInterval;
|
|
const elapsed = Date.now() - captureStart;
|
|
|
|
// Wait until we reach the target time
|
|
if (elapsed < targetTime) {
|
|
await new Promise(r => setTimeout(r, targetTime - elapsed));
|
|
}
|
|
|
|
const frameNum = String(i + 1).padStart(4, '0');
|
|
await page.screenshot({
|
|
path: path.join(outputDir, `frame-${frameNum}.png`),
|
|
type: 'png'
|
|
});
|
|
|
|
if (i % 30 === 0) console.log(`Frame ${i + 1}/${totalFrames} (${Math.round((i/totalFrames)*100)}%)`);
|
|
}
|
|
|
|
await browser.close();
|
|
|
|
// Create MP4
|
|
console.log('Creating MP4...');
|
|
const mp4Path = path.join(__dirname, 'output/stripe-v5.mp4');
|
|
|
|
try {
|
|
execSync(`ffmpeg -y -framerate ${fps} -i "${outputDir}/frame-%04d.png" -c:v libx264 -pix_fmt yuv420p -crf 18 "${mp4Path}"`, { stdio: 'inherit' });
|
|
console.log(`\nMP4 created: ${mp4Path}`);
|
|
} catch (e) {
|
|
console.error('FFmpeg error:', e.message);
|
|
}
|
|
}
|
|
|
|
captureFrames().catch(console.error);
|