From d8298a12eb211f20248a0b04b0d37793802612a6 Mon Sep 17 00:00:00 2001 From: JonnyWong16 Date: Sun, 25 Mar 2018 11:00:58 -0700 Subject: [PATCH 01/16] Clear PMS selectize when dropdown opens --- data/interfaces/default/settings.html | 3 +++ data/interfaces/default/welcome.html | 3 +++ 2 files changed, 6 insertions(+) diff --git a/data/interfaces/default/settings.html b/data/interfaces/default/settings.html index e223fcd9..aee5e3b9 100644 --- a/data/interfaces/default/settings.html +++ b/data/interfaces/default/settings.html @@ -1861,6 +1861,9 @@ $(document).ready(function() { $('#pms_url_manual').prop('checked', false); $('#pms_url').val('Please verify your server above to retrieve the URL'); PMSCloudCheck(); + }, + onDropdownOpen: function() { + this.clear(); } }); var select_pms = $select_pms[0].selectize; diff --git a/data/interfaces/default/welcome.html b/data/interfaces/default/welcome.html index 2a7cff1c..d61900a9 100644 --- a/data/interfaces/default/welcome.html +++ b/data/interfaces/default/welcome.html @@ -374,6 +374,9 @@ $(document).ready(function() { $('#pms_is_remote_checkbox').prop('disabled', false); $('#pms_ssl_checkbox').prop('disabled', false); } + }, + onDropdownOpen: function() { + this.clear(); } }); var select_pms = $select_pms[0].selectize; From 5108e1bb09fe1fc2b37964d0d5f4a037f9095a01 Mon Sep 17 00:00:00 2001 From: JonnyWong16 Date: Sun, 25 Mar 2018 11:38:35 -0700 Subject: [PATCH 02/16] Add quick websocket test when verifying server --- data/interfaces/default/settings.html | 32 ++++++++++++--------------- plexpy/webserve.py | 21 +++++++++++++++++- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/data/interfaces/default/settings.html b/data/interfaces/default/settings.html index aee5e3b9..d1195d49 100644 --- a/data/interfaces/default/settings.html +++ b/data/interfaces/default/settings.html @@ -666,12 +666,7 @@
-
- - - - -
+

