initial commit

master
Anton Lydike 4 years ago
commit 142d08a06c

@ -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)

@ -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()

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

@ -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)
Loading…
Cancel
Save