Merge pull request #629 from JackDandy/feature/ChangeEmailNotif

Change refactor email notifier.
This commit is contained in:
JackDandy 2016-01-20 19:32:15 +00:00
commit 38db11d3fe
8 changed files with 460 additions and 415 deletions

View file

@ -19,6 +19,7 @@
* Update Requests library 2.7.0 (5d6d1bc) to 2.9.1 (a1c9b84)
* Update SimpleJSON library 3.8.0 (a37a9bd) to 3.8.1 (6022794)
* Update Six compatibility library 1.9.0 (r400) to 1.10.0 (r405)
* Change refactor email notifier
### 0.11.3 (2016-01-16 20:00:00 UTC)

View file

@ -1177,14 +1177,16 @@ pre{
/* =======================================================================
input sizing (for config pages)
========================================================================== */
.showlist-select optgroup,
#pickShow optgroup,
#showfilter optgroup,
#editAProvider optgroup{
color:#eee;
background-color:rgb(51, 51, 51)
}
.showlist-select optgroup option,
#pickShow optgroup option,
#showfilter optgroup option,
#editAProvider optgroup option{

View file

@ -1142,6 +1142,7 @@ pre{
input sizing (for config pages)
========================================================================== */
.showlist-select optgroup,
#pickShow optgroup,
#showfilter optgroup,
#editAProvider optgroup{
@ -1149,6 +1150,7 @@ input sizing (for config pages)
background-color:#888
}
.showlist-select optgroup option,
#pickShow optgroup option,
#showfilter optgroup option,
#editAProvider optgroup option{

View file

@ -1637,7 +1637,7 @@
<div class="component-group-desc">
<img class="notifier-icon" src="$sbRoot/images/notifiers/email.png" alt="" title="Email" />
<h3><a href="<%= anon_url('http://en.wikipedia.org/wiki/Comparison_of_webmail_providers') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Email</a></h3>
<p>Allows configuration of email notifications on a per show basis.</p>
<p>Email notification settings.</p>
</div>
<fieldset class="component-group-list">
<div class="field-pair">
@ -1678,95 +1678,71 @@
</span>
</label>
</div>
<div class="field-pair">
<label for="email_host">
<span class="component-title">SMTP host</span>
<input type="text" name="email_host" id="email_host" value="$sickbeard.EMAIL_HOST" class="form-control input-sm input250" />
</label>
<label>
<span class="component-title">&nbsp;</span>
<span class="component-desc">hostname of your SMTP email server.</span>
</label>
</div>
<div class="field-pair">
<label for="email_port">
<span class="component-title">SMTP port</span>
<input type="text" name="email_port" id="email_port" value="$sickbeard.EMAIL_PORT" class="form-control input-sm input75" />
</label>
<label>
<span class="component-title">&nbsp;</span>
<span class="component-desc">port number used to connect to your SMTP host.</span>
</label>
</div>
<div class="field-pair">
<label for="email_from">
<span class="component-title">SMTP from</span>
<input type="text" name="email_from" id="email_from" value="$sickbeard.EMAIL_FROM" class="form-control input-sm input250" />
<span class="component-title">SMTP sender email address</span>
<span class="component-desc">
<input type="text" name="email_from" id="email_from" value="$sickbeard.EMAIL_FROM" class="form-control input-sm input250" />
<div class="clear-left"><p>some recipient hosts require a real address</p></div>
</span>
</label>
<label>
<span class="component-title">&nbsp;</span>
<span class="component-desc">sender email address, some hosts require a real address.</span>
</label>
</div>
<div class="field-pair">
<div style="margin-bottom:5px;display:inline-block">
<span class="component-title">SMTP server hostname</span>
<span class="component-desc">
<input type="text" name="email_host" id="email_host" value="$sickbeard.EMAIL_HOST" class="form-control input-sm input250" />
<span style="float:left;margin-right:6px">and port</span><input type="text" name="email_port" id="email_port" value="$sickbeard.EMAIL_PORT" class="form-control input-sm input75" style="width:60px" />
</span>
</div>
<label for="email_tls">
<span class="component-title">Use TLS</span>
<span class="component-title">Enable TLS encryption</span>
<span class="component-desc">
<input type="checkbox" name="email_tls" id="email_tls" #if $sickbeard.EMAIL_TLS then 'checked="checked"' else ''# />
<p>check to use TLS encryption.</p>
</span>
</label>
<label for="email_user">
<span class="component-title">SMTP server username</span>
<span class="component-desc">
<input type="text" name="email_user" id="email_user" value="$sickbeard.EMAIL_USER" class="form-control input-sm input250" />
<p>(optional)</p>
</span>
</label>
<label for="email_password">
<span class="component-title">SMTP server password</span>
<span class="component-desc">
<input type="password" name="email_password" id="email_password" value="#echo '*' * len($sickbeard.EMAIL_PASSWORD)#" class="form-control input-sm input250" />
<p>(optional)</p>
</span>
</label>
</div>
<div class="field-pair">
<label for="email_user">
<span class="component-title">SMTP user</span>
<input type="text" name="email_user" id="email_user" value="$sickbeard.EMAIL_USER" class="form-control input-sm input250" />
</label>
<label>
<span class="component-title">&nbsp;</span>
<span class="component-desc">(optional) your SMTP server username.</span>
</label>
</div>
<div class="field-pair">
<label for="email_password">
<span class="component-title">SMTP password</span>
<input type="password" name="email_password" id="email_password" value="#echo '*' * len($sickbeard.EMAIL_PASSWORD)#" class="form-control input-sm input250" />
</label>
<label>
<span class="component-title">&nbsp;</span>
<span class="component-desc">(optional) your SMTP server password.</span>
</label>
</div>
<div class="field-pair">
<label for="email_list">
<span class="component-title">Global email list</span>
<input type="text" name="email_list" id="email_list" value="$sickbeard.EMAIL_LIST" class="form-control input-sm input350" />
</label>
<label>
<span class="component-title">&nbsp;</span>
<span class="component-desc">all emails here receive notifications for <b>all</b> shows.</span>
<span class="component-title">Notify for every show</span>
<span class="component-desc">
<input type="text" name="email_list" id="email_list" value="$sickbeard.EMAIL_LIST" class="form-control input-sm input350" />
<div class="clear-left"><p>comma separated email list</p></div>
</span>
</label>
</div>
<div class="field-pair">
<label for="email_show">
<span class="component-title">Show notification list</span>
<select name="email_show" id="email_show" class="form-control input-sm">
<option value="-1">-- Select a Show --</option>
<span class="component-title">Notify on a per show basis</span>
<select name="email_show" id="email_show" class="showlist-select form-control input-sm">
<option value="-1">-- Shows appear here --</option>
</select>
</label>
<label>
<span class="component-title">&nbsp;</span>
<input type="text" name="email_show_list" id="email_show_list" class="form-control input-sm input350" />
</label>
<label>
<span class="component-title">&nbsp;</span>
<span class="component-desc">configure per show notifications here.</span>
<span class="component-desc">
<input type="text" name="show_email_list" id="show_email_list" class="form-control input-sm input350" />
<input type="button" value="Save Show" id="save_show_email" class="btn" />
<div class="clear-left"><p>comma separated email list for each show</p></div>
</span>
</label>
</div>
<div class="testNotification" id="testEmail-result">Click below to test.</div>
<input class="btn" type="button" value="Test Email" id="testEmail" />
<input class="btn" type="submit" class="config_submitter" value="Save Changes" />
<input type="button" class="btn" value="Test Email" id="testEmail" />
<input type="submit" class="btn config_submitter" value="Save Changes" />
</div><!-- /content_use_email //-->
</fieldset>
</div><!-- /email component-group //-->

