import os
import threading
from flask import Flask, request, Response, jsonify
from playwright.sync_api import sync_playwright
app = Flask(__name__)
VIEWPORT = {"width": 1280, "height": 800}
_lock = threading.Lock()
_state = {"playwright": None, "browser": None, "page": None}
USER_AGENT = (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36"
)
STEALTH_JS = """
Object.defineProperty(navigator, 'webdriver', {get: () => undefined});
Object.defineProperty(navigator, 'plugins', {get: () => [1, 2, 3, 4, 5]});
Object.defineProperty(navigator, 'languages', {get: () => ['en-US', 'en']});
window.chrome = { runtime: {} };
const originalQuery = window.navigator.permissions.query;
window.navigator.permissions.query = (parameters) => (
parameters.name === 'notifications'
? Promise.resolve({ state: Notification.permission })
: originalQuery(parameters)
);
"""
def get_page():
if _state["page"] is None:
_state["playwright"] = sync_playwright().start()
_state["browser"] = _state["playwright"].chromium.launch(
headless=True,
args=[
"--no-sandbox",
"--disable-dev-shm-usage",
"--disable-blink-features=AutomationControlled",
],
ignore_default_args=["--enable-automation"],
)
context = _state["browser"].new_context(
viewport=VIEWPORT,
user_agent=USER_AGENT,
locale="en-US",
timezone_id="America/New_York",
)
context.add_init_script(STEALTH_JS)
_state["page"] = context.new_page()
_state["page"].goto("https://example.com", wait_until="domcontentloaded")
return _state["page"]
HOME = """
Remote Browser
"""
@app.route("/")
def home():
html = HOME.replace("__VW__", str(VIEWPORT["width"])).replace(
"__VH__", str(VIEWPORT["height"])
)
return html
@app.route("/screenshot")
def screenshot():
with _lock:
page = get_page()
try:
data = page.screenshot(type="jpeg", quality=100)
except Exception:
data = b""
return Response(data, content_type="image/jpeg")
@app.route("/go", methods=["POST"])
def go():
url = (request.json or {}).get("url", "").strip()
if not url:
return jsonify(ok=False)
if not url.startswith(("http://", "https://")):
url = "https://" + url
with _lock:
page = get_page()
try:
page.goto(url, wait_until="domcontentloaded", timeout=20000)
except Exception as e:
return jsonify(ok=False, error=str(e))
return jsonify(ok=True)
@app.route("/back", methods=["POST"])
def back():
with _lock:
page = get_page()
try:
page.go_back(timeout=10000)
except Exception:
pass
return jsonify(ok=True)
@app.route("/click", methods=["POST"])
def click():
data = request.json or {}
with _lock:
page = get_page()
try:
page.mouse.click(data.get("x", 0), data.get("y", 0))
except Exception:
pass
return jsonify(ok=True)
@app.route("/key", methods=["POST"])
def key():
data = request.json or {}
with _lock:
page = get_page()
try:
page.keyboard.press(data.get("key", ""))
except Exception:
pass
return jsonify(ok=True)
@app.route("/scroll", methods=["POST"])
def scroll():
data = request.json or {}
with _lock:
page = get_page()
try:
page.mouse.wheel(0, data.get("dy", 0))
except Exception:
pass
return jsonify(ok=True)
if __name__ == "__main__":
port = int(os.environ.get("PORT", 7860))
app.run(host="0.0.0.0", port=port, threaded=False)