metrics
This commit is contained in:
parent
29f111e98f
commit
176b15ee49
78
app.py
78
app.py
@ -1,7 +1,8 @@
|
||||
#!/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 time import gmtime, strftime, time
|
||||
from uuid import uuid4
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
import requests
|
||||
import json
|
||||
@ -73,9 +74,13 @@ oauth_secret = config['OAuth2']['ClientSecret']
|
||||
# Database
|
||||
conn = sqlite3.connect(config['Streaming']['Database'], check_same_thread=False)
|
||||
|
||||
# Streamer Cache
|
||||
# Streamer memcache
|
||||
stream_cache = {}
|
||||
|
||||
# Stat memcache
|
||||
stat_cache = False
|
||||
stat_cache_updated = 0
|
||||
|
||||
# Check if user is a streamer
|
||||
def valid_streamer(uuid):
|
||||
streamer = None
|
||||
@ -94,6 +99,60 @@ def valid_streamer(uuid):
|
||||
|
||||
return streamer
|
||||
|
||||
def pull_stream_metrics(uuid):
|
||||
global stat_cache
|
||||
global stat_cache_updated
|
||||
updated = False
|
||||
if not stat_cache or stat_cache_updated < int(time()) - 5:
|
||||
metric_path = stream_server + "stat"
|
||||
r = requests.get(metric_path)
|
||||
stat_cache = BeautifulSoup(r.text, "xml")
|
||||
updated = True
|
||||
print("Updating per request..")
|
||||
|
||||
allapps = stat_cache.find_all("application")
|
||||
relevant = None
|
||||
for app in allapps:
|
||||
if str(app.find("name").string) == "live":
|
||||
relevant = app
|
||||
|
||||
if not relevant:
|
||||
return None
|
||||
streams = relevant.find_all("stream")
|
||||
|
||||
if not streams:
|
||||
return None
|
||||
|
||||
if updated:
|
||||
stat_cache_updated = int(time())
|
||||
|
||||
relevant = None
|
||||
for stream in streams:
|
||||
name = stream.find("name").string
|
||||
if name == uuid:
|
||||
relevant = stream
|
||||
|
||||
if not relevant:
|
||||
return None
|
||||
|
||||
data = {
|
||||
'time' : relevant.time.string,
|
||||
'bytes' : relevant.bytes_in.string,
|
||||
'video': {
|
||||
'width': relevant.meta.video.width.string,
|
||||
'height': relevant.meta.video.height.string,
|
||||
'frame_rate': relevant.meta.video.frame_rate.string,
|
||||
'codec': relevant.meta.video.codec.string,
|
||||
},
|
||||
'audio': {
|
||||
'sample_rate': relevant.meta.audio.sample_rate.string,
|
||||
'channels': relevant.meta.audio.channels.string,
|
||||
'codec': relevant.meta.audio.codec.string,
|
||||
}
|
||||
}
|
||||
|
||||
return data
|
||||
|
||||
@app.route("/")
|
||||
def index():
|
||||
streamer = False
|
||||
@ -115,6 +174,21 @@ def dashboard():
|
||||
return render_template("dashboard.html", stream = streamkey,
|
||||
server = config['Streaming']['PublishAddress'].format(streamer = "", host = config['Streaming']['ServerHost']))
|
||||
|
||||
@app.route("/dashboard/stats")
|
||||
def dashboard_stats():
|
||||
if not 'uuid' in session:
|
||||
return jsonify({'error': 'Unauthorized'})
|
||||
|
||||
streamkey = valid_streamer(session['uuid'])
|
||||
if not streamkey:
|
||||
return jsonify({'error': 'Unauthorized'})
|
||||
|
||||
data = pull_stream_metrics(streamkey)
|
||||
if not data:
|
||||
return jsonify({'error': 'No data was returned..'})
|
||||
|
||||
return jsonify(data)
|
||||
|
||||
@app.route("/dashboard/data")
|
||||
def dashboard_data():
|
||||
if not 'uuid' in session:
|
||||
|
@ -1,5 +1,27 @@
|
||||
import $ from 'jquery'
|
||||
|
||||
// https://stackoverflow.com/a/18650828
|
||||
function formatBytes(a,b){if(0==a)return"0 Bytes";var c=1024,d=b||2,e=["Bytes","KB","MB","GB","TB","PB","EB","ZB","YB"],f=Math.floor(Math.log(a)/Math.log(c));return parseFloat((a/Math.pow(c,f)).toFixed(d))+" "+e[f]}
|
||||
|
||||
function recursive_stats(table, subtable) {
|
||||
var key
|
||||
for (key in table) {
|
||||
var val = table[key]
|
||||
if (typeof(val) == "object") {
|
||||
recursive_stats(val, key)
|
||||
} else {
|
||||
if (key == "time") {
|
||||
var date = new Date(null);
|
||||
date.setSeconds(Math.floor(parseInt(val)/1000)); // specify value for SECONDS here
|
||||
val = date.toISOString().substr(11, 8);
|
||||
} else if (key.indexOf("bytes") != -1) {
|
||||
val = formatBytes(val, 3)
|
||||
}
|
||||
$("#stat_" + (subtable ? subtable + "_" : "") + key).text(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function dashboard () {
|
||||
$.get("/dashboard/data", function (res) {
|
||||
if (res.error) {
|
||||
@ -7,14 +29,23 @@ function dashboard () {
|
||||
return
|
||||
}
|
||||
|
||||
var fullURL = window.location.origin + "/player/" + res.name
|
||||
var fullURL = window.location.origin + "/watch/" + res.name
|
||||
$('#myStream').attr('src', fullURL)
|
||||
$('#stream_url').text(fullURL).attr("href", fullURL)
|
||||
console.log(res)
|
||||
$('#stream_live').text(res.live ? 'Yes' : 'No')
|
||||
})
|
||||
|
||||
$('#show_key').click(function () {
|
||||
$('#show_key').html(window.STREAM_KEY)
|
||||
})
|
||||
|
||||
setInterval(function () {
|
||||
$.get("/dashboard/stats", function (res) {
|
||||
if (res.error) return
|
||||
recursive_stats(res, "")
|
||||
})
|
||||
}, 5000)
|
||||
}
|
||||
|
||||
export default {start: dashboard}
|
||||
|
@ -1,6 +1,8 @@
|
||||
import css from './css/player.css'
|
||||
import Hls from 'hls.js'
|
||||
|
||||
css.ref()
|
||||
|
||||
let player
|
||||
let vid
|
||||
let btn
|
||||
|
@ -32,7 +32,7 @@
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
|
||||
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4" id="page-dashboard">
|
||||
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pb-2 mb-3 border-bottom">
|
||||
<h1 class="h2">Dashboard</h1>
|
||||
</div>
|
||||
@ -40,6 +40,15 @@
|
||||
<iframe allowfullscreen src="" id="myStream" width="1542" height="651" style="display: block; width: 1542px; height: 651px;"></iframe>
|
||||
<h1 class="h2">Information</h1>
|
||||
<p class="lead">
|
||||
<div class="row">
|
||||
<div class="col-2">
|
||||
<label>Live</label>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span id="stream_live">No</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-2">
|
||||
<label>Stream Server</label>
|
||||
@ -67,6 +76,52 @@
|
||||
</div>
|
||||
</div>
|
||||
</p>
|
||||
|
||||
<h1 class="h2">Metrics</h1>
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Time</th>
|
||||
<th>Bytes</th>
|
||||
<th>Video</th>
|
||||
<th>Audio</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span id="stat_time"> </span></td>
|
||||
<td><span id="stat_bytes"> </span></td>
|
||||
<td>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Width</th>
|
||||
<th>Height</th>
|
||||
<th>Frame Rate</th>
|
||||
<th>Codec</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span id="stat_video_width"> </span></td>
|
||||
<td><span id="stat_video_height"> </span></td>
|
||||
<td><span id="stat_video_frame_rate"> </span></td>
|
||||
<td><span id="stat_video_codec"> </span></td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
<td>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Channels</th>
|
||||
<th>Sample Rate</th>
|
||||
<th>Codec</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span id="stat_audio_channels"> </span></td>
|
||||
<td><span id="stat_audio_sample_rate"> </span></td>
|
||||
<td><span id="stat_audio_codec"> </span></td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
Reference in New Issue
Block a user