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)