metrics
This commit is contained in:
parent
29f111e98f
commit
176b15ee49
78
app.py
78
app.py
@ -1,7 +1,8 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from flask import Flask, request, make_response, session, redirect, render_template, send_from_directory, jsonify
|
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 uuid import uuid4
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
import json
|
import json
|
||||||
@ -73,9 +74,13 @@ oauth_secret = config['OAuth2']['ClientSecret']
|
|||||||
# Database
|
# Database
|
||||||
conn = sqlite3.connect(config['Streaming']['Database'], check_same_thread=False)
|
conn = sqlite3.connect(config['Streaming']['Database'], check_same_thread=False)
|
||||||
|
|
||||||
# Streamer Cache
|
# Streamer memcache
|
||||||
stream_cache = {}
|
stream_cache = {}
|
||||||
|
|
||||||
|
# Stat memcache
|
||||||
|
stat_cache = False
|
||||||
|
stat_cache_updated = 0
|
||||||
|
|
||||||
# Check if user is a streamer
|
# Check if user is a streamer
|
||||||
def valid_streamer(uuid):
|
def valid_streamer(uuid):
|
||||||
streamer = None
|
streamer = None
|
||||||
@ -94,6 +99,60 @@ def valid_streamer(uuid):
|
|||||||
|
|
||||||
return streamer
|
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("/")
|
@app.route("/")
|
||||||
def index():
|
def index():
|
||||||
streamer = False
|
streamer = False
|
||||||
@ -115,6 +174,21 @@ def dashboard():
|
|||||||
return render_template("dashboard.html", stream = streamkey,
|
return render_template("dashboard.html", stream = streamkey,
|
||||||
server = config['Streaming']['PublishAddress'].format(streamer = "", host = config['Streaming']['ServerHost']))
|
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")
|
@app.route("/dashboard/data")
|
||||||
def dashboard_data():
|
def dashboard_data():
|
||||||
if not 'uuid' in session:
|
if not 'uuid' in session:
|
||||||
|
@ -1,5 +1,27 @@
|
|||||||
import $ from 'jquery'
|
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 () {
|
function dashboard () {
|
||||||
$.get("/dashboard/data", function (res) {
|
$.get("/dashboard/data", function (res) {
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
@ -7,14 +29,23 @@ function dashboard () {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var fullURL = window.location.origin + "/player/" + res.name
|
var fullURL = window.location.origin + "/watch/" + res.name
|
||||||
$('#myStream').attr('src', fullURL)
|
$('#myStream').attr('src', fullURL)
|
||||||
$('#stream_url').text(fullURL).attr("href", fullURL)
|
$('#stream_url').text(fullURL).attr("href", fullURL)
|
||||||
|
console.log(res)
|
||||||
|
$('#stream_live').text(res.live ? 'Yes' : 'No')
|
||||||
})
|
})
|
||||||
|
|
||||||
$('#show_key').click(function () {
|
$('#show_key').click(function () {
|
||||||
$('#show_key').html(window.STREAM_KEY)
|
$('#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}
|
export default {start: dashboard}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import css from './css/player.css'
|
import css from './css/player.css'
|
||||||
import Hls from 'hls.js'
|
import Hls from 'hls.js'
|
||||||
|
|
||||||
|
css.ref()
|
||||||
|
|
||||||
let player
|
let player
|
||||||
let vid
|
let vid
|
||||||
let btn
|
let btn
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</nav>
|
</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">
|
<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>
|
<h1 class="h2">Dashboard</h1>
|
||||||
</div>
|
</div>
|
||||||
@ -40,6 +40,15 @@
|
|||||||
<iframe allowfullscreen src="" id="myStream" width="1542" height="651" style="display: block; width: 1542px; height: 651px;"></iframe>
|
<iframe allowfullscreen src="" id="myStream" width="1542" height="651" style="display: block; width: 1542px; height: 651px;"></iframe>
|
||||||
<h1 class="h2">Information</h1>
|
<h1 class="h2">Information</h1>
|
||||||
<p class="lead">
|
<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="row">
|
||||||
<div class="col-2">
|
<div class="col-2">
|
||||||
<label>Stream Server</label>
|
<label>Stream Server</label>
|
||||||
@ -67,6 +76,52 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</p>
|
</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>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Reference in New Issue
Block a user