View file

@ -75,6 +75,16 @@
});
});
// show instructions for plex when enabled
$('#use_plex').click(function() {
if ( $(this).is(':checked') ) {
$('.plexinfo').removeClass('hide');
} else {
$('.plexinfo').addClass('hide');
}
});
if ($('input[id="use_plex"]').is(':checked')) {$('.plexinfo').removeClass('hide')}
$('#testPMC').click(function () {
var plex_host = $.trim($('#plex_host').val());
var plex_username = $.trim($('#plex_username').val());
@ -224,30 +234,6 @@
$.get(sbRoot + '/home/testLibnotify',
function (data) { $('#testLibnotify-result').html(data); });
});
$('#twitterStep1').click(function() {
$('#testTwitter-result').html(loading);
$.get(sbRoot + '/home/twitterStep1', function (data) {window.open(data); })
.done(function () { $('#testTwitter-result').html('<b>Step1:</b> Confirm Authorization'); });
});
$('#twitterStep2').click(function () {
var twitter_key = $.trim($('#twitter_key').val());
if (!twitter_key) {
$('#testTwitter-result').html('Please fill out the necessary fields above.');
$('#twitter_key').addClass('warning');
return;
}
$('#twitter_key').removeClass('warning');
$('#testTwitter-result').html(loading);
$.get(sbRoot + '/home/twitterStep2', {'key': twitter_key},
function (data) { $('#testTwitter-result').html(data); });
});
$('#testTwitter').click(function() {
$.get(sbRoot + '/home/testTwitter',
function (data) { $('#testTwitter-result').html(data); });
});
$('#settingsNMJ').click(function() {
if (!$('#nmj_host').val()) {
@ -352,6 +338,139 @@
});
});
$('#testNMA').click(function () {
var nma_api = $.trim($('#nma_api').val());
var nma_priority = $('#nma_priority').val();
if (!nma_api) {
$('#testNMA-result').html('Please fill out the necessary fields above.');
$('#nma_api').addClass('warning');
return;
}
$('#nma_api').removeClass('warning');
$(this).prop('disabled', true);
$('#testNMA-result').html(loading);
$.get(sbRoot + '/home/testNMA', {'nma_api': nma_api, 'nma_priority': nma_priority})
.done(function (data) {
$('#testNMA-result').html(data);
$('#testNMA').prop('disabled', false);
});
});
$('#testPushalot').click(function () {
var pushalot_authorizationtoken = $.trim($('#pushalot_authorizationtoken').val());
if (!pushalot_authorizationtoken) {
$('#testPushalot-result').html('Please fill out the necessary fields above.');
$('#pushalot_authorizationtoken').addClass('warning');
return;
}
$('#pushalot_authorizationtoken').removeClass('warning');
$(this).prop('disabled', true);
$('#testPushalot-result').html(loading);
$.get(sbRoot + '/home/testPushalot', {'authorizationToken': pushalot_authorizationtoken})
.done(function (data) {
$('#testPushalot-result').html(data);
$('#testPushalot').prop('disabled', false);
});
});
$('#testPushbullet').click(function () {
var pushbullet_access_token = $.trim($('#pushbullet_access_token').val());
var pushbullet_device_iden = $('#pushbullet_device_iden').val();
if (!pushbullet_access_token) {
$('#testPushbullet-result').html('Please fill out the necessary fields above.');
$('#pushbullet_access_token').addClass('warning');
return;
}
$('#pushbullet_access_token').removeClass('warning');
$(this).prop('disabled', true);
$('#testPushbullet-result').html(loading);
$.get(sbRoot + '/home/testPushbullet', {'accessToken': pushbullet_access_token, 'device_iden': pushbullet_device_iden})
.done(function (data) {
$('#testPushbullet-result').html(data);
$('#testPushbullet').prop('disabled', false);
});
});
function get_pushbullet_devices (msg) {
var pushbullet_access_token = $.trim($('#pushbullet_access_token').val());
if (!pushbullet_access_token) {
$('#testPushbullet-result').html('Please fill out the necessary fields above.');
$('#pushbullet_access_token').addClass('warning');
return;
}
$(this).prop("disabled", true);
if (msg) {
$('#testPushbullet-result').html(loading);
}
var current_pushbullet_device = $('#pushbullet_device_iden').val();
$.get(sbRoot + '/home/getPushbulletDevices', {'accessToken': pushbullet_access_token})
.done(function (data) {
var devices = jQuery.parseJSON(data || '{}').devices;
var error = jQuery.parseJSON(data || '{}').error;
$('#pushbullet_device_list').html('');
if (devices) {
// add default option to send to all devices
$('#pushbullet_device_list').append('<option value="" selected="selected">-- All Devices --</option>');
for (var i = 0; i < devices.length; i++) {
// only list active device targets
if (devices[i].active == true) {
// if a device in the list matches our current iden, select it
if (current_pushbullet_device == devices[i].iden) {
$('#pushbullet_device_list').append('<option value="' + devices[i].iden + '" selected="selected">' + devices[i].manufacturer + ' ' + devices[i].nickname + '</option>');
} else {
$('#pushbullet_device_list').append('<option value="' + devices[i].iden + '">' + devices[i].manufacturer + ' ' + devices[i].nickname + '</option>');
}
}
}
}
$('#getPushbulletDevices').prop('disabled', false);
if (msg) {
if (error.message) {
$('#testPushbullet-result').html(error.message);
} else {
$('#testPushbullet-result').html(msg);
}
}
});
$('#pushbullet_device_list').change(function () {
$('#pushbullet_device_iden').val($('#pushbullet_device_list').val());
$('#testPushbullet-result').html('Don\'t forget to save your new Pushbullet settings.');
});
}
$('#getPushbulletDevices').click(function () {
get_pushbullet_devices('Device list updated. Select specific device to use.');
});
if ($('#use_pushbullet').prop('checked')) {
get_pushbullet_devices();
}
$('#twitterStep1').click(function() {
$('#testTwitter-result').html(loading);
$.get(sbRoot + '/home/twitterStep1', function (data) {window.open(data); })
.done(function () { $('#testTwitter-result').html('<b>Step1:</b> Confirm Authorization'); });
});
$('#twitterStep2').click(function () {
var twitter_key = $.trim($('#twitter_key').val());
if (!twitter_key) {
$('#testTwitter-result').html('Please fill out the necessary fields above.');
$('#twitter_key').addClass('warning');
return;
}
$('#twitter_key').removeClass('warning');
$('#testTwitter-result').html(loading);
$.get(sbRoot + '/home/twitterStep2', {'key': twitter_key},
function (data) { $('#testTwitter-result').html(data); });
});
$('#testTwitter').click(function() {
$.get(sbRoot + '/home/testTwitter',
function (data) { $('#testTwitter-result').html(data); });
});
var elTraktAuth = $('#trakt-authenticate'), elTraktAuthResult = $('#trakt-authentication-result');
function trakt_send_auth(){
@ -510,6 +629,67 @@
});
});
function load_show_notify_lists() {
$.get(sbRoot + '/home/loadShowNotifyLists', function (data) {
var list, html, item, len= 0, el;
list = $.parseJSON(data);
html = [];
for (item in list) {
for (var k in list[item]) {
if ($.isArray(list[item][k])) {
len += list[item][k].length;
html.push('\t<optgroup label="' + k + '">');
for (var show in list[item][k]) {
html.push('\t\t<option value="' + list[item][k][show].id + '"'
+ ' data="' + list[item][k][show].list + '"'
+ '>' + list[item][k][show].name + '</option>');
}
html.push('\t</optgroup>');
}
}
}
if (len) {
el = $('#email_show');
el.html('<option value="-1">-- Select show --</option>'
+ html.join('\n'));
$('#show_email_list').val('');
el.change(function () {
$('#show_email_list').val(
$(this).find('option[value="' + $(this).val() + '"]').attr('data'))
});
}
});
}
// Load the per show notify lists everytime this page is loaded
load_show_notify_lists();
// Update the internal data struct anytime settings are saved to the server
$('#email_show').bind('notify', function () { load_show_notify_lists(); });
$('#save_show_email').click(
function(){
var show = $('#email_show').val();
if ('-1' == show) {
$('#testEmail-result').html('No show selected for save.');
return
}
$.post(sbRoot + '/home/save_show_email', {
show: show,
emails: $('#show_email_list').val()},
function (data){
// Reload the per show notify lists to reflect changes
load_show_notify_lists();
var result = $.parseJSON(data),
show = $('#email_show').find('option[value="' + result.id + '"]').text();
$('#testEmail-result').html(result.success
? 'Success. Notify list updated for show "' + show + '". Click below to test.'
: 'Error saving notify list for show %s' % show);
});
});
$('#testEmail').click(function () {
var status, host, port, tls, from, user, pwd, err, to;
status = $('#testEmail-result');
@ -523,174 +703,27 @@
from = from.length > 0 ? from : 'root@localhost';
user = $('#email_user').val().trim();
pwd = $('#email_password').val();
err = '';
if (host === null) {
err += '<li style="color: red;">You must specify an SMTP hostname!</li>';
err = [];
if (null == host) {
err.push('SMTP server hostname');
}
if (port === null) {
err += '<li style="color: red;">You must specify an SMTP port!</li>';
} else if (port.match(/^\d+$/) === null || parseInt(port, 10) > 65535) {
err += '<li style="color: red;">SMTP port must be between 0 and 65535!</li>';
if (null == port) {
err.push('SMTP server host port');
} else if (null == port.match(/^\d+$/) || parseInt(port, 10) > 65535) {
err.push('SMTP server host port must be between 0 and 65535');
}
if (err.length > 0) {
err = '<ol>' + err + '</ol>';
status.html(err);
if (0 < err.length) {
status.html('Required: ' + err.join(', '));
} else {
to = prompt('Enter an email address to send the test to:', null);
if (to === null || to.length === 0 || to.match(/.*@.*/) === null) {
status.html('<p style="color: red;">You must provide a recipient email address!</p>');
to = prompt('Enter an email address to send the test to:', '');
if (null == to || 0 == to.length || null == to.match(/.*@.*/)) {
status.html('Required: A valid address for email test');
} else {
$.get(sbRoot + '/home/testEmail', {host: host, port: port, smtp_from: from, use_tls: tls, user: user, pwd: pwd, to: to},
function (msg) { $('#testEmail-result').html(msg); });
$.get(sbRoot + '/home/testEmail',
{host:host, port:port, smtp_from:from, use_tls:tls, user:user, pwd:pwd, to:to},
function(msg) {$('#testEmail-result').html(msg);});
}
}
});
$('#testNMA').click(function () {
var nma_api = $.trim($('#nma_api').val());
var nma_priority = $('#nma_priority').val();
if (!nma_api) {
$('#testNMA-result').html('Please fill out the necessary fields above.');
$('#nma_api').addClass('warning');
return;
}
$('#nma_api').removeClass('warning');
$(this).prop('disabled', true);
$('#testNMA-result').html(loading);
$.get(sbRoot + '/home/testNMA', {'nma_api': nma_api, 'nma_priority': nma_priority})
.done(function (data) {
$('#testNMA-result').html(data);
$('#testNMA').prop('disabled', false);
});
});
$('#testPushalot').click(function () {
var pushalot_authorizationtoken = $.trim($('#pushalot_authorizationtoken').val());
if (!pushalot_authorizationtoken) {
$('#testPushalot-result').html('Please fill out the necessary fields above.');
$('#pushalot_authorizationtoken').addClass('warning');
return;
}
$('#pushalot_authorizationtoken').removeClass('warning');
$(this).prop('disabled', true);
$('#testPushalot-result').html(loading);
$.get(sbRoot + '/home/testPushalot', {'authorizationToken': pushalot_authorizationtoken})
.done(function (data) {
$('#testPushalot-result').html(data);
$('#testPushalot').prop('disabled', false);
});
});
$('#testPushbullet').click(function () {
var pushbullet_access_token = $.trim($('#pushbullet_access_token').val());
var pushbullet_device_iden = $('#pushbullet_device_iden').val();
if (!pushbullet_access_token) {
$('#testPushbullet-result').html('Please fill out the necessary fields above.');
$('#pushbullet_access_token').addClass('warning');
return;
}
$('#pushbullet_access_token').removeClass('warning');
$(this).prop('disabled', true);
$('#testPushbullet-result').html(loading);
$.get(sbRoot + '/home/testPushbullet', {'accessToken': pushbullet_access_token, 'device_iden': pushbullet_device_iden})
.done(function (data) {
$('#testPushbullet-result').html(data);
$('#testPushbullet').prop('disabled', false);
});
});
function get_pushbullet_devices (msg) {
var pushbullet_access_token = $.trim($('#pushbullet_access_token').val());
if (!pushbullet_access_token) {
$('#testPushbullet-result').html('Please fill out the necessary fields above.');
$('#pushbullet_access_token').addClass('warning');
return;
}
$(this).prop("disabled", true);
if (msg) {
$('#testPushbullet-result').html(loading);
}
var current_pushbullet_device = $('#pushbullet_device_iden').val();
$.get(sbRoot + '/home/getPushbulletDevices', {'accessToken': pushbullet_access_token})
.done(function (data) {
var devices = jQuery.parseJSON(data || '{}').devices;
var error = jQuery.parseJSON(data || '{}').error;
$('#pushbullet_device_list').html('');
if (devices) {
// add default option to send to all devices
$('#pushbullet_device_list').append('<option value="" selected="selected">-- All Devices --</option>');
for (var i = 0; i < devices.length; i++) {
// only list active device targets
if (devices[i].active == true) {
// if a device in the list matches our current iden, select it
if (current_pushbullet_device == devices[i].iden) {
$('#pushbullet_device_list').append('<option value="' + devices[i].iden + '" selected="selected">' + devices[i].manufacturer + ' ' + devices[i].nickname + '</option>');
} else {
$('#pushbullet_device_list').append('<option value="' + devices[i].iden + '">' + devices[i].manufacturer + ' ' + devices[i].nickname + '</option>');
}
}
}
}
$('#getPushbulletDevices').prop('disabled', false);
if (msg) {
if (error.message) {
$('#testPushbullet-result').html(error.message);
} else {
$('#testPushbullet-result').html(msg);
}
}
});
$('#pushbullet_device_list').change(function () {
$('#pushbullet_device_iden').val($('#pushbullet_device_list').val());
$('#testPushbullet-result').html('Don\'t forget to save your new Pushbullet settings.');
});
}
$('#getPushbulletDevices').click(function () {
get_pushbullet_devices('Device list updated. Select specific device to use.');
});
if ($('#use_pushbullet').prop('checked')) {
get_pushbullet_devices();
}
$('#email_show').change(function () {
var key = parseInt($('#email_show').val(), 10);
$('#email_show_list').val(key >= 0 ? notify_data[key.toString()].list : '');
});
// Update the internal data struct anytime settings are saved to the server
$('#email_show').bind('notify', function () { load_show_notify_lists(); });
function load_show_notify_lists() {
$.get(sbRoot + "/home/loadShowNotifyLists", function (data) {
var list, html, s;
list = $.parseJSON(data);
notify_data = list;
if (list._size === 0) {
return;
}
html = '<option value="-1">-- Select --</option>';
for (s in list) {
if (s.charAt(0) !== '_') {
html += '<option value="' + list[s].id + '">' + $('<div/>').text(list[s].name).html() + '</option>';
}
}
$('#email_show').html(html);
$('#email_show_list').val('');
});
}
// Load the per show notify lists everytime this page is loaded
load_show_notify_lists();
// show instructions for plex when enabled
$('#use_plex').click(function() {
if ( $(this).is(':checked') ) {
$('.plexinfo').removeClass('hide');
} else {
$('.plexinfo').addClass('hide');
}
});
if ($('input[id="use_plex"]').is(':checked')) {$('.plexinfo').removeClass('hide')}
});

View file

@ -393,6 +393,7 @@ PUSHBULLET_ACCESS_TOKEN = None
PUSHBULLET_DEVICE_IDEN = None
USE_EMAIL = False
EMAIL_OLD_SUBJECTS = None
EMAIL_NOTIFY_ONSNATCH = False
EMAIL_NOTIFY_ONDOWNLOAD = False
EMAIL_NOTIFY_ONSUBTITLEDOWNLOAD = False
@ -511,7 +512,7 @@ def initialize(consoleLogging=True):
USE_PUSHOVER, PUSHOVER_USERKEY, PUSHOVER_APIKEY, PUSHOVER_NOTIFY_ONDOWNLOAD, PUSHOVER_NOTIFY_ONSUBTITLEDOWNLOAD, PUSHOVER_NOTIFY_ONSNATCH, PUSHOVER_PRIORITY, PUSHOVER_DEVICE, PUSHOVER_SOUND, \
USE_LIBNOTIFY, LIBNOTIFY_NOTIFY_ONSNATCH, LIBNOTIFY_NOTIFY_ONDOWNLOAD, LIBNOTIFY_NOTIFY_ONSUBTITLEDOWNLOAD, USE_NMJ, NMJ_HOST, NMJ_DATABASE, NMJ_MOUNT, USE_NMJv2, NMJv2_HOST, NMJv2_DATABASE, NMJv2_DBLOC, USE_SYNOINDEX, \
USE_SYNOLOGYNOTIFIER, SYNOLOGYNOTIFIER_NOTIFY_ONSNATCH, SYNOLOGYNOTIFIER_NOTIFY_ONDOWNLOAD, SYNOLOGYNOTIFIER_NOTIFY_ONSUBTITLEDOWNLOAD, \
USE_EMAIL, EMAIL_HOST, EMAIL_PORT, EMAIL_TLS, EMAIL_USER, EMAIL_PASSWORD, EMAIL_FROM, EMAIL_NOTIFY_ONSNATCH, EMAIL_NOTIFY_ONDOWNLOAD, EMAIL_NOTIFY_ONSUBTITLEDOWNLOAD, EMAIL_LIST, \
USE_EMAIL, EMAIL_OLD_SUBJECTS, EMAIL_HOST, EMAIL_PORT, EMAIL_TLS, EMAIL_USER, EMAIL_PASSWORD, EMAIL_FROM, EMAIL_NOTIFY_ONSNATCH, EMAIL_NOTIFY_ONDOWNLOAD, EMAIL_NOTIFY_ONSUBTITLEDOWNLOAD, EMAIL_LIST, \
METADATA_XBMC, METADATA_XBMC_12PLUS, METADATA_MEDIABROWSER, METADATA_PS3, METADATA_KODI, metadata_provider_dict, \
GIT_PATH, MOVE_ASSOCIATED_FILES, POSTPONE_IF_SYNC_FILES, recentSearchScheduler, NFO_RENAME, \
GUI_NAME, DEFAULT_HOME, HOME_LAYOUT, HISTORY_LAYOUT, DISPLAY_SHOW_SPECIALS, EPISODE_VIEW_LAYOUT, EPISODE_VIEW_SORT, EPISODE_VIEW_DISPLAY_PAUSED, EPISODE_VIEW_MISSED_RANGE, FUZZY_DATING, TRIM_ZERO, DATE_PRESET, TIME_PRESET, TIME_PRESET_W_SECONDS, THEME_NAME, \
@ -922,6 +923,8 @@ def initialize(consoleLogging=True):
PUSHBULLET_DEVICE_IDEN = check_setting_str(CFG, 'Pushbullet', 'pushbullet_device_iden', '')
USE_EMAIL = bool(check_setting_int(CFG, 'Email', 'use_email', 0))
EMAIL_OLD_SUBJECTS = bool(check_setting_int(CFG, 'Email', 'email_old_subjects',
None is not EMAIL_HOST and any(EMAIL_HOST)))
EMAIL_NOTIFY_ONSNATCH = bool(check_setting_int(CFG, 'Email', 'email_notify_onsnatch', 0))
EMAIL_NOTIFY_ONDOWNLOAD = bool(check_setting_int(CFG, 'Email', 'email_notify_ondownload', 0))
EMAIL_NOTIFY_ONSUBTITLEDOWNLOAD = bool(check_setting_int(CFG, 'Email', 'email_notify_onsubtitledownload', 0))
@ -1740,6 +1743,7 @@ def save_config():
new_config['Email'] = {}
new_config['Email']['use_email'] = int(USE_EMAIL)
new_config['Email']['email_old_subjects'] = int(EMAIL_OLD_SUBJECTS)
new_config['Email']['email_notify_onsnatch'] = int(EMAIL_NOTIFY_ONSNATCH)
new_config['Email']['email_notify_ondownload'] = int(EMAIL_NOTIFY_ONDOWNLOAD)
new_config['Email']['email_notify_onsubtitledownload'] = int(EMAIL_NOTIFY_ONSUBTITLEDOWNLOAD)

View file

@ -19,191 +19,186 @@
# You should have received a copy of the GNU General Public License
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import re
import sickbeard
import smtplib
from sickbeard import logger, common
from sickbeard import db
from sickbeard.exceptions import ex
from sickbeard import logger
from sickbeard.common import notifyStrings, NOTIFY_SNATCH, NOTIFY_DOWNLOAD, NOTIFY_SUBTITLE_DOWNLOAD
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.utils import formatdate
class EmailNotifier:
def __init__(self):
self.last_err = None
def test_notify(self, host, port, smtp_from, use_tls, user, pwd, to):
msg = MIMEText('This is a test message from SickGear. If you\'re reading this, the test succeeded.')
msg['Subject'] = 'SickGear: Test Message'
msg = MIMEText('Success. This is a SickGear test message. Typically sent on, %s' %
notifyStrings[NOTIFY_DOWNLOAD])
msg['Subject'] = 'SickGear: Test message'
msg['From'] = smtp_from
msg['To'] = to
msg['Date'] = formatdate(localtime=True)
return self._sendmail(host, port, smtp_from, use_tls, user, pwd, [to], msg, True)
def notify_snatch(self, ep_name, title='Snatched:'):
def _send_email(self, title, ep_name, lang='', extra='', force=False):
if not sickbeard.USE_EMAIL and not force:
return
ep_name = ep_name.encode('utf-8', 'replace')
show = self._parse_ep(ep_name)
to = self._generate_recipients(show)
if not any(to):
logger.log(u'No email recipients to notify, skipping', logger.WARNING)
return
try:
msg = MIMEMultipart('alternative')
msg.attach(MIMEText(
'<body style="font-family:Helvetica, Arial, sans-serif;">' +
'<h3>SickGear Notification - %s</h3>\n' % title +
'<p>Show: <b>' + re.search('(.+?) -.+', ep_name).group(1) +
'</b></p>\n<p>Episode: <b>' + re.search('.+ - (.+?-.+) -.+', ep_name).group(1) +
extra +
'</b></p>\n\n' +
'<footer style="margin-top:2.5em;padding:.7em 0;color:#777;border-top:#BBB solid 1px;">' +
'Powered by SickGear.</footer></body>',
'html'))
except:
try:
msg = MIMEText(ep_name)
except:
msg = MIMEText('Episode %s' % title)
msg['Subject'] = '%s%s: %s' % (lang, title, ep_name)
msg['From'] = sickbeard.EMAIL_FROM
msg['To'] = ','.join(to)
msg['Date'] = formatdate(localtime=True)
if self._sendmail(sickbeard.EMAIL_HOST, sickbeard.EMAIL_PORT, sickbeard.EMAIL_FROM, sickbeard.EMAIL_TLS,
sickbeard.EMAIL_USER, sickbeard.EMAIL_PASSWORD, to, msg):
logger.log(u'%s notification sent to [%s] for "%s"' % (title, to, ep_name), logger.DEBUG)
else:
logger.log(u'%s notification ERROR: %s' % (title, self.last_err), logger.ERROR)
def notify_snatch(self, ep_name, title=notifyStrings[NOTIFY_SNATCH]):
"""
Send a notification that an episode was snatched
ep_name: The name of the episode that was snatched
title: The title of the notification (optional)
:param ep_name: The name of the episode that was snatched
:param title: The title of the notification (optional)
"""
ep_name = ep_name.encode('utf-8', 'replace')
if sickbeard.EMAIL_NOTIFY_ONSNATCH:
show = self._parseEp(ep_name)
to = self._generate_recepients(show)
if len(to) == 0:
logger.log('Skipping email notification because there are no configured recepients', logger.WARNING)
else:
try:
msg = MIMEMultipart('alternative')
msg.attach(MIMEText(
"<body style='font-family:Helvetica, Arial, sans-serif;'><h3>SickGear Notification - Snatched</h3>\n<p>Show: <b>" + re.search(
'(.+?) -.+', ep_name).group(1) + '</b></p>\n<p>Episode: <b>' + re.search(
'.+ - (.+?-.+) -.+', ep_name).group(
1) + "</b></p>\n\n<footer style='margin-top: 2.5em; padding: .7em 0; color: #777; border-top: #BBB solid 1px;'>Powered by SickGear.</footer></body>",
'html'))
except:
msg = MIMEText(ep_name)
title = sickbeard.EMAIL_OLD_SUBJECTS and 'Snatched' or title
self._send_email(title, ep_name)
msg['Subject'] = 'Snatched: ' + ep_name
msg['From'] = sickbeard.EMAIL_FROM
msg['To'] = ','.join(to)
if self._sendmail(sickbeard.EMAIL_HOST, sickbeard.EMAIL_PORT, sickbeard.EMAIL_FROM, sickbeard.EMAIL_TLS,
sickbeard.EMAIL_USER, sickbeard.EMAIL_PASSWORD, to, msg):
logger.log('Snatch notification sent to [%s] for "%s"' % (to, ep_name), logger.DEBUG)
else:
logger.log('Snatch notification ERROR: %s' % self.last_err, logger.ERROR)
def notify_download(self, ep_name, title='Completed:'):
def notify_download(self, ep_name, title=notifyStrings[NOTIFY_DOWNLOAD]):
"""
Send a notification that an episode was downloaded
ep_name: The name of the episode that was downloaded
title: The title of the notification (optional)
:param ep_name: The name of the episode that was downloaded
:param title: The title of the notification (optional)
"""
ep_name = ep_name.encode('utf-8', 'replace')
if sickbeard.EMAIL_NOTIFY_ONDOWNLOAD:
show = self._parseEp(ep_name)
to = self._generate_recepients(show)
if len(to) == 0:
logger.log('Skipping email notification because there are no configured recepients', logger.WARNING)
else:
try:
msg = MIMEMultipart('alternative')
msg.attach(MIMEText(
"<body style='font-family:Helvetica, Arial, sans-serif;'><h3>SickGear Notification - Downloaded</h3>\n<p>Show: <b>" + re.search(
'(.+?) -.+', ep_name).group(1) + '</b></p>\n<p>Episode: <b>' + re.search(
'.+ - (.+?-.+) -.+', ep_name).group(
1) + "</b></p>\n\n<footer style='margin-top: 2.5em; padding: .7em 0; color: #777; border-top: #BBB solid 1px;'>Powered by SickGear.</footer></body>",
'html'))
except:
msg = MIMEText(ep_name)
title = sickbeard.EMAIL_OLD_SUBJECTS and 'Downloaded' or title
self._send_email(title, ep_name)
msg['Subject'] = 'Downloaded: ' + ep_name
msg['From'] = sickbeard.EMAIL_FROM
msg['To'] = ','.join(to)
if self._sendmail(sickbeard.EMAIL_HOST, sickbeard.EMAIL_PORT, sickbeard.EMAIL_FROM, sickbeard.EMAIL_TLS,
sickbeard.EMAIL_USER, sickbeard.EMAIL_PASSWORD, to, msg):
logger.log('Download notification sent to [%s] for "%s"' % (to, ep_name), logger.DEBUG)
else:
logger.log('Download notification ERROR: %s' % self.last_err, logger.ERROR)
def notify_subtitle_download(self, ep_name, lang, title='Downloaded subtitle:'):
def notify_subtitle_download(self, ep_name, lang, title=notifyStrings[NOTIFY_SUBTITLE_DOWNLOAD]):
"""
Send a notification that an subtitle was downloaded
Send a notification that a subtitle was downloaded
ep_name: The name of the episode that was downloaded
lang: Subtitle language wanted
:param ep_name: The name of the episode that was downloaded
:param lang: Subtitle language
:param title: The title of the notification (optional)
"""
ep_name = ep_name.encode('utf-8', 'replace')
if sickbeard.EMAIL_NOTIFY_ONSUBTITLEDOWNLOAD:
show = self._parseEp(ep_name)
to = self._generate_recepients(show)
if len(to) == 0:
logger.log('Skipping email notification because there are no configured recepients', logger.WARNING)
else:
try:
msg = MIMEMultipart('alternative')
msg.attach(MIMEText(
"<body style='font-family:Helvetica, Arial, sans-serif;'><h3>SickGear Notification - Subtitle Downloaded</h3>\n<p>Show: <b>" + re.search(
'(.+?) -.+', ep_name).group(1) + '</b></p>\n<p>Episode: <b>' + re.search(
'.+ - (.+?-.+) -.+', ep_name).group(
1) + '</b></p>\n<p>Language: <b>' + lang + "</b></p>\n\n<footer style='margin-top: 2.5em; padding: .7em 0; color: #777; border-top: #BBB solid 1px;'>Powered by SickGear.</footer></body>",
'html'))
except:
msg = MIMEText(ep_name + ': ' + lang)
msg['Subject'] = lang + ' Subtitle Downloaded: ' + ep_name
msg['From'] = sickbeard.EMAIL_FROM
msg['To'] = ','.join(to)
if self._sendmail(sickbeard.EMAIL_HOST, sickbeard.EMAIL_PORT, sickbeard.EMAIL_FROM, sickbeard.EMAIL_TLS,
sickbeard.EMAIL_USER, sickbeard.EMAIL_PASSWORD, to, msg):
logger.log('Download notification sent to [%s] for "%s"' % (to, ep_name), logger.DEBUG)
else:
logger.log('Download notification ERROR: %s' % self.last_err, logger.ERROR)
title = sickbeard.EMAIL_OLD_SUBJECTS and 'Subtitle Downloaded' or title
self._send_email(title, ep_name, '%s ' % lang, '</b></p>\n<p>Language: <b>%s' % lang)
def notify_git_update(self, new_version='??'):
pass
def _generate_recepients(self, show):
@staticmethod
def _generate_recipients(show):
addrs = []
# Grab the global recipients
for addr in sickbeard.EMAIL_LIST.split(','):
if (len(addr.strip()) > 0):
addrs.append(addr)
if sickbeard.EMAIL_LIST:
for addr in sickbeard.EMAIL_LIST.split(','):
if any(addr.strip()):
addrs.append(addr)
# Grab the recipients for the show
myDB = db.DBConnection()
for s in show:
for subs in myDB.select('SELECT notify_list FROM tv_shows WHERE show_name = ?', (s,)):
if subs['notify_list']:
for addr in subs['notify_list'].split(','):
if (len(addr.strip()) > 0):
addrs.append(addr)
if None is not show:
my_db = db.DBConnection()
for name in show:
for subs in my_db.select('SELECT notify_list FROM tv_shows WHERE show_name = ?', (name,)):
if subs['notify_list']:
for addr in subs['notify_list'].split(','):
if any(addr.strip()):
addrs.append(addr)
addrs = set(addrs)
logger.log('Notification recepients: %s' % addrs, logger.DEBUG)
logger.log(u'Email recipients to notify: %s' % addrs, logger.DEBUG)
return addrs
def _sendmail(self, host, port, smtp_from, use_tls, user, pwd, to, msg, smtpDebug=False):
logger.log('HOST: %s; PORT: %s; FROM: %s, TLS: %s, USER: %s, PWD: %s, TO: %s' % (
host, port, smtp_from, use_tls, user, pwd, to), logger.DEBUG)
def _sendmail(self, host, port, smtp_from, use_tls, user, pwd, to, msg, smtp_debug=False):
use_tls = 1 == sickbeard.helpers.tryInt(use_tls)
login = any(user) and any(pwd)
logger.log(u'Sendmail HOST: %s; PORT: %s; LOGIN: %s, TLS: %s, USER: %s, FROM: %s, TO: %s' % (
host, port, login, use_tls, user, smtp_from, to), logger.DEBUG)
try:
srv = smtplib.SMTP(host, int(port))
if smtpDebug:
if smtp_debug:
srv.set_debuglevel(1)
if (use_tls == '1' or use_tls == True) or (len(user) > 0 and len(pwd) > 0):
if use_tls or login:
srv.ehlo()
logger.log('Sent initial EHLO command!', logger.DEBUG)
if use_tls == '1' or use_tls == True:
srv.starttls()
logger.log('Sent STARTTLS command!', logger.DEBUG)
if len(user) > 0 and len(pwd) > 0:
srv.login(user, pwd)
logger.log('Sent LOGIN command!', logger.DEBUG)
logger.log(u'Sent initial EHLO command', logger.DEBUG)
if use_tls:
srv.starttls()
srv.ehlo()
logger.log(u'Sent STARTTLS and EHLO command', logger.DEBUG)
if login:
srv.login(user, pwd)
logger.log(u'Sent LOGIN command', logger.DEBUG)
srv.sendmail(smtp_from, to, msg.as_string())
srv.quit()
return True
except Exception as e:
self.last_err = '%s' % e
return False
def _parseEp(self, ep_name):
ep_name = ep_name.encode('utf-8', 'replace')
return True
@staticmethod
def _parse_ep(ep_name):
ep_name = ep_name.encode('utf-8', 'replace')
sep = ' - '
titles = ep_name.split(sep)
titles.sort(key=len, reverse=True)
logger.log('TITLES: %s' % titles, logger.DEBUG)
logger.log(u'TITLES: %s' % titles, logger.DEBUG)
return titles

View file

@ -937,16 +937,38 @@ class Home(MainHandler):
def loadShowNotifyLists(self, *args, **kwargs):
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
myDB = db.DBConnection()
rows = myDB.select('SELECT show_id, show_name, notify_list FROM tv_shows ORDER BY show_name ASC')
my_db = db.DBConnection()
rows = my_db.select('SELECT indexer_id, indexer, notify_list FROM tv_shows ' +
'WHERE notify_list NOTNULL and notify_list != ""')
notify_lists = {}
for r in filter(lambda x: x['notify_list'].strip(), rows):
notify_lists['%s_%s' % (r['indexer'], r['indexer_id'])] = r['notify_list']
data = {}
size = 0
for r in rows:
data[r['show_id']] = {'id': r['show_id'], 'name': r['show_name'], 'list': r['notify_list']}
size += 1
data['_size'] = size
return json.dumps(data)
sorted_show_lists = self.sorted_show_lists()
response = []
for current_group in sorted_show_lists:
data = []
for current_show in current_group[1]:
uid = '%s_%s' % (current_show.indexer, current_show.indexerid)
data.append({'id': uid, 'name': current_show.name,
'list': '' if uid not in notify_lists else notify_lists[uid]})
if data:
response.append({current_group[0]: data})
return json.dumps(response)
@staticmethod
def save_show_email(show=None, emails=None):
# self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
my_db = db.DBConnection()
success = False
parse = show.split('_')
if 1 < len(parse) and \
my_db.action('UPDATE tv_shows SET notify_list = ? WHERE indexer = ? AND indexer_id = ?',
[emails, parse[0], parse[1]]):
success = True
return json.dumps({'id': show, 'success': success})
def testEmail(self, host=None, port=None, smtp_from=None, use_tls=None, user=None, pwd=None, to=None):
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
@ -956,9 +978,8 @@ class Home(MainHandler):
host = config.clean_host(host)
if notifiers.email_notifier.test_notify(host, port, smtp_from, use_tls, user, pwd, to):
return 'Test email sent successfully! Check inbox.'
else:
return 'ERROR: %s' % notifiers.email_notifier.last_err
return 'Success. Test email sent. Check inbox.'
return 'ERROR: %s' % notifiers.email_notifier.last_err
def testNMA(self, nma_api=None, nma_priority=0):
self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
@ -1217,32 +1238,11 @@ class Home(MainHandler):
display_seasons += [1]
display_seasons += [highest_season]
def titler(x):
return (remove_article(x), x)[not x or sickbeard.SORT_ARTICLE]
if sickbeard.SHOWLIST_TAGVIEW == 'custom':
t.sortedShowLists = []
for tag in sickbeard.SHOW_TAGS:
results = filter(lambda x: x.tag == tag, sickbeard.showList)
if results:
t.sortedShowLists.append([tag, sorted(results, lambda x, y: cmp(titler(x.name), titler(y.name)))])
elif sickbeard.SHOWLIST_TAGVIEW == 'anime':
shows = []
anime = []
for show in sickbeard.showList:
if show.is_anime:
anime.append(show)
else:
shows.append(show)
t.sortedShowLists = [['Shows', sorted(shows, lambda x, y: cmp(titler(x.name), titler(y.name)))],
['Anime', sorted(anime, lambda x, y: cmp(titler(x.name), titler(y.name)))]]
else:
t.sortedShowLists = [
['Show List', sorted(sickbeard.showList, lambda x, y: cmp(titler(x.name), titler(y.name)))]]
t.sortedShowLists = self.sorted_show_lists()
tvshows = []
tvshow_names = []
cur_sel = None
for tvshow_types in t.sortedShowLists:
for tvshow in tvshow_types[1]:
tvshows.append(tvshow.indexerid)
@ -1252,8 +1252,11 @@ class Home(MainHandler):
t.tvshow_id_csv = ','.join(str(x) for x in tvshows)
last_item = len(tvshow_names)
t.prev_title = 'Prev show, %s' % tvshow_names[(cur_sel - 2, last_item - 1)[1 == cur_sel]]
t.next_title = 'Next show, %s' % tvshow_names[(cur_sel, 0)[last_item == cur_sel]]
t.prev_title = ''
t.next_title = ''
if cur_sel:
t.prev_title = 'Prev show, %s' % tvshow_names[(cur_sel - 2, last_item - 1)[1 == cur_sel]]
t.next_title = 'Next show, %s' % tvshow_names[(cur_sel, 0)[last_item == cur_sel]]
t.bwl = None
if showObj.is_anime:
@ -1276,6 +1279,35 @@ class Home(MainHandler):
return t.respond()
@staticmethod
def sorted_show_lists():
def titler(x):
return (remove_article(x), x)[not x or sickbeard.SORT_ARTICLE]
if 'custom' == sickbeard.SHOWLIST_TAGVIEW:
sorted_show_lists = []
for tag in sickbeard.SHOW_TAGS:
results = filter(lambda x: x.tag == tag, sickbeard.showList)
if results:
sorted_show_lists.append([tag, sorted(results, lambda x, y: cmp(titler(x.name), titler(y.name)))])
elif 'anime' == sickbeard.SHOWLIST_TAGVIEW:
shows = []
anime = []
for show in sickbeard.showList:
if show.is_anime:
anime.append(show)
else:
shows.append(show)
sorted_show_lists = [['Shows', sorted(shows, lambda x, y: cmp(titler(x.name), titler(y.name)))],
['Anime', sorted(anime, lambda x, y: cmp(titler(x.name), titler(y.name)))]]
else:
sorted_show_lists = [
['Show List', sorted(sickbeard.showList, lambda x, y: cmp(titler(x.name), titler(y.name)))]]
return sorted_show_lists
def plotDetails(self, show, season, episode):
myDB = db.DBConnection()
result = myDB.select(