const express = require('express'); const { chromium } = require('playwright'); const QRCode = require('qrcode'); const Jimp = require('jimp'); const QrCodeReader = require('qrcode-reader'); //const fetch = require('node-fetch'); const app = express(); const port = 7860; // Global variable to hold the browser instance and context let browser; let browserContext; app.use(express.json()); app.get('/', (req, res) => { console.log('request to /'); res.send('halo kontol'); }); app.post('/screenshot', async (req, res) => { console.log('request to /screenshot'); const { ua, url, type, width, height, language, fullpage } = req.body; if (!url) return res.send('please input url'); await processScreenshot(req, res, { ua, url, type, width, height, language, fullpage }); }); app.get('/screenshot', async (req, res) => { console.log('request to /screenshot (get)'); const { ua, url, type, width, height, language, fullpage } = req.query; if (!url) return res.send('please input url'); await processScreenshot(req, res, { ua, url, type, width, height, language, fullpage }); }); app.get('/qrcode', async (req, res) => { try { const { mode, url } = req.query; if (!mode || !url) { return res.status(400).send('what?'); } if (mode === 'create') { QRCode.toDataURL(url, (err, src) => { if (err) return res.send({ error: 'Error generating QR code' }); res.type('png').send(Buffer.from(src.split(",")[1], "base64")); }); } else if (['read', 'scan'].includes(mode)) { const rts = await fetch(url); if (!rts.ok || rts.status !== 200) return res.send({ status: 400, message: 'Can\'t buffer url' }); if (!/image/.test(rts.headers.get('content-type'))) return res.send({ status: 400, message: 'Only image type' }); const buffer = Buffer.from(await rts.arrayBuffer()); const image = await Jimp.read(buffer); image.grayscale().contrast(1).normalize(); const qrCodeReader = new QrCodeReader(); qrCodeReader.callback = (err, value) => { if (err) return res.send({ error: 'Error reading QR code' }); res.json({ result: value.result }); }; const bitmap = image.bitmap; qrCodeReader.decode({ width: bitmap.width, height: bitmap.height, data: bitmap.data }); } else { QRCode.toDataURL(url, (err, src) => { if (err) return res.send({ error: 'Error generating QR code' }); res.type('png').send(Buffer.from(src.split(",")[1], "base64")); }); } } catch (error) { console.error(error); res.send({ error: 'Internal Server Error' }); } }); async function processScreenshot(req, res, { ua, url, type, width, height, language, fullpage }) { try { console.log('process screenshot'); // Launch browser and create a context if it doesn't exist if (!browser) { browser = await chromium.launch({ headless: true, args: ["--no-sandbox", "--disable-setuid-sandbox"] }); } // Reuse or create a new context if (!browserContext) { browserContext = await browser.newContext({ userAgent: ua || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', viewport: { width: Number(width) || 1280, height: Number(height) || 800 }, deviceScaleFactor: 2, locale: language || 'id-ID', extraHTTPHeaders: { 'Accept-Language': language || 'id-ID', } }); } const page = await browserContext.newPage(); if (language) { await page.emulateMedia({ colorScheme: "dark" }); } await page.goto(url, { waitUntil: 'networkidle' }); // Handle JavaScript challenges await page.waitForLoadState('networkidle'); let screenshotOptions = { fullPage: fullpage == 1 ? true : false, type: 'png', omitBackground: false }; if (type === 'desktop') { await page.setViewportSize({ width: 1920, height: 1080, }); } else if (type === 'mobile') { await page.setViewportSize({ width: 375, height: 667, }); } else if (type === 'tablet') { await page.setViewportSize({ width: 768, height: 1024, }); } else if (type === 'iphone') { await page.setViewportSize({ width: 375, height: 667, }); } else if (type === 'custom') { if (width && height > 4096) { await page.close(); return res.json({ "status": 400, "message": "Width and height values should not exceed 4096 pixels." }); } if (width && height < 400) { await page.close(); return res.json({ "status": 400, "message": "Width and height values should not be less than 400 pixels." }); } await page.setViewportSize({ width: Number(width) || 375, height: Number(height) || 667, }); } await page.waitForTimeout(5000); const screenshot = await page.screenshot(screenshotOptions); res.writeHead(200, { 'Content-Type': 'image/png', 'Content-Length': screenshot.length, }); res.end(screenshot); console.log('process done'); await page.close(); // Close the tab instead of the browser // Periodically close and reopen the browser to avoid memory bloat if (browserContext.pages().length > 10) { // Arbitrary threshold await browserContext.close(); browserContext = await browser.newContext(); } // Trigger garbage collection if (global.gc) { global.gc(); } } catch (e) { console.error(e); return res.json({ status: 400, message: 'error when take a screenshot' }); } } app.listen(port, () => { console.log(`Server is running on port ${port}`); });