Compare commits

..

21 Commits

Author SHA1 Message Date
JonnyWong16
b8ef56574a v2.1.40 2019-12-30 10:24:46 -08:00
JonnyWong16
bc491628d4 Add links to 3rd Party APIs guide 2019-12-29 15:03:22 -08:00
JonnyWong16
629800c239 Change metadata to links for lookup help text 2019-12-29 11:05:21 -08:00
JonnyWong16
8bf876f88c Add note to save the MaxMind license key 2019-12-28 11:09:52 -08:00
JonnyWong16
a81ad6d73e Set maximum GeoLite2 database update interval to 30 days 2019-12-28 11:06:04 -08:00
JonnyWong16
02220209e3 Add default activity refresh interval to setting help text 2019-12-27 09:48:38 -08:00
JonnyWong16
9450a1434d Remove line break from Python version in startup logs 2019-12-25 14:10:35 -08:00
JonnyWong16
c358693fb2 Fix GeoLite2 update scheduler 2019-12-25 14:10:16 -08:00
JonnyWong16
e7b3d768ce Prevent installing GeoLite2 database without license key 2019-12-25 14:07:59 -08:00
JonnyWong16
ee91da2ff1 Force reinstall of GeoLite2 database 2019-12-25 13:43:12 -08:00
JonnyWong16
0428df8e3f Fix GeoLite2 update button for previous installs 2019-12-25 13:31:46 -08:00
JonnyWong16
d4fee1d701 Fix GeoLite2 last updated time for previous installs 2019-12-25 13:23:33 -08:00
JonnyWong16
3b44a3afd2 Add setting for GeoLite2 database update interval 2019-12-25 12:51:59 -08:00
JonnyWong16
7ee1c51810 Go to correct setting for installing GeoLite2 database 2019-12-25 12:40:58 -08:00
JonnyWong16
f958de2de6 Move 3rd Party API settings to new tab 2019-12-25 11:41:53 -08:00
JonnyWong16
b83eb2e763 Add auto-updating of GeoLite2 database 2019-12-25 11:17:45 -08:00
JonnyWong16
41c9369b43 Change GeoIP database install using license key 2019-12-23 23:58:36 -08:00
JonnyWong16
554be92c39 Update go to setting links 2019-12-23 22:30:49 -08:00
JonnyWong16
7486a50c33 Calculate months for graphs using datetime 2019-12-12 09:12:29 -08:00
JonnyWong16
1e777b1a1b Add logging for notify_action (trigger) 2019-12-11 11:35:30 -08:00
JonnyWong16
b7bb159630 Fix regex IP match in hostname resolution 2019-12-10 10:07:34 -08:00
14 changed files with 329 additions and 164 deletions

View File

@@ -1,5 +1,17 @@
# Changelog # Changelog
## v2.1.40 (2019-12-30)
* UI:
* Change: Moved 3rd Party API settings to new tab in the settings.
* Graphs:
* Change: Improve calculating month ranges for Play Totals graphs.
* Other:
* Fix: Failing to verify a Plex Media Server using a hostname.
* Change: A license key is now required to install the MaxMind GeoLite2 database for IP geolocation. Please follow the guide in the wiki to reinstall the GeoLite2 database.
* Change: The GeoLite2 database will now automatically update periodically if installed.
## v2.1.39 (2019-12-08) ## v2.1.39 (2019-12-08)
* UI: * UI:

View File

