Merge pull request #145 from JonnyWong16/server-stats

Initial implementation of server statistics
This commit is contained in:
drzoidberg33 2015-08-30 17:46:04 +02:00
commit dceab3791f
5 changed files with 363 additions and 15 deletions

View file

@ -1433,7 +1433,7 @@ a .season-episodes-card-overlay:hover {
font-size: 12px; font-size: 12px;
float: left; float: left;
position: relative; position: relative;
top: 14px; top: 15px;
left: 0px; left: 0px;
} }
.user-overview-stats-instance h3 strong{ .user-overview-stats-instance h3 strong{
@ -1516,7 +1516,7 @@ a .season-episodes-card-overlay:hover {
font-size: 12px; font-size: 12px;
float: left; float: left;
position: relative; position: relative;
top: 14px; top: 15px;
left: 0px; left: 0px;
} }
.home-platforms { .home-platforms {
@ -1572,6 +1572,38 @@ a .season-episodes-card-overlay:hover {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.home-platforms-instance-name2 {
position: absolute;
top: 34px;
left: 215px;
}
.home-platforms-instance-name2 h5 {
font-size: 14px;
line-height: 16px;
margin: 15px 0 2px 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.home-platforms-instance-name2 h3 {
font-size: 30px;
font-weight: bold;
color: #F9AA03;
line-height: 22px;
position: relative;
top: 5px;
margin: 0 5px 0 0;
padding-top: 6px;
float: left;
}
.home-platforms-instance-name2 p {
color: #aaa;
font-size: 12px;
float: left;
position: relative;
top: 21px;
left: 0px;
}
.home-platforms-instance-playcount { .home-platforms-instance-playcount {
float: left; float: left;
position: relative; position: relative;
@ -1593,7 +1625,7 @@ a .season-episodes-card-overlay:hover {
font-size: 12px; font-size: 12px;
float: left; float: left;
position: relative; position: relative;
top: 14px; top: 15px;
left: 0px; left: 0px;
margin-right: 5px; margin-right: 5px;
} }
@ -1606,7 +1638,7 @@ a .season-episodes-card-overlay:hover {
.home-platforms-instance-last-user h5 { .home-platforms-instance-last-user h5 {
font-size: 12px; font-size: 12px;
position: relative; position: relative;
margin: 0 0 3px 0; margin: 0 0 2px 0;
float: left; float: left;
} }
.home-platforms-instance-last-user p { .home-platforms-instance-last-user p {
@ -1633,6 +1665,13 @@ a .season-episodes-card-overlay:hover {
-moz-box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1); -moz-box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1);
box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1); box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1);
} }
.home-platforms-instance-poster .home-platforms-server-thumb {
background-position: center;
background-size: cover;
height: 80px;
width: 80px;
margin-top: 20px;
}
.home-platforms-instance-box { .home-platforms-instance-box {
background-position: center; background-position: center;
background-size: cover; background-size: cover;

View file

@ -19,7 +19,7 @@
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<div class="padded-header"> <div class="padded-header">
<h3>Statistics <small>Last ${config['home_stats_length']} days</small></h3> <h3>Watch Statistics <small>Last ${config['home_stats_length']} days</small></h3>
</div> </div>
<div id="home-stats" class="home-platforms"> <div id="home-stats" class="home-platforms">
<div class='text-muted'><i class="fa fa-refresh fa-spin"></i> Loading stats...</div> <div class='text-muted'><i class="fa fa-refresh fa-spin"></i> Loading stats...</div>
@ -27,6 +27,17 @@
</div> </div>
</div> </div>
</div> </div>
<div class="row">
<div class="col-md-12">
<div class="padded-header" id="server-statistics-header">
<h3>Server Statistics</h3>
</div>
<div id="server-stats" class="server-platforms">
<div class='text-muted'><i class="fa fa-refresh fa-spin"></i> Loading stats...</div>
<br>
</div>
</div>
</div>
<div class='row'> <div class='row'>
<div class="col-md-12"> <div class="col-md-12">
<div class="padded-header"> <div class="padded-header">
@ -45,17 +56,18 @@
<script src="interfaces/default/js/moment-with-locale.js"></script> <script src="interfaces/default/js/moment-with-locale.js"></script>
<script> <script>
function getHomeStats(days, stat_type, stat_count) { function currentActivityHeader() {
$.ajax({ $.ajax({
url: 'home_stats', url: 'get_current_activity_header',
cache: false, cache: false,
async: true, async: true,
data: {time_range: days, stat_type: stat_type, stat_count: stat_count},
complete: function(xhr, status) { complete: function(xhr, status) {
$("#home-stats").html(xhr.responseText); $("#current-activity-header").html(xhr.responseText);
} }
}); });
} }
currentActivityHeader();
setInterval(currentActivityHeader, 15000);
function currentActivity() { function currentActivity() {
$.ajax({ $.ajax({
@ -70,18 +82,50 @@
currentActivity(); currentActivity();
setInterval(currentActivity, 15000); setInterval(currentActivity, 15000);
function currentActivityHeader() { function getHomeStats(days, stat_type, stat_count) {
$.ajax({ $.ajax({
url: 'get_current_activity_header', url: 'home_stats',
cache: false, cache: false,
async: true, async: true,
data: {time_range: days, stat_type: stat_type, stat_count: stat_count},
complete: function(xhr, status) { complete: function(xhr, status) {
$("#current-activity-header").html(xhr.responseText); $("#home-stats").html(xhr.responseText);
}
});
}
function getServerStatsHeader() {
$.ajax({
"url": "get_servers_info",
type: "post",
cache: false,
async: true,
data: { },
complete: function (xhr, status) {
server_info = $.parseJSON(xhr.responseText);
var server_name = 'Server name not found';
for (var i in server_info) {
if (server_info[i].machine_identifier == '${config['pms_identifier']}') {
server_name = server_info[i].name
break;
}
}
$('#server-statistics-header h3').append(' <small>' + server_name + '</small>')
}
});
}
function getServerStats() {
$.ajax({
url: 'server_stats',
cache: false,
async: true,
data: { },
complete: function(xhr, status) {
$("#server-stats").html(xhr.responseText);
} }
}); });
} }
currentActivityHeader();
setInterval(currentActivityHeader, 15000);
function recentlyAdded() { function recentlyAdded() {
var widthVal = $('body').find(".container-fluid").width(); var widthVal = $('body').find(".container-fluid").width();
@ -122,6 +166,8 @@
}); });
getHomeStats(${config['home_stats_length']}, ${config['home_stats_type']}, ${config['home_stats_count']}); getHomeStats(${config['home_stats_length']}, ${config['home_stats_type']}, ${config['home_stats_count']});
getServerStatsHeader();
getServerStats();
</script> </script>

View file

@ -0,0 +1,78 @@
<%doc>
USAGE DOCUMENTATION :: PLEASE LEAVE THIS AT THE TOP OF THIS FILE
For Mako templating syntax documentation please visit: http://docs.makotemplates.org/en/latest/
Filename: server_stats.html
Version: 0.1
Variable names: data [array]
data[array_index] :: Usable parameters
data['type'] Returns the type of the library. Either 'movie', 'show', 'photo', or 'artist'.
data['rows'] Returns an array containing stat data
data[array_index]['rows'] :: Usable parameters
title Returns the title of the library.
thumb Returns the thumb of the library.
count Returns the number of items in the library.
count_type Returns the sorting type for the library
== Only if 'type' is 'show'
episode_count Return the number of episodes in the library.
episode_count_type Return the sorting type for the episodes.
== Only if 'type' is 'artist'
album_count Return the number of episodes in the library.
album_count_type Return the sorting type for the episodes.
DOCUMENTATION :: END
</%doc>
% if data:
<ul class="list-unstyled">
% for library in data:
<div class="home-platforms-instance">
<li>
<div class="home-platforms-instance-info">
<div class="home-platforms-instance-name">
<h4>${library['rows']['title']}</h4>
<h5>${library['rows']['count_type']}</h5>
</div>
<div class="home-platforms-instance-playcount">
<h3>${library['rows']['count']}</h3>
<p> items</p>
</div>
% if library['type'] == 'show':
<div class="home-platforms-instance-name2">
<h5>${library['rows']['episode_count_type']}</h5>
<h3>${library['rows']['episode_count']}</h3>
<p> items</p>
</div>
% endif
% if library['type'] == 'artist':
<div class="home-platforms-instance-name2">
<h5>${library['rows']['album_count_type']}</h5>
<h3>${library['rows']['album_count']}</h3>
<p> items</p>
</div>
% endif
</div>
% if library['rows']['thumb']:
<div class="home-platforms-instance-poster">
<div class="home-platforms-server-thumb" style="background-image: url(pms_image_proxy?img=${library['rows']['thumb']}&width=300&height=300&fallback=poster);"></div>
</div>
% else:
<div class="home-platforms-instance-poster">
<div class="home-platforms-server-thumb" style="background-image: url(interfaces/default/images/poster.png);"></div>
</div>
% endif
</li>
</div>
% endfor
</ul>
% else:
<div class="text-muted">Unable to retrieve data from database. Please check your <a href="settings">settings</a>.
</div><br>
% endif

View file

@ -171,6 +171,38 @@ class PmsConnect(object):
return request return request
"""
Return list of libraries on server.
Optional parameters: output_format { dict, json }
Output: array
"""
def get_libraries_list(self, output_format=''):
uri = '/library/sections'
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
return request
"""
Return list of items in library on server.
Optional parameters: output_format { dict, json }
Output: array
"""
def get_library_list(self, section_key='', list_type='all', count='0', sort_type='', output_format=''):
uri = '/library/sections/' + section_key + '/' + list_type +'?X-Plex-Container-Start=0&X-Plex-Container-Size=' + count + sort_type
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
return request
""" """
Return sync item details. Return sync item details.
@ -984,6 +1016,151 @@ class PmsConnect(object):
logger.debug(u"Server preferences queried but no parameter received.") logger.debug(u"Server preferences queried but no parameter received.")
return None return None
"""
Return processed and validated server libraries list.
Output: array
"""
def get_server_children(self):
libraries_data = self.get_libraries_list(output_format='xml')
try:
xml_head = libraries_data.getElementsByTagName('MediaContainer')
except:
logger.warn("Unable to parse XML for get_libraries_list.")
return []
libraries_list = []
for a in xml_head:
if a.getAttribute('size'):
if a.getAttribute('size') == '0':
logger.debug(u"No libraries data.")
libraries_list = {'libraries_count': '0',
'libraries_list': []
}
return libraries_list
if a.getElementsByTagName('Directory'):
result_data = a.getElementsByTagName('Directory')
for result in result_data:
libraries_output = {'key': helpers.get_xml_attr(result, 'key'),
'type': helpers.get_xml_attr(result, 'type'),
'title': helpers.get_xml_attr(result, 'title'),
'thumb': helpers.get_xml_attr(result, 'thumb')
}
libraries_list.append(libraries_output)
output = {'libraries_count': helpers.get_xml_attr(xml_head[0], 'size'),
'title': helpers.get_xml_attr(xml_head[0], 'title1'),
'libraries_list': libraries_list
}
return output
"""
Return processed and validated server library items list.
Parameters required: library_type { movie, show, episode, artist }
section_key { unique library key }
Output: array
"""
def get_library_children(self, library_type='', section_key='', list_type='all', sort_type = ''):
# Currently only grab the library with 1 items so 'size' is not 0
count = '1'
if library_type == 'movie':
sort_type = '&type=1'
elif library_type == 'show':
sort_type = '&type=2'
elif library_type == 'episode':
sort_type = '&type=4'
elif library_type == 'album':
list_type = 'albums'
library_data = self.get_library_list(section_key, list_type, count, sort_type, output_format='xml')
try:
xml_head = library_data.getElementsByTagName('MediaContainer')
except:
logger.warn("Unable to parse XML for get_library_children.")
return []
library_list = []
for a in xml_head:
if a.getAttribute('size'):
if a.getAttribute('size') == '0':
logger.debug(u"No library data.")
library_list = {'library_count': '0',
'library_list': []
}
return library_list
if a.getElementsByTagName('Directory'):
result_data = a.getElementsByTagName('Directory')
for result in result_data:
library_output = {'key': helpers.get_xml_attr(result, 'key'),
'type': helpers.get_xml_attr(result, 'type'),
'title': helpers.get_xml_attr(result, 'title'),
'thumb': helpers.get_xml_attr(result, 'thumb')
}
library_list.append(library_output)
output = {'library_count': helpers.get_xml_attr(xml_head[0], 'totalSize'),
'count_type': helpers.get_xml_attr(xml_head[0], 'title2'),
'library_list': library_list
}
return output
"""
Return processed and validated server statistics.
Output: array
"""
def get_server_stats(self):
server_info = self.get_servers_info()
server_libraries = self.get_server_children()
server_stats = []
if server_libraries['libraries_count'] != '0':
libraries_list = server_libraries['libraries_list']
for library in libraries_list:
library_type = library['type']
section_key = library['key']
library_list = self.get_library_children(library_type, section_key)
if library_list['library_count'] != '0':
library_stats = {'title': library['title'],
'thumb': library['thumb'],
'count': library_list['library_count'],
'count_type': library_list['count_type']
}
if library_type == 'show':
episode_list = self.get_library_children(library_type='episode', section_key=section_key)
episode_stats = {'episode_count': episode_list['library_count'],
'episode_count_type': 'All Episodes'
}
library_stats.update(episode_stats)
if library_type == 'artist':
album_list = self.get_library_children(library_type='album', section_key=section_key)
album_stats = {'album_count': album_list['library_count'],
'album_count_type': 'All Albums'
}
library_stats.update(album_stats)
server_stats.append({'type': library_type,
'rows': library_stats})
return server_stats
""" """
Return image data as array. Return image data as array.
Array contains the image content type and image binary Array contains the image content type and image binary

View file

@ -67,7 +67,8 @@ class WebInterface(object):
config = { config = {
"home_stats_length": plexpy.CONFIG.HOME_STATS_LENGTH, "home_stats_length": plexpy.CONFIG.HOME_STATS_LENGTH,
"home_stats_type": plexpy.CONFIG.HOME_STATS_TYPE, "home_stats_type": plexpy.CONFIG.HOME_STATS_TYPE,
"home_stats_count": plexpy.CONFIG.HOME_STATS_COUNT "home_stats_count": plexpy.CONFIG.HOME_STATS_COUNT,
"pms_identifier": plexpy.CONFIG.PMS_IDENTIFIER,
} }
return serve_template(templatename="index.html", title="Home", config=config) return serve_template(templatename="index.html", title="Home", config=config)
@ -126,6 +127,13 @@ class WebInterface(object):
return serve_template(templatename="home_stats.html", title="Stats", data=stats_data) return serve_template(templatename="home_stats.html", title="Stats", data=stats_data)
@cherrypy.expose
def server_stats(self, **kwargs):
pms_connect = pmsconnect.PmsConnect()
stats_data = pms_connect.get_server_stats()
return serve_template(templatename="server_stats.html", title="Server Stats", data=stats_data)
@cherrypy.expose @cherrypy.expose
def history(self): def history(self):
return serve_template(templatename="history.html", title="History") return serve_template(templatename="history.html", title="History")