← back to Goodquestion Ai
video-html/batch-generate.js
111 lines
#!/usr/bin/env node
/**
* batch-generate.js — Run generate-video.js for all configs in configs/
*
* Processes sequentially (Puppeteer + ffmpeg are memory-heavy).
* Logs progress, timing, and any failures.
*
* Usage:
* node batch-generate.js # Run all
* node batch-generate.js --from 5 # Start from config 5
* node batch-generate.js --only 3,7,12 # Only specific configs
*/
const fs = require('fs');
const path = require('path');
const { generateVideo } = require('./generate-video');
const CONFIGS_DIR = path.join(__dirname, 'configs');
const VIDEOS_DIR = '/root/Projects/goodquestion-ai/videos';
fs.mkdirSync(VIDEOS_DIR, { recursive: true });
function parseArgs() {
const args = process.argv.slice(2);
const opts = { from: 1, only: null };
for (let i = 0; i < args.length; i++) {
if (args[i] === '--from' && args[i + 1]) opts.from = parseInt(args[++i], 10);
if (args[i] === '--only' && args[i + 1]) opts.only = args[++i].split(',').map(Number);
}
return opts;
}
async function main() {
const opts = parseArgs();
const configFiles = fs.readdirSync(CONFIGS_DIR)
.filter(f => f.endsWith('.json'))
.sort();
let toProcess = configFiles;
if (opts.only) {
toProcess = configFiles.filter((f, i) => opts.only.includes(i + 1));
} else if (opts.from > 1) {
toProcess = configFiles.slice(opts.from - 1);
}
console.log('╔══════════════════════════════════════════════════════╗');
console.log('║ BATCH VIDEO GENERATION — 18 Blog Posts ║');
console.log('╚══════════════════════════════════════════════════════╝');
console.log(`\nConfigs to process: ${toProcess.length}`);
console.log(`Output directory: ${VIDEOS_DIR}\n`);
const results = [];
const batchStart = Date.now();
for (let i = 0; i < toProcess.length; i++) {
const file = toProcess[i];
const configPath = path.join(CONFIGS_DIR, file);
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
console.log(`\n${'█'.repeat(60)}`);
console.log(`█ VIDEO ${i + 1}/${toProcess.length}: ${config.title}`);
console.log(`█ Config: ${file}`);
console.log(`${'█'.repeat(60)}`);
const videoStart = Date.now();
try {
const output = await generateVideo(config);
const elapsed = ((Date.now() - videoStart) / 1000).toFixed(1);
results.push({ file, title: config.title, status: 'OK', time: elapsed, output });
console.log(`\n✓ Completed in ${elapsed}s`);
} catch (err) {
const elapsed = ((Date.now() - videoStart) / 1000).toFixed(1);
results.push({ file, title: config.title, status: 'FAIL', time: elapsed, error: err.message });
console.error(`\n✗ FAILED after ${elapsed}s: ${err.message}`);
}
}
// Summary
const totalTime = ((Date.now() - batchStart) / 1000).toFixed(1);
const ok = results.filter(r => r.status === 'OK').length;
const fail = results.filter(r => r.status === 'FAIL').length;
console.log('\n\n' + '═'.repeat(60));
console.log('BATCH COMPLETE');
console.log('═'.repeat(60));
console.log(`Total time: ${totalTime}s (${(totalTime / 60).toFixed(1)} min)`);
console.log(`Success: ${ok}/${results.length}`);
if (fail > 0) console.log(`Failed: ${fail}`);
console.log('\nResults:');
results.forEach((r, i) => {
const icon = r.status === 'OK' ? '✓' : '✗';
console.log(` ${icon} ${String(i + 1).padStart(2)}. ${r.title} (${r.time}s)`);
if (r.status === 'FAIL') console.log(` Error: ${r.error}`);
});
// Count output files
const outputFiles = fs.readdirSync(VIDEOS_DIR).filter(f => f.endsWith('.mp4'));
const landscapeCount = outputFiles.filter(f => f.includes('-landscape')).length;
const verticalCount = outputFiles.filter(f => f.includes('-vertical')).length;
console.log(`\nOutput files: ${outputFiles.length} total (${landscapeCount} landscape + ${verticalCount} vertical)`);
if (fail > 0) process.exit(1);
}
main().catch(err => {
console.error('Batch failed:', err);
process.exit(1);
});