diff --git a/endpoint.py b/endpoint.py index be6f3da..9712a7f 100644 --- a/endpoint.py +++ b/endpoint.py @@ -3,16 +3,99 @@ import csv from aiohttp import web from .log import mklog -from .utils import backup_file, here, import_install, reqs_map, run_command, styles_dir +from .utils import ( + backup_file, + here, + import_install, + reqs_map, + run_command, + styles_dir, +) endlog = mklog("mtb endpoint") # - ACTIONS +import asyncio import platform import sys from pathlib import Path +import websockets.server + import_install("requirements") +import io + +import numpy as np +from PIL import Image + + +def generate_random_frame(): + # Generate a random image frame + width, height = 640, 480 + image = np.random.randint(0, 256, (height, width, 3), dtype=np.uint8) + pil_image = Image.fromarray(image) + byte_buffer = io.BytesIO() + pil_image.save(byte_buffer, format="JPEG") + frame_data = byte_buffer.getvalue() + return frame_data + + +class VideoStreamingManager: + def __init__(self): + self.video_servers = {} + self.next_port = ( + 8767 # Start with a default port and increment for each server + ) + + async def start_video_streaming_server(self, video_id): + if video_id not in self.video_servers: + # Create and start a new video streaming server for the specified video + video_server = await self.create_video_streaming_server(video_id) + self.video_servers[video_id] = video_server + + return video_server + + async def video_stream(self, websocket, path): + # Implement the logic to continuously capture and send video frames here + while True: + # frame_data = capture_and_encode_frame() # Implement this function + frame_data = generate_random_frame() + await websocket.send(frame_data) + await asyncio.sleep(0.033) # Adjust the frame rate as needed + + async def create_video_streaming_server(self, video_id): + # Create and start a new WebSocket server for the specified video + port = self.next_port + self.next_port += 1 # Increment port number for the next server + + server = await websockets.server.serve( + self.video_stream, "localhost", port + ) + + return server + + async def stop_video_streaming_server(self, video_id): + if video_id in self.video_servers: + # Terminate and remove the video streaming server for the specified video + video_server = self.video_servers[video_id] + video_server.close() + await video_server.wait_closed() + del self.video_servers[video_id] + + +async def start_video_streaming_server(): + async def video_stream(websocket, path): + # Continuously capture and send video frames here + while True: + frame_data = capture_and_encode_frame() # Implement this function + await websocket.send(frame_data) + await asyncio.sleep(0.033) # Adjust the frame rate as needed + + start_server = websockets.server.serve( + video_stream, "localhost", 8766 + ) # Use a different port (e.g., 8766) + + return await start_server def ACTIONS_installDependency(dependency_names=None): @@ -22,7 +105,9 @@ def ACTIONS_installDependency(dependency_names=None): # reqs = [] resolved_names = [reqs_map.get(name, name) for name in dependency_names] try: - run_command([Path(sys.executable), "-m", "pip", "install"] + resolved_names) + run_command( + [Path(sys.executable), "-m", "pip", "install"] + resolved_names + ) return {"success": True} except Exception as e: @@ -55,7 +140,9 @@ def ACTIONS_getStyles(style_name=None): if not key.startswith("__") and key not in match_list } if style_name: - return filtered_styles.get(style_name, {"error": "Style not found"}) + return filtered_styles.get( + style_name, {"error": "Style not found"} + ) return filtered_styles return {"error": "No styles found"} @@ -75,7 +162,9 @@ def ACTIONS_saveStyle(data): break if not target: - endlog.warning(f"Could not determine the target file for {data.keys()}") + endlog.warning( + f"Could not determine the target file for {data.keys()}" + ) return {"error": "Could not determine the target file for the style"} backup_file(target) @@ -103,11 +192,16 @@ async def do_action(request) -> web.Response: return web.json_response({"result": result}) available_methods = [ - attr[len("ACTIONS_") :] for attr in globals() if attr.startswith("ACTIONS_") + attr[len("ACTIONS_") :] + for attr in globals() + if attr.startswith("ACTIONS_") ] return web.json_response( - {"error": "Invalid method name.", "available_methods": available_methods} + { + "error": "Invalid method name.", + "available_methods": available_methods, + } ) @@ -235,7 +329,9 @@ def add_split_pane(left_content, right_content, vertical=True): def add_dropdown(title, options): - option_str = "\n".join([f"" for opt in options]) + option_str = "\n".join( + [f"" for opt in options] + ) return f"""