From 1262de2ae2390ad2bd0fc68ac0784bb4acd4f2ec Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Thu, 10 Dec 2015 21:00:00 -0800 Subject: [PATCH 01/15] Clean up notifiers --- plexpy/notifiers.py | 34 +--------------------------------- 1 file changed, 1 insertion(+), 33 deletions(-) diff --git a/plexpy/notifiers.py b/plexpy/notifiers.py index c0fb44e6..18241fec 100644 --- a/plexpy/notifiers.py +++ b/plexpy/notifiers.py @@ -385,9 +385,6 @@ class GROWL(object): self.enabled = plexpy.CONFIG.GROWL_ENABLED self.host = plexpy.CONFIG.GROWL_HOST self.password = plexpy.CONFIG.GROWL_PASSWORD - self.on_play = plexpy.CONFIG.GROWL_ON_PLAY - self.on_stop = plexpy.CONFIG.GROWL_ON_STOP - self.on_watched = plexpy.CONFIG.GROWL_ON_WATCHED def conf(self, options): return cherrypy.config['config'].get('Growl', options) @@ -490,9 +487,6 @@ class PROWL(object): self.enabled = plexpy.CONFIG.PROWL_ENABLED self.keys = plexpy.CONFIG.PROWL_KEYS self.priority = plexpy.CONFIG.PROWL_PRIORITY - self.on_play = plexpy.CONFIG.PROWL_ON_PLAY - self.on_stop = plexpy.CONFIG.PROWL_ON_STOP - self.on_watched = plexpy.CONFIG.PROWL_ON_WATCHED def conf(self, options): return cherrypy.config['config'].get('Prowl', options) @@ -565,9 +559,6 @@ class XBMC(object): self.hosts = plexpy.CONFIG.XBMC_HOST self.username = plexpy.CONFIG.XBMC_USERNAME self.password = plexpy.CONFIG.XBMC_PASSWORD - self.on_play = plexpy.CONFIG.XBMC_ON_PLAY - self.on_stop = plexpy.CONFIG.XBMC_ON_STOP - self.on_watched = plexpy.CONFIG.XBMC_ON_WATCHED def _sendhttp(self, host, command): url_command = urllib.urlencode(command) @@ -648,9 +639,6 @@ class Plex(object): self.client_hosts = plexpy.CONFIG.PLEX_CLIENT_HOST self.username = plexpy.CONFIG.PLEX_USERNAME self.password = plexpy.CONFIG.PLEX_PASSWORD - self.on_play = plexpy.CONFIG.PLEX_ON_PLAY - self.on_stop = plexpy.CONFIG.PLEX_ON_STOP - self.on_watched = plexpy.CONFIG.PLEX_ON_WATCHED def _sendhttp(self, host, command): @@ -728,9 +716,6 @@ class NMA(object): def __init__(self): self.api = plexpy.CONFIG.NMA_APIKEY self.nma_priority = plexpy.CONFIG.NMA_PRIORITY - self.on_play = plexpy.CONFIG.NMA_ON_PLAY - self.on_stop = plexpy.CONFIG.NMA_ON_STOP - self.on_watched = plexpy.CONFIG.NMA_ON_WATCHED def notify(self, subject=None, message=None): if not subject or not message: @@ -790,9 +775,6 @@ class PUSHBULLET(object): self.apikey = plexpy.CONFIG.PUSHBULLET_APIKEY self.deviceid = plexpy.CONFIG.PUSHBULLET_DEVICEID self.channel_tag = plexpy.CONFIG.PUSHBULLET_CHANNEL_TAG - self.on_play = plexpy.CONFIG.PUSHBULLET_ON_PLAY - self.on_stop = plexpy.CONFIG.PUSHBULLET_ON_STOP - self.on_watched = plexpy.CONFIG.PUSHBULLET_ON_WATCHED def conf(self, options): return cherrypy.config['config'].get('PUSHBULLET', options) @@ -869,9 +851,6 @@ class PUSHALOT(object): def __init__(self): self.api_key = plexpy.CONFIG.PUSHALOT_APIKEY - self.on_play = plexpy.CONFIG.PUSHALOT_ON_PLAY - self.on_stop = plexpy.CONFIG.PUSHALOT_ON_STOP - self.on_watched = plexpy.CONFIG.PUSHALOT_ON_WATCHED def notify(self, message, event): if not message or not event: @@ -928,9 +907,6 @@ class PUSHOVER(object): self.keys = plexpy.CONFIG.PUSHOVER_KEYS self.priority = plexpy.CONFIG.PUSHOVER_PRIORITY self.sound = plexpy.CONFIG.PUSHOVER_SOUND - self.on_play = plexpy.CONFIG.PUSHOVER_ON_PLAY - self.on_stop = plexpy.CONFIG.PUSHOVER_ON_STOP - self.on_watched = plexpy.CONFIG.PUSHOVER_ON_WATCHED if plexpy.CONFIG.PUSHOVER_APITOKEN: self.application_token = plexpy.CONFIG.PUSHOVER_APITOKEN @@ -1158,9 +1134,6 @@ class TwitterNotifier(object): class OSX_NOTIFY(object): def __init__(self): - self.on_play = plexpy.CONFIG.OSX_NOTIFY_ON_PLAY - self.on_stop = plexpy.CONFIG.OSX_NOTIFY_ON_STOP - self.on_watched = plexpy.CONFIG.OSX_NOTIFY_ON_WATCHED try: self.objc = __import__("objc") self.AppKit = __import__("AppKit") @@ -1247,9 +1220,6 @@ class BOXCAR(object): self.url = 'https://new.boxcar.io/api/notifications' self.token = plexpy.CONFIG.BOXCAR_TOKEN self.sound = plexpy.CONFIG.BOXCAR_SOUND - self.on_play = plexpy.CONFIG.BOXCAR_ON_PLAY - self.on_stop = plexpy.CONFIG.BOXCAR_ON_STOP - self.on_watched = plexpy.CONFIG.BOXCAR_ON_WATCHED def notify(self, title, message): if not title or not message: @@ -1322,9 +1292,7 @@ class BOXCAR(object): class Email(object): def __init__(self): - self.on_play = plexpy.CONFIG.EMAIL_ON_PLAY - self.on_stop = plexpy.CONFIG.EMAIL_ON_STOP - self.on_watched = plexpy.CONFIG.EMAIL_ON_WATCHED + pass def notify(self, subject, message): if not subject or not message: From c042d9e39a421c0531d961cf236db5d0c11eb491 Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Sat, 12 Dec 2015 13:16:35 -0800 Subject: [PATCH 02/15] Fix metadata for grouped recently added notifications --- data/interfaces/default/settings.html | 3 ++- plexpy/notification_handler.py | 24 +++++++++++++++++++----- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/data/interfaces/default/settings.html b/data/interfaces/default/settings.html index 66a5be3d..384290f9 100644 --- a/data/interfaces/default/settings.html +++ b/data/interfaces/default/settings.html @@ -533,7 +533,8 @@ available_notification_agents = sorted(notifiers.available_notification_agents() -

Enable to only get one TV Show or Artist notification for recently added Episodes or Tracks. Movies are unaffected.

+

Enable to only get one TV Show or Artist notification for a batch of recently added Episodes or Tracks. Movies are unaffected.
+ Note: No Season/Episode or Album/Track metadata will be available.

diff --git a/plexpy/notification_handler.py b/plexpy/notification_handler.py index 4d4560de..fdbe3f22 100644 --- a/plexpy/notification_handler.py +++ b/plexpy/notification_handler.py @@ -408,6 +408,20 @@ def build_notify_text(session=None, timeline=None, state=None): progress_percent = helpers.get_percent(view_offset, duration) + # Fix metadata params for notify recently added grandparent + if plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_GRANDPARENT: + show_name = metadata['title'] + episode_name = '' + artist_name = metadata['title'] + album_name = '' + track_name = '' + else: + show_name = metadata['grandparent_title'] + episode_name = metadata['title'] + artist_name = metadata['grandparent_title'] + album_name = metadata['parent_title'] + track_name = metadata['title'] + available_params = {'server_name': server_name, 'server_uptime': server_uptime, 'user': user, @@ -416,11 +430,11 @@ def build_notify_text(session=None, timeline=None, state=None): 'ip_address': ip_address, 'media_type': metadata['media_type'], 'title': full_title, - 'show_name': metadata['grandparent_title'], - 'episode_name': metadata['title'], - 'artist_name': metadata['grandparent_title'], - 'album_name': metadata['parent_title'], - 'track_name': metadata['title'], + 'show_name': show_name, + 'episode_name': episode_name, + 'artist_name': artist_name, + 'album_name': album_name, + 'track_name': track_name, 'season_num': metadata['parent_index'].zfill(1), 'season_num00': metadata['parent_index'].zfill(2), 'episode_num': metadata['index'].zfill(1), From 6e62ffdd222995126e2bd4b3251446fab77e5706 Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Sat, 12 Dec 2015 14:08:34 -0800 Subject: [PATCH 03/15] Get Pushbullet devices automatically using API --- .../default/notification_config.html | 29 +++++++++++----- plexpy/notifiers.py | 33 +++++++++++++++++-- 2 files changed, 50 insertions(+), 12 deletions(-) diff --git a/data/interfaces/default/notification_config.html b/data/interfaces/default/notification_config.html index 9d8ab9a1..dc997d5b 100644 --- a/data/interfaces/default/notification_config.html +++ b/data/interfaces/default/notification_config.html @@ -84,15 +84,8 @@ from plexpy import helpers % endif + + +

${top_stat['rows'][0]['count']}

+

streams

+
+ +
+
+
+ + % endif % endfor diff --git a/data/interfaces/default/images/home-stat_most-concurrent.png b/data/interfaces/default/images/home-stat_most-concurrent.png new file mode 100644 index 0000000000000000000000000000000000000000..085dc93eb193971cd91cfca0c794b52db91c1260 GIT binary patch literal 4864 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 GhyM)~t94HR literal 0 HcmV?d00001 diff --git a/data/interfaces/default/settings.html b/data/interfaces/default/settings.html index 384290f9..ee3643ff 100644 --- a/data/interfaces/default/settings.html +++ b/data/interfaces/default/settings.html @@ -111,6 +111,7 @@ available_notification_agents = sorted(notifiers.available_notification_agents() + diff --git a/plexpy/config.py b/plexpy/config.py index 491f2081..664a69ec 100644 --- a/plexpy/config.py +++ b/plexpy/config.py @@ -98,7 +98,8 @@ _CONFIG_DEFINITIONS = { 'HOME_STATS_LENGTH': (int, 'General', 30), 'HOME_STATS_TYPE': (int, 'General', 0), 'HOME_STATS_COUNT': (int, 'General', 5), - 'HOME_STATS_CARDS': (str, 'General', 'watch_statistics, top_tv, popular_tv, top_movies, popular_movies, top_music, popular_music, top_users, top_platforms, last_watched'), + 'HOME_STATS_CARDS': (str, 'General', 'watch_statistics, top_tv, popular_tv, top_movies, popular_movies, ' \ + 'top_music, popular_music, last_watched, top_users, top_platforms, most_concurrent'), 'HTTPS_CERT': (str, 'General', ''), 'HTTPS_KEY': (str, 'General', ''), 'HTTP_HOST': (str, 'General', '0.0.0.0'), diff --git a/plexpy/datafactory.py b/plexpy/datafactory.py index 8d797fae..d1b656e5 100644 --- a/plexpy/datafactory.py +++ b/plexpy/datafactory.py @@ -589,6 +589,38 @@ class DataFactory(object): home_stats.append({'stat_id': stat, 'rows': last_watched}) + elif stat == 'most_concurrent': + try: + query = 'SELECT started, stopped ' \ + 'FROM session_history ' + result = monitor_db.select(query) + except: + logger.warn("Unable to execute database query for get_home_stats: most_concurrent.") + return None + + times = {} + for item in result: + times.update({str(item['stopped']) + 'A': -1, str(item['started']) + 'B': 1}) + + count = 0 + last_start = 0 + most_concurrent = {'count': count} + + for key in sorted(times): + if times[key] == 1: + count += times[key] + if count >= most_concurrent['count']: + last_start = key + else: + if count >= most_concurrent['count']: + most_concurrent = {'count': count, + 'started': last_start[:-1], + 'stopped': key[:-1]} + count += times[key] + + home_stats.append({'stat_id': stat, + 'rows': [most_concurrent]}) + return home_stats def get_stream_details(self, row_id=None): From cb7ba7fddeedd4a8ec207027b2bbe1d8b4c3bde5 Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Sat, 12 Dec 2015 14:36:57 -0800 Subject: [PATCH 05/15] Clean up if statement in current activity header --- data/interfaces/default/current_activity_header.html | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/data/interfaces/default/current_activity_header.html b/data/interfaces/default/current_activity_header.html index 0727e558..04d8f65b 100644 --- a/data/interfaces/default/current_activity_header.html +++ b/data/interfaces/default/current_activity_header.html @@ -17,12 +17,10 @@ DOCUMENTATION :: END % if data != None: % if data == '0':

Activity

+ % elif data == '1': +

Activity ${data} stream

% else: - % if data == '1': -

Activity ${data} stream

- % else: -

Activity ${data} streams

- % endif +

Activity ${data} streams

% endif % else:

Activity

From 53876e8f0dcc32f813d35bd89b7c40bf32846c93 Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Sat, 12 Dec 2015 16:21:54 -0800 Subject: [PATCH 06/15] Add time range to most concurrent stat * Reorder cards --- data/interfaces/default/settings.html | 2 +- plexpy/datafactory.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/data/interfaces/default/settings.html b/data/interfaces/default/settings.html index ee3643ff..e5e1cca0 100644 --- a/data/interfaces/default/settings.html +++ b/data/interfaces/default/settings.html @@ -109,10 +109,10 @@ available_notification_agents = sorted(notifiers.available_notification_agents() + - diff --git a/plexpy/datafactory.py b/plexpy/datafactory.py index d1b656e5..6dcdef26 100644 --- a/plexpy/datafactory.py +++ b/plexpy/datafactory.py @@ -592,7 +592,9 @@ class DataFactory(object): elif stat == 'most_concurrent': try: query = 'SELECT started, stopped ' \ - 'FROM session_history ' + 'FROM session_history ' \ + 'WHERE datetime(stopped, "unixepoch", "localtime") ' \ + '>= datetime("now", "-%s days", "localtime") ' % time_range result = monitor_db.select(query) except: logger.warn("Unable to execute database query for get_home_stats: most_concurrent.") From 66bb922012bc5597aa4904f9fb5828834fdc9b98 Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Sat, 12 Dec 2015 17:31:52 -0800 Subject: [PATCH 07/15] Add stream details to notification options * Also beautify modal --- data/interfaces/default/css/plexpy.css | 25 ++ data/interfaces/default/settings.html | 324 ++++++++++++++++--------- plexpy/notification_handler.py | 74 +++++- 3 files changed, 296 insertions(+), 127 deletions(-) diff --git a/data/interfaces/default/css/plexpy.css b/data/interfaces/default/css/plexpy.css index cfbf5933..ef06c9dc 100644 --- a/data/interfaces/default/css/plexpy.css +++ b/data/interfaces/default/css/plexpy.css @@ -2520,4 +2520,29 @@ table[id^='history_child'] thead th { -ms-transition: background 0.3s; -o-transition: background 0.3s; transition: background 0.3s; +} + +.notification-params { + margin-top: 10px; + background-color: #282828; +} +.notification-params th { + padding-left: 10px; + height: 30px; +} +.notification-params td { + height: 25px; +} +.notification-params td:first-child { + padding-left: 10px; + width: 200px; +} +.notification-params td:not(:first-child) { + padding-right: 10px; +} +.notification-params tr:nth-child(odd) td { + background-color: rgba(255,255,255,0.035); +} +.notification-params tr:nth-child(even) td { + background-color: rgba(255,255,255,0.010); } \ No newline at end of file diff --git a/data/interfaces/default/settings.html b/data/interfaces/default/settings.html index e5e1cca0..b70f78b7 100644 --- a/data/interfaces/default/settings.html +++ b/data/interfaces/default/settings.html @@ -930,155 +930,248 @@ available_notification_agents = sorted(notifiers.available_notification_agents() diff --git a/data/interfaces/default/settings.html b/data/interfaces/default/settings.html index b70f78b7..86819976 100644 --- a/data/interfaces/default/settings.html +++ b/data/interfaces/default/settings.html @@ -716,6 +716,40 @@ available_notification_agents = sorted(notifiers.available_notification_agents() +
  • + + +
  • +
  • + + +
  • diff --git a/plexpy/activity_pinger.py b/plexpy/activity_pinger.py index 6cf12443..ee0479d3 100644 --- a/plexpy/activity_pinger.py +++ b/plexpy/activity_pinger.py @@ -36,6 +36,11 @@ def check_active_sessions(ws_request=False): global int_ping_count if session_list: + if int_ping_count >= 3: + logger.info(u"PlexPy Monitor :: The Plex Media Server is back up.") + # Fire off notifications + threading.Thread(target=notification_handler.notify_timeline, + kwargs=dict(notify_action='intup')).start() int_ping_count = 0 media_container = session_list['sessions'] @@ -248,7 +253,7 @@ def check_server_response(): server_response = pms_connect.get_server_response() global ext_ping_count - + # Check for remote access if server_response: @@ -267,6 +272,11 @@ def check_server_response(): % str(ext_ping_count)) # Reset external ping counter else: + if ext_ping_count >= 3: + logger.info(u"PlexPy Monitor :: Plex remote access is back up.") + # Fire off notifications + threading.Thread(target=notification_handler.notify_timeline, + kwargs=dict(notify_action='extup')).start() ext_ping_count = 0 if ext_ping_count == 3: diff --git a/plexpy/config.py b/plexpy/config.py index 664a69ec..5ae5a68f 100644 --- a/plexpy/config.py +++ b/plexpy/config.py @@ -48,6 +48,8 @@ _CONFIG_DEFINITIONS = { 'BOXCAR_ON_CREATED': (int, 'Boxcar', 0), 'BOXCAR_ON_EXTDOWN': (int, 'Boxcar', 0), 'BOXCAR_ON_INTDOWN': (int, 'Boxcar', 0), + 'BOXCAR_ON_EXTUP': (int, 'Boxcar', 0), + 'BOXCAR_ON_INTUP': (int, 'Boxcar', 0), 'BUFFER_THRESHOLD': (int, 'Monitoring', 3), 'BUFFER_WAIT': (int, 'Monitoring', 900), 'CACHE_DIR': (str, 'General', ''), @@ -75,6 +77,8 @@ _CONFIG_DEFINITIONS = { 'EMAIL_ON_CREATED': (int, 'Email', 0), 'EMAIL_ON_EXTDOWN': (int, 'Email', 0), 'EMAIL_ON_INTDOWN': (int, 'Email', 0), + 'EMAIL_ON_EXTUP': (int, 'Email', 0), + 'EMAIL_ON_INTUP': (int, 'Email', 0), 'ENABLE_HTTPS': (int, 'General', 0), 'FIRST_RUN_COMPLETE': (int, 'General', 0), 'FREEZE_DB': (int, 'General', 0), @@ -94,6 +98,8 @@ _CONFIG_DEFINITIONS = { 'GROWL_ON_CREATED': (int, 'Growl', 0), 'GROWL_ON_EXTDOWN': (int, 'Growl', 0), 'GROWL_ON_INTDOWN': (int, 'Growl', 0), + 'GROWL_ON_EXTUP': (int, 'Growl', 0), + 'GROWL_ON_INTUP': (int, 'Growl', 0), 'HOME_LIBRARY_CARDS': (str, 'General', 'library_statistics_first'), 'HOME_STATS_LENGTH': (int, 'General', 30), 'HOME_STATS_TYPE': (int, 'General', 0), @@ -122,6 +128,8 @@ _CONFIG_DEFINITIONS = { 'IFTTT_ON_CREATED': (int, 'IFTTT', 0), 'IFTTT_ON_EXTDOWN': (int, 'IFTTT', 0), 'IFTTT_ON_INTDOWN': (int, 'IFTTT', 0), + 'IFTTT_ON_EXTUP': (int, 'IFTTT', 0), + 'IFTTT_ON_INTUP': (int, 'IFTTT', 0), 'JOURNAL_MODE': (str, 'Advanced', 'wal'), 'LAUNCH_BROWSER': (int, 'General', 1), 'LOG_DIR': (str, 'General', ''), @@ -151,6 +159,8 @@ _CONFIG_DEFINITIONS = { 'NMA_ON_CREATED': (int, 'NMA', 0), 'NMA_ON_EXTDOWN': (int, 'NMA', 0), 'NMA_ON_INTDOWN': (int, 'NMA', 0), + 'NMA_ON_EXTUP': (int, 'NMA', 0), + 'NMA_ON_INTUP': (int, 'NMA', 0), 'NOTIFY_CONSECUTIVE': (int, 'Monitoring', 1), 'NOTIFY_RECENTLY_ADDED': (int, 'Monitoring', 0), 'NOTIFY_RECENTLY_ADDED_GRANDPARENT': (int, 'Monitoring', 0), @@ -174,6 +184,10 @@ _CONFIG_DEFINITIONS = { 'NOTIFY_ON_EXTDOWN_BODY_TEXT': (unicode, 'Monitoring', 'The Plex Media Server remote access is down.'), 'NOTIFY_ON_INTDOWN_SUBJECT_TEXT': (unicode, 'Monitoring', 'PlexPy ({server_name})'), 'NOTIFY_ON_INTDOWN_BODY_TEXT': (unicode, 'Monitoring', 'The Plex Media Server is down.'), + 'NOTIFY_ON_EXTUP_SUBJECT_TEXT': (unicode, 'Monitoring', 'PlexPy ({server_name})'), + 'NOTIFY_ON_EXTUP_BODY_TEXT': (unicode, 'Monitoring', 'The Plex Media Server remote access is back up.'), + 'NOTIFY_ON_INTUP_SUBJECT_TEXT': (unicode, 'Monitoring', 'PlexPy ({server_name})'), + 'NOTIFY_ON_INTUP_BODY_TEXT': (unicode, 'Monitoring', 'The Plex Media Server is back up.'), 'OSX_NOTIFY_APP': (str, 'OSX_Notify', '/Applications/PlexPy'), 'OSX_NOTIFY_ENABLED': (int, 'OSX_Notify', 0), 'OSX_NOTIFY_ON_PLAY': (int, 'OSX_Notify', 0), @@ -185,6 +199,8 @@ _CONFIG_DEFINITIONS = { 'OSX_NOTIFY_ON_CREATED': (int, 'OSX_Notify', 0), 'OSX_NOTIFY_ON_EXTDOWN': (int, 'OSX_Notify', 0), 'OSX_NOTIFY_ON_INTDOWN': (int, 'OSX_Notify', 0), + 'OSX_NOTIFY_ON_EXTUP': (int, 'OSX_Notify', 0), + 'OSX_NOTIFY_ON_INTUP': (int, 'OSX_Notify', 0), 'PLEX_CLIENT_HOST': (str, 'Plex', ''), 'PLEX_ENABLED': (int, 'Plex', 0), 'PLEX_PASSWORD': (str, 'Plex', ''), @@ -198,6 +214,8 @@ _CONFIG_DEFINITIONS = { 'PLEX_ON_CREATED': (int, 'Plex', 0), 'PLEX_ON_EXTDOWN': (int, 'Plex', 0), 'PLEX_ON_INTDOWN': (int, 'Plex', 0), + 'PLEX_ON_EXTUP': (int, 'Plex', 0), + 'PLEX_ON_INTUP': (int, 'Plex', 0), 'PROWL_ENABLED': (int, 'Prowl', 0), 'PROWL_KEYS': (str, 'Prowl', ''), 'PROWL_PRIORITY': (int, 'Prowl', 0), @@ -210,6 +228,8 @@ _CONFIG_DEFINITIONS = { 'PROWL_ON_CREATED': (int, 'Prowl', 0), 'PROWL_ON_EXTDOWN': (int, 'Prowl', 0), 'PROWL_ON_INTDOWN': (int, 'Prowl', 0), + 'PROWL_ON_EXTUP': (int, 'Prowl', 0), + 'PROWL_ON_INTUP': (int, 'Prowl', 0), 'PUSHALOT_APIKEY': (str, 'Pushalot', ''), 'PUSHALOT_ENABLED': (int, 'Pushalot', 0), 'PUSHALOT_ON_PLAY': (int, 'Pushalot', 0), @@ -221,6 +241,8 @@ _CONFIG_DEFINITIONS = { 'PUSHALOT_ON_CREATED': (int, 'Pushalot', 0), 'PUSHALOT_ON_EXTDOWN': (int, 'Pushalot', 0), 'PUSHALOT_ON_INTDOWN': (int, 'Pushalot', 0), + 'PUSHALOT_ON_EXTUP': (int, 'Pushalot', 0), + 'PUSHALOT_ON_INTUP': (int, 'Pushalot', 0), 'PUSHBULLET_APIKEY': (str, 'PushBullet', ''), 'PUSHBULLET_DEVICEID': (str, 'PushBullet', ''), 'PUSHBULLET_CHANNEL_TAG': (str, 'PushBullet', ''), @@ -234,6 +256,8 @@ _CONFIG_DEFINITIONS = { 'PUSHBULLET_ON_CREATED': (int, 'PushBullet', 0), 'PUSHBULLET_ON_EXTDOWN': (int, 'PushBullet', 0), 'PUSHBULLET_ON_INTDOWN': (int, 'PushBullet', 0), + 'PUSHBULLET_ON_EXTUP': (int, 'PushBullet', 0), + 'PUSHBULLET_ON_INTUP': (int, 'PushBullet', 0), 'PUSHOVER_APITOKEN': (str, 'Pushover', ''), 'PUSHOVER_ENABLED': (int, 'Pushover', 0), 'PUSHOVER_KEYS': (str, 'Pushover', ''), @@ -248,6 +272,8 @@ _CONFIG_DEFINITIONS = { 'PUSHOVER_ON_CREATED': (int, 'Pushover', 0), 'PUSHOVER_ON_EXTDOWN': (int, 'Pushover', 0), 'PUSHOVER_ON_INTDOWN': (int, 'Pushover', 0), + 'PUSHOVER_ON_EXTUP': (int, 'Pushover', 0), + 'PUSHOVER_ON_INTUP': (int, 'Pushover', 0), 'REFRESH_USERS_INTERVAL': (int, 'Monitoring', 12), 'REFRESH_USERS_ON_STARTUP': (int, 'Monitoring', 1), 'TELEGRAM_BOT_TOKEN': (str, 'Telegram', ''), @@ -262,6 +288,8 @@ _CONFIG_DEFINITIONS = { 'TELEGRAM_ON_CREATED': (int, 'Telegram', 0), 'TELEGRAM_ON_EXTDOWN': (int, 'Telegram', 0), 'TELEGRAM_ON_INTDOWN': (int, 'Telegram', 0), + 'TELEGRAM_ON_EXTUP': (int, 'Telegram', 0), + 'TELEGRAM_ON_INTUP': (int, 'Telegram', 0), 'TV_LOGGING_ENABLE': (int, 'Monitoring', 1), 'TV_NOTIFY_ENABLE': (int, 'Monitoring', 0), 'TV_NOTIFY_ON_START': (int, 'Monitoring', 1), @@ -280,6 +308,8 @@ _CONFIG_DEFINITIONS = { 'TWITTER_ON_CREATED': (int, 'Twitter', 0), 'TWITTER_ON_EXTDOWN': (int, 'Twitter', 0), 'TWITTER_ON_INTDOWN': (int, 'Twitter', 0), + 'TWITTER_ON_EXTUP': (int, 'Twitter', 0), + 'TWITTER_ON_INTUP': (int, 'Twitter', 0), 'UPDATE_DB_INTERVAL': (int, 'General', 24), 'VERIFY_SSL_CERT': (bool_int, 'Advanced', 1), 'VIDEO_LOGGING_ENABLE': (int, 'Monitoring', 1), @@ -295,7 +325,9 @@ _CONFIG_DEFINITIONS = { 'XBMC_ON_WATCHED': (int, 'XBMC', 0), 'XBMC_ON_CREATED': (int, 'XBMC', 0), 'XBMC_ON_EXTDOWN': (int, 'XBMC', 0), - 'XBMC_ON_INTDOWN': (int, 'XBMC', 0) + 'XBMC_ON_INTDOWN': (int, 'XBMC', 0), + 'XBMC_ON_EXTUP': (int, 'XBMC', 0), + 'XBMC_ON_INTUP': (int, 'XBMC', 0) } # pylint:disable=R0902 # it might be nice to refactor for fewer instance variables diff --git a/plexpy/notification_handler.py b/plexpy/notification_handler.py index b5554252..3bf22332 100644 --- a/plexpy/notification_handler.py +++ b/plexpy/notification_handler.py @@ -199,6 +199,18 @@ def notify_timeline(timeline_data=None, notify_action=None): notifiers.send_notification(config_id=agent['id'], subject=notify_strings[0], body=notify_strings[1]) + if agent['on_extup'] and notify_action == 'extup': + # Build and send notification + notify_strings = build_server_notify_text(state=notify_action) + notifiers.send_notification(config_id=agent['id'], + subject=notify_strings[0], + body=notify_strings[1]) + if agent['on_intup'] and notify_action == 'intup': + # Build and send notification + notify_strings = build_server_notify_text(state=notify_action) + notifiers.send_notification(config_id=agent['id'], + subject=notify_strings[0], + body=notify_strings[1]) else: logger.debug(u"PlexPy Notifier :: Notify timeline called but incomplete data received.") @@ -698,6 +710,10 @@ def build_server_notify_text(state=None): on_extdown_body = plexpy.CONFIG.NOTIFY_ON_EXTDOWN_BODY_TEXT on_intdown_subject = plexpy.CONFIG.NOTIFY_ON_INTDOWN_SUBJECT_TEXT on_intdown_body = plexpy.CONFIG.NOTIFY_ON_INTDOWN_BODY_TEXT + on_extup_subject = plexpy.CONFIG.NOTIFY_ON_EXTUP_SUBJECT_TEXT + on_extup_body = plexpy.CONFIG.NOTIFY_ON_EXTUP_BODY_TEXT + on_intup_subject = plexpy.CONFIG.NOTIFY_ON_INTUP_SUBJECT_TEXT + on_intup_body = plexpy.CONFIG.NOTIFY_ON_INTUP_BODY_TEXT available_params = {'server_name': server_name, 'server_uptime': server_uptime} @@ -746,6 +762,50 @@ def build_server_notify_text(state=None): except: logger.error(u"PlexPy Notifier :: Unable to parse custom notification body. Using fallback.") + return [subject_text, body_text] + else: + return [subject_text, body_text] + if state == 'extup': + # Default body text + body_text = 'The Plex Media Server remote access is back up.' + + if on_extup_subject and on_extup_body: + try: + subject_text = unicode(on_extup_subject).format(**available_params) + except LookupError, e: + logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification subject. Using fallback." % e) + except: + logger.error(u"PlexPy Notifier :: Unable to parse custom notification subject. Using fallback.") + + try: + body_text = unicode(on_extup_body).format(**available_params) + except LookupError, e: + logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification body. Using fallback." % e) + except: + logger.error(u"PlexPy Notifier :: Unable to parse custom notification body. Using fallback.") + + return [subject_text, body_text] + else: + return [subject_text, body_text] + elif state == 'intup': + # Default body text + body_text = 'The Plex Media Server is back up.' + + if on_intup_subject and on_intup_body: + try: + subject_text = unicode(on_intup_subject).format(**available_params) + except LookupError, e: + logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification subject. Using fallback." % e) + except: + logger.error(u"PlexPy Notifier :: Unable to parse custom notification subject. Using fallback.") + + try: + body_text = unicode(on_intup_body).format(**available_params) + except LookupError, e: + logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification body. Using fallback." % e) + except: + logger.error(u"PlexPy Notifier :: Unable to parse custom notification body. Using fallback.") + return [subject_text, body_text] else: return [subject_text, body_text] diff --git a/plexpy/notifiers.py b/plexpy/notifiers.py index 9c616af3..aa126df4 100644 --- a/plexpy/notifiers.py +++ b/plexpy/notifiers.py @@ -68,7 +68,9 @@ def available_notification_agents(): 'on_watched': plexpy.CONFIG.GROWL_ON_WATCHED, 'on_created': plexpy.CONFIG.GROWL_ON_CREATED, 'on_extdown': plexpy.CONFIG.GROWL_ON_EXTDOWN, - 'on_intdown': plexpy.CONFIG.GROWL_ON_INTDOWN + 'on_intdown': plexpy.CONFIG.GROWL_ON_INTDOWN, + 'on_extup': plexpy.CONFIG.GROWL_ON_EXTUP, + 'on_intup': plexpy.CONFIG.GROWL_ON_INTUP }, {'name': 'Prowl', 'id': AGENT_IDS['Prowl'], @@ -83,7 +85,9 @@ def available_notification_agents(): 'on_watched': plexpy.CONFIG.PROWL_ON_WATCHED, 'on_created': plexpy.CONFIG.PROWL_ON_CREATED, 'on_extdown': plexpy.CONFIG.PROWL_ON_EXTDOWN, - 'on_intdown': plexpy.CONFIG.PROWL_ON_INTDOWN + 'on_intdown': plexpy.CONFIG.PROWL_ON_INTDOWN, + 'on_extup': plexpy.CONFIG.PROWL_ON_EXTUP, + 'on_intup': plexpy.CONFIG.PROWL_ON_INTUP }, {'name': 'XBMC', 'id': AGENT_IDS['XBMC'], @@ -98,7 +102,9 @@ def available_notification_agents(): 'on_watched': plexpy.CONFIG.XBMC_ON_WATCHED, 'on_created': plexpy.CONFIG.XBMC_ON_CREATED, 'on_extdown': plexpy.CONFIG.XBMC_ON_EXTDOWN, - 'on_intdown': plexpy.CONFIG.XBMC_ON_INTDOWN + 'on_intdown': plexpy.CONFIG.XBMC_ON_INTDOWN, + 'on_extup': plexpy.CONFIG.XBMC_ON_EXTUP, + 'on_intup': plexpy.CONFIG.XBMC_ON_INTUP }, {'name': 'Plex', 'id': AGENT_IDS['Plex'], @@ -113,7 +119,9 @@ def available_notification_agents(): 'on_watched': plexpy.CONFIG.PLEX_ON_WATCHED, 'on_created': plexpy.CONFIG.PLEX_ON_CREATED, 'on_extdown': plexpy.CONFIG.PLEX_ON_EXTDOWN, - 'on_intdown': plexpy.CONFIG.PLEX_ON_INTDOWN + 'on_intdown': plexpy.CONFIG.PLEX_ON_INTDOWN, + 'on_extup': plexpy.CONFIG.PLEX_ON_EXTUP, + 'on_intup': plexpy.CONFIG.PLEX_ON_INTUP }, {'name': 'NotifyMyAndroid', 'id': AGENT_IDS['NMA'], @@ -128,7 +136,9 @@ def available_notification_agents(): 'on_watched': plexpy.CONFIG.NMA_ON_WATCHED, 'on_created': plexpy.CONFIG.NMA_ON_CREATED, 'on_extdown': plexpy.CONFIG.NMA_ON_EXTDOWN, - 'on_intdown': plexpy.CONFIG.NMA_ON_INTDOWN + 'on_intdown': plexpy.CONFIG.NMA_ON_INTDOWN, + 'on_extup': plexpy.CONFIG.NMA_ON_EXTUP, + 'on_intup': plexpy.CONFIG.NMA_ON_INTUP }, {'name': 'Pushalot', 'id': AGENT_IDS['Pushalot'], @@ -143,7 +153,9 @@ def available_notification_agents(): 'on_watched': plexpy.CONFIG.PUSHALOT_ON_WATCHED, 'on_created': plexpy.CONFIG.PUSHALOT_ON_CREATED, 'on_extdown': plexpy.CONFIG.PUSHALOT_ON_EXTDOWN, - 'on_intdown': plexpy.CONFIG.PUSHALOT_ON_INTDOWN + 'on_intdown': plexpy.CONFIG.PUSHALOT_ON_INTDOWN, + 'on_extup': plexpy.CONFIG.PUSHALOT_ON_EXTUP, + 'on_intup': plexpy.CONFIG.PUSHALOT_ON_INTUP }, {'name': 'Pushbullet', 'id': AGENT_IDS['Pushbullet'], @@ -158,7 +170,9 @@ def available_notification_agents(): 'on_watched': plexpy.CONFIG.PUSHBULLET_ON_WATCHED, 'on_created': plexpy.CONFIG.PUSHBULLET_ON_CREATED, 'on_extdown': plexpy.CONFIG.PUSHBULLET_ON_EXTDOWN, - 'on_intdown': plexpy.CONFIG.PUSHBULLET_ON_INTDOWN + 'on_intdown': plexpy.CONFIG.PUSHBULLET_ON_INTDOWN, + 'on_extup': plexpy.CONFIG.PUSHBULLET_ON_EXTUP, + 'on_intup': plexpy.CONFIG.PUSHBULLET_ON_INTUP }, {'name': 'Pushover', 'id': AGENT_IDS['Pushover'], @@ -173,7 +187,9 @@ def available_notification_agents(): 'on_watched': plexpy.CONFIG.PUSHOVER_ON_WATCHED, 'on_created': plexpy.CONFIG.PUSHOVER_ON_CREATED, 'on_extdown': plexpy.CONFIG.PUSHOVER_ON_EXTDOWN, - 'on_intdown': plexpy.CONFIG.PUSHOVER_ON_INTDOWN + 'on_intdown': plexpy.CONFIG.PUSHOVER_ON_INTDOWN, + 'on_extup': plexpy.CONFIG.PUSHOVER_ON_EXTUP, + 'on_intup': plexpy.CONFIG.PUSHOVER_ON_INTUP }, {'name': 'Boxcar2', 'id': AGENT_IDS['Boxcar2'], @@ -188,7 +204,9 @@ def available_notification_agents(): 'on_watched': plexpy.CONFIG.BOXCAR_ON_WATCHED, 'on_created': plexpy.CONFIG.BOXCAR_ON_CREATED, 'on_extdown': plexpy.CONFIG.BOXCAR_ON_EXTDOWN, - 'on_intdown': plexpy.CONFIG.BOXCAR_ON_INTDOWN + 'on_intdown': plexpy.CONFIG.BOXCAR_ON_INTDOWN, + 'on_extup': plexpy.CONFIG.BOXCAR_ON_EXTUP, + 'on_intup': plexpy.CONFIG.BOXCAR_ON_INTUP }, {'name': 'E-mail', 'id': AGENT_IDS['Email'], @@ -203,7 +221,9 @@ def available_notification_agents(): 'on_watched': plexpy.CONFIG.EMAIL_ON_WATCHED, 'on_created': plexpy.CONFIG.EMAIL_ON_CREATED, 'on_extdown': plexpy.CONFIG.EMAIL_ON_EXTDOWN, - 'on_intdown': plexpy.CONFIG.EMAIL_ON_INTDOWN + 'on_intdown': plexpy.CONFIG.EMAIL_ON_INTDOWN, + 'on_extup': plexpy.CONFIG.EMAIL_ON_EXTUP, + 'on_intup': plexpy.CONFIG.EMAIL_ON_INTUP }, {'name': 'Twitter', 'id': AGENT_IDS['Twitter'], @@ -218,7 +238,9 @@ def available_notification_agents(): 'on_watched': plexpy.CONFIG.TWITTER_ON_WATCHED, 'on_created': plexpy.CONFIG.TWITTER_ON_CREATED, 'on_extdown': plexpy.CONFIG.TWITTER_ON_EXTDOWN, - 'on_intdown': plexpy.CONFIG.TWITTER_ON_INTDOWN + 'on_intdown': plexpy.CONFIG.TWITTER_ON_INTDOWN, + 'on_extup': plexpy.CONFIG.TWITTER_ON_EXTUP, + 'on_intup': plexpy.CONFIG.TWITTER_ON_INTUP }, {'name': 'IFTTT', 'id': AGENT_IDS['IFTTT'], @@ -233,7 +255,9 @@ def available_notification_agents(): 'on_watched': plexpy.CONFIG.IFTTT_ON_WATCHED, 'on_created': plexpy.CONFIG.IFTTT_ON_CREATED, 'on_extdown': plexpy.CONFIG.IFTTT_ON_EXTDOWN, - 'on_intdown': plexpy.CONFIG.IFTTT_ON_INTDOWN + 'on_intdown': plexpy.CONFIG.IFTTT_ON_INTDOWN, + 'on_extup': plexpy.CONFIG.IFTTT_ON_EXTUP, + 'on_intup': plexpy.CONFIG.IFTTT_ON_INTUP }, {'name': 'Telegram', 'id': AGENT_IDS['Telegram'], @@ -248,7 +272,9 @@ def available_notification_agents(): 'on_watched': plexpy.CONFIG.TELEGRAM_ON_WATCHED, 'on_created': plexpy.CONFIG.TELEGRAM_ON_CREATED, 'on_extdown': plexpy.CONFIG.TELEGRAM_ON_EXTDOWN, - 'on_intdown': plexpy.CONFIG.TELEGRAM_ON_INTDOWN + 'on_intdown': plexpy.CONFIG.TELEGRAM_ON_INTDOWN, + 'on_extup': plexpy.CONFIG.TELEGRAM_ON_EXTUP, + 'on_intup': plexpy.CONFIG.TELEGRAM_ON_INTUP } ] @@ -268,7 +294,9 @@ def available_notification_agents(): 'on_watched': plexpy.CONFIG.OSX_NOTIFY_ON_WATCHED, 'on_created': plexpy.CONFIG.OSX_NOTIFY_ON_CREATED, 'on_extdown': plexpy.CONFIG.OSX_NOTIFY_ON_EXTDOWN, - 'on_intdown': plexpy.CONFIG.OSX_NOTIFY_ON_INTDOWN + 'on_intdown': plexpy.CONFIG.OSX_NOTIFY_ON_INTDOWN, + 'on_extup': plexpy.CONFIG.OSX_NOTIFY_ON_EXTUP, + 'on_intup': plexpy.CONFIG.OSX_NOTIFY_ON_INTUP }) return agents diff --git a/plexpy/webserve.py b/plexpy/webserve.py index fb260fe6..ff52d69e 100644 --- a/plexpy/webserve.py +++ b/plexpy/webserve.py @@ -467,6 +467,10 @@ class WebInterface(object): "notify_on_extdown_body_text": plexpy.CONFIG.NOTIFY_ON_EXTDOWN_BODY_TEXT, "notify_on_intdown_subject_text": plexpy.CONFIG.NOTIFY_ON_INTDOWN_SUBJECT_TEXT, "notify_on_intdown_body_text": plexpy.CONFIG.NOTIFY_ON_INTDOWN_BODY_TEXT, + "notify_on_extup_subject_text": plexpy.CONFIG.NOTIFY_ON_EXTUP_SUBJECT_TEXT, + "notify_on_extup_body_text": plexpy.CONFIG.NOTIFY_ON_EXTUP_BODY_TEXT, + "notify_on_intup_subject_text": plexpy.CONFIG.NOTIFY_ON_INTUP_SUBJECT_TEXT, + "notify_on_intup_body_text": plexpy.CONFIG.NOTIFY_ON_INTUP_BODY_TEXT, "home_stats_length": plexpy.CONFIG.HOME_STATS_LENGTH, "home_stats_type": checked(plexpy.CONFIG.HOME_STATS_TYPE), "home_stats_count": plexpy.CONFIG.HOME_STATS_COUNT, From f9f65eae5330d02ffe854bc49ec5a5b3f3c4029a Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Sun, 13 Dec 2015 09:36:54 -0800 Subject: [PATCH 10/15] Add duration to history table footer --- .../default/js/tables/history_table.js | 5 ++- plexpy/datafactory.py | 36 +++++++++++++++++-- plexpy/helpers.py | 20 ++++++----- plexpy/webserve.py | 2 +- 4 files changed, 50 insertions(+), 13 deletions(-) diff --git a/data/interfaces/default/js/tables/history_table.js b/data/interfaces/default/js/tables/history_table.js index 1cafd218..516ed0bc 100644 --- a/data/interfaces/default/js/tables/history_table.js +++ b/data/interfaces/default/js/tables/history_table.js @@ -18,7 +18,7 @@ history_table_options = { "lengthMenu":"Show _MENU_ entries per page", "info":"Showing _START_ to _END_ of _TOTAL_ history items", "infoEmpty":"Showing 0 to 0 of 0 entries", - "infoFiltered":"(filtered from _MAX_ total entries)", + "infoFiltered":"", "emptyTable": "No data in table" }, "pagingType": "bootstrap", @@ -265,6 +265,9 @@ history_table_options = { createChildTable(this, rowData) } }); + + $("#history_table_info").append(''); }, "preDrawCallback": function(settings) { var msg = " Fetching rows..."; diff --git a/plexpy/datafactory.py b/plexpy/datafactory.py index 6dcdef26..4d7a4fdb 100644 --- a/plexpy/datafactory.py +++ b/plexpy/datafactory.py @@ -45,7 +45,7 @@ class DataFactory(object): 'platform', 'player', 'ip_address', - 'session_history_metadata.media_type', + 'session_history.media_type', 'session_history_metadata.rating_key', 'session_history_metadata.parent_rating_key', 'session_history_metadata.grandparent_rating_key', @@ -80,7 +80,7 @@ class DataFactory(object): ['session_history.id', 'session_history_media_info.id']], kwargs=kwargs) except: - logger.warn("Unable to execute database query.") + logger.warn("Unable to execute database query for get_history.") return {'recordsFiltered': 0, 'recordsTotal': 0, 'draw': 0, @@ -89,8 +89,13 @@ class DataFactory(object): history = query['result'] + filter_duration = 0 + total_duration = self.get_total_duration(custom_where=custom_where) + rows = [] for item in history: + filter_duration += int(item['duration']) + if item["media_type"] == 'episode' and item["parent_thumb"]: thumb = item["parent_thumb"] elif item["media_type"] == 'episode': @@ -144,7 +149,9 @@ class DataFactory(object): dict = {'recordsFiltered': query['filteredCount'], 'recordsTotal': query['totalCount'], 'data': rows, - 'draw': query['draw'] + 'draw': query['draw'], + 'filter_duration': helpers.human_duration(filter_duration, sig='dhm'), + 'total_duration': helpers.human_duration(total_duration, sig='dhm') } return dict @@ -1083,3 +1090,26 @@ class DataFactory(object): ip_address = item['ip_address'] return ip_address + + def get_total_duration(self, custom_where=None): + monitor_db = database.MonitorDatabase() + + # Split up custom wheres + if custom_where: + where = 'WHERE ' + ' AND '.join([w[0] + ' = "' + w[1] + '"' for w in custom_where]) + else: + where = '' + + 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 + result = monitor_db.select(query) + except: + logger.warn("Unable to execute database query for get_total_duration.") + return None + + for item in result: + total_duration = item['total_duration'] + + return total_duration \ No newline at end of file diff --git a/plexpy/helpers.py b/plexpy/helpers.py index b4f86f49..6319f423 100644 --- a/plexpy/helpers.py +++ b/plexpy/helpers.py @@ -146,7 +146,7 @@ def now(): now = datetime.datetime.now() return now.strftime("%Y-%m-%d %H:%M:%S") -def human_duration(s): +def human_duration(s, sig='dhms'): hd = '' @@ -157,20 +157,24 @@ def human_duration(s): s = int(((s % 84600) % 3600) % 60) hd_list = [] - if d > 0: + if sig >= 'd' and d > 0: + d = d + 1 if sig == 'd' and h >= 12 else d hd_list.append(str(d) + ' days') - if h > 0: + + if sig >= 'dh' and h > 0: + h = h + 1 if sig == 'dh' and m >= 30 else h hd_list.append(str(h) + ' hrs') - if m > 0: + + if sig >= 'dhm' and m > 0: + m = m + 1 if sig == 'dhm' and s >= 30 else m hd_list.append(str(m) + ' mins') - if s > 0: + + if sig >= 'dhms' and s > 0: hd_list.append(str(s) + ' secs') hd = ' '.join(hd_list) - return hd - else: - return hd + return hd def get_age(date): diff --git a/plexpy/webserve.py b/plexpy/webserve.py index ff52d69e..2251b85e 100644 --- a/plexpy/webserve.py +++ b/plexpy/webserve.py @@ -617,7 +617,7 @@ class WebInterface(object): if 'media_type' in kwargs: media_type = kwargs.get('media_type', "") if media_type != 'all': - custom_where.append(['session_history_metadata.media_type', media_type]) + custom_where.append(['session_history.media_type', media_type]) data_factory = datafactory.DataFactory() history = data_factory.get_history(kwargs=kwargs, custom_where=custom_where, grouping=grouping, watched_percent=watched_percent) From c6cc2b88311744625510c61611943b3943021e03 Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Sun, 13 Dec 2015 11:35:33 -0800 Subject: [PATCH 11/15] Save graph type/days/tab to config file * Change input for graph days range --- data/interfaces/default/css/plexpy.css | 9 + data/interfaces/default/graphs.html | 260 ++++++++++++++----------- plexpy/config.py | 3 + plexpy/webserve.py | 17 ++ 4 files changed, 173 insertions(+), 116 deletions(-) diff --git a/data/interfaces/default/css/plexpy.css b/data/interfaces/default/css/plexpy.css index ef06c9dc..5673d854 100644 --- a/data/interfaces/default/css/plexpy.css +++ b/data/interfaces/default/css/plexpy.css @@ -2545,4 +2545,13 @@ table[id^='history_child'] thead th { } .notification-params tr:nth-child(even) td { background-color: rgba(255,255,255,0.010); +} + +#days-selection label { + margin-bottom: 0; +} +#graph-days { + margin: 0; + width: 75px; + height: 34px; } \ No newline at end of file diff --git a/data/interfaces/default/graphs.html b/data/interfaces/default/graphs.html index 8f114b67..9143bb5a 100644 --- a/data/interfaces/default/graphs.html +++ b/data/interfaces/default/graphs.html @@ -13,110 +13,132 @@
    + % if config['graph_tab'] != 'tabs-2' and config['graph_tab'] != 'tabs-3':
    -
    -
    -

    Daily Play count Last 30 days

    -

    - The total play count or duration of tv, movies, and music played per day. Click a graph point to open up a list of items played for that specific date. -

    -
    -
    -
    Loading chart...
    -
    + % else: +
    + % endif +
    +
    +

    Daily Play count Last 30 days

    +

    + The total play count or duration of tv, movies, and music played per day. Click a graph point to open up a list of items played for that specific date. +

    +
    +
    +
    Loading chart...
    +
    +
    +
    +
    +
    +
    +
    +

    Play count by day of week Last 30 days

    +

    + The combined total of tv, movies, and music played per day of the week. +

    +
    +
    +
    + Loading chart... +
    +
    +
    +
    +
    +
    +

    Play count by hour of day Last 30 days

    +

    + The combined total of tv, movies, and music played per hour of the day. +

    +
    +
    +
    + Loading chart... +
    +
    +
    +
    +
    +
    +
    +
    +

    Play count by top 10 platforms Last 30 days

    +

    + The combined total of tv, movies, and music played by top 10 most active platforms. +

    +
    +
    +
    + Loading chart... +
    +
    +
    +
    +
    +
    +

    Play count by top 10 users Last 30 days

    +

    + The combined total of tv, movies, and music played by top 10 most active users. +

    +
    +
    +
    + Loading chart... +
    +
    +
    -
    -
    -

    Play count by day of week Last 30 days

    -

    - The combined total of tv, movies, and music played per day of the week. -

    -
    -
    -
    Loading chart... -
    -
    -
    -
    -
    -
    -

    Play count by hour of day Last 30 days

    -

    - The combined total of tv, movies, and music played per hour of the day. -

    -
    -
    -
    Loading chart... -
    -
    -
    -
    -
    -
    -
    -
    -

    Play count by top 10 platforms Last 30 days

    -

    - The combined total of tv, movies, and music played by top 10 most active platforms. -

    -
    -
    -
    Loading chart... -
    -
    -
    -
    -
    -
    -

    Play count by top 10 users Last 30 days

    -

    - The combined total of tv, movies, and music played by top 10 most active users. -

    -
    -
    -
    Loading chart... -
    -
    -
    -
    -
    -
    -
    + % if config['graph_tab'] == 'tabs-2': +
    + % else:
    + % endif

    Daily Stream type breakdown Last 30 days

    @@ -189,7 +211,11 @@
    + % if config['graph_tab'] == 'tabs-3': +
    + % else:
    + % endif

    Plays by Month Last 12 months

    @@ -263,36 +289,12 @@