import express from 'express'; import { chromium } from 'playwright'; import cors from 'cors'; import dotenv from 'dotenv'; import os from 'os'; import sharp from 'sharp'; import path from 'path'; import fs from 'fs'; import crypto from 'crypto'; import { tmpdir } from 'os'; import webp from 'node-webpmux'; dotenv.config(); const config = { maxTextLength: 100, viewport: { width: 1920, height: 1080 }, userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', metadata: { packname: "HD Bart Generator", author: "API", categories: ["meme"] } }; const utils = { async addExifMetadata(buffer, metadata) { const tmpFileIn = path.join(tmpdir(), `${crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.webp`); const tmpFileOut = path.join(tmpdir(), `${crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.webp`); fs.writeFileSync(tmpFileIn, buffer); const img = new webp.Image(); const json = { "sticker-pack-id": `https://github.com/DikaArdnt/Hisoka-Morou`, "sticker-pack-name": metadata.packname, "sticker-pack-publisher": metadata.author, "emojis": metadata.categories || [""] }; const exifAttr = Buffer.from([0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x41, 0x57, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00]); const jsonBuff = Buffer.from(JSON.stringify(json), "utf-8"); const exif = Buffer.concat([exifAttr, jsonBuff]); exif.writeUIntLE(jsonBuff.length, 14, 4); await img.load(tmpFileIn); fs.unlinkSync(tmpFileIn); img.exif = exif; await img.save(tmpFileOut); const finalBuffer = fs.readFileSync(tmpFileOut); fs.unlinkSync(tmpFileOut); return finalBuffer; }, async createBrowserInstance() { const browser = await chromium.launch({ headless: true }); const context = await browser.newContext({ viewport: config.viewport, userAgent: config.userAgent }); await context.route('**/*', (route) => { const url = route.request().url(); if (url.endsWith('.png') || url.endsWith('.jpg') || url.includes('google-analytics')) { return route.abort(); } route.continue(); }); const page = await context.newPage(); await page.goto('https://www.bratgenerator.com/', { waitUntil: 'domcontentloaded', timeout: 10000 }); try { await page.click('#onetrust-accept-btn-handler', { timeout: 2000 }); } catch { } await page.evaluate(() => setupTheme('white')); return { browser, page }; }, async generateBrat(text, metadata) { const { browser, page } = await this.createBrowserInstance(); try { await page.fill('#textInput', text); const overlay = page.locator('#textOverlay'); // Take screenshot and convert to WebP const pngBuffer = await overlay.screenshot({ timeout: 3000, type: 'png' }); const webpBuffer = await sharp(pngBuffer) .webp({ quality: 80 }) .toBuffer(); // Add EXIF metadata return await this.addExifMetadata(webpBuffer, metadata); } finally { await browser.close(); } } }; const app = express(); app.use(express.json()); app.use(cors()); app.get('*', async (req, res) => { try { const { q, packname, author } = req.query; if (!q) { return res.json({ name: 'HD Bart Generator API', message: 'Parameter q diperlukan', 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` }, parameters: { q: "Teks yang akan ditampilkan (required)", packname: "Nama pack sticker (optional)", author: "Nama author sticker (optional)" } }); } // Create metadata object using query parameters or defaults const metadata = { packname: packname || config.metadata.packname, author: author || config.metadata.author, categories: config.metadata.categories }; const imageBuffer = await utils.generateBrat(q, metadata); res.set('Content-Type', 'image/webp'); res.send(imageBuffer); } catch (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, () => { console.log(`Server running on port ${PORT}`); });