diff --git a/README.md b/README.md index f3469fd7..1212e9ab 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ A python based web front-end for plexWatch. * full user list with general information and comparison stats -* individual user information **PARTIALLY IMPLEMENTED** +* individual user information - username and gravatar (if available) - daily, weekly, monthly, all time stats for play count and duration length - individual platform stats for each user diff --git a/data/interfaces/default/css/plexwatch.css b/data/interfaces/default/css/plexwatch.css index 6a676997..23a4502b 100644 --- a/data/interfaces/default/css/plexwatch.css +++ b/data/interfaces/default/css/plexwatch.css @@ -7635,6 +7635,7 @@ button.close { } .user-info-poster-face img { bottom: 0; + margin-right: 15px; overflow: hidden; float: left; background-color: #323232; @@ -7651,12 +7652,11 @@ button.close { color: #fff; position: relative; top: 27px; - left: 15px; } .user-info-nav { position: relative; top: 15px; - left: 3px; + left: -5px; } .user-info-nav > .active > a, .nav-tabs > .active > a:hover, .nav-tabs > .active > a:focus { color: #F9AA03; diff --git a/data/interfaces/default/js/script.js b/data/interfaces/default/js/script.js index e156acb9..c031448f 100644 --- a/data/interfaces/default/js/script.js +++ b/data/interfaces/default/js/script.js @@ -210,13 +210,17 @@ function getPlatformImagePath(platformName) { } function isPrivateIP(ip_address) { - var parts = ip_address.split('.'); - if (parts[0] === '10' || - (parts[0] === '172' && (parseInt(parts[1], 10) >= 16 && parseInt(parts[1], 10) <= 31)) || - (parts[0] === '192' && parts[1] === '168')) { + if (ip_address.indexOf(".") > -1) { + var parts = ip_address.split('.'); + if (parts[0] === '10' || + (parts[0] === '172' && (parseInt(parts[1], 10) >= 16 && parseInt(parts[1], 10) <= 31)) || + (parts[0] === '192' && parts[1] === '168')) { + return true; + } + return false; + } else { return true; } - return false; } function humanTime(seconds) { diff --git a/data/interfaces/default/js/tables/user_ips.js b/data/interfaces/default/js/tables/user_ips.js index 99314278..96ff785b 100644 --- a/data/interfaces/default/js/tables/user_ips.js +++ b/data/interfaces/default/js/tables/user_ips.js @@ -32,7 +32,11 @@ user_ip_table_options = { "className": "modal-control", "createdCell": function (td, cellData, rowData, row, col) { if (isPrivateIP(cellData)) { - $(td).html(cellData); + if (cellData != '') { + $(td).html(cellData); + } else { + $(td).html('n/a'); + } } else { $(td).html(' ' + cellData +''); } diff --git a/data/interfaces/default/js/tables/users.js b/data/interfaces/default/js/tables/users.js index cbde3b2e..2fade934 100644 --- a/data/interfaces/default/js/tables/users.js +++ b/data/interfaces/default/js/tables/users.js @@ -25,13 +25,13 @@ users_list_table_options = { "columnDefs": [ { "targets": [0], - "data": null, + "data": "thumb", "createdCell": function (td, cellData, rowData, row, col) { - //if (rowData['user_thumb'] === '') { + if (cellData === '') { $(td).html('User Logo'); - //} else { - // $(td).html('User Logo'); - //} + } else { + $(td).html('User Logo'); + } }, "orderable": false, "className": "users-poster-face", diff --git a/data/interfaces/default/user.html b/data/interfaces/default/user.html index 52fdf409..33840fc6 100644 --- a/data/interfaces/default/user.html +++ b/data/interfaces/default/user.html @@ -13,7 +13,7 @@
@@ -58,7 +58,7 @@
-
+
Loading data...

@@ -73,8 +73,8 @@

Recently watched

-
-
+
+
Loading data...

@@ -88,7 +88,7 @@
-

Public IP Addresses for +

IP Addresses for ${user}

@@ -190,6 +190,7 @@ diff --git a/data/interfaces/default/user_platform_stats.html b/data/interfaces/default/user_platform_stats.html new file mode 100644 index 00000000..ae6ce780 --- /dev/null +++ b/data/interfaces/default/user_platform_stats.html @@ -0,0 +1,22 @@ +% if platform_stats != None: + % for a in platform_stats: +
    +
    +
  • + +
    + ${a['platform_name']} +
    +
    +

    ${a['total_plays']}

    plays

    +
    +
  • +
    +
+ + % endfor +% else: +
There was an error loading your PlexWatch data. Please check your settings.

+% endif \ No newline at end of file diff --git a/data/interfaces/default/user_recently_watched.html b/data/interfaces/default/user_recently_watched.html index a36e2504..23884fdb 100644 --- a/data/interfaces/default/user_recently_watched.html +++ b/data/interfaces/default/user_recently_watched.html @@ -32,5 +32,5 @@
% else: -
There was an error retrieving some data. Please check your settings.

+
There was an error loading your PlexWatch data. Please check your settings.

% endif \ No newline at end of file diff --git a/data/interfaces/default/user_watch_time_stats.html b/data/interfaces/default/user_watch_time_stats.html index e95471e7..86ec886a 100644 --- a/data/interfaces/default/user_watch_time_stats.html +++ b/data/interfaces/default/user_watch_time_stats.html @@ -9,7 +9,7 @@ % elif a['query_days'] == 1:

Last 24 hours

% else: -

Last ${a['query_days']} day(s)

+

Last ${a['query_days']} days

% endif

${a['total_plays']}

plays

@@ -21,4 +21,6 @@ % endfor +% else: +
There was an error loading your PlexWatch data. Please check your settings.

% endif \ No newline at end of file diff --git a/plexpy/datatables.py b/plexpy/datatables.py index 1df9df89..a8812df5 100644 --- a/plexpy/datatables.py +++ b/plexpy/datatables.py @@ -76,8 +76,8 @@ class DataTables(object): order, custom_where) # logger.debug(u"Query string: %s" % query) - filtered = self.ssp_db.select(query) + if search_value == '': totalcount = len(filtered) else: diff --git a/plexpy/plexwatch.py b/plexpy/plexwatch.py index 8d53794a..e4eb0e22 100644 --- a/plexpy/plexwatch.py +++ b/plexpy/plexwatch.py @@ -72,27 +72,35 @@ class PlexWatch(object): 'time', 'ip_address', 'COUNT(title) as plays'] - - query = data_tables.ssp_query(table_name=self.get_user_table_name(), - columns=columns, - start=start, - length=length, - order_column=int(order_column), - order_dir=order_dir, - search_value=search_value, - search_regex=search_regex, - custom_where='', - group_by='user', - kwargs=kwargs) + try: + query = data_tables.ssp_query(table_name=self.get_user_table_name(), + columns=columns, + start=start, + length=length, + order_column=int(order_column), + order_dir=order_dir, + search_value=search_value, + search_regex=search_regex, + custom_where='', + group_by='user', + kwargs=kwargs) + except: + logger.warn("Unable to open PlexWatch database.") + return {'recordsFiltered': 0, + 'recordsTotal': 0, + 'data': 'null'}, users = query['result'] rows = [] for item in users: + thumb = self.get_user_gravatar_image(item['user']) + row = {"plays": item['plays'], "time": item['time'], "user": item["user"], - "ip_address": item["ip_address"] + "ip_address": item["ip_address"], + "thumb": thumb['user_thumb'] } rows.append(row) @@ -136,17 +144,23 @@ class PlexWatch(object): 'orig_title as last_watched' ] - query = data_tables.ssp_query(table_name=self.get_user_table_name(), - columns=columns, - start=start, - length=length, - order_column=int(order_column), - order_dir=order_dir, - search_value=search_value, - search_regex=search_regex, - custom_where=custom_where, - group_by='ip_address', - kwargs=kwargs) + try: + query = data_tables.ssp_query(table_name=self.get_user_table_name(), + columns=columns, + start=start, + length=length, + order_column=int(order_column), + order_dir=order_dir, + search_value=search_value, + search_regex=search_regex, + custom_where=custom_where, + group_by='ip_address', + kwargs=kwargs) + except: + logger.warn("Unable to open PlexWatch database.") + return {'recordsFiltered': 0, + 'recordsTotal': 0, + 'data': 'null'}, results = query['result'] @@ -207,18 +221,23 @@ class PlexWatch(object): julianday(datetime(time, "unixepoch", "localtime"))) * 86400) - \ (case when paused_counter is null then 0 else paused_counter end) as duration' ] - - query = data_tables.ssp_query(table_name=self.get_history_table_name(), - columns=columns, - start=start, - length=length, - order_column=int(order_column), - order_dir=order_dir, - search_value=search_value, - search_regex=search_regex, - custom_where=custom_where, - group_by='', - kwargs=kwargs) + try: + query = data_tables.ssp_query(table_name=self.get_history_table_name(), + columns=columns, + start=start, + length=length, + order_column=int(order_column), + order_dir=order_dir, + search_value=search_value, + search_regex=search_regex, + custom_where=custom_where, + group_by='', + kwargs=kwargs) + except: + logger.warn("Unable to open PlexWatch database.") + return {'recordsFiltered': 0, + 'recordsTotal': 0, + 'data': 'null'}, history = query['result'] @@ -380,14 +399,18 @@ class PlexWatch(object): if not limit.isdigit(): limit = '10' - if user: - query = 'SELECT time, user, xml FROM %s WHERE user = "%s" ORDER BY time DESC LIMIT %s' % \ - (self.get_user_table_name(), user, limit) - xml = myDB.select(query) - else: - query = 'SELECT time, user, xml FROM %s ORDER BY time DESC LIMIT %s' % \ - (self.get_user_table_name(), limit) - xml = myDB.select(query) + try: + if user: + query = 'SELECT time, user, xml FROM %s WHERE user = "%s" ORDER BY time DESC LIMIT %s' % \ + (self.get_user_table_name(), user, limit) + xml = myDB.select(query) + else: + query = 'SELECT time, user, xml FROM %s ORDER BY time DESC LIMIT %s' % \ + (self.get_user_table_name(), limit) + xml = myDB.select(query) + except: + logger.warn("Unable to open PlexWatch database.") + return None for row in xml: xml_data = helpers.latinToAscii(row[2]) @@ -435,10 +458,14 @@ class PlexWatch(object): else: where = 'WHERE user = "%s"' % user - query = 'SELECT (SUM(stopped - time) - SUM(CASE WHEN paused_counter is null THEN 0 ELSE paused_counter END)) as total_time, ' \ - 'COUNT(id) AS total_plays ' \ - 'FROM %s %s' % (self.get_user_table_name(), where) - result = myDB.select(query) + try: + query = 'SELECT (SUM(stopped - time) - SUM(CASE WHEN paused_counter is null THEN 0 ELSE paused_counter END)) as total_time, ' \ + 'COUNT(id) AS total_plays ' \ + 'FROM %s %s' % (self.get_user_table_name(), where) + result = myDB.select(query) + except: + logger.warn("Unable to open PlexWatch database.") + return None for item in result: if item[0]: @@ -455,4 +482,84 @@ class PlexWatch(object): user_watch_time_stats.append(row) - return user_watch_time_stats \ No newline at end of file + return user_watch_time_stats + + def get_user_platform_stats(self, user=None): + myDB = db.DBConnection() + + platform_stats = [] + result_id = 0 + + try: + query = 'SELECT platform, COUNT(platform) as platform_count, xml ' \ + 'FROM %s ' \ + 'WHERE user = "%s" ' \ + 'GROUP BY platform ' \ + 'ORDER BY platform_count DESC' % (self.get_user_table_name(), user) + result = myDB.select(query) + except: + logger.warn("Unable to open PlexWatch database.") + return None + + for item in result: + xml_data = helpers.latinToAscii(item[2]) + + try: + xml_parse = minidom.parseString(xml_data) + except: + logger.warn("Error parsing XML for Plex stream data.") + return None + + xml_head = xml_parse.getElementsByTagName('Player') + if not xml_head: + logger.warn("Error parsing XML for Plex stream data.") + return None + + for a in xml_head: + platform_type = self.get_xml_attr(a, 'platform') + + row = {'platform_name': item[0], + 'platform_type': platform_type, + 'total_plays': item[1], + 'result_id': result_id + } + platform_stats.append(row) + result_id += 1 + + return platform_stats + + def get_user_gravatar_image(self, user=None): + myDB = db.DBConnection() + user_info = None + + try: + query = 'SELECT xml ' \ + 'FROM %s ' \ + 'WHERE user = "%s" ' \ + 'ORDER BY id DESC LIMIT 1' % (self.get_user_table_name(), user) + result = myDB.select_single(query) + except: + logger.warn("Unable to open PlexWatch database.") + return None + + xml_data = helpers.latinToAscii(result) + + try: + xml_parse = minidom.parseString(xml_data) + except: + logger.warn("Error parsing XML for Plexwatch Database.") + return None + + xml_head = xml_parse.getElementsByTagName('User') + if not xml_head: + logger.warn("Error parsing XML for Plexwatch Database.") + return None + + for a in xml_head: + user_id = self.get_xml_attr(a, 'id') + user_thumb = self.get_xml_attr(a, 'thumb') + + user_info = {'user_id': user_id, + 'user_thumb': user_thumb} + + return user_info \ No newline at end of file diff --git a/plexpy/webserve.py b/plexpy/webserve.py index 3d622d58..23b331d6 100644 --- a/plexpy/webserve.py +++ b/plexpy/webserve.py @@ -434,7 +434,6 @@ class WebInterface(object): logger.warn(msg) return msg - @cherrypy.expose def get_pms_token(self): @@ -447,7 +446,6 @@ class WebInterface(object): logger.warn('Unable to retrieve Plex.tv token.') return False - @cherrypy.expose def get_pms_sessions_json(self, **kwargs): @@ -543,7 +541,7 @@ class WebInterface(object): if result: return serve_template(templatename="user_recently_watched.html", recently_watched=result, title="Recently Watched") else: - return serve_template(templatename="user_recently_watched.html", recently_watched='', title="Recently Watched") + return serve_template(templatename="user_recently_watched.html", recently_watched=None, title="Recently Watched") logger.warn('Unable to retrieve data.') @cherrypy.expose @@ -555,7 +553,19 @@ class WebInterface(object): if result: return serve_template(templatename="user_watch_time_stats.html", watch_stats=result, title="Watch Stats") else: - return serve_template(templatename="user_watch_time_stats.html", watch_stats='', title="Watch Stats") + return serve_template(templatename="user_watch_time_stats.html", watch_stats=None, title="Watch Stats") + logger.warn('Unable to retrieve data.') + + @cherrypy.expose + def get_user_platform_stats(self, user=None, **kwargs): + + plex_watch = plexwatch.PlexWatch() + result = plex_watch.get_user_platform_stats(user) + + if result: + return serve_template(templatename="user_platform_stats.html", platform_stats=result, title="Platform Stats") + else: + return serve_template(templatename="user_platform_stats.html", platform_stats=None, title="Platform Stats") logger.warn('Unable to retrieve data.') @cherrypy.expose @@ -637,6 +647,30 @@ class WebInterface(object): plex_watch = plexwatch.PlexWatch() result = plex_watch.get_user_watch_time_stats(user) + if result: + cherrypy.response.headers['Content-type'] = 'application/json' + return json.dumps(result) + else: + logger.warn('Unable to retrieve data.') + + @cherrypy.expose + def get_platform_stats(self, user=None, **kwargs): + + plex_watch = plexwatch.PlexWatch() + result = plex_watch.get_user_platform_stats(user) + + if result: + cherrypy.response.headers['Content-type'] = 'application/json' + return json.dumps(result) + else: + logger.warn('Unable to retrieve data.') + + @cherrypy.expose + def get_user_gravatar_image(self, user=None, **kwargs): + + plex_watch = plexwatch.PlexWatch() + result = plex_watch.get_user_gravatar_image(user) + if result: cherrypy.response.headers['Content-type'] = 'application/json' return json.dumps(result)