← 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();
    }
  })();
}