@@ -53,14 +53,6 @@ DOCUMENTATION :: END
<td>Newsletter Directory:</td> <td>Newsletter Directory:</td>
<td>${plexpy.CONFIG.NEWSLETTER_DIR}</td> <td>${plexpy.CONFIG.NEWSLETTER_DIR}</td>
</tr> </tr>
<tr>
<td>GeoLite2 Database:</td>
% if plexpy.CONFIG.GEOIP_DB:
<td>${plexpy.CONFIG.GEOIP_DB} | <a class="no-highlight" href="#" id="reinstall_geoip_db">Reinstall / Update</a> | <a class="no-highlight" href="#" id="uninstall_geoip_db">Uninstall</a></td>
% else:
<td><a class="no-highlight" href="#" id="install_geoip_db">Click here to install the GeoLite2 database.</a></td>
% endif
</tr>
% if plexpy.ARGS: % if plexpy.ARGS:
<tr> <tr>
<td>Arguments:</td> <td>Arguments:</td>
@@ -102,22 +94,6 @@ DOCUMENTATION :: END
<script> <script>
$(document).ready(function () { $(document).ready(function () {
$("#install_geoip_db, #reinstall_geoip_db").click(function () {
var msg = 'Are you sure you want to install the GeoLite2 database?<br /><br />' +
'The database is used to lookup IP address geolocation info.<br />' +
'The database will be downloaded from <a href="${anon_url("https://dev.maxmind.com/geoip/geoip2/geolite2/")}" target="_blank">MaxMind</a>, <br />' +
'and requires <strong>100MB</strong> of free space to install in your Tautulli directory.<br />'
var url = 'install_geoip_db';
confirmAjaxCall(url, msg, null, 'Installing GeoLite2 database.', getConfigurationTable);
});
$("#uninstall_geoip_db").click(function () {
var msg = 'Are you sure you want to uninstall the GeoLite2 database?<br /><br />' +
'You will not be able to lookup IP address geolocation info.';
var url = 'uninstall_geoip_db';
confirmAjaxCall(url, msg, null, 'Uninstalling GeoLite2 database.', getConfigurationTable);
});
$('.guidelines-modal-link').on('click', function (e) { $('.guidelines-modal-link').on('click', function (e) {
e.preventDefault(); e.preventDefault();
$('#guidelines-type').text($(this).data('id')) $('#guidelines-type').text($(this).data('id'))

View File

@@ -271,7 +271,7 @@
</div> </div>
<p class="help-block"> <p class="help-block">
Select an existing notification agent where the subject and body text will be sent.<br> Select an existing notification agent where the subject and body text will be sent.<br>
Note: Self-hosted newsletters must be enabled under <a data-tab-destination="tabs-notifications" data-dismiss="modal" data-target="#newsletter_self_hosted">Newsletters</a> to include a link to the newsletter. Note: Self-hosted newsletters must be enabled under <a data-tab-destination="notifications" data-dismiss="modal" data-target="newsletter_self_hosted">Newsletters</a> to include a link to the newsletter.
</p> </p>
</div> </div>
<div id="newsletter-email-config"> <div id="newsletter-email-config">

View File

@@ -485,7 +485,7 @@
'<div class="form-group">' + '<div class="form-group">' +
'<label>Warning</label>' + '<label>Warning</label>' +
'<p class="help-block" style="color: #eb8600;">Facebook requires HTTPS for authorization. ' + '<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" data-target="#enable_https">Web Interface</a>.</p>' + 'Please enable HTTPS for Tautulli under <a data-tab-destination="web_interface" data-dismiss="modal" data-target="enable_https">Web Interface</a>.</p>' +
'</div>' '</div>'
); );
$('#facebook_redirect_uri').val('HTTPS not enabled'); $('#facebook_redirect_uri').val('HTTPS not enabled');

View File

@@ -56,6 +56,7 @@
<li role="presentation"><a href="#tabs-notifications" aria-controls="tabs-notifications" role="tab" data-toggle="tab">Notifications & Newsletters</a></li> <li role="presentation"><a href="#tabs-notifications" aria-controls="tabs-notifications" role="tab" data-toggle="tab">Notifications & Newsletters</a></li>
<li role="presentation"><a href="#tabs-notification_agents" aria-controls="tabs-notification_agents" role="tab" data-toggle="tab">Notification Agents</a></li> <li role="presentation"><a href="#tabs-notification_agents" aria-controls="tabs-notification_agents" role="tab" data-toggle="tab">Notification Agents</a></li>
<li role="presentation"><a href="#tabs-newsletter_agents" aria-controls="tabs-newsletter_agents" role="tab" data-toggle="tab">Newsletter Agents</a></li> <li role="presentation"><a href="#tabs-newsletter_agents" aria-controls="tabs-newsletter_agents" role="tab" data-toggle="tab">Newsletter Agents</a></li>
<li role="presentation"><a href="#tabs-3rd_party_apis" aria-controls="tabs-3rd_party_apis" role="tab" data-toggle="tab">3rd Party APIs</a></li>
<li role="presentation"><a href="#tabs-import_backups" aria-controls="tabs-import_backups" role="tab" data-toggle="tab">Import & Backups</a></li> <li role="presentation"><a href="#tabs-import_backups" aria-controls="tabs-import_backups" role="tab" data-toggle="tab">Import & Backups</a></li>
<li role="presentation"><a href="#tabs-android_app" aria-controls="tabs-android_app" role="tab" data-toggle="tab">Tautulli Remote Android App <sup><small>beta</small></sup></a></li> <li role="presentation"><a href="#tabs-android_app" aria-controls="tabs-android_app" role="tab" data-toggle="tab">Tautulli Remote Android App <sup><small>beta</small></sup></a></li>
</ul> </ul>
@@ -284,7 +285,7 @@
</div> </div>
<div id="home_refresh_interval_error" class="alert alert-danger settings-alert" role="alert"></div> <div id="home_refresh_interval_error" class="alert alert-danger settings-alert" role="alert"></div>
</div> </div>
<p class="help-block">Set the interval (in seconds) to refresh the current activity on the homepage. Minimum 2.</p> <p class="help-block">Set the interval (in seconds) to refresh the current activity on the homepage. Minimum 2, default 10.</p>
</div> </div>
<div class="padded-header"> <div class="padded-header">
@@ -1008,7 +1009,7 @@
<p class="help-block" id="self_host_newsletter_message"> <p class="help-block" id="self_host_newsletter_message">
Note: The <span class="inline-pre">${http_root}newsletter</span> endpoint on your domain must be publicly accessible from the internet. Note: The <span class="inline-pre">${http_root}newsletter</span> endpoint on your domain must be publicly accessible from the internet.
</p> </p>
<p class="help-block settings-warning base-url-warning">Warning: Public Tautulli domain not set under <a data-tab-destination="tabs-web_interface" data-target="#http_base_url">Web Interface</a>.</p> <p class="help-block settings-warning base-url-warning">Warning: Public Tautulli domain not set under <a data-tab-destination="web_interface" data-target="http_base_url">Web Interface</a>.</p>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="newsletter_auth">Newsletter Authentication</label> <label for="newsletter_auth">Newsletter Authentication</label>
@@ -1022,7 +1023,7 @@
</div> </div>
</div> </div>
<p class="help-block">Select the authentication method to use for self-hosted newsletters.</p> <p class="help-block">Select the authentication method to use for self-hosted newsletters.</p>
<p class="help-block settings-warning newsletter-guest-access-warning">Warning: Guest Access is not enabled under <a data-tab-destination="tabs-web_interface" data-target="#allow_guest_access">Web Interface</a>.</p> <p class="help-block settings-warning newsletter-guest-access-warning">Warning: Guest Access is not enabled under <a data-tab-destination="web_interface" data-target="allow_guest_access">Web Interface</a>.</p>
</div> </div>
<div class="form-group" id="newsletter_password_option"> <div class="form-group" id="newsletter_password_option">
<label for="newsletter_password">Newsletter Password</label> <label for="newsletter_password">Newsletter Password</label>
@@ -1063,12 +1064,60 @@
<p class="help-block">Enter the full path to where newsletter files will be saved.</p> <p class="help-block">Enter the full path to where newsletter files will be saved.</p>
</div> </div>
<div class="padded-header"> <p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
<h3>3rd Party APIs</h3>
</div> </div>
<div role="tabpanel" class="tab-pane" id="tabs-notification_agents">
<div class="padded-header">
<h3>Notification Agents</h3>
</div>
<p class="help-block">
Add a new notification agent, or configure an existing notification agent by clicking the settings icon on the right.
</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 />
<div id="plexpy-notifiers-table">
<div class='text-muted'><i class="fa fa-refresh fa-spin"></i> Loading notification agents...</div>
<br>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tabs-newsletter_agents">
<div class="padded-header">
<h3>Newsletter Agents</h3>
</div>
<p class="help-block">
Add a new newsletter agent, or configure an existing newsletter agent by clicking the settings icon on the right.
</p>
<p class="help-block settings-warning" id="newsletter_upload_warning">
Warning: The <a data-tab-destination="3rd_party_apis" data-target="notify_upload_posters">Image Hosting</a> setting must be enabled for images to display on the newsletter.</span>
</p>
<br/>
<div id="plexpy-newsletters-table">
<div class='text-muted'><i class="fa fa-refresh fa-spin"></i> Loading newsletter agents...</div>
<br>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tabs-3rd_party_apis">
<div class="padded-header">
<h3>Image Hosting</h3>
</div>
<p class="help-block">Image hosting is used to provide posters and artwork for some notification agents and newsletters.</p>
<div class="form-group"> <div class="form-group">
<label for="notify_upload_posters">Image Hosting</label> <label for="notify_upload_posters">Image Host</label>
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<div class="${'input-group' if config['notify_upload_posters'] in (1, 3) else ''}"> <div class="${'input-group' if config['notify_upload_posters'] in (1, 3) else ''}">
@@ -1090,8 +1139,8 @@
</div> </div>
<div id="imgur_upload_options" style="overlfow: hidden; display: ${'none' if config['notify_upload_posters'] != 1 else 'block'}"> <div id="imgur_upload_options" style="overlfow: hidden; display: ${'none' if config['notify_upload_posters'] != 1 else 'block'}">
<div class="form-group"> <div class="form-group">
<p class="help-block" id="imgur_upload_message"> <p class="help-block">
You can register a new Imgur application <a href="${anon_url('https://api.imgur.com/oauth2/addclient')}" target="_blank">here</a>.<br> Please see the <a target='_blank' href='${anon_url('https://github.com/%s/%s-Wiki/wiki/3rd-Party-APIs-Guide' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO))}'>3rd Party APIs Guide</a> for instructions on setting up Imgur.<br>
Warning: Imgur uploads are rate-limited and newsletters may exceed the limit. Please use Cloudinary for newsletters instead. Warning: Imgur uploads are rate-limited and newsletters may exceed the limit. Please use Cloudinary for newsletters instead.
</p> </p>
</div> </div>
@@ -1108,13 +1157,13 @@
<div id="self_host_image_options" style="overlfow: hidden; display: ${'none' if config['notify_upload_posters'] != 2 else 'block'}"> <div id="self_host_image_options" style="overlfow: hidden; display: ${'none' if config['notify_upload_posters'] != 2 else 'block'}">
<div class="form-group"> <div class="form-group">
<p class="help-block" id="self_host_image_message">Note: The <span class="inline-pre">${http_root}image</span> endpoint on your domain must be publicly accessible from the internet.</p> <p class="help-block" id="self_host_image_message">Note: The <span class="inline-pre">${http_root}image</span> endpoint on your domain must be publicly accessible from the internet.</p>
<p class="help-block settings-warning base-url-warning">Warning: Public Tautulli domain not set under <a data-tab-destination="tabs-web_interface" data-target="#http_base_url">Web Interface</a>.</p> <p class="help-block settings-warning base-url-warning">Warning: Public Tautulli domain not set under <a data-tab-destination="web_interface" data-target="http_base_url">Web Interface</a>.</p>
</div> </div>
</div> </div>
<div id="cloudinary_upload_options" style="overlfow: hidden; display: ${'none' if config['notify_upload_posters'] != 3 else 'block'}"> <div id="cloudinary_upload_options" style="overlfow: hidden; display: ${'none' if config['notify_upload_posters'] != 3 else 'block'}">
<div class="form-group"> <div class="form-group">
<p class="help-block" id="imgur_upload_message"> <p class="help-block">
You can sign up for Cloudinary <a href="${anon_url('https://cloudinary.com')}" target="_blank">here</a>.<br> Please see the <a target='_blank' href='${anon_url('https://github.com/%s/%s-Wiki/wiki/3rd-Party-APIs-Guide' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO))}'>3rd Party APIs Guide</a> for instructions on setting up Cloudinary.
</p> </p>
</div> </div>
<div class="form-group"> <div class="form-group">
@@ -1151,6 +1200,13 @@
</p> </p>
</div> </div>
</div> </div>
<div class="padded-header">
<h3>Metadata Lookups</h3>
</div>
<p class="help-block">Metadata lookups are used to provide additional links for notifications when available.</p>
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input type="checkbox" name="themoviedb_lookup" id="themoviedb_lookup" value="1" ${config['themoviedb_lookup']}> Lookup TheMovieDB Links <input type="checkbox" name="themoviedb_lookup" id="themoviedb_lookup" value="1" ${config['themoviedb_lookup']}> Lookup TheMovieDB Links
@@ -1170,50 +1226,57 @@
<p class="help-block">Enable to lookup links to MusicBrainz for music when available.</p> <p class="help-block">Enable to lookup links to MusicBrainz for music when available.</p>
</div> </div>
<div class="padded-header">
<h3>Geolocation Database</h3>
</div>
<p class="help-block">The GeoLite2 database is used to geolocate IP addresses.</p>
<p class="help-block">
Please see the <a target='_blank' href='${anon_url('https://github.com/%s/%s-Wiki/wiki/3rd-Party-APIs-Guide' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO))}'>3rd Party APIs Guide</a> for instructions on setting up MaxMind.<br>
</p>
<div class="form-group">
<label for="maxmind_license_key">MaxMind License Key</label>
<div class="row">
<div class="col-md-6">
<input type="text" class="form-control" id="maxmind_license_key" name="maxmind_license_key" value="${config['maxmind_license_key']}" data-parsley-trigger="change">
</div>
</div>
<p class="help-block">
Enter and save your MaxMind License Key to install the GeoLite2 database.
</p>
</div>
<div class="form-group">
<label for="geoip_db">GeoLite2 Database File</label> ${docker_msg | n}
<div class="row">
<div class="col-md-9">
<div class="input-group">
<input type="text" class="form-control" id="geoip_db" name="geoip_db" value="${config['geoip_db']}" ${docker_setting}>
<span class="input-group-btn">
<button class="btn btn-form" type="button" id="install_geoip_db" disabled>${'Update' if config["geoip_db_installed"] else 'Install'}</button>
<button class="btn btn-form" type="button" id="uninstall_geoip_db">Uninstall</button>
</span>
</div>
</div>
</div>
<p class="help-block">
GeoLite2 Database last updated <strong><span id="geoip_db_updated">never</span></strong>.
</p>
</div>
<div class="form-group advanced-setting">
<label for="geoip_db_update_days">GeoLite2 Database Update Interval</label>
<div class="row">
<div class="col-md-2">
<input type="text" class="form-control" data-parsley-type="integer" id="geoip_db_update_days" name="geoip_db_update_days" value="${config['geoip_db_update_days']}" size="5" data-parsley-range="[7, 30]" data-parsley-trigger="change" data-parsley-errors-container="#geoip_db_update_days_error" required>
</div>
<div id="geoip_db_update_days_error" class="alert alert-danger settings-alert" role="alert"></div>
</div>
<p class="help-block">The interval (in days) Tautulli will automatically update the GeoLite2 database. Minimum 7, maximum 30, default 30.</p>
</div>
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p> <p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
</div> </div>
<div role="tabpanel" class="tab-pane" id="tabs-notification_agents">
<div class="padded-header">
<h3>Notification Agents</h3>
</div>
<p class="help-block">
Add a new notification agent, or configure an existing notification agent by clicking the settings icon on the right.
</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 />
<div id="plexpy-notifiers-table">
<div class='text-muted'><i class="fa fa-refresh fa-spin"></i> Loading notification agents...</div>
<br>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tabs-newsletter_agents">
<div class="padded-header">
<h3>Newsletter Agents</h3>
</div>
<p class="help-block">
Add a new newsletter agent, or configure an existing newsletter agent by clicking the settings icon on the right.
</p>
<p class="help-block settings-warning" id="newsletter_upload_warning">
Warning: The <a data-tab-destination="tabs-notifications" data-target="#notify_upload_posters">Image Hosting</a> setting must be enabled for images to display on the newsletter.</span>
</p>
<br/>
<div id="plexpy-newsletters-table">
<div class='text-muted'><i class="fa fa-refresh fa-spin"></i> Loading newsletter agents...</div>
<br>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tabs-import_backups"> <div role="tabpanel" class="tab-pane" id="tabs-import_backups">
<div class="padded-header"> <div class="padded-header">
@@ -1316,7 +1379,7 @@
<div class="form-group"> <div class="form-group">
<label>Registered Devices</label> <label>Registered Devices</label>
<p class="help-block">Register a new device using a QR code, or configure an existing device by clicking the settings icon on the right.</p> <p class="help-block">Register a new device using a QR code, or configure an existing device by clicking the settings icon on the right.</p>
<p id="app_api_msg" style="color: #eb8600;">The API must be enabled under <a data-tab-destination="tabs-web_interface" data-target="#api_enabled">Web Interface</a> to use the app.</p> <p id="app_api_msg" style="color: #eb8600;">Warning: The API must be enabled under <a data-tab-destination="web_interface" data-target="api_enabled">Web Interface</a> to use the app.</p>
<div class="row"> <div class="row">
<div id="plexpy-mobile-devices-table" class="col-md-12"> <div id="plexpy-mobile-devices-table" class="col-md-12">
<div class='text-muted'><i class="fa fa-refresh fa-spin"></i> Loading registered devices...</div> <div class='text-muted'><i class="fa fa-refresh fa-spin"></i> Loading registered devices...</div>
@@ -1796,11 +1859,6 @@ Rating: {rating}/10 --> Rating: /10
async: true, async: true,
complete: function(xhr, status) { complete: function(xhr, status) {
$("#plexpy-configuration-table").html(xhr.responseText); $("#plexpy-configuration-table").html(xhr.responseText);
if ("${kwargs.get('install_geoip')}" == 'true') {
$('#install_geoip_db').removeClass('no-highlight').css('color','#e9a049');
} else if ("${kwargs.get('reinstall_geoip')}" == 'true') {
$('#reinstall_geoip_db').removeClass('no-highlight').css('color','#e9a049');
}
} }
}); });
} }
@@ -1943,6 +2001,7 @@ $(document).ready(function() {
getNewslettersTable(); getNewslettersTable();
getMobileDevicesTable(); getMobileDevicesTable();
loadUpdateDistros(); loadUpdateDistros();
enableGeoLiteInstallButton();
settingsChanged = false; settingsChanged = false;
} }
@@ -2773,25 +2832,75 @@ $(document).ready(function() {
$('#allow_guest_access').click(function () { $('#allow_guest_access').click(function () {
newsletterPasswordEnabled(); newsletterPasswordEnabled();
}) });
function gotoSetting(tab, setting){
$("a[href=#tabs-" + tab + "]").click();
if (setting) {
_setting = '#' + setting;
if ($(_setting).closest('.advanced-setting').length && !$('#menu_link_show_advanced_settings').hasClass('active')) {
$('#menu_link_show_advanced_settings').click()
}
var body_container = $('.body-container');
var scroll_pos = setting ? body_container.scrollTop() + $(_setting).offset().top - 100 : 0;
body_container.animate({scrollTop: scroll_pos});
$(_setting).closest('.form-group, .checkbox').delay(500).fadeOut().fadeIn('slow').fadeOut().fadeIn('slow');
}
}
$('body').on('click', 'a[data-tab-destination]', function () { $('body').on('click', 'a[data-tab-destination]', function () {
var tab = $(this).data('tab-destination'); var tab = $(this).data('tab-destination');
$("a[href=#" + tab + "]").click(); var setting = $(this).data('target');
var scroll_destination = $(this).data('target'); gotoSetting(tab, setting)
if (scroll_destination) {
if ($(scroll_destination).closest('.advanced-setting').length && !$('#menu_link_show_advanced_settings').hasClass('active')) {
$('#menu_link_show_advanced_settings').click()
}
var body_container = $('.body-container')
var scroll_pos = scroll_destination ? body_container.scrollTop() + $(scroll_destination).offset().top - 100 : 0;
body_container.animate({scrollTop: scroll_pos});
}
}); });
$('#resources-xml').on('tripleclick', function () { $('#resources-xml').on('tripleclick', function () {
openPlexXML('/api/resources', true, {includeHttps: 1}); openPlexXML('/api/resources', true, {includeHttps: 1});
}); });
if ("${kwargs.get('install_geoip')}" === 'true') {
gotoSetting('3rd_party_apis', 'geoip_db')
}
function enableGeoLiteInstallButton() {
$('#install_geoip_db').prop('disabled', !(Boolean($("#maxmind_license_key").val())));
}
enableGeoLiteInstallButton();
if ("${config['geoip_db_installed']}" > "0") {
$("#geoip_db_updated").text(moment("${config['geoip_db_installed']}", "X").fromNow());
}
$("#install_geoip_db").click(function () {
var msg = 'Are you sure you want to install the GeoLite2 database?<br /><br />' +
'The database is used to lookup IP address geolocation info.<br />' +
'The database will be downloaded from <a href="${anon_url("https://dev.maxmind.com/geoip/geoip2/geolite2/")}" target="_blank">MaxMind</a>, <br />' +
'and requires <strong>100MB</strong> of free space to install in your Tautulli directory.<br />';
var url = 'install_geoip_db';
if ($(this).text() === 'Update') {
url += '?update=true';
}
confirmAjaxCall(url, msg, null, 'Installing GeoLite2 database.', function (result) {
if (result.result === "success") {
$('#install_geoip_db').text('Update');
$('#geoip_db_updated').text(moment(result.updated, "X").fromNow());
}
getSchedulerTable();
});
});
$("#uninstall_geoip_db").click(function () {
var msg = 'Are you sure you want to uninstall the GeoLite2 database?<br /><br />' +
'You will not be able to lookup IP address geolocation info.';
var url = 'uninstall_geoip_db';
confirmAjaxCall(url, msg, null, 'Uninstalling GeoLite2 database.', function (result) {
if (result.result === "success") {
$('#install_geoip_db').text('Install');
$('#geoip_db_updated').text('never');
}
getSchedulerTable();
});
});
}); });
</script> </script>
</%def> </%def>

View File

@@ -39,6 +39,7 @@ import activity_pinger
import common import common
import database import database
import datafactory import datafactory
import helpers
import libraries import libraries
import logger import logger
import mobile_app import mobile_app
@@ -166,7 +167,7 @@ def initialize(config_file):
plexpy.SYS_TIMEZONE.zone, plexpy.SYS_UTC_OFFSET plexpy.SYS_TIMEZONE.zone, plexpy.SYS_UTC_OFFSET
)) ))
logger.info(u"Python {}".format( logger.info(u"Python {}".format(
sys.version sys.version.replace('\n', '')
)) ))
logger.info(u"Program Dir: {}".format( logger.info(u"Program Dir: {}".format(
PROG_DIR PROG_DIR
@@ -444,6 +445,8 @@ def initialize_scheduler():
hours=backup_hours, minutes=0, seconds=0, args=(True, True)) hours=backup_hours, minutes=0, seconds=0, args=(True, True))
schedule_job(config.make_backup, 'Backup Tautulli config', schedule_job(config.make_backup, 'Backup Tautulli config',
hours=backup_hours, minutes=0, seconds=0, args=(True, True)) hours=backup_hours, minutes=0, seconds=0, args=(True, True))
schedule_job(helpers.update_geoip_db, 'Update GeoLite2 database',
hours=12 * bool(CONFIG.GEOIP_DB_INSTALLED), minutes=0, seconds=0)
if WS_CONNECTED and CONFIG.PMS_IP and CONFIG.PMS_TOKEN: if WS_CONNECTED and CONFIG.PMS_IP and CONFIG.PMS_TOKEN:
schedule_job(plextv.get_server_resources, 'Refresh Plex server URLs', schedule_job(plextv.get_server_resources, 'Refresh Plex server URLs',

View File

@@ -197,7 +197,8 @@ SCHEDULER_LIST = [
'Refresh libraries list', 'Refresh libraries list',
'Refresh Plex server URLs', 'Refresh Plex server URLs',
'Backup Tautulli database', 'Backup Tautulli database',
'Backup Tautulli config' 'Backup Tautulli config',
'Update GeoLite2 database'
] ]
DATE_TIME_FORMATS = [ DATE_TIME_FORMATS = [

View File

@@ -175,6 +175,8 @@ _CONFIG_DEFINITIONS = {
'FIRST_RUN_COMPLETE': (int, 'General', 0), 'FIRST_RUN_COMPLETE': (int, 'General', 0),
'FREEZE_DB': (int, 'General', 0), 'FREEZE_DB': (int, 'General', 0),
'GEOIP_DB': (str, 'General', ''), 'GEOIP_DB': (str, 'General', ''),
'GEOIP_DB_INSTALLED': (int, 'General', 0),
'GEOIP_DB_UPDATE_DAYS': (int, 'General', 30),
'GET_FILE_SIZES': (int, 'General', 0), 'GET_FILE_SIZES': (int, 'General', 0),
'GET_FILE_SIZES_HOLD': (dict, 'General', {'section_ids': [], 'rating_keys': []}), 'GET_FILE_SIZES_HOLD': (dict, 'General', {'section_ids': [], 'rating_keys': []}),
'GIT_BRANCH': (str, 'General', 'master'), 'GIT_BRANCH': (str, 'General', 'master'),
@@ -289,6 +291,7 @@ _CONFIG_DEFINITIONS = {
'LOG_BLACKLIST': (int, 'General', 1), 'LOG_BLACKLIST': (int, 'General', 1),
'LOG_DIR': (str, 'General', ''), 'LOG_DIR': (str, 'General', ''),
'LOGGING_IGNORE_INTERVAL': (int, 'Monitoring', 120), 'LOGGING_IGNORE_INTERVAL': (int, 'Monitoring', 120),
'MAXMIND_LICENSE_KEY': (str, 'General', ''),
'METADATA_CACHE_SECONDS': (int, 'Advanced', 1800), 'METADATA_CACHE_SECONDS': (int, 'Advanced', 1800),
'MOVIE_LOGGING_ENABLE': (int, 'Monitoring', 1), 'MOVIE_LOGGING_ENABLE': (int, 'Monitoring', 1),
'MOVIE_NOTIFY_ENABLE': (int, 'Monitoring', 0), 'MOVIE_NOTIFY_ENABLE': (int, 'Monitoring', 0),
@@ -923,3 +926,9 @@ class Config(object):
self.BUFFER_THRESHOLD = max(self.BUFFER_THRESHOLD, 10) self.BUFFER_THRESHOLD = max(self.BUFFER_THRESHOLD, 10)
self.CONFIG_VERSION = 13 self.CONFIG_VERSION = 13
if self.CONFIG_VERSION == 13:
if not self.GEOIP_DB:
self.GEOIP_DB = os.path.join(plexpy.DATA_DIR, 'GeoLite2-City.mmdb')
self.CONFIG_VERSION = 14

View File

@@ -369,19 +369,23 @@ class Graphs(object):
# create our date range as some months may not have any data # create our date range as some months may not have any data
# but we still want to display them # but we still want to display them
base = time.localtime() dt_today = datetime.date.today()
month_range = [time.localtime( dt = dt_today
time.mktime((base.tm_year, base.tm_mon - n, 1, 0, 0, 0, 0, 0, 0))) for n in range(int(time_range))] month_range = [dt]
for n in range(int(time_range)-1):
if not ((dt_today.month-n) % 12)-1:
dt = datetime.date(dt.year-1, 12, 1)
else:
dt = datetime.date(dt.year, dt.month-1, 1)
month_range.append(dt)
categories = [] categories = []
series_1 = [] series_1 = []
series_2 = [] series_2 = []
series_3 = [] series_3 = []
for month_item in sorted(month_range): for dt in sorted(month_range):
dt = datetime.datetime(*month_item[:6])
date_string = dt.strftime('%Y-%m') date_string = dt.strftime('%Y-%m')
categories.append(dt.strftime('%b %Y').decode(plexpy.SYS_ENCODING, 'replace')) categories.append(dt.strftime('%b %Y').decode(plexpy.SYS_ENCODING, 'replace'))
series_1_value = 0 series_1_value = 0
series_2_value = 0 series_2_value = 0

View File

@@ -36,6 +36,7 @@ import re
import shlex import shlex
import socket import socket
import sys import sys
import tarfile
import time import time
import unicodedata import unicodedata
import urllib, urllib2 import urllib, urllib2
@@ -565,7 +566,7 @@ def get_ip(host):
ip_address = '' ip_address = ''
if is_valid_ip(host): if is_valid_ip(host):
return host return host
elif not re.fullmatch(r'[0-9]+(?:\.[0-9]+){3}(?!\d*-[a-z0-9]{6})', host): elif not re.match(r'^[0-9]+(?:\.[0-9]+){3}(?!\d*-[a-z0-9]{6})$', host):
try: try:
ip_address = socket.getaddrinfo(host, None)[0][4][0] ip_address = socket.getaddrinfo(host, None)[0][4][0]
logger.debug(u"IP Checker :: Resolved %s to %s." % (host, ip_address)) logger.debug(u"IP Checker :: Resolved %s to %s." % (host, ip_address))
@@ -583,83 +584,118 @@ def is_valid_ip(address):
return False return False
def install_geoip_db(): def update_geoip_db():
maxmind_url = 'http://geolite.maxmind.com/download/geoip/database/' if plexpy.CONFIG.GEOIP_DB_INSTALLED:
geolite2_gz = 'GeoLite2-City.mmdb.gz' logger.info(u"Tautulli Helpers :: Checking for GeoLite2 database updates.")
geolite2_md5 = 'GeoLite2-City.md5' now = int(time.time())
geolite2_db = geolite2_gz[:-3] if now - plexpy.CONFIG.GEOIP_DB_INSTALLED >= plexpy.CONFIG.GEOIP_DB_UPDATE_DAYS * 24 * 60 * 60:
md5_checksum = '' return install_geoip_db(update=True)
logger.info(u"Tautulli Helpers :: GeoLite2 database already updated within the last 30 days.")
def install_geoip_db(update=False):
if not plexpy.CONFIG.MAXMIND_LICENSE_KEY:
logger.error(u"Tautulli Helpers :: Failed to download GeoLite2 database file from MaxMind: Missing MaxMindLicense Key")
return False
maxmind_db = 'GeoLite2-City'
maxmind_url = 'https://download.maxmind.com/app/geoip_download?edition_id={db}&suffix={{suffix}}&license_key={key}'.format(
db=maxmind_db, key=plexpy.CONFIG.MAXMIND_LICENSE_KEY)
geolite2_db_url = maxmind_url.format(suffix='tar.gz')
geolite2_md5_url = maxmind_url.format(suffix='tar.gz.md5')
geolite2_gz = maxmind_db + '.tar.gz'
geolite2_md5 = geolite2_gz + '.md5'
geolite2_db = maxmind_db + '.mmdb'
geolite2_db_path = plexpy.CONFIG.GEOIP_DB or os.path.join(plexpy.DATA_DIR, geolite2_db)
temp_gz = os.path.join(plexpy.CONFIG.CACHE_DIR, geolite2_gz) temp_gz = os.path.join(plexpy.CONFIG.CACHE_DIR, geolite2_gz)
geolite2_db = plexpy.CONFIG.GEOIP_DB or os.path.join(plexpy.DATA_DIR, geolite2_db) temp_md5 = os.path.join(plexpy.CONFIG.CACHE_DIR, geolite2_md5)
# Retrieve the GeoLite2 gzip file # Retrieve the GeoLite2 gzip file
logger.debug(u"Tautulli Helpers :: Downloading GeoLite2 gzip file from MaxMind...") logger.debug(u"Tautulli Helpers :: Downloading GeoLite2 gzip file from MaxMind...")
try: try:
maxmind = urllib.URLopener() maxmind = urllib.URLopener()
maxmind.retrieve(maxmind_url + geolite2_gz, temp_gz) maxmind.retrieve(geolite2_db_url, temp_gz)
md5_checksum = urllib2.urlopen(maxmind_url + geolite2_md5).read() maxmind.retrieve(geolite2_md5_url, temp_md5)
except Exception as e: except Exception as e:
logger.error(u"Tautulli Helpers :: Failed to download GeoLite2 gzip file from MaxMind: %s" % e) logger.error(u"Tautulli Helpers :: Failed to download GeoLite2 gzip file from MaxMind: %s" % e)
return False return False
# Extract the GeoLite2 database file # Check MD5 hash for GeoLite2 tar.gz file
logger.debug(u"Tautulli Helpers :: Extracting GeoLite2 database...") logger.debug(u"Tautulli Helpers :: Checking MD5 checksum for GeoLite2 gzip file...")
try:
with gzip.open(temp_gz, 'rb') as gz:
with open(geolite2_db, 'wb') as db:
db.write(gz.read())
except Exception as e:
logger.error(u"Tautulli Helpers :: Failed to extract the GeoLite2 database: %s" % e)
return False
# Check MD5 hash for GeoLite2 database file
logger.debug(u"Tautulli Helpers :: Checking MD5 checksum for GeoLite2 database...")
try: try:
hash_md5 = hashlib.md5() hash_md5 = hashlib.md5()
with open(geolite2_db, 'rb') as f: with open(temp_gz, 'rb') as f:
for chunk in iter(lambda: f.read(4096), b""): for chunk in iter(lambda: f.read(4096), b""):
hash_md5.update(chunk) hash_md5.update(chunk)
md5_hash = hash_md5.hexdigest() md5_hash = hash_md5.hexdigest()
with open(temp_md5, 'r') as f:
md5_checksum = f.read()
if md5_hash != md5_checksum: if md5_hash != md5_checksum:
logger.error(u"Tautulli Helpers :: MD5 checksum doesn't match for GeoLite2 database. " logger.error(u"Tautulli Helpers :: MD5 checksum doesn't match for GeoLite2 database. "
"Checksum: %s, file hash: %s" % (md5_checksum, md5_hash)) "Checksum: %s, file hash: %s" % (md5_checksum, md5_hash))
return False return False
except Exception as e: except Exception as e:
logger.error(u"Tautulli Helpers :: Failed to generate MD5 checksum for GeoLite2 database: %s" % e) logger.error(u"Tautulli Helpers :: Failed to generate MD5 checksum for GeoLite2 gzip file: %s" % e)
return False
# Extract the GeoLite2 database file
logger.debug(u"Tautulli Helpers :: Extracting GeoLite2 database...")
try:
with tarfile.open(temp_gz, 'r:gz') as tar:
for tarinfo in tar:
if tarinfo.isdir():
member = tar.getmember(os.path.join(tarinfo.name, geolite2_db))
mmdb = tar.extractfile(member)
with open(geolite2_db_path, 'wb') as db:
db.write(mmdb.read())
break
except Exception as e:
logger.error(u"Tautulli Helpers :: Failed to extract the GeoLite2 database: %s" % e)
return False return False
# Delete temportary GeoLite2 gzip file # Delete temportary GeoLite2 gzip file
logger.debug(u"Tautulli Helpers :: Deleting temporary GeoLite2 gzip file...") logger.debug(u"Tautulli Helpers :: Deleting temporary GeoLite2 gzip file...")
try: try:
os.remove(temp_gz) os.remove(temp_gz)
os.remove(temp_md5)
except Exception as e: except Exception as e:
logger.warn(u"Tautulli Helpers :: Failed to remove temporary GeoLite2 gzip file: %s" % e) logger.warn(u"Tautulli Helpers :: Failed to remove temporary GeoLite2 gzip file: %s" % e)
logger.debug(u"Tautulli Helpers :: GeoLite2 database installed successfully.") plexpy.CONFIG.__setattr__('GEOIP_DB', geolite2_db_path)
plexpy.CONFIG.__setattr__('GEOIP_DB', geolite2_db) plexpy.CONFIG.__setattr__('GEOIP_DB_INSTALLED', int(time.time()))
plexpy.CONFIG.write() plexpy.CONFIG.write()
return True logger.debug(u"Tautulli Helpers :: GeoLite2 database installed successfully.")
if not update:
plexpy.schedule_job(update_geoip_db, 'Update GeoLite2 database', hours=12, minutes=0, seconds=0)
return plexpy.CONFIG.GEOIP_DB_INSTALLED
def uninstall_geoip_db(): def uninstall_geoip_db():
logger.debug(u"Tautulli Helpers :: Uninstalling the GeoLite2 database...") logger.debug(u"Tautulli Helpers :: Uninstalling the GeoLite2 database...")
try: try:
os.remove(plexpy.CONFIG.GEOIP_DB) os.remove(plexpy.CONFIG.GEOIP_DB)
plexpy.CONFIG.__setattr__('GEOIP_DB', '')
plexpy.CONFIG.write()
except Exception as e: except Exception as e:
logger.error(u"Tautulli Helpers :: Failed to uninstall the GeoLite2 database: %s" % e) logger.error(u"Tautulli Helpers :: Failed to uninstall the GeoLite2 database: %s" % e)
return False return False
plexpy.CONFIG.__setattr__('GEOIP_DB_INSTALLED', 0)
plexpy.CONFIG.write()
logger.debug(u"Tautulli Helpers :: GeoLite2 database uninstalled successfully.") logger.debug(u"Tautulli Helpers :: GeoLite2 database uninstalled successfully.")
plexpy.schedule_job(update_geoip_db, 'Update GeoLite2 database', hours=0, minutes=0, seconds=0)
return True return True
def geoip_lookup(ip_address): def geoip_lookup(ip_address):
if not plexpy.CONFIG.GEOIP_DB: if not plexpy.CONFIG.GEOIP_DB_INSTALLED:
return 'GeoLite2 database not installed. Please install from the ' \ return 'GeoLite2 database not installed. Please install from the ' \
'<a href="settings?install_geoip=true">Settings</a> page.' '<a href="settings?install_geoip=true">Settings</a> page.'
@@ -677,7 +713,7 @@ def geoip_lookup(ip_address):
'<a href="settings?install_geoip=true">Settings</a> page.' '<a href="settings?install_geoip=true">Settings</a> page.'
except maxminddb.InvalidDatabaseError as e: except maxminddb.InvalidDatabaseError as e:
return 'Invalid GeoLite2 database. Please reinstall from the ' \ return 'Invalid GeoLite2 database. Please reinstall from the ' \
'<a href="settings?reinstall_geoip=true">Settings</a> page.' '<a href="settings?install_geoip=true">Settings</a> page.'
except geoip2.errors.AddressNotFoundError as e: except geoip2.errors.AddressNotFoundError as e:
return '%s' % e return '%s' % e
except Exception as e: except Exception as e:

View File

@@ -90,6 +90,8 @@ def add_notifier_each(notifier_id=None, notify_action=None, stream_data=None, ti
notifiers_enabled = notifiers.get_notifiers(notify_action=notify_action) notifiers_enabled = notifiers.get_notifiers(notify_action=notify_action)
if notifiers_enabled and not manual_trigger: if notifiers_enabled and not manual_trigger:
logger.debug(u"Tautulli NotificationHandler :: Notifiers enabled for notify_action '%s'." % notify_action)
# Check if notification conditions are satisfied # Check if notification conditions are satisfied
conditions = notify_conditions(notify_action=notify_action, conditions = notify_conditions(notify_action=notify_action,
stream_data=stream_data, stream_data=stream_data,
@@ -98,6 +100,9 @@ def add_notifier_each(notifier_id=None, notify_action=None, stream_data=None, ti
conditions = True conditions = True
if notifiers_enabled and (manual_trigger or conditions): if notifiers_enabled and (manual_trigger or conditions):
if manual_trigger:
logger.debug(u"Tautulli NotificationHandler :: Notifiers enabled for notify_action '%s' (manual trigger)." % notify_action)
if stream_data or timeline_data: if stream_data or timeline_data:
# Build the notification parameters # Build the notification parameters
parameters = build_media_notify_params(notify_action=notify_action, parameters = build_media_notify_params(notify_action=notify_action,
@@ -138,6 +143,7 @@ def add_notifier_each(notifier_id=None, notify_action=None, stream_data=None, ti
def notify_conditions(notify_action=None, stream_data=None, timeline_data=None): def notify_conditions(notify_action=None, stream_data=None, timeline_data=None):
# Activity notifications # Activity notifications
if stream_data: if stream_data:
logger.debug(u"Tautulli NotificationHandler :: Checking global notification conditions.")
# Check if notifications enabled for user and library # Check if notifications enabled for user and library
# user_data = users.Users() # user_data = users.Users()
@@ -163,36 +169,37 @@ def notify_conditions(notify_action=None, stream_data=None, timeline_data=None):
user_sessions = [s for s in result['sessions'] if s['user_id'] == stream_data['user_id']] user_sessions = [s for s in result['sessions'] if s['user_id'] == stream_data['user_id']]
if plexpy.CONFIG.NOTIFY_CONCURRENT_BY_IP: if plexpy.CONFIG.NOTIFY_CONCURRENT_BY_IP:
return len(Counter(s['ip_address'] for s in user_sessions)) >= plexpy.CONFIG.NOTIFY_CONCURRENT_THRESHOLD evaluated = len(Counter(s['ip_address'] for s in user_sessions)) >= plexpy.CONFIG.NOTIFY_CONCURRENT_THRESHOLD
else: else:
return len(user_sessions) >= plexpy.CONFIG.NOTIFY_CONCURRENT_THRESHOLD evaluated = len(user_sessions) >= plexpy.CONFIG.NOTIFY_CONCURRENT_THRESHOLD
elif notify_action == 'on_newdevice': elif notify_action == 'on_newdevice':
data_factory = datafactory.DataFactory() data_factory = datafactory.DataFactory()
user_devices = data_factory.get_user_devices(user_id=stream_data['user_id']) user_devices = data_factory.get_user_devices(user_id=stream_data['user_id'])
return stream_data['machine_id'] not in user_devices evaluated = stream_data['machine_id'] not in user_devices
elif stream_data['media_type'] in ('movie', 'episode', 'clip'): elif stream_data['media_type'] in ('movie', 'episode', 'clip'):
progress_percent = helpers.get_percent(stream_data['view_offset'], stream_data['duration']) progress_percent = helpers.get_percent(stream_data['view_offset'], stream_data['duration'])
if notify_action == 'on_stop': if notify_action == 'on_stop':
return (plexpy.CONFIG.NOTIFY_CONSECUTIVE or evaluated = (plexpy.CONFIG.NOTIFY_CONSECUTIVE or
(stream_data['media_type'] == 'movie' and progress_percent < plexpy.CONFIG.MOVIE_WATCHED_PERCENT) or (stream_data['media_type'] == 'movie' and progress_percent < plexpy.CONFIG.MOVIE_WATCHED_PERCENT) or
(stream_data['media_type'] == 'episode' and progress_percent < plexpy.CONFIG.TV_WATCHED_PERCENT)) (stream_data['media_type'] == 'episode' and progress_percent < plexpy.CONFIG.TV_WATCHED_PERCENT))
elif notify_action == 'on_resume': elif notify_action == 'on_resume':
return plexpy.CONFIG.NOTIFY_CONSECUTIVE or progress_percent < 99 evaluated = plexpy.CONFIG.NOTIFY_CONSECUTIVE or progress_percent < 99
# All other activity notify actions # All other activity notify actions
else: else:
return True evaluated = True
elif stream_data['media_type'] == 'track': elif stream_data['media_type'] == 'track':
return True evaluated = True
else: else:
return False evaluated = False
logger.debug(u"Tautulli NotificationHandler :: Global notification conditions evaluated to '{}'.".format(evaluated))
# Recently Added notifications # Recently Added notifications
elif timeline_data: elif timeline_data:
@@ -204,11 +211,13 @@ def notify_conditions(notify_action=None, stream_data=None, timeline_data=None):
# # logger.debug(u"Tautulli NotificationHandler :: Notifications for library '%s' is disabled." % library_details['section_name']) # # logger.debug(u"Tautulli NotificationHandler :: Notifications for library '%s' is disabled." % library_details['section_name'])
# return False # return False
return True evaluated = True
# Server notifications # Server notifications
else: else:
return True evaluated = True
return evaluated
def notify_custom_conditions(notifier_id=None, parameters=None): def notify_custom_conditions(notifier_id=None, parameters=None):

View File

@@ -999,8 +999,8 @@ class ANDROIDAPP(Notifier):
config_option.append({ config_option.append({
'label': 'Device', 'label': 'Device',
'description': 'No devices registered. ' 'description': 'No devices registered. '
'<a data-tab-destination="tabs-android_app" data-toggle="tab" data-dismiss="modal" ' '<a data-tab-destination="android_app" data-toggle="tab" data-dismiss="modal">'
'data-target="#top">Get the Android App</a> and register a device.', 'Get the Android App</a> and register a device.',
'input_type': 'help' 'input_type': 'help'
}) })
else: else:
@@ -1009,8 +1009,8 @@ class ANDROIDAPP(Notifier):
'value': self.config['device_id'], 'value': self.config['device_id'],
'name': 'androidapp_device_id', 'name': 'androidapp_device_id',
'description': 'Set your Android app device or ' 'description': 'Set your Android app device or '
'<a data-tab-destination="tabs-android_app" data-toggle="tab" data-dismiss="modal" ' '<a data-tab-destination="android_app" data-toggle="tab" data-dismiss="modal">'
'data-target="#top">register a new device</a> with Tautulli.', 'register a new device</a> with Tautulli.',
'input_type': 'select', 'input_type': 'select',
'select_options': devices 'select_options': devices
}) })
@@ -1264,8 +1264,8 @@ class DISCORD(Notifier):
'value': self.config['incl_card'], 'value': self.config['incl_card'],
'name': 'discord_incl_card', 'name': 'discord_incl_card',
'description': 'Include an info card with a poster and metadata with the notifications.<br>' 'description': 'Include an info card with a poster and metadata with the notifications.<br>'
'Note: <a data-tab-destination="tabs-notifications" data-dismiss="modal" ' 'Note: <a data-tab-destination="3rd_party_apis" data-dismiss="modal" '
'data-target="#notify_upload_posters">Image Hosting</a> ' 'data-target="notify_upload_posters">Image Hosting</a> '
'must be enabled under the notifications settings tab.', 'must be enabled under the notifications settings tab.',
'input_type': 'checkbox' 'input_type': 'checkbox'
}, },
@@ -1639,8 +1639,8 @@ class FACEBOOK(Notifier):
'value': self.config['incl_card'], 'value': self.config['incl_card'],
'name': 'facebook_incl_card', 'name': 'facebook_incl_card',
'description': 'Include an info card with a poster and metadata with the notifications.<br>' 'description': 'Include an info card with a poster and metadata with the notifications.<br>'
'Note: <a data-tab-destination="tabs-notifications" data-dismiss="modal" ' 'Note: <a data-tab-destination="3rd_party_apis" data-dismiss="modal" '
'data-target="#notify_upload_posters">Image Hosting</a> ' 'data-target="notify_upload_posters">Image Hosting</a> '
'must be enabled under the notifications settings tab.', 'must be enabled under the notifications settings tab.',
'input_type': 'checkbox' 'input_type': 'checkbox'
}, },
@@ -1962,8 +1962,8 @@ class HIPCHAT(Notifier):
'value': self.config['incl_card'], 'value': self.config['incl_card'],
'name': 'hipchat_incl_card', 'name': 'hipchat_incl_card',
'description': 'Include an info card with a poster and metadata with the notifications.<br>' 'description': 'Include an info card with a poster and metadata with the notifications.<br>'
'Note: <a data-tab-destination="tabs-notifications" data-dismiss="modal" ' 'Note: <a data-tab-destination="3rd_party_apis" data-dismiss="modal" '
'data-target="#notify_upload_posters">Image Hosting</a> ' 'data-target="notify_upload_posters">Image Hosting</a> '
'must be enabled under the notifications settings tab.<br>' 'must be enabled under the notifications settings tab.<br>'
'Note: This will change the notification type to HTML and emoticons will no longer work.', 'Note: This will change the notification type to HTML and emoticons will no longer work.',
'input_type': 'checkbox' 'input_type': 'checkbox'
@@ -2184,8 +2184,8 @@ class JOIN(Notifier):
'value': self.config['incl_poster'], 'value': self.config['incl_poster'],
'name': 'join_incl_poster', 'name': 'join_incl_poster',
'description': 'Include a poster with the notifications.<br>' 'description': 'Include a poster with the notifications.<br>'
'Note: <a data-tab-destination="tabs-notifications" data-dismiss="modal" ' 'Note: <a data-tab-destination="3rd_party_apis" data-dismiss="modal" '
'data-target="#notify_upload_posters">Image Hosting</a> ' 'data-target="notify_upload_posters">Image Hosting</a> '
'must be enabled under the notifications settings tab.', 'must be enabled under the notifications settings tab.',
'input_type': 'checkbox' 'input_type': 'checkbox'
}, },
@@ -3348,8 +3348,8 @@ class SLACK(Notifier):
'value': self.config['incl_card'], 'value': self.config['incl_card'],
'name': 'slack_incl_card', 'name': 'slack_incl_card',
'description': 'Include an info card with a poster and metadata with the notifications.<br>' 'description': 'Include an info card with a poster and metadata with the notifications.<br>'
'Note: <a data-tab-destination="tabs-notifications" data-dismiss="modal" ' 'Note: <a data-tab-destination="3rd_party_apis" data-dismiss="modal" '
'data-target="#notify_upload_posters">Image Hosting</a> ' 'data-target="notify_upload_posters">Image Hosting</a> '
'must be enabled under the notifications settings tab.', 'must be enabled under the notifications settings tab.',
'input_type': 'checkbox' 'input_type': 'checkbox'
}, },
@@ -3593,8 +3593,8 @@ class TWITTER(Notifier):
'value': self.config['incl_poster'], 'value': self.config['incl_poster'],
'name': 'twitter_incl_poster', 'name': 'twitter_incl_poster',
'description': 'Include a poster with the notifications.<br>' 'description': 'Include a poster with the notifications.<br>'
'Note: <a data-tab-destination="tabs-notifications" data-dismiss="modal" ' 'Note: <a data-tab-destination="3rd_party_apis" data-dismiss="modal" '
'data-target="#notify_upload_posters">Image Hosting</a> ' 'data-target="notify_upload_posters">Image Hosting</a> '
'must be enabled under the notifications settings tab.', 'must be enabled under the notifications settings tab.',
'input_type': 'checkbox' 'input_type': 'checkbox'
} }

View File

@@ -1,2 +1,2 @@
PLEXPY_BRANCH = "master" PLEXPY_BRANCH = "master"
PLEXPY_RELEASE_VERSION = "v2.1.39" PLEXPY_RELEASE_VERSION = "v2.1.40"

View File

@@ -2819,7 +2819,11 @@ class WebInterface(object):
"newsletter_password": plexpy.CONFIG.NEWSLETTER_PASSWORD, "newsletter_password": plexpy.CONFIG.NEWSLETTER_PASSWORD,
"newsletter_inline_styles": checked(plexpy.CONFIG.NEWSLETTER_INLINE_STYLES), "newsletter_inline_styles": checked(plexpy.CONFIG.NEWSLETTER_INLINE_STYLES),
"newsletter_custom_dir": plexpy.CONFIG.NEWSLETTER_CUSTOM_DIR, "newsletter_custom_dir": plexpy.CONFIG.NEWSLETTER_CUSTOM_DIR,
"win_sys_tray": checked(plexpy.CONFIG.WIN_SYS_TRAY) "win_sys_tray": checked(plexpy.CONFIG.WIN_SYS_TRAY),
"maxmind_license_key": plexpy.CONFIG.MAXMIND_LICENSE_KEY,
"geoip_db": plexpy.CONFIG.GEOIP_DB,
"geoip_db_installed": plexpy.CONFIG.GEOIP_DB_INSTALLED,
"geoip_db_update_days": plexpy.CONFIG.GEOIP_DB_UPDATE_DAYS
} }
return serve_template(templatename="settings.html", title="Settings", config=config, kwargs=kwargs) return serve_template(templatename="settings.html", title="Settings", config=config, kwargs=kwargs)
@@ -3055,15 +3059,17 @@ class WebInterface(object):
@cherrypy.tools.json_out() @cherrypy.tools.json_out()
@requireAuth(member_of("admin")) @requireAuth(member_of("admin"))
@addtoapi() @addtoapi()
def install_geoip_db(self, **kwargs): def install_geoip_db(self, update=False, **kwargs):
""" Downloads and installs the GeoLite2 database """ """ Downloads and installs the GeoLite2 database """
result = helpers.install_geoip_db() update = True if update == 'true' else False
result = helpers.install_geoip_db(update=update)
if result: if result:
return {'result': 'success', 'message': 'GeoLite2 database installed successful.'} return {'result': 'success', 'message': 'GeoLite2 database installed successful.', 'updated': result}
else: else:
return {'result': 'error', 'message': 'GeoLite2 database install failed.'} return {'result': 'error', 'message': 'GeoLite2 database install failed.', 'updated': 0}
@cherrypy.expose @cherrypy.expose
@cherrypy.tools.json_out() @cherrypy.tools.json_out()