contaigents
Version:
Modular AI Content Ecosystem with Audio Generation
182 lines (181 loc) • 6.23 kB
JavaScript
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 };