← back to Watches
utils/screenshot-capture.js
270 lines
/**
* Screenshot Capture Utility using Puppeteer
* Captures watch images and dashboard screenshots
*/
import puppeteer from 'puppeteer';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
class ScreenshotCapture {
constructor() {
this.browser = null;
this.screenshotsDir = path.join(__dirname, '../public/screenshots');
if (!fs.existsSync(this.screenshotsDir)) {
fs.mkdirSync(this.screenshotsDir, { recursive: true });
}
}
/**
* Initialize browser
*/
async init() {
if (!this.browser) {
this.browser = await puppeteer.launch({
headless: true,
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage'
]
});
}
return this.browser;
}
/**
* Capture full page screenshot
*/
async captureFullPage(url, outputName, options = {}) {
await this.init();
const page = await this.browser.newPage();
try {
await page.setViewport({ width: 1920, height: 1080 });
await page.goto(url, { waitUntil: 'networkidle0', timeout: 30000 });
// Wait for any specified selector
if (options.waitForSelector) {
await page.waitForSelector(options.waitForSelector, { timeout: 10000 });
}
const screenshotPath = path.join(this.screenshotsDir, `${outputName}.png`);
await page.screenshot({
path: screenshotPath,
fullPage: options.fullPage !== false,
type: 'png'
});
console.log(`✓ Screenshot captured: ${screenshotPath}`);
return screenshotPath;
} catch (error) {
console.error(`✗ Screenshot capture failed:`, error);
throw error;
} finally {
await page.close();
}
}
/**
* Capture element screenshot
*/
async captureElement(url, selector, outputName) {
await this.init();
const page = await this.browser.newPage();
try {
await page.setViewport({ width: 1920, height: 1080 });
await page.goto(url, { waitUntil: 'networkidle0', timeout: 30000 });
const element = await page.waitForSelector(selector, { timeout: 10000 });
const screenshotPath = path.join(this.screenshotsDir, `${outputName}.png`);
await element.screenshot({
path: screenshotPath,
type: 'png'
});
console.log(`✓ Element screenshot captured: ${screenshotPath}`);
return screenshotPath;
} catch (error) {
console.error(`✗ Element screenshot failed:`, error);
throw error;
} finally {
await page.close();
}
}
/**
* Capture dashboard screenshots
*/
async captureDashboard(baseUrl = 'http://localhost:7600') {
console.log('📸 Capturing dashboard screenshots...');
const pages = [
{ url: `${baseUrl}/`, name: 'dashboard-home', waitFor: '.grid' },
{ url: `${baseUrl}/index-new.html`, name: 'dashboard-analytics', waitFor: '.card' },
{ url: `${baseUrl}/simple.html`, name: 'dashboard-simple', waitFor: '.grid' }
];
const screenshots = [];
for (const page of pages) {
try {
const screenshot = await this.captureFullPage(page.url, page.name, {
waitForSelector: page.waitFor
});
screenshots.push(screenshot);
// Delay between captures
await new Promise(resolve => setTimeout(resolve, 1000));
} catch (error) {
console.error(`Failed to capture ${page.name}:`, error.message);
}
}
console.log(`✓ Captured ${screenshots.length} dashboard screenshots`);
return screenshots;
}
/**
* Capture watch product images from dealer sites
*/
async captureWatchImage(url, watchId) {
await this.init();
const page = await this.browser.newPage();
try {
await page.setViewport({ width: 1920, height: 1080 });
await page.goto(url, { waitUntil: 'networkidle0', timeout: 30000 });
// Common selectors for watch product images
const selectors = [
'.product-image img',
'.main-image img',
'[data-testid="product-image"]',
'.gallery-main img',
'img.product-photo'
];
let imageElement = null;
for (const selector of selectors) {
try {
imageElement = await page.$(selector);
if (imageElement) break;
} catch (e) {
continue;
}
}
if (!imageElement) {
throw new Error('No product image found');
}
const screenshotPath = path.join(this.screenshotsDir, `watch-${watchId}.png`);
await imageElement.screenshot({
path: screenshotPath,
type: 'png'
});
console.log(`✓ Watch image captured: ${screenshotPath}`);
return screenshotPath;
} catch (error) {
console.error(`✗ Watch image capture failed:`, error);
throw error;
} finally {
await page.close();
}
}
/**
* Create thumbnails from screenshots
*/
async createThumbnail(screenshotPath, width = 400) {
const page = await this.browser.newPage();
try {
await page.setViewport({ width, height: Math.round(width * 0.75) });
const content = `
<!DOCTYPE html>
<html>
<head>
<style>
body { margin: 0; padding: 0; }
img { width: 100%; height: auto; }
</style>
</head>
<body>
<img src="file://${screenshotPath}" />
</body>
</html>
`;
await page.setContent(content);
const thumbnailPath = screenshotPath.replace('.png', '-thumb.png');
await page.screenshot({
path: thumbnailPath,
type: 'png'
});
console.log(`✓ Thumbnail created: ${thumbnailPath}`);
return thumbnailPath;
} finally {
await page.close();
}
}
/**
* Close browser
*/
async close() {
if (this.browser) {
await this.browser.close();
this.browser = null;
}
}
}
export default ScreenshotCapture;
// CLI usage
if (import.meta.url === `file://${process.argv[1]}`) {
const capture = new ScreenshotCapture();
const args = process.argv.slice(2);
if (args.length === 0) {
console.log('Usage: node screenshot-capture.js <url> [output-name]');
console.log(' or: node screenshot-capture.js --dashboard');
process.exit(1);
}
(async () => {
try {
if (args[0] === '--dashboard') {
await capture.captureDashboard();
} else {
await capture.captureFullPage(args[0], args[1] || 'screenshot');
}
} catch (error) {
console.error('Error:', error);
process.exit(1);
} finally {
await capture.close();
}
})();
}