UNPKG

contaigents

Version:

Modular AI Content Ecosystem with Audio Generation

182 lines (181 loc) 6.23 kB
import { chromium } from 'playwright'; import { execSync } from 'child_process'; /** * BrowserService - Lightweight browser service for HTML-to-image conversion * * This service provides a simple interface for: * - Creating browser instances * - Rendering HTML content * - Taking high-quality screenshots * - Managing browser lifecycle */ export class BrowserService { constructor() { this.browser = null; this.page = null; } /** * Initialize browser instance */ async initialize() { if (this.browser) { return; // Already initialized } try { this.browser = await chromium.launch({ headless: true, args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--disable-accelerated-2d-canvas', '--no-first-run', '--no-zygote', '--disable-gpu' ] }); this.page = await this.browser.newPage(); } catch (error) { const errorMessage = error.message; // Check if it's a browser not found error if (errorMessage.includes("Executable doesn't exist") || errorMessage.includes("browserType.launch")) { console.log('🔧 Playwright browsers not found. Installing automatically...'); try { // Try to install browsers automatically execSync('npx playwright install chromium --with-deps', { stdio: 'inherit', timeout: 120000 // 2 minutes timeout }); console.log('✅ Playwright browsers installed successfully. Retrying...'); // Retry browser launch after installation this.browser = await chromium.launch({ headless: true, args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--disable-background-timer-throttling', '--disable-backgrounding-occluded-windows', '--no-zygote', '--disable-gpu' ] }); this.page = await this.browser.newPage(); return; } catch (installError) { throw new Error(`Failed to initialize browser: ${errorMessage}\n\nAutomatic browser installation failed: ${installError.message}\n\nPlease run manually: npx playwright install chromium --with-deps`); } } throw new Error(`Failed to initialize browser: ${errorMessage}`); } } /** * Render HTML content and take a screenshot */ async renderToImage(htmlContent, options) { if (!this.page) { throw new Error('Browser not initialized. Call initialize() first.'); } try { // Set viewport size await this.page.setViewportSize({ width: options.width, height: options.height }); // Set content and wait for it to load await this.page.setContent(htmlContent, { waitUntil: 'networkidle' }); // Wait a bit more for any CSS animations or font loading await this.page.waitForTimeout(500); // Take screenshot with optional transparency support const screenshotOptions = { path: options.outputPath, type: 'png', fullPage: false, // Use viewport size clip: { x: 0, y: 0, width: options.width, height: options.height } }; // Enable transparency for PNG if requested if (options.transparent) { screenshotOptions.omitBackground = true; } await this.page.screenshot(screenshotOptions); } catch (error) { throw new Error(`Failed to render HTML to image: ${error.message}`); } } /** * Close browser instance and cleanup */ async close() { try { if (this.page) { await this.page.close(); this.page = null; } if (this.browser) { await this.browser.close(); this.browser = null; } } catch (error) { // Log error but don't throw - cleanup should be best effort console.warn(`Warning during browser cleanup: ${error.message}`); } } /** * Check if browser is initialized */ isInitialized() { return this.browser !== null && this.page !== null; } /** * Get page instance for advanced operations */ getPage() { return this.page; } } /** * Singleton browser service instance for reuse across tool calls */ class BrowserServiceManager { /** * Get or create browser service instance */ static async getInstance() { if (!this.instance) { this.instance = new BrowserService(); } // Initialize if not already initialized if (!this.instance.isInitialized() && !this.initPromise) { this.initPromise = this.instance.initialize(); } if (this.initPromise) { await this.initPromise; this.initPromise = null; } return this.instance; } /** * Close and cleanup browser service */ static async cleanup() { if (this.instance) { await this.instance.close(); this.instance = null; } this.initPromise = null; } } BrowserServiceManager.instance = null; BrowserServiceManager.initPromise = null; export { BrowserServiceManager };