#!/usr/bin/env python3 from flask import Flask, request, make_response, session, redirect, render_template, send_from_directory, jsonify from time import gmtime, strftime from uuid import uuid4 import requests import json import sqlite3 import base64 import configparser import os """ SQLite3 Database Schema CREATE TABLE channels ( "id" INTEGER PRIMARY KEY, "name" TEXT NOT NULL, "key" TEXT NOT NULL, "live_at" TEXT, "password" TEXT, "user_uuid" TEXT , "last_stream" TEXT); CREATE TABLE signed_users ( "id" INTEGER PRIMARY KEY, "uuid" TEXT NOT NULL, "name" TEXT NOT NULL ); d2c45910-8eb8-11e8-b357-b10f0028b927 """ # Load configuration config = configparser.ConfigParser() config['Streaming'] = { 'Database': 'streaming.db', 'StreamServer': 'https://tv.icynet.eu/live/', 'ServerHost': 'icynet.eu', 'PublishAddress': 'rtmp://{host}:1935/hls-live/{streamer}', 'Secret': 'changeme', } config['Auth'] = { 'Server': 'http://localhost:8282', 'Redirect': 'http://localhost:5000/auth/_callback/', } config['OAuth2'] = { 'ClientID': '1', 'ClientSecret': 'changeme', } if os.path.exists('config.ini'): config.read('config.ini') with open('config.ini', 'w') as configfile: config.write(configfile) # App settings app = Flask(__name__) app.secret_key = config['Streaming']['Secret'] app.config['SESSION_TYPE'] = 'redis' auth_image = "{server}/api/avatar/{uuid}" oauth_auth = "{server}/oauth2/authorize?response_type=code&state={state}&redirect_uri={redirect}&client_id={client}&scope=image" stream_server = config['Streaming']['StreamServer'] auth_server = config['Auth']['Server'] oauth_redirect = config['Auth']['Redirect'] oauth_id = int(config['OAuth2']['ClientID']) oauth_secret = config['OAuth2']['ClientSecret'] # Database conn = sqlite3.connect(config['Streaming']['Database'], check_same_thread=False) # Streamer Cache stream_cache = {} # Check if user is a streamer def valid_streamer(uuid): streamer = None if 'uuid' in session: if session['uuid'] in stream_cache: streamer = stream_cache[session['uuid']] else: # Find key in database data = conn.execute('SELECT * FROM channels WHERE user_uuid=?', (session['uuid'],)) row = data.fetchone() # Deny stream publish if row: streamer = row[2] return streamer @app.route("/") def index(): streamer = False if 'uuid' in session: if valid_streamer(session['uuid']): streamer = True return render_template("index.html", streamer = streamer) @app.route("/dashboard") def dashboard(): if not 'uuid' in session: return redirect("/login", code = 302) streamkey = valid_streamer(session['uuid']) if not streamkey: return make_response("Unauthorized.", 402) return render_template("dashboard.html", stream = streamkey, server = config['Streaming']['PublishAddress'].format(streamer = "", host = config['Streaming']['ServerHost'])) @app.route("/dashboard/data") def dashboard_data(): if not 'uuid' in session: return jsonify({'error': 'Unauthorized'}) streamkey = valid_streamer(session['uuid']) if not streamkey: return jsonify({'error': 'Unauthorized'}) # Find key in database data = conn.execute('SELECT * FROM channels WHERE key=?', (streamkey,)) row = data.fetchone() if not row: return jsonify({'error': 'Unauthorized'}) livedate = row[3] data = { 'name': row[1], 'key': streamkey, 'uuid': session['uuid'], 'live': livedate != None, 'live_at': livedate, 'last_stream': row[6], } return jsonify(data) # Called when starting publishing @app.route("/publish", methods=["POST"]) def publish(): print(json.dumps(request.form, ensure_ascii=False)) streamkey = request.form["name"] # Find key in database data = conn.execute('SELECT * FROM channels WHERE key=?', (streamkey,)) row = data.fetchone() # Deny stream publish if not row: return make_response("Request Denied", 400) streamer = row[1] print("Streamer %s has started streaming!" % streamer) # Redirect stream publish to stream name url = config['Streaming']['PublishAddress'].format(streamer = streamer, host = "127.0.0.1") response = make_response(url, 302) response.headers["Location"] = url # Update database with stream timestamp starttime = strftime("%Y-%m-%d %H:%M:%S", gmtime()) conn.execute('UPDATE channels SET live_at=? WHERE id=?', (starttime, row[0])) conn.commit() return response # Called when stopped publishing @app.route("/publish_done", methods=["POST"]) def publish_done(): streamkey = request.form["name"] # Update database with stream end time endtime = strftime("%Y-%m-%d %H:%M:%S", gmtime()) conn.execute('UPDATE channels SET live_at=NULL, last_stream=? WHERE key=?', (endtime, streamkey,)) conn.commit() return "OK" @app.route("/login") def login(): if 'uuid' in session: return make_response("Already authenticated as %s!" % session['username']) state = str(uuid4()) session['state'] = state return redirect(oauth_auth.format(state = state, client = oauth_id, redirect = oauth_redirect, server = auth_server), code = 302) @app.route("/logout") def logout(): session.clear() return redirect("/", code = 302) @app.route("/auth/_callback/") def cb(): if not 'state' in session: return make_response("Something went wrong!", 402) code = request.args.get('code') state = request.args.get('state') if session['state'] != state: return make_response("Something went wrong!", 402) if not code: return make_response("Authorization denied by user", 402) # Get access token r = requests.post(auth_server + "/oauth2/token", data = { 'grant_type': 'authorization_code', 'code': code, 'redirect_uri': oauth_redirect, 'client_id': oauth_id, 'client_secret': oauth_secret }, headers = { 'Authorization': 'Basic ' + str(base64.b64encode(bytes("%s:%s" % (oauth_id, oauth_secret), 'utf-8'))) }) res_token = None try: res_token = r.json() except ValueError: return make_response("Something went wrong while getting an access token!") if 'error' in res_token: return make_response("%s: %s" % (res_token['error'], res_token['error_description']), 500) token = res_token['access_token'] # Get user info ru = requests.get(auth_server + "/oauth2/user", headers = { 'Authorization': 'Bearer ' + token }) res_uinfo = None try: res_uinfo = ru.json() except ValueError: return make_response("Something went wrong while getting user information!") udata = conn.execute('SELECT * FROM signed_users WHERE uuid=?', (res_uinfo['uuid'],)) row = udata.fetchone() if not row: conn.execute('INSERT INTO signed_users (uuid, name) VALUES (?, ?)', (res_uinfo['uuid'], res_uinfo['username'])) conn.commit() session['uuid'] = res_uinfo['uuid'] session['username'] = res_uinfo['username'] return redirect("/", code = 302) @app.route("/watch/") def watch(name): return render_template("player.html", name = name, server = stream_server) @app.route("/player/") def watch_old(name): return redirect("/watch/%s" % name, code = 302) @app.route("/dist/") def dist(path): return send_from_directory("dist", path) if __name__ == '__main__': app.secret_key = '00-wegrhr[gqw[er=1ew qwergfdq.///**+' app.debug = True app.run()