Spaces:
Paused
Paused
| from fastapi import FastAPI, UploadFile | |
| from fastapi.responses import HTMLResponse | |
| from fastapi.staticfiles import StaticFiles | |
| import cv2 | |
| import os | |
| import numpy as np | |
| import uvicorn | |
| app = FastAPI() | |
| FRAME_FOLDER = "frames" | |
| MAP_W = 3 | |
| MAP_H = 2 | |
| ADMIN_KEY = "supersecret123" | |
| os.makedirs(FRAME_FOLDER, exist_ok=True) | |
| app.mount("/frames", StaticFiles(directory="frames"), name="frames") | |
| current_streamer = None | |
| def home(nick: str = "Unknown"): | |
| return f""" | |
| <html> | |
| <body> | |
| <h2>Вы: {nick}</h2> | |
| <h3>Текущий стример: <span id="streamer">None</span></h3> | |
| <button onclick="startStream()">START</button> | |
| <button onclick="stopStream()">STOP</button> | |
| <script> | |
| let nick = "{nick}" | |
| let running = false | |
| let videoStream = null | |
| async function updateStatus(){{ | |
| let r = await fetch("/status") | |
| let data = await r.json() | |
| document.getElementById("streamer").innerText = data.streamer | |
| }} | |
| setInterval(updateStatus,1000) | |
| async function startStream(){{ | |
| let r = await fetch("/start?nick="+nick) | |
| let data = await r.json() | |
| if(data.error){{ | |
| alert(data.error) | |
| return | |
| }} | |
| videoStream = await navigator.mediaDevices.getDisplayMedia({{ | |
| video:true | |
| }}) | |
| const video = document.createElement("video") | |
| video.srcObject = videoStream | |
| video.play() | |
| const canvas = document.createElement("canvas") | |
| const ctx = canvas.getContext("2d") | |
| running = true | |
| async function sendFrame(){{ | |
| if(!running) return | |
| canvas.width = 384 | |
| canvas.height = 256 | |
| ctx.drawImage(video,0,0,384,256) | |
| canvas.toBlob(async blob => {{ | |
| const form = new FormData() | |
| form.append("file", blob, "frame.jpg") | |
| await fetch("/frame_upload", {{ | |
| method:"POST", | |
| body:form | |
| }}) | |
| }},"image/jpeg") | |
| setTimeout(sendFrame,200) | |
| }} | |
| sendFrame() | |
| }} | |
| async function stopStream(){{ | |
| running=false | |
| if(videoStream){{ | |
| videoStream.getTracks().forEach(t=>t.stop()) | |
| }} | |
| await fetch("/stop?nick="+nick) | |
| }} | |
| </script> | |
| </body> | |
| </html> | |
| """ | |
| def status(): | |
| return {"streamer": current_streamer} | |
| def start(nick: str): | |
| global current_streamer | |
| if current_streamer is not None: | |
| return {"error": "Кто-то уже стримит!"} | |
| current_streamer = nick | |
| return {"ok": True} | |
| def stop(nick: str): | |
| global current_streamer | |
| if current_streamer == nick: | |
| current_streamer = None | |
| return {"ok": True} | |
| async def frame_upload(file: UploadFile): | |
| data = await file.read() | |
| nparr = np.frombuffer(data, np.uint8) | |
| frame = cv2.imdecode(nparr, cv2.IMREAD_COLOR) | |
| frame = cv2.resize(frame, (MAP_W*128, MAP_H*128)) | |
| for y in range(MAP_H): | |
| for x in range(MAP_W): | |
| tile = frame[ | |
| y*128:(y+1)*128, | |
| x*128:(x+1)*128 | |
| ] | |
| map_id = y*MAP_W + x | |
| path = f"{FRAME_FOLDER}/map{map_id}.jpg" | |
| cv2.imwrite(path, tile) | |
| return {"ok": True} | |
| def force_stop(key: str): | |
| global current_streamer | |
| if key != ADMIN_KEY: | |
| return {"error": "access denied"} | |
| current_streamer = None | |
| return {"status": "stream force stopped"} | |
| if __name__ == "__main__": | |
| uvicorn.run(app, host="0.0.0.0", port=7860) |