init
This commit is contained in:
commit
d1c12a08b0
BIN
__pycache__/screen.cpython-313.pyc
Normal file
BIN
__pycache__/screen.cpython-313.pyc
Normal file
Binary file not shown.
7
docker-compose.yml
Normal file
7
docker-compose.yml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
services:
|
||||||
|
python:
|
||||||
|
volumes:
|
||||||
|
- './:/usr/src/app'
|
||||||
|
working_dir: /usr/src/app
|
||||||
|
build: .
|
||||||
|
command: python server.py
|
10
dockerfile
Normal file
10
dockerfile
Normal file
|
@ -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" ]
|
173
index.html
Normal file
173
index.html
Normal file
|
@ -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>
|
3
requirements.txt
Normal file
3
requirements.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
flask
|
||||||
|
websockets
|
||||||
|
pillow
|
60
screen.py
Normal file
60
screen.py
Normal file
|
@ -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()
|
95
server.py
Normal file
95
server.py
Normal file
|
@ -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()
|
Loading…
Reference in New Issue
Block a user