Update app.py
Browse files
app.py
CHANGED
|
@@ -8,23 +8,20 @@ from aiohttp import web
|
|
| 8 |
from aiortc import RTCPeerConnection, RTCSessionDescription, RTCIceServer, RTCConfiguration
|
| 9 |
|
| 10 |
logging.basicConfig(level=logging.INFO)
|
| 11 |
-
logger = logging.getLogger("
|
| 12 |
|
|
|
|
| 13 |
VNC_PORT = 5900
|
|
|
|
|
|
|
|
|
|
| 14 |
|
| 15 |
def start_system():
|
| 16 |
os.environ["DISPLAY"] = ":99"
|
| 17 |
-
|
| 18 |
-
# 1. Start Xvfb
|
| 19 |
subprocess.Popen(["Xvfb", ":99", "-screen", "0", "1280x720x24", "-ac"])
|
| 20 |
time.sleep(1)
|
| 21 |
-
|
| 22 |
-
# 2. Start YOUR BUILT VNC TOOL
|
| 23 |
-
logger.info("Starting custom built minivnc...")
|
| 24 |
subprocess.Popen(["/usr/local/bin/minivnc"])
|
| 25 |
time.sleep(1)
|
| 26 |
-
|
| 27 |
-
# 3. Start Window Manager & Opera
|
| 28 |
subprocess.Popen("matchbox-window-manager -use_titlebar no", shell=True)
|
| 29 |
subprocess.Popen("opera --no-sandbox --user-data-dir=/home/user/opera-data", shell=True)
|
| 30 |
|
|
@@ -36,29 +33,53 @@ async def bridge_vnc(channel):
|
|
| 36 |
data = await reader.read(65536)
|
| 37 |
if not data: break
|
| 38 |
channel.send(data)
|
| 39 |
-
|
| 40 |
@channel.on("message")
|
| 41 |
def on_message(msg):
|
| 42 |
if isinstance(msg, bytes): writer.write(msg)
|
| 43 |
-
|
| 44 |
await vnc_to_rtc()
|
| 45 |
except Exception as e: logger.error(f"Bridge fail: {e}")
|
| 46 |
|
| 47 |
async def offer(request):
|
| 48 |
params = await request.json()
|
| 49 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
@pc.on("datachannel")
|
| 51 |
def on_dc(channel):
|
| 52 |
asyncio.create_task(bridge_vnc(channel))
|
| 53 |
-
|
| 54 |
await pc.setRemoteDescription(RTCSessionDescription(sdp=params["sdp"], type=params["type"]))
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
|
|
|
|
| 60 |
if __name__ == "__main__":
|
| 61 |
start_system()
|
| 62 |
app = web.Application()
|
| 63 |
app.router.add_post("/offer", offer)
|
| 64 |
-
|
|
|
|
|
|
| 8 |
from aiortc import RTCPeerConnection, RTCSessionDescription, RTCIceServer, RTCConfiguration
|
| 9 |
|
| 10 |
logging.basicConfig(level=logging.INFO)
|
| 11 |
+
logger = logging.getLogger("HF-VNC-Bridge")
|
| 12 |
|
| 13 |
+
# Configuration
|
| 14 |
VNC_PORT = 5900
|
| 15 |
+
# Cloudflare TURN helps bypass Hugging Face's strict network/firewall
|
| 16 |
+
TURN_USER = "g08abe68c81a07f098bb5f0914549bb32440e5aad0b216c7fba2b61e76fd62c6"
|
| 17 |
+
TURN_PASS = "aed1a10dd10eba9401ad9d99e5c66036d8a970eab5ba8e6dc9845ab57c771a7d"
|
| 18 |
|
| 19 |
def start_system():
|
| 20 |
os.environ["DISPLAY"] = ":99"
|
|
|
|
|
|
|
| 21 |
subprocess.Popen(["Xvfb", ":99", "-screen", "0", "1280x720x24", "-ac"])
|
| 22 |
time.sleep(1)
|
|
|
|
|
|
|
|
|
|
| 23 |
subprocess.Popen(["/usr/local/bin/minivnc"])
|
| 24 |
time.sleep(1)
|
|
|
|
|
|
|
| 25 |
subprocess.Popen("matchbox-window-manager -use_titlebar no", shell=True)
|
| 26 |
subprocess.Popen("opera --no-sandbox --user-data-dir=/home/user/opera-data", shell=True)
|
| 27 |
|
|
|
|
| 33 |
data = await reader.read(65536)
|
| 34 |
if not data: break
|
| 35 |
channel.send(data)
|
|
|
|
| 36 |
@channel.on("message")
|
| 37 |
def on_message(msg):
|
| 38 |
if isinstance(msg, bytes): writer.write(msg)
|
|
|
|
| 39 |
await vnc_to_rtc()
|
| 40 |
except Exception as e: logger.error(f"Bridge fail: {e}")
|
| 41 |
|
| 42 |
async def offer(request):
|
| 43 |
params = await request.json()
|
| 44 |
+
|
| 45 |
+
# Crucial: Use ICE Servers for remote connection
|
| 46 |
+
config = RTCConfiguration(iceServers=[
|
| 47 |
+
RTCIceServer(urls=["stun:stun.l.google.com:19302"]),
|
| 48 |
+
RTCIceServer(urls=["turns:turn.cloudflare.com:443?transport=tcp"], username=TURN_USER, credential=TURN_PASS)
|
| 49 |
+
])
|
| 50 |
+
|
| 51 |
+
pc = RTCPeerConnection(config)
|
| 52 |
+
pcs.add(pc)
|
| 53 |
+
|
| 54 |
@pc.on("datachannel")
|
| 55 |
def on_dc(channel):
|
| 56 |
asyncio.create_task(bridge_vnc(channel))
|
| 57 |
+
|
| 58 |
await pc.setRemoteDescription(RTCSessionDescription(sdp=params["sdp"], type=params["type"]))
|
| 59 |
+
answer = await pc.createAnswer()
|
| 60 |
+
await pc.setLocalDescription(answer)
|
| 61 |
+
|
| 62 |
+
return web.Response(
|
| 63 |
+
content_type="application/json",
|
| 64 |
+
text=json.dumps({"sdp": pc.localDescription.sdp, "type": pc.localDescription.type}),
|
| 65 |
+
headers={
|
| 66 |
+
"Access-Control-Allow-Origin": "*", # Allow connections from your other site
|
| 67 |
+
"Access-Control-Allow-Methods": "POST, OPTIONS",
|
| 68 |
+
"Access-Control-Allow-Headers": "Content-Type"
|
| 69 |
+
}
|
| 70 |
+
)
|
| 71 |
+
|
| 72 |
+
async def options(request):
|
| 73 |
+
return web.Response(headers={
|
| 74 |
+
"Access-Control-Allow-Origin": "*",
|
| 75 |
+
"Access-Control-Allow-Methods": "POST, OPTIONS",
|
| 76 |
+
"Access-Control-Allow-Headers": "Content-Type"
|
| 77 |
+
})
|
| 78 |
|
| 79 |
+
pcs = set()
|
| 80 |
if __name__ == "__main__":
|
| 81 |
start_system()
|
| 82 |
app = web.Application()
|
| 83 |
app.router.add_post("/offer", offer)
|
| 84 |
+
app.router.add_options("/offer", options)
|
| 85 |
+
web.run_app(app, host="0.0.0.0", port=7860)
|