Merge branch 'nightly' into v2-newsletter

This commit is contained in:
JonnyWong16 2018-03-18 17:49:14 -07:00
commit b6bd305694
11 changed files with 138 additions and 91 deletions

View file

@ -1,5 +1,16 @@
# Changelog # 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) ## v2.0.23-beta (2018-03-16)
* Monitoring: * Monitoring:

View file

@ -55,7 +55,7 @@ DOCUMENTATION :: END
}) })
} }
return deferred; return deferred;
} };
function checkQRAddress(url) { function checkQRAddress(url) {
var parser = document.createElement('a'); var parser = document.createElement('a');
@ -82,7 +82,7 @@ DOCUMENTATION :: END
verifiedDevice = false; verifiedDevice = false;
getPlexPyURL().then(function (url) { getPlexPyURL().then(function (url) {
checkQRAddress(url) checkQRAddress(url);
$.get('generate_api_key', { device: true }).then(function (token) { $.get('generate_api_key', { device: true }).then(function (token) {
$('#api_qr_address').val(url); $('#api_qr_address').val(url);
@ -120,7 +120,7 @@ DOCUMENTATION :: END
$('#api_qr_address').change(function () { $('#api_qr_address').change(function () {
var url = $(this).val(); var url = $(this).val();
checkQRAddress(url) checkQRAddress(url);
$('#api_qr_code').empty().qrcode({ $('#api_qr_code').empty().qrcode({
text: url + '|' + $('#api_qr_token').val() text: url + '|' + $('#api_qr_token').val()

View file

@ -43,9 +43,6 @@
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<input type="${item['input_type']}" class="form-control" id="${item['name']}" name="${item['name']}" value="${item['value']}" size="30" ${'readonly' if item.get('readonly') else ''}> <input type="${item['input_type']}" class="form-control" id="${item['name']}" name="${item['name']}" value="${item['value']}" size="30" ${'readonly' if item.get('readonly') else ''}>
% if item['name'] == 'osx_notify_app':
<a href="javascript:void(0)" id="osxnotifyregister">Register</a>
% endif
</div> </div>
</div> </div>
<p class="help-block">${item['description'] | n}</p> <p class="help-block">${item['description'] | n}</p>
@ -431,16 +428,30 @@
}); });
% if notifier['agent_name'] == 'facebook': % if notifier['agent_name'] == 'facebook':
if (location.protocol !== 'https:') {
$('#tabs-config .form-group:first').prepend(
'<div class="form-group">' +
'<label>Warning</label>' +
'<p class="help-block" style="color: #eb8600;">Facebook requires HTTPS for authorization. ' +
'Please enable HTTPS for Tautulli under <a data-tab-destination="tabs-web_interface" data-dismiss="modal" style="cursor: pointer;">Web Interface</a>.</p>' +
'</div>'
);
$('#facebook_redirect_uri').val('HTTPS not enabled');
} else {
$('#facebook_redirect_uri').val(location.href.split('/settings')[0] + '/facebook_redirect');
}
function disableFacebookRequest() { function disableFacebookRequest() {
if ($('#facebook_app_id').val() !== '' && $('#facebook_app_secret').val() !== '') { $('#facebook_facebookStep1').prop('disabled', false); } if ($('#facebook_app_id').val() !== '' && $('#facebook_app_secret').val() !== '') { $('#facebook_facebook_auth').prop('disabled', false); }
else { $('#facebook_facebookStep1').prop('disabled', true); } else { $('#facebook_facebook_auth').prop('disabled', true); }
} }
disableFacebookRequest(); disableFacebookRequest();
$('#facebook_app_id, #facebook_app_secret').on('change', function () { $('#facebook_app_id, #facebook_app_secret').on('change', function () {
disableFacebookRequest(); disableFacebookRequest();
}); });
$('#facebook_facebookStep1').click(function () { $('#facebook_facebook_auth').click(function () {
// Remove trailing '/' from Facebook redirect URI // Remove trailing '/' from Facebook redirect URI
if ($('#facebook_redirect_uri') && $('#facebook_redirect_uri').val().endsWith('/')) { if ($('#facebook_redirect_uri') && $('#facebook_redirect_uri').val().endsWith('/')) {
$('#facebook_redirect_uri').val($('#facebook_redirect_uri').val().slice(0, -1)); $('#facebook_redirect_uri').val($('#facebook_redirect_uri').val().slice(0, -1));
@ -448,7 +459,7 @@
var facebook_token; var facebook_token;
$.ajax({ $.ajax({
url: 'facebookStep1', url: 'facebook_auth',
data: { data: {
app_id: $('#facebook_app_id').val(), app_id: $('#facebook_app_id').val(),
app_secret: $('#facebook_app_secret').val(), app_secret: $('#facebook_app_secret').val(),
@ -506,7 +517,7 @@
}); });
% elif notifier['agent_name'] == 'osx': % elif notifier['agent_name'] == 'osx':
$('#osxnotifyregister').click(function () { $('#osx_notify_register').click(function () {
var osx_notify_app = $('#osx_notify_app').val(); var osx_notify_app = $('#osx_notify_app').val();
$.get('osxnotifyregister', { 'app': osx_notify_app }, function (data) { showMsg('<i class="fa fa-check"></i> ' + data, false, true, 3000); }); $.get('osxnotifyregister', { 'app': osx_notify_app }, function (data) { showMsg('<i class="fa fa-check"></i> ' + data, false, true, 3000); });
}); });

View file

@ -10,7 +10,7 @@ DOCUMENTATION :: END
</%doc> </%doc>
<ul class="stacked-configs list-unstyled"> <ul class="stacked-configs list-unstyled">
% 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'])):
<li class="notification-agent" data-id="${notifier['id']}"> <li class="notification-agent" data-id="${notifier['id']}">
<span> <span>
<span class="toggle-left trigger-tooltip ${'active' if notifier['active'] else ''}" data-toggle="tooltip" data-placement="top" title="Triggers ${'active' if notifier['active'] else 'inactive'}"><i class="fa fa-lg fa-bell"></i></span> <span class="toggle-left trigger-tooltip ${'active' if notifier['active'] else ''}" data-toggle="tooltip" data-placement="top" title="Triggers ${'active' if notifier['active'] else 'inactive'}"><i class="fa fa-lg fa-bell"></i></span>

View file

@ -971,6 +971,9 @@
<p class="help-block"> <p class="help-block">
Add a new notification agent, or configure an existing notification agent by clicking the settings icon on the right. Add a new notification agent, or configure an existing notification agent by clicking the settings icon on the right.
</p> </p>
<p class="help-block">
Please see the <a target='_blank' href='${anon_url('https://github.com/%s/%s-Wiki/wiki/Notification-Agents-Guide' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO))}'>Notification Agents Guide</a> for instructions on setting up each notification agent.
</p>
<br /> <br />
<div id="plexpy-notifiers-table"> <div id="plexpy-notifiers-table">
<div class='text-muted'><i class="fa fa-refresh fa-spin"></i> Loading notification agents...</div> <div class='text-muted'><i class="fa fa-refresh fa-spin"></i> Loading notification agents...</div>
@ -1002,7 +1005,7 @@
<h3>Database Import</h3> <h3>Database Import</h3>
</div> </div>
<p class="help-block">Click a button below to import an exisiting database from another app.</p> <p class="help-block">Click a button below to import an existing database from another app.</p>
<div class="btn-group"> <div class="btn-group">
<button class="btn btn-form toggle-app-import-modal" type="button" data-target="#app-import-modal" data-toggle="modal" data-app="plexwatch">PlexWatch</button> <button class="btn btn-form toggle-app-import-modal" type="button" data-target="#app-import-modal" data-toggle="modal" data-app="plexwatch">PlexWatch</button>
<button class="btn btn-form toggle-app-import-modal" type="button" data-target="#app-import-modal" data-toggle="modal" data-app="plexivity">Plexivity</button> <button class="btn btn-form toggle-app-import-modal" type="button" data-target="#app-import-modal" data-toggle="modal" data-app="plexivity">Plexivity</button>
@ -1249,7 +1252,7 @@
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<ul class="stacked-configs list-unstyled"> <ul class="stacked-configs list-unstyled">
% for agent in available_notification_agents: % for agent in sorted(available_notification_agents, key=lambda k: k['label'].lower()):
<li class="new-notification-agent" data-id="${agent['id']}"> <li class="new-notification-agent" data-id="${agent['id']}">
<span>${agent['label']}</span> <span>${agent['label']}</span>
</li> </li>

View file

@ -58,6 +58,10 @@ DOCUMENTATION :: END
<div class="col-sm-12 text-muted stream-info-current"> <div class="col-sm-12 text-muted stream-info-current">
<i class="fa fa-exclamation-circle"></i> Current session. Updated stream details below may be delayed. <i class="fa fa-exclamation-circle"></i> Current session. Updated stream details below may be delayed.
</div> </div>
% elif data['pre_tautulli']:
<div class="col-sm-12 text-muted stream-info-current">
<i class="fa fa-exclamation-circle"></i> Pre-Tautulli history. Stream details below may be incorrect.
</div>
% endif % endif
<table class="stream-info" style="margin-top: 0;"> <table class="stream-info" style="margin-top: 0;">
<thead> <thead>
@ -84,8 +88,8 @@ DOCUMENTATION :: END
<tbody> <tbody>
<tr> <tr>
<td>Bitrate</td> <td>Bitrate</td>
<td>${data['stream_bitrate']} kbps</td> <td>${data['stream_bitrate']} ${'kbps' if data['stream_bitrate'] else ''}</td>
<td>${data['bitrate']} kbps</td> <td>${data['bitrate']} ${'kbps' if data['bitrate'] else ''}</td>
</tr> </tr>
% if data['media_type'] != 'track': % if data['media_type'] != 'track':
<tr> <tr>
@ -154,8 +158,8 @@ DOCUMENTATION :: END
</tr> </tr>
<tr> <tr>
<td>Bitrate</td> <td>Bitrate</td>
<td>${data['stream_video_bitrate']} kbps</td> <td>${data['stream_video_bitrate']} ${'kbps' if data['stream_video_bitrate'] else ''}</td>
<td>${data['video_bitrate']} kbps</td> <td>${data['video_bitrate']} ${'kbps' if data['video_bitrate'] else ''}</td>
</tr> </tr>
<tr> <tr>
<td>Width</td> <td>Width</td>
@ -199,8 +203,8 @@ DOCUMENTATION :: END
</tr> </tr>
<tr> <tr>
<td>Bitrate</td> <td>Bitrate</td>
<td>${data['stream_audio_bitrate']} kbps</td> <td>${data['stream_audio_bitrate']} ${'kbps' if data['stream_audio_bitrate'] else ''}</td>
<td>${data['audio_bitrate']} kbps</td> <td>${data['audio_bitrate']} ${'kbps' if data['audio_bitrate'] else ''}</td>
</tr> </tr>
<tr> <tr>
<td>Channels</td> <td>Channels</td>

View file

@ -1622,6 +1622,15 @@ def dbcheck():
'ALTER TABLE poster_urls ADD COLUMN delete_hash TEXT' '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. # Add "Local" user to database as default unauthenticated user.
result = c_db.execute('SELECT id FROM users WHERE username = "Local"') result = c_db.execute('SELECT id FROM users WHERE username = "Local"')
if not result.fetchone(): if not result.fetchone():

View file

@ -885,6 +885,9 @@ class DataFactory(object):
'stream_audio_decision, stream_audio_codec, stream_audio_bitrate, stream_audio_channels, ' \ 'stream_audio_decision, stream_audio_codec, stream_audio_bitrate, stream_audio_channels, ' \
'subtitles, stream_subtitle_decision, stream_subtitle_codec, ' \ 'subtitles, stream_subtitle_decision, stream_subtitle_codec, ' \
'transcode_hw_decoding, transcode_hw_encoding, ' \ '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 ' \ 'session_history_metadata.media_type, title, grandparent_title ' \
'FROM session_history_media_info ' \ 'FROM session_history_media_info ' \
'JOIN session_history ON session_history_media_info.id = session_history.id ' \ '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, ' \ 'stream_audio_decision, stream_audio_codec, stream_audio_bitrate, stream_audio_channels, ' \
'subtitles, stream_subtitle_decision, stream_subtitle_codec, ' \ 'subtitles, stream_subtitle_decision, stream_subtitle_codec, ' \
'transcode_hw_decoding, transcode_hw_encoding, ' \ '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 ' \ 'media_type, title, grandparent_title ' \
'FROM sessions ' \ 'FROM sessions ' \
'WHERE session_key = ? %s' % user_cond 'WHERE session_key = ? %s' % user_cond
@ -913,6 +919,23 @@ class DataFactory(object):
stream_output = {} stream_output = {}
for item in result: 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'], stream_output = {'bitrate': item['bitrate'],
'video_resolution': item['video_resolution'], 'video_resolution': item['video_resolution'],
'optimized_version': item['optimized_version'], 'optimized_version': item['optimized_version'],
@ -951,10 +974,13 @@ class DataFactory(object):
'stream_subtitle_codec': item['stream_subtitle_codec'], 'stream_subtitle_codec': item['stream_subtitle_codec'],
'transcode_hw_decoding': item['transcode_hw_decoding'], 'transcode_hw_decoding': item['transcode_hw_decoding'],
'transcode_hw_encoding': item['transcode_hw_encoding'], 'transcode_hw_encoding': item['transcode_hw_encoding'],
'video_decision': item['video_decision'],
'audio_decision': item['audio_decision'],
'media_type': item['media_type'], 'media_type': item['media_type'],
'title': item['title'], 'title': item['title'],
'grandparent_title': item['grandparent_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()} stream_output = {k: v or '' for k, v in stream_output.iteritems()}

View file

@ -143,6 +143,10 @@ def available_notification_agents():
'name': 'join', 'name': 'join',
'id': AGENT_IDS['join'] 'id': AGENT_IDS['join']
}, },
{'label': 'Kodi',
'name': 'xbmc',
'id': AGENT_IDS['xbmc']
},
{'label': 'Notify My Android', {'label': 'Notify My Android',
'name': 'nma', 'name': 'nma',
'id': AGENT_IDS['nma'] 'id': AGENT_IDS['nma']
@ -159,10 +163,10 @@ def available_notification_agents():
'name': 'prowl', 'name': 'prowl',
'id': AGENT_IDS['prowl'] 'id': AGENT_IDS['prowl']
}, },
{'label': 'Pushalot', # {'label': 'Pushalot',
'name': 'pushalot', # 'name': 'pushalot',
'id': AGENT_IDS['pushalot'] # 'id': AGENT_IDS['pushalot']
}, # },
{'label': 'Pushbullet', {'label': 'Pushbullet',
'name': 'pushbullet', 'name': 'pushbullet',
'id': AGENT_IDS['pushbullet'] 'id': AGENT_IDS['pushbullet']
@ -187,10 +191,6 @@ def available_notification_agents():
'name': 'twitter', 'name': 'twitter',
'id': AGENT_IDS['twitter'] 'id': AGENT_IDS['twitter']
}, },
{'label': 'XBMC',
'name': 'xbmc',
'id': AGENT_IDS['xbmc']
},
{'label': 'Zapier', {'label': 'Zapier',
'name': 'zapier', 'name': 'zapier',
'id': AGENT_IDS['zapier'] 'id': AGENT_IDS['zapier']
@ -199,7 +199,7 @@ def available_notification_agents():
# OSX Notifications should only be visible if it can be used # OSX Notifications should only be visible if it can be used
if OSX().validate(): if OSX().validate():
agents.append({'label': 'OSX Notify', agents.append({'label': 'macOS Notification Center',
'name': 'osx', 'name': 'osx',
'id': AGENT_IDS['osx'] 'id': AGENT_IDS['osx']
}) })
@ -928,11 +928,9 @@ class ANDROIDAPP(Notifier):
'The content of your notifications will be sent unencrypted!</strong><br>' 'The content of your notifications will be sent unencrypted!</strong><br>'
'Please install the library to encrypt the notification contents. ' 'Please install the library to encrypt the notification contents. '
'Instructions can be found in the ' 'Instructions can be found in the '
'<a href="' + helpers.anon_url( '<a href="https://github.com/%s/%s-Wiki/wiki/'
'https://github.com/%s/%s-Wiki/wiki/' 'Frequently-Asked-Questions#notifications-pycryptodome'
'Frequently-Asked-Questions#notifications-pycryptodome' '" target="_blank">FAQ</a>.' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO),
% (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO)) +
'" target="_blank">FAQ</a>.',
'input_type': 'help' 'input_type': 'help'
}) })
else: else:
@ -1454,7 +1452,7 @@ class FACEBOOK(Notifier):
plexpy.CONFIG.FACEBOOK_TOKEN = 'temp' plexpy.CONFIG.FACEBOOK_TOKEN = 'temp'
return facebook.auth_url(app_id=app_id, return facebook.auth_url(app_id=app_id,
canvas_url=redirect_uri + '/facebookStep2', canvas_url=redirect_uri,
perms=['user_managed_groups','publish_actions']) perms=['user_managed_groups','publish_actions'])
def _get_credentials(self, code=''): def _get_credentials(self, code=''):
@ -1468,7 +1466,7 @@ class FACEBOOK(Notifier):
# Request user access token # Request user access token
api = facebook.GraphAPI(version='2.12') api = facebook.GraphAPI(version='2.12')
response = api.get_access_token_from_code(code=code, response = api.get_access_token_from_code(code=code,
redirect_uri=redirect_uri + '/facebookStep2', redirect_uri=redirect_uri,
app_id=app_id, app_id=app_id,
app_secret=app_secret) app_secret=app_secret)
access_token = response['access_token'] access_token = response['access_token']
@ -1532,25 +1530,11 @@ class FACEBOOK(Notifier):
return self._post_facebook(**data) return self._post_facebook(**data)
def return_config_options(self): def return_config_options(self):
config_option = [{'label': 'Instructions', config_option = [{'label': 'OAuth Redirect URI',
'description': 'Step 1: Visit <a href="' + helpers.anon_url('https://developers.facebook.com/apps') + '" target="_blank">'
'Facebook Developers</a> to add a new app using <strong>basic setup</strong>.<br>'
'Step 2: Click <strong>Add Product</strong> on the left, then <strong>Get Started</strong>'
'for <strong>Facebook Login</strong>.<br>'
'Step 3: Fill in <strong>Valid OAuth redirect URIs</strong> with your Tautulli URL (e.g. http://localhost:8181).<br>'
'Step 4: Click <strong>App Review</strong> on the left and toggle "make public" to <strong>Yes</strong>.<br>'
'Step 5: Fill in the <strong>Tautulli URL</strong> below with the exact same URL from Step 3.<br>'
'Step 6: Fill in the <strong>App ID</strong> and <strong>App Secret</strong> below.<br>'
'Step 7: Click the <strong>Request Authorization</strong> button below to retrieve your access token.<br>'
'Step 8: Fill in your <strong>Access Token</strong> below if it is not filled in automatically.<br>'
'Step 9: Fill in your <strong>Group ID</strong> number below. It can be found in the URL of your group page.',
'input_type': 'help'
},
{'label': 'Tautulli URL',
'value': self.config['redirect_uri'], 'value': self.config['redirect_uri'],
'name': 'facebook_redirect_uri', 'name': 'facebook_redirect_uri',
'description': 'Your Tautulli URL. This will tell Facebook where to redirect you after authorization.\ 'description': 'Fill in this address for the "Valid OAuth redirect URIs" '
(e.g. http://localhost:8181)', 'in your Facebook App.',
'input_type': 'text' 'input_type': 'text'
}, },
{'label': 'Facebook App ID', {'label': 'Facebook App ID',
@ -1567,14 +1551,15 @@ class FACEBOOK(Notifier):
}, },
{'label': 'Request Authorization', {'label': 'Request Authorization',
'value': 'Request Authorization', 'value': 'Request Authorization',
'name': 'facebook_facebookStep1', 'name': 'facebook_facebook_auth',
'description': 'Request Facebook authorization. (Ensure you allow the browser pop-up).', 'description': 'Request Facebook authorization. (Ensure you allow the browser pop-up).',
'input_type': 'button' 'input_type': 'button'
}, },
{'label': 'Facebook Access Token', {'label': 'Facebook Access Token',
'value': self.config['access_token'], 'value': self.config['access_token'],
'name': 'facebook_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' 'input_type': 'text'
}, },
{'label': 'Facebook Group ID', {'label': 'Facebook Group ID',
@ -1773,7 +1758,7 @@ class GROWL(Notifier):
config_option = [{'label': 'Growl Host', config_option = [{'label': 'Growl Host',
'value': self.config['host'], 'value': self.config['host'],
'name': 'growl_host', 'name': 'growl_host',
'description': 'Your Growl hostname.', 'description': 'Your Growl hostname or IP address.',
'input_type': 'text' 'input_type': 'text'
}, },
{'label': 'Growl Password', {'label': 'Growl Password',
@ -1875,7 +1860,7 @@ class HIPCHAT(Notifier):
return self.make_request(self.config['hook'], headers=headers, json=data) return self.make_request(self.config['hook'], headers=headers, json=data)
def return_config_options(self): def return_config_options(self):
config_option = [{'label': 'Hipchat Custom Integrations Full URL', config_option = [{'label': 'Hipchat Custom Integrations URL',
'value': self.config['hook'], 'value': self.config['hook'],
'name': 'hipchat_hook', 'name': 'hipchat_hook',
'description': 'Your Hipchat BYO integration URL. You can get a key from' 'description': 'Your Hipchat BYO integration URL. You can get a key from'
@ -2330,9 +2315,9 @@ class NMA(Notifier):
class OSX(Notifier): class OSX(Notifier):
""" """
OSX notifications macOS notifications
""" """
NAME = 'OSX Notify' NAME = 'macOS'
_DEFAULT_CONFIG = {'notify_app': '/Applications/Tautulli' _DEFAULT_CONFIG = {'notify_app': '/Applications/Tautulli'
} }
@ -2415,9 +2400,15 @@ class OSX(Notifier):
config_option = [{'label': 'Register Notify App', config_option = [{'label': 'Register Notify App',
'value': self.config['notify_app'], 'value': self.config['notify_app'],
'name': 'osx_notify_app', 'name': 'osx_notify_app',
'description': 'Enter the path/application name to be registered with the ' 'description': 'Enter the path/application name to be registered with the Notification Center. '
'Notification Center, default is /Applications/Tautulli.', 'Default is <span class="inline-pre">/Applications/Tautulli</span>.',
'input_type': 'text' '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 return True
def return_config_options(self): 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'], 'value': self.config['hosts'],
'name': 'plex_hosts', 'name': 'plex_hosts',
'description': 'Host running Plex Home Theater (eg. http://localhost:3005). Separate multiple hosts with commas (,).', 'description': 'Host running Plex Home Theater (eg. http://localhost:3005). Separate multiple hosts with commas (,).',
@ -2689,10 +2680,10 @@ class PUSHBULLET(Notifier):
return {'': ''} return {'': ''}
def return_config_options(self): def return_config_options(self):
config_option = [{'label': 'Pushbullet API Key', config_option = [{'label': 'Pushbullet Access Token',
'value': self.config['api_key'], 'value': self.config['api_key'],
'name': 'pushbullet_api_key', 'name': 'pushbullet_api_key',
'description': 'Your Pushbullet API key.', 'description': 'Your Pushbullet access token.',
'input_type': 'text', 'input_type': 'text',
'refresh': True 'refresh': True
}, },
@ -2974,6 +2965,7 @@ class SCRIPTS(Notifier):
'TAUTULLI_URL': helpers.get_plexpy_url(hostname='localhost'), 'TAUTULLI_URL': helpers.get_plexpy_url(hostname='localhost'),
'TAUTULLI_APIKEY': plexpy.CONFIG.API_KEY 'TAUTULLI_APIKEY': plexpy.CONFIG.API_KEY
} }
env.update(os.environ)
self.script_killed = False self.script_killed = False
output = error = '' output = error = ''
@ -3439,16 +3431,7 @@ class TWITTER(Notifier):
return self._send_tweet(body, attachment=poster_url) return self._send_tweet(body, attachment=poster_url)
def return_config_options(self): def return_config_options(self):
config_option = [{'label': 'Instructions', config_option = [{'label': 'Twitter Consumer Key',
'description': 'Step 1: Visit <a href="' + helpers.anon_url('https://apps.twitter.com') + '" target="_blank">'
'Twitter Apps</a> to <strong>Create New App</strong>. A vaild "Website" is not required.<br>'
'Step 2: Go to <strong>Keys and Access Tokens</strong> and click '
'<strong>Create my access token</strong>.<br>'
'Step 3: Fill in the <strong>Consumer Key</strong>, <strong>Consumer Secret</strong>, '
'<strong>Access Token</strong>, and <strong>Access Token Secret</strong> below.',
'input_type': 'help'
},
{'label': 'Twitter Consumer Key',
'value': self.config['consumer_key'], 'value': self.config['consumer_key'],
'name': 'twitter_consumer_key', 'name': 'twitter_consumer_key',
'description': 'Your Twitter consumer key.', 'description': 'Your Twitter consumer key.',
@ -3492,9 +3475,9 @@ class TWITTER(Notifier):
class XBMC(Notifier): class XBMC(Notifier):
""" """
XBMC notifications Kodi notifications
""" """
NAME = 'XBMC' NAME = 'Kodi'
_DEFAULT_CONFIG = {'hosts': '', _DEFAULT_CONFIG = {'hosts': '',
'username': '', 'username': '',
'password': '', 'password': '',
@ -3564,22 +3547,22 @@ class XBMC(Notifier):
return True return True
def return_config_options(self): def return_config_options(self):
config_option = [{'label': 'XBMC Host:Port', config_option = [{'label': 'Kodi Host Address',
'value': self.config['hosts'], 'value': self.config['hosts'],
'name': 'xbmc_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' 'input_type': 'text'
}, },
{'label': 'XBMC Username', {'label': 'Kodi Username',
'value': self.config['username'], 'value': self.config['username'],
'name': 'xbmc_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' 'input_type': 'text'
}, },
{'label': 'XBMC Password', {'label': 'Kodi Password',
'value': self.config['password'], 'value': self.config['password'],
'name': 'xbmc_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' 'input_type': 'password'
}, },
{'label': 'Notification Duration', {'label': 'Notification Duration',

View file

@ -1,2 +1,2 @@
PLEXPY_BRANCH = "beta" PLEXPY_BRANCH = "master"
PLEXPY_RELEASE_VERSION = "v2.0.23-beta" PLEXPY_RELEASE_VERSION = "v2.0.24"

View file

@ -3193,7 +3193,7 @@ class WebInterface(object):
@cherrypy.expose @cherrypy.expose
@cherrypy.tools.json_out() @cherrypy.tools.json_out()
@requireAuth(member_of("admin")) @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" cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store"
facebook_notifier = notifiers.FACEBOOK() facebook_notifier = notifiers.FACEBOOK()
@ -3208,7 +3208,7 @@ class WebInterface(object):
@cherrypy.expose @cherrypy.expose
@requireAuth(member_of("admin")) @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" cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store"
facebook = notifiers.FACEBOOK() 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!', 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!', 'Your clothes, give them to me, now!',
'Do it!', 'Do it!',
'If it bleeds, we can kill it', 'If it bleeds, we can kill it.',
'See you at the party Richter!', 'See you at the party Richter!',
'Let off some steam, Bennett', 'Let off some steam, Bennett.',
'I\'ll be back', 'I\'ll be back.',
'Get to the chopper!', 'Get to the chopper!',
'Hasta La Vista, Baby!', 'Hasta La Vista, Baby!',
'It\'s not a tumor!', 'It\'s not a tumor!',
@ -5175,7 +5175,7 @@ class WebInterface(object):
'What killed the dinosaurs? The Ice Age!', 'What killed the dinosaurs? The Ice Age!',
'That\'s for sleeping with my wife!', 'That\'s for sleeping with my wife!',
'Remember when I said I\'d kill you last... I lied!', '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.', 'Now, this is the plan. Get your ass to Mars.',
'I just had a terrible thought... What if this is a dream?', 'I just had a terrible thought... What if this is a dream?',
'Well, listen to this one: Rubber baby buggy bumpers!', 'Well, listen to this one: Rubber baby buggy bumpers!',