initial commit

This commit is contained in:
Anton Lydike 2020-08-30 20:04:02 +02:00
commit 142d08a06c
5 changed files with 151 additions and 0 deletions

14
README.md Normal file
View File

@ -0,0 +1,14 @@
# Beam - a simple video streaming service
Two scripts for setting up local video streaming (audio might work aswell)
## Server
Run `server.py` with python3, the only dependency is flask and vlc player. This will open a server on port 5005, listening for these two routes:
* `/open?host=<host>[&port=<port>]` This will run vlc on the server and attempt to stream from `http://<host>:<port>/stream` (default port is 4040). The response will contain the session ID that can be used to control the playback
* `/close/<id>` will close the vlc player from the specified connection id
## Client
Running `client.py <video file>` will create a symlink inside an empty temporary directory and start an http server there. Then it will call the `/open` API of the server specified in the `BEAM_SERVER` variable inside. Once evrything is running, it waits for any user input and will then clean up (stop the server, remove temp dir, call close on the server)

77
client.py Normal file
View File

@ -0,0 +1,77 @@
import os
import sys
import socket
import secrets
import tempfile
import http.server
import socketserver
from threading import Thread
from urllib.request import urlopen
PORT = 4040
BEAM_SERVER = '192.168.0.3:5005'
d = tempfile.TemporaryDirectory()
print("using tempdir " + d.name)
def handler_for_dir(dir):
def myhandler(*args, **kwargs):
kwargs['directory'] = dir
return http.server.SimpleHTTPRequestHandler(*args, **kwargs)
return myhandler
def get_request(url):
resp = urlopen(url)
return (200 <= resp.status <= 299, "\n".join([d.decode() for d in resp.readlines()]))
# get ip address (sourced from https://stackoverflow.com/a/28950776/4141651)
def get_ip():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
# doesn't even have to be reachable
s.connect(('10.255.255.255', 1))
IP = s.getsockname()[0]
except Exception:
IP = '127.0.0.1'
finally:
s.close()
return IP
target = sys.argv[1]
id = secrets.token_hex(16)
# symlink target to tempdir so we only expose a single file
os.symlink(os.path.abspath(target), d.name + '/stream')
handler = handler_for_dir(d.name + '/')
token = None
try:
with http.server.HTTPServer(("0.0.0.0", PORT), handler) as httpd:
try:
print("serving at port", PORT)
t = Thread(target=httpd.serve_forever)
t.start()
print("calling beam target...")
host = get_ip()
resp = get_request(f'http://{BEAM_SERVER}/open?host={host}&port={PORT}')
if resp[0]:
token = resp[1].strip()
print(f"successfully started video - session {token}")
input("Just press enter when you are done...")
else:
print("Error statrtig video!")
finally:
print("shutting down server")
httpd.shutdown()
finally:
print("cleaning up")
if token:
get_request(f'http://{BEAM_SERVER}/stop/{token}')
d.cleanup()

11
default.nix Normal file
View File

@ -0,0 +1,11 @@
with import <nixpkgs> {};
stdenv.mkDerivation rec {
name = "beam-env";
buildInputs = [
(python3.withPackages (ps: with ps; [
flask
]))
];
}

1
requirements.txt Normal file
View File

@ -0,0 +1 @@
flask

48
server.py Normal file
View File

@ -0,0 +1,48 @@
import secrets
from time import sleep
from flask import Flask, request
from subprocess import Popen, STDOUT, PIPE
SESSIONS = dict()
app = Flask(__name__)
@app.route('/open')
def open_stream():
url = "http://" + request.args['host'] + ':' + request.args.get('port', '4040') + '/stream'
id = secrets.token_hex(16)
SESSIONS[id] = {
'url': url,
'id': id,
'proc': Popen(['vlc', url])
}
return id
@app.route('/stop/<id>')
def stream_stop(id):
if id in SESSIONS:
SESSIONS[id]['proc'].terminate()
return "true"
return "false"
def cleanup_thread():
while True:
dead_sessions = []
for session in SESSIONS.values():
if session['proc'].poll() != None:
# thread has terminated
dead_sessions.append(session['id'])
for id in dead_sessions:
url = SESSIONS[id]['url']
print(f"Removing dead session {id} for url {url}")
del SESSIONS[id]
sleep(10)
t = Thread(target=cleanup_thread)
t.start()
app.run(host='0.0.0.0', port=5005)