From 048b31c87a2522aaa519435158a6054101dc9b02 Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Mon, 14 Sep 2015 20:59:04 -0700 Subject: [PATCH 01/18] Fix music visible on graph only if "Log music" is enabled --- data/interfaces/default/graphs.html | 8 ++++++++ plexpy/webserve.py | 7 ++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/data/interfaces/default/graphs.html b/data/interfaces/default/graphs.html index af6ed442..8f114b67 100644 --- a/data/interfaces/default/graphs.html +++ b/data/interfaces/default/graphs.html @@ -294,6 +294,8 @@ $('a[data-toggle=tab][href=' + current_tab + ']').trigger('click'); } + var music_visible = (${config['music_logging_enable']} == 1 ? true : false); + function loadGraphsTab1(time_range, yaxis) { setGraphFormat(yaxis); @@ -319,6 +321,7 @@ hc_plays_by_day_options.yAxis.min = 0; hc_plays_by_day_options.xAxis.categories = dateArray; hc_plays_by_day_options.series = data.series; + hc_plays_by_day_options.series[2].visible = music_visible; var hc_plays_by_day = new Highcharts.Chart(hc_plays_by_day_options); } }); @@ -331,6 +334,7 @@ success: function(data) { hc_plays_by_dayofweek_options.xAxis.categories = data.categories; hc_plays_by_dayofweek_options.series = data.series; + hc_plays_by_dayofweek_options.series[2].visible = music_visible; var hc_plays_by_dayofweek = new Highcharts.Chart(hc_plays_by_dayofweek_options); } }); @@ -343,6 +347,7 @@ success: function(data) { hc_plays_by_hourofday_options.xAxis.categories = data.categories; hc_plays_by_hourofday_options.series = data.series; + hc_plays_by_hourofday_options.series[2].visible = music_visible; var hc_plays_by_hourofday = new Highcharts.Chart(hc_plays_by_hourofday_options); } }); @@ -355,6 +360,7 @@ success: function(data) { hc_plays_by_platform_options.xAxis.categories = data.categories; hc_plays_by_platform_options.series = data.series; + hc_plays_by_platform_options.series[2].visible = music_visible; var hc_plays_by_platform = new Highcharts.Chart(hc_plays_by_platform_options); } }); @@ -367,6 +373,7 @@ success: function(data) { hc_plays_by_user_options.xAxis.categories = data.categories; hc_plays_by_user_options.series = data.series; + hc_plays_by_user_options.series[2].visible = music_visible; var hc_plays_by_user = new Highcharts.Chart(hc_plays_by_user_options); } }); @@ -462,6 +469,7 @@ hc_plays_by_month_options.yAxis.min = 0; hc_plays_by_month_options.xAxis.categories = data.categories; hc_plays_by_month_options.series = data.series; + hc_plays_by_month_options.series[2].visible = music_visible; var hc_plays_by_month = new Highcharts.Chart(hc_plays_by_month_options); } }); diff --git a/plexpy/webserve.py b/plexpy/webserve.py index 6bb6f9ac..681c0ccc 100644 --- a/plexpy/webserve.py +++ b/plexpy/webserve.py @@ -144,7 +144,12 @@ class WebInterface(object): @cherrypy.expose def graphs(self): - return serve_template(templatename="graphs.html", title="Graphs") + + config = { + "music_logging_enable": plexpy.CONFIG.MUSIC_LOGGING_ENABLE + } + + return serve_template(templatename="graphs.html", title="Graphs", config=config) @cherrypy.expose def sync(self): From 7170dbd800fb8195fbd5369b6380d9e2ac64433e Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Wed, 16 Sep 2015 01:13:08 -0700 Subject: [PATCH 02/18] Fix home stats Last Watched to use watched percent specified in settings --- data/interfaces/default/index.html | 12 +++++++++--- plexpy/datafactory.py | 11 ++++++++--- plexpy/webserve.py | 8 ++++++-- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/data/interfaces/default/index.html b/data/interfaces/default/index.html index b73bdbe0..ed983ca6 100644 --- a/data/interfaces/default/index.html +++ b/data/interfaces/default/index.html @@ -82,12 +82,15 @@ currentActivity(); setInterval(currentActivity, 15000); - function getHomeStats(days, stat_type, stat_count) { + function getHomeStats(days, stat_type, stat_count, notify_watched_percent) { $.ajax({ url: 'home_stats', cache: false, async: true, - data: {time_range: days, stat_type: stat_type, stat_count: stat_count}, + data: {time_range: days, + stat_type: stat_type, + stat_count: stat_count, + notify_watched_percent: notify_watched_percent}, complete: function(xhr, status) { $("#home-stats").html(xhr.responseText); } @@ -165,7 +168,10 @@ } }); - 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']}, + ${config['notify_watched_percent']}); getLibraryStatsHeader(); getLibraryStats(); diff --git a/plexpy/datafactory.py b/plexpy/datafactory.py index 4a988480..5412922d 100644 --- a/plexpy/datafactory.py +++ b/plexpy/datafactory.py @@ -131,7 +131,7 @@ class DataFactory(object): return dict - def get_home_stats(self, time_range='30', stat_type='0', stat_count='5'): + def get_home_stats(self, time_range='30', stat_type='0', stat_count='5', notify_watched_percent='85'): monitor_db = database.MonitorDatabase() if not time_range.isdigit(): @@ -432,7 +432,11 @@ class DataFactory(object): 'session_history_metadata.thumb, ' \ 'session_history_metadata.grandparent_thumb, ' \ 'MAX(session_history.started) as last_watch, ' \ - 'session_history.player as platform ' \ + 'session_history.player as platform, ' \ + '((CASE WHEN session_history.view_offset IS NULL THEN 0.1 ELSE \ + session_history.view_offset * 1.0 END) / \ + (CASE WHEN session_history_metadata.duration IS NULL THEN 1.0 ELSE \ + session_history_metadata.duration * 1.0 END) * 100) as percent_complete ' \ 'FROM session_history_metadata ' \ 'JOIN session_history ON session_history_metadata.id = session_history.id ' \ 'LEFT OUTER JOIN users ON session_history.user_id = users.user_id ' \ @@ -440,9 +444,10 @@ class DataFactory(object): '>= datetime("now", "-%s days", "localtime") ' \ 'AND (session_history_metadata.media_type = "movie" ' \ 'OR session_history_metadata.media_type = "episode") ' \ + 'AND percent_complete >= %s ' \ 'GROUP BY session_history_metadata.full_title ' \ 'ORDER BY last_watch DESC ' \ - 'LIMIT %s' % (time_range, stat_count) + 'LIMIT %s' % (time_range, notify_watched_percent, stat_count) result = monitor_db.select(query) except: logger.warn("Unable to execute database query.") diff --git a/plexpy/webserve.py b/plexpy/webserve.py index 681c0ccc..de5af414 100644 --- a/plexpy/webserve.py +++ b/plexpy/webserve.py @@ -69,6 +69,7 @@ class WebInterface(object): "home_stats_type": plexpy.CONFIG.HOME_STATS_TYPE, "home_stats_count": plexpy.CONFIG.HOME_STATS_COUNT, "pms_identifier": plexpy.CONFIG.PMS_IDENTIFIER, + "notify_watched_percent": plexpy.CONFIG.NOTIFY_WATCHED_PERCENT } return serve_template(templatename="index.html", title="Home", config=config) @@ -121,9 +122,12 @@ class WebInterface(object): return json.dumps(formats) @cherrypy.expose - def home_stats(self, time_range='30', stat_type='0', stat_count='5', **kwargs): + def home_stats(self, time_range='30', stat_type='0', stat_count='5', notify_watched_percent='85', **kwargs): data_factory = datafactory.DataFactory() - stats_data = data_factory.get_home_stats(time_range=time_range, stat_type=stat_type, stat_count=stat_count) + stats_data = data_factory.get_home_stats(time_range=time_range, + stat_type=stat_type, + stat_count=stat_count, + notify_watched_percent=notify_watched_percent) return serve_template(templatename="home_stats.html", title="Stats", data=stats_data) From 1f3a238ab28f9bcbdbaf843aaa9e7df2e3639711 Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Wed, 16 Sep 2015 15:42:45 -0700 Subject: [PATCH 03/18] Fix logic to add "Direct Stream" state to current activity --- data/interfaces/default/current_activity.html | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/data/interfaces/default/current_activity.html b/data/interfaces/default/current_activity.html index 04632966..00a96048 100644 --- a/data/interfaces/default/current_activity.html +++ b/data/interfaces/default/current_activity.html @@ -119,6 +119,8 @@ DOCUMENTATION :: END % if a['type'] == 'track': % if a['audio_decision'] == 'direct play': Stream  Direct Play + % elif a['audio_decision'] == 'copy': + Stream  Direct Stream % else: Stream  Transcoding (Speed: ${a['transcode_speed']}) @@ -136,8 +138,10 @@ DOCUMENTATION :: END Audio  Transcode (${a['transcode_audio_codec']}) (${a['transcode_audio_channels']}ch) % endif % elif a['type'] == 'episode' or a['type'] == 'movie' or a['type'] == 'clip': - % if a['video_decision'] == 'direct play': + % if a['video_decision'] == 'direct play' and a['audio_decision'] == 'direct play': Stream  Direct Play + % elif a['video_decision'] == 'copy' and a['audio_decision'] == 'copy': + Stream  Direct Stream % else: Stream  Transcoding (Speed: ${a['transcode_speed']}) @@ -165,6 +169,8 @@ DOCUMENTATION :: END % elif a['type'] == 'photo': % if a['video_decision'] == 'direct play': Stream  Direct Play + % elif a['video_decision'] == 'copy': + Stream  Direct Stream % else: Stream   Transcoding From 483f5825db400d96e28cf29f3e8854730438bdd2 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 17 Sep 2015 21:04:10 +0200 Subject: [PATCH 04/18] Add check for webbrowser import which may not be available in all Python builds (like certain NAS devices). --- plexpy/__init__.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/plexpy/__init__.py b/plexpy/__init__.py index ad02333c..fc9639c4 100644 --- a/plexpy/__init__.py +++ b/plexpy/__init__.py @@ -17,11 +17,16 @@ import os import sys import subprocess import threading -import webbrowser import sqlite3 import cherrypy import datetime import uuid +# Some cut down versions of Python may not include this module and it's not critical for us +try: + import webbrowser + no_browser = False +except ImportError: + no_browser = True from apscheduler.schedulers.background import BackgroundScheduler from apscheduler.triggers.interval import IntervalTrigger @@ -228,18 +233,19 @@ def daemonize(): def launch_browser(host, port, root): - if host == '0.0.0.0': - host = 'localhost' + if not no_browser: + if host == '0.0.0.0': + host = 'localhost' - if CONFIG.ENABLE_HTTPS: - protocol = 'https' - else: - protocol = 'http' + if CONFIG.ENABLE_HTTPS: + protocol = 'https' + else: + protocol = 'http' - try: - webbrowser.open('%s://%s:%i%s' % (protocol, host, port, root)) - except Exception as e: - logger.error('Could not launch browser: %s', e) + try: + webbrowser.open('%s://%s:%i%s' % (protocol, host, port, root)) + except Exception as e: + logger.error('Could not launch browser: %s', e) def initialize_scheduler(): From 6eec4d1ca6d02f1f6644aeefd2a4f5d4de474e53 Mon Sep 17 00:00:00 2001 From: Tim Date: Sat, 19 Sep 2015 00:57:24 +0200 Subject: [PATCH 05/18] Some Twitter love. Resolves Issue #47. --- .../default/notification_config.html | 34 ++++++++- plexpy/config.py | 8 +- plexpy/notifiers.py | 75 ++++++++++++++----- 3 files changed, 95 insertions(+), 22 deletions(-) diff --git a/data/interfaces/default/notification_config.html b/data/interfaces/default/notification_config.html index 58f6436e..41e4a341 100644 --- a/data/interfaces/default/notification_config.html +++ b/data/interfaces/default/notification_config.html @@ -9,9 +9,9 @@
-
+
% for item in data: - % if item['input_type'] != 'checkbox': + % if item['input_type'] == 'text' or item['input_type'] == 'number' or item['input_type'] == 'password':
@@ -20,6 +20,11 @@ % endif

${item['description']}

+ % elif item['input_type'] == 'button': +
+ +
+

${item['description']}

% elif item['input_type'] == 'checkbox':
@@ -53,4 +65,20 @@ doAjaxCall('set_notification_config',$(this),'tabs',true); return false; }); + + $('#twitterStep1').click(function () { + $.get("/twitterStep1", function (data) {window.open(data); }) + .done(function () { $('#ajaxMsg').html("
Confirm Authorization. Check pop-up blocker if no response.
"); }); + $('#ajaxMsg').addClass('success').fadeIn().delay(3000).fadeOut(); + }); + $('#twitterStep2').click(function () { + var twitter_key = $("#twitter_key").val(); + $.get("/twitterStep2", {'key': twitter_key}, function (data) { $('#ajaxMsg').html("
"+data+"
"); }); + $('#ajaxMsg').addClass('success').fadeIn().delay(3000).fadeOut(); + }); + $('#testTwitter').click(function () { + $.get("/testTwitter", + function (data) { $('#ajaxMsg').html("
"+data+"
"); }); + $('#ajaxMsg').addClass('success').fadeIn().delay(3000).fadeOut(); + }); diff --git a/plexpy/config.py b/plexpy/config.py index 51145685..9d41b67a 100644 --- a/plexpy/config.py +++ b/plexpy/config.py @@ -194,8 +194,14 @@ _CONFIG_DEFINITIONS = { 'TV_NOTIFY_ON_PAUSE': (int, 'Monitoring', 0), 'TWITTER_ENABLED': (int, 'Twitter', 0), 'TWITTER_PASSWORD': (str, 'Twitter', ''), - 'TWITTER_PREFIX': (str, 'Twitter', 'Headphones'), + 'TWITTER_PREFIX': (str, 'Twitter', 'PlexPy'), 'TWITTER_USERNAME': (str, 'Twitter', ''), + 'TWITTER_ON_PLAY': (int, 'Twitter', 0), + 'TWITTER_ON_STOP': (int, 'Twitter', 0), + 'TWITTER_ON_PAUSE': (int, 'Twitter', 0), + 'TWITTER_ON_RESUME': (int, 'Twitter', 0), + 'TWITTER_ON_BUFFER': (int, 'Twitter', 0), + 'TWITTER_ON_WATCHED': (int, 'Twitter', 0), 'UPDATE_DB_INTERVAL': (int, 'General', 24), 'VERIFY_SSL_CERT': (bool_int, 'Advanced', 1), 'VIDEO_LOGGING_ENABLE': (int, 'Monitoring', 1), diff --git a/plexpy/notifiers.py b/plexpy/notifiers.py index 2c348b5a..6d3abda1 100644 --- a/plexpy/notifiers.py +++ b/plexpy/notifiers.py @@ -49,7 +49,8 @@ AGENT_IDS = {"Growl": 0, "Pushover": 7, "OSX Notify": 8, "Boxcar2": 9, - "Email": 10} + "Email": 10, + "Twitter": 11} def available_notification_agents(): agents = [{'name': 'Growl', @@ -171,6 +172,18 @@ def available_notification_agents(): 'on_resume': plexpy.CONFIG.EMAIL_ON_RESUME, 'on_buffer': plexpy.CONFIG.EMAIL_ON_BUFFER, 'on_watched': plexpy.CONFIG.EMAIL_ON_WATCHED + }, + {'name': 'Twitter', + 'id': AGENT_IDS['Twitter'], + 'config_prefix': 'twitter', + 'has_config': True, + 'state': checked(plexpy.CONFIG.TWITTER_ENABLED), + 'on_play': plexpy.CONFIG.TWITTER_ON_PLAY, + 'on_stop': plexpy.CONFIG.TWITTER_ON_STOP, + 'on_pause': plexpy.CONFIG.TWITTER_ON_PAUSE, + 'on_resume': plexpy.CONFIG.TWITTER_ON_RESUME, + 'on_buffer': plexpy.CONFIG.TWITTER_ON_BUFFER, + 'on_watched': plexpy.CONFIG.TWITTER_ON_WATCHED } ] @@ -229,6 +242,9 @@ def get_notification_agent_config(config_id): elif config_id == 10: email = Email() return email.return_config_options() + elif config_id == 11: + tweet = TwitterNotifier() + return tweet.return_config_options() else: return [] else: @@ -271,6 +287,9 @@ def send_notification(config_id, subject, body): elif config_id == 10: email = Email() email.notify(subject=subject, message=body) + elif config_id == 11: + tweet = TwitterNotifier() + tweet.notify(subject=subject, message=body) else: logger.debug(u"PlexPy Notifier :: Unknown agent id received.") else: @@ -912,19 +931,17 @@ class TwitterNotifier(object): SIGNIN_URL = 'https://api.twitter.com/oauth/authenticate' def __init__(self): - self.consumer_key = "oYKnp2ddX5gbARjqX8ZAAg" - self.consumer_secret = "A4Xkw9i5SjHbTk7XT8zzOPqivhj9MmRDR9Qn95YA9sk" + self.consumer_key = "2LdJKXHDUwJtjYBsdwJisIOsh" + self.consumer_secret = "QWbUcZzAIiL4zbDCIhy2EdUkV8yEEav3qMdo5y3FugxCFelWrA" - def notify_snatch(self, title): - if plexpy.CONFIG.TWITTER_ONSNATCH: - self._notifyTwitter(common.notifyStrings[common.NOTIFY_SNATCH] + ': ' + title + ' at ' + helpers.now()) - - def notify_download(self, title): - if plexpy.CONFIG.TWITTER_ENABLED: - self._notifyTwitter(common.notifyStrings[common.NOTIFY_DOWNLOAD] + ': ' + title + ' at ' + helpers.now()) + def notify(self, subject, message): + if not subject or not message: + return + else: + self._send_tweet(subject + ': ' + message) def test_notify(self): - return self._notifyTwitter("This is a test notification from PlexPy at " + helpers.now(), force=True) + return self._send_tweet("This is a test notification from PlexPy at " + helpers.now()) def _get_authorization(self): @@ -979,7 +996,6 @@ class TwitterNotifier(object): return True def _send_tweet(self, message=None): - username = self.consumer_key password = self.consumer_secret access_token_key = plexpy.CONFIG.TWITTER_USERNAME @@ -997,13 +1013,36 @@ class TwitterNotifier(object): return True - def _notifyTwitter(self, message='', force=False): - prefix = plexpy.CONFIG.TWITTER_PREFIX + def return_config_options(self): + config_option = [{'label': 'Request Authorisation', + 'value': 'Request Authorisation', + 'name': 'twitterStep1', + 'description': 'Step 1: Click Request button above. (Ensure you allow the browser pop-up).', + 'input_type': 'button' + }, + {'label': 'Authorisation Key', + 'value': '', + 'name': 'twitter_key', + 'description': 'Step 2: Input the authorisation key you received from Step 1.', + 'input_type': 'text' + }, + {'label': 'Verify Key', + 'value': 'Verify Key', + 'name': 'twitterStep2', + 'description': 'Step 3: Verify the key.', + 'input_type': 'button' + }, + {'label': 'Test Twitter', + 'value': 'Test Twitter', + 'name': 'testTwitter', + 'description': 'Test if Twitter notifications are working. See logs for troubleshooting.', + 'input_type': 'button' + }, + {'input_type': 'nosave' + } + ] - if not plexpy.CONFIG.TWITTER_ENABLED and not force: - return False - - return self._send_tweet(prefix + ": " + message) + return config_option class OSX_NOTIFY(object): From 20056718dbe7475d09ab76be8fca743943c1577f Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Fri, 18 Sep 2015 18:52:46 -0700 Subject: [PATCH 06/18] Add setting to selectively hide watch statistic cards on homepage --- data/interfaces/default/css/plexpy.css | 27 ++++++++++ data/interfaces/default/index.html | 2 + data/interfaces/default/settings.html | 75 +++++++++++++++++++------- plexpy/config.py | 1 + plexpy/webserve.py | 5 ++ 5 files changed, 91 insertions(+), 19 deletions(-) diff --git a/data/interfaces/default/css/plexpy.css b/data/interfaces/default/css/plexpy.css index ff44dbea..649f36ac 100644 --- a/data/interfaces/default/css/plexpy.css +++ b/data/interfaces/default/css/plexpy.css @@ -33,6 +33,33 @@ select.input-sm { color: #999; outline: none; } +select[multiple] { + height: 125px; + margin: 5px 0 5px 0; + color: #fff; + border: 0px solid #444; + background: #555; + padding: 2px 2px; + background-color: #555; + border-radius: 3px; + transition: background-color .3s; +} +select[multiple]:focus { + outline: 0; + outline: thin dotted \9; + color: #555; + background-color: #fff; + transition: background-color .3s; +} +select[multiple]:focus::-webkit-scrollbar-thumb { + background-color: rgba(0,0,0,.15); +} +select[multiple] option { + padding: 6px 10px; + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + border-radius: 2px; +} img { -webkit-box-sizing: content-box; -moz-box-sizing: content-box; diff --git a/data/interfaces/default/index.html b/data/interfaces/default/index.html index ed983ca6..fab5d5b3 100644 --- a/data/interfaces/default/index.html +++ b/data/interfaces/default/index.html @@ -16,6 +16,7 @@
+ % if config['home_stats_cards'] > 'watch_statistics':
@@ -27,6 +28,7 @@
+ % endif
diff --git a/data/interfaces/default/settings.html b/data/interfaces/default/settings.html index d13e8700..ee37f5fd 100644 --- a/data/interfaces/default/settings.html +++ b/data/interfaces/default/settings.html @@ -34,14 +34,15 @@ available_notification_agents = notifiers.available_notification_agents()
@@ -83,10 +84,33 @@ available_notification_agents = notifiers.available_notification_agents()

Set your preferred time format. Click here to see the parameter list.

+

+
+
-

Homepage Statistics

+

Watch Statistics

+
+ +

Select the cards to show in the watch statistics on the home page.

+
+
+ +
+
+
@@ -95,7 +119,7 @@ available_notification_agents = notifiers.available_notification_agents()
-

Specify the number of days for the statistics on the home page. Default is 30 days.

+

Specify the number of days for the watch statistics on the home page. Default is 30 days.

@@ -105,7 +129,7 @@ available_notification_agents = notifiers.available_notification_agents()
-

Specify the number of items to show in the top lists for the statistics on the home page. Max is 10 items, default is 5 items, 0 to disable.

+

Specify the number of items to show in the top lists for the watch statistics on the home page. Max is 10 items, default is 5 items, 0 to disable.

-
+

Web Interface

@@ -164,7 +188,7 @@ available_notification_agents = notifiers.available_notification_agents()

-
+

Authentication

@@ -216,7 +240,7 @@ available_notification_agents = notifiers.available_notification_agents()

-
+

Plex Media Server

@@ -272,7 +296,7 @@ available_notification_agents = notifiers.available_notification_agents()
-
+

Plex.tv Authentication

@@ -315,7 +339,7 @@ available_notification_agents = notifiers.available_notification_agents()

-
+

Extra Settings

@@ -334,7 +358,7 @@ available_notification_agents = notifiers.available_notification_agents()

-
+

Monitoring Settings

@@ -416,7 +440,7 @@ available_notification_agents = notifiers.available_notification_agents()

-
+

Global Notification Toggles

@@ -566,7 +590,7 @@ available_notification_agents = notifiers.available_notification_agents()

-
+

Notification Agents

@@ -1178,6 +1202,19 @@ $(document).ready(function() { } var accordion = new Accordion($('#accordion'), false); + + var cards = "${config['home_stats_cards']}".split(/[\s,]+/); + cards.forEach(function (item) { + $('#card-'+item).prop('selected', !$(this).prop('selected')); + }); + $('#home_stats_cards').on('mousedown', function(e) { + e.preventDefault(); + var scroll = this.scrollTop; + e.target.selected = !e.target.selected; + this.scrollTop = scroll; + }).on('mousemove', function(e) { + e.preventDefault() + }); }); diff --git a/plexpy/config.py b/plexpy/config.py index 9d41b67a..7288ac34 100644 --- a/plexpy/config.py +++ b/plexpy/config.py @@ -85,6 +85,7 @@ _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', 'top_tv, popular_tv, top_movies, popular_movies, top_music, popular_music, top_users, top_platforms, last_watched'), 'HTTPS_CERT': (str, 'General', ''), 'HTTPS_KEY': (str, 'General', ''), 'HTTP_HOST': (str, 'General', '0.0.0.0'), diff --git a/plexpy/webserve.py b/plexpy/webserve.py index de5af414..c1507dee 100644 --- a/plexpy/webserve.py +++ b/plexpy/webserve.py @@ -472,6 +472,7 @@ class WebInterface(object): "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, + "home_stats_cards": plexpy.CONFIG.HOME_STATS_CARDS, "buffer_threshold": plexpy.CONFIG.BUFFER_THRESHOLD, "buffer_wait": plexpy.CONFIG.BUFFER_WAIT } @@ -524,6 +525,10 @@ class WebInterface(object): if kwargs['pms_ip'] != plexpy.CONFIG.PMS_IP: refresh_users = True + if 'home_stats_cards' in kwargs: + if kwargs['home_stats_cards'] != 'watch_statistics': + kwargs['home_stats_cards'] = ', '.join(kwargs['home_stats_cards']) + plexpy.CONFIG.process_kwargs(kwargs) # Write the config From 78f959d39a8c2d2d0b2a0e680c465c9d1fbf9232 Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Fri, 18 Sep 2015 18:54:48 -0700 Subject: [PATCH 07/18] Clean up passing unnecessary configs to homepage --- data/interfaces/default/index.html | 12 +++------ plexpy/datafactory.py | 43 +++++++++++++++--------------- plexpy/webserve.py | 20 +++++++++----- 3 files changed, 38 insertions(+), 37 deletions(-) diff --git a/data/interfaces/default/index.html b/data/interfaces/default/index.html index fab5d5b3..e18cfdf8 100644 --- a/data/interfaces/default/index.html +++ b/data/interfaces/default/index.html @@ -84,15 +84,12 @@ currentActivity(); setInterval(currentActivity, 15000); - function getHomeStats(days, stat_type, stat_count, notify_watched_percent) { + function getHomeStats(days) { $.ajax({ url: 'home_stats', cache: false, async: true, - data: {time_range: days, - stat_type: stat_type, - stat_count: stat_count, - notify_watched_percent: notify_watched_percent}, + data: { }, complete: function(xhr, status) { $("#home-stats").html(xhr.responseText); } @@ -170,10 +167,7 @@ } }); - getHomeStats(${config['home_stats_length']}, - ${config['home_stats_type']}, - ${config['home_stats_count']}, - ${config['notify_watched_percent']}); + getHomeStats(); getLibraryStatsHeader(); getLibraryStats(); diff --git a/plexpy/datafactory.py b/plexpy/datafactory.py index 5412922d..ebf7f339 100644 --- a/plexpy/datafactory.py +++ b/plexpy/datafactory.py @@ -131,19 +131,12 @@ class DataFactory(object): return dict - def get_home_stats(self, time_range='30', stat_type='0', stat_count='5', notify_watched_percent='85'): + def get_home_stats(self, time_range='30', stats_type='0', stats_count='5', stats_cards='', notify_watched_percent='85'): monitor_db = database.MonitorDatabase() - if not time_range.isdigit(): - time_range = '30' + sort_type = 'total_plays' if stats_type == '0' else 'total_duration' - sort_type = 'total_plays' if stat_type == '0' else 'total_duration' - - if not time_range.isdigit(): - stat_count = '5' - - # This actually determines the output order in the home page - stats_queries = ["top_tv", "popular_tv", "top_movies", "popular_movies", "top_users", "top_platforms", "last_watched"] + stats_queries = stats_cards.split(', ') home_stats = [] for stat in stats_queries: @@ -166,7 +159,7 @@ class DataFactory(object): '>= datetime("now", "-%s days", "localtime") ' \ 'AND session_history_metadata.media_type = "episode" ' \ 'GROUP BY session_history_metadata.grandparent_title ' \ - 'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stat_count) + 'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stats_count) result = monitor_db.select(query) except: logger.warn("Unable to execute database query.") @@ -202,6 +195,10 @@ class DataFactory(object): 'session_history_metadata.grandparent_rating_key, ' \ 'MAX(session_history.started) as last_watch, ' \ 'COUNT(session_history.id) as total_plays, ' \ + 'SUM(case when session_history.stopped > 0 ' \ + 'then (session_history.stopped - session_history.started) ' \ + ' - (case when session_history.paused_counter is NULL then 0 else session_history.paused_counter end) ' \ + 'else 0 end) as total_duration, ' \ 'session_history_metadata.grandparent_thumb ' \ 'FROM session_history_metadata ' \ 'JOIN session_history ON session_history_metadata.id = session_history.id ' \ @@ -209,8 +206,8 @@ class DataFactory(object): '>= datetime("now", "-%s days", "localtime") ' \ 'AND session_history_metadata.media_type = "episode" ' \ 'GROUP BY session_history_metadata.grandparent_title ' \ - 'ORDER BY users_watched DESC, total_plays DESC ' \ - 'LIMIT %s' % (time_range, stat_count) + 'ORDER BY users_watched DESC, %s DESC ' \ + 'LIMIT %s' % (time_range, sort_type, stats_count) result = monitor_db.select(query) except: logger.warn("Unable to execute database query.") @@ -222,7 +219,7 @@ class DataFactory(object): 'rating_key': item[3], 'last_play': item[4], 'total_plays': item[5], - 'grandparent_thumb': item[6], + 'grandparent_thumb': item[7], 'thumb': '', 'user': '', 'friendly_name': '', @@ -254,7 +251,7 @@ class DataFactory(object): '>= datetime("now", "-%s days", "localtime") ' \ 'AND session_history_metadata.media_type = "movie" ' \ 'GROUP BY session_history_metadata.full_title ' \ - 'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stat_count) + 'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stats_count) result = monitor_db.select(query) except: logger.warn("Unable to execute database query.") @@ -290,6 +287,10 @@ class DataFactory(object): 'session_history_metadata.rating_key, ' \ 'MAX(session_history.started) as last_watch, ' \ 'COUNT(session_history.id) as total_plays, ' \ + 'SUM(case when session_history.stopped > 0 ' \ + 'then (session_history.stopped - session_history.started) ' \ + ' - (case when session_history.paused_counter is NULL then 0 else session_history.paused_counter end) ' \ + 'else 0 end) as total_duration, ' \ 'session_history_metadata.thumb ' \ 'FROM session_history_metadata ' \ 'JOIN session_history ON session_history_metadata.id = session_history.id ' \ @@ -297,8 +298,8 @@ class DataFactory(object): '>= datetime("now", "-%s days", "localtime") ' \ 'AND session_history_metadata.media_type = "movie" ' \ 'GROUP BY session_history_metadata.full_title ' \ - 'ORDER BY users_watched DESC, total_plays DESC ' \ - 'LIMIT %s' % (time_range, stat_count) + 'ORDER BY users_watched DESC, %s DESC ' \ + 'LIMIT %s' % (time_range, sort_type, stats_count) result = monitor_db.select(query) except: logger.warn("Unable to execute database query.") @@ -311,7 +312,7 @@ class DataFactory(object): 'last_play': item[4], 'total_plays': item[5], 'grandparent_thumb': '', - 'thumb': item[6], + 'thumb': item[7], 'user': '', 'friendly_name': '', 'platform_type': '', @@ -343,7 +344,7 @@ class DataFactory(object): 'WHERE datetime(session_history.stopped, "unixepoch", "localtime") >= ' \ 'datetime("now", "-%s days", "localtime") '\ 'GROUP BY session_history.user_id ' \ - 'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stat_count) + 'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stats_count) result = monitor_db.select(query) except: logger.warn("Unable to execute database query.") @@ -391,7 +392,7 @@ class DataFactory(object): 'WHERE datetime(session_history.stopped, "unixepoch", "localtime") ' \ '>= datetime("now", "-%s days", "localtime") ' \ 'GROUP BY session_history.platform ' \ - 'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stat_count) + 'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stats_count) result = monitor_db.select(query) except: logger.warn("Unable to execute database query.") @@ -447,7 +448,7 @@ class DataFactory(object): 'AND percent_complete >= %s ' \ 'GROUP BY session_history_metadata.full_title ' \ 'ORDER BY last_watch DESC ' \ - 'LIMIT %s' % (time_range, notify_watched_percent, stat_count) + 'LIMIT %s' % (time_range, notify_watched_percent, stats_count) result = monitor_db.select(query) except: logger.warn("Unable to execute database query.") diff --git a/plexpy/webserve.py b/plexpy/webserve.py index c1507dee..42dd50e0 100644 --- a/plexpy/webserve.py +++ b/plexpy/webserve.py @@ -66,10 +66,8 @@ class WebInterface(object): def home(self): config = { "home_stats_length": plexpy.CONFIG.HOME_STATS_LENGTH, - "home_stats_type": plexpy.CONFIG.HOME_STATS_TYPE, - "home_stats_count": plexpy.CONFIG.HOME_STATS_COUNT, - "pms_identifier": plexpy.CONFIG.PMS_IDENTIFIER, - "notify_watched_percent": plexpy.CONFIG.NOTIFY_WATCHED_PERCENT + "home_stats_cards": plexpy.CONFIG.HOME_STATS_CARDS, + "pms_identifier": plexpy.CONFIG.PMS_IDENTIFIER } return serve_template(templatename="index.html", title="Home", config=config) @@ -122,11 +120,19 @@ class WebInterface(object): return json.dumps(formats) @cherrypy.expose - def home_stats(self, time_range='30', stat_type='0', stat_count='5', notify_watched_percent='85', **kwargs): + def home_stats(self, **kwargs): data_factory = datafactory.DataFactory() + + time_range = plexpy.CONFIG.HOME_STATS_LENGTH + stats_type = plexpy.CONFIG.HOME_STATS_TYPE + stats_count = plexpy.CONFIG.HOME_STATS_COUNT + stats_cards = plexpy.CONFIG.HOME_STATS_CARDS + notify_watched_percent = plexpy.CONFIG.NOTIFY_WATCHED_PERCENT + stats_data = data_factory.get_home_stats(time_range=time_range, - stat_type=stat_type, - stat_count=stat_count, + stats_type=stats_type, + stats_count=stats_count, + stats_cards=stats_cards, notify_watched_percent=notify_watched_percent) return serve_template(templatename="home_stats.html", title="Stats", data=stats_data) From 8a989d71ca2a743530c2ae23bd50e7845d7e02e2 Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Fri, 18 Sep 2015 18:58:59 -0700 Subject: [PATCH 08/18] Add top music and popular music cards to watch statistics --- data/interfaces/default/home_stats.html | 160 +++++++++++++++++++++++- plexpy/datafactory.py | 92 ++++++++++++++ 2 files changed, 248 insertions(+), 4 deletions(-) diff --git a/data/interfaces/default/home_stats.html b/data/interfaces/default/home_stats.html index 01fb0cdd..36c17d7f 100644 --- a/data/interfaces/default/home_stats.html +++ b/data/interfaces/default/home_stats.html @@ -17,19 +17,19 @@ data[array_index]['rows'] :: Usable parameters row_id Return the db row id for a metadata item if one exists -== Only if 'stat_id' is 'top_tv' or 'popular_tv' or 'top_movies' or 'popular_movies' or 'last_watched' == +== Only if 'stat_id' is 'top_tv' or 'popular_tv' or 'top_movies' or 'popular_movies' or 'top_music' or 'popular_music' or 'last_watched' == thumb Return the thumb for the media item. -== Only if 'stat_id' is 'top_tv' or 'popular_tv' == +== Only if 'stat_id' is 'top_tv' or 'popular_tv' or 'top_music' or 'popular_music' == grandparent_thumb Returns location of the item's thumbnail. Use with pms_image_proxy. rating_key Returns the unique identifier for the media item. title Returns the title for the associated stat. -== Only if 'stat_id' is 'top_tv' or 'top_movies' or 'top_user' or 'top_platform' == +== Only if 'stat_id' is 'top_tv' or 'top_movies' or 'top_music' or 'top_user' or 'top_platform' == total_plays Returns the count for the associated stat. total_duration Returns the total duration for the associated stat. -== Only of 'stat_id' is 'popular_tv' or 'popular_movies' == +== Only of 'stat_id' is 'popular_tv' or 'popular_movies' or 'popular_music' == users_watched Returns the count for the associated stat. == Only if 'stat_id' is 'top_user' or 'last_watched' == @@ -372,6 +372,158 @@ DOCUMENTATION :: END % endif
+ % elif top_stat['stat_id'] == 'top_music' and top_stat['rows']: +
+
  • +
    +
    +

    Most Listened to Artist

    +
    +
    +

    + + ${top_stat['rows'][0]['title']} + +

    + % if top_stat['stat_type'] == 'total_plays': +

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

    +

    plays

    + % else: + ${top_stat['rows'][0]['total_duration'] | hd} + % endif +
    +
    + + % if top_stat['rows'][0]['grandparent_thumb']: +
    +
    +
    + % else: +
    +
    +
    + % endif +
    + %if len(top_stat['rows']) > 1: +
    + + % endif +
  • +
    + % elif top_stat['stat_id'] == 'popular_music' and top_stat['rows']: +
    +
  • +
    +
    +

    Most Popular Artist

    +
    +
    +

    + + ${top_stat['rows'][0]['title']} + +

    +

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

    +

    users

    +
    +
    + + % if top_stat['rows'][0]['grandparent_thumb'] != '': +
    +
    +
    + % else: +
    +
    +
    + % endif +
    + %if len(top_stat['rows']) > 1: +
    + + % endif +
  • +
    % elif top_stat['stat_id'] == 'top_users' and top_stat['rows']:
  • diff --git a/plexpy/datafactory.py b/plexpy/datafactory.py index ebf7f339..83698d6e 100644 --- a/plexpy/datafactory.py +++ b/plexpy/datafactory.py @@ -324,6 +324,98 @@ class DataFactory(object): home_stats.append({'stat_id': stat, 'rows': popular_movies}) + elif 'top_music' in stat: + top_music = [] + try: + query = 'SELECT session_history_metadata.id, ' \ + 'session_history_metadata.grandparent_title, ' \ + 'COUNT(session_history_metadata.grandparent_title) as total_plays, ' \ + 'SUM(case when session_history.stopped > 0 ' \ + 'then (session_history.stopped - session_history.started) ' \ + ' - (case when session_history.paused_counter is NULL then 0 else session_history.paused_counter end) ' \ + 'else 0 end) as total_duration, ' \ + 'session_history_metadata.grandparent_rating_key, ' \ + 'MAX(session_history.started) as last_watch,' \ + 'session_history_metadata.grandparent_thumb ' \ + 'FROM session_history_metadata ' \ + 'JOIN session_history on session_history_metadata.id = session_history.id ' \ + 'WHERE datetime(session_history.stopped, "unixepoch", "localtime") ' \ + '>= datetime("now", "-%s days", "localtime") ' \ + 'AND session_history_metadata.media_type = "track" ' \ + 'GROUP BY session_history_metadata.grandparent_title ' \ + 'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stats_count) + result = monitor_db.select(query) + except: + logger.warn("Unable to execute database query.") + return None + + for item in result: + row = {'title': item[1], + 'total_plays': item[2], + 'total_duration': item[3], + 'users_watched': '', + 'rating_key': item[4], + 'last_play': item[5], + 'grandparent_thumb': item[6], + 'thumb': '', + 'user': '', + 'friendly_name': '', + 'platform_type': '', + 'platform': '', + 'row_id': item[0] + } + top_music.append(row) + + home_stats.append({'stat_id': stat, + 'stat_type': sort_type, + 'rows': top_music}) + + elif 'popular_music' in stat: + popular_music = [] + try: + query = 'SELECT session_history_metadata.id, ' \ + 'session_history_metadata.grandparent_title, ' \ + 'COUNT(DISTINCT session_history.user_id) as users_watched, ' \ + 'session_history_metadata.grandparent_rating_key, ' \ + 'MAX(session_history.started) as last_watch, ' \ + 'COUNT(session_history.id) as total_plays, ' \ + 'SUM(case when session_history.stopped > 0 ' \ + 'then (session_history.stopped - session_history.started) ' \ + ' - (case when session_history.paused_counter is NULL then 0 else session_history.paused_counter end) ' \ + 'else 0 end) as total_duration, ' \ + 'session_history_metadata.grandparent_thumb ' \ + 'FROM session_history_metadata ' \ + 'JOIN session_history ON session_history_metadata.id = session_history.id ' \ + 'WHERE datetime(session_history.stopped, "unixepoch", "localtime") ' \ + '>= datetime("now", "-%s days", "localtime") ' \ + 'AND session_history_metadata.media_type = "track" ' \ + 'GROUP BY session_history_metadata.grandparent_title ' \ + 'ORDER BY users_watched DESC, %s DESC ' \ + 'LIMIT %s' % (time_range, sort_type, stats_count) + result = monitor_db.select(query) + except: + logger.warn("Unable to execute database query.") + return None + + for item in result: + row = {'title': item[1], + 'users_watched': item[2], + 'rating_key': item[3], + 'last_play': item[4], + 'total_plays': item[5], + 'grandparent_thumb': item[7], + 'thumb': '', + 'user': '', + 'friendly_name': '', + 'platform_type': '', + 'platform': '', + 'row_id': item[0] + } + popular_music.append(row) + + home_stats.append({'stat_id': stat, + 'rows': popular_music}) + elif 'top_users' in stat: top_users = [] try: From 078f4babf5750d69b36ee217dd8b8693ea7ee537 Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Fri, 18 Sep 2015 23:42:16 -0700 Subject: [PATCH 09/18] Add setting to selectively hide library statistics cards on the homepage --- data/interfaces/default/index.html | 2 ++ data/interfaces/default/settings.html | 47 ++++++++++++++++++++++++++- plexpy/config.py | 3 +- plexpy/pmsconnect.py | 13 +++++--- plexpy/webserve.py | 31 +++++++++++++++++- 5 files changed, 89 insertions(+), 7 deletions(-) diff --git a/data/interfaces/default/index.html b/data/interfaces/default/index.html index e18cfdf8..5f8caeba 100644 --- a/data/interfaces/default/index.html +++ b/data/interfaces/default/index.html @@ -29,6 +29,7 @@
  • % endif + % if config['home_library_cards'] > 'library_statistics':
    @@ -40,6 +41,7 @@
    + % endif
    diff --git a/data/interfaces/default/settings.html b/data/interfaces/default/settings.html index ee37f5fd..394c3d63 100644 --- a/data/interfaces/default/settings.html +++ b/data/interfaces/default/settings.html @@ -93,7 +93,7 @@ available_notification_agents = notifiers.available_notification_agents()
    -

    Select the cards to show in the watch statistics on the home page.

    +

    Select the cards to show in the watch statistics on the home page. Select none to disable.

    + + +
    +
    +

    +
    @@ -1215,6 +1232,34 @@ $(document).ready(function() { }).on('mousemove', function(e) { e.preventDefault() }); + + $.ajax({ + url: 'get_server_children', + data: { }, + async: true, + complete: function (xhr, status) { + server_children_info = $.parseJSON(xhr.responseText); + libraries_list = server_children_info.libraries_list; + for (var i in libraries_list) { + title = libraries_list[i].title; + key = libraries_list[i].key; + $('#home_library_cards').append('') + } + var cards = "${config['home_library_cards']}".split(/[\s,]+/); + cards.forEach(function (item) { + $('#card-'+item).prop('selected', !$(this).prop('selected')); + }); + } + }); + $('#home_library_cards').on('mousedown', function(e) { + e.preventDefault(); + var scroll = this.scrollTop; + e.target.selected = !e.target.selected; + this.scrollTop = scroll; + }).on('mousemove', function(e) { + e.preventDefault() + }); + }); diff --git a/plexpy/config.py b/plexpy/config.py index 7288ac34..c48a1858 100644 --- a/plexpy/config.py +++ b/plexpy/config.py @@ -82,10 +82,11 @@ _CONFIG_DEFINITIONS = { 'GROWL_ON_RESUME': (int, 'Growl', 0), 'GROWL_ON_BUFFER': (int, 'Growl', 0), 'GROWL_ON_WATCHED': (int, 'Growl', 0), + 'HOME_LIBRARY_CARDS': (str, 'General', 'library_statistics_first'), 'HOME_STATS_LENGTH': (int, 'General', 30), 'HOME_STATS_TYPE': (int, 'General', 0), 'HOME_STATS_COUNT': (int, 'General', 5), - 'HOME_STATS_CARDS': (str, 'General', '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_first'), 'HTTPS_CERT': (str, 'General', ''), 'HTTPS_KEY': (str, 'General', ''), 'HTTP_HOST': (str, 'General', '0.0.0.0'), diff --git a/plexpy/pmsconnect.py b/plexpy/pmsconnect.py index 25b23253..97b159e2 100644 --- a/plexpy/pmsconnect.py +++ b/plexpy/pmsconnect.py @@ -1208,7 +1208,7 @@ class PmsConnect(object): 'title': helpers.get_xml_attr(xml_head[0], 'title1'), 'libraries_list': libraries_list } - + return output """ @@ -1270,13 +1270,15 @@ class PmsConnect(object): return output """ - Return processed and validated server statistics. + Return processed and validated library statistics. Output: array """ - def get_library_stats(self): + def get_library_stats(self, library_cards=''): server_libraries = self.get_server_children() + library_keys = library_cards.split(', ') + server_library_stats = [] if server_libraries['libraries_count'] != '0': @@ -1285,7 +1287,10 @@ class PmsConnect(object): for library in libraries_list: library_type = library['type'] section_key = library['key'] - library_list = self.get_library_children(library_type, section_key) + if section_key in library_keys: + library_list = self.get_library_children(library_type, section_key) + else: + continue if library_list['library_count'] != '0': library_stats = {'title': library['title'], diff --git a/plexpy/webserve.py b/plexpy/webserve.py index 42dd50e0..5bc15399 100644 --- a/plexpy/webserve.py +++ b/plexpy/webserve.py @@ -67,6 +67,7 @@ class WebInterface(object): config = { "home_stats_length": plexpy.CONFIG.HOME_STATS_LENGTH, "home_stats_cards": plexpy.CONFIG.HOME_STATS_CARDS, + "home_library_cards": plexpy.CONFIG.HOME_LIBRARY_CARDS, "pms_identifier": plexpy.CONFIG.PMS_IDENTIFIER } return serve_template(templatename="index.html", title="Home", config=config) @@ -140,7 +141,10 @@ class WebInterface(object): @cherrypy.expose def library_stats(self, **kwargs): pms_connect = pmsconnect.PmsConnect() - stats_data = pms_connect.get_library_stats() + + library_cards = plexpy.CONFIG.HOME_LIBRARY_CARDS + + stats_data = pms_connect.get_library_stats(library_cards=library_cards) return serve_template(templatename="library_stats.html", title="Library Stats", data=stats_data) @@ -479,6 +483,7 @@ class WebInterface(object): "home_stats_type": checked(plexpy.CONFIG.HOME_STATS_TYPE), "home_stats_count": plexpy.CONFIG.HOME_STATS_COUNT, "home_stats_cards": plexpy.CONFIG.HOME_STATS_CARDS, + "home_library_cards": plexpy.CONFIG.HOME_LIBRARY_CARDS, "buffer_threshold": plexpy.CONFIG.BUFFER_THRESHOLD, "buffer_wait": plexpy.CONFIG.BUFFER_WAIT } @@ -535,6 +540,10 @@ class WebInterface(object): if kwargs['home_stats_cards'] != 'watch_statistics': kwargs['home_stats_cards'] = ', '.join(kwargs['home_stats_cards']) + if 'home_library_cards' in kwargs: + if kwargs['home_library_cards'] != 'library_statistics': + kwargs['home_library_cards'] = ', '.join(kwargs['home_library_cards']) + plexpy.CONFIG.process_kwargs(kwargs) # Write the config @@ -1132,6 +1141,26 @@ class WebInterface(object): else: logger.warn('Unable to retrieve data.') + @cherrypy.expose + def get_server_children(self, **kwargs): + + pms_connect = pmsconnect.PmsConnect() + result = pms_connect.get_server_children() + + if plexpy.CONFIG.HOME_LIBRARY_CARDS == '': + library_keys = ['library_statistics'] + for library in result['libraries_list']: + library_keys.append(library['key']) + + plexpy.CONFIG.HOME_LIBRARY_CARDS = ', '.join(library_keys) + plexpy.CONFIG.write() + + if result: + cherrypy.response.headers['Content-type'] = 'application/json' + return json.dumps(result) + else: + logger.warn('Unable to retrieve data.') + @cherrypy.expose def get_activity(self, **kwargs): From ad183ff9fe91ab72947b7a59ea19b7e9f2a3ca80 Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Fri, 18 Sep 2015 23:43:01 -0700 Subject: [PATCH 10/18] Update message to prompt the user to enable cards for homepage statistics --- data/interfaces/default/home_stats.html | 2 +- data/interfaces/default/library_stats.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/interfaces/default/home_stats.html b/data/interfaces/default/home_stats.html index 36c17d7f..85f83496 100644 --- a/data/interfaces/default/home_stats.html +++ b/data/interfaces/default/home_stats.html @@ -802,6 +802,6 @@ DOCUMENTATION :: END
    No stats for selected period.

    % endif % else: -
    Unable to retrieve data from database. Please check your settings. +
    No cards to show. Please enable cards in the settings.

    % endif \ No newline at end of file diff --git a/data/interfaces/default/library_stats.html b/data/interfaces/default/library_stats.html index 34ef2a91..aef22736 100644 --- a/data/interfaces/default/library_stats.html +++ b/data/interfaces/default/library_stats.html @@ -73,6 +73,6 @@ DOCUMENTATION :: END % endfor % else: -
    Unable to retrieve data from database. Please check your settings. +
    No cards to show. Please enable cards in the settings.

    % endif \ No newline at end of file From d61e699dc98050a6d4a0668daa4ce2a15dc7af04 Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Fri, 18 Sep 2015 23:49:34 -0700 Subject: [PATCH 11/18] Remove test code --- plexpy/webserve.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/plexpy/webserve.py b/plexpy/webserve.py index 5bc15399..be877bd9 100644 --- a/plexpy/webserve.py +++ b/plexpy/webserve.py @@ -1146,14 +1146,6 @@ class WebInterface(object): pms_connect = pmsconnect.PmsConnect() result = pms_connect.get_server_children() - - if plexpy.CONFIG.HOME_LIBRARY_CARDS == '': - library_keys = ['library_statistics'] - for library in result['libraries_list']: - library_keys.append(library['key']) - - plexpy.CONFIG.HOME_LIBRARY_CARDS = ', '.join(library_keys) - plexpy.CONFIG.write() if result: cherrypy.response.headers['Content-type'] = 'application/json' From de4d8fb277265a11f7188741b2987fc39bcd1d6a Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Sat, 19 Sep 2015 00:42:00 -0700 Subject: [PATCH 12/18] Default to show all cards until the user disables them. --- data/interfaces/default/home_stats.html | 2 +- data/interfaces/default/library_stats.html | 2 +- plexpy/config.py | 2 +- plexpy/datafactory.py | 3 +-- plexpy/pmsconnect.py | 4 +--- plexpy/webserve.py | 15 +++++++++++++-- 6 files changed, 18 insertions(+), 10 deletions(-) diff --git a/data/interfaces/default/home_stats.html b/data/interfaces/default/home_stats.html index 85f83496..36c17d7f 100644 --- a/data/interfaces/default/home_stats.html +++ b/data/interfaces/default/home_stats.html @@ -802,6 +802,6 @@ DOCUMENTATION :: END
    No stats for selected period.

    % endif % else: -
    No cards to show. Please enable cards in the settings. +
    Unable to retrieve data from database. Please check your settings.

    % endif \ No newline at end of file diff --git a/data/interfaces/default/library_stats.html b/data/interfaces/default/library_stats.html index aef22736..12e7135c 100644 --- a/data/interfaces/default/library_stats.html +++ b/data/interfaces/default/library_stats.html @@ -73,6 +73,6 @@ DOCUMENTATION :: END % endfor % else: -
    No cards to show. Please enable cards in the settings. +
    Unable to retrieve data from server. Please check your settings.

    % endif \ No newline at end of file diff --git a/plexpy/config.py b/plexpy/config.py index c48a1858..73034be2 100644 --- a/plexpy/config.py +++ b/plexpy/config.py @@ -86,7 +86,7 @@ _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_first'), + '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'), '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 83698d6e..52d21e42 100644 --- a/plexpy/datafactory.py +++ b/plexpy/datafactory.py @@ -136,10 +136,9 @@ class DataFactory(object): sort_type = 'total_plays' if stats_type == '0' else 'total_duration' - stats_queries = stats_cards.split(', ') home_stats = [] - for stat in stats_queries: + for stat in stats_cards: if 'top_tv' in stat: top_tv = [] try: diff --git a/plexpy/pmsconnect.py b/plexpy/pmsconnect.py index 97b159e2..0fa2e061 100644 --- a/plexpy/pmsconnect.py +++ b/plexpy/pmsconnect.py @@ -1277,8 +1277,6 @@ class PmsConnect(object): def get_library_stats(self, library_cards=''): server_libraries = self.get_server_children() - library_keys = library_cards.split(', ') - server_library_stats = [] if server_libraries['libraries_count'] != '0': @@ -1287,7 +1285,7 @@ class PmsConnect(object): for library in libraries_list: library_type = library['type'] section_key = library['key'] - if section_key in library_keys: + if section_key in library_cards: library_list = self.get_library_children(library_type, section_key) else: continue diff --git a/plexpy/webserve.py b/plexpy/webserve.py index be877bd9..2851605d 100644 --- a/plexpy/webserve.py +++ b/plexpy/webserve.py @@ -127,7 +127,7 @@ class WebInterface(object): time_range = plexpy.CONFIG.HOME_STATS_LENGTH stats_type = plexpy.CONFIG.HOME_STATS_TYPE stats_count = plexpy.CONFIG.HOME_STATS_COUNT - stats_cards = plexpy.CONFIG.HOME_STATS_CARDS + stats_cards = plexpy.CONFIG.HOME_STATS_CARDS.split(', ') notify_watched_percent = plexpy.CONFIG.NOTIFY_WATCHED_PERCENT stats_data = data_factory.get_home_stats(time_range=time_range, @@ -142,7 +142,18 @@ class WebInterface(object): def library_stats(self, **kwargs): pms_connect = pmsconnect.PmsConnect() - library_cards = plexpy.CONFIG.HOME_LIBRARY_CARDS + library_cards = plexpy.CONFIG.HOME_LIBRARY_CARDS.split(', ') + + if library_cards == ['library_statistics_first']: + library_cards = ['library_statistics'] + server_children = pms_connect.get_server_children() + server_libraries = server_children['libraries_list'] + + for library in server_libraries: + library_cards.append(library['key']) + + plexpy.CONFIG.HOME_LIBRARY_CARDS = ', '.join(library_cards) + plexpy.CONFIG.write() stats_data = pms_connect.get_library_stats(library_cards=library_cards) From 924ed70458499c60d03681eb912010780c4543c9 Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Sat, 19 Sep 2015 00:53:18 -0700 Subject: [PATCH 13/18] Move cards settings help text --- data/interfaces/default/settings.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/interfaces/default/settings.html b/data/interfaces/default/settings.html index 394c3d63..90a0c185 100644 --- a/data/interfaces/default/settings.html +++ b/data/interfaces/default/settings.html @@ -93,7 +93,6 @@ available_notification_agents = notifiers.available_notification_agents()
    -

    Select the cards to show in the watch statistics on the home page. Select none to disable.

    +

    Select the cards to show in the watch statistics on the home page. Select none to disable.

    @@ -144,7 +144,6 @@ available_notification_agents = notifiers.available_notification_agents()
    -

    Select the cards to show in the library statistics on the home page. Select none to disable.

    +

    Select the cards to show in the library statistics on the home page. Select none to disable.

    From 7c6619ebc533f72e9536ce7a85835fdce19d7a0c Mon Sep 17 00:00:00 2001 From: Tim Date: Sat, 19 Sep 2015 12:37:51 +0200 Subject: [PATCH 14/18] Fix Email TLS checkbox bug. Clean up some notifier code. --- .../default/notification_config.html | 16 +++++- plexpy/notifiers.py | 2 +- plexpy/webserve.py | 56 +------------------ 3 files changed, 17 insertions(+), 57 deletions(-) diff --git a/data/interfaces/default/notification_config.html b/data/interfaces/default/notification_config.html index 41e4a341..8e685d89 100644 --- a/data/interfaces/default/notification_config.html +++ b/data/interfaces/default/notification_config.html @@ -1,3 +1,6 @@ +<%! +from plexpy import helpers +%> % if data: -

    The interval (in seconds) PlexPy will wait for a video item to be active before logging it. 0 to disable.

    +

    The interval (in seconds) an item must be in a playing state before logging it. 0 to disable.