initial commit

This commit is contained in:
herby2212 2023-04-15 19:53:36 +02:00
commit 3b18432788
5 changed files with 257 additions and 1 deletions

View file

@ -138,6 +138,20 @@
</div> </div>
</div> </div>
</div> </div>
<div class="row">
<div class="col-md-12">
<h4><i class="fa fa-video-camera"></i> Daily concurrent stream count</span> by stream type <small>Last <span class="days">30</span> days</small></h4>
<p class="help-block">
The total count of concurrent streams of tv, movies, and music by the transcode decision.
</p>
<div class="graphs-instance">
<div class="watch-chart" id="graph_concurrent_streams_by_stream_type">
<div class="graphs-load"><i class="fa fa-refresh fa-spin"></i> Loading chart...</div>
<br>
</div>
</div>
</div>
</div>
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<h4><i class="fa fa-expand-arrows-alt"></i> <span class="yaxis-text">Play count</span> by source resolution <small>Last <span class="days">30</span> days</small></h4> <h4><i class="fa fa-expand-arrows-alt"></i> <span class="yaxis-text">Play count</span> by source resolution <small>Last <span class="days">30</span> days</small></h4>
@ -327,6 +341,7 @@
<script src="${http_root}js/graphs/plays_by_platform.js${cache_param}"></script> <script src="${http_root}js/graphs/plays_by_platform.js${cache_param}"></script>
<script src="${http_root}js/graphs/plays_by_user.js${cache_param}"></script> <script src="${http_root}js/graphs/plays_by_user.js${cache_param}"></script>
<script src="${http_root}js/graphs/plays_by_stream_type.js${cache_param}"></script> <script src="${http_root}js/graphs/plays_by_stream_type.js${cache_param}"></script>
<script src="${http_root}js/graphs/concurrent_streams_by_stream_type.js${cache_param}"></script>
<script src="${http_root}js/graphs/plays_by_source_resolution.js${cache_param}"></script> <script src="${http_root}js/graphs/plays_by_source_resolution.js${cache_param}"></script>
<script src="${http_root}js/graphs/plays_by_stream_resolution.js${cache_param}"></script> <script src="${http_root}js/graphs/plays_by_stream_resolution.js${cache_param}"></script>
<script src="${http_root}js/graphs/plays_by_platform_by_stream_type.js${cache_param}"></script> <script src="${http_root}js/graphs/plays_by_platform_by_stream_type.js${cache_param}"></script>
@ -519,6 +534,33 @@
} }
}); });
$.ajax({
url: "get_concurrent_streams_by_stream_type",
type: 'get',
data: { time_range: time_range },
dataType: "json",
success: function(data) {
var dateArray = [];
$.each(data.categories, function (i, day) {
dateArray.push(moment(day, 'YYYY-MM-DD').valueOf());
// Highlight the weekend
if ((moment(day, 'YYYY-MM-DD').format('ddd') == 'Sat') ||
(moment(day, 'YYYY-MM-DD').format('ddd') == 'Sun')) {
hc_plays_by_day_options.xAxis.plotBands.push({
from: i-0.5,
to: i+0.5,
color: 'rgba(80,80,80,0.3)'
});
}
});
hc_concurrent_streams_by_stream_type_options.yAxis.min = 0;
hc_concurrent_streams_by_stream_type_options.xAxis.categories = dateArray;
hc_concurrent_streams_by_stream_type_options.series = getGraphVisibility(hc_concurrent_streams_by_stream_type_options.chart.renderTo, data.series);
hc_concurrent_streams_by_stream_type_options.colors = getGraphColors(data.series);
var hc_plays_by_stream_type = new Highcharts.Chart(hc_concurrent_streams_by_stream_type_options);
}
});
$.ajax({ $.ajax({
url: "get_plays_by_source_resolution", url: "get_plays_by_source_resolution",
type: 'get', type: 'get',
@ -727,6 +769,7 @@
hc_plays_by_day_options.xAxis.plotBands = []; hc_plays_by_day_options.xAxis.plotBands = [];
hc_plays_by_stream_type_options.xAxis.plotBands = []; hc_plays_by_stream_type_options.xAxis.plotBands = [];
hc_concurrent_streams_by_stream_type_options.xAxis.plotBands = [];
hc_plays_by_day_options.yAxis.labels.formatter = yaxis_format; hc_plays_by_day_options.yAxis.labels.formatter = yaxis_format;
hc_plays_by_dayofweek_options.yAxis.labels.formatter = yaxis_format; hc_plays_by_dayofweek_options.yAxis.labels.formatter = yaxis_format;
@ -734,6 +777,7 @@
hc_plays_by_platform_options.yAxis.labels.formatter = yaxis_format; hc_plays_by_platform_options.yAxis.labels.formatter = yaxis_format;
hc_plays_by_user_options.yAxis.labels.formatter = yaxis_format; hc_plays_by_user_options.yAxis.labels.formatter = yaxis_format;
hc_plays_by_stream_type_options.yAxis.labels.formatter = yaxis_format; hc_plays_by_stream_type_options.yAxis.labels.formatter = yaxis_format;
hc_concurrent_streams_by_stream_type_options.yAxis.labels.formatter = yaxis_format;
hc_plays_by_source_resolution_options.yAxis.labels.formatter = yaxis_format; hc_plays_by_source_resolution_options.yAxis.labels.formatter = yaxis_format;
hc_plays_by_stream_resolution_options.yAxis.labels.formatter = yaxis_format; hc_plays_by_stream_resolution_options.yAxis.labels.formatter = yaxis_format;
hc_plays_by_platform_by_stream_type_options.yAxis.labels.formatter = yaxis_format; hc_plays_by_platform_by_stream_type_options.yAxis.labels.formatter = yaxis_format;
@ -746,6 +790,7 @@
hc_plays_by_platform_options.tooltip.formatter = tooltip_format; hc_plays_by_platform_options.tooltip.formatter = tooltip_format;
hc_plays_by_user_options.tooltip.formatter = tooltip_format; hc_plays_by_user_options.tooltip.formatter = tooltip_format;
hc_plays_by_stream_type_options.tooltip.formatter = tooltip_format; hc_plays_by_stream_type_options.tooltip.formatter = tooltip_format;
hc_concurrent_streams_by_stream_type_options.tooltip.formatter = tooltip_format;
hc_plays_by_source_resolution_options.tooltip.formatter = tooltip_format; hc_plays_by_source_resolution_options.tooltip.formatter = tooltip_format;
hc_plays_by_stream_resolution_options.tooltip.formatter = tooltip_format; hc_plays_by_stream_resolution_options.tooltip.formatter = tooltip_format;
hc_plays_by_platform_by_stream_type_options.tooltip.formatter = tooltip_format; hc_plays_by_platform_by_stream_type_options.tooltip.formatter = tooltip_format;

View file

@ -0,0 +1,71 @@
var hc_concurrent_streams_by_stream_type_options = {
chart: {
type: 'line',
backgroundColor: 'rgba(0,0,0,0)',
renderTo: 'graph_concurrent_streams_by_stream_type'
},
title: {
text: ''
},
legend: {
enabled: true,
itemStyle: {
font: '9pt "Open Sans", sans-serif',
color: '#A0A0A0'
},
itemHoverStyle: {
color: '#FFF'
},
itemHiddenStyle: {
color: '#444'
}
},
credits: {
enabled: false
},
plotOptions: {
series: {
cursor: 'pointer',
point: {
events: {
click: function() {
selectHandler(this.category, this.series.name);
}
}
},
events: {
legendItemClick: function() {
setGraphVisibility(this.chart.renderTo.id, this.chart.series, this.name);
}
}
}
},
xAxis: {
type: 'datetime',
labels: {
formatter: function() {
return moment(this.value).format("MMM D");
},
style: {
color: '#aaa'
}
},
categories: [{}],
plotBands: []
},
yAxis: {
title: {
text: null
},
labels: {
style: {
color: '#aaa'
}
}
},
tooltip: {
shared: true,
crosshairs: true
},
series: [{}]
};

View file

@ -22,7 +22,7 @@ from future.builtins import object
import arrow import arrow
import datetime import datetime
import itertools
import plexpy import plexpy
if plexpy.PYTHON2: if plexpy.PYTHON2:
import common import common
@ -854,6 +854,103 @@ class Graphs(object):
'series': [series_1_output, series_2_output, series_3_output]} 'series': [series_1_output, series_2_output, series_3_output]}
return output return output
def get_total_concurrent_streams_per_stream_type(self, time_range='30'):
monitor_db = database.MonitorDatabase()
time_range = helpers.cast_to_int(time_range) or 30
timestamp = helpers.timestamp() - time_range * 24 * 60 * 60
def calc_most_concurrent(result):
'''
Function to calculate most concurrent streams
Input: Stat title, SQLite query result
Output: Dict {title, count, started, stopped}
'''
times = []
for item in result:
times.append({'time': str(item['started']) + 'B', 'count': 1})
times.append({'time': str(item['stopped']) + 'A', 'count': -1})
times = sorted(times, key=lambda k: k['time'])
count = 0
last_count = 0
last_start = ''
concurrent = { 'count': 0,
'started': None,
'stopped': None
}
for d in times:
if d['count'] == 1:
count += d['count']
if count >= last_count:
last_start = d['time']
else:
if count >= last_count:
last_count = count
concurrent['count'] = count
concurrent['started'] = last_start[:-1]
concurrent['stopped'] = d['time'][:-1]
count += d['count']
return concurrent
try:
query = 'SELECT sh.date_played, sh.started, sh.stopped, shmi.transcode_decision ' \
'FROM (SELECT *, ' \
'date(started, "unixepoch", "localtime") AS date_played ' \
'FROM session_history) AS sh ' \
'JOIN session_history_media_info AS shmi ON sh.id = shmi.id ' \
'WHERE sh.stopped >= %s ' \
'ORDER BY sh.date_played' % timestamp
result = monitor_db.select(query)
except Exception as e:
logger.warn("Tautulli Graphs :: Unable to execute database query for get_total_plays_per_stream_type: %s." % e)
return None
# create our date range as some days may not have any data
# but we still want to display them
base = datetime.date.today()
date_list = [base - datetime.timedelta(days=x) for x in range(0, int(time_range))]
categories = []
series_1 = []
series_2 = []
series_3 = []
grouped_result = helpers.group_by_keys(result, ('date_played','transcode_decision'))
for date_item in sorted(date_list):
date_string = date_item.strftime('%Y-%m-%d')
categories.append(date_string)
series_1_value = 0
series_2_value = 0
series_3_value = 0
for item in grouped_result:
if item['key'] == (date_string,'direct play'):
series_1_value = calc_most_concurrent(item['value'])['count']
elif item['key'] == (date_string,'copy'):
series_2_value = calc_most_concurrent(item['value'])['count']
elif item['key'] == (date_string,'transcode'):
series_3_value = calc_most_concurrent(item['value'])['count']
series_1.append(series_1_value)
series_2.append(series_2_value)
series_3.append(series_3_value)
series_1_output = {'name': 'Direct Play',
'data': series_1}
series_2_output = {'name': 'Direct Stream',
'data': series_2}
series_3_output = {'name': 'Transcode',
'data': series_3}
output = {'categories': categories,
'series': [series_1_output, series_2_output, series_3_output]}
return output
def get_total_plays_by_source_resolution(self, time_range='30', y_axis='plays', user_id=None, grouping=None): def get_total_plays_by_source_resolution(self, time_range='30', y_axis='plays', user_id=None, grouping=None):
monitor_db = database.MonitorDatabase() monitor_db = database.MonitorDatabase()

View file

@ -32,6 +32,7 @@ import datetime
from functools import reduce, wraps from functools import reduce, wraps
import hashlib import hashlib
import imghdr import imghdr
from itertools import groupby
from future.moves.itertools import islice, zip_longest from future.moves.itertools import islice, zip_longest
import ipwhois import ipwhois
import ipwhois.exceptions import ipwhois.exceptions
@ -1240,6 +1241,11 @@ def grouper(iterable, n, fillvalue=None):
args = [iter(iterable)] * n args = [iter(iterable)] * n
return zip_longest(fillvalue=fillvalue, *args) return zip_longest(fillvalue=fillvalue, *args)
def group_by_keys(iterable, keys):
key_function = operator.itemgetter(*keys)
sorted_iterable = sorted(iterable, key=key_function)
return[{'key': key, 'value': list(group)} for key, group in groupby(sorted_iterable, key_function)]
def chunk(it, size): def chunk(it, size):
it = iter(it) it = iter(it)

View file

@ -2519,6 +2519,43 @@ class WebInterface(object):
logger.warn("Unable to retrieve data for get_plays_by_stream_type.") logger.warn("Unable to retrieve data for get_plays_by_stream_type.")
return result return result
@cherrypy.expose
@cherrypy.tools.json_out()
@requireAuth()
@addtoapi()
def get_concurrent_streams_by_stream_type(self, time_range='30', **kwargs):
""" Get graph data for concurrent streams by stream type by date.
```
Required parameters:
None
Optional parameters:
time_range (str): The number of days of data to return
Returns:
json:
{"categories":
["YYYY-MM-DD", "YYYY-MM-DD", ...]
"series":
[{"name": "Direct Play", "data": [...]}
{"name": "Direct Stream", "data": [...]},
{"name": "Transcode", "data": [...]}
]
}
```
"""
grouping = helpers.bool_true(grouping, return_none=True)
graph = graphs.Graphs()
result = graph.get_total_concurrent_streams_per_stream_type(time_range=time_range)
if result:
return result
else:
logger.warn("Unable to retrieve data for get_concurrent_streams_by_stream_type.")
return result
@cherrypy.expose @cherrypy.expose
@cherrypy.tools.json_out() @cherrypy.tools.json_out()
@requireAuth() @requireAuth()