Spaces:
Build error
Build error
File size: 6,674 Bytes
0b2be8e a1ed453 6cbbb50 0b2be8e 6cbbb50 0b2be8e a1ed453 0b2be8e 150f7ac 0b2be8e 6cbbb50 0b2be8e 6cbbb50 0b2be8e 6cbbb50 0b2be8e 6cbbb50 0b2be8e 6cbbb50 20d9067 0b2be8e 20d9067 0b2be8e a1ed453 0b2be8e 6cbbb50 0b2be8e 6cbbb50 0b2be8e 150f7ac a1ed453 0b2be8e 150f7ac 0b2be8e 6cbbb50 0b2be8e 6cbbb50 0b2be8e 6cbbb50 0b2be8e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | import asyncio
import logging
import contextlib
from fastapi import FastAPI, Request, WebSocket, WebSocketDisconnect
from fastapi.responses import HTMLResponse
import uuid
import html
logger = logging.getLogger(__name__)
class JSExecutionContext:
def __init__(self, endpoint: str, result_future: asyncio.Future):
self.endpoint = endpoint
self.result_future = result_future
self.hidden_iframe_name = "hidden-iframe"
class JSExecutionRouter:
def __init__(self):
self.app = None
self.active_routes = set()
def init_app(self, app: FastAPI):
self.app = app
def get_eval_js_html(self, js_code: str, result_id: str):
# escaped_js = html.escape(js_code)
logger.info(f"Generating HTML for JavaScript execution. Result ID: {result_id}")
return f"""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Execute JavaScript</title>
</head>
<body>
<script>
console.log("JavaScript execution page loaded");
async function executeAndReturn() {{
console.log("executeAndReturn function called");
try {{
const result = await (async () => {{ {js_code} }})();
console.log("JavaScript execution result:", result);
await fetch('/js-result/{result_id}', {{
method: 'POST',
headers: {{ 'Content-Type': 'application/json' }},
body: JSON.stringify({{ result }})
}});
}} catch (error) {{
console.error('Error:', error);
await fetch('/js-result/{result_id}', {{
method: 'POST',
headers: {{ 'Content-Type': 'application/json' }},
body: JSON.stringify({{ error: error.toString() }})
}});
}}
}}
executeAndReturn();
</script>
</body>
</html>
"""
@contextlib.asynccontextmanager
async def create_dynamic_endpoint(self, js_code: str):
if not self.app:
raise RuntimeError("DynamicRouter not initialized with FastAPI app")
result_id = str(uuid.uuid4())
result_future = asyncio.Future()
@self.app.get(f"/eval-js/{result_id}", response_class=HTMLResponse)
async def serve_eval_js_page():
logger.info(f"Serving eval-js page for result ID: {result_id}")
return HTMLResponse(content=self.get_eval_js_html(js_code, result_id))
@self.app.post(f"/js-result/{result_id}")
async def receive_js_result(data: dict):
logger.info(f"Received result for ID: {result_id}")
if 'error' in data:
result_future.set_exception(Exception(data['error']))
else:
result_future.set_result(data['result'])
return {"status": "received"}
self.active_routes.add(result_id)
try:
yield JSExecutionContext(f"/eval-js/{result_id}", result_future)
finally:
self.cleanup_route(result_id)
def cleanup_route(self, result_id: str):
if result_id in self.active_routes:
self.app.router.routes = [route for route in self.app.router.routes if route.path not in [f"/eval-js/{result_id}", f"/js-result/{result_id}"]]
self.active_routes.remove(result_id)
js_execution_router = JSExecutionRouter()
def add_js_execution_routes(app: FastAPI):
js_execution_router.init_app(app)
@app.websocket("/ws/camera")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
try:
while True:
data = await websocket.receive_text()
await broadcast_camera_frame(data)
except WebSocketDisconnect:
pass
except Exception as e:
logger.error(f"WebSocket error: {str(e)}")
# Add this new function to handle broadcasting
async def broadcast_camera_frame(frame_data: str):
# This function will be called from the Flet app to set the callback
if hasattr(broadcast_camera_frame, 'callback'):
await broadcast_camera_frame.callback(frame_data)
# Function to set the callback from the Flet app
def set_camera_frame_callback(callback):
broadcast_camera_frame.callback = callback
def mount_flet_app(app: FastAPI, route: str, flet_app):
# Mount the Flet app
app.mount("/flet-app-internal", flet_app)
app.flet_app = flet_app # Store reference to the Flet app
@app.get(route, response_class=HTMLResponse)
async def serve_main_page(request: Request):
html_content = f"""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Camera Stream App</title>
<style>
body, html {{
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
}}
#flet-app-frame {{
width: 100%;
height: 100%;
border: none;
position: absolute;
top: 0;
left: 0;
}}
</style>
</head>
<body>
<iframe id="flet-app-frame"></iframe>
<iframe name="hidden-iframe" style="display:none;"></iframe>
<script>
window.onload = function() {{
var iframe = document.getElementById('flet-app-frame');
var currentProtocol = window.location.protocol;
var currentHost = window.location.host;
var src = currentProtocol + '//' + currentHost + '/flet-app-internal/';
iframe.src = src;
// Set Content-Security-Policy based on the current protocol
var meta = document.createElement('meta');
meta.httpEquiv = "Content-Security-Policy";
meta.content = currentProtocol === 'https:' ? "upgrade-insecure-requests" : "";
document.head.appendChild(meta);
}};
</script>
</body>
</html>
"""
return HTMLResponse(content=html_content)
# Add the JS execution routes
add_js_execution_routes(app) |