|
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'); |
|
|
|
|
|
const pngBuffer = await overlay.screenshot({ |
|
timeout: 3000, |
|
type: 'png' |
|
}); |
|
|
|
const webpBuffer = await sharp(pngBuffer) |
|
.webp({ quality: 80 }) |
|
.toBuffer(); |
|
|
|
|
|
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)" |
|
} |
|
}); |
|
} |
|
|
|
|
|
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}`); |
|
}); |