Spaces:
Running
Running
| """ | |
| keepalive.py β server-side "Keep Awake" for B24 Browser. | |
| SCOPE (read this before wiring it up): | |
| This pings a URL periodically from the backend, so a tracked tab stays | |
| "active" even while the user's phone is asleep or the app is backgrounded | |
| or fully closed β as long as this backend itself keeps running (note: a | |
| free HF Space can also idle-sleep, so this isn't infinite either). | |
| What this does NOT do: it does not simulate mouse movement, keystrokes, | |
| or run the page's real JavaScript. It's a plain periodic HTTP request. | |
| That's enough for many simple session-timeout mechanisms (dashboards, | |
| admin panels, some web tools) but will NOT fool sites that check real | |
| interaction via WebSocket heartbeats or JS-side activity checks β those | |
| need a full headless browser per session, which isn't safe to run at | |
| scale on limited RAM, and many such platforms (e.g. hosted notebook / | |
| compute services) explicitly prohibit keep-alive workarounds in their | |
| ToS. Keep this scoped to "don't lose my session on a page I'm using | |
| while I multitask", not "trick any site indefinitely" β and maybe warn | |
| users in the UI if a URL looks like a known compute/notebook host. | |
| """ | |
| import time | |
| import threading | |
| import requests | |
| MAX_SESSIONS_PER_USER = 3 | |
| MAX_DURATION_SECONDS = 6 * 60 * 60 # 6h safety cap, then auto-stop | |
| MIN_INTERVAL_SECONDS = 30 | |
| _sessions = {} # session_id -> dict(user_id, url, interval, started_at, stop_event, thread) | |
| _lock = threading.Lock() | |
| def _run(session_id, url, interval, headers, stop_event, started_at): | |
| while not stop_event.is_set(): | |
| if time.time() - started_at > MAX_DURATION_SECONDS: | |
| break | |
| try: | |
| requests.get(url, headers=headers or {}, timeout=10) | |
| except requests.RequestException: | |
| pass # transient failure β just try again next tick | |
| stop_event.wait(interval) | |
| with _lock: | |
| _sessions.pop(session_id, None) | |
| def start(user_id: str, url: str, interval: int = 60, headers: dict = None): | |
| """Begin pinging `url` every `interval` seconds on behalf of user_id.""" | |
| interval = max(interval, MIN_INTERVAL_SECONDS) | |
| with _lock: | |
| active_for_user = [s for s in _sessions.values() if s["user_id"] == user_id] | |
| if len(active_for_user) >= MAX_SESSIONS_PER_USER: | |
| return {"error": f"max {MAX_SESSIONS_PER_USER} keep-alive sessions per user"} | |
| session_id = f"{user_id}:{int(time.time() * 1000)}" | |
| stop_event = threading.Event() | |
| thread = threading.Thread( | |
| target=_run, | |
| args=(session_id, url, interval, headers, stop_event, time.time()), | |
| daemon=True, | |
| ) | |
| _sessions[session_id] = { | |
| "user_id": user_id, | |
| "url": url, | |
| "interval": interval, | |
| "started_at": time.time(), | |
| "stop_event": stop_event, | |
| "thread": thread, | |
| } | |
| thread.start() | |
| return {"session_id": session_id, "interval": interval, "max_duration": MAX_DURATION_SECONDS} | |
| def stop(user_id: str, session_id: str): | |
| with _lock: | |
| session = _sessions.get(session_id) | |
| if not session or session["user_id"] != user_id: | |
| return {"error": "session not found"} | |
| session["stop_event"].set() | |
| return {"stopped": True} | |
| def status(user_id: str): | |
| with _lock: | |
| return [ | |
| { | |
| "session_id": sid, | |
| "url": s["url"], | |
| "interval": s["interval"], | |
| "elapsed_seconds": int(time.time() - s["started_at"]), | |
| "remaining_seconds": max(0, MAX_DURATION_SECONDS - int(time.time() - s["started_at"])), | |
| } | |
| for sid, s in _sessions.items() | |
| if s["user_id"] == user_id | |
| ] | |