commit d1c12a08b0f3b0bcdab4711c6de25b4c388e21da Author: Your Name <you@example.com> Date: Mon Dec 2 02:35:49 2024 +0500 init diff --git a/__pycache__/screen.cpython-313.pyc b/__pycache__/screen.cpython-313.pyc new file mode 100644 index 0000000..192e388 Binary files /dev/null and b/__pycache__/screen.cpython-313.pyc differ diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..e28e6aa --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,7 @@ +services: + python: + volumes: + - './:/usr/src/app' + working_dir: /usr/src/app + build: . + command: python server.py diff --git a/dockerfile b/dockerfile new file mode 100644 index 0000000..b9bedfb --- /dev/null +++ b/dockerfile @@ -0,0 +1,10 @@ +FROM python:3 + +WORKDIR /usr/src/app + +COPY requirements.txt ./ +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +CMD [ "python", "./server.py" ] diff --git a/index.html b/index.html new file mode 100644 index 0000000..652f6fc --- /dev/null +++ b/index.html @@ -0,0 +1,173 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Document</title> + <style> + body{ + padding:0; + margin:0; + background-repeat: no-repeat; + background-size:contain; + background-position: center; + height: 100vh; + background-color: #4b444b; + } + @media (height < 700px) { + body { + height: 88vh; + } + } + #top_bar { + background-color: #6e84a3; + color: white; + font: bold 24px Helvetica; + padding: 6px 5px 4px 5px; + border-bottom: 1px outset; + } + + #status { + text-align: center; + } + </style> + <script> + function createCookie(name, value, days) { + if (days) { + var date = new Date(); + date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); + var expires = "; expires=" + date.toGMTString(); + } + else var expires = ""; + + document.cookie = name + "=" + value + expires + "; path=/"; + } + + function readCookie(name) { + var nameEQ = name + "="; + var ca = document.cookie.split(';'); + for (var i = 0; i < ca.length; i++) { + var c = ca[i]; + while (c.charAt(0) == ' ') c = c.substring(1, c.length); + if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length); + } + return null; + } + + function eraseCookie(name) { + createCookie(name, "", -1); + } + + + function readQueryVariable(name, defaultValue) { + // A URL with a query parameter can look like this: + // https://www.example.com?myqueryparam=myvalue + // + // Note that we use location.href instead of location.search + // because Firefox < 53 has a bug w.r.t location.search + const re = new RegExp('.*[?&]' + name + '=([^&#]*)'), + match = document.location.href.match(re); + + if (match) { + // We have to decode the URL since want the cleartext value + return decodeURIComponent(match[1]); + } + + return defaultValue; + } + + function uuidv4() { + return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, c => + (+c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> +c / 4).toString(16) + ); + } + + let uuid = readCookie("uuid") + if (uuid==null){ + uuid = uuidv4(); + createCookie("uuid",uuid) + } + function status(text) { + document.getElementById('status').textContent = text; + } + window.onload = () => { + if ('WebSocket' in window) { + let was_connect = false; + // create websocket connection + let ws = null; + con() + + function con(){ + ws = new WebSocket('wss://ws.n0r.su/'); + ws.onopen = () => { + console.log('websocket success---'); + get() + } + ws.onclose = (e) => { + no_stream(); + setTimeout(()=>{con()},5000) + }; + ws.onerror = (e) => { + setTimeout(()=>{con()},5000) + no_stream(); + // console.error('websocket fail'); + } + } + function get() { + ws.send(`get_stream%uuid=${uuid}`); + // setTimeout(() => { + // get(); + // }, 100); + ws.onmessage = (message) => { + if (message.data != "null"){ + was_connect = true; + let data = message.data; + // console.log('get websocket message---', JSON.parse(data)[1]); + document.body.style.backgroundImage = `url(data:image/jpeg;base64,${JSON.parse(data)[1]})`; + document.getElementById("top_bar").setAttribute("hidden",""); + status("conected") + setTimeout(()=>{get()},200) + } + else{ + setTimeout(()=>{get();},5000) + no_stream(); + } + } + } + ws.onerror = (e) => { + setTimeout(()=>{con()},5000) + no_stream(); + // console.error('websocket fail'); + } + ws.onclose = (e) => { + setTimeout(()=>{con()},5000) + no_stream() + // console.log("The connection has been closed successfully.") + }; + + function no_stream(){ + if (document.body.style.backgroundImage != `url(/papi/savehouse/?const=club_dance)`){ + document.body.style.backgroundImage = `url(/papi/savehouse/?const=club_dance)`; + } + document.getElementById("top_bar").removeAttribute("hidden"); + if(was_connect){ + status("Stream has ended") + } + else{ + status("Stream is closed") + } + } + } else { + console.error('dont support websocket'); + }; + }; +</script> + +</head> +<body> + <div id="top_bar"> + <div id="status">Loading</div> + </div> +</body> +</html> diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..448ed83 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +flask +websockets +pillow diff --git a/screen.py b/screen.py new file mode 100644 index 0000000..34c43fc --- /dev/null +++ b/screen.py @@ -0,0 +1,60 @@ +#Copyright (C) 2021 Qijun Gu +# +#This file is part of Screenshare. +# +#Screenshare is free software: you can redistribute it and/or modify +#it under the terms of the GNU General Public License as published by +#the Free Software Foundation, either version 3 of the License, or +#(at your option) any later version. +# +#Screenshare is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +# +#You should have received a copy of the GNU General Public License +#along with Screenshare. If not, see <https://www.gnu.org/licenses/>. + +import threading, time, base64, sys + +ver = sys.version_info.major +if ver==2: + import StringIO as io +elif ver==3: + import io + +from PIL import ImageGrab as ig + +class Screen(): + def __init__(self): + self.FPS = 30 + self.screenbuf = "" + self.password = "" + if ver==2: + self.screenfile = io.StringIO() + elif ver==3: + self.screenfile = io.BytesIO() + threading.Thread(target=self.getframes).start() + + def __del__(self): + self.screenfile.close() + + def getframes(self): + while True: + im = ig.grab(bbox=(0, 0, 1920, 1080), include_layered_windows=False, all_screens=False, xdisplay=":0") + self.screenfile.seek(0) + self.screenfile.truncate(0) + im_converted = im.convert("RGB") + im_converted.save(self.screenfile, format="jpeg", quality=60, progressive=True) + self.screenbuf = base64.b64encode(self.screenfile.getvalue()) + time.sleep(1.0/self.FPS) + + def gen(self): + s = '' + if ver==2: + s = self.screenbuf + elif ver==3: + s = self.screenbuf.decode() + return s + +screenlive = Screen() diff --git a/server.py b/server.py new file mode 100644 index 0000000..0785dec --- /dev/null +++ b/server.py @@ -0,0 +1,95 @@ +from screen import screenlive +import json, argparse +from flask import Flask, json +import threading +from time import sleep +from websockets.sync.server import serve +import websockets +import array +import time + +api = Flask(__name__) +watchers_last_seen = {} +stream = False +debug = False + +def current_time(): + return str(int(time.time())) + +@api.route("/off", methods=["GET"]) +def streaOff(): + global stream + stream=False + return "True" + +@api.route("/on", methods=["GET"]) +def streamOn(): + global stream + stream=True + return "True" + +@api.route("/views", methods=["GET"]) +def views(): + global watchers_last_seen + return str(len(watchers_last_seen)) + + +def ws_server(websocket): + global stream, watchers_last_seen + if debug: print("WebSocket: Server Started.") + try: + while True: + req=websocket.recv() + if "%" in req: + args= req.split("%")[1] + args = dict(x.split("=") for x in args.split(";")) + req = req.split("%")[0] + if debug: print(req,"-----",args) + match (req): + case ("get_stream"): + if stream: + watchers_last_seen[args["uuid"]] = current_time() + websocket.send(f""+str(json.dumps([True, screenlive.gen()]))) + else: + websocket.send(f"null") + case (_): + websocket.send(f"null") + except websockets.exceptions.ConnectionClosedOK: + if debug: print("hungup") + except websockets.ConnectionClosedError: + if debug: print("Internal Server Error.") + +def run_ws_server(): + with serve(ws_server, "0.0.0.0", 7890) as server: + server.serve_forever() + +def timeout_task(): + while True: + for key,value in watchers_last_seen.items(): + if debug: print(key+"||||"+value) + if (int(current_time())-int(value)) > 5: + if debug: print(key+"is timed out") + watchers_last_seen.pop(key) + break + if debug: print("time -",current_time()) + if debug: print("watchers -",watchers_last_seen.keys()) + sleep(10) + +def run_flask_server(): + api.run() + +def main(): + t1 = threading.Thread(target=run_ws_server) + t2 = threading.Thread(target=run_flask_server) + t3 = threading.Thread(target=timeout_task) + + t1.start() + t2.start() + t3.start() + + t1.join() + t2.join() + t3.join() + +if __name__ == "__main__": + main()