diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2ea41a00..d4f734df 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,16 @@
# Changelog
+## v2.0.24 (2018-03-18)
+
+* Monitoring:
+ * Fix: Fix stream data not showing for history recorded before v2.
+* Notifications:
+ * Fix: Set all environment variables for scripts.
+ * Change: Moved all notification agent instructions to the wiki.
+ * Change: XBMC notification agent renamed to Kodi.
+ * Change: OSX Notify notification agent renamed to macOS Notification Center.
+
+
## v2.0.23-beta (2018-03-16)
* Monitoring:
diff --git a/data/interfaces/default/mobile_devices_table.html b/data/interfaces/default/mobile_devices_table.html
index 6ccec57b..df605971 100644
--- a/data/interfaces/default/mobile_devices_table.html
+++ b/data/interfaces/default/mobile_devices_table.html
@@ -55,7 +55,7 @@ DOCUMENTATION :: END
})
}
return deferred;
- }
+ };
function checkQRAddress(url) {
var parser = document.createElement('a');
@@ -82,7 +82,7 @@ DOCUMENTATION :: END
verifiedDevice = false;
getPlexPyURL().then(function (url) {
- checkQRAddress(url)
+ checkQRAddress(url);
$.get('generate_api_key', { device: true }).then(function (token) {
$('#api_qr_address').val(url);
@@ -120,7 +120,7 @@ DOCUMENTATION :: END
$('#api_qr_address').change(function () {
var url = $(this).val();
- checkQRAddress(url)
+ checkQRAddress(url);
$('#api_qr_code').empty().qrcode({
text: url + '|' + $('#api_qr_token').val()
diff --git a/data/interfaces/default/notifier_config.html b/data/interfaces/default/notifier_config.html
index 8514f9ea..9028131e 100644
--- a/data/interfaces/default/notifier_config.html
+++ b/data/interfaces/default/notifier_config.html
@@ -43,9 +43,6 @@
${item['description'] | n}
@@ -431,16 +428,30 @@
});
% if notifier['agent_name'] == 'facebook':
+ if (location.protocol !== 'https:') {
+ $('#tabs-config .form-group:first').prepend(
+ ''
+ );
+ $('#facebook_redirect_uri').val('HTTPS not enabled');
+
+ } else {
+ $('#facebook_redirect_uri').val(location.href.split('/settings')[0] + '/facebook_redirect');
+ }
+
function disableFacebookRequest() {
- if ($('#facebook_app_id').val() !== '' && $('#facebook_app_secret').val() !== '') { $('#facebook_facebookStep1').prop('disabled', false); }
- else { $('#facebook_facebookStep1').prop('disabled', true); }
+ if ($('#facebook_app_id').val() !== '' && $('#facebook_app_secret').val() !== '') { $('#facebook_facebook_auth').prop('disabled', false); }
+ else { $('#facebook_facebook_auth').prop('disabled', true); }
}
disableFacebookRequest();
$('#facebook_app_id, #facebook_app_secret').on('change', function () {
disableFacebookRequest();
});
- $('#facebook_facebookStep1').click(function () {
+ $('#facebook_facebook_auth').click(function () {
// Remove trailing '/' from Facebook redirect URI
if ($('#facebook_redirect_uri') && $('#facebook_redirect_uri').val().endsWith('/')) {
$('#facebook_redirect_uri').val($('#facebook_redirect_uri').val().slice(0, -1));
@@ -448,7 +459,7 @@
var facebook_token;
$.ajax({
- url: 'facebookStep1',
+ url: 'facebook_auth',
data: {
app_id: $('#facebook_app_id').val(),
app_secret: $('#facebook_app_secret').val(),
@@ -506,7 +517,7 @@
});
% elif notifier['agent_name'] == 'osx':
- $('#osxnotifyregister').click(function () {
+ $('#osx_notify_register').click(function () {
var osx_notify_app = $('#osx_notify_app').val();
$.get('osxnotifyregister', { 'app': osx_notify_app }, function (data) { showMsg(' ' + data, false, true, 3000); });
});
diff --git a/data/interfaces/default/notifiers_table.html b/data/interfaces/default/notifiers_table.html
index 32f055c2..da0cb624 100644
--- a/data/interfaces/default/notifiers_table.html
+++ b/data/interfaces/default/notifiers_table.html
@@ -10,7 +10,7 @@ DOCUMENTATION :: END
%doc>
- % for notifier in sorted(notifiers_list, key=lambda k: (k['agent_label'], k['friendly_name'], k['id'])):
+ % for notifier in sorted(notifiers_list, key=lambda k: (k['agent_label'].lower(), k['friendly_name'], k['id'])):
-
diff --git a/data/interfaces/default/settings.html b/data/interfaces/default/settings.html
index ab56dd60..781f8ecc 100644
--- a/data/interfaces/default/settings.html
+++ b/data/interfaces/default/settings.html
@@ -971,6 +971,9 @@
Add a new notification agent, or configure an existing notification agent by clicking the settings icon on the right.
+
+ Please see the Notification Agents Guide for instructions on setting up each notification agent.
+
Loading notification agents...
@@ -1002,7 +1005,7 @@
Database Import
- Click a button below to import an exisiting database from another app.
+ Click a button below to import an existing database from another app.
@@ -1249,7 +1252,7 @@
- % for agent in available_notification_agents:
+ % for agent in sorted(available_notification_agents, key=lambda k: k['label'].lower()):
-
${agent['label']}
diff --git a/data/interfaces/default/stream_data.html b/data/interfaces/default/stream_data.html
index 5ec03824..64d20788 100644
--- a/data/interfaces/default/stream_data.html
+++ b/data/interfaces/default/stream_data.html
@@ -58,6 +58,10 @@ DOCUMENTATION :: END
Current session. Updated stream details below may be delayed.
+ % elif data['pre_tautulli']:
+
+ Pre-Tautulli history. Stream details below may be incorrect.
+
% endif
@@ -84,8 +88,8 @@ DOCUMENTATION :: END
Bitrate |
- ${data['stream_bitrate']} kbps |
- ${data['bitrate']} kbps |
+ ${data['stream_bitrate']} ${'kbps' if data['stream_bitrate'] else ''} |
+ ${data['bitrate']} ${'kbps' if data['bitrate'] else ''} |
% if data['media_type'] != 'track':
@@ -154,8 +158,8 @@ DOCUMENTATION :: END
Bitrate |
- ${data['stream_video_bitrate']} kbps |
- ${data['video_bitrate']} kbps |
+ ${data['stream_video_bitrate']} ${'kbps' if data['stream_video_bitrate'] else ''} |
+ ${data['video_bitrate']} ${'kbps' if data['video_bitrate'] else ''} |
Width |
@@ -199,8 +203,8 @@ DOCUMENTATION :: END
Bitrate |
- ${data['stream_audio_bitrate']} kbps |
- ${data['audio_bitrate']} kbps |
+ ${data['stream_audio_bitrate']} ${'kbps' if data['stream_audio_bitrate'] else ''} |
+ ${data['audio_bitrate']} ${'kbps' if data['audio_bitrate'] else ''} |
Channels |
diff --git a/plexpy/__init__.py b/plexpy/__init__.py
index 54d12682..09f87f26 100644
--- a/plexpy/__init__.py
+++ b/plexpy/__init__.py
@@ -1622,6 +1622,15 @@ def dbcheck():
'ALTER TABLE poster_urls ADD COLUMN delete_hash TEXT'
)
+ # Rename notifiers in the database
+ logger.debug(u"Altering database. Renaming notifiers.")
+ c_db.execute(
+ 'UPDATE notifiers SET agent_label = "Kodi" WHERE agent_label = "XBMC"'
+ )
+ c_db.execute(
+ 'UPDATE notifiers SET agent_label = "macOS Notification Center" WHERE agent_label = "OSX Notify"'
+ )
+
# Add "Local" user to database as default unauthenticated user.
result = c_db.execute('SELECT id FROM users WHERE username = "Local"')
if not result.fetchone():
diff --git a/plexpy/datafactory.py b/plexpy/datafactory.py
index a972ca1b..423a994c 100644
--- a/plexpy/datafactory.py
+++ b/plexpy/datafactory.py
@@ -885,6 +885,9 @@ class DataFactory(object):
'stream_audio_decision, stream_audio_codec, stream_audio_bitrate, stream_audio_channels, ' \
'subtitles, stream_subtitle_decision, stream_subtitle_codec, ' \
'transcode_hw_decoding, transcode_hw_encoding, ' \
+ 'video_decision, audio_decision, transcode_decision, width, height, container, ' \
+ 'transcode_container, transcode_video_codec, transcode_audio_codec, transcode_audio_channels, ' \
+ 'transcode_width, transcode_height, ' \
'session_history_metadata.media_type, title, grandparent_title ' \
'FROM session_history_media_info ' \
'JOIN session_history ON session_history_media_info.id = session_history.id ' \
@@ -903,6 +906,9 @@ class DataFactory(object):
'stream_audio_decision, stream_audio_codec, stream_audio_bitrate, stream_audio_channels, ' \
'subtitles, stream_subtitle_decision, stream_subtitle_codec, ' \
'transcode_hw_decoding, transcode_hw_encoding, ' \
+ 'video_decision, audio_decision, transcode_decision, width, height, container, ' \
+ 'transcode_container, transcode_video_codec, transcode_audio_codec, transcode_audio_channels, ' \
+ 'transcode_width, transcode_height, ' \
'media_type, title, grandparent_title ' \
'FROM sessions ' \
'WHERE session_key = ? %s' % user_cond
@@ -913,6 +919,23 @@ class DataFactory(object):
stream_output = {}
for item in result:
+ pre_tautulli = 0
+
+ # For backwards compatibility. Pick one new Tautulli key to check and override with old values.
+ if not item['stream_video_resolution']:
+ item['stream_video_resolution'] = item['video_resolution']
+ item['stream_container'] = item['transcode_container'] or item['container']
+ item['stream_video_decision'] = item['video_decision']
+ item['stream_video_codec'] = item['transcode_video_codec'] or item['video_codec']
+ item['stream_video_width'] = item['transcode_width'] or item['width']
+ item['stream_video_height'] = item['transcode_height'] or item['height']
+ item['stream_audio_decision'] = item['audio_decision']
+ item['stream_audio_codec'] = item['transcode_audio_codec'] or item['audio_codec']
+ item['stream_audio_channels'] = item['transcode_audio_channels'] or item['audio_channels']
+ item['video_width'] = item['width']
+ item['video_height'] = item['height']
+ pre_tautulli = 1
+
stream_output = {'bitrate': item['bitrate'],
'video_resolution': item['video_resolution'],
'optimized_version': item['optimized_version'],
@@ -951,10 +974,13 @@ class DataFactory(object):
'stream_subtitle_codec': item['stream_subtitle_codec'],
'transcode_hw_decoding': item['transcode_hw_decoding'],
'transcode_hw_encoding': item['transcode_hw_encoding'],
+ 'video_decision': item['video_decision'],
+ 'audio_decision': item['audio_decision'],
'media_type': item['media_type'],
'title': item['title'],
'grandparent_title': item['grandparent_title'],
- 'current_session': 1 if session_key else 0
+ 'current_session': 1 if session_key else 0,
+ 'pre_tautulli': pre_tautulli
}
stream_output = {k: v or '' for k, v in stream_output.iteritems()}
diff --git a/plexpy/notifiers.py b/plexpy/notifiers.py
index 6757d15f..84570a6c 100644
--- a/plexpy/notifiers.py
+++ b/plexpy/notifiers.py
@@ -143,6 +143,10 @@ def available_notification_agents():
'name': 'join',
'id': AGENT_IDS['join']
},
+ {'label': 'Kodi',
+ 'name': 'xbmc',
+ 'id': AGENT_IDS['xbmc']
+ },
{'label': 'Notify My Android',
'name': 'nma',
'id': AGENT_IDS['nma']
@@ -159,10 +163,10 @@ def available_notification_agents():
'name': 'prowl',
'id': AGENT_IDS['prowl']
},
- {'label': 'Pushalot',
- 'name': 'pushalot',
- 'id': AGENT_IDS['pushalot']
- },
+ # {'label': 'Pushalot',
+ # 'name': 'pushalot',
+ # 'id': AGENT_IDS['pushalot']
+ # },
{'label': 'Pushbullet',
'name': 'pushbullet',
'id': AGENT_IDS['pushbullet']
@@ -187,10 +191,6 @@ def available_notification_agents():
'name': 'twitter',
'id': AGENT_IDS['twitter']
},
- {'label': 'XBMC',
- 'name': 'xbmc',
- 'id': AGENT_IDS['xbmc']
- },
{'label': 'Zapier',
'name': 'zapier',
'id': AGENT_IDS['zapier']
@@ -199,7 +199,7 @@ def available_notification_agents():
# OSX Notifications should only be visible if it can be used
if OSX().validate():
- agents.append({'label': 'OSX Notify',
+ agents.append({'label': 'macOS Notification Center',
'name': 'osx',
'id': AGENT_IDS['osx']
})
@@ -928,11 +928,9 @@ class ANDROIDAPP(Notifier):
'The content of your notifications will be sent unencrypted!
'
'Please install the library to encrypt the notification contents. '
'Instructions can be found in the '
- 'FAQ.',
+ 'FAQ.' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO),
'input_type': 'help'
})
else:
@@ -1454,7 +1452,7 @@ class FACEBOOK(Notifier):
plexpy.CONFIG.FACEBOOK_TOKEN = 'temp'
return facebook.auth_url(app_id=app_id,
- canvas_url=redirect_uri + '/facebookStep2',
+ canvas_url=redirect_uri,
perms=['user_managed_groups','publish_actions'])
def _get_credentials(self, code=''):
@@ -1468,7 +1466,7 @@ class FACEBOOK(Notifier):
# Request user access token
api = facebook.GraphAPI(version='2.12')
response = api.get_access_token_from_code(code=code,
- redirect_uri=redirect_uri + '/facebookStep2',
+ redirect_uri=redirect_uri,
app_id=app_id,
app_secret=app_secret)
access_token = response['access_token']
@@ -1532,25 +1530,11 @@ class FACEBOOK(Notifier):
return self._post_facebook(**data)
def return_config_options(self):
- config_option = [{'label': 'Instructions',
- 'description': 'Step 1: Visit '
- 'Facebook Developers to add a new app using basic setup.
'
- 'Step 2: Click Add Product on the left, then Get Started'
- 'for Facebook Login.
'
- 'Step 3: Fill in Valid OAuth redirect URIs with your Tautulli URL (e.g. http://localhost:8181).
'
- 'Step 4: Click App Review on the left and toggle "make public" to Yes.
'
- 'Step 5: Fill in the Tautulli URL below with the exact same URL from Step 3.
'
- 'Step 6: Fill in the App ID and App Secret below.
'
- 'Step 7: Click the Request Authorization button below to retrieve your access token.
'
- 'Step 8: Fill in your Access Token below if it is not filled in automatically.
'
- 'Step 9: Fill in your Group ID number below. It can be found in the URL of your group page.',
- 'input_type': 'help'
- },
- {'label': 'Tautulli URL',
+ config_option = [{'label': 'OAuth Redirect URI',
'value': self.config['redirect_uri'],
'name': 'facebook_redirect_uri',
- 'description': 'Your Tautulli URL. This will tell Facebook where to redirect you after authorization.\
- (e.g. http://localhost:8181)',
+ 'description': 'Fill in this address for the "Valid OAuth redirect URIs" '
+ 'in your Facebook App.',
'input_type': 'text'
},
{'label': 'Facebook App ID',
@@ -1567,14 +1551,15 @@ class FACEBOOK(Notifier):
},
{'label': 'Request Authorization',
'value': 'Request Authorization',
- 'name': 'facebook_facebookStep1',
+ 'name': 'facebook_facebook_auth',
'description': 'Request Facebook authorization. (Ensure you allow the browser pop-up).',
'input_type': 'button'
},
{'label': 'Facebook Access Token',
'value': self.config['access_token'],
'name': 'facebook_access_token',
- 'description': 'Your Facebook access token. Automatically filled in after requesting authorization.',
+ 'description': 'Your Facebook access token. '
+ 'Automatically filled in after requesting authorization.',
'input_type': 'text'
},
{'label': 'Facebook Group ID',
@@ -1773,7 +1758,7 @@ class GROWL(Notifier):
config_option = [{'label': 'Growl Host',
'value': self.config['host'],
'name': 'growl_host',
- 'description': 'Your Growl hostname.',
+ 'description': 'Your Growl hostname or IP address.',
'input_type': 'text'
},
{'label': 'Growl Password',
@@ -1875,7 +1860,7 @@ class HIPCHAT(Notifier):
return self.make_request(self.config['hook'], headers=headers, json=data)
def return_config_options(self):
- config_option = [{'label': 'Hipchat Custom Integrations Full URL',
+ config_option = [{'label': 'Hipchat Custom Integrations URL',
'value': self.config['hook'],
'name': 'hipchat_hook',
'description': 'Your Hipchat BYO integration URL. You can get a key from'
@@ -2330,9 +2315,9 @@ class NMA(Notifier):
class OSX(Notifier):
"""
- OSX notifications
+ macOS notifications
"""
- NAME = 'OSX Notify'
+ NAME = 'macOS'
_DEFAULT_CONFIG = {'notify_app': '/Applications/Tautulli'
}
@@ -2415,9 +2400,15 @@ class OSX(Notifier):
config_option = [{'label': 'Register Notify App',
'value': self.config['notify_app'],
'name': 'osx_notify_app',
- 'description': 'Enter the path/application name to be registered with the '
- 'Notification Center, default is /Applications/Tautulli.',
+ 'description': 'Enter the path/application name to be registered with the Notification Center. '
+ 'Default is /Applications/Tautulli.',
'input_type': 'text'
+ },
+ {'label': 'Register App',
+ 'value': 'Register App',
+ 'name': 'osx_notify_register',
+ 'description': 'Register Tautulli with the Notification Center.',
+ 'input_type': 'button'
}
]
@@ -2498,7 +2489,7 @@ class PLEX(Notifier):
return True
def return_config_options(self):
- config_option = [{'label': 'Plex Home Theater Host:Port',
+ config_option = [{'label': 'Plex Home Theater Host Address',
'value': self.config['hosts'],
'name': 'plex_hosts',
'description': 'Host running Plex Home Theater (eg. http://localhost:3005). Separate multiple hosts with commas (,).',
@@ -2689,10 +2680,10 @@ class PUSHBULLET(Notifier):
return {'': ''}
def return_config_options(self):
- config_option = [{'label': 'Pushbullet API Key',
+ config_option = [{'label': 'Pushbullet Access Token',
'value': self.config['api_key'],
'name': 'pushbullet_api_key',
- 'description': 'Your Pushbullet API key.',
+ 'description': 'Your Pushbullet access token.',
'input_type': 'text',
'refresh': True
},
@@ -2974,6 +2965,7 @@ class SCRIPTS(Notifier):
'TAUTULLI_URL': helpers.get_plexpy_url(hostname='localhost'),
'TAUTULLI_APIKEY': plexpy.CONFIG.API_KEY
}
+ env.update(os.environ)
self.script_killed = False
output = error = ''
@@ -3439,16 +3431,7 @@ class TWITTER(Notifier):
return self._send_tweet(body, attachment=poster_url)
def return_config_options(self):
- config_option = [{'label': 'Instructions',
- 'description': 'Step 1: Visit '
- 'Twitter Apps to Create New App. A vaild "Website" is not required.
'
- 'Step 2: Go to Keys and Access Tokens and click '
- 'Create my access token.
'
- 'Step 3: Fill in the Consumer Key, Consumer Secret, '
- 'Access Token, and Access Token Secret below.',
- 'input_type': 'help'
- },
- {'label': 'Twitter Consumer Key',
+ config_option = [{'label': 'Twitter Consumer Key',
'value': self.config['consumer_key'],
'name': 'twitter_consumer_key',
'description': 'Your Twitter consumer key.',
@@ -3492,9 +3475,9 @@ class TWITTER(Notifier):
class XBMC(Notifier):
"""
- XBMC notifications
+ Kodi notifications
"""
- NAME = 'XBMC'
+ NAME = 'Kodi'
_DEFAULT_CONFIG = {'hosts': '',
'username': '',
'password': '',
@@ -3564,22 +3547,22 @@ class XBMC(Notifier):
return True
def return_config_options(self):
- config_option = [{'label': 'XBMC Host:Port',
+ config_option = [{'label': 'Kodi Host Address',
'value': self.config['hosts'],
'name': 'xbmc_hosts',
- 'description': 'Host running XBMC (e.g. http://localhost:8080). Separate multiple hosts with commas (,).',
+ 'description': 'Host running Kodi (e.g. http://localhost:8080). Separate multiple hosts with commas (,).',
'input_type': 'text'
},
- {'label': 'XBMC Username',
+ {'label': 'Kodi Username',
'value': self.config['username'],
'name': 'xbmc_username',
- 'description': 'Username of your XBMC client API (blank for none).',
+ 'description': 'Username of your Kodi client API (blank for none).',
'input_type': 'text'
},
- {'label': 'XBMC Password',
+ {'label': 'Kodi Password',
'value': self.config['password'],
'name': 'xbmc_password',
- 'description': 'Password of your XBMC client API (blank for none).',
+ 'description': 'Password of your Kodi client API (blank for none).',
'input_type': 'password'
},
{'label': 'Notification Duration',
diff --git a/plexpy/version.py b/plexpy/version.py
index b2f01ea3..cd47c2a9 100644
--- a/plexpy/version.py
+++ b/plexpy/version.py
@@ -1,2 +1,2 @@
-PLEXPY_BRANCH = "beta"
-PLEXPY_RELEASE_VERSION = "v2.0.23-beta"
+PLEXPY_BRANCH = "master"
+PLEXPY_RELEASE_VERSION = "v2.0.24"
diff --git a/plexpy/webserve.py b/plexpy/webserve.py
index 3c7cf050..7e9077f8 100644
--- a/plexpy/webserve.py
+++ b/plexpy/webserve.py
@@ -3193,7 +3193,7 @@ class WebInterface(object):
@cherrypy.expose
@cherrypy.tools.json_out()
@requireAuth(member_of("admin"))
- def facebookStep1(self, app_id='', app_secret='', redirect_uri='', **kwargs):
+ def facebook_auth(self, app_id='', app_secret='', redirect_uri='', **kwargs):
cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store"
facebook_notifier = notifiers.FACEBOOK()
@@ -3208,7 +3208,7 @@ class WebInterface(object):
@cherrypy.expose
@requireAuth(member_of("admin"))
- def facebookStep2(self, code='', **kwargs):
+ def facebook_redirect(self, code='', **kwargs):
cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store"
facebook = notifiers.FACEBOOK()
@@ -5151,10 +5151,10 @@ class WebInterface(object):
quote_list = ['To crush your enemies, see them driven before you, and to hear the lamentation of their women!',
'Your clothes, give them to me, now!',
'Do it!',
- 'If it bleeds, we can kill it',
+ 'If it bleeds, we can kill it.',
'See you at the party Richter!',
- 'Let off some steam, Bennett',
- 'I\'ll be back',
+ 'Let off some steam, Bennett.',
+ 'I\'ll be back.',
'Get to the chopper!',
'Hasta La Vista, Baby!',
'It\'s not a tumor!',
@@ -5175,7 +5175,7 @@ class WebInterface(object):
'What killed the dinosaurs? The Ice Age!',
'That\'s for sleeping with my wife!',
'Remember when I said I\'d kill you last... I lied!',
- 'You want to be a farmer? Here\'s a couple of acres',
+ 'You want to be a farmer? Here\'s a couple of acres.',
'Now, this is the plan. Get your ass to Mars.',
'I just had a terrible thought... What if this is a dream?',
'Well, listen to this one: Rubber baby buggy bumpers!',