Browse Source

Merge pull request #629 from JackDandy/feature/ChangeEmailNotif

Change refactor email notifier.
pull/632/head
JackDandy 9 years ago
parent
commit
38db11d3fe
  1. 1
      CHANGES.md
  2. 2
      gui/slick/css/dark.css
  3. 2
      gui/slick/css/light.css
  4. 92
      gui/slick/interfaces/default/config_notifications.tmpl
  5. 405
      gui/slick/js/configNotifications.js
  6. 6
      sickbeard/__init__.py
  7. 207
      sickbeard/notifiers/emailnotify.py
  8. 100
      sickbeard/webserve.py

1
CHANGES.md

@ -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)

2
gui/slick/css/dark.css

@ -1178,6 +1178,7 @@ pre{
input sizing (for config pages)
========================================================================== */
.showlist-select optgroup,
#pickShow optgroup,
#showfilter optgroup,
#editAProvider optgroup{
@ -1185,6 +1186,7 @@ input sizing (for config pages)
background-color:rgb(51, 51, 51)
}
.showlist-select optgroup option,
#pickShow optgroup option,
#showfilter optgroup option,
#editAProvider optgroup option{

2
gui/slick/css/light.css

@ -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{

92
gui/slick/interfaces/default/config_notifications.tmpl

@ -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">
@ -1679,94 +1679,70 @@
</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>
<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 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>
<div class="field-pair">
<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>
</div>
<div class="field-pair">
<label for="email_user">
<span class="component-title">SMTP user</span>
<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>
<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>
<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" />
</label>
<label>
<span class="component-title">&nbsp;</span>
<span class="component-desc">(optional) your SMTP server password.</span>
<p>(optional)</p>
</span>
</label>
</div>
<div class="field-pair">
<label for="email_list">
<span class="component-title">Global email list</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" />
</label>
<label>
<span class="component-title">&nbsp;</span>
<span class="component-desc">all emails here receive notifications for <b>all</b> shows.</span>
<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 //-->

405
gui/slick/js/configNotifications.js

@ -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());
@ -225,30 +235,6 @@
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()) {
alert('Please fill in the Popcorn IP address');
@ -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,187 +629,101 @@
});
});
$('#testEmail').click(function () {
var status, host, port, tls, from, user, pwd, err, to;
status = $('#testEmail-result');
status.html(loading);
host = $('#email_host').val();
host = host.length > 0 ? host : null;
port = $('#email_port').val();
port = port.length > 0 ? port : null;
tls = $('#email_tls').attr('checked') !== undefined ? 1 : 0;
from = $('#email_from').val();
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>';
}
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 (err.length > 0) {
err = '<ol>' + err + '</ol>';
status.html(err);
} 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>');
} 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); });
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>');
}
});
$('#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);
});
});
if (len) {
el = $('#email_show');
el.html('<option value="-1">-- Select show --</option>'
+ html.join('\n'));
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);
}
}
});
$('#show_email_list').val('');
$('#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.');
el.change(function () {
$('#show_email_list').val(
$(this).find('option[value="' + $(this).val() + '"]').attr('data'))
});
}
$('#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 : '');
});
// 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(); });
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('');
});
$('#save_show_email').click(
function(){
var show = $('#email_show').val();
if ('-1' == show) {
$('#testEmail-result').html('No show selected for save.');
return
}
// Load the per show notify lists everytime this page is loaded
$.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);
});
});
// show instructions for plex when enabled
$('#use_plex').click(function() {
if ( $(this).is(':checked') ) {
$('.plexinfo').removeClass('hide');
$('#testEmail').click(function () {
var status, host, port, tls, from, user, pwd, err, to;
status = $('#testEmail-result');
status.html(loading);
host = $('#email_host').val();
host = host.length > 0 ? host : null;
port = $('#email_port').val();
port = port.length > 0 ? port : null;
tls = $('#email_tls').attr('checked') !== undefined ? 1 : 0;
from = $('#email_from').val();
from = from.length > 0 ? from : 'root@localhost';
user = $('#email_user').val().trim();
pwd = $('#email_password').val();
err = [];
if (null == host) {
err.push('SMTP server hostname');
}
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 (0 < err.length) {
status.html('Required: ' + err.join(', '));
} else {
$('.plexinfo').addClass('hide');
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);});
}
}
});
if ($('input[id="use_plex"]').is(':checked')) {$('.plexinfo').removeClass('hide')}
});

6
sickbeard/__init__.py

@ -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)

207
sickbeard/notifiers/emailnotify.py

@ -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:'):
"""
Send a notification that an episode was snatched
def _send_email(self, title, ep_name, lang='', extra='', force=False):
if not sickbeard.USE_EMAIL and not force:
return
ep_name: The name of the episode that was snatched
title: The title of the notification (optional)
"""
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
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>",
'<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'] = 'Snatched: ' + ep_name
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('Snatch notification sent to [%s] for "%s"' % (to, ep_name), logger.DEBUG)
logger.log(u'%s notification sent to [%s] for "%s"' % (title, to, ep_name), logger.DEBUG)
else:
logger.log('Snatch notification ERROR: %s' % self.last_err, logger.ERROR)
logger.log(u'%s notification ERROR: %s' % (title, self.last_err), logger.ERROR)
def notify_download(self, ep_name, title='Completed:'):
def notify_snatch(self, ep_name, title=notifyStrings[NOTIFY_SNATCH]):
"""
Send a notification that an episode was snatched
:param ep_name: The name of the episode that was snatched
:param title: The title of the notification (optional)
"""
if sickbeard.EMAIL_NOTIFY_ONSNATCH:
title = sickbeard.EMAIL_OLD_SUBJECTS and 'Snatched' or title
self._send_email(title, ep_name)
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)
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)
title = sickbeard.EMAIL_OLD_SUBJECTS and 'Downloaded' or title
self._send_email(title, ep_name)
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
if sickbeard.EMAIL_LIST:
for addr in sickbeard.EMAIL_LIST.split(','):
if (len(addr.strip()) > 0):
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 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 (len(addr.strip()) > 0):
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:
logger.log(u'Sent initial EHLO command', logger.DEBUG)
if use_tls:
srv.starttls()
logger.log('Sent STARTTLS command!', logger.DEBUG)
if len(user) > 0 and len(pwd) > 0:
srv.ehlo()
logger.log(u'Sent STARTTLS and EHLO command', logger.DEBUG)
if login:
srv.login(user, pwd)
logger.log('Sent LOGIN command!', logger.DEBUG)
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

100
sickbeard/webserve.py

@ -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']
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')
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)
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,8 +978,7 @@ 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 'Success. Test email sent. Check inbox.'
return 'ERROR: %s' % notifiers.email_notifier.last_err
def testNMA(self, nma_api=None, nma_priority=0):
@ -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,6 +1252,9 @@ class Home(MainHandler):
t.tvshow_id_csv = ','.join(str(x) for x in tvshows)
last_item = len(tvshow_names)
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]]
@ -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(

Loading…
Cancel
Save