@@ -1937,11 +1932,11 @@ $(document).ready(function() { data: { hostname: pms_ip, port: pms_port, - identifier: pms_identifier, ssl: pms_ssl, remote: pms_is_remote, manual: pms_url_manual, - get_url: serverChanged + get_url: true, + test_websocket: true }, cache: true, async: true, @@ -1954,15 +1949,23 @@ $(document).ready(function() { var result = xhr; var identifier = result.identifier; var url = result.url; + var ws = result.ws; if (identifier) { $("#pms_identifier").val(identifier); + if (url) { $("#pms_url").val(url); } - $("#pms_verify").html('').fadeIn('fast'); - $("#pms_ip_group").removeClass("has-error"); - serverChanged = false; + if (ws === false) { + $("#pms_verify").html('').fadeIn('fast'); + $("#pms_ip_group").addClass("has-error"); + showMsg(' Server found but unable to connect websocket.
Check the logs for errors.', false, true, 5000, true) + } else { + $("#pms_verify").html('').fadeIn('fast'); + $("#pms_ip_group").removeClass("has-error"); + serverChanged = false; + } if (_callback) { _callback(); @@ -1990,13 +1993,6 @@ $(document).ready(function() { $("#pms_web_url").val(pms_web_url || 'https://app.plex.tv/desktop'); } - $('#test_pms_url_button').on('click', function(){ - var pms_url = $.trim($("#pms_url").val()); - if (pms_url.startsWith('http')) { - window.open(pms_url + '/web', '_blank'); - } - }); - $('#test_pms_web_button').on('click', function(){ var pms_web_url = $.trim($("#pms_web_url").val()); window.open(pms_web_url, '_blank'); diff --git a/plexpy/webserve.py b/plexpy/webserve.py index 3af68ea4..282e5517 100644 --- a/plexpy/webserve.py +++ b/plexpy/webserve.py @@ -27,6 +27,8 @@ from hashing_passwords import make_hash from mako.lookup import TemplateLookup from mako import exceptions +import websocket + import plexpy import activity_pinger import common @@ -3463,7 +3465,7 @@ class WebInterface(object): @requireAuth(member_of("admin")) @addtoapi() def get_server_id(self, hostname=None, port=None, identifier=None, ssl=0, remote=0, manual=0, - get_url=False, **kwargs): + get_url=False, test_websocket=False, **kwargs): """ Get the PMS server identifier. ``` @@ -3519,6 +3521,23 @@ class WebInterface(object): pms_url_manual=manual, pms_identifier=identifier) result['url'] = server['pms_url'] + result['ws'] = None + + if test_websocket == 'true': + # Quick test websocket connection + ws_url = result['url'].replace('http', 'ws', 1) + '/:/websockets/notifications' + header = ['X-Plex-Token: %s' % plexpy.CONFIG.PMS_TOKEN] + + logger.debug("Testing websocket connection...") + try: + test_ws = websocket.create_connection(ws_url, header=header) + test_ws.close() + logger.debug("Websocket test connection successful.") + result['ws'] = True + except (websocket.WebSocketException, IOError, Exception) as e: + logger.error("Websocket test connection failed: %s" % e) + result['ws'] = False + return result else: logger.warn('Unable to retrieve the PMS identifier.') From 683a7827234dad6d773d50eabf747e435c90ef10 Mon Sep 17 00:00:00 2001 From: JonnyWong16 Date: Sun, 25 Mar 2018 11:58:57 -0700 Subject: [PATCH 03/16] Fix typo (Closes Tautulli/Tautulli-Issues#35) --- plexpy/webserve.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plexpy/webserve.py b/plexpy/webserve.py index 282e5517..57804472 100644 --- a/plexpy/webserve.py +++ b/plexpy/webserve.py @@ -3532,10 +3532,10 @@ class WebInterface(object): try: test_ws = websocket.create_connection(ws_url, header=header) test_ws.close() - logger.debug("Websocket test connection successful.") + logger.debug("Websocket connection test successful.") result['ws'] = True except (websocket.WebSocketException, IOError, Exception) as e: - logger.error("Websocket test connection failed: %s" % e) + logger.error("Websocket connection test failed: %s" % e) result['ws'] = False return result From 2aff7713cdc459002286c6eed83044dd1b10b326 Mon Sep 17 00:00:00 2001 From: JonnyWong16 Date: Sun, 25 Mar 2018 12:39:20 -0700 Subject: [PATCH 04/16] Fix invalid link to playlist in sync table (Fixes Tautulli/Tautulli-Issues#34) --- data/interfaces/default/js/tables/sync_table.js | 10 +++++++--- plexpy/webserve.py | 3 +++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/data/interfaces/default/js/tables/sync_table.js b/data/interfaces/default/js/tables/sync_table.js index 942e37eb..748e0c51 100644 --- a/data/interfaces/default/js/tables/sync_table.js +++ b/data/interfaces/default/js/tables/sync_table.js @@ -37,7 +37,6 @@ sync_table_options = { "data": "state", "createdCell": function (td, cellData, rowData, row, col) { if (cellData === 'pending') { - $(td).addClass('currentlyWatching'); $(td).html('Pending...'); } else { $(td).html(cellData.toProperCase()); @@ -66,7 +65,7 @@ sync_table_options = { "data": "sync_title", "createdCell": function (td, cellData, rowData, row, col) { if (cellData !== '') { - if (rowData['metadata_type'] !== '') { + if (rowData['rating_key']) { $(td).html('' + cellData + ''); } else { $(td).html(cellData); @@ -74,7 +73,7 @@ sync_table_options = { } }, "className": "datatable-wrap" -}, + }, { "targets": [4], "data": "metadata_type", @@ -150,6 +149,11 @@ sync_table_options = { "preDrawCallback": function (settings) { var msg = " Fetching rows..."; showMsg(msg, false, false, 0) + }, + "rowCallback": function (row, rowData, rowIndex) { + if (rowData['state'] === 'pending') { + $(row).addClass('current-activity-row'); + } } }; diff --git a/plexpy/webserve.py b/plexpy/webserve.py index 57804472..5ae75877 100644 --- a/plexpy/webserve.py +++ b/plexpy/webserve.py @@ -3716,6 +3716,9 @@ class WebInterface(object): @cherrypy.expose @requireAuth() def info(self, rating_key=None, source=None, query=None, **kwargs): + if rating_key and not str(rating_key).isdigit(): + raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT) + metadata = None config = { From 084732706dd3a5144c5def2f4f63c96707984cb8 Mon Sep 17 00:00:00 2001 From: JonnyWong16 Date: Sun, 25 Mar 2018 13:25:18 -0700 Subject: [PATCH 05/16] Add setting to change homepage refresh interval --- data/interfaces/default/index.html | 4 ++-- data/interfaces/default/settings.html | 15 +++++++++++++++ plexpy/config.py | 1 + plexpy/webserve.py | 2 ++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/data/interfaces/default/index.html b/data/interfaces/default/index.html index f72f27f9..e41e661d 100644 --- a/data/interfaces/default/index.html +++ b/data/interfaces/default/index.html @@ -589,7 +589,7 @@ if (!(create_instances.length) && activity_ready) { getCurrentActivity(); } - }, 2000); + }, ${config['home_refresh_interval'] * 1000}); setInterval(function(){ $('.progress_time_offset').each(function () { @@ -604,7 +604,7 @@ if ($(this).data('state') === 'playing' && $(this).data('view_offset') >= 0) { var view_offset = parseInt($(this).data('view_offset')); var stream_duration = parseInt($(this).data('stream_duration')); - var progress_percent = Math.min(Math.trunc(view_offset / stream_duration * 100), 100) + var progress_percent = Math.min(Math.trunc(view_offset / stream_duration * 100), 100); $(this).width(progress_percent - 3 + '%').html(progress_percent + '%') .attr('data-original-title', 'Stream Progress ' + progress_percent + '%') .data('view_offset', Math.min(view_offset + 1000, stream_duration)); diff --git a/data/interfaces/default/settings.html b/data/interfaces/default/settings.html index d1195d49..e81a1dd9 100644 --- a/data/interfaces/default/settings.html +++ b/data/interfaces/default/settings.html @@ -267,6 +267,21 @@

+
+

Activity

+
+ +
+ +
+
+ +
+ +
+

Set the interval (in seconds) to refresh the current activity on the homepage. Minimum 2.

+
+

Sections

diff --git a/plexpy/config.py b/plexpy/config.py index e4d750a2..bda19bc0 100644 --- a/plexpy/config.py +++ b/plexpy/config.py @@ -209,6 +209,7 @@ _CONFIG_DEFINITIONS = { 'HOME_STATS_CARDS': (list, 'General', ['top_movies', 'popular_movies', 'top_tv', 'popular_tv', 'top_music', \ 'popular_music', 'last_watched', 'top_users', 'top_platforms', 'most_concurrent']), 'HOME_STATS_RECENTLY_ADDED_COUNT': (int, 'General', 50), + 'HOME_REFRESH_INTERVAL': (int, 'General', 2), 'HTTPS_CREATE_CERT': (int, 'General', 1), 'HTTPS_CERT': (str, 'General', ''), 'HTTPS_CERT_CHAIN': (str, 'General', ''), diff --git a/plexpy/webserve.py b/plexpy/webserve.py index 5ae75877..aed8edbe 100644 --- a/plexpy/webserve.py +++ b/plexpy/webserve.py @@ -173,6 +173,7 @@ class WebInterface(object): "home_stats_type": plexpy.CONFIG.HOME_STATS_TYPE, "home_stats_count": plexpy.CONFIG.HOME_STATS_COUNT, "home_stats_recently_added_count": plexpy.CONFIG.HOME_STATS_RECENTLY_ADDED_COUNT, + "home_refresh_interval": plexpy.CONFIG.HOME_REFRESH_INTERVAL, "pms_name": plexpy.CONFIG.PMS_NAME, "pms_is_cloud": plexpy.CONFIG.PMS_IS_CLOUD, "update_show_changelog": plexpy.CONFIG.UPDATE_SHOW_CHANGELOG @@ -2642,6 +2643,7 @@ class WebInterface(object): "home_sections": json.dumps(plexpy.CONFIG.HOME_SECTIONS), "home_stats_cards": json.dumps(plexpy.CONFIG.HOME_STATS_CARDS), "home_library_cards": json.dumps(plexpy.CONFIG.HOME_LIBRARY_CARDS), + "home_refresh_interval": plexpy.CONFIG.HOME_REFRESH_INTERVAL, "buffer_threshold": plexpy.CONFIG.BUFFER_THRESHOLD, "buffer_wait": plexpy.CONFIG.BUFFER_WAIT, "group_history_tables": checked(plexpy.CONFIG.GROUP_HISTORY_TABLES), From 749e1fcebe0219837a27762caadb75c1ed9e1e1b Mon Sep 17 00:00:00 2001 From: JonnyWong16 Date: Mon, 26 Mar 2018 08:53:40 -0700 Subject: [PATCH 06/16] Move refresh interval setting to homepage --- data/interfaces/default/css/tautulli.css | 16 +++++++--- data/interfaces/default/index.html | 39 +++++++++++++++++++----- data/interfaces/default/settings.html | 11 ------- plexpy/webserve.py | 7 +++-- 4 files changed, 48 insertions(+), 25 deletions(-) diff --git a/data/interfaces/default/css/tautulli.css b/data/interfaces/default/css/tautulli.css index b392bffa..7522c517 100644 --- a/data/interfaces/default/css/tautulli.css +++ b/data/interfaces/default/css/tautulli.css @@ -294,10 +294,6 @@ object { font-weight: bold; text-transform: uppercase; } -.padded-header h3 small { - font-size: 13px; - text-transform: none; -} .btn { outline:0px !important; } @@ -2377,6 +2373,18 @@ a .library-user-instance-box:hover { margin-top: 9px; width: 175px; } +.home-padded-header .info-bar { + float: left; + margin-left: 15px; + line-height: 35px; +} +.home-padded-header .info-bar small { + font-size: 13px; + font-weight: normal; + text-transform: none; + line-height: 1; + color: #777; +} .home-padded-header .button-bar { float: left; } diff --git a/data/interfaces/default/index.html b/data/interfaces/default/index.html index e41e661d..844c1940 100644 --- a/data/interfaces/default/index.html +++ b/data/interfaces/default/index.html @@ -10,8 +10,16 @@ % if section == 'current_activity':
-
-

Activity    +
+

Current Activity

+
+
+ Refresh every + + seconds +
+
+
-

+
<% from plexpy import PLEX_SERVER_UP %> @@ -585,11 +593,16 @@ } getCurrentActivity(); - setInterval(function () { - if (!(create_instances.length) && activity_ready) { - getCurrentActivity(); - } - }, ${config['home_refresh_interval'] * 1000}); + + function refreshActivity(seconds) { + return setInterval(function () { + if (!(create_instances.length) && activity_ready) { + getCurrentActivity(); + } + }, seconds * 1000); + } + var refresh_interval = $('#activity-refresh-interval').val(); + var activityRefresh = refreshActivity(refresh_interval); setInterval(function(){ $('.progress_time_offset').each(function () { @@ -685,6 +698,16 @@ window.open(sessions_url, '_blank'); }); }); + + $('#activity-refresh-interval').change(function () { + forceMinMax($(this)); + clearInterval(activityRefresh); + refresh_interval = $(this).val(); + activityRefresh = refreshActivity(refresh_interval); + $.post('set_home_stats_config', { refresh_interval: refresh_interval }); + }); + + $('#activity-refresh-interval').tooltip({ container: 'body', placement: 'top', html: true }); % endif % endif diff --git a/data/interfaces/default/settings.html b/data/interfaces/default/settings.html index e81a1dd9..4de9c055 100644 --- a/data/interfaces/default/settings.html +++ b/data/interfaces/default/settings.html @@ -271,17 +271,6 @@

Activity

-
- -
-
- -
- -
-

Set the interval (in seconds) to refresh the current activity on the homepage. Minimum 2.

-
-

Sections

diff --git a/plexpy/webserve.py b/plexpy/webserve.py index aed8edbe..124b6a46 100644 --- a/plexpy/webserve.py +++ b/plexpy/webserve.py @@ -286,7 +286,11 @@ class WebInterface(object): @cherrypy.expose @requireAuth(member_of("admin")) - def set_home_stats_config(self, time_range=None, stats_type=None, stats_count=None, recently_added_count=None, **kwargs): + def set_home_stats_config(self, refresh_interval=None, time_range=None, stats_type=None, stats_count=None, + recently_added_count=None, **kwargs): + if refresh_interval: + plexpy.CONFIG.__setattr__('HOME_REFRESH_INTERVAL', refresh_interval) + plexpy.CONFIG.write() if time_range: plexpy.CONFIG.__setattr__('HOME_STATS_LENGTH', time_range) plexpy.CONFIG.write() @@ -2643,7 +2647,6 @@ class WebInterface(object): "home_sections": json.dumps(plexpy.CONFIG.HOME_SECTIONS), "home_stats_cards": json.dumps(plexpy.CONFIG.HOME_STATS_CARDS), "home_library_cards": json.dumps(plexpy.CONFIG.HOME_LIBRARY_CARDS), - "home_refresh_interval": plexpy.CONFIG.HOME_REFRESH_INTERVAL, "buffer_threshold": plexpy.CONFIG.BUFFER_THRESHOLD, "buffer_wait": plexpy.CONFIG.BUFFER_WAIT, "group_history_tables": checked(plexpy.CONFIG.GROUP_HISTORY_TABLES), From c35fcc727c15f2468789974c268bc1c2b06f4a09 Mon Sep 17 00:00:00 2001 From: JonnyWong16 Date: Tue, 27 Mar 2018 22:08:12 -0700 Subject: [PATCH 07/16] Change default refresh to 10 seconds --- data/interfaces/default/index.html | 2 +- plexpy/config.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/interfaces/default/index.html b/data/interfaces/default/index.html index 844c1940..c7bf5ba3 100644 --- a/data/interfaces/default/index.html +++ b/data/interfaces/default/index.html @@ -14,7 +14,7 @@

Current Activity

- Refresh every + Refresh Every seconds
diff --git a/plexpy/config.py b/plexpy/config.py index bda19bc0..4af41ee0 100644 --- a/plexpy/config.py +++ b/plexpy/config.py @@ -209,7 +209,7 @@ _CONFIG_DEFINITIONS = { 'HOME_STATS_CARDS': (list, 'General', ['top_movies', 'popular_movies', 'top_tv', 'popular_tv', 'top_music', \ 'popular_music', 'last_watched', 'top_users', 'top_platforms', 'most_concurrent']), 'HOME_STATS_RECENTLY_ADDED_COUNT': (int, 'General', 50), - 'HOME_REFRESH_INTERVAL': (int, 'General', 2), + 'HOME_REFRESH_INTERVAL': (int, 'General', 10), 'HTTPS_CREATE_CERT': (int, 'General', 1), 'HTTPS_CERT': (str, 'General', ''), 'HTTPS_CERT_CHAIN': (str, 'General', ''), From 36b80aa6d34528e2aee79e0a335d6051713c5f2b Mon Sep 17 00:00:00 2001 From: JonnyWong16 Date: Wed, 28 Mar 2018 18:08:57 -0700 Subject: [PATCH 08/16] Make sure all datatables are using POST --- data/interfaces/default/history.html | 3 ++- data/interfaces/default/info.html | 6 +++--- data/interfaces/default/libraries.html | 2 +- data/interfaces/default/library.html | 4 ++-- data/interfaces/default/logs.html | 16 ++++++++++------ data/interfaces/default/sync.html | 5 +++-- data/interfaces/default/user.html | 8 +++++--- data/interfaces/default/users.html | 2 +- 8 files changed, 27 insertions(+), 19 deletions(-) diff --git a/data/interfaces/default/history.html b/data/interfaces/default/history.html index c34cf652..fe3436a0 100644 --- a/data/interfaces/default/history.html +++ b/data/interfaces/default/history.html @@ -113,7 +113,7 @@ // Load user ids and names (for the selector) $.ajax({ url: 'get_user_names', - type: 'get', + type: 'GET', dataType: 'json', success: function (data) { var select = $('#history-user'); @@ -130,6 +130,7 @@ function loadHistoryTable(media_type, selected_user_id) { history_table_options.ajax = { url: 'get_history', + type: 'POST', data: function (d) { return { json_data: JSON.stringify(d), diff --git a/data/interfaces/default/info.html b/data/interfaces/default/info.html index 93632bcc..b5c0dcd8 100644 --- a/data/interfaces/default/info.html +++ b/data/interfaces/default/info.html @@ -547,7 +547,7 @@ DOCUMENTATION :: END function get_history() { history_table_options.ajax = { url: 'get_history', - type: 'post', + type: 'POST', data: function ( d ) { return { json_data: JSON.stringify( d ), @@ -563,7 +563,7 @@ DOCUMENTATION :: END function get_history() { history_table_options.ajax = { url: 'get_history', - type: 'post', + type: 'POST', data: function ( d ) { return { json_data: JSON.stringify( d ), @@ -579,7 +579,7 @@ DOCUMENTATION :: END function get_history() { history_table_options.ajax = { url: 'get_history', - type: 'post', + type: 'POST', data: function ( d ) { return { json_data: JSON.stringify( d ), diff --git a/data/interfaces/default/libraries.html b/data/interfaces/default/libraries.html index caf34c27..fb5b7d7d 100644 --- a/data/interfaces/default/libraries.html +++ b/data/interfaces/default/libraries.html @@ -91,7 +91,7 @@ json_data: JSON.stringify(d) }; } - } + }; libraries_list_table = $('#libraries_list_table').DataTable(libraries_list_table_options); var colvis = new $.fn.dataTable.ColVis(libraries_list_table, { buttonText: ' Select columns', buttonClass: 'btn btn-dark', exclude: [0, 1] }); diff --git a/data/interfaces/default/library.html b/data/interfaces/default/library.html index 7d2b6481..1382d991 100644 --- a/data/interfaces/default/library.html +++ b/data/interfaces/default/library.html @@ -374,7 +374,7 @@ DOCUMENTATION :: END // Build watch history table history_table_options.ajax = { url: 'get_history', - type: 'post', + type: 'POST', data: function ( d ) { return { json_data: JSON.stringify( d ), @@ -406,7 +406,7 @@ DOCUMENTATION :: END // Build media info table media_info_table_options.ajax = { url: 'get_library_media_info', - type: 'post', + type: 'POST', data: function ( d ) { return { json_data: JSON.stringify( d ), diff --git a/data/interfaces/default/logs.html b/data/interfaces/default/logs.html index 78a96e63..696ac747 100644 --- a/data/interfaces/default/logs.html +++ b/data/interfaces/default/logs.html @@ -229,8 +229,8 @@ var selected_log_level = null; function loadtautullilogs(logfile, selected_log_level) { log_table_options.ajax = { - url: "get_log", - type: 'post', + url: 'get_log', + type: 'POST', data: function (d) { return { logfile: logfile, @@ -249,7 +249,8 @@ function loadPlexLogs() { plex_log_table_options.ajax = { - url: "get_plex_log?log_type=server" + url: 'get_plex_log?log_type=server', + type: 'POST' }; plex_log_table_options.initComplete = bindLogLevelFilter; plex_log_table = $('#plex_log_table').DataTable(plex_log_table_options); @@ -257,7 +258,8 @@ function loadPlexScannerLogs() { plex_log_table_options.ajax = { - url: "get_plex_log?log_type=scanner" + url: 'get_plex_log?log_type=scanner', + type: 'POST' }; plex_log_table_options.initComplete = bindLogLevelFilter; plex_scanner_log_table = $('#plex_scanner_log_table').DataTable(plex_log_table_options); @@ -265,7 +267,8 @@ function loadNotificationLogs() { notification_log_table_options.ajax = { - url: "get_notification_log", + url: 'get_notification_log', + type: 'POST', data: function (d) { return { json_data: JSON.stringify(d) @@ -278,7 +281,8 @@ function loadLoginLogs() { login_log_table_options.pageLength = 50; login_log_table_options.ajax = { - url: "get_user_logins", + url: 'get_user_logins', + type: 'POST', data: function (d) { return { json_data: JSON.stringify(d) diff --git a/data/interfaces/default/sync.html b/data/interfaces/default/sync.html index 3197b01b..e1d71656 100644 --- a/data/interfaces/default/sync.html +++ b/data/interfaces/default/sync.html @@ -100,7 +100,7 @@ // Load user ids and names (for the selector) $.ajax({ url: 'get_user_names', - type: 'get', + type: 'GET', dataType: 'json', success: function (data) { var select = $('#sync-user'); @@ -116,7 +116,8 @@ function loadSyncTable(selected_user_id) { sync_table_options.ajax = { - url: 'get_sync?user_id=' + selected_user_id + url: 'get_sync?user_id=' + selected_user_id, + type: 'POST' }; sync_table = $('#sync_table').DataTable(sync_table_options); var colvis = new $.fn.dataTable.ColVis(sync_table, { diff --git a/data/interfaces/default/user.html b/data/interfaces/default/user.html index 1d76ed6f..251fec10 100644 --- a/data/interfaces/default/user.html +++ b/data/interfaces/default/user.html @@ -413,7 +413,7 @@ DOCUMENTATION :: END // Build watch history table history_table_options.ajax = { url: 'get_history', - type: 'post', + type: 'POST', data: function ( d ) { return { json_data: JSON.stringify( d ), @@ -442,7 +442,8 @@ DOCUMENTATION :: END function loadSyncTable() { // Build user sync table sync_table_options.ajax = { - url: 'get_sync?user_id=' + user_id + url: 'get_sync?user_id=' + user_id, + type: 'POST' }; sync_table = $('#sync_table-UID-${data["user_id"]}').DataTable(sync_table_options); sync_table.column(2).visible(false); @@ -457,7 +458,7 @@ DOCUMENTATION :: END // Build user IP table user_ip_table_options.ajax = { url: 'get_user_ips', - type: 'post', + type: 'POST', data: function ( d ) { return { json_data: JSON.stringify( d ), @@ -474,6 +475,7 @@ DOCUMENTATION :: END // Build user login table login_log_table_options.ajax = { url: 'get_user_logins', + type: 'POST', data: function(d) { return { json_data: JSON.stringify(d), diff --git a/data/interfaces/default/users.html b/data/interfaces/default/users.html index 6c0d5690..926dafe2 100644 --- a/data/interfaces/default/users.html +++ b/data/interfaces/default/users.html @@ -94,7 +94,7 @@ json_data: JSON.stringify(d) }; } - } + }; users_list_table = $('#users_list_table').DataTable(users_list_table_options); var colvis = new $.fn.dataTable.ColVis(users_list_table, { buttonText: ' Select columns', buttonClass: 'btn btn-dark', exclude: [0, 1] }); From 91c647f9aef8297020c21786dd576425cc3acbc6 Mon Sep 17 00:00:00 2001 From: JonnyWong16 Date: Thu, 29 Mar 2018 19:54:43 -0700 Subject: [PATCH 09/16] Show extra type on activity cards --- .../default/current_activity_instance.html | 26 ++++++++++++------- data/interfaces/default/index.html | 16 +++++------- plexpy/common.py | 10 +++++++ plexpy/pmsconnect.py | 8 ++++-- 4 files changed, 40 insertions(+), 20 deletions(-) diff --git a/data/interfaces/default/current_activity_instance.html b/data/interfaces/default/current_activity_instance.html index 1aa409d9..1670077c 100644 --- a/data/interfaces/default/current_activity_instance.html +++ b/data/interfaces/default/current_activity_instance.html @@ -64,7 +64,7 @@ DOCUMENTATION :: END from collections import defaultdict from urllib import quote from plexpy import helpers - from plexpy.common import VIDEO_RESOLUTION_OVERRIDES, AUDIO_CODEC_OVERRIDES + from plexpy.common import VIDEO_RESOLUTION_OVERRIDES, AUDIO_CODEC_OVERRIDES, EXTRA_TYPES import plexpy %> <% @@ -108,7 +108,11 @@ DOCUMENTATION :: END
% elif data['media_type'] in ('photo', 'clip'): -
+ % if data['extra_type']: +
+ % else: +
+ % endif % else:
% endif @@ -301,14 +305,13 @@ DOCUMENTATION :: END
  • Bandwidth
    - % if data['media_type'] != 'photo' and helpers.cast_to_int(data['bandwidth']): + % if data['media_type'] != 'photo' and data['bandwidth'] != 'Unknown': <% bw = helpers.cast_to_int(data['bandwidth']) - if bw != "Unknown": - if bw > 1000: - bw = str(round(bw / 1000.0, 1)) + ' Mbps' - else: - bw = str(bw) + ' kbps' + if bw > 1000: + bw = str(round(bw / 1000.0, 1)) + ' Mbps' + else: + bw = str(bw) + ' kbps' %> ${bw} @@ -440,7 +443,12 @@ DOCUMENTATION :: END % elif data['media_type'] == 'photo': ${data['title']} % else: - ${data['year']} + % if data['extra_type']: + <% extra_type = EXTRA_TYPES.get(data['extra_type'], data['sub_type'].capitalize()) %> + ${data['year']} (${extra_type}) + % else: + ${data['year']} + % endif % endif % elif data['channel_title']: ${data['channel_title']} diff --git a/data/interfaces/default/index.html b/data/interfaces/default/index.html index c7bf5ba3..0df99af8 100644 --- a/data/interfaces/default/index.html +++ b/data/interfaces/default/index.html @@ -515,17 +515,15 @@ $('#location-' + key).html(s.location.toUpperCase()); - if (s.media_type !== 'photo' && parseInt(s.bandwidth)) { - var bw = parseInt(s.bandwidth); - if (bw !== "Unknown") { - if (bw > 1000) { - bw = (bw / 1000).toFixed(1) + ' Mbps'; - } else { - bw = bw + ' kbps' - } + if (s.media_type !== 'photo' && s.bandwidth !== 'Unknown') { + var bw = parseInt(s.bandwidth) || 0; + if (bw > 1000) { + bw = (bw / 1000).toFixed(1) + ' Mbps'; + } else { + bw = bw + ' kbps' } $('#stream-bandwidth-' + key).html(bw); - } + }; // Update the stream progress times $('#stream-eta-' + key).html(moment().add(parseInt(s.duration) - parseInt(s.view_offset), 'milliseconds').format(time_format)); diff --git a/plexpy/common.py b/plexpy/common.py index 553b85d9..0ffd3d65 100644 --- a/plexpy/common.py +++ b/plexpy/common.py @@ -171,6 +171,16 @@ HW_ENCODERS = [ 'nvenc' ] +EXTRA_TYPES = { + '1': 'Trailer', + '2': 'Deleted Scene', + '3': 'Interview', + '5': 'Behind the Scenes', + '6': 'Scene', + '10': 'Featurette', + '11': 'Short' +} + SCHEDULER_LIST = [ 'Check GitHub for updates', 'Check for server response', diff --git a/plexpy/pmsconnect.py b/plexpy/pmsconnect.py index b1de933b..21f8afdb 100644 --- a/plexpy/pmsconnect.py +++ b/plexpy/pmsconnect.py @@ -1096,7 +1096,9 @@ class PmsConnect(object): 'genres': genres, 'labels': labels, 'collections': collections, - 'full_title': helpers.get_xml_attr(metadata_main, 'title') + 'full_title': helpers.get_xml_attr(metadata_main, 'title'), + 'extra_type': helpers.get_xml_attr(metadata_main, 'extraType'), + 'sub_type': helpers.get_xml_attr(metadata_main, 'subtype') } else: @@ -1687,7 +1689,9 @@ class PmsConnect(object): 'audio_channel_layout': common.AUDIO_CHANNELS.get(audio_channels, audio_channels), 'channel_icon': helpers.get_xml_attr(session, 'sourceIcon'), 'channel_title': helpers.get_xml_attr(session, 'sourceTitle'), - 'live': int(helpers.get_xml_attr(session, 'live') == '1') + 'live': int(helpers.get_xml_attr(session, 'live') == '1'), + 'extra_type': helpers.get_xml_attr(session, 'extraType'), + 'sub_type': helpers.get_xml_attr(session, 'subtype') } else: channel_stream = 0 From a69008e1792083408a9e45c3490cb323f66afb77 Mon Sep 17 00:00:00 2001 From: JonnyWong16 Date: Fri, 30 Mar 2018 09:23:38 -0700 Subject: [PATCH 10/16] Send Telegram notification separately if caption is longer than 200 characters (Closes Tautulli/Tautulli-Issues#20) --- plexpy/notifiers.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/plexpy/notifiers.py b/plexpy/notifiers.py index 9e0e44b7..64d57d2e 100644 --- a/plexpy/notifiers.py +++ b/plexpy/notifiers.py @@ -3307,10 +3307,17 @@ class TELEGRAM(Notifier): if poster_content: poster_filename = 'poster_{}.jpg'.format(pretty_metadata.parameters['rating_key']) files = {'photo': (poster_filename, poster_content, 'image/jpeg')} - data['caption'] = text - return self.make_request('https://api.telegram.org/bot{}/sendPhoto'.format(self.config['bot_token']), - data=data, files=files) + if len(text) > 200: + data['disable_notification'] = True + else: + data['caption'] = text + + r = self.make_request('https://api.telegram.org/bot{}/sendPhoto'.format(self.config['bot_token']), + data=data, files=files) + + if not data.pop('disable_notification', None): + return r data['text'] = text From 818e7723ff20badc026905edf67c6d81979408da Mon Sep 17 00:00:00 2001 From: JonnyWong16 Date: Fri, 30 Mar 2018 09:29:47 -0700 Subject: [PATCH 11/16] v2.0.26-beta --- CHANGELOG.md | 11 +++++++++++ plexpy/version.py | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 418c496c..20b931b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## v2.0.26-beta (2018-03-30) + +* Monitoring: + * New: Setting to change the refresh interval on the homepage. + * Fix: Identify extras correctly on the activity cards. +* Notifications: + * Change: Send Telegram image and text separately if the caption is longer than 200 characters. +* UI: + * Fix: Error when clicking on synced playlist links. + + ## v2.0.25 (2018-03-22) * Monitoring: diff --git a/plexpy/version.py b/plexpy/version.py index 97c5050d..99028b9b 100644 --- a/plexpy/version.py +++ b/plexpy/version.py @@ -1,2 +1,2 @@ -PLEXPY_BRANCH = "master" -PLEXPY_RELEASE_VERSION = "v2.0.25" +PLEXPY_BRANCH = "beta" +PLEXPY_RELEASE_VERSION = "v2.0.26-beta" From 1032fdfe7a594a008941ebd89ec5e7f88e020843 Mon Sep 17 00:00:00 2001 From: JonnyWong16 Date: Mon, 2 Apr 2018 14:13:52 -0700 Subject: [PATCH 12/16] Move refresh interval setting back to the settings page --- data/interfaces/default/css/tautulli.css | 16 +++------- data/interfaces/default/index.html | 37 +++++------------------- data/interfaces/default/settings.html | 11 +++++++ plexpy/webserve.py | 7 ++--- 4 files changed, 24 insertions(+), 47 deletions(-) diff --git a/data/interfaces/default/css/tautulli.css b/data/interfaces/default/css/tautulli.css index 7522c517..b392bffa 100644 --- a/data/interfaces/default/css/tautulli.css +++ b/data/interfaces/default/css/tautulli.css @@ -294,6 +294,10 @@ object { font-weight: bold; text-transform: uppercase; } +.padded-header h3 small { + font-size: 13px; + text-transform: none; +} .btn { outline:0px !important; } @@ -2373,18 +2377,6 @@ a .library-user-instance-box:hover { margin-top: 9px; width: 175px; } -.home-padded-header .info-bar { - float: left; - margin-left: 15px; - line-height: 35px; -} -.home-padded-header .info-bar small { - font-size: 13px; - font-weight: normal; - text-transform: none; - line-height: 1; - color: #777; -} .home-padded-header .button-bar { float: left; } diff --git a/data/interfaces/default/index.html b/data/interfaces/default/index.html index 0df99af8..54335be4 100644 --- a/data/interfaces/default/index.html +++ b/data/interfaces/default/index.html @@ -11,15 +11,7 @@
    -

    Current Activity

    -
    -
    - Refresh Every - - seconds -
    -
    -
    +

    Activity    -

    +
    <% from plexpy import PLEX_SERVER_UP %> @@ -591,16 +583,11 @@ } getCurrentActivity(); - - function refreshActivity(seconds) { - return setInterval(function () { - if (!(create_instances.length) && activity_ready) { - getCurrentActivity(); - } - }, seconds * 1000); - } - var refresh_interval = $('#activity-refresh-interval').val(); - var activityRefresh = refreshActivity(refresh_interval); + setInterval(function () { + if (!(create_instances.length) && activity_ready) { + getCurrentActivity(); + } + }, ${config['home_refresh_interval'] * 1000}); setInterval(function(){ $('.progress_time_offset').each(function () { @@ -696,16 +683,6 @@ window.open(sessions_url, '_blank'); }); }); - - $('#activity-refresh-interval').change(function () { - forceMinMax($(this)); - clearInterval(activityRefresh); - refresh_interval = $(this).val(); - activityRefresh = refreshActivity(refresh_interval); - $.post('set_home_stats_config', { refresh_interval: refresh_interval }); - }); - - $('#activity-refresh-interval').tooltip({ container: 'body', placement: 'top', html: true }); % endif % endif diff --git a/data/interfaces/default/settings.html b/data/interfaces/default/settings.html index 4de9c055..e81a1dd9 100644 --- a/data/interfaces/default/settings.html +++ b/data/interfaces/default/settings.html @@ -271,6 +271,17 @@

    Activity

    +
    + +
    +
    + +
    + +
    +

    Set the interval (in seconds) to refresh the current activity on the homepage. Minimum 2.

    +
    +

    Sections

    diff --git a/plexpy/webserve.py b/plexpy/webserve.py index 124b6a46..aed8edbe 100644 --- a/plexpy/webserve.py +++ b/plexpy/webserve.py @@ -286,11 +286,7 @@ class WebInterface(object): @cherrypy.expose @requireAuth(member_of("admin")) - def set_home_stats_config(self, refresh_interval=None, time_range=None, stats_type=None, stats_count=None, - recently_added_count=None, **kwargs): - if refresh_interval: - plexpy.CONFIG.__setattr__('HOME_REFRESH_INTERVAL', refresh_interval) - plexpy.CONFIG.write() + def set_home_stats_config(self, time_range=None, stats_type=None, stats_count=None, recently_added_count=None, **kwargs): if time_range: plexpy.CONFIG.__setattr__('HOME_STATS_LENGTH', time_range) plexpy.CONFIG.write() @@ -2647,6 +2643,7 @@ class WebInterface(object): "home_sections": json.dumps(plexpy.CONFIG.HOME_SECTIONS), "home_stats_cards": json.dumps(plexpy.CONFIG.HOME_STATS_CARDS), "home_library_cards": json.dumps(plexpy.CONFIG.HOME_LIBRARY_CARDS), + "home_refresh_interval": plexpy.CONFIG.HOME_REFRESH_INTERVAL, "buffer_threshold": plexpy.CONFIG.BUFFER_THRESHOLD, "buffer_wait": plexpy.CONFIG.BUFFER_WAIT, "group_history_tables": checked(plexpy.CONFIG.GROUP_HISTORY_TABLES), From 1f587ed698a3a793e4bbf720b14adb87bf882f46 Mon Sep 17 00:00:00 2001 From: JonnyWong16 Date: Mon, 2 Apr 2018 14:17:48 -0700 Subject: [PATCH 13/16] v2.0.27 --- CHANGELOG.md | 6 ++++++ plexpy/version.py | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20b931b7..bff96a3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## v2.0.27 (2018-04-02) + +* Monitoring: + * Change: Move activity refresh interval setting to the settings page. + + ## v2.0.26-beta (2018-03-30) * Monitoring: diff --git a/plexpy/version.py b/plexpy/version.py index 99028b9b..98af6247 100644 --- a/plexpy/version.py +++ b/plexpy/version.py @@ -1,2 +1,2 @@ -PLEXPY_BRANCH = "beta" -PLEXPY_RELEASE_VERSION = "v2.0.26-beta" +PLEXPY_BRANCH = "master" +PLEXPY_RELEASE_VERSION = "v2.0.27" From 7da5730c730c6206ff9d6a9f363d8adc46f97438 Mon Sep 17 00:00:00 2001 From: JonnyWong16 Date: Mon, 2 Apr 2018 14:21:34 -0700 Subject: [PATCH 14/16] Fix activity header text --- data/interfaces/default/css/tautulli.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data/interfaces/default/css/tautulli.css b/data/interfaces/default/css/tautulli.css index b392bffa..71836d63 100644 --- a/data/interfaces/default/css/tautulli.css +++ b/data/interfaces/default/css/tautulli.css @@ -294,10 +294,6 @@ object { font-weight: bold; text-transform: uppercase; } -.padded-header h3 small { - font-size: 13px; - text-transform: none; -} .btn { outline:0px !important; } @@ -2377,6 +2373,10 @@ a .library-user-instance-box:hover { margin-top: 9px; width: 175px; } +.home-padded-header h3 small { + font-size: 13px; + text-transform: none; +} .home-padded-header .button-bar { float: left; } From 793665d62a5a9218e3e50c360527cb6734eb8c88 Mon Sep 17 00:00:00 2001 From: JonnyWong16 Date: Mon, 2 Apr 2018 14:25:54 -0700 Subject: [PATCH 15/16] Revert home activity header --- data/interfaces/default/css/tautulli.css | 8 ++++---- data/interfaces/default/index.html | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/data/interfaces/default/css/tautulli.css b/data/interfaces/default/css/tautulli.css index 71836d63..b392bffa 100644 --- a/data/interfaces/default/css/tautulli.css +++ b/data/interfaces/default/css/tautulli.css @@ -294,6 +294,10 @@ object { font-weight: bold; text-transform: uppercase; } +.padded-header h3 small { + font-size: 13px; + text-transform: none; +} .btn { outline:0px !important; } @@ -2373,10 +2377,6 @@ a .library-user-instance-box:hover { margin-top: 9px; width: 175px; } -.home-padded-header h3 small { - font-size: 13px; - text-transform: none; -} .home-padded-header .button-bar { float: left; } diff --git a/data/interfaces/default/index.html b/data/interfaces/default/index.html index 54335be4..132a9251 100644 --- a/data/interfaces/default/index.html +++ b/data/interfaces/default/index.html @@ -10,7 +10,7 @@ % if section == 'current_activity':
    -
    +

    Activity