import express from 'express'; import { chromium } from 'playwright'; import cors from 'cors'; import dotenv from 'dotenv'; import os from 'os'; import fetch from 'node-fetch'; import sharp from 'sharp'; 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' }; let browser, page; const utils = { async initialize() { if (!browser) { 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(); }); 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')); } }, async generateBrat(text) { await page.fill('#textInput', text); const overlay = page.locator('#textOverlay'); return overlay.screenshot({ timeout: 3000 }); }, async close() { if (browser) await browser.close(); } }; const app = express(); app.use(express.json()); app.use(cors()); app.get('/brat', async (req, res) => { try { const { q } = req.query; if (!q) { return res.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 = 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 font = 'BlueArchive'; const crossBuffer = Buffer.from(''); const haloBuffer = Buffer.from(''); const splitText = (text) => { if (Array.isArray(text)) return text; if (text.includes(' ')) { return text.split(' ').filter(i => i.trim()); } else if (text.match(/^[A-Z][a-z]*[A-Z][a-z]*$/)) { return text.replace(/[A-Z]/g, t => ' ' + t).trim().split(' '); } else { const h = Math.floor(text.length / 2); return [text.substring(0, h), text.substring(h)]; } }; const baLogo = async (text, left = 0) => { const [head, tail] = splitText(text); if (!head || !tail) throw new Error('Invalid input text'); let width = 32, height = 260; const top = 208; const matrix = [[1, -0.35], [0, 1]]; const comps = []; const headPart = sharp({ text: { font, text: `${head}`, dpi: 72, rgba: true, } }).affine(matrix, { background: '#fff0', interpolator: sharp.interpolators.nohalo }).png(); const headMeta = await headPart.metadata(); const w = width + headMeta.width - 162; const dl = w < 0 ? 0 : w + left; const tailPart = sharp({ text: { font, text: `${head}${tail}`, dpi: 72, rgba: true, } }).affine(matrix, { background: '#fff0', interpolator: sharp.interpolators.nohalo }).png(); const tailMeta = await tailPart.metadata(); comps.push({ input: await tailPart.toBuffer(), left: width, top, }); comps.push({ input: crossBuffer, left: dl + 4, top: 4, }); width += (tailMeta.width < 256 ? 256 : tailMeta.width ) + 64; height += 144; if (width < 500) width = 500; return sharp({ create: { width, height, channels: 4, background: '#fff', }, }).composite(comps).png(); }; app.get('/balogo', async (req, res) => { try { const { text, text2 } = req.query; if (!text) return res.status(400).send('Text is required'); const logoImage = await baLogo(text); res.set('Content-Type', 'image/png'); res.send(await logoImage.toBuffer()); } catch (error) { res.status(500).send('Error generating logo: ' + error.message); } }); async function baLogov2(textL = "蔚蓝", textR = "档案", x = "-15", y = "0", tp = false) { const browser = await chromium.launch({ headless: true }); const context = await browser.newContext({ 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' }); const page = await context.newPage(); await page.goto('https://symbolon.pages.dev/'); await page.waitForSelector('#canvas'); await page.fill('#textL', textL); await page.fill('#textR', textR); await page.fill('#graphX', x); await page.fill('#graphY', y); await page.waitForTimeout(500); const imageBuffer = await page.$eval('#canvas', (canvas) => { return canvas.toDataURL('image/png').split(',')[1]; }); const buffer = Buffer.from(imageBuffer, 'base64'); await browser.close(); return buffer; } app.get('/balogo/v2', async (req, res) => { try { const { textL = "蔚蓝", textR = "档案", x = "-15", y = "0", tp = "false" } = req.query; const tpBool = tp === "true"; const imageBuffer = await baLogov2(textL, textR, x, y, tpBool); res.setHeader('Content-Type', 'image/png'); res.send(imageBuffer); } catch (error) { console.error(error); res.status(500).json({ error: "Error generating logo" }); } }); async function baLogov3(textL = "蔚蓝", textR = "档案") { const browser = await chromium.launch({ headless: true }); const context = await browser.newContext({ 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' }); const page = await context.newPage(); await page.goto('https://appleneko2001-bluearchive-logo.vercel.app/'); try { await page.waitForSelector('#canvas', { timeout: 30000 }); } catch (err) { console.error('Canvas tidak ditemukan:', err); } try { await page.waitForSelector('#textL', { state: 'visible', timeout: 10000 }); await page.fill('#textL', textL); } catch (err) { console.error('Elemen #textL gagal diisi:', err); } try { await page.waitForSelector('#textR', { state: 'visible', timeout: 10000 }); await page.fill('#textR', textR); } catch (err) { console.error('Elemen #textR gagal diisi:', err); } await page.waitForTimeout(2000); const imageBuffer = await page.$eval('#canvas', (canvas) => { return canvas.toDataURL('image/png').split(',')[1]; }); const buffer = Buffer.from(imageBuffer, 'base64'); await browser.close(); return buffer; } app.get('/balogo/v3', async (req, res) => { try { const { textL = "蔚蓝", textR = "档案" } = req.query; const imageBuffer = await baLogov3(textL, textR); res.setHeader('Content-Type', 'image/png'); res.send(imageBuffer); } catch (error) { console.error(error); res.status(500).json({ error: "Error generating logo" }); } }); async function baLogov4(textL = "蔚蓝", textR = "档案") { const browser = await chromium.launch({ headless: true }); const context = await browser.newContext({ 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' }); const page = await context.newPage(); await page.goto('https://ba.api.hli.icu/'); try { await page.waitForSelector('#canvas', { timeout: 30000 }); } catch (err) { console.error('Canvas tidak ditemukan:', err); } try { await page.waitForSelector('#textL', { state: 'visible', timeout: 10000 }); await page.fill('#textL', textL); } catch (err) { console.error('Elemen #textL gagal diisi:', err); } try { await page.waitForSelector('#textR', { state: 'visible', timeout: 10000 }); await page.fill('#textR', textR); } catch (err) { console.error('Elemen #textR gagal diisi:', err); } await page.waitForTimeout(500); const imageBuffer = await page.$eval('#canvas', (canvas) => { return canvas.toDataURL('image/png').split(',')[1]; }); const buffer = Buffer.from(imageBuffer, 'base64'); await browser.close(); return buffer; } app.get('/balogo/v4', async (req, res) => { try { const { textL = "蔚蓝", textR = "档案" } = req.query; const imageBuffer = await baLogov4(textL, textR); res.setHeader('Content-Type', 'image/png'); res.send(imageBuffer); } catch (error) { console.error(error); res.status(500).json({ error: "Error generating logo" }); } }); app.get('/rayso', async (req, res) => { const { code, title = 'app.js', theme = 'supabase', language = '', darkMode = 'true', background = 'false' } = req.query; if (!code) return res.status(400).json({ error: 'Parameter "code" (Base64 encoded) diperlukan' }); try { const encodedCode = Buffer.from(code, 'base64'); const url = `https://www.ray.so/#code=${encodeURIComponent(encodedCode)}&title=${encodeURIComponent(title)}&theme=${theme}&language=${language}&background=${background}&darkMode=${darkMode}`; const browser = await chromium.launch({ headless: true }); const context = await browser.newContext({ 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' }); const page = await context.newPage(); await page.goto(url); await page.waitForSelector('.Frame_frame__rcr69'); const elementHandle = await page.$('.Frame_frame__rcr69'); const screenshotBuffer = await elementHandle.screenshot({ fullPage: true }); await browser.close(); res.setHeader('Content-Type', 'image/png'); return res.status(200).end(screenshotBuffer); } catch (error) { return res.status(500).json({ error: 'Gagal mengambil screenshot', details: error.message }); } }); app.get('/carbon', async (req, res) => { const { code, theme } = req.query; if (!code) return res.status(400).json({ error: 'Parameter "code" diperlukan.' }); if (!theme) return res.status(400).json({ error: 'Parameter "theme" diperlukan.' }); try { const browser = await chromium.launch({ headless: true }); const context = await browser.newContext({ 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', acceptDownloads: true }); const page = await context.newPage(); await page.goto('https://carbon.now.sh/'); await page.click('input[aria-labelledby="theme-dropdown"]'); await page.keyboard.type(theme); await page.keyboard.press('Enter'); await page.click('pre.CodeMirror-line span[role="presentation"]'); await page.keyboard.press('Control+A'); await page.keyboard.press('Backspace'); await page.keyboard.type(code); await page.waitForSelector('button[data-cy="quick-export-button"]'); const [download] = await Promise.all([ page.waitForEvent('download'), page.click('button[data-cy="quick-export-button"]') ]); const downloadPath = await download.path(); res.setHeader('Content-Type', 'application/octet-stream'); return res.status(200).sendFile(downloadPath); } catch (error) { return res.status(500).json({ error: 'Gagal memproses permintaan.', details: error.message }); } finally { await browser.close(); } }); app.get('/recoded', async (req, res) => { const { code, colors, font, lang, bg } = req.query; if (!code) return res.status(400).json({ error: 'Parameter "code" diperlukan.' }); if (!colors) return res.status(400).json({ error: 'Parameter "colors" diperlukan.' }); if (!font) return res.status(400).json({ error: 'Parameter "font" diperlukan.' }); if (!lang) return res.status(400).json({ error: 'Parameter "lang" diperlukan.' }); if (!bg) return res.status(400).json({ error: 'Parameter "bg" diperlukan.' }); try { const browser = await chromium.launch({ headless: true }); const context = await browser.newContext({ 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', acceptDownloads: true, }); const page = await context.newPage(); await page.goto('https://recoded.netlify.app/'); await page.fill('textarea.npm__react-simple-code-editor__textarea', code); await page.click('div.input-box[style*="120px"]:has-text("VS Code Dark")'); await page.keyboard.type(colors); await page.keyboard.press('Enter'); await page.click('div[data-testid="font-select"]'); await page.keyboard.type(font); await page.keyboard.press('Enter'); await page.click('div[data-testid="language-select"]'); await page.keyboard.type(lang); await page.keyboard.press('Enter'); await page.click('div[data-testid="background-color-select"]'); await page.keyboard.type(bg); await page.keyboard.press('Enter'); const [download] = await Promise.all([ page.waitForEvent('download'), page.click('button.button.export') ]); const downloadPath = await download.path(); res.setHeader('Content-Type', 'image/png'); return res.status(200).send(downloadPath); } catch (error) { return res.status(500).json({ error: 'Gagal memproses permintaan.', details: error.message }); } finally { await browser.close(); } }); app.get('/chalkist', async (req, res) => { const { code, title } = req.query; if (!code) return res.status(400).json({ error: 'Parameter "code" diperlukan.' }); if (!title) return res.status(400).json({ error: 'Parameter "title" diperlukan.' }); let browser; try { browser = await chromium.launch({ headless: true }); const context = await browser.newContext({ 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', }); const page = await context.newPage(); await page.goto('https://chalk.ist/'); // Isi kolom judul const titleSelector = 'div[data-placeholder="Untitled..."]'; await page.focus(titleSelector); await page.keyboard.type(title); // Isi kolom kode const codeSelector = 'textarea.editor.font-config'; await page.fill(codeSelector, code); // Tunggu elemen muncul untuk screenshot const screenshotSelector = 'div[data-editor-frame]'; await page.waitForSelector(screenshotSelector); // Ambil screenshot elemen const element = await page.$(screenshotSelector); const screenshotBuffer = await element.screenshot(); res.setHeader('Content-Type', 'image/png'); res.status(200).send(screenshotBuffer); } catch (error) { console.error('Error:', error); res.status(500).json({ error: 'Gagal memproses permintaan.', details: error.message }); } finally { if (browser) await browser.close(); } }); app.get('/brat/v2', async (req, res) => { const { text } = req.query; if (!text) return res.status(400).json({ error: 'Parameter "text" diperlukan.' }); try { const browser = await chromium.launch({ headless: true }); const context = await browser.newContext({ 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', acceptDownloads: true, }); const page = await context.newPage(); await page.goto('https://brat-generator.net/'); await page.fill('input[class="w-full p-3 text-base border border-gray-200 rounded-lg mb-5 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 transition-colors"]', text); try { await page.waitForSelector('button[class="px-5 py-3 text-sm font-medium rounded-lg flex items-center justify-between flex-1 transition-all bg-white border-2 border-blue-500 text-blue-600"]', { visible: true }); await page.click('button[class="px-5 py-3 text-sm font-medium rounded-lg flex items-center justify-between flex-1 transition-all bg-white border-2 border-blue-500 text-blue-600"]'); } catch (error) { console.log("Tombol 'White Background' tidak ditemukan atau tidak terlihat, melewati langkah ini."); } try { await page.waitForSelector('button[class="w-full mt-5 px-5 py-3 text-sm font-medium rounded-lg transition-all bg-blue-500 text-white hover:bg-blue-600"]', { visible: true }); await page.click('button[class="w-full mt-5 px-5 py-3 text-sm font-medium rounded-lg transition-all bg-blue-500 text-white hover:bg-blue-600"]'); } catch (error) { console.log("Tombol 'Download Image' tidak ditemukan atau tidak terlihat, melewati langkah ini."); } await page.waitForSelector('canvas[class="absolute top-0 left-0 w-full h-full bg-white shadow-inner"]', { visible: true }); await page.waitForTimeout(800); const canvas = await page.$('canvas[class="absolute top-0 left-0 w-full h-full bg-white shadow-inner"]'); const screenshot = await canvas.screenshot(); res.setHeader('Content-Type', 'image/png'); res.status(200).send(screenshot); } catch (error) { return res.status(500).json({ error: 'Gagal memproses permintaan.', details: error.message }); } finally { await browser.close(); } }); app.get('/', async (req, res) => { res.status(200).send('Maker API'); }); const PORT = process.env.PORT || 7860; app.listen(PORT, async () => { console.log(`Server running on port ${PORT}`); await utils.initialize(); }); process.on('SIGINT', async () => { await utils.close(); process.exit(0); });