const express = require('express'); const { chromium, devices } = require('playwright'); const cors = require('cors'); const dotenv = require('dotenv'); const os = require('os'); dotenv.config(); const config = { maxTextLength: 100, defaultViewport: { width: 1920, height: 1080 }, iPhoneViewport: { width: 390, height: 844 } }; let defaultBrowser, defaultPage, iPhoneBrowser, iPhonePage; const utils = { async initialize() { if (!defaultBrowser) { defaultBrowser = await chromium.launch({ headless: true }); const context = await defaultBrowser.newContext({ viewport: config.defaultViewport }); defaultPage = await context.newPage(); await defaultPage.goto('https://www.bratgenerator.com/', { waitUntil: 'networkidle' }); try { await defaultPage.click('#onetrust-accept-btn-handler', { timeout: 2000 }); } catch {} await defaultPage.evaluate(() => setupTheme('white')); } }, async generateBrat(text) { await defaultPage.fill('#textInput', text); await defaultPage.waitForTimeout(500); const overlay = defaultPage.locator('#textOverlay'); return overlay.screenshot({ timeout: 5000 }); }, async close() { if (defaultBrowser) await defaultBrowser.close(); } }; const utils2 = { async initialize() { if (!iPhoneBrowser) { iPhoneBrowser = await chromium.launch({ headless: true }); // Buat context dengan konfigurasi iPhone yang spesifik const context = await iPhoneBrowser.newContext({ ...devices['iPhone 13 Pro'], locale: 'en-US', deviceScaleFactor: 3, isMobile: true, hasTouch: true, colorScheme: 'light', forcedColors: 'none', viewport: config.iPhoneViewport, userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1' }); iPhonePage = await context.newPage(); // Inject necessary meta tags and CSS await iPhonePage.route('**/*', async route => { if (route.request().resourceType() === 'document') { const response = await route.fetch(); const text = await response.text(); const modifiedHtml = text.replace('', ` ` ); route.fulfill({ response, body: modifiedHtml, headers: { ...response.headers(), 'content-type': 'text/html' } }); } else { route.continue(); } }); await iPhonePage.goto('https://www.bratgenerator.com/', { waitUntil: 'networkidle', timeout: 30000 }); // Set viewport dan emulasi perangkat await iPhonePage.setViewportSize(config.iPhoneViewport); // Inject script untuk mengubah emoji await iPhonePage.addScriptTag({ content: ` function replaceWithEmojiSpans() { const textInput = document.querySelector('#textInput'); const textOverlay = document.querySelector('#textOverlay'); if (textInput && textOverlay) { const emojiRegex = /[\u{1F300}-\u{1F9FF}]/gu; const wrapEmojisWithSpan = (text) => { return text.replace(emojiRegex, match => '' + match + '' ); }; const originalHandler = textInput.oninput; textInput.oninput = function(e) { if (originalHandler) originalHandler.call(this, e); textOverlay.innerHTML = wrapEmojisWithSpan(this.value); }; } } replaceWithEmojiSpans(); ` }); try { await iPhonePage.click('#onetrust-accept-btn-handler', { timeout: 2000 }); } catch {} await iPhonePage.evaluate(() => setupTheme('white')); } }, async generateBrat(text) { await iPhonePage.fill('#textInput', text); // Tunggu animasi dan render selesai await iPhonePage.waitForTimeout(1000); // Pastikan emoji sudah di-wrap dengan span await iPhonePage.evaluate((text) => { const textOverlay = document.querySelector('#textOverlay'); const emojiRegex = /[\u{1F300}-\u{1F9FF}]/gu; const wrappedText = text.replace(emojiRegex, match => '' + match + '' ); textOverlay.innerHTML = wrappedText; }, text); await iPhonePage.waitForTimeout(500); const overlay = iPhonePage.locator('#textOverlay'); return overlay.screenshot({ timeout: 5000, scale: 'device' }); }, async close() { if (iPhoneBrowser) await iPhoneBrowser.close(); } }; const app = express(); app.use(express.json()); app.use(cors()); app.set('json spaces', 3); app.get('*', async (req, res) => { try { const { q, type } = req.query; if (!q) { return res.status(200).json({ name: 'HD Bart Generator API', message: 'Parameter q di perlukan', version: '2.1.0', runtime: { os: os.type(), platform: os.platform(), architecture: os.arch(), cpuCount: os.cpus().length, uptime: `${os.uptime()} seconds`, memoryUsage: `${Math.round((os.totalmem() - os.freemem()) / 1024 / 1024)} MB used of ${Math.round(os.totalmem() / 1024 / 1024)} MB` } }); } const imageBuffer = type === "iphone" ? await utils2.generateBrat(q) : await utils.generateBrat(q); res.set('Content-Type', 'image/png'); res.send(imageBuffer); } catch (error) { console.error(error); res.status(500).json({ status: false, message: 'Error generating image', error: process.env.NODE_ENV === 'development' ? error.message : undefined }); } }); const PORT = process.env.PORT || 7860; app.listen(PORT, async () => { console.log(`Server running on port ${PORT}`); await Promise.all([utils.initialize(), utils2.initialize()]); }); process.on('SIGINT', async () => { await Promise.all([utils.close(), utils2.close()]); process.exit(0); });