Spaces:
Sleeping
Sleeping
| const express = require("express"); | |
| const http = require("http"); | |
| const crypto = require("crypto"); | |
| const { chromium } = require("playwright-core"); | |
| const WebSocket = require("ws"); | |
| const app = express(); | |
| const server = http.createServer(app); | |
| const wss = new WebSocket.Server({ server }); | |
| const sessions = new Map(); | |
| const PORT = process.env.PORT || 7860; | |
| const CHROME = process.env.CHROMIUM_PATH || "/usr/bin/chromium"; | |
| app.use(express.static("public")); | |
| function token() { | |
| return crypto.randomBytes(24).toString("hex"); | |
| } | |
| async function createSession() { | |
| const browser = await chromium.launch({ | |
| executablePath: CHROME, | |
| headless: true, | |
| args: [ | |
| "--no-sandbox", | |
| "--disable-setuid-sandbox", | |
| "--disable-dev-shm-usage", | |
| "--disable-gpu", | |
| "--disable-extensions", | |
| "--disable-background-networking", | |
| "--disable-sync" | |
| ] | |
| }); | |
| const context = await browser.newContext({ | |
| viewport: { | |
| width: 1280, | |
| height: 720 | |
| }, | |
| deviceScaleFactor: 1, | |
| userAgent: | |
| "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + | |
| "AppleWebKit/537.36 (KHTML, like Gecko) " + | |
| "Chrome/124.0.0.0 Safari/537.36" | |
| }); | |
| const page = await context.newPage(); | |
| return { | |
| browser, | |
| context, | |
| page, | |
| clients: new Set() | |
| }; | |
| } | |
| async function destroySession(id) { | |
| const s = sessions.get(id); | |
| if (!s) return; | |
| sessions.delete(id); | |
| try { | |
| await s.browser.close(); | |
| } catch {} | |
| } | |
| wss.on("connection", async ws => { | |
| const id = token(); | |
| let session; | |
| try { | |
| session = await createSession(); | |
| sessions.set(id, session); | |
| session.clients.add(ws); | |
| ws.send(JSON.stringify({ | |
| type: "ready", | |
| token: id | |
| })); | |
| } catch { | |
| ws.close(); | |
| return; | |
| } | |
| const sendFrame = async () => { | |
| if (ws.readyState !== WebSocket.OPEN) return; | |
| try { | |
| const image = await session.page.screenshot({ | |
| type: "jpeg", | |
| quality: 65 | |
| }); | |
| ws.send(JSON.stringify({ | |
| type: "frame", | |
| data: image.toString("base64") | |
| })); | |
| } catch {} | |
| }; | |
| const timer = setInterval(sendFrame, 125); | |
| ws.on("message", async raw => { | |
| let msg; | |
| try { | |
| msg = JSON.parse(raw.toString()); | |
| } catch { | |
| return; | |
| } | |
| try { | |
| if (msg.type === "navigate") { | |
| let url = String(msg.url || "").trim(); | |
| if (!/^https?:\/\//i.test(url)) { | |
| url = "https://" + url; | |
| } | |
| await session.page.goto(url, { | |
| waitUntil: "domcontentloaded", | |
| timeout: 30000 | |
| }); | |
| return; | |
| } | |
| if (msg.type === "mouse") { | |
| await session.page.mouse.move( | |
| Number(msg.x), | |
| Number(msg.y) | |
| ); | |
| if (msg.action === "down") { | |
| await session.page.mouse.down(); | |
| } | |
| if (msg.action === "up") { | |
| await session.page.mouse.up(); | |
| } | |
| return; | |
| } | |
| if (msg.type === "wheel") { | |
| await session.page.mouse.wheel( | |
| Number(msg.dx || 0), | |
| Number(msg.dy || 0) | |
| ); | |
| return; | |
| } | |
| if (msg.type === "touch") { | |
| const client = | |
| await session.context.newCDPSession( | |
| session.page | |
| ); | |
| await client.send( | |
| "Input.dispatchTouchEvent", | |
| { | |
| type: msg.action, | |
| touchPoints: | |
| msg.action === "touchEnd" | |
| ? [] | |
| : [{ | |
| x: Number(msg.x), | |
| y: Number(msg.y) | |
| }] | |
| } | |
| ); | |
| await client.detach(); | |
| return; | |
| } | |
| if (msg.type === "key") { | |
| await session.page.keyboard.press( | |
| String(msg.key) | |
| ); | |
| return; | |
| } | |
| if (msg.type === "text") { | |
| await session.page.keyboard.insertText( | |
| String(msg.text || "") | |
| ); | |
| return; | |
| } | |
| if (msg.type === "reload") { | |
| await session.page.reload({ | |
| waitUntil: "domcontentloaded" | |
| }); | |
| } | |
| } catch {} | |
| }); | |
| ws.on("close", async () => { | |
| clearInterval(timer); | |
| await destroySession(id); | |
| }); | |
| }); | |
| server.listen(PORT, "0.0.0.0", () => { | |
| console.log(`Orbit Chromium Engine :${PORT}`); | |
| }); |