mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-10 23:42:37 -07:00
Merge branch 'nightly' into v2-newsletter
This commit is contained in:
commit
b6bd305694
11 changed files with 138 additions and 91 deletions
11
CHANGELOG.md
11
CHANGELOG.md
|
@ -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:
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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); });
|
||||||
});
|
});
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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():
|
||||||
|
|
|
@ -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()}
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
PLEXPY_BRANCH = "beta"
|
PLEXPY_BRANCH = "master"
|
||||||
PLEXPY_RELEASE_VERSION = "v2.0.23-beta"
|
PLEXPY_RELEASE_VERSION = "v2.0.24"
|
||||||
|
|
|
@ -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!',
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue