playwright / index.mjs
and
w
9609abc
raw
history blame
6.44 kB
import * as http from 'http';
import * as url from 'url';
import * as path from 'path';
import { chromium } from 'playwright';
import { locks } from 'web-locks';
import crypto from 'crypto';
import fs from 'fs';
import { setTimeout } from 'timers/promises';
import { WebSocketServer } from 'ws';
// npm i playwright
// npm i web-locks
// npm i ws
// nohup cloudflared tunnel --url http://localhost:8080 --no-autoupdate & (or setsid)
// setsid node playwright.mjs (nohup doesnt work)
// curl 'https://gowah44030-playwright.hf.space/..
// https://gowah44030-playwright.hf.space/screenshot?js=1&url=https://www.investing.com
globalThis.state = Object.assign(globalThis.state||{}, {
browser: null,
context: null,
});
async function text_from_url(url, cookie) {
let { browser, context } = globalThis.state;
if (!browser) {
await locks.request('playwright_browser', async lock => {
browser = globalThis.state.browser = await chromium.launch();
context = globalThis.state.context = await browser.newContext({
javaScriptEnabled: false
}/*devices['iPhone 11']*/);
});
}
const page = await context.newPage();
if (cookie) {
if (cookie.endsWith(';')) cookie = cookie.slice(0, -1);
let cookies = cookie.split(';');
cookies = cookies.map(it=>{
let [name, value] = it.split('=');
return {name: name.trim(), value: value.trim(), url};
});
context.addCookies(cookies);
}
await context.route("**/*.{png,jpg,jpeg,css,js}", route => route.abort());
await page.goto(url);
let text;
let i = 0;
while (true) {
let new_text = await page.evaluate(() => document.body.innerText);
if (i > 5 || new_text?.length > 200) {
text = new_text;
break;
}
i++;
await new Promise(resolve=>setTimeout(resolve, 1000));
}
await page.close();
//await context.close();
// await browser.close();
return text;
}
async function screenshot(url, cookie, js_enabled) {
let { browser, context } = globalThis.state;
if (!browser) {
await locks.request('playwright_browser', async lock => {
browser = globalThis.state.browser = await chromium.launch();
context = globalThis.state.context = await browser.newContext({
javaScriptEnabled: js_enabled
}/*devices['iPhone 11']*/);
});
}
const page = await context.newPage();
if (cookie) {
if (cookie.endsWith(';')) cookie = cookie.slice(0, -1);
let cookies = cookie.split(';');
cookies = cookies.map(it=>{
let [name, value] = it.split('=');
return {name: name.trim(), value: value.trim(), url};
});
context.addCookies(cookies);
}
//await context.route("**/*.{png,jpg,jpeg,css,js}", route => route.abort());
await page.goto(url);
await setTimeout(2000);
// let id = crypto.randomUUID();
// let path = `/code/${id}.png`;
// await page.screenshot({ path, fullPage: true });
const buffer = await page.screenshot({fullPage: true});
await page.close();
//await context.close();
// await browser.close();
return buffer;
}
async function evaluate(url, code) {
let { browser, context } = globalThis.state;
if (!browser) {
await locks.request('playwright_browser', async lock => {
browser = globalThis.state.browser = await chromium.launch();
context = globalThis.state.context = await browser.newContext({
javaScriptEnabled: true
}/*devices['iPhone 11']*/);
});
}
const page = await context.newPage();
page.on('console', async (msg) => {console.log(msg);});
//await context.route("**/*.{png,jpg,jpeg,css,js}", route => route.abort());
await page.goto(url);
let result = await page.evaluate(code);
await page.close();
return result;
}
const server = http.createServer(async function(req, res) {
const {query, pathname} = url.parse(req.url, true);
res.setHeader('Access-Control-Allow-Origin', '*')
let _url = query.url;
let cookie = query.cookie;
let js_enabled = query.js;
try {
if (pathname == '/text') {
let text = await text_from_url(_url, cookie);
res.end(text);
} else if (pathname == '/screenshot') {
let buffer = await screenshot(_url, cookie, js_enabled);
res.writeHead(200,{'Content-type':'image/png'});
res.end(buffer);
// const readStream = fs.createReadStream(path);
// res.writeHead(200,{'Content-type':'image/png'});
// readStream.pipe(res);
// res.end();
} else if (pathname == '/evaluate') {
const buffers = [];
for await (const chunk of req) {
buffers.push(chunk);
}
const data = Buffer.concat(buffers).toString();
const {url, code} = JSON.parse(data);
try {
let result = await evaluate(url, code);
res.end(''+result);
} catch (e) {
res.end(e.toString());
}
} else {
res.end('hello');
}
} catch (e) {
res.end(e.toString());
}
});
const wss = new WebSocketServer({ server });
wss.on('connection', function connection(ws, req) {
console.log('wss.connection', req.url, req.headers);
ws.isAlive = true;
ws.on('pong', function heartbeat(){ //'Pong messages are automatically sent in response to ping messages as required by the spec.'
//console.log('pong');
this.isAlive = true;
});
ws.on('message', function message(data) {
console.log('received: %s', data);
});
ws.addEventListener('close', function close() {
console.log('ws.close');
});
//ws.send('something');
});
// https://github.com/websockets/ws
const interval = setInterval(function ping() {
wss.clients.forEach(function each(ws) {
if (ws.isAlive === false) return ws.terminate();
ws.isAlive = false;
ws.ping();
ws.send('ping');//why: used by client to detect dead connection (because browser cant access ping/pong frames...(?))
});
}, 30000);
server.listen(7860);