Automasi max.ai converter PDF to HTML Tech : Node.js (common), Socket.io, Puppeteer target : https://www.maxai.co/pdf-tools/pdf-to-html/ selector : 1. input = <input type="file" title="" accept="application/pdf" multiple="" class="css-1gcv6dl"> 2. finish button = <button class="MuiButtonBase-root MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeLarge MuiButton-containedSizeLarge MuiButton-colorPrimary MuiButton-disableElevation MuiButton-fullWidth MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeLarge MuiButton-containedSizeLarge MuiButton-colorPrimary MuiButton-disableElevation MuiButton-fullWidth css-y450x3" tabindex="0" type="button"><p class="MuiTypography-root MuiTypography-body1 css-1ncveae">Finish</p><svg class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium css-10lx4aw" focusable="false" aria-hidden="true" viewBox="0 0 24 24" data-testid="EastIcon"><path d="m15 5-1.41 1.41L18.17 11H2v2h16.17l-4.59 4.59L15 19l7-7z"></path></svg><span class="MuiTouchRipple-root css-w0pj6f"></span></button> 3. download button = <button class="MuiButtonBase-root MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeMedium MuiButton-containedSizeMedium MuiButton-colorPrimary MuiButton-disableElevation MuiButton-fullWidth MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeMedium MuiButton-containedSizeMedium MuiButton-colorPrimary MuiButton-disableElevation MuiButton-fullWidth css-1kwztf9" tabindex="0" type="button"><div class="MuiBox-root css-axw7ok"><svg class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium css-wbvhrc" focusable="false" aria-hidden="true" viewBox="0 0 24 24" data-testid="FileDownloadOutlinedIcon"><path d="M18 15v3H6v-3H4v3c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2v-3zm-1-4-1.41-1.41L13 12.17V4h-2v8.17L8.41 9.59 7 11l5 5z"></path></svg><p class="MuiTypography-root MuiTypography-body1 css-ww1t3z">Download</p></div><span class="MuiTouchRipple-root css-w0pj6f"></span></button> 4. new upload = <button class="MuiButtonBase-root MuiButton-root MuiButton-outlined MuiButton-outlinedPrimary MuiButton-sizeMedium MuiButton-outlinedSizeMedium MuiButton-colorPrimary MuiButton-disableElevation MuiButton-fullWidth MuiButton-root MuiButton-outlined MuiButton-outlinedPrimary MuiButton-sizeMedium MuiButton-outlinedSizeMedium MuiButton-colorPrimary MuiButton-disableElevation MuiButton-fullWidth css-bty1zy" tabindex="0" type="button"><div class="MuiBox-root css-axw7ok"><svg class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium css-wbvhrc" focusable="false" aria-hidden="true" viewBox="0 0 24 24" data-testid="RestartAltOutlinedIcon"><path d="M6 13c0-1.65.67-3.15 1.76-4.24L6.34 7.34C4.9 8.79 4 10.79 4 13c0 4.08 3.05 7.44 7 7.93v-2.02c-2.83-.48-5-2.94-5-5.91m14 0c0-4.42-3.58-8-8-8-.06 0-.12.01-.18.01l1.09-1.09L11.5 2.5 8 6l3.5 3.5 1.41-1.41-1.08-1.08c.06 0 .12-.01.17-.01 3.31 0 6 2.69 6 6 0 2.97-2.17 5.43-5 5.91v2.02c3.95-.49 7-3.85 7-7.93"></path></svg><p class="MuiTypography-root MuiTypography-body1 css-ww1t3z">Start over</p></div><span class="MuiTouchRipple-root css-w0pj6f"></span></button> Puppeteernya buatkan safe/stealth berikut existing project structurenya: pacakge.json { "name": "pdftohtml_cvgen", "version": "1.0.0", "description": "", "homepage": "https://github.com/refkinscallv/pdftohtml_cvgen#readme", "bugs": { "url": "https://github.com/refkinscallv/pdftohtml_cvgen/issues" }, "repository": { "type": "git", "url": "git+https://github.com/refkinscallv/pdftohtml_cvgen.git" }, "license": "MIT", "author": "Refkinscallv <refkinscallv@gmail.com>", "type": "commonjs", "main": "src/index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "node src/index.js", "dev": "nodemon", "format": "prettier --ignore-path .prettierignore --write src/**/*.js" }, "dependencies": { "@refkinscallv/express-routing": "^1.2.1", "dotenv": "^17.2.1", "puppeteer-extra": "^3.3.6", "puppeteer-extra-plugin-stealth": "^2.11.2", "socket.io": "^4.8.1" }, "devDependencies": { "nodemon": "^3.1.10", "prettier": "^3.6.2" } } nodemon.json { "watch": ["src"], "ext": "js", "exec": "node ./src/index.js" } src/index.js const Boot = require('./boot') Boot.run({ server: { url: 'http://localhost:3456', port: 3456, }, }) src/boot.js const Boot = require('./boot') Boot.run({ server: { url: 'http://localhost:3456', port: 3456, }, }) src/server.js const http = require('http') module.exports = class Server { static server = null static init(data) { const { port, url } = data this.server = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'text/html' }) res.end('Server is running\n') }) this.server.listen(port, () => { console.log([SERVER] Application already running : ${url || http://localhost:${port}}) }) this.server.on('error', (err) => { console.error('[SERVER] Error:', err.message) }) } } src/socket.js const { Server: SocketIO } = require('socket.io') const Server = require('./server') module.exports = class Socket { static io = null static init() { this.io = new SocketIO(Server.server, { cors: { origin: '*' } }) this.io.on('connection', (socket) => { console.log('[SOCKET] Client connected:', socket.id) socket.on('disconnect', () => { console.log('[SOCKET] Client disconnected:', socket.id) }) }) } } tinggal puppeteer yang belum