From 5fedac691d6a3317e75a23d057679dea9a3022a3 Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Sat, 2 Jan 2016 16:02:22 -0800 Subject: [PATCH] Add individual library page --- data/interfaces/default/css/plexpy.css | 35 +- data/interfaces/default/edit_library.html | 171 +++++++ data/interfaces/default/info.html | 38 +- .../interfaces/default/js/tables/libraries.js | 15 +- data/interfaces/default/libraries.html | 2 +- data/interfaces/default/library.html | 365 ++++++++++++++ data/interfaces/default/library_stats.html | 6 +- .../default/library_user_stats.html | 43 ++ plexpy/__init__.py | 2 +- plexpy/datafactory.py | 17 +- plexpy/libraries.py | 447 ++++-------------- plexpy/pmsconnect.py | 5 +- plexpy/webserve.py | 83 +++- 13 files changed, 836 insertions(+), 393 deletions(-) create mode 100644 data/interfaces/default/edit_library.html create mode 100644 data/interfaces/default/library.html create mode 100644 data/interfaces/default/library_user_stats.html diff --git a/data/interfaces/default/css/plexpy.css b/data/interfaces/default/css/plexpy.css index 37032b0a..85553887 100644 --- a/data/interfaces/default/css/plexpy.css +++ b/data/interfaces/default/css/plexpy.css @@ -504,9 +504,9 @@ textarea.form-control:focus { background-size: contain; height: 40px; width: 40px; - -webkit-box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1); + /*-webkit-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);*/ } a .poster-face:hover, a .cover-face:hover, @@ -1664,7 +1664,6 @@ a:hover .item-children-poster { } .user-player-instance-box { float: left; - width: 75px; border-radius: 3px; -webkit-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); @@ -1686,6 +1685,7 @@ a:hover .item-children-poster { font-weight: normal; width: 140px; margin-left: 10px; + margin-bottom: 10px; } .user-player-instance-playcount h3 { font-size: 30px; @@ -1705,6 +1705,35 @@ a:hover .item-children-poster { top: 15px; left: 0px; } +.library-info-poster-face { + float: left; + margin-top: 15px; + margin-right: 15px; + background-size: contain; + height: 80px; + width: 80px; + /*-webkit-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);*/ +} +.library-user-instance-box { + float: left; + -webkit-border-radius: 50%; + -moz-border-radius: 50%; + border-radius: 50%; + -webkit-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); + background-size: contain; + position: relative; + height: 80px; + width: 80px; +} +.library-user-instance-box:hover { + -webkit-box-shadow: inset 0 0 0 2px #e9a049; + -moz-box-shadow: inset 0 0 0 2px #e9a049; + box-shadow: inset 0 0 0 2px #e9a049; +} .home-platforms { } .home-platforms ul { diff --git a/data/interfaces/default/edit_library.html b/data/interfaces/default/edit_library.html new file mode 100644 index 00000000..056de42c --- /dev/null +++ b/data/interfaces/default/edit_library.html @@ -0,0 +1,171 @@ +<%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: edit_library.html +Version: 0.1 +Variable names: data [list] + +data :: Usable parameters + +== Global keys == +section_id Returns the library id of the library. +section_name Returns the name of the library. +section_type Returns the type of the library. +library_thumb Returns the thumbnail for the library. +count Returns the item count for the library. +parent_count Returns the parent item count for the library. +child_count Returns the child item count for the library. +do_notify Returns bool value for whether to send notifications for the library. +keep_history Returns bool value for whether to keep history for the library. + +DOCUMENTATION :: END + + +<%! + from plexpy import helpers +%> + +% if data != None: + + + + +% endif \ No newline at end of file diff --git a/data/interfaces/default/info.html b/data/interfaces/default/info.html index b6c3a51e..fa10faeb 100644 --- a/data/interfaces/default/info.html +++ b/data/interfaces/default/info.html @@ -53,32 +53,30 @@ DOCUMENTATION :: END
- % if data['media_type'] != 'library':
- % elif data['media_type'] != 'library': + % else:
@@ -138,9 +135,7 @@ DOCUMENTATION :: END
- % endif
- % if data['media_type'] != 'library':
% if data['media_type'] == 'movie' or data['media_type'] == 'show' or data['media_type'] == 'season': @@ -292,8 +287,8 @@ DOCUMENTATION :: END
% endif - % endif
+ % if data['media_type'] == 'library' and config['update_library_ids'] == 1:
Updating library ids in the database. This could take a few minutes depending on the size of your database. @@ -374,6 +369,7 @@ DOCUMENTATION :: END % if data: + % if data['media_type'] == 'library': % endif -% if data['media_type'] != 'library': - -% endif -% if data['media_type'] != 'library' and data['rating']: +% if data['rating']: % endif + + + + + + + + +% else: +
+
+
+
+

Error retrieving library information. Please see the logs for more details.

+
+
+
+% endif \ No newline at end of file diff --git a/data/interfaces/default/library_stats.html b/data/interfaces/default/library_stats.html index 2eae932a..a62f52bf 100644 --- a/data/interfaces/default/library_stats.html +++ b/data/interfaces/default/library_stats.html @@ -30,13 +30,9 @@ DOCUMENTATION :: END
  • % if library['section_type'] == 'movie':
    diff --git a/data/interfaces/default/library_user_stats.html b/data/interfaces/default/library_user_stats.html new file mode 100644 index 00000000..4cee9a14 --- /dev/null +++ b/data/interfaces/default/library_user_stats.html @@ -0,0 +1,43 @@ +<%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: library_user_stats.html +Version: 0.1 +Variable names: data [array] + +data[array_index] :: Usable parameters + +== Global keys == +user Returns the name of the user. +user_id Returns the user id of the user. +thumb Returns the avatar of the user. +total_plays Returns the play count for the user. + +DOCUMENTATION :: END + + +% if data != None: +% for a in data: + +% endfor +% else: +
    Unable to retrieve data from database. Please check your settings. +

    +% endif \ No newline at end of file diff --git a/plexpy/__init__.py b/plexpy/__init__.py index eafe4820..59d05688 100644 --- a/plexpy/__init__.py +++ b/plexpy/__init__.py @@ -440,7 +440,7 @@ def dbcheck(): c_db.execute( 'CREATE TABLE IF NOT EXISTS library_sections (id INTEGER PRIMARY KEY AUTOINCREMENT, ' 'server_id TEXT, section_id INTEGER UNIQUE, section_name TEXT, section_type TEXT, ' - 'thumb TEXT, custom_thumb_url TEXT, count INTEGER, parent_count INTEGER, child_count INTEGER, ' + 'thumb TEXT, custom_thumb_url TEXT, art TEXT, count INTEGER, parent_count INTEGER, child_count INTEGER, ' 'do_notify INTEGER DEFAULT 1, keep_history INTEGER DEFAULT 1)' ) diff --git a/plexpy/datafactory.py b/plexpy/datafactory.py index ee817d51..298b33dd 100644 --- a/plexpy/datafactory.py +++ b/plexpy/datafactory.py @@ -725,7 +725,7 @@ class DataFactory(object): return stream_output - def get_recently_watched(self, user=None, user_id=None, limit='10'): + def get_recently_watched(self, user=None, user_id=None, library_id=None, limit='10'): monitor_db = database.MonitorDatabase() recently_watched = [] @@ -755,6 +755,17 @@ class DataFactory(object): ' ELSE session_history.rating_key END) ' \ 'ORDER BY started DESC LIMIT ?' result = monitor_db.select(query, args=[user, limit]) + elif library_id: + query = 'SELECT session_history.id, session_history.media_type, session_history.rating_key, session_history.parent_rating_key, ' \ + 'title, parent_title, grandparent_title, thumb, parent_thumb, grandparent_thumb, media_index, parent_media_index, ' \ + 'year, started, user ' \ + 'FROM session_history_metadata ' \ + 'JOIN session_history ON session_history_metadata.id = session_history.id ' \ + 'WHERE library_id = ? ' \ + 'GROUP BY (CASE WHEN session_history.media_type = "track" THEN session_history.parent_rating_key ' \ + ' ELSE session_history.rating_key END) ' \ + 'ORDER BY started DESC LIMIT ?' + result = monitor_db.select(query, args=[library_id, limit]) else: query = 'SELECT session_history.id, session_history.media_type, session_history.rating_key, session_history.parent_rating_key, ' \ 'title, parent_title, grandparent_title, thumb, parent_thumb, grandparent_thumb, media_index, parent_media_index, ' \ @@ -1177,7 +1188,9 @@ class DataFactory(object): try: query = 'SELECT SUM(CASE WHEN stopped > 0 THEN (stopped - started) ELSE 0 END) - ' \ 'SUM(CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) AS total_duration ' \ - 'FROM session_history %s ' % where + 'FROM session_history ' \ + 'JOIN session_history_metadata ON session_history_metadata.id = session_history.id ' \ + '%s ' % where result = monitor_db.select(query) except: logger.warn("Unable to execute database query for get_total_duration.") diff --git a/plexpy/libraries.py b/plexpy/libraries.py index 8df0ec0e..13202724 100644 --- a/plexpy/libraries.py +++ b/plexpy/libraries.py @@ -30,8 +30,10 @@ class Libraries(object): 'library_sections.count as count', 'library_sections.parent_count', 'library_sections.child_count', - '(CASE WHEN library_sections.custom_thumb_url IS NULL THEN library_sections.thumb ELSE ' \ - 'custom_thumb_url END) AS library_thumb', + 'library_sections.thumb AS library_thumb', + '(CASE WHEN library_sections.custom_thumb_url == library_sections.thumb \ + THEN NULL ELSE custom_thumb_url END) AS custom_thumb', + 'library_sections.art', 'COUNT(session_history.id) as plays', 'MAX(session_history.started) as last_accessed', 'session_history_metadata.full_title as last_watched', @@ -91,6 +93,8 @@ class Libraries(object): 'count': item['count'], 'parent_count': item['parent_count'], 'library_thumb': item['library_thumb'], + 'custom_thumb': item['custom_thumb'], + 'library_art': item['art'], 'child_count': item['child_count'], 'do_notify': helpers.checked(item['do_notify']), 'keep_history': helpers.checked(item['keep_history']) @@ -106,93 +110,6 @@ class Libraries(object): return dict - def get_user_unique_ips(self, kwargs=None, custom_where=None): - data_tables = datatables.DataTables() - - # Change custom_where column name due to ambiguous column name after JOIN - custom_where[0][0] = 'custom_user_id' if custom_where[0][0] == 'user_id' else custom_where[0][0] - - columns = ['session_history.id', - 'session_history.started as last_seen', - 'session_history.ip_address as ip_address', - 'COUNT(session_history.id) as play_count', - 'session_history.platform as platform', - 'session_history.player as player', - 'session_history_metadata.full_title as last_watched', - 'session_history_metadata.thumb', - 'session_history_metadata.parent_thumb', - 'session_history_metadata.grandparent_thumb', - 'session_history_metadata.media_type', - 'session_history.rating_key as rating_key', - 'session_history_media_info.video_decision', - 'session_history.user as user', - 'session_history.user_id as custom_user_id', - '(case when users.friendly_name is null then users.username else \ - users.friendly_name end) as friendly_name' - ] - - try: - query = data_tables.ssp_query(table_name='session_history', - columns=columns, - custom_where=custom_where, - group_by=['ip_address'], - join_types=['JOIN', - 'JOIN', - 'JOIN'], - join_tables=['users', - 'session_history_metadata', - 'session_history_media_info'], - join_evals=[['session_history.user_id', 'users.user_id'], - ['session_history.id', 'session_history_metadata.id'], - ['session_history.id', 'session_history_media_info.id']], - kwargs=kwargs) - except: - logger.warn("Unable to execute database query.") - return {'recordsFiltered': 0, - 'recordsTotal': 0, - 'draw': 0, - 'data': 'null', - 'error': 'Unable to execute database query.'} - - results = query['result'] - - rows = [] - for item in results: - if item["media_type"] == 'episode' and item["parent_thumb"]: - thumb = item["parent_thumb"] - elif item["media_type"] == 'episode': - thumb = item["grandparent_thumb"] - else: - thumb = item["thumb"] - - # Rename Mystery platform names - platform = common.PLATFORM_NAME_OVERRIDES.get(item["platform"], item["platform"]) - - row = {"id": item['id'], - "last_seen": item['last_seen'], - "ip_address": item['ip_address'], - "play_count": item['play_count'], - "platform": platform, - "player": item['player'], - "last_watched": item['last_watched'], - "thumb": thumb, - "media_type": item['media_type'], - "rating_key": item['rating_key'], - "video_decision": item['video_decision'], - "friendly_name": item['friendly_name'] - } - - rows.append(row) - - dict = {'recordsFiltered': query['filteredCount'], - 'recordsTotal': query['totalCount'], - 'data': rows, - 'draw': query['draw'] - } - - return dict - - # TODO: The getter and setter for this needs to become a config getter/setter for more than just friendlyname def set_library_config(self, section_id=None, do_notify=1, keep_history=1, custom_thumb=''): if section_id: monitor_db = database.MonitorDatabase() @@ -206,255 +123,110 @@ class Libraries(object): except: logger.warn("Unable to execute database query for set_user_friendly_name.") - def set_user_profile_url(self, user=None, user_id=None, profile_url=None): - if user_id: - if profile_url.strip() == '': - profile_url = None - - monitor_db = database.MonitorDatabase() - - control_value_dict = {"user_id": user_id} - new_value_dict = {"custom_avatar_url": profile_url} - try: - monitor_db.upsert('users', new_value_dict, control_value_dict) - except Exception, e: - logger.debug(u"Uncaught exception %s" % e) - if user: - if profile_url.strip() == '': - profile_url = None - - monitor_db = database.MonitorDatabase() - - control_value_dict = {"username": user} - new_value_dict = {"custom_avatar_url": profile_url} - try: - monitor_db.upsert('users', new_value_dict, control_value_dict) - except Exception, e: - logger.debug(u"Uncaught exception %s" % e) - - def get_user_friendly_name(self, user=None, user_id=None): - if user_id: - monitor_db = database.MonitorDatabase() - query = 'select username, ' \ - '(CASE WHEN friendly_name IS NULL THEN username ELSE friendly_name END) as friendly_name,' \ - 'do_notify, keep_history, custom_avatar_url as thumb ' \ - 'FROM users WHERE user_id = ?' - result = monitor_db.select(query, args=[user_id]) - if result: - user_detail = {'user_id': user_id, - 'user': result[0]['username'], - 'friendly_name': result[0]['friendly_name'], - 'thumb': result[0]['thumb'], - 'do_notify': helpers.checked(result[0]['do_notify']), - 'keep_history': helpers.checked(result[0]['keep_history']) - } - return user_detail - else: - user_detail = {'user_id': user_id, - 'user': '', - 'friendly_name': '', - 'do_notify': '', - 'thumb': '', - 'keep_history': ''} - return user_detail - elif user: - monitor_db = database.MonitorDatabase() - query = 'select user_id, ' \ - '(CASE WHEN friendly_name IS NULL THEN username ELSE friendly_name END) as friendly_name,' \ - 'do_notify, keep_history, custom_avatar_url as thumb ' \ - 'FROM users WHERE username = ?' - result = monitor_db.select(query, args=[user]) - if result: - user_detail = {'user_id': result[0]['user_id'], - 'user': user, - 'friendly_name': result[0]['friendly_name'], - 'thumb': result[0]['thumb'], - 'do_notify': helpers.checked(result[0]['do_notify']), - 'keep_history': helpers.checked(result[0]['keep_history'])} - return user_detail - else: - user_detail = {'user_id': None, - 'user': user, - 'friendly_name': '', - 'do_notify': '', - 'thumb': '', - 'keep_history': ''} - return user_detail - - return None - - def get_user_id(self, user=None): - if user: - try: - monitor_db = database.MonitorDatabase() - query = 'select user_id FROM users WHERE username = ?' - result = monitor_db.select_single(query, args=[user]) - if result: - return result - else: - return None - except: - return None - - return None - - def get_user_details(self, user=None, user_id=None): - from plexpy import plextv + def get_library_details(self, section_id=None): + from plexpy import pmsconnect monitor_db = database.MonitorDatabase() - if user: - query = 'SELECT user_id, username, friendly_name, email, ' \ - 'custom_avatar_url as thumb, is_home_user, is_allow_sync, is_restricted, do_notify ' \ - 'FROM users ' \ - 'WHERE username = ? ' \ - 'UNION ALL ' \ - 'SELECT null, user, null, null, null, null, null, null, null ' \ - 'FROM session_history ' \ - 'WHERE user = ? ' \ - 'GROUP BY user ' \ - 'LIMIT 1' - result = monitor_db.select(query, args=[user, user]) - elif user_id: - query = 'SELECT user_id, username, friendly_name, email, ' \ - 'custom_avatar_url as thumb, is_home_user, is_allow_sync, is_restricted, do_notify ' \ - 'FROM users ' \ - 'WHERE user_id = ? ' \ - 'UNION ALL ' \ - 'SELECT user_id, user, null, null, null, null, null, null, null ' \ - 'FROM session_history ' \ - 'WHERE user_id = ? ' \ - 'GROUP BY user ' \ - 'LIMIT 1' - result = monitor_db.select(query, args=[user_id, user_id]) + if section_id: + query = 'SELECT section_id, section_name, section_type, count, parent_count, child_count, ' \ + 'thumb AS library_thumb, (CASE WHEN library_sections.custom_thumb_url == library_sections.thumb ' \ + ' THEN NULL ELSE custom_thumb_url END) AS custom_thumb, art, do_notify, keep_history ' \ + 'FROM library_sections ' \ + 'WHERE section_id = ? ' + result = monitor_db.select(query, args=[section_id]) else: result = None if result: - user_details = {} + library_details = {} for item in result: - if not item['friendly_name']: - friendly_name = item['username'] - else: - friendly_name = item['friendly_name'] - if not item['thumb'] or item['thumb'] == '': - user_thumb = common.DEFAULT_USER_THUMB - else: - user_thumb = item['thumb'] - - user_details = {"user_id": item['user_id'], - "username": item['username'], - "friendly_name": friendly_name, - "email": item['email'], - "thumb": user_thumb, - "is_home_user": item['is_home_user'], - "is_allow_sync": item['is_allow_sync'], - "is_restricted": item['is_restricted'], - "do_notify": item['do_notify'] - } - return user_details + library_details = {'section_id': item['section_id'], + 'section_name': item['section_name'], + 'section_type': item['section_type'], + 'library_thumb': item['library_thumb'], + 'custom_thumb': item['custom_thumb'], + 'library_art': item['art'], + 'count': item['count'], + 'parent_count': item['parent_count'], + 'child_count': item['child_count'], + 'do_notify': item['do_notify'], + 'keep_history': item['keep_history'] + } + return library_details else: - logger.warn(u"PlexPy :: Unable to retrieve user from local database. Requesting user list refresh.") + logger.warn(u"PlexPy :: Unable to retrieve library from local database. Requesting library list refresh.") # Let's first refresh the user list to make sure the user isn't newly added and not in the db yet - if user: - # Refresh users - plextv.refresh_users() - query = 'SELECT user_id, username, friendly_name, email, ' \ - 'custom_avatar_url as thumb, is_home_user, is_allow_sync, is_restricted, do_notify ' \ - 'FROM users ' \ - 'WHERE username = ? ' \ - 'UNION ALL ' \ - 'SELECT null, user, null, null, null, null, null, null, null ' \ - 'FROM session_history ' \ - 'WHERE user = ? ' \ - 'GROUP BY user ' \ - 'LIMIT 1' - result = monitor_db.select(query, args=[user, user]) - elif user_id: - # Refresh users - plextv.refresh_users() - query = 'SELECT user_id, username, friendly_name, email, ' \ - 'custom_avatar_url as thumb, is_home_user, is_allow_sync, is_restricted, do_notify ' \ - 'FROM users ' \ - 'WHERE user_id = ? ' \ - 'UNION ALL ' \ - 'SELECT user_id, user, null, null, null, null, null, null, null ' \ - 'FROM session_history ' \ - 'WHERE user_id = ? ' \ - 'GROUP BY user ' \ - 'LIMIT 1' - result = monitor_db.select(query, args=[user_id, user_id]) + if section_id: + # Refresh libraries + pmsconnect.refresh_libraries() + query = 'SELECT section_id, section_name, section_type, count, parent_count, child_count, ' \ + 'thumb AS library_thumb, (CASE WHEN library_sections.custom_thumb_url == library_sections.thumb ' \ + ' THEN NULL ELSE custom_thumb_url END) AS custom_thumb, art, do_notify, keep_history ' \ + 'FROM library_sections ' \ + 'WHERE section_id = ? ' + result = monitor_db.select(query, args=[section_id]) else: result = None if result: - user_details = {} + library_details = {} for item in result: - if not item['friendly_name']: - friendly_name = item['username'] - else: - friendly_name = item['friendly_name'] - if not item['thumb'] or item['thumb'] == '': - user_thumb = common.DEFAULT_USER_THUMB - else: - user_thumb = item['thumb'] - user_details = {"user_id": item['user_id'], - "username": item['username'], - "friendly_name": friendly_name, - "email": item['email'], - "thumb": user_thumb, - "is_home_user": item['is_home_user'], - "is_allow_sync": item['is_allow_sync'], - "is_restricted": item['is_restricted'], - "do_notify": item['do_notify'] - } + library_details = {'section_id': item['section_id'], + 'section_name': item['section_name'], + 'section_type': item['section_type'], + 'library_thumb': item['library_thumb'], + 'custom_thumb': item['custom_thumb'], + 'library_art': item['art'], + 'count': item['count'], + 'parent_count': item['parent_count'], + 'child_count': item['child_count'], + 'do_notify': item['do_notify'], + 'keep_history': item['keep_history'] + } return user_details else: - # If there is no user data we must return something + # If there is no library data we must return something # Use "Local" user to retain compatibility with PlexWatch database value - return {"user_id": None, - "username": 'Local', - "friendly_name": 'Local', - "email": '', - "thumb": '', - "is_home_user": 0, - "is_allow_sync": 0, - "is_restricted": 0, - "do_notify": 0 + return {'section_id': None, + 'section_name': '', + 'section_type': '', + 'library_thumb': '', + 'custom_thumb': '', + 'library_art': '', + 'count': 0, + 'parent_count': 0, + 'child_count': 0, + 'do_notify': 0, + 'keep_history': 0 } - def get_user_watch_time_stats(self, user=None, user_id=None): + def get_library_watch_time_stats(self, library_id=None): monitor_db = database.MonitorDatabase() time_queries = [1, 7, 30, 0] - user_watch_time_stats = [] + library_watch_time_stats = [] for days in time_queries: if days > 0: - if user_id: + if library_id: query = 'SELECT (SUM(stopped - started) - ' \ 'SUM(CASE WHEN paused_counter is null THEN 0 ELSE paused_counter END)) as total_time, ' \ - 'COUNT(id) AS total_plays ' \ + 'COUNT(session_history.id) AS total_plays ' \ 'FROM session_history ' \ + 'JOIN session_history_metadata ON session_history_metadata.id = session_history.id ' \ 'WHERE datetime(stopped, "unixepoch", "localtime") >= datetime("now", "-%s days", "localtime") ' \ - 'AND user_id = ?' % days - result = monitor_db.select(query, args=[user_id]) - elif user: - query = 'SELECT (SUM(stopped - started) - ' \ - 'SUM(CASE WHEN paused_counter is null THEN 0 ELSE paused_counter END)) as total_time, ' \ - 'COUNT(id) AS total_plays ' \ - 'FROM session_history ' \ - 'WHERE datetime(stopped, "unixepoch", "localtime") >= datetime("now", "-%s days", "localtime") ' \ - 'AND user = ?' % days - result = monitor_db.select(query, args=[user]) + 'AND library_id = ?' % days + result = monitor_db.select(query, args=[library_id]) else: query = 'SELECT (SUM(stopped - started) - ' \ 'SUM(CASE WHEN paused_counter is null THEN 0 ELSE paused_counter END)) as total_time, ' \ - 'COUNT(id) AS total_plays ' \ + 'COUNT(session_history.id) AS total_plays ' \ 'FROM session_history ' \ - 'WHERE user = ?' - result = monitor_db.select(query, args=[user]) + 'JOIN session_history_metadata ON session_history_metadata.id = session_history.id ' \ + 'WHERE library_id = ?' + result = monitor_db.select(query, args=[library_id]) for item in result: if item['total_time']: @@ -469,73 +241,64 @@ class Libraries(object): 'total_plays': total_plays } - user_watch_time_stats.append(row) + library_watch_time_stats.append(row) - return user_watch_time_stats + return library_watch_time_stats - def get_user_player_stats(self, user=None, user_id=None): + def get_library_user_stats(self, library_id=None): monitor_db = database.MonitorDatabase() - player_stats = [] - result_id = 0 + user_stats = [] try: - if user_id: - query = 'SELECT player, COUNT(player) as player_count, platform ' \ + if library_id: + query = 'SELECT (CASE WHEN users.friendly_name IS NULL THEN users.username ' \ + 'ELSE users.friendly_name END) AS user, users.user_id, users.thumb, COUNT(user) AS user_count ' \ 'FROM session_history ' \ - 'WHERE user_id = ? ' \ - 'GROUP BY player ' \ - 'ORDER BY player_count DESC' - result = monitor_db.select(query, args=[user_id]) - else: - query = 'SELECT player, COUNT(player) as player_count, platform ' \ - 'FROM session_history ' \ - 'WHERE user = ? ' \ - 'GROUP BY player ' \ - 'ORDER BY player_count DESC' - result = monitor_db.select(query, args=[user]) + 'JOIN session_history_metadata ON session_history_metadata.id = session_history.id ' \ + 'JOIN users ON users.user_id = session_history.user_id ' \ + 'WHERE library_id = ? ' \ + 'GROUP BY user ' \ + 'ORDER BY user_count DESC' + result = monitor_db.select(query, args=[library_id]) except: - logger.warn("Unable to execute database query.") + logger.warn("Unable to execute database query for get_library_user_stats.") return None - + for item in result: - # Rename Mystery platform names - platform_type = common.PLATFORM_NAME_OVERRIDES.get(item['platform'], item['platform']) - - row = {'player_name': item['player'], - 'platform_type': platform_type, - 'total_plays': item['player_count'], - 'result_id': result_id + row = {'user': item['user'], + 'user_id': item['user_id'], + 'thumb': item['thumb'], + 'total_plays': item['user_count'] } - player_stats.append(row) - result_id += 1 + user_stats.append(row) + + return user_stats - return player_stats - - def delete_all_library_history(self, library_id=None): + def delete_all_library_history(self, section_id=None): monitor_db = database.MonitorDatabase() - if library_id.isdigit(): - logger.info(u"PlexPy Libraries :: Deleting all history for library id %s from database." % library_id) + if section_id.isdigit(): + logger.info(u"PlexPy Libraries :: Deleting all history for library id %s from database." % section_id) session_history_media_info_del = \ monitor_db.action('DELETE FROM ' 'session_history_media_info ' 'WHERE session_history_media_info.id IN (SELECT session_history_media_info.id ' 'FROM session_history_media_info ' 'JOIN session_history_metadata ON session_history_media_info.id = session_history_metadata.id ' - 'WHERE session_history_metadata.library_id = ?)', [library_id]) + 'WHERE session_history_metadata.library_id = ?)', [section_id]) session_history_del = \ monitor_db.action('DELETE FROM ' 'session_history ' 'WHERE session_history.id IN (SELECT session_history.id ' 'FROM session_history ' 'JOIN session_history_metadata ON session_history.id = session_history_metadata.id ' - 'WHERE session_history_metadata.library_id = ?)', [library_id]) + 'WHERE session_history_metadata.library_id = ?)', [section_id]) session_history_metadata_del = \ monitor_db.action('DELETE FROM ' 'session_history_metadata ' - 'WHERE session_history_metadata.library_id = ?', [library_id]) + 'WHERE session_history_metadata.library_id = ?', [section_id]) - return 'Deleted all items for library_id %s.' % library_id + return 'Deleted all items for library_id %s.' % section_id else: - return 'Unable to delete items. Input library_id not valid.' + return 'Unable to delete items. Input library_id not valid.' \ No newline at end of file diff --git a/plexpy/pmsconnect.py b/plexpy/pmsconnect.py index 649af3e3..79dc1837 100644 --- a/plexpy/pmsconnect.py +++ b/plexpy/pmsconnect.py @@ -61,6 +61,7 @@ def refresh_libraries(): 'section_name': section['title'], 'section_type': section['type'], 'thumb': section['thumb'], + 'art': section['art'], 'count': section['count'], 'parent_count': section.get('parent_count', None), 'child_count': section.get('child_count', None) @@ -1460,7 +1461,8 @@ class PmsConnect(object): 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') + 'thumb': helpers.get_xml_attr(result, 'thumb'), + 'art': helpers.get_xml_attr(result, 'art') } libraries_list.append(libraries_output) @@ -1564,6 +1566,7 @@ class PmsConnect(object): 'title': library['title'], 'type': library_type, 'thumb': library['thumb'], + 'art': library['art'], 'count': library_list['library_count'], 'count_type': library_list['count_type'] } diff --git a/plexpy/webserve.py b/plexpy/webserve.py index 5970e3c3..b43d1d44 100644 --- a/plexpy/webserve.py +++ b/plexpy/webserve.py @@ -266,6 +266,32 @@ class WebInterface(object): status_message = "Failed to update user." return status_message + @cherrypy.expose + def library(self, section_id=None): + library_data = libraries.Libraries() + if section_id: + try: + library_details = library_data.get_library_details(section_id=section_id) + except: + logger.warn("Unable to retrieve user details for library_id %s " % section_id) + else: + logger.debug(u"Library page requested but no parameters received.") + raise cherrypy.HTTPRedirect("home") + + return serve_template(templatename="library.html", title="Library", data=library_details) + + @cherrypy.expose + def edit_library_dialog(self, section_id=None, **kwargs): + library_data = libraries.Libraries() + if section_id: + result = library_data.get_library_details(section_id=section_id) + status_message = '' + else: + result = None + status_message = 'An error occured.' + + return serve_template(templatename="edit_library.html", title="Edit Library", data=result, status_message=status_message) + @cherrypy.expose def edit_library(self, section_id=None, **kwargs): do_notify = kwargs.get('do_notify', 0) @@ -670,9 +696,9 @@ class WebInterface(object): if 'reference_id' in kwargs: reference_id = kwargs.get('reference_id', "") custom_where.append(['session_history.reference_id', reference_id]) - if 'library_id' in kwargs: - library_id = kwargs.get('library_id', "") - custom_where.append(['session_history_metadata.library_id', library_id]) + if 'section_id' in kwargs: + section_id = kwargs.get('section_id', "") + custom_where.append(['session_history_metadata.library_id', section_id]) if 'media_type' in kwargs: media_type = kwargs.get('media_type', "") if media_type != 'all': @@ -888,7 +914,7 @@ class WebInterface(object): return None @cherrypy.expose - def info(self, library_id=None, rating_key=None, source=None, **kwargs): + def info(self, rating_key=None, source=None, **kwargs): # Make sure our library sections are up to date. data_factory = datafactory.DataFactory() data_factory.update_library_sections() @@ -905,11 +931,6 @@ class WebInterface(object): if source == 'history': data_factory = datafactory.DataFactory() metadata = data_factory.get_metadata_details(rating_key=rating_key) - elif library_id: - pms_connect = pmsconnect.PmsConnect() - result = pms_connect.get_library_metadata_details(library_id=library_id) - if result: - metadata = result['metadata'] else: pms_connect = pmsconnect.PmsConnect() result = pms_connect.get_metadata_details(rating_key=rating_key) @@ -972,6 +993,20 @@ class WebInterface(object): return serve_template(templatename="user_recently_watched.html", data=None, title="Recently Watched") + @cherrypy.expose + def get_library_recently_watched(self, library_id=None, limit='10', **kwargs): + + data_factory = datafactory.DataFactory() + result = data_factory.get_recently_watched(library_id=library_id, limit=limit) + + if result: + return serve_template(templatename="user_recently_watched.html", data=result, + title="Recently Watched") + else: + logger.warn('Unable to retrieve data.') + return serve_template(templatename="user_recently_watched.html", data=None, + title="Recently Watched") + @cherrypy.expose def get_user_watch_time_stats(self, user=None, user_id=None, **kwargs): @@ -984,6 +1019,18 @@ class WebInterface(object): logger.warn('Unable to retrieve data.') return serve_template(templatename="user_watch_time_stats.html", data=None, title="Watch Stats") + @cherrypy.expose + def get_library_watch_time_stats(self, library_id=None, **kwargs): + + library_data = libraries.Libraries() + result = library_data.get_library_watch_time_stats(library_id=library_id) + + if result: + return serve_template(templatename="user_watch_time_stats.html", data=result, title="Watch Stats") + else: + logger.warn('Unable to retrieve data.') + return serve_template(templatename="user_watch_time_stats.html", data=None, title="Watch Stats") + @cherrypy.expose def get_user_player_stats(self, user=None, user_id=None, **kwargs): @@ -997,6 +1044,18 @@ class WebInterface(object): logger.warn('Unable to retrieve data.') return serve_template(templatename="user_player_stats.html", data=None, title="Player Stats") + @cherrypy.expose + def get_library_user_stats(self, library_id=None, **kwargs): + + library_data = libraries.Libraries() + result = library_data.get_library_user_stats(library_id=library_id) + + if result: + return serve_template(templatename="library_user_stats.html", data=result, title="Player Stats") + else: + logger.warn('Unable to retrieve data.') + return serve_template(templatename="library_user_stats.html", data=None, title="Player Stats") + @cherrypy.expose def get_item_children(self, rating_key='', **kwargs): @@ -1594,11 +1653,11 @@ class WebInterface(object): return json.dumps({'message': 'no data received'}) @cherrypy.expose - def delete_all_library_history(self, library_id, **kwargs): + def delete_all_library_history(self, section_id, **kwargs): library_data = libraries.Libraries() - if library_id: - delete_row = library_data.delete_all_library_history(library_id=library_id) + if section_id: + delete_row = library_data.delete_all_library_history(section_id=section_id) if delete_row: cherrypy.response.headers['Content-type'] = 'application/json'