diff --git a/gui/slick/css/dark.css b/gui/slick/css/dark.css
index d2189bcdf49877d145658397429240e438aa65ad..2310d85bb4c384222613e33fc38b7484648cc4e2 100644
--- a/gui/slick/css/dark.css
+++ b/gui/slick/css/dark.css
@@ -265,7 +265,7 @@ home.mako
     color: #fff;
 }
 
-.show {
+.show-container {
     margin: 12px;
     width: 188px;
     height: 352px;
diff --git a/gui/slick/css/style.css b/gui/slick/css/style.css
index 119a4f9e7371b2df5098d29f3be9c7b34a144516..dd7d31b8f57060db1a9899d235fe0527e1ced587 100644
--- a/gui/slick/css/style.css
+++ b/gui/slick/css/style.css
@@ -627,7 +627,7 @@ home.mako
     margin: 0 auto;
 }
 
-.show {
+.show-container {
     margin: 12px;
     width: 188px;
     height: 352px;
@@ -644,28 +644,28 @@ home.mako
     border-top-right-radius: 5px;
 }
 
-.show .ui-progressbar {
+.show-container .ui-progressbar {
     height: 7px !important;
     top: -2px;
 }
 
-.show .ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br {
+.show-container .ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br {
     border-bottom-right-radius: 0px;
 }
 
-.show .ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl {
+.show-container .ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl {
     border-bottom-left-radius: 0px;
 }
 
-.show .ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr {
+.show-container .ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr {
     border-top-right-radius: 0px;
 }
 
-.show .ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl {
+.show-container .ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl {
     border-top-left-radius: 0px;
 }
 
-.show .ui-widget-content {
+.show-container .ui-widget-content {
     border-top: 1px solid #111;
     border-bottom: 1px solid #111;
     border-left: 0px;
@@ -677,10 +677,10 @@ home.mako
 .ui-progressbar .progress-20 {
     border: none;
 }
-.show .progress-20,
-.show .progress-40,
-.show .progress-60,
-.show .progress-80 {
+.show-container .progress-20,
+.show-container .progress-40,
+.show-container .progress-60,
+.show-container .progress-80 {
     border-radius: 0px;
     height: 7px
 }
diff --git a/gui/slick/images/network/fx (us).png b/gui/slick/images/network/fx (us).png
new file mode 100644
index 0000000000000000000000000000000000000000..62f98632041d66906180e5f1cddab1bda6bd0ee1
Binary files /dev/null and b/gui/slick/images/network/fx (us).png differ
diff --git a/gui/slick/images/providers/elitetorrent.png b/gui/slick/images/providers/elitetorrent.png
new file mode 100644
index 0000000000000000000000000000000000000000..bc84adbfed862f969ba72eae59d659f4d9a7f6b6
Binary files /dev/null and b/gui/slick/images/providers/elitetorrent.png differ
diff --git a/gui/slick/js/core.js b/gui/slick/js/core.js
index d36035d44fc9e44f6ff1d9e2fe609a160ed29885..496c0cc2f8e822e0d653bc55d824d08706a5ba37 100644
--- a/gui/slick/js/core.js
+++ b/gui/slick/js/core.js
@@ -19,6 +19,7 @@ var configSuccess = function(){
         window.location.href = srRoot + '/config/providers/';
     });
     $('#email_show').trigger('notify');
+    $('#prowl_show').trigger('notify');
 };
 
 var SICKRAGE = {
@@ -276,106 +277,8 @@ var SICKRAGE = {
             $('#backupDir').fileBrowser({ title: 'Select backup folder to save to', key: 'backupPath' });
             $('#backupFile').fileBrowser({ title: 'Select backup files to restore', key: 'backupFile', includeFiles: 1 });
             $('#config-components').tabs();
-
-            $(".enabler").each(function(){
-                if (!$(this).prop('checked')) { $('#content_'+$(this).attr('id')).hide(); }
-            });
-
-            $(".enabler").on('click', function() {
-                if ($(this).prop('checked')){
-                    $('#content_'+$(this).attr('id')).fadeIn("fast", "linear");
-                } else {
-                    $('#content_'+$(this).attr('id')).fadeOut("fast", "linear");
-                }
-            });
-
-            $(".viewIf").on('click', function() {
-                if ($(this).prop('checked')) {
-                    $('.hide_if_'+$(this).attr('id')).css('display','none');
-                    $('.show_if_'+$(this).attr('id')).fadeIn("fast", "linear");
-                } else {
-                    $('.show_if_'+$(this).attr('id')).css('display','none');
-                    $('.hide_if_'+$(this).attr('id')).fadeIn("fast", "linear");
-                }
-            });
-
-            $(".datePresets").on('click', function() {
-                var def = $('#date_presets').val();
-                if ($(this).prop('checked') && '%x' == def) { // jshint ignore:line
-                    def = '%a, %b %d, %Y';
-                    $('#date_use_system_default').html('1');
-                } else if (!$(this).prop('checked') && '1' == $('#date_use_system_default').html()){ // jshint ignore:line
-                    def = '%x';
-                }
-
-                $('#date_presets').attr('name', 'date_preset_old');
-                $('#date_presets').attr('id', 'date_presets_old');
-
-                $('#date_presets_na').attr('name', 'date_preset');
-                $('#date_presets_na').attr('id', 'date_presets');
-
-                $('#date_presets_old').attr('name', 'date_preset_na');
-                $('#date_presets_old').attr('id', 'date_presets_na');
-
-                if (def) { $('#date_presets').val(def); }
-            });
-
-            // bind 'myForm' and provide a simple callback function
-            $('#configForm').ajaxForm({
-                beforeSubmit: function(){
-                    $('.config_submitter .config_submitter_refresh').each(function(){
-                        $(this).attr("disabled", "disabled");
-                        $(this).after('<span><img src="' + srRoot + '/images/loading16' + themeSpinner + '.gif"> Saving...</span>');
-                        $(this).hide();
-                    });
-                },
-                success: function(){
-                    setTimeout(function () {
-                        "use strict";
-                        configSuccess();
-                    }, 2000);
-                }
-            });
-
-            $('#api_key').on('click', function(){
-                $('#api_key').select();
-            });
-
-            $("#generate_new_apikey").on('click', function(){
-                $.get(srRoot + '/config/general/generateApiKey', function(data){
-                    if (data.error !== undefined) {
-                        alert(data.error);
-                        return;
-                    }
-                    $('#api_key').val(data);
-                });
-            });
-
-            $('#branchCheckout').on('click', function() {
-                var url = srRoot+'/home/branchCheckout?branch='+$("#branchVersion").val();
-                var checkDBversion = srRoot + "/home/getDBcompare";
-                $.getJSON(checkDBversion, function(data){
-                    if (data.status.toLowerCase() === "success") {
-                        if (data.message.toLowerCase() === "equal") {
-                            //Checkout Branch
-                            window.location.href = url;
-                        }
-                        if (data.message.toLowerCase() === "upgrade") {
-                            if ( confirm("Changing branch will upgrade your database.\nYou won't be able to downgrade afterward.\nDo you want to continue?") ) {
-                                //Checkout Branch
-                                window.location.href = url;
-                            }
-                        }
-                        if (data.message.toLowerCase() === "downgrade") {
-                            alert("Can't switch branch as this will result in a database downgrade.");
-                        }
-                    }
-                });
-            });
         },
         notifications: function() {
-            $('#config-components').tabs();
-
             $('#testGrowl').on('click', function () {
                 var growl = {};
                 growl.host = $.trim($('#growl_host').val());
@@ -388,7 +291,10 @@ var SICKRAGE = {
                 $('#growl_host').removeClass('warning');
                 $(this).prop('disabled', true);
                 $('#testGrowl-result').html(loading);
-                $.get(srRoot + '/home/testGrowl', {'host': growl.host, 'password': growl.password}).done(function (data) {
+                $.get(srRoot + '/home/testGrowl', {
+                    'host': growl.host,
+                    'password': growl.password
+                }).done(function (data) {
                     $('#testGrowl-result').html(data);
                     $('#testGrowl').prop('disabled', false);
                 });
@@ -406,7 +312,10 @@ var SICKRAGE = {
                 $('#prowl_api').removeClass('warning');
                 $(this).prop('disabled', true);
                 $('#testProwl-result').html(loading);
-                $.get(srRoot + '/home/testProwl', {'prowl_api': prowl.api, 'prowl_priority': prowl.priority}).done(function (data) {
+                $.get(srRoot + '/home/testProwl', {
+                    'prowl_api': prowl.api,
+                    'prowl_priority': prowl.priority
+                }).done(function (data) {
                     $('#testProwl-result').html(data);
                     $('#testProwl').prop('disabled', false);
                 });
@@ -425,7 +334,11 @@ var SICKRAGE = {
                 $('#kodi_host').removeClass('warning');
                 $(this).prop('disabled', true);
                 $('#testKODI-result').html(loading);
-                $.get(srRoot + '/home/testKODI', {'host': kodi.host, 'username': kodi.username, 'password': kodi.password}).done(function (data) {
+                $.get(srRoot + '/home/testKODI', {
+                    'host': kodi.host,
+                    'username': kodi.username,
+                    'password': kodi.password
+                }).done(function (data) {
                     $('#testKODI-result').html(data);
                     $('#testKODI').prop('disabled', false);
                 });
@@ -445,7 +358,11 @@ var SICKRAGE = {
                 $('#plex_host').removeClass('warning');
                 $(this).prop('disabled', true);
                 $('#testPMC-result').html(loading);
-                $.get(srRoot + '/home/testPMC', {'host': plex.client.host, 'username': plex.client.username, 'password': plex.client.password}).done(function (data) {
+                $.get(srRoot + '/home/testPMC', {
+                    'host': plex.client.host,
+                    'username': plex.client.username,
+                    'password': plex.client.password
+                }).done(function (data) {
                     $('#testPMC-result').html(data);
                     $('#testPMC').prop('disabled', false);
                 });
@@ -466,7 +383,12 @@ var SICKRAGE = {
                 $('#plex_server_host').removeClass('warning');
                 $(this).prop('disabled', true);
                 $('#testPMS-result').html(loading);
-                $.get(srRoot + '/home/testPMS', {'host': plex.server.host, 'username': plex.username, 'password': plex.password, 'plex_server_token': plex.server.token}).done(function (data) {
+                $.get(srRoot + '/home/testPMS', {
+                    'host': plex.server.host,
+                    'username': plex.username,
+                    'password': plex.password,
+                    'plex_server_token': plex.server.token
+                }).done(function (data) {
                     $('#testPMS-result').html(data);
                     $('#testPMS').prop('disabled', false);
                 });
@@ -493,7 +415,10 @@ var SICKRAGE = {
                 $('#emby_host,#emby_apikey').removeClass('warning');
                 $(this).prop('disabled', true);
                 $('#testEMBY-result').html(loading);
-                $.get(srRoot + '/home/testEMBY', {'host': emby.host, 'emby_apikey': emby.apikey}).done(function (data) {
+                $.get(srRoot + '/home/testEMBY', {
+                    'host': emby.host,
+                    'emby_apikey': emby.apikey
+                }).done(function (data) {
                     $('#testEMBY-result').html(data);
                     $('#testEMBY').prop('disabled', false);
                 });
@@ -510,7 +435,9 @@ var SICKRAGE = {
                 $('#boxcar2_accesstoken').removeClass('warning');
                 $(this).prop('disabled', true);
                 $('#testBoxcar2-result').html(loading);
-                $.get(srRoot + '/home/testBoxcar2', {'accesstoken': boxcar2.accesstoken}).done(function (data) {
+                $.get(srRoot + '/home/testBoxcar2', {
+                    'accesstoken': boxcar2.accesstoken
+                }).done(function (data) {
                     $('#testBoxcar2-result').html(data);
                     $('#testBoxcar2').prop('disabled', false);
                 });
@@ -537,7 +464,10 @@ var SICKRAGE = {
                 $('#pushover_userkey,#pushover_apikey').removeClass('warning');
                 $(this).prop('disabled', true);
                 $('#testPushover-result').html(loading);
-                $.get(srRoot + '/home/testPushover', {'userKey': pushover.userkey, 'apiKey': pushover.apikey}).done(function (data) {
+                $.get(srRoot + '/home/testPushover', {
+                    'userKey': pushover.userkey,
+                    'apiKey': pushover.apikey
+                }).done(function (data) {
                     $('#testPushover-result').html(data);
                     $('#testPushover').prop('disabled', false);
                 });
@@ -569,7 +499,9 @@ var SICKRAGE = {
                 }
                 $('#twitter_key').removeClass('warning');
                 $('#testTwitter-result').html(loading);
-                $.get(srRoot + '/home/twitterStep2', {'key': twitter.key}, function(data) {
+                $.get(srRoot + '/home/twitterStep2', {
+                    'key': twitter.key
+                }, function(data) {
                     $('#testTwitter-result').html(data);
                 });
             });
@@ -626,7 +558,11 @@ var SICKRAGE = {
                 $('#nmj_host').removeClass('warning');
                 $(this).prop('disabled', true);
                 $('#testNMJ-result').html(loading);
-                $.get(srRoot + '/home/testNMJ', {'host': nmj.host, 'database': nmj.database, 'mount': nmj.mount}).done(function (data) {
+                $.get(srRoot + '/home/testNMJ', {
+                    'host': nmj.host,
+                    'database': nmj.database,
+                    'mount': nmj.mount
+                }).done(function (data) {
                     $('#testNMJ-result').html(data);
                     $('#testNMJ').prop('disabled', false);
                 });
@@ -651,7 +587,11 @@ var SICKRAGE = {
                 }
 
                 nmjv2.dbinstance=$('#NMJv2db_instance').val();
-                $.get(srRoot + '/home/settingsNMJv2', {'host': nmjv2.host,'dbloc': nmjv2.dbloc,'instance': nmjv2.dbinstance}, function (data){
+                $.get(srRoot + '/home/settingsNMJv2', {
+                    'host': nmjv2.host,
+                    'dbloc': nmjv2.dbloc,
+                    'instance': nmjv2.dbinstance
+                }, function (data){
                     if (data === null) {
                         $('#nmjv2_database').removeAttr('readonly');
                     }
@@ -678,7 +618,9 @@ var SICKRAGE = {
                 $('#nmjv2_host').removeClass('warning');
                 $(this).prop('disabled', true);
                 $('#testNMJv2-result').html(loading);
-                $.get(srRoot + '/home/testNMJv2', {'host': nmjv2.host}) .done(function (data) {
+                $.get(srRoot + '/home/testNMJv2', {
+                    'host': nmjv2.host
+                }) .done(function (data) {
                     $('#testNMJv2-result').html(data);
                     $('#testNMJv2').prop('disabled', false);
                 });
@@ -705,24 +647,22 @@ var SICKRAGE = {
                 $('#freemobile_id,#freemobile_apikey').removeClass('warning');
                 $(this).prop('disabled', true);
                 $('#testFreeMobile-result').html(loading);
-                $.get(srRoot + '/home/testFreeMobile', {'freemobile_id': freemobile.id, 'freemobile_apikey': freemobile.apikey}).done(function (data) {
+                $.get(srRoot + '/home/testFreeMobile', {
+                    'freemobile_id': freemobile.id,
+                    'freemobile_apikey': freemobile.apikey
+                }).done(function (data) {
                     $('#testFreeMobile-result').html(data);
                     $('#testFreeMobile').prop('disabled', false);
                 });
             });
 
             $('#TraktGetPin').on('click', function () {
-                var trakt = {};
-                trakt.pinUrl = $('#trakt_pin_url').val();
-                window.open(trakt.pinUrl, "popUp", "toolbar=no, scrollbars=no, resizable=no, top=200, left=200, width=650, height=550");
+                window.open($('#trakt_pin_url').val(), "popUp", "toolbar=no, scrollbars=no, resizable=no, top=200, left=200, width=650, height=550");
                 $('#trakt_pin').removeClass('hide');
             });
 
             $('#trakt_pin').on('keyup change', function(){
-                var trakt = {};
-                trakt.pin = $('#trakt_pin').val();
-
-                if (trakt.pin.length !== 0) {
+                if ($('#trakt_pin').val().length !== 0) {
                     $('#TraktGetPin').addClass('hide');
                     $('#authTrakt').removeClass('hide');
                 } else {
@@ -735,7 +675,9 @@ var SICKRAGE = {
                 var trakt = {};
                 trakt.pin = $('#trakt_pin').val();
                 if (trakt.pin.length !== 0) {
-                    $.get(srRoot + '/home/getTraktToken', { "trakt_pin": trakt.pin }).done(function (data) {
+                    $.get(srRoot + '/home/getTraktToken', {
+                        'trakt_pin': trakt.pin
+                    }).done(function (data) {
                         $('#testTrakt-result').html(data);
                         $('#authTrakt').addClass('hide');
                         $('#trakt_pin').addClass('hide');
@@ -767,7 +709,10 @@ var SICKRAGE = {
                 $('#trakt_blacklist_name').removeClass('warning');
                 $(this).prop('disabled', true);
                 $('#testTrakt-result').html(loading);
-                $.get(srRoot + '/home/testTrakt', {'username': trakt.username, 'blacklist_name': trakt.trendingBlacklist}).done(function (data) {
+                $.get(srRoot + '/home/testTrakt', {
+                    'username': trakt.username,
+                    'blacklist_name': trakt.trendingBlacklist
+                }).done(function (data) {
                     $('#testTrakt-result').html(data);
                     $('#testTrakt').prop('disabled', false);
                 });
@@ -830,7 +775,10 @@ var SICKRAGE = {
                 $('#nma_api').removeClass('warning');
                 $(this).prop('disabled', true);
                 $('#testNMA-result').html(loading);
-                $.get(srRoot + '/home/testNMA', {'nma_api': nma.api, 'nma_priority': nma.priority}).done(function (data) {
+                $.get(srRoot + '/home/testNMA', {
+                    'nma_api': nma.api,
+                    'nma_priority': nma.priority
+                }).done(function (data) {
                     $('#testNMA-result').html(data);
                     $('#testNMA').prop('disabled', false);
                 });
@@ -847,7 +795,9 @@ var SICKRAGE = {
                 $('#pushalot_authorizationtoken').removeClass('warning');
                 $(this).prop('disabled', true);
                 $('#testPushalot-result').html(loading);
-                $.get(srRoot + '/home/testPushalot', {'authorizationToken': pushalot.authToken}).done(function (data) {
+                $.get(srRoot + '/home/testPushalot', {
+                    'authorizationToken': pushalot.authToken
+                }).done(function (data) {
                     $('#testPushalot-result').html(data);
                     $('#testPushalot').prop('disabled', false);
                 });
@@ -864,7 +814,9 @@ var SICKRAGE = {
                 $('#pushbullet_api').removeClass('warning');
                 $(this).prop('disabled', true);
                 $('#testPushbullet-result').html(loading);
-                $.get(srRoot + '/home/testPushbullet', {'api': pushbullet.api}).done(function (data) {
+                $.get(srRoot + '/home/testPushbullet', {
+                    'api': pushbullet.api
+                }).done(function (data) {
                     $('#testPushbullet-result').html(data);
                     $('#testPushbullet').prop('disabled', false);
                 });
@@ -884,8 +836,10 @@ var SICKRAGE = {
                     return false;
                 }
 
-                $.get(srRoot + "/home/getPushbulletDevices", {'api': pushbullet.api}, function (data) {
-                    pushbullet.devices = jQuery.parseJSON(data).devices;
+                $.get(srRoot + "/home/getPushbulletDevices", {
+                    'api': pushbullet.api
+                }, function (data) {
+                    pushbullet.devices = $.parseJSON(data).devices;
                     pushbullet.currentDevice = $("#pushbullet_device").val();
                     $("#pushbullet_device_list").html('');
                     for (var i = 0, len = pushbullet.devices.length; i < len; i++) {
@@ -897,14 +851,8 @@ var SICKRAGE = {
                             }
                         }
                     }
-                    if (pushbullet.currentDevice === '') {
-                        $("#pushbullet_device_list").prepend('<option value="" selected>All devices</option>');
-                    } else {
-                        $("#pushbullet_device_list").prepend('<option value="">All devices</option>');
-                    }
-                    if(msg) {
-                        $('#testPushbullet-result').html(msg);
-                    }
+                    $("#pushbullet_device_list").prepend('<option value="" '+ (pushbullet.currentDevice === '' ? 'selected' : '') + '>All devices</option>');
+                    if(msg) { $('#testPushbullet-result').html(msg); }
                 });
 
                 $("#pushbullet_device_list").on('change', function(){
@@ -925,11 +873,18 @@ var SICKRAGE = {
                 var key = parseInt($('#email_show').val(), 10);
                 $('#email_show_list').val(key >= 0 ? notify_data[key.toString()].list : ''); // jshint ignore:line
             });
+            $('#prowl_show').on('change', function() {
+                var key = parseInt($('#prowl_show').val(), 10);
+                $('#prowl_show_list').val(key >= 0 ? notify_data[key.toString()].prowl_notify_list  : ''); // jshint ignore:line
+            });
 
             // Update the internal data struct anytime settings are saved to the server
             $('#email_show').on('notify', function() {
                 loadShowNotifyLists();
             });
+            $('#prowl_show').on('notify', function() {
+                loadShowNotifyLists();
+            });
 
             function loadShowNotifyLists() {
                 $.get(srRoot + "/home/loadShowNotifyLists", function(data) {
@@ -937,14 +892,30 @@ var SICKRAGE = {
                     list = $.parseJSON(data); // @TODO The line below this is the same as the $('#email_show') function above
                     notify_data = list; // jshint ignore:line
                     if (list._size === 0) { return; }
-                    html = '<option value="-1">-- Select --</option>';
+
+                    // Convert the 'list' object to a js array of objects so that we can sort it
+                    var _list = [];
                     for (s in list) {
                         if (s.charAt(0) !== '_') {
-                            html += '<option value="' + list[s].id + '">' + $('<div/>').text(list[s].name).html() + '</option>';
+                            _list.push(list[s]);
+                        }
+                    }
+                    var sortedList = _list.sort(function(a,b) {
+                        if (a.name < b.name) { return -1; }
+                        if (a.name > b.name) { return 1;  }
+                        return 0;
+                    });
+                    html = '<option value="-1">-- Select --</option>';
+                    for (s in sortedList) {
+                        if (sortedList[s].id && sortedList[s].name) {
+                            html += '<option value="' + sortedList[s].id + '">' + $('<div/>').text(sortedList[s].name).html() + '</option>';
                         }
                     }
                     $('#email_show').html(html);
                     $('#email_show_list').val('');
+
+                    $('#prowl_show').html(html);
+                    $('#prowl_show_list').val('');
                 });
             }
             // Load the per show notify lists everytime this page is loaded
@@ -959,6 +930,15 @@ var SICKRAGE = {
                     loadShowNotifyLists();
                 });
             });
+            $('#prowl_show_save').on('click', function() {
+                $.post(srRoot + "/home/saveShowNotifyList", {
+                    'show': $('#prowl_show').val(),
+                    'prowlAPIs': $('#prowl_show_list').val()
+                }, function() {
+                    // Reload the per show notify lists to reflect changes
+                    loadShowNotifyLists();
+                });
+            });
 
             // show instructions for plex when enabled
             $('#use_plex').on('click', function() {
@@ -1106,7 +1086,7 @@ var SICKRAGE = {
 
                 $.get(srRoot + '/config/postProcessing/testNaming', {
                     'pattern': pattern,
-                    'sports': 'True'
+                    'sports': 'True' // @TODO does this actually need to be a string or can it be a boolean?
                 }, function (data) {
                     if (data) {
                         $('#naming_sports_example').text(data + '.ext');
@@ -1118,7 +1098,7 @@ var SICKRAGE = {
 
                 $.get(srRoot + '/config/postProcessing/isNamingValid', {
                     'pattern': pattern,
-                    'sports': 'True'
+                    'sports': 'True' // @TODO does this actually need to be a string or can it be a boolean?
                 }, function (data) {
                     if (data === "invalid") {
                         $('#naming_sports_pattern').qtip('option', {
@@ -1206,6 +1186,8 @@ var SICKRAGE = {
                 });
             }
 
+            // @TODO all of these setup funcitons should be able to be rolled into a generic jQuery function
+
             function setupNaming() {
                 // if it is a custom selection then show the text box
                 if ($('#name_presets :selected').val().toLowerCase() === "custom...") {
@@ -1258,6 +1240,9 @@ var SICKRAGE = {
                 }
             });
 
+            // @TODO all of these on change funcitons should be able to be rolled into a generic jQuery function or maybe we could
+            //       move all of the setup functions into these handlers?
+
             $('#name_presets').on('change', function(){
                 setupNaming();
             });
@@ -1290,6 +1275,9 @@ var SICKRAGE = {
                 setupAnimeNaming();
             });
 
+            // @TODO We might be able to change these from typewatch to _ debounce like we've done on the log page
+            //       The main reason for doing this would be to use only open source stuff that's still being maintained
+
             $('#naming_multi_ep').on('change', fillExamples);
             $('#naming_pattern').on('focusout', fillExamples);
             $('#naming_pattern').on('keyup', function() {
@@ -1344,6 +1332,8 @@ var SICKRAGE = {
                 $('#naming_custom').show();
                 $('#naming_pattern').focus();
             });
+
+            // @TODO We should see if these can be added with the on click or if we need to even call them on load
             setupNaming();
             setupAbdNaming();
             setupSportsNaming();
@@ -1739,6 +1729,9 @@ var SICKRAGE = {
                 placeholder: 'ui-state-highlight',
                 update: function() {
                     $(this).refreshServiceList();
+                },
+                create: function() {
+                    $(this).refreshServiceList();
                 }
             });
 
@@ -1759,6 +1752,16 @@ var SICKRAGE = {
                 $('table').trigger('filterReset');
             });
 
+            // Handle filtering in the poster layout
+            $('#filterShowName').on('input', _.debounce(function (e) {
+                $('#container, #container-anime').isotope({
+                    filter: function () {
+                      var name = $('div.show-title', this).text();
+                      return (name.toLowerCase().indexOf(e.target.value.toLowerCase()) !== -1);
+                    }
+                });
+            }, 500));
+
             // This needs to be refined to work a little faster.
             $('.progressbar').each(function(){
                 var percentage = $(this).data('progress-percentage');
@@ -1786,112 +1789,84 @@ var SICKRAGE = {
                     6: function(node) { return $(node).find("img").attr("alt"); }
                 },
                 widgets: ['saveSort', 'zebra', 'stickyHeaders', 'filter', 'columnSelector'],
-                headers: (function(){
-                    if(metaToBool('sickbeard.FILTER_ROW')){
-                        return {
-                            0: { sorter: 'realISODate' },
-                            1: { sorter: 'realISODate' },
-                            2: { sorter: 'loadingNames' },
-                            4: { sorter: 'quality' },
-                            5: { sorter: 'eps' },
-                            6: { filter : 'parsed' }
-                        };
-                    } else {
-                        return {
-                            0: { sorter: 'realISODate' },
-                            1: { sorter: 'realISODate' },
-                            2: { sorter: 'loadingNames' },
-                            4: { sorter: 'quality' },
-                            5: { sorter: 'eps' }
-                        };
-                    }
-                }()),
-                widgetOptions: (function(){
-                    if(metaToBool('sickbeard.FILTER_ROW')){
-                        return {
-                            filter_columnFilters: true, // jshint ignore:line
-                            filter_hideFilters : true, // jshint ignore:line
-                            filter_saveFilters : true, // jshint ignore:line
-                            filter_functions : { // jshint ignore:line
-                                5:function(e, n, f) {
-                                    var test = false;
-                                    var pct = Math.floor((n % 1) * 1000);
-                                    if (f === '') {
-                                        test = true;
-                                    } else {
-                                        var result = f.match(/(<|<=|>=|>)\s(\d+)/i);
-                                        if (result) {
-                                            if (result[1] === "<") {
-                                                if (pct < parseInt(result[2])) {
-                                                    test = true;
-                                                }
-                                            } else if (result[1] === "<=") {
-                                                if (pct <= parseInt(result[2])) {
-                                                    test = true;
-                                                }
-                                            } else if (result[1] === ">=") {
-                                                if (pct >= parseInt(result[2])) {
-                                                    test = true;
-                                                }
-                                            } else if (result[1] === ">") {
-                                                if (pct > parseInt(result[2])) {
-                                                    test = true;
-                                                }
-                                            }
+                headers: {
+                    0: { sorter: 'realISODate' },
+                    1: { sorter: 'realISODate' },
+                    2: { sorter: 'loadingNames' },
+                    4: { sorter: 'quality' },
+                    5: { sorter: 'eps' },
+                    6: { filter: 'parsed' }
+                },
+                widgetOptions: {
+                    filter_columnFilters: true, // jshint ignore:line
+                    filter_hideFilters: true, // jshint ignore:line
+                    filter_saveFilters: true, // jshint ignore:line
+                    filter_functions: { // jshint ignore:line
+                        5: function(e, n, f) {
+                            var test = false;
+                            var pct = Math.floor((n % 1) * 1000);
+                            if (f === '') {
+                                test = true;
+                            } else {
+                                var result = f.match(/(<|<=|>=|>)\s(\d+)/i);
+                                if (result) {
+                                    if (result[1] === "<") {
+                                        if (pct < parseInt(result[2])) {
+                                            test = true;
                                         }
-
-                                        result = f.match(/(\d+)\s(-|to)\s(\d+)/i);
-                                        if (result) {
-                                            if ((result[2] === "-") || (result[2] === "to")) {
-                                                if ((pct >= parseInt(result[1])) && (pct <= parseInt(result[3]))) {
-                                                    test = true;
-                                                }
-                                            }
+                                    } else if (result[1] === "<=") {
+                                        if (pct <= parseInt(result[2])) {
+                                            test = true;
+                                        }
+                                    } else if (result[1] === ">=") {
+                                        if (pct >= parseInt(result[2])) {
+                                            test = true;
                                         }
+                                    } else if (result[1] === ">") {
+                                        if (pct > parseInt(result[2])) {
+                                            test = true;
+                                        }
+                                    }
+                                }
 
-                                        result = f.match(/(=)?\s?(\d+)\s?(=)?/i);
-                                        if (result) {
-                                            if ((result[1] === "=") || (result[3] === "=")) {
-                                                if (parseInt(result[2]) === pct) {
-                                                    test = true;
-                                                }
-                                            }
+                                result = f.match(/(\d+)\s(-|to)\s(\d+)/i);
+                                if (result) {
+                                    if ((result[2] === "-") || (result[2] === "to")) {
+                                        if ((pct >= parseInt(result[1])) && (pct <= parseInt(result[3]))) {
+                                            test = true;
                                         }
+                                    }
+                                }
 
-                                        if (!isNaN(parseFloat(f)) && isFinite(f)) {
-                                            if (parseInt(f) === pct) {
-                                                test = true;
-                                            }
+                                result = f.match(/(=)?\s?(\d+)\s?(=)?/i);
+                                if (result) {
+                                    if ((result[1] === "=") || (result[3] === "=")) {
+                                        if (parseInt(result[2]) === pct) {
+                                            test = true;
                                         }
                                     }
-                                    return test;
                                 }
-                            },
-                            'columnSelector_mediaquery': false
-                        };
-                    } else {
-                        return {
-                            'filter_columnFilters': false
-                        };
-                    }
-                }()),
+
+                                if (!isNaN(parseFloat(f)) && isFinite(f)) {
+                                    if (parseInt(f) === pct) {
+                                        test = true;
+                                    }
+                                }
+                            }
+                            return test;
+                        }
+                    },
+                    'columnSelector_mediaquery': false
+                },
                 sortStable: true,
                 sortAppend: [[2,0]]
             });
 
-            if ($("#showListTableShows").find("tbody").find("tr").size() > 0){
-                $.tablesorter.filter.bindSearch( "#showListTableShows", $('.search') );
-            }
-
-            if(metaToBool('sickbeard.ANIME_SPLIT_HOME')){
-                if($("#showListTableAnime").find("tbody").find("tr").size() > 0){
-                    $.tablesorter.filter.bindSearch( "#showListTableAnime", $('.search') );
-                }
-            }
-
+            // @TODO we need to check if doing a $('') with a comma would be quicker than a seperate
+            //       isotope function for each as it does here
             $.each([$('#container'), $('#container-anime')], function (){
                 this.isotope({
-                    itemSelector: '.show',
+                    itemSelector: '.show-container',
                     sortBy : getMeta('sickbeard.POSTER_SORTBY'),
                     sortAscending: getMeta('sickbeard.POSTER_SORTDIR'),
                     layoutMode: 'masonry',
@@ -2050,7 +2025,7 @@ var SICKRAGE = {
             });
 
             // show/hide different types of rows when the checkboxes are changed
-            $("#checkboxControls input").change(function () {
+            $("#checkboxControls input").on('change', function () {
                 var whichClass = $(this).attr('id');
                 $(this).showHideRows(whichClass);
             });
@@ -2110,7 +2085,7 @@ var SICKRAGE = {
                     'sceneSeason': sceneSeason,
                     'sceneEpisode': sceneEpisode
                 }, function(data) {
-                    //	Set the values we get back
+                    // Set the values we get back
                     if (data.sceneSeason === null || data.sceneEpisode === null) {
                         $('#sceneSeasonXEpisode_' + showId + '_' + forSeason + '_' + forEpisode).val('');
                     } else {
@@ -2140,7 +2115,7 @@ var SICKRAGE = {
                     'sceneAbsolute': sceneAbsolute
                 },
                 function(data) {
-                    //	Set the values we get back
+                    // Set the values we get back
                     if (data.sceneAbsolute === null) {
                         $('#sceneAbsolute_' + showId + '_' + forAbsolute).val('');
                     } else {
@@ -2157,7 +2132,7 @@ var SICKRAGE = {
             }
 
             $('.sceneSeasonXEpisode').on('change', function() {
-                //	Strip non-numeric characters
+                // Strip non-numeric characters
                 $(this).val($(this).val().replace(/[^0-9xX]*/g, ''));
                 var forSeason = $(this).attr('data-for-season');
                 var forEpisode = $(this).attr('data-for-episode');
@@ -2171,7 +2146,7 @@ var SICKRAGE = {
             });
 
             $('.sceneAbsolute').on('change', function() {
-                //	Strip non-numeric characters
+                // Strip non-numeric characters
                 $(this).val($(this).val().replace(/[^0-9xX]*/g, ''));
                 var forAbsolute = $(this).attr('data-for-absolute');
 
@@ -2192,7 +2167,9 @@ var SICKRAGE = {
                 });
             });
             $.fn.generateStars = function() {
-                return this.each(function(i,e){$(e).html($('<span/>').width($(e).text()*12));});
+                return this.each(function(i,e){
+                    $(e).html($('<span/>').width($(e).text()*12));
+                });
             };
 
             $('.imdbstars').generateStars();
@@ -2449,7 +2426,7 @@ var SICKRAGE = {
                 if (!clicked) {
                     $.getJSON(srRoot + '/manage/showSubtitleMissed', {
                         'indexer_id': indexerId,
-                        whichSubs: $('#selectSubLang').val()
+                        'whichSubs': $('#selectSubLang').val()
                     }, function(data) {
                         $.each(data, function(season, eps) {
                             $.each(eps, function(episode, data) {
@@ -2470,6 +2447,8 @@ var SICKRAGE = {
                 }
             });
 
+            // @TODO these two should be able to be merged by using a generic class for the selector
+
             // selects all visible episode checkboxes.
             $('.selectAllShows').on('click', function(){
                 $('.allCheck').each(function(){
@@ -2590,21 +2569,12 @@ var SICKRAGE = {
                         8: { sorter: false },
                         9: { sorter: false }
                     },
-                    widgetOptions: (function() {
-                        if (metaToBool('sickbeard.FILTER_ROW')) {
-                            return {
-                                'filter_columnFilters': true,
-                                'filter_hideFilters': true,
-                                'filter_saveFilters': true,
-                                'columnSelector_mediaquery': false
-                            };
-                        } else {
-                            return {
-                                'filter_columnFilters': false,
-                                'columnSelector_mediaquery': false
-                            };
-                        }
-                    }())
+                    widgetOptions: {
+                        'filter_columnFilters': true,
+                        'filter_hideFilters': true,
+                        'filter_saveFilters': true,
+                        'columnSelector_mediaquery': false
+                    }
                 });
 
                 $('#srRoot').ajaxEpSearch();
diff --git a/gui/slick/js/core.min.js b/gui/slick/js/core.min.js
index 93decfd76f1258eb1f4a317e1d6679e7406c6869..ec512de8b35ca25d9fc76f1969820616877f9338 100644
Binary files a/gui/slick/js/core.min.js and b/gui/slick/js/core.min.js differ
diff --git a/gui/slick/js/vender.min.js b/gui/slick/js/vender.min.js
index b7dc4f9eb8b4ddfc8589f789c0bf2878517c747c..9167a17446d294abb22ed0d82b51ddf6341d7d2a 100644
Binary files a/gui/slick/js/vender.min.js and b/gui/slick/js/vender.min.js differ
diff --git a/gui/slick/views/apiBuilder.mako b/gui/slick/views/apiBuilder.mako
index c174c1b588ae715d579acc5ca5e3d82d4ebfbbd6..a1aa2b7ae2f6c80dec14c4ddaa3ccb79fe8a34e0 100644
--- a/gui/slick/views/apiBuilder.mako
+++ b/gui/slick/views/apiBuilder.mako
@@ -34,7 +34,6 @@
     <meta data-var="sickbeard.COMING_EPS_LAYOUT" data-content="${sickbeard.COMING_EPS_LAYOUT}">
     <meta data-var="sickbeard.COMING_EPS_SORT" data-content="${sickbeard.COMING_EPS_SORT}">
     <meta data-var="sickbeard.DATE_PRESET" data-content="${sickbeard.DATE_PRESET}">
-    <meta data-var="sickbeard.FILTER_ROW" data-content="${sickbeard.FILTER_ROW}">
     <meta data-var="sickbeard.FUZZY_DATING" data-content="${sickbeard.FUZZY_DATING}">
     <meta data-var="sickbeard.HISTORY_LAYOUT" data-content="${sickbeard.HISTORY_LAYOUT}">
     <meta data-var="sickbeard.HOME_LAYOUT" data-content="${sickbeard.HOME_LAYOUT}">
diff --git a/gui/slick/views/config_general.mako b/gui/slick/views/config_general.mako
index 0d74ea107e9cfd7cec3aaa5c07175684e1ce04b0..d0f4f429b2561ed9432430f5bf201600fa2790fc 100644
--- a/gui/slick/views/config_general.mako
+++ b/gui/slick/views/config_general.mako
@@ -271,18 +271,6 @@
                                 </span>
                             </label>
                         </div>
-                        <div class="field-pair">
-                            <label for="filter_row">
-                                <span class="component-title">Filter Row</span>
-                                <span class="component-desc">
-                                    <input type="checkbox" name="filter_row" id="filter_row" ${('', 'checked="checked"')[bool(sickbeard.FILTER_ROW)]}/>
-                                    <p>Add a filter row to the show display on the home page</p>
-                                    <p>Supports =, >, >=, <=, <, xx to yy , xx - yy</p>
-                                    <p><b>Note:</b> =, >, >=, <=, < should be first, followed by a space, then the value.</p>
-                                    <p>Examples: '> 90', '= 100', '0 to 99'</p>
-                                </span>
-                            </label>
-                        </div>
                         <div class="field-pair">
                             <label for="coming_eps_missed_range">
                                 <span class="component-title">Missed episodes range</span>
diff --git a/gui/slick/views/config_notifications.mako b/gui/slick/views/config_notifications.mako
index fa45a445132e4b0851360a3771934fc4878eb003..69f41f94d3ea1f41285f0afa1ca61469978525ea 100644
--- a/gui/slick/views/config_notifications.mako
+++ b/gui/slick/views/config_notifications.mako
@@ -409,7 +409,7 @@
                             <div class="field-pair">
                                 <label for="nmj_database">
                                     <span class="component-title">NMJ database</span>
-                                    <input type="text" name="nmj_database" id="nmj_database" value="${sickbeard.NMJ_DATABASE}" class="form-control input-sm input250" ${(' readonly="readonly"', '')[sickbeard.NMJ_DATABASE == True]}/>
+                                    <input type="text" name="nmj_database" id="nmj_database" value="${sickbeard.NMJ_DATABASE}" class="form-control input-sm input250" ${(' readonly="readonly"', '')[sickbeard.NMJ_DATABASE is True]}/>
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -419,7 +419,7 @@
                             <div class="field-pair">
                                 <label for="nmj_mount">
                                     <span class="component-title">NMJ mount url</span>
-                                    <input type="text" name="nmj_mount" id="nmj_mount" value="${sickbeard.NMJ_MOUNT}" class="form-control input-sm input250" ${(' readonly="readonly"', '')[sickbeard.NMJ_MOUNT == True]}/>
+                                    <input type="text" name="nmj_mount" id="nmj_mount" value="${sickbeard.NMJ_MOUNT}" class="form-control input-sm input250" ${(' readonly="readonly"', '')[sickbeard.NMJ_MOUNT is True]}/>
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -506,7 +506,7 @@
                             <div class="field-pair">
                                 <label for="nmjv2_database">
                                     <span class="component-title">NMJv2 database</span>
-                                    <input type="text" name="nmjv2_database" id="nmjv2_database" value="${sickbeard.NMJv2_DATABASE}" class="form-control input-sm input250" ${(' readonly="readonly"', '')[sickbeard.NMJv2_DATABASE == True]}/>
+                                    <input type="text" name="nmjv2_database" id="nmjv2_database" value="${sickbeard.NMJv2_DATABASE}" class="form-control input-sm input250" ${(' readonly="readonly"', '')[sickbeard.NMJv2_DATABASE is True]}/>
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -793,14 +793,46 @@
                                     </span>
                                 </label>
                             </div>
+                            <div class="field-pair">
+                                 <label for="prowl_message_title">
+                                     <span class="component-title">Prowl Message Title:</span>
+                                     <input type="text" name="prowl_message_title" id="prowl_message_title" value="${sickbeard.PROWL_MESSAGE_TITLE}" class="form-control input-sm input250" />
+                                 </label>
+                            </div>
                             <div class="field-pair">
                                 <label for="prowl_api">
-                                    <span class="component-title">Prowl API key:</span>
+                                    <span class="component-title">Global Prowl API key(s):</span>
                                     <input type="text" name="prowl_api" id="prowl_api" value="${sickbeard.PROWL_API}" class="form-control input-sm input250" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
-                                    <span class="component-desc">get your key at: <a href="${anon_url('https://www.prowlapp.com/api_settings.php')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">https://www.prowlapp.com/api_settings.php</a></span>
+                                    <span class="component-desc">Prowl API(s) listed here, separated by commas if applicable, will<br /> receive notifications for <b>all</b> shows.  
+                                                                 Your Prowl API key is available at: 
+                                                                 <a href="${anon_url('https://www.prowlapp.com/api_settings.php')}" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">
+                                                                 https://www.prowlapp.com/api_settings.php</a><br />
+                                                                 (This field may be blank except when testing.)</span>
+                                </label>
+                            </div>
+                            <div class="field-pair">
+                                <label for="prowl_show">
+                                    <span class="component-title">Show notification list</span>
+                                    <select name="prowl_show" id="prowl_show" class="form-control input-sm">
+                                        <option value="-1">-- Select a Show --</option>
+                                    </select>
+                                </label>
+                                <label>
+                                    <span class="component-title">&nbsp;</span>
+                                    <input type="text" name="prowl_show_list" id="prowl_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 by entering Prowl API key(s), separated by commas,
+                                                                 after selecting a show in the drop-down box.   Be sure to activate the 'Save for this show'
+                                                                 button below after each entry.</span>
+                                </label>
+                                <label>
+                                    <span class="component-title">&nbsp;</span>
+                                    <input id="prowl_show_save" class="btn" type="button" value="Save for this show" />
                                 </label>
                             </div>
                             <div class="field-pair">
@@ -1114,7 +1146,7 @@
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
-                                    <span class="component-desc">(multiple keys must be seperated by commas, up to a maximum of 5)</span>
+                                    <span class="component-desc">(multiple keys must be separated by commas, up to a maximum of 5)</span>
                                 </label>
                             </div>
                             <div class="field-pair">
@@ -1560,7 +1592,7 @@
                                     </label>
                                     <label>
                                         <span class="component-title">&nbsp;</span>
-                                        <span class="component-desc">method in which to download episodes for new show's.</span>
+                                        <span class="component-desc">method in which to download episodes for new shows.</span>
                                     </label>
                                 </div>
                                 <div class="field-pair">
@@ -1595,7 +1627,7 @@
                                         <span class="component-title">Start paused</span>
                                         <span class="component-desc">
                                             <input type="checkbox" name="trakt_start_paused" id="trakt_start_paused" ${('', 'checked="checked"')[bool(sickbeard.TRAKT_START_PAUSED)]}/>
-                                            <p>show's grabbed from your trakt watchlist start paused.</p>
+                                            <p>shows grabbed from your trakt watchlist start paused.</p>
                                         </span>
                                     </label>
                                 </div>
@@ -1728,7 +1760,8 @@
                                 </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-desc">Email addresses listed here, separated by commas if applicable, will<br /> receive notifications for <b>all</b> shows.<br />
+                                                                 (This field may be blank except when testing.)</span>
                                 </label>
                             </div>
                             <div class="field-pair">
@@ -1744,7 +1777,9 @@
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
-                                    <span class="component-desc">configure per show notifications here.</span>
+                                    <span class="component-desc">Configure per-show notifications here by entering email address(es), separated by commas,
+                                                                 after selecting a show in the drop-down box.   Be sure to activate the 'Save for this show'
+                                                                 button below after each entry.</span>
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
diff --git a/gui/slick/views/config_postProcessing.mako b/gui/slick/views/config_postProcessing.mako
index cb202f50dcabc8b674a29b9b7851f9e19c31f463..51ce0a4c1fd014150e86ef64a76dac7cf40c1223 100644
--- a/gui/slick/views/config_postProcessing.mako
+++ b/gui/slick/views/config_postProcessing.mako
@@ -1,6 +1,7 @@
 <%inherit file="/layouts/main.mako"/>
 <%!
     import os.path
+    import datetime
     import sickbeard
     from sickbeard.common import SKIPPED, WANTED, UNAIRED, ARCHIVED, IGNORED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, FAILED
     from sickbeard.common import Quality, qualityPresets, statusStrings, qualityPresetStrings, cpu_presets, multiEpStrings
@@ -378,6 +379,21 @@
                                           <td>%E_N</td>
                                           <td>Episode_Name</td>
                                         </tr>
+                                        <tr>
+                                          <td class="align-right"><b>Air Date:</b></td>
+                                          <td>%M</td>
+                                          <td>${datetime.date.today().month}</td>
+                                        </tr>
+                                        <tr class="even">
+                                          <td>&nbsp;</td>
+                                          <td>%D</td>
+                                          <td>${datetime.date.today().day}</td>
+                                        </tr>
+                                        <tr>
+                                          <td>&nbsp;</td>
+                                          <td>%Y</td>
+                                          <td>${datetime.date.today().year}</td>
+                                        </tr>                                  
                                         <tr>
                                           <td class="align-right"><b>Quality:</b></td>
                                           <td>%QN</td>
diff --git a/gui/slick/views/config_providers.mako b/gui/slick/views/config_providers.mako
index ca7ccfbb831e6373d72dcaac255ed00a728be03f..40808db9b0d223fc52864c26f4467b9b0cfc0a23 100644
--- a/gui/slick/views/config_providers.mako
+++ b/gui/slick/views/config_providers.mako
@@ -76,10 +76,14 @@ $('#config-components').tabs();
                                     continue
 
                                 curName = curProvider.getID()
+                                if hasattr(curProvider, 'custom_url'):
+                                    curURL = curProvider.custom_url or curProvider.url
+                                else:
+                                    curURL = curProvider.url
                             %>
                             <li class="ui-state-default ${('nzb-provider', 'torrent-provider')[bool(curProvider.providerType == "torrent")]}" id="${curName}">
-                                <input type="checkbox" id="enable_${curName}" class="provider_enabler" ${('', 'checked="checked"')[curProvider.isEnabled() == True]}/>
-                                <a href="${anon_url(curProvider.url)}" class="imgLink" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;"><img src="${srRoot}/images/providers/${curProvider.imageName()}" alt="${curProvider.name}" title="${curProvider.name}" width="16" height="16" style="vertical-align:middle;"/></a>
+                                <input type="checkbox" id="enable_${curName}" class="provider_enabler" ${('', 'checked="checked"')[curProvider.isEnabled() is True]}/>
+                                <a href="${anon_url(curURL)}" class="imgLink" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;"><img src="${srRoot}/images/providers/${curProvider.imageName()}" alt="${curProvider.name}" title="${curProvider.name}" width="16" height="16" style="vertical-align:middle;"/></a>
                                 <span style="vertical-align:middle;">${curProvider.name}</span>
                                 ${('*', '')[bool(curProvider.supportsBacklog)]}
                                 <span class="ui-icon ui-icon-arrowthick-2-n-s pull-right" style="vertical-align:middle;"></span>
@@ -301,6 +305,24 @@ $('#config-components').tabs();
 
                     % for curTorrentProvider in [curProvider for curProvider in sickbeard.providers.sortedProviderList() if curProvider.providerType == GenericProvider.TORRENT]:
                     <div class="providerDiv" id="${curTorrentProvider.getID()}Div">
+
+                        % if hasattr(curTorrentProvider, 'custom_url'):
+                        <div class="field-pair">
+                            <label for="${curTorrentProvider.getID()}_custom_url">
+                                <span class="component-title">Custom URL:</span>
+                                <span class="component-desc">
+                                    <input type="text" name="${curTorrentProvider.getID()}_custom_url" id="${curTorrentProvider.getID()}_custom_url" value="${curTorrentProvider.custom_url}" class="form-control input-sm input350" />
+                                </span>
+                            </label>
+                            <label>
+                                <span class="component-title">&nbsp;</span>
+                                <span class="component-desc">
+                                    <p>The URL should include the protocol (and port if applicable).  Examples:  http://192.168.1.4/ or http://localhost:3000/</p>
+                                </span>
+                            </label>
+                        </div>
+                        % endif
+
                         % if hasattr(curTorrentProvider, 'api_key'):
                         <div class="field-pair">
                             <label for="${curTorrentProvider.getID()}_api_key">
diff --git a/gui/slick/views/config_search.mako b/gui/slick/views/config_search.mako
index 8beab44b50b58c03bfb4e521465188ef95c2a202..61e3054dfd68e15001d3933a3efe8f28235f4c52 100644
--- a/gui/slick/views/config_search.mako
+++ b/gui/slick/views/config_search.mako
@@ -318,7 +318,7 @@
                                 </label>
                             </div>
 
-                            % if sickbeard.ALLOW_HIGH_PRIORITY == True:
+                            % if sickbeard.ALLOW_HIGH_PRIORITY is True:
                             <div class="field-pair">
                                 <label for="sab_forced">
                                     <span class="component-title">Use forced priority</span>
diff --git a/gui/slick/views/config_subtitles.mako b/gui/slick/views/config_subtitles.mako
index 63fd4faf61aa43b7d2d8c9c60455e5fef26a6806..b84bfc6b946110f26ffeba8f490f54a0acbe3bd1 100644
--- a/gui/slick/views/config_subtitles.mako
+++ b/gui/slick/views/config_subtitles.mako
@@ -121,7 +121,7 @@ $('#subtitles_dir').fileBrowser({ title: 'Select Subtitles Download Directory' }
                                 <div class="field-pair">
                                     <label class="nocheck">
                                         <span class="component-title">Extra Scripts</span>
-                                           <input type="text" name="subtitles_extra_scripts" value="<%'|'.join(sickbeard.SUBTITLES_EXTRA_SCRIPTS)%>" class="form-control input-sm input350" />
+                                           <input type="text" name="subtitles_extra_scripts" value="${'|'.join(sickbeard.SUBTITLES_EXTRA_SCRIPTS)}" class="form-control input-sm input350" />
                                     </label>
                                     <label class="nocheck">
                                         <span class="component-title">&nbsp;</span>
@@ -162,7 +162,7 @@ $('#subtitles_dir').fileBrowser({ title: 'Select Subtitles Download Directory' }
                         <ul id="service_order_list">
                         % for curService in sickbeard.subtitles.sortedServiceList():
                             <li class="ui-state-default" id="${curService['name']}">
-                                <input type="checkbox" id="enable_${curService['name']}" class="service_enabler" ${('', 'checked="checked"')[curService['enabled'] == True]}/>
+                                <input type="checkbox" id="enable_${curService['name']}" class="service_enabler" ${('', 'checked="checked"')[curService['enabled'] is True]}/>
                                 <a href="${anon_url(curService['url'])}" class="imgLink" target="_new">
                                     <img src="${srRoot}/images/subtitles/${curService['image']}" alt="${curService['url']}" title="${curService['url']}" width="16" height="16" style="vertical-align:middle;"/>
                                 </a>
@@ -212,6 +212,7 @@ $('#subtitles_dir').fileBrowser({ title: 'Select Subtitles Download Directory' }
                         <br><input type="submit" class="btn config_submitter" value="Save Changes" /><br>
                     </fieldset>
                 </div><!-- /component-group3 //-->
+                <br><input type="submit" class="btn config_submitter" value="Save Changes" /><br>
             </div><!-- /config-components //-->
 
 </form>
diff --git a/gui/slick/views/displayShow.mako b/gui/slick/views/displayShow.mako
index b8da99c0f5e6a59ac9ade423dcac79608dac2e51..23fd582196121cf9e83f8f05437af381a446130d 100644
--- a/gui/slick/views/displayShow.mako
+++ b/gui/slick/views/displayShow.mako
@@ -334,7 +334,7 @@
         <tr style="height: 60px;">
             <th class="row-seasonheader displayShowTable" colspan="13" style="vertical-align: bottom; width: auto;">
                 <h3 style="display: inline;"><a name="season-${epResult["season"]}"></a>${("Specials", "Season " + str(epResult["season"]))[int(epResult["season"]) > 0]}</h3>
-                % if sickbeard.DISPLAY_ALL_SEASONS == False:
+                % if sickbeard.DISPLAY_ALL_SEASONS is False:
                     <button id="showseason-${epResult['season']}" type="button" class="btn btn-xs pull-right" data-toggle="collapse" data-target="#collapseSeason-${epResult['season']}">Show Episodes</button>
                     <script type="text/javascript">
                         $(function() {
@@ -374,7 +374,7 @@
         <tr style="height: 60px;">
             <th class="row-seasonheader displayShowTable" colspan="13" style="vertical-align: bottom; width: auto;">
                 <h3 style="display: inline;"><a name="season-${epResult["season"]}"></a>${("Specials", "Season " + str(epResult["season"]))[bool(int(epResult["season"]))]}</h3>
-                % if sickbeard.DISPLAY_ALL_SEASONS == False:
+                % if sickbeard.DISPLAY_ALL_SEASONS is False:
                     <button id="showseason-${epResult['season']}" type="button" class="btn btn-xs pull-right" data-toggle="collapse" data-target="#collapseSeason-${epResult['season']}">Show Episodes</button>
                     <script type="text/javascript">
                         $(function() {
@@ -410,7 +410,7 @@
         </tr>
             % endif
     </tbody>
-        % if sickbeard.DISPLAY_ALL_SEASONS == False:
+        % if sickbeard.DISPLAY_ALL_SEASONS is False:
         <tbody class="collapse${("", " in")[curSeason == -1]}" id="collapseSeason-${epResult['season']}">
         % else:
         <tbody>
@@ -428,7 +428,7 @@
             <td align="center">
             <%
                 text = str(epResult['episode'])
-                if epLoc != '' and epLoc != None:
+                if epLoc != '' and epLoc is not None:
                     text = '<span title="' + epLoc + '" class="addQTip">' + text + "</span>"
             %>
                 ${text}
@@ -459,7 +459,7 @@
                         style="padding: 0; text-align: center; max-width: 60px;" />
             </td>
             <td class="col-name">
-            % if epResult["description"] != "" and epResult["description"] != None:
+            % if epResult["description"] != "" and epResult["description"] is not None:
                 <img src="${srRoot}/images/info32.png" width="16" height="16" class="plotInfo" alt="" id="plot_info_${str(show.indexerid)}_${str(epResult["season"])}_${str(epResult["episode"])}" />
             % else:
                 <img src="${srRoot}/images/info32.png" width="16" height="16" class="plotInfoNone" alt="" />
diff --git a/gui/slick/views/editShow.mako b/gui/slick/views/editShow.mako
index b1901f664591d2fde71c109595223dd1e0fd51cd..299168570c6e184e613eb6e108dfe120d375dc2f 100644
--- a/gui/slick/views/editShow.mako
+++ b/gui/slick/views/editShow.mako
@@ -106,7 +106,7 @@
                             <label for="subtitles">
                                 <span class="component-title">Subtitles</span>
                                 <span class="component-desc">
-                                    <input type="checkbox" id="subtitles" name="subtitles" ${('', 'checked="checked"')[show.subtitles == 1 and sickbeard.USE_SUBTITLES == True]} ${('disabled="disabled"', '')[bool(sickbeard.USE_SUBTITLES)]}/> search for subtitles
+                                    <input type="checkbox" id="subtitles" name="subtitles" ${('', 'checked="checked"')[show.subtitles == 1 and sickbeard.USE_SUBTITLES is True]} ${('disabled="disabled"', '')[bool(sickbeard.USE_SUBTITLES)]}/> search for subtitles
                                 </span>
                             </label>
                         </div>
diff --git a/gui/slick/views/history.mako b/gui/slick/views/history.mako
index 8f9de80b3a77af59b13c1a3c22e72b6a34d08403..8895b5b044e84ddc132f883bc49daa0c6fe38929 100644
--- a/gui/slick/views/history.mako
+++ b/gui/slick/views/history.mako
@@ -87,7 +87,7 @@
                     % if hItem["provider"] > 0:
                         % if curStatus in [SNATCHED, FAILED]:
                             <% provider = providers.getProviderClass(generic.GenericProvider.makeID(hItem["provider"])) %>
-                            % if provider != None:
+                            % if provider is not None:
                                 <img src="${srRoot}/images/providers/${provider.imageName()}" width="16" height="16" style="vertical-align:middle;" /> <span style="vertical-align:middle;">${provider.name}</span>
                             % else:
                                 <img src="${srRoot}/images/providers/missing.png" width="16" height="16" style="vertical-align:middle;" title="missing provider"/> <span style="vertical-align:middle;">Missing Provider</span>
@@ -142,7 +142,7 @@
                         <% curStatus, curQuality = Quality.splitCompositeStatus(int(action["action"])) %>
                         % if curStatus in [SNATCHED, FAILED]:
                             <% provider = providers.getProviderClass(generic.GenericProvider.makeID(action["provider"])) %>
-                            % if provider != None:
+                            % if provider is not None:
                                 <img src="${srRoot}/images/providers/${provider.imageName()}" width="16" height="16" style="vertical-align:middle;" alt="${provider.name}" style="cursor: help;" title="${provider.name}: ${os.path.basename(action["resource"])}"/>
                             % else:
                                 <img src="${srRoot}/images/providers/missing.png" width="16" height="16" style="vertical-align:middle;" alt="missing provider" title="missing provider"/>
diff --git a/gui/slick/views/home.mako b/gui/slick/views/home.mako
index 9b02a63f37a2bb581488952e6639657a60231760..431137155ad592f82290d61f807952451c1ea06e 100644
--- a/gui/slick/views/home.mako
+++ b/gui/slick/views/home.mako
@@ -19,24 +19,21 @@
 
 <div id="HomeLayout" class="pull-right hidden-print" style="margin-top: -40px;">
     % if sickbeard.HOME_LAYOUT != 'poster':
+    <span>
         <button id="popover" type="button" class="btn btn-inline">Select Columns <b class="caret"></b></button>
-    % endif
-    <span> Layout:
-        <select name="layout" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;">
-            <option value="${srRoot}/setHomeLayout/?layout=poster" ${('', 'selected="selected"')[sickbeard.HOME_LAYOUT == 'poster']}>Poster</option>
-            <option value="${srRoot}/setHomeLayout/?layout=small" ${('', 'selected="selected"')[sickbeard.HOME_LAYOUT == 'small']}>Small Poster</option>
-            <option value="${srRoot}/setHomeLayout/?layout=banner" ${('', 'selected="selected"')[sickbeard.HOME_LAYOUT == 'banner']}>Banner</option>
-            <option value="${srRoot}/setHomeLayout/?layout=simple" ${('', 'selected="selected"')[sickbeard.HOME_LAYOUT == 'simple']}>Simple</option>
-        </select>
-        % if sickbeard.HOME_LAYOUT != 'poster':
-        Search:
-            <input class="search form-control form-control-inline input-sm input200" type="search" data-column="2" placeholder="Search Show Name">
-            <button type="button" class="resetsorting btn btn-inline">Reset Search</button>
-        % endif
     </span>
+    &nbsp;
+    <span>
+        <button type="button" class="resetsorting btn btn-inline">Clear Filter(s)</button>
+    </span>
+    % endif
 
     % if sickbeard.HOME_LAYOUT == 'poster':
     &nbsp;
+    <span>
+        <input id="filterShowName" class="form-control form-control-inline input-sm input200" type="search" placeholder="Filter Show Name">
+    </span>
+    &nbsp;
     <span> Sort By:
         <select id="postersort" class="form-control form-control-inline input-sm">
             <option value="name" data-sort="${srRoot}/setPosterSortBy/?sort=name" ${('', 'selected="selected"')[sickbeard.POSTER_SORTBY == 'name']}>Name</option>
@@ -46,15 +43,23 @@
         </select>
     </span>
     &nbsp;
-    <span> Sort Order:
+    <span> Direction:
         <select id="postersortdirection" class="form-control form-control-inline input-sm">
-            <option value="true" data-sort="${srRoot}/setPosterSortDir/?direction=1" ${('', 'selected="selected"')[sickbeard.POSTER_SORTDIR == 1]}>Asc</option>
-            <option value="false" data-sort="${srRoot}/setPosterSortDir/?direction=0" ${('', 'selected="selected"')[sickbeard.POSTER_SORTDIR == 0]}>Desc</option>
+            <option value="true" data-sort="${srRoot}/setPosterSortDir/?direction=1" ${('', 'selected="selected"')[sickbeard.POSTER_SORTDIR == 1]}>A &#10140; Z</option>
+            <option value="false" data-sort="${srRoot}/setPosterSortDir/?direction=0" ${('', 'selected="selected"')[sickbeard.POSTER_SORTDIR == 0]}>Z &#10140; A</option>
         </select>
     </span>
-    &nbsp;
-
     % endif
+
+    &nbsp;
+    <span> Layout:
+        <select name="layout" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;">
+            <option value="${srRoot}/setHomeLayout/?layout=poster" ${('', 'selected="selected"')[sickbeard.HOME_LAYOUT == 'poster']}>Poster</option>
+            <option value="${srRoot}/setHomeLayout/?layout=small" ${('', 'selected="selected"')[sickbeard.HOME_LAYOUT == 'small']}>Small Poster</option>
+            <option value="${srRoot}/setHomeLayout/?layout=banner" ${('', 'selected="selected"')[sickbeard.HOME_LAYOUT == 'banner']}>Banner</option>
+            <option value="${srRoot}/setHomeLayout/?layout=simple" ${('', 'selected="selected"')[sickbeard.HOME_LAYOUT == 'simple']}>Simple</option>
+        </select>
+    </span>
 </div>
 
 % for curShowlist in showlists:
@@ -67,8 +72,8 @@
 <div id="${('container', 'container-anime')[curListType == 'Anime' and sickbeard.HOME_LAYOUT == 'poster']}" class="clearfix">
 <div class="posterview">
 % for curLoadingShow in sickbeard.showQueueScheduler.action.loadingShowList:
-    % if curLoadingShow.show == None:
-        <div class="show" data-name="0" data-date="010101" data-network="0" data-progress="101">
+    % if curLoadingShow.show is None:
+        <div class="show-container" data-name="0" data-date="010101" data-network="0" data-progress="101">
             <img alt="" title="${curLoadingShow.show_name}" class="show-image" style="border-bottom: 1px solid #111;" src="${srRoot}/images/poster.png" />
             <div class="show-details">
                 <div class="show-add">Loading... (${curLoadingShow.show_name})</div>
@@ -141,7 +146,7 @@
         elif 'nded' in display_status:
             data_date = '5000000100.0'
 %>
-    <div class="show" id="show${curShow.indexerid}" data-name="${curShow.name}" data-date="${data_date}" data-network="${curShow.network}" data-progress="${progressbar_percent}">
+    <div class="show-container" id="show${curShow.indexerid}" data-name="${curShow.name}" data-date="${data_date}" data-network="${curShow.network}" data-progress="${progressbar_percent}">
         <div class="show-image">
             <a href="${srRoot}/home/displayShow?show=${curShow.indexerid}"><img alt="" class="show-image" src="${srRoot}/showPoster/?show=${curShow.indexerid}&amp;which=poster_thumb" /></a>
         </div>
@@ -244,14 +249,14 @@
     <tbody class="tablesorter-infoOnly">
 % for curLoadingShow in sickbeard.showQueueScheduler.action.loadingShowList:
 
-    % if curLoadingShow.show != None and curLoadingShow.show in sickbeard.showList:
+    % if curLoadingShow.show is not None and curLoadingShow.show in sickbeard.showList:
          <% continue %>
     % endif
   <tr>
     <td align="center">(loading)</td>
     <td></td>
     <td>
-    % if curLoadingShow.show == None:
+    % if curLoadingShow.show is None:
     <span title="">Loading... (${curLoadingShow.show_name})</span>
     % else:
     <a href="displayShow?show=${curLoadingShow.show.indexerid}">${curLoadingShow.show.name}</a>
diff --git a/gui/slick/views/home_addShows.mako b/gui/slick/views/home_addShows.mako
index 16da9f75c1dac61cf22c99dbb32ae7c387ede771..a42f588421e63b2d443f5d2360babae47b8f3141 100644
--- a/gui/slick/views/home_addShows.mako
+++ b/gui/slick/views/home_addShows.mako
@@ -21,7 +21,7 @@
     </a>
 
     <br><br>
-    % if sickbeard.USE_TRAKT == True:
+    % if sickbeard.USE_TRAKT is True:
     <a href="${srRoot}/home/addShows/trendingShows/" id="btnNewShow" class="btn btn-large">
         <div class="button"><div class="icon-addtrendingshow"></div></div>
         <div class="buttontext">
diff --git a/gui/slick/views/inc_defs.mako b/gui/slick/views/inc_defs.mako
index eaa54116836d691c2ff9eecc16cf25c501a2f502..f8ff29646e906f4cc13b9d163d4f77cec5711fe6 100644
--- a/gui/slick/views/inc_defs.mako
+++ b/gui/slick/views/inc_defs.mako
@@ -42,7 +42,7 @@
         cssClass = "Custom"
         qualityString = "Custom"
 
-    if overrideClass == None:
+    if overrideClass is None:
         cssClass = "quality " + cssClass
     else:
         cssClass = overrideClass
diff --git a/gui/slick/views/layouts/main.mako b/gui/slick/views/layouts/main.mako
index d1bcc84bf59e367f31a8cefa795a3b9d5ad8df53..19fcb9965b463ad3cd2721cec972f892edd7e1da 100644
--- a/gui/slick/views/layouts/main.mako
+++ b/gui/slick/views/layouts/main.mako
@@ -49,7 +49,6 @@
         <meta data-var="sickbeard.COMING_EPS_LAYOUT" data-content="${sickbeard.COMING_EPS_LAYOUT}">
         <meta data-var="sickbeard.COMING_EPS_SORT" data-content="${sickbeard.COMING_EPS_SORT}">
         <meta data-var="sickbeard.DATE_PRESET" data-content="${sickbeard.DATE_PRESET}">
-        <meta data-var="sickbeard.FILTER_ROW" data-content="${sickbeard.FILTER_ROW}">
         <meta data-var="sickbeard.FUZZY_DATING" data-content="${sickbeard.FUZZY_DATING}">
         <meta data-var="sickbeard.HISTORY_LAYOUT" data-content="${sickbeard.HISTORY_LAYOUT}">
         <meta data-var="sickbeard.HOME_LAYOUT" data-content="${sickbeard.HOME_LAYOUT}">
@@ -221,7 +220,7 @@
                                 <li><a href="${srRoot}/home/updateCheck?pid=${sbPID}"><i class="menu-icon-update"></i>&nbsp;Check For Updates</a></li>
                                 <li><a href="${srRoot}/home/restart/?pid=${sbPID}" class="confirm restart"><i class="menu-icon-restart"></i>&nbsp;Restart</a></li>
                                 <li><a href="${srRoot}/home/shutdown/?pid=${sbPID}" class="confirm shutdown"><i class="menu-icon-shutdown"></i>&nbsp;Shutdown</a></li>
-                                % if srLogin != True:
+                                % if srLogin is not True:
                                     <li><a href="${srRoot}/logout" class="confirm logout"><i class="menu-icon-shutdown"></i>&nbsp;Logout</a></li>
                                 % endif
                                 <li role="separator" class="divider"></li>
diff --git a/gui/slick/views/manage_backlogOverview.mako b/gui/slick/views/manage_backlogOverview.mako
index 637c9d1143f8c681b1f8242e1d685a78bc0e9fdd..449418457d9ecb1f435f3f0cd25912fc4d3894a7 100644
--- a/gui/slick/views/manage_backlogOverview.mako
+++ b/gui/slick/views/manage_backlogOverview.mako
@@ -19,13 +19,14 @@
     <h1 class="title">${title}</h1>
 % endif
 
-<% totalWanted = 0 %>
-<% totalQual = 0 %>
-
-% for curShow in sickbeard.showList:
-    <% totalWanted = totalWanted + showCounts[curShow.indexerid][Overview.WANTED] %>
-    <% totalQual = totalQual + showCounts[curShow.indexerid][Overview.QUAL] %>
-% endfor
+<%
+    totalWanted = 0
+    totalQual = 0
+    backLogShows = sorted([x for x in sickbeard.showList if showCounts[x.indexerid][Overview.QUAL] + showCounts[x.indexerid][Overview.WANTED]], key=lambda x: x.name)
+    for curShow in backLogShows:
+        totalWanted += showCounts[curShow.indexerid][Overview.WANTED]
+        totalQual += showCounts[curShow.indexerid][Overview.QUAL]
+%>
 
 <div class="h2footer pull-right">
     <span class="listing-key wanted">Wanted: <b>${totalWanted}</b></span>
@@ -35,20 +36,14 @@
 <div class="float-left">
 Jump to Show
     <select id="pickShow" class="form-control form-control-inline input-sm">
-    % for curShow in sorted(sickbeard.showList, key=lambda x: x.name):
-        % if showCounts[curShow.indexerid][Overview.QUAL] + showCounts[curShow.indexerid][Overview.WANTED] != 0:
+    % for curShow in backLogShows:
         <option value="${curShow.indexerid}">${curShow.name}</option>
-        % endif
     % endfor
     </select>
 </div>
 
 <table class="sickbeardTable" cellspacing="0" border="0" cellpadding="0">
-% for curShow in sorted(sickbeard.showList, key=lambda x: x.name):
-
-    % if showCounts[curShow.indexerid][Overview.QUAL] + showCounts[curShow.indexerid][Overview.WANTED] == 0:
-        <% continue %>
-    % endif
+% for curShow in backLogShows:
     <tr class="seasonheader" id="show-${curShow.indexerid}">
         <td colspan="3" class="align-left">
             <br><h2><a href="${srRoot}/home/displayShow?show=${curShow.indexerid}">${curShow.name}</a></h2>
@@ -63,17 +58,11 @@ Jump to Show
     <tr class="seasoncols"><th>Episode</th><th>Name</th><th class="nowrap">Airdate</th></tr>
 
     % for curResult in showSQLResults[curShow.indexerid]:
-        <% whichStr = str(curResult['season']) + 'x' + str(curResult['episode']) %>
-        % try:
-            <% overview = showCats[curShow.indexerid][whichStr] %>
-        % except Exception:
-            <% continue %>
-        % endtry
-
-        % if overview not in (Overview.QUAL, Overview.WANTED):
-            <% continue %>
-        % endif
-
+        <%
+            whichStr = 'S%02dE%02d' % (curResult['season'], curResult['episode'])
+            if whichStr not in showCats[curShow.indexerid] or showCats[curShow.indexerid][whichStr] not in (Overview.QUAL, Overview.WANTED):
+                continue
+        %>
         <tr class="seasonstyle ${Overview.overviewStrings[showCats[curShow.indexerid][whichStr]]}">
             <td class="tableleft" align="center">${whichStr}</td>
             <td class="tableright" align="center" class="nowrap">
@@ -81,7 +70,7 @@ Jump to Show
             </td>
             <td>
             <% airDate = sbdatetime.sbdatetime.convert_to_setting(network_timezones.parse_date_time(curResult['airdate'], curShow.airs, curShow.network)) %>
-            % if int(curResult['airdate']) != 1:
+            % if int(curResult['airdate']) > 1:
                 <time datetime="${airDate.isoformat('T')}" class="date">${sbdatetime.sbdatetime.sbfdatetime(airDate)}</time>
             % else:
                 Never
@@ -90,7 +79,6 @@ Jump to Show
         </tr>
     % endfor
 % endfor
-
 </table>
 </div>
 </%block>
diff --git a/gui/slick/views/manage_failedDownloads.mako b/gui/slick/views/manage_failedDownloads.mako
index bac45ec04062b86ec34972a6f6ae6754fe47775e..920e04f8a3054b4f2b94905664d58b7079af0821 100644
--- a/gui/slick/views/manage_failedDownloads.mako
+++ b/gui/slick/views/manage_failedDownloads.mako
@@ -54,7 +54,7 @@
     </td>
     <td align="center">
     <% provider = providers.getProviderClass(generic.GenericProvider.makeID(hItem["provider"])) %>
-    % if provider != None:
+    % if provider is not None:
         <img src="${srRoot}/images/providers/${provider.imageName()}" width="16" height="16" alt="${provider.name}" title="${provider.name}"/>
     % else:
         <img src="${srRoot}/images/providers/missing.png" width="16" height="16" alt="missing provider" title="missing provider"/>
diff --git a/gui/slick/views/manage_manageSearches.mako b/gui/slick/views/manage_manageSearches.mako
index e3caf400a321675cff1cb5f4b58377af69d2cd8d..3a2ee26f97c85aea44892082c6521d4ae3a77477 100644
--- a/gui/slick/views/manage_manageSearches.mako
+++ b/gui/slick/views/manage_manageSearches.mako
@@ -44,6 +44,17 @@ ${('Not in progress', 'In Progress')[dailySearchStatus]}<br>
 % endif
 <br>
 
+<h3>Subtitle Search:</h3>
+<a class="btn ${('disabled', '')[bool(sickbeard.USE_SUBTITLES)]}" href="${srRoot}/manage/manageSearches/forceSubtitlesFinder"><i class="icon-exclamation-sign"></i> Force</a>
+% if not sickbeard.USE_SUBTITLES:
+    Subtitle search disabled <br>
+% elif not subtitlesFinderStatus:
+    Not in progress<br>
+% else:
+    In Progress<br>
+% endif
+<br>
+
 <h3>Search Queue:</h3>
 Backlog: <i>${queueLength['backlog']} pending items</i></br>
 Daily: <i>${queueLength['daily']} pending items</i></br>
diff --git a/gui/slick/views/manage_massEdit.mako b/gui/slick/views/manage_massEdit.mako
index 7d44bb7c46aeb6a570269ba9256007ffd17a8a80..0892016a97e75f1e78b853719456eb3d2fefc9cf 100644
--- a/gui/slick/views/manage_massEdit.mako
+++ b/gui/slick/views/manage_massEdit.mako
@@ -9,7 +9,7 @@
 
 <%block name="scripts">
 <%
-    if quality_value != None:
+    if quality_value is not None:
         initial_quality = int(quality_value)
     else:
         initial_quality = common.SD
@@ -90,7 +90,7 @@
                                 <span class="component-title">Preferred Quality</span>
                                 <span class="component-desc">
                                     <%
-                                        if quality_value != None:
+                                        if quality_value is not None:
                                             initial_quality = int(quality_value)
                                         else:
                                             initial_quality = common.SD
@@ -101,7 +101,7 @@
                                     <select id="qualityPreset" name="quality_preset" class="form-control form-control-inline input-sm">
                                         <option value="keep">&lt; Keep &gt;</option>
                                         <% selected = None %>
-                                        <option value="0" ${('', 'selected="selected"')[quality_value != None and quality_value not in common.qualityPresets]}>Custom</option>
+                                        <option value="0" ${('', 'selected="selected"')[quality_value is not None and quality_value not in common.qualityPresets]}>Custom</option>
                                         % for curPreset in sorted(common.qualityPresets):
                                         <option value="${curPreset}" ${('', 'selected="selected"')[quality_value == curPreset]}>${common.qualityPresetStrings[curPreset]}</option>
                                         % endfor
@@ -137,9 +137,9 @@
                                 <span class="component-title">Archive on first match</span>
                                 <span class="component-desc">
                                     <select id="edit_archive_firstmatch" name="archive_firstmatch" class="form-control form-control-inline input-sm">
-                                        <option value="keep" ${('', 'selected="selected"')[archive_firstmatch_value == None]}>&lt; Keep &gt;</option>
-                                        <option value="enable" ${('', 'selected="selected"')[archive_firstmatch_value == 1]}>Yes</option>
-                                        <option value="disable" ${('', 'selected="selected"')[archive_firstmatch_value == 0]}>No</option>
+                                        <option value="keep" ${('', 'selected="selected"')[archive_firstmatch_value is None]}>&lt; Keep &gt;</option>
+                                        <option value="enable" ${('', 'selected="selected"')[archive_firstmatch_value is 1]}>Yes</option>
+                                        <option value="disable" ${('', 'selected="selected"')[archive_firstmatch_value is 0]}>No</option>
                                     </select><br>
                                     Archive episode after the first best match is found from your archive quality list.
                                 </span>
@@ -151,7 +151,7 @@
                                 <span class="component-title">Season folders (<span class="separator">*</span>)</span>
                                 <span class="component-desc">
                                     <select id="" name="flatten_folders" class="form-control form-control-inline input-sm">
-                                        <option value="keep" ${('', 'selected="selected"')[flatten_folders_value == None]}>&lt; Keep &gt;</option>
+                                        <option value="keep" ${('', 'selected="selected"')[flatten_folders_value is None]}>&lt; Keep &gt;</option>
                                         <option value="enable" ${('', 'selected="selected"')[flatten_folders_value == 0]}>Yes</option>
                                         <option value="disable" ${('', 'selected="selected"')[flatten_folders_value == 1]}>No</option>
                                     </select><br>
@@ -165,7 +165,7 @@
                                 <span class="component-title">Paused</span>
                                 <span class="component-desc">
                                     <select id="edit_paused" name="paused" class="form-control form-control-inline input-sm">
-                                        <option value="keep" ${('', 'selected="selected"')[paused_value == None]}>&lt; Keep &gt;</option>
+                                        <option value="keep" ${('', 'selected="selected"')[paused_value is None]}>&lt; Keep &gt;</option>
                                         <option value="enable" ${('', 'selected="selected"')[paused_value == 1]}>Yes</option>
                                         <option value="disable" ${('', 'selected="selected"')[paused_value == 0]}>No</option>
                                     </select><br/ >
@@ -194,7 +194,7 @@
                                 <span class="component-title">Scene Numbering</span>
                                 <span class="component-desc">
                                     <select id="edit_scene" name="scene" class="form-control form-control-inline input-sm">
-                                        <option value="keep" ${('', 'selected="selected"')[scene_value == None]}>&lt; Keep &gt;</option>
+                                        <option value="keep" ${('', 'selected="selected"')[scene_value is None]}>&lt; Keep &gt;</option>
                                         <option value="enable" ${('', 'selected="selected"')[scene_value == 1]}>Yes</option>
                                         <option value="disable" ${('', 'selected="selected"')[scene_value == 0]}>No</option>
                                     </select><br>
@@ -208,7 +208,7 @@
                                 <span class="component-title">Anime</span>
                                 <span class="component-desc">
                                     <select id="edit_anime" name="anime" class="form-control form-control-inline input-sm">
-                                        <option value="keep" ${('', 'selected="selected"')[anime_value == None]}>&lt; Keep &gt;</option>
+                                        <option value="keep" ${('', 'selected="selected"')[anime_value is None]}>&lt; Keep &gt;</option>
                                         <option value="enable" ${('', 'selected="selected"')[anime_value == 1]}>Yes</option>
                                         <option value="disable" ${('', 'selected="selected"')[anime_value == 0]}>No</option>
                                     </select><br>
@@ -222,7 +222,7 @@
                                 <span class="component-title">Sports</span>
                                 <span class="component-desc">
                                     <select id="edit_sports" name="sports" class="form-control form-control-inline input-sm">
-                                        <option value="keep" ${('', 'selected="selected"')[sports_value == None]}>&lt; Keep &gt;</option>
+                                        <option value="keep" ${('', 'selected="selected"')[sports_value is None]}>&lt; Keep &gt;</option>
                                         <option value="enable" ${('', 'selected="selected"')[sports_value == 1]}>Yes</option>
                                         <option value="disable" ${('', 'selected="selected"')[sports_value == 0]}>No</option>
                                     </select><br>
@@ -237,7 +237,7 @@
                                 <span class="component-title">Air by date</span>
                                 <span class="component-desc">
                                     <select id="edit_air_by_date" name="air_by_date" class="form-control form-control-inline input-sm">
-                                        <option value="keep" ${('', 'selected="selected"')[air_by_date_value == None]}>&lt; Keep &gt;</option>
+                                        <option value="keep" ${('', 'selected="selected"')[air_by_date_value is None]}>&lt; Keep &gt;</option>
                                         <option value="enable" ${('', 'selected="selected"')[air_by_date_value == 1]}>Yes</option>
                                         <option value="disable" ${('', 'selected="selected"')[air_by_date_value == 0]}>No</option>
                                     </select><br>
@@ -252,7 +252,7 @@
                                 <span class="component-title">Subtitles</span>
                                 <span class="component-desc">
                                     <select id="edit_subtitles" name="subtitles" class="form-control form-control-inline input-sm">
-                                        <option value="keep" ${('', 'selected="selected"')[subtitles_value == None]}>&lt; Keep &gt;</option>
+                                        <option value="keep" ${('', 'selected="selected"')[subtitles_value is None]}>&lt; Keep &gt;</option>
                                         <option value="enable" ${('', 'selected="selected"')[subtitles_value == 1]}>Yes</option>
                                         <option value="disable" ${('', 'selected="selected"')[subtitles_value == 0]}>No</option>
                                     </select><br>
diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py
index 765cb7626f51adc6a439087e0f43185bb0fce420..0ddd3a8a710040211449176321a59bc7fbd19229 100644
--- a/sickbeard/__init__.py
+++ b/sickbeard/__init__.py
@@ -376,6 +376,7 @@ PROWL_NOTIFY_ONDOWNLOAD = False
 PROWL_NOTIFY_ONSUBTITLEDOWNLOAD = False
 PROWL_API = None
 PROWL_PRIORITY = 0
+PROWL_MESSAGE_TITLE = 'SickRage'
 
 USE_TWITTER = False
 TWITTER_NOTIFY_ONSNATCH = False
@@ -508,7 +509,6 @@ TIMEZONE_DISPLAY = None
 THEME_NAME = None
 POSTER_SORTBY = None
 POSTER_SORTDIR = None
-FILTER_ROW = True
 
 USE_SUBTITLES = False
 SUBTITLES_LANGUAGES = []
@@ -586,7 +586,7 @@ def initialize(consoleLogging=True):
             NEWZNAB_DATA, NZBS, NZBS_UID, NZBS_HASH, INDEXER_DEFAULT, INDEXER_TIMEOUT, USENET_RETENTION, TORRENT_DIR, \
             QUALITY_DEFAULT, FLATTEN_FOLDERS_DEFAULT, SUBTITLES_DEFAULT, STATUS_DEFAULT, STATUS_DEFAULT_AFTER, \
             GROWL_NOTIFY_ONSNATCH, GROWL_NOTIFY_ONDOWNLOAD, GROWL_NOTIFY_ONSUBTITLEDOWNLOAD, TWITTER_NOTIFY_ONSNATCH, TWITTER_NOTIFY_ONDOWNLOAD, TWITTER_NOTIFY_ONSUBTITLEDOWNLOAD, USE_FREEMOBILE, FREEMOBILE_ID, FREEMOBILE_APIKEY, FREEMOBILE_NOTIFY_ONSNATCH, FREEMOBILE_NOTIFY_ONDOWNLOAD, FREEMOBILE_NOTIFY_ONSUBTITLEDOWNLOAD, \
-            USE_GROWL, GROWL_HOST, GROWL_PASSWORD, USE_PROWL, PROWL_NOTIFY_ONSNATCH, PROWL_NOTIFY_ONDOWNLOAD, PROWL_NOTIFY_ONSUBTITLEDOWNLOAD, PROWL_API, PROWL_PRIORITY, \
+            USE_GROWL, GROWL_HOST, GROWL_PASSWORD, USE_PROWL, PROWL_NOTIFY_ONSNATCH, PROWL_NOTIFY_ONDOWNLOAD, PROWL_NOTIFY_ONSUBTITLEDOWNLOAD, PROWL_API, PROWL_PRIORITY, PROWL_MESSAGE_TITLE, \
             USE_PYTIVO, PYTIVO_NOTIFY_ONSNATCH, PYTIVO_NOTIFY_ONDOWNLOAD, PYTIVO_NOTIFY_ONSUBTITLEDOWNLOAD, PYTIVO_UPDATE_LIBRARY, PYTIVO_HOST, PYTIVO_SHARE_NAME, PYTIVO_TIVO_NAME, \
             USE_NMA, NMA_NOTIFY_ONSNATCH, NMA_NOTIFY_ONDOWNLOAD, NMA_NOTIFY_ONSUBTITLEDOWNLOAD, NMA_API, NMA_PRIORITY, \
             USE_PUSHALOT, PUSHALOT_NOTIFY_ONSNATCH, PUSHALOT_NOTIFY_ONDOWNLOAD, PUSHALOT_NOTIFY_ONSUBTITLEDOWNLOAD, PUSHALOT_AUTHORIZATIONTOKEN, \
@@ -605,7 +605,7 @@ def initialize(consoleLogging=True):
             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_LISTVIEW, METADATA_KODI, METADATA_KODI_12PLUS, METADATA_MEDIABROWSER, METADATA_PS3, metadata_provider_dict, \
             NEWZBIN, NEWZBIN_USERNAME, NEWZBIN_PASSWORD, GIT_PATH, MOVE_ASSOCIATED_FILES, SYNC_FILES, POSTPONE_IF_SYNC_FILES, POSTPONE_IF_NO_SUBS, dailySearchScheduler, NFO_RENAME, \
-            GUI_NAME, HOME_LAYOUT, HISTORY_LAYOUT, DISPLAY_SHOW_SPECIALS, COMING_EPS_LAYOUT, COMING_EPS_SORT, COMING_EPS_DISPLAY_PAUSED, COMING_EPS_MISSED_RANGE, FUZZY_DATING, TRIM_ZERO, DATE_PRESET, TIME_PRESET, TIME_PRESET_W_SECONDS, THEME_NAME, FILTER_ROW, \
+            GUI_NAME, HOME_LAYOUT, HISTORY_LAYOUT, DISPLAY_SHOW_SPECIALS, COMING_EPS_LAYOUT, COMING_EPS_SORT, COMING_EPS_DISPLAY_PAUSED, COMING_EPS_MISSED_RANGE, FUZZY_DATING, TRIM_ZERO, DATE_PRESET, TIME_PRESET, TIME_PRESET_W_SECONDS, THEME_NAME, \
             POSTER_SORTBY, POSTER_SORTDIR, HISTORY_LIMIT, CREATE_MISSING_SHOW_DIRS, ADD_SHOWS_WO_DIR, \
             METADATA_WDTV, METADATA_TIVO, METADATA_MEDE8ER, IGNORE_WORDS, TRACKERS_LIST, IGNORED_SUBS_LIST, REQUIRE_WORDS, CALENDAR_UNPROTECTED, CALENDAR_ICONS, NO_RESTART, \
             USE_SUBTITLES, SUBTITLES_LANGUAGES, SUBTITLES_DIR, SUBTITLES_SERVICES_LIST, SUBTITLES_SERVICES_ENABLED, SUBTITLES_HISTORY, SUBTITLES_FINDER_FREQUENCY, SUBTITLES_MULTI, EMBEDDED_SUBTITLES_ALL, SUBTITLES_EXTRA_SCRIPTS, subtitlesFinderScheduler, \
@@ -1018,6 +1018,7 @@ def initialize(consoleLogging=True):
         PROWL_NOTIFY_ONSUBTITLEDOWNLOAD = bool(check_setting_int(CFG, 'Prowl', 'prowl_notify_onsubtitledownload', 0))
         PROWL_API = check_setting_str(CFG, 'Prowl', 'prowl_api', '', censor_log=True)
         PROWL_PRIORITY = check_setting_str(CFG, 'Prowl', 'prowl_priority', "0")
+        PROWL_MESSAGE_TITLE = check_setting_str(CFG, 'Prowl', 'prowl_message_title', "SickRage")
 
         USE_TWITTER = bool(check_setting_int(CFG, 'Twitter', 'use_twitter', 0))
         TWITTER_NOTIFY_ONSNATCH = bool(check_setting_int(CFG, 'Twitter', 'twitter_notify_onsnatch', 0))
@@ -1209,7 +1210,6 @@ def initialize(consoleLogging=True):
         TIMEZONE_DISPLAY = check_setting_str(CFG, 'GUI', 'timezone_display', 'local')
         POSTER_SORTBY = check_setting_str(CFG, 'GUI', 'poster_sortby', 'name')
         POSTER_SORTDIR = check_setting_int(CFG, 'GUI', 'poster_sortdir', 1)
-        FILTER_ROW = bool(check_setting_int(CFG, 'GUI', 'filter_row', 1))
         DISPLAY_ALL_SEASONS = bool(check_setting_int(CFG, 'General', 'display_all_seasons', 1))
 
         # initialize NZB and TORRENT providers
@@ -1226,6 +1226,9 @@ def initialize(consoleLogging=True):
                                    curProvider.providerType == GenericProvider.TORRENT]:
             curTorrentProvider.enabled = bool(check_setting_int(CFG, curTorrentProvider.getID().upper(),
                                                                 curTorrentProvider.getID(), 0))
+            if hasattr(curTorrentProvider, 'custom_url'):
+                curTorrentProvider.custom_url = check_setting_str(CFG, curTorrentProvider.getID().upper(),
+                                                                curTorrentProvider.getID() + '_custom_url', '', censor_log=True)                                           
             if hasattr(curTorrentProvider, 'api_key'):
                 curTorrentProvider.api_key = check_setting_str(CFG, curTorrentProvider.getID().upper(),
                                                                curTorrentProvider.getID() + '_api_key', '', censor_log=True)
@@ -1787,6 +1790,9 @@ def save_config():
                                curProvider.providerType == GenericProvider.TORRENT]:
         new_config[curTorrentProvider.getID().upper()] = {}
         new_config[curTorrentProvider.getID().upper()][curTorrentProvider.getID()] = int(curTorrentProvider.enabled)
+        if hasattr(curTorrentProvider, 'custom_url'):
+            new_config[curTorrentProvider.getID().upper()][
+                curTorrentProvider.getID() + '_custom_url'] = curTorrentProvider.custom_url
         if hasattr(curTorrentProvider, 'digest'):
             new_config[curTorrentProvider.getID().upper()][
                 curTorrentProvider.getID() + '_digest'] = curTorrentProvider.digest
@@ -1980,6 +1986,7 @@ def save_config():
     new_config['Prowl']['prowl_notify_onsubtitledownload'] = int(PROWL_NOTIFY_ONSUBTITLEDOWNLOAD)
     new_config['Prowl']['prowl_api'] = PROWL_API
     new_config['Prowl']['prowl_priority'] = PROWL_PRIORITY
+    new_config['Prowl']['prowl_message_title'] = PROWL_MESSAGE_TITLE
 
     new_config['Twitter'] = {}
     new_config['Twitter']['use_twitter'] = int(USE_TWITTER)
@@ -2125,7 +2132,6 @@ def save_config():
     new_config['GUI']['timezone_display'] = TIMEZONE_DISPLAY
     new_config['GUI']['poster_sortby'] = POSTER_SORTBY
     new_config['GUI']['poster_sortdir'] = POSTER_SORTDIR
-    new_config['GUI']['filter_row'] = int(FILTER_ROW)
 
     new_config['Subtitles'] = {}
     new_config['Subtitles']['use_subtitles'] = int(USE_SUBTITLES)
@@ -2182,13 +2188,13 @@ def launchBrowser(protocol='http', startPort=None, web_root='/'):
 
 
 def getEpList(epIDs, showid=None):
-    if epIDs == None or len(epIDs) == 0:
+    if epIDs is None or len(epIDs) == 0:
         return []
 
     query = "SELECT * FROM tv_episodes WHERE indexerid in (%s)" % (",".join(['?'] * len(epIDs)),)
     params = epIDs
 
-    if showid != None:
+    if showid is not None:
         query += " AND showid = ?"
         params.append(showid)
 
diff --git a/sickbeard/common.py b/sickbeard/common.py
index b9ae20bb365b84496b67eb17394c2589a65079cd..a23afacfe6f39bf424f9e473c0ab1082204450ee 100644
--- a/sickbeard/common.py
+++ b/sickbeard/common.py
@@ -54,24 +54,16 @@ if SPOOF_USER_AGENT:
     shuffle(user_agents)
     USER_AGENT = user_agents[0]
 
-mediaExtensions = ['avi', 'mkv', 'mpg', 'mpeg', 'wmv',
-                   'ogm', 'mp4', 'iso', 'img', 'divx',
-                   'm2ts', 'm4v', 'ts', 'flv', 'f4v',
-                   'mov', 'rmvb', 'vob', 'dvr-ms', 'wtv',
-                   'ogv', '3gp', 'webm', 'tp']
-
-subtitleExtensions = ['srt', 'sub', 'ass', 'idx', 'ssa']
-
 cpu_presets = {'HIGH': 5,
                'NORMAL': 2,
                'LOW': 1
               }
 
-### Other constants
+# Other constants
 MULTI_EP_RESULT = -1
 SEASON_RESULT = -2
 
-### Notification Types
+# Notification Types
 NOTIFY_SNATCH = 1
 NOTIFY_DOWNLOAD = 2
 NOTIFY_SUBTITLE_DOWNLOAD = 3
@@ -85,7 +77,7 @@ notifyStrings[NOTIFY_SUBTITLE_DOWNLOAD] = "Subtitle Download Finished"
 notifyStrings[NOTIFY_GIT_UPDATE] = "SickRage Updated"
 notifyStrings[NOTIFY_GIT_UPDATE_TEXT] = "SickRage Updated To Commit#: "
 
-### Episode statuses
+# Episode statuses
 UNKNOWN = -1  # should never happen
 UNAIRED = 1  # episodes that haven't aired yet
 SNATCHED = 2  # qualified with quality
@@ -572,6 +564,7 @@ statusStrings = StatusStrings(
      }
 )
 
+
 # pylint: disable=R0903
 class Overview(object):
     UNAIRED = UNAIRED  # 1
@@ -590,11 +583,6 @@ class Overview(object):
                        UNAIRED: "unaired",
                        SNATCHED: "snatched"}
 
-
-# Get our xml namespaces correct for lxml
-XML_NSMAP = {'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
-             'xsd': 'http://www.w3.org/2001/XMLSchema'}
-
 countryList = {'Australia': 'AU',
                'Canada': 'CA',
                'USA': 'US'
diff --git a/sickbeard/config.py b/sickbeard/config.py
index 4bc496076ab559335b4dba553cac2b5f6238037d..f6766920a604dbeb0782f927e802cd8c4896fc7f 100644
--- a/sickbeard/config.py
+++ b/sickbeard/config.py
@@ -120,7 +120,7 @@ def change_LOG_DIR(log_dir, web_log):
         else:
             return False
 
-    if sickbeard.WEB_LOG != web_log_value or log_dir_changed == True:
+    if sickbeard.WEB_LOG != web_log_value or log_dir_changed is True:
         sickbeard.WEB_LOG = web_log_value
 
     return True
@@ -282,7 +282,7 @@ def change_VERSION_NOTIFY(version_notify):
     if not version_notify:
         sickbeard.NEWEST_VERSION_STRING = None
 
-    if oldSetting == False and version_notify == True:
+    if oldSetting is False and version_notify is True:
         sickbeard.versionCheckScheduler.forceRun()
 
 def change_DOWNLOAD_PROPERS(download_propers):
@@ -595,7 +595,8 @@ def check_setting_str(config, cfg_name, item_name, def_val, silent=True, censor_
             config[cfg_name][item_name] = helpers.encrypt(my_val, encryption_version)
 
     if censor_log or (cfg_name, item_name) in logger.censoredItems.iteritems():
-        logger.censoredItems[cfg_name, item_name] = my_val
+        if not item_name.endswith('custom_url'):
+            logger.censoredItems[cfg_name, item_name] = my_val
 
     if not silent:
         logger.log(item_name + " -> " + my_val, logger.DEBUG)
diff --git a/sickbeard/db.py b/sickbeard/db.py
index 95d2add4e8e613c46cdcd142bf77493017ffdfc4..92d5e08189b2a6bcfe2717286c783a42941ed56a 100644
--- a/sickbeard/db.py
+++ b/sickbeard/db.py
@@ -183,7 +183,7 @@ class DBConnection(object):
         :param fetchone: Boolean to indicate one result must be fetched (to walk results for instance)
         :return: query results
         """
-        if query == None:
+        if query is None:
             return
 
         sqlResult = None
@@ -192,7 +192,7 @@ class DBConnection(object):
         with db_locks[self.filename]:
             while attempt < 5:
                 try:
-                    if args == None:
+                    if args is None:
                         logger.log(self.filename + ": " + query, logger.DB)
                     else:
                         logger.log(self.filename + ": " + query + " with args " + str(args), logger.DB)
@@ -229,7 +229,7 @@ class DBConnection(object):
 
         sqlResults = self.action(query, args, fetchall=True)
 
-        if sqlResults == None:
+        if sqlResults is None:
             return []
 
         return sqlResults
@@ -244,7 +244,7 @@ class DBConnection(object):
         """
         sqlResults = self.action(query, args, fetchone=True)
 
-        if sqlResults == None:
+        if sqlResults is None:
             return []
 
         return sqlResults
diff --git a/sickbeard/helpers.py b/sickbeard/helpers.py
index 035501b454909ae3764c8fc8877007ecdd3e8550..c3a77d1906dae914984d8b7f37cbffe5147cd598 100644
--- a/sickbeard/helpers.py
+++ b/sickbeard/helpers.py
@@ -51,12 +51,11 @@ from socket import timeout as SocketTimeout
 
 from sickbeard import logger, classes
 from sickbeard.common import USER_AGENT
-from sickbeard.common import mediaExtensions
-from sickbeard.common import subtitleExtensions
 from sickbeard import db
 from sickbeard.notifiers.synoindex import notifier as synoindex_notifier
 from sickbeard import clients
 from sickbeard.subtitles import isValidLanguage
+from sickrage.helper.common import media_extensions, subtitle_extensions
 from sickrage.helper.encoding import ek
 from sickrage.helper.exceptions import ex, MultipleShowObjectsException
 from cachecontrol import CacheControl, caches
@@ -105,7 +104,7 @@ def remove_extension(name):
     if name and "." in name:
         # pylint: disable=W0612
         base_name, sep, extension = name.rpartition('.')  # @UnusedVariable
-        if base_name and extension.lower() in ['nzb', 'torrent'] + mediaExtensions:
+        if base_name and extension.lower() in ['nzb', 'torrent'] + media_extensions:
             name = base_name
 
     return name
@@ -168,7 +167,7 @@ def remove_non_release_groups(name):
         elif remove_type == 'searchre':
             _name = re.sub(r'(?i)' + remove_string, '', _name)
 
-    return _name.strip('.- []{}')
+    return _name
 
 
 def replaceExtension(filename, newExt):
@@ -244,7 +243,7 @@ def isMediaFile(filename):
     if re.search('extras?$', sepFile[0], re.I):
         return False
 
-    if sepFile[2].lower() in mediaExtensions:
+    if sepFile[2].lower() in media_extensions:
         return True
     else:
         return False
@@ -649,7 +648,7 @@ def rename_ep_file(cur_path, new_path, old_path_length=0):
         cur_file_ext = cur_path[old_path_length:]
         cur_file_name = cur_path[:old_path_length]
 
-    if cur_file_ext[1:] in subtitleExtensions:
+    if cur_file_ext[1:] in subtitle_extensions:
         # Extract subtitle language from filename
         sublang = os.path.splitext(cur_file_name)[1][1:]
 
diff --git a/sickbeard/history.py b/sickbeard/history.py
index accf006f380ab2a278bf2c277443a123561717f0..e0a4b1f44f7fc841dc967057f1a8503181a49c8c 100644
--- a/sickbeard/history.py
+++ b/sickbeard/history.py
@@ -62,7 +62,7 @@ def logSnatch(searchResult):
         version = searchResult.version
 
         providerClass = searchResult.provider
-        if providerClass != None:
+        if providerClass is not None:
             provider = providerClass.name
         else:
             provider = "unknown"
diff --git a/sickbeard/image_cache.py b/sickbeard/image_cache.py
index 10c1d2e416d9c50f56a03c43f4ee1a7416fd1ab9..7ab282fee4c344aba20106db3e39662aab38a6fd 100644
--- a/sickbeard/image_cache.py
+++ b/sickbeard/image_cache.py
@@ -289,7 +289,7 @@ class ImageCache:
                         cur_file_name = os.path.abspath(cur_provider.get_poster_path(show_obj))
                         cur_file_type = self.which_type(cur_file_name)
 
-                        if cur_file_type == None:
+                        if cur_file_type is None:
                             logger.log(u"Unable to retrieve image type, not using the image from " + str(cur_file_name),
                                        logger.WARNING)
                             continue
diff --git a/sickbeard/naming.py b/sickbeard/naming.py
index 1e3c0fe62f76a3856769f1f6668275474785360f..9b8db1d06a1492501059e061a365060d259ac407 100644
--- a/sickbeard/naming.py
+++ b/sickbeard/naming.py
@@ -123,15 +123,15 @@ def check_force_season_folders(pattern=None, multi=None, anime_type=None):
 
     :return: true if season folders need to be forced on or false otherwise.
     """
-    if pattern == None:
+    if pattern is None:
         pattern = sickbeard.NAMING_PATTERN
 
-    if anime_type == None:
+    if anime_type is None:
         anime_type = sickbeard.NAMING_ANIME
 
     valid = not validate_name(pattern, None, anime_type, file_only=True)
 
-    if multi != None:
+    if multi is not None:
         valid = valid or not validate_name(pattern, multi, anime_type, file_only=True)
 
     return valid
@@ -143,16 +143,16 @@ def check_valid_naming(pattern=None, multi=None, anime_type=None):
 
     :return: true if the naming is valid, false if not.
     """
-    if pattern == None:
+    if pattern is None:
         pattern = sickbeard.NAMING_PATTERN
 
-    if anime_type == None:
+    if anime_type is None:
         anime_type = sickbeard.NAMING_ANIME
 
     logger.log(u"Checking whether the pattern " + pattern + " is valid for a single episode", logger.DEBUG)
     valid = validate_name(pattern, None, anime_type)
 
-    if multi != None:
+    if multi is not None:
         logger.log(u"Checking whether the pattern " + pattern + " is valid for a multi episode", logger.DEBUG)
         valid = valid and validate_name(pattern, multi, anime_type)
 
@@ -165,7 +165,7 @@ def check_valid_abd_naming(pattern=None):
 
     :return: true if the naming is valid, false if not.
     """
-    if pattern == None:
+    if pattern is None:
         pattern = sickbeard.NAMING_PATTERN
 
     logger.log(u"Checking whether the pattern " + pattern + " is valid for an air-by-date episode", logger.DEBUG)
@@ -180,7 +180,7 @@ def check_valid_sports_naming(pattern=None):
 
     :return: true if the naming is valid, false if not.
     """
-    if pattern == None:
+    if pattern is None:
         pattern = sickbeard.NAMING_PATTERN
 
     logger.log(u"Checking whether the pattern " + pattern + " is valid for an sports episode", logger.DEBUG)
@@ -263,7 +263,7 @@ def generate_sample_ep(multi=None, abd=False, sports=False, anime_type=None):
         else:
             ep._release_name = 'Show.Name.S02E03.HDTV.XviD-RLSGROUP'
 
-    if multi != None:
+    if multi is not None:
         ep._name = "Ep Name (1)"
 
         if anime_type != 3:
diff --git a/sickbeard/notifiers/emailnotify.py b/sickbeard/notifiers/emailnotify.py
index 03dbdbc1e332aa5fa714b6a7eea58af7c4bbd870..34249c92de064129a725b9fcab8a7d0e73b216ab 100644
--- a/sickbeard/notifiers/emailnotify.py
+++ b/sickbeard/notifiers/emailnotify.py
@@ -20,9 +20,12 @@
 #
 # You should have received a copy of the GNU General Public License
 # along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
 
 import smtplib
 import traceback
+import ast
 from email.mime.multipart import MIMEMultipart
 from email.mime.text import MIMEText
 from email.utils import formatdate
@@ -61,7 +64,7 @@ class EmailNotifier(object):
             show = self._parseEp(ep_name)
             to = self._generate_recipients(show)
             if len(to) == 0:
-                logger.log(u'Skipping email notify because there are no configured recipients', logger.WARNING)
+                logger.log(u'Skipping email notify because there are no configured recipients', logger.DEBUG)
             else:
                 try:
                     msg = MIMEMultipart('alternative')
@@ -100,7 +103,7 @@ class EmailNotifier(object):
             show = self._parseEp(ep_name)
             to = self._generate_recipients(show)
             if len(to) == 0:
-                logger.log(u'Skipping email notify because there are no configured recipients', logger.WARNING)
+                logger.log(u'Skipping email notify because there are no configured recipients', logger.DEBUG)
             else:
                 try:
                     msg = MIMEMultipart('alternative')
@@ -139,7 +142,7 @@ class EmailNotifier(object):
             show = self._parseEp(ep_name)
             to = self._generate_recipients(show)
             if len(to) == 0:
-                logger.log(u'Skipping email notify because there are no configured recipients', logger.WARNING)
+                logger.log(u'Skipping email notify because there are no configured recipients', logger.DEBUG)
             else:
                 try:
                     msg = MIMEMultipart('alternative')
@@ -169,20 +172,28 @@ class EmailNotifier(object):
 
     def _generate_recipients(self, show):
         addrs = []
+        myDB = db.DBConnection()
 
         # Grab the global recipients
-        for addr in sickbeard.EMAIL_LIST.split(','):
-            if len(addr.strip()) > 0:
-                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 sickbeard.EMAIL_LIST:
+            for addr in sickbeard.EMAIL_LIST.split(','):
+                if len(addr.strip()) > 0:
+                    addrs.append(addr)
+
+        # Grab the per-show-notification recipients
+        if show is not None:
+            for s in show:
+                for subs in myDB.select("SELECT notify_list FROM tv_shows WHERE show_name = ?", (s,)):
+                    if subs['notify_list']:
+                        if subs['notify_list'][0] == '{':
+                            entries = dict(ast.literal_eval(subs['notify_list']))
+                            for addr in entries['emails'].split(','):
+                                if len(addr.strip()) > 0:
+                                    addrs.append(addr)
+                        else:                                           # Legacy
+                            for addr in subs['notify_list'].split(','):
+                                if len(addr.strip()) > 0:
+                                    addrs.append(addr)
 
         addrs = set(addrs)
         logger.log(u'Notification recipients: %s' % addrs, logger.DEBUG)
diff --git a/sickbeard/notifiers/prowl.py b/sickbeard/notifiers/prowl.py
index 20c40c47b18ae842ba0400a1cd602eab3c95b8e1..8f1bde7513000c7c962459a29398ba97c36bfb4c 100644
--- a/sickbeard/notifiers/prowl.py
+++ b/sickbeard/notifiers/prowl.py
@@ -17,6 +17,8 @@
 #
 # You should have received a copy of the GNU General Public License
 # along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
 
 import socket
 from httplib import HTTPSConnection, HTTPException
@@ -31,51 +33,103 @@ except ImportError:
         pass
 
 import sickbeard
+import time
+import ast
 
-from sickbeard import logger, common
-
+from sickbeard import logger, common, db
+from sickrage.helper.encoding import ss
 
 class ProwlNotifier(object):
     def test_notify(self, prowl_api, prowl_priority):
-        return self._sendProwl(prowl_api, prowl_priority, event="Test",
-                               message="Testing Prowl settings from SickRage", force=True)
+        return self._send_prowl(prowl_api, prowl_priority, event="Test", message="Testing Prowl settings from SickRage", force=True)
 
     def notify_snatch(self, ep_name):
+        ep_name = ss(ep_name)
         if sickbeard.PROWL_NOTIFY_ONSNATCH:
-            self._sendProwl(prowl_api=None, prowl_priority=None, event=common.notifyStrings[common.NOTIFY_SNATCH],
-                            message=ep_name)
+            show = self._parse_episode(ep_name)
+            recipients = self._generate_recipients(show)
+            if len(recipients) == 0:
+                logger.log('Skipping prowl notify because there are no configured recipients', logger.DEBUG)
+            else:
+                for api in recipients:
+                    self._send_prowl(prowl_api=api, prowl_priority=None, event=common.notifyStrings[common.NOTIFY_SNATCH],
+                                     message=ep_name+" :: "+time.strftime(sickbeard.DATE_PRESET+" "+sickbeard.TIME_PRESET))
 
     def notify_download(self, ep_name):
+        ep_name = ss(ep_name)
         if sickbeard.PROWL_NOTIFY_ONDOWNLOAD:
-            self._sendProwl(prowl_api=None, prowl_priority=None, event=common.notifyStrings[common.NOTIFY_DOWNLOAD],
-                            message=ep_name)
+            show = self._parse_episode(ep_name)
+            recipients = self._generate_recipients(show)
+            if len(recipients) == 0:
+                logger.log('Skipping prowl notify because there are no configured recipients', logger.DEBUG)
+            else:
+                for api in recipients:
+                    self._send_prowl(prowl_api=api, prowl_priority=None, event=common.notifyStrings[common.NOTIFY_DOWNLOAD],
+                                     message=ep_name+" :: "+time.strftime(sickbeard.DATE_PRESET+" "+sickbeard.TIME_PRESET))
 
     def notify_subtitle_download(self, ep_name, lang):
+        ep_name = ss(ep_name)
         if sickbeard.PROWL_NOTIFY_ONSUBTITLEDOWNLOAD:
-            self._sendProwl(prowl_api=None, prowl_priority=None,
-                            event=common.notifyStrings[common.NOTIFY_SUBTITLE_DOWNLOAD], message=ep_name + ": " + lang)
-                            
+            show = self._parse_episode(ep_name)
+            recipients = self._generate_recipients(show)
+            if len(recipients) == 0:
+                logger.log('Skipping prowl notify because there are no configured recipients', logger.DEBUG)
+            else:
+                for api in recipients:
+                    self._send_prowl(prowl_api=api, prowl_priority=None, event=common.notifyStrings[common.NOTIFY_SUBTITLE_DOWNLOAD],
+                                     message=ep_name+" ["+lang+"] :: "+time.strftime(sickbeard.DATE_PRESET+" "+sickbeard.TIME_PRESET))
+
     def notify_git_update(self, new_version="??"):
         if sickbeard.USE_PROWL:
             update_text = common.notifyStrings[common.NOTIFY_GIT_UPDATE_TEXT]
             title = common.notifyStrings[common.NOTIFY_GIT_UPDATE]
-            self._sendProwl(prowl_api=None, prowl_priority=None,
-                            event=title, message=update_text + new_version)
-
-    def _sendProwl(self, prowl_api=None, prowl_priority=None, event=None, message=None, force=False):
+            self._send_prowl(prowl_api=None, prowl_priority=None,
+                             event=title, message=update_text + new_version)
+
+
+    @staticmethod
+    def _generate_recipients(show=None):
+        apis = []
+        mydb = db.DBConnection()
+
+        # Grab the global recipient(s)
+        if sickbeard.PROWL_API:
+            for api in sickbeard.PROWL_API.split(','):
+                if len(api.strip()) > 0:
+                    apis.append(api)
+
+        # Grab the per-show-notification recipients
+        if show is not None:
+            for value in show:
+                for subs in mydb.select("SELECT notify_list FROM tv_shows WHERE show_name = ?", (value,)):
+                    if subs['notify_list']:
+                        if subs['notify_list'][0] == '{':               # legacy format handling
+                            entries = dict(ast.literal_eval(subs['notify_list']))
+                            for api in entries['prowlAPIs'].split(','):
+                                if len(api.strip()) > 0:
+                                    apis.append(api)
+
+        apis = set(apis)
+        return apis
+
+    @staticmethod
+    def _send_prowl(prowl_api=None, prowl_priority=None, event=None, message=None, force=False):
 
         if not sickbeard.USE_PROWL and not force:
             return False
 
         if prowl_api is None:
             prowl_api = sickbeard.PROWL_API
+            if len(prowl_api) == 0:
+                return False
 
         if prowl_priority is None:
             prowl_priority = sickbeard.PROWL_PRIORITY
 
-        title = "SickRage"
+        title = sickbeard.PROWL_MESSAGE_TITLE
 
-        logger.log(u"PROWL: Sending notice with details: event=\"%s\", message=\"%s\", priority=%s, api=%s" % (event, message, prowl_priority, prowl_api), logger.DEBUG)
+        logger.log(u"PROWL: Sending notice with details: title=\"%s\" event=\"%s\", message=\"%s\", priority=%s, api=%s"
+                   % (title, event, message, prowl_priority, prowl_api), logger.DEBUG)
 
         http_handler = HTTPSConnection("api.prowlapp.com")
 
@@ -106,5 +160,14 @@ class ProwlNotifier(object):
             logger.log(u"Prowl notification failed.", logger.ERROR)
             return False
 
+    @staticmethod
+    def _parse_episode(ep_name):
+        ep_name = ss(ep_name)
+
+        sep = " - "
+        titles = ep_name.split(sep)
+        titles.sort(key=len, reverse=True)
+        logger.log("TITLES: %s" % titles, logger.DEBUG)
+        return titles
 
 notifier = ProwlNotifier
diff --git a/sickbeard/notifiers/pushover.py b/sickbeard/notifiers/pushover.py
index f4f102b0bbaa3ef526f2282a815b6a35c0345445..1dc232f16ed34ec2d9172aaa9b695d978070f1c6 100644
--- a/sickbeard/notifiers/pushover.py
+++ b/sickbeard/notifiers/pushover.py
@@ -47,13 +47,13 @@ class PushoverNotifier(object):
         returns: True if the message succeeded, False otherwise
         """
 
-        if userKey == None:
+        if userKey is None:
             userKey = sickbeard.PUSHOVER_USERKEY
 
-        if apiKey == None:
+        if apiKey is None:
             apiKey = sickbeard.PUSHOVER_APIKEY
 
-        if sound == None:
+        if sound is None:
             sound = sickbeard.PUSHOVER_SOUND
 
         logger.log(u"Pushover API KEY in use: " + apiKey, logger.DEBUG)
diff --git a/sickbeard/nzbSplitter.py b/sickbeard/nzbSplitter.py
index 09cb3df81ab386d9a3a29ce469d48acafb91d9e2..311443aa5077f21f403276a03dcd8f446daf44a6 100644
--- a/sickbeard/nzbSplitter.py
+++ b/sickbeard/nzbSplitter.py
@@ -145,7 +145,7 @@ def splitResult(result):
         return False
 
     # bust it up
-    season = parse_result.season_number if parse_result.season_number != None else 1
+    season = parse_result.season_number if parse_result.season_number is not None else 1
 
     separateNZBs, xmlns = getSeasonNZBs(result.name, urlData, season)
 
@@ -167,8 +167,8 @@ def splitResult(result):
             return False
 
         # make sure the result is sane
-        if (parse_result.season_number != None and parse_result.season_number != season) or (
-                parse_result.season_number == None and season != 1):
+        if (parse_result.season_number is not None and parse_result.season_number != season) or (
+                parse_result.season_number is None and season != 1):
             logger.log(
                 u"Found " + newNZB + " inside " + result.name + " but it doesn't seem to belong to the same season, ignoring it",
                 logger.WARNING)
diff --git a/sickbeard/nzbget.py b/sickbeard/nzbget.py
index 809133a97952190fb636fe75c17ae3c4543133b0..5e12b521aac631e9e36b8b9290dd5dbaf6860891 100644
--- a/sickbeard/nzbget.py
+++ b/sickbeard/nzbget.py
@@ -47,8 +47,8 @@ def sendNZB(nzb, proper=False):
     else:
         nzbgetXMLrpc = "http://%(username)s:%(password)s@%(host)s/xmlrpc"
 
-    if sickbeard.NZBGET_HOST == None:
-        logger.log(u"No NZBget host found in configuration. Please configure it.", logger.ERROR)
+    if sickbeard.NZBGET_HOST is None:
+        logger.log(u"No NZBget host found in configuration. Please configure it.", logger.WARNING)
         return False
 
     url = nzbgetXMLrpc % {"host": sickbeard.NZBGET_HOST, "username": sickbeard.NZBGET_USERNAME,
@@ -59,17 +59,17 @@ def sendNZB(nzb, proper=False):
         if nzbGetRPC.writelog("INFO", "SickRage connected to drop of %s any moment now." % (nzb.name + ".nzb")):
             logger.log(u"Successful connected to NZBget", logger.DEBUG)
         else:
-            logger.log(u"Successful connected to NZBget, but unable to send a message", logger.ERROR)
+            logger.log(u"Successful connected to NZBget, but unable to send a message", logger.WARNING)
 
     except httplib.socket.error:
         logger.log(
             u"Please check your NZBget host and port (if it is running). NZBget is not responding to this combination",
-            logger.ERROR)
+            logger.WARNING)
         return False
 
     except xmlrpclib.ProtocolError, e:
         if e.errmsg == "Unauthorized":
-            logger.log(u"NZBget username or password is incorrect.", logger.ERROR)
+            logger.log(u"NZBget username or password is incorrect.", logger.WARNING)
         else:
             logger.log(u"Protocol Error: " + e.errmsg, logger.ERROR)
         return False
@@ -116,7 +116,7 @@ def sendNZB(nzb, proper=False):
                 if nzb.resultType == "nzb":
                     genProvider = GenericProvider("")
                     data = genProvider.getURL(nzb.url)
-                    if data == None:
+                    if data is None:
                         return False
                     nzbcontent64 = standard_b64encode(data)
                 nzbget_result = nzbGetRPC.append(nzb.name + ".nzb", category, addToTop, nzbcontent64)
@@ -146,8 +146,8 @@ def sendNZB(nzb, proper=False):
             logger.log(u"NZB sent to NZBget successfully", logger.DEBUG)
             return True
         else:
-            logger.log(u"NZBget could not add %s to the queue" % (nzb.name + ".nzb"), logger.ERROR)
+            logger.log(u"NZBget could not add %s to the queue" % (nzb.name + ".nzb"), logger.WARNING)
             return False
     except Exception:
-        logger.log(u"Connect Error to NZBget: could not add %s to the queue" % (nzb.name + ".nzb"), logger.ERROR)
+        logger.log(u"Connect Error to NZBget: could not add %s to the queue" % (nzb.name + ".nzb"), logger.WARNING)
         return False
diff --git a/sickbeard/postProcessor.py b/sickbeard/postProcessor.py
index ab01e21d2618b89ca3eb6e9186c8b4645c126c0c..de0602842985001cc4cd24ab4d0dc15b5864c63e 100644
--- a/sickbeard/postProcessor.py
+++ b/sickbeard/postProcessor.py
@@ -36,6 +36,7 @@ from sickbeard import notifiers
 from sickbeard import show_name_helpers
 from sickbeard import failed_history
 from sickbeard.name_parser.parser import NameParser, InvalidNameException, InvalidShowException
+from sickrage.helper.common import subtitle_extensions
 from sickrage.helper.encoding import ek
 from sickrage.helper.exceptions import EpisodeNotFoundException, EpisodePostProcessingFailedException, ex
 from sickrage.helper.exceptions import ShowDirectoryNotFoundException
@@ -192,18 +193,29 @@ class PostProcessor(object):
 
             checklist = glob.glob(ek(os.path.join, ek(os.path.dirname, globbable_file_path), '*')) # get a list of all the files in the folder
             for filefound in checklist: # loop through all the files in the folder, and check if they are the same name even when the cases don't match
+
                 file_name = filefound.rpartition('.')[0]
                 file_extension = filefound.rpartition('.')[2]
+                is_subtitle = None
+
+                if file_extension in subtitle_extensions:
+                    is_subtitle = True
+
                 if not base_name_only:
                     new_file_name = file_name + '.'
-                if new_file_name.lower() == base_name.lower().replace('[[]', '[').replace('[]]', ']'): # if there's no difference in the filename add it to the filelist
-                    filelist.append(filefound)
-                elif file_extension in common.subtitleExtensions:
+                    sub_file_name = file_name.rpartition('.')[0] + '.'
+                else:
+                    new_file_name = file_name
+                    sub_file_name = file_name.rpartition('.')[0]
+
+                if is_subtitle and sub_file_name.lower() == base_name.lower().replace('[[]', '[').replace('[]]', ']'):
                     language_extensions = tuple('.' + c for c in language_converters['opensubtitles'].codes)
                     if file_name.lower().endswith(language_extensions) and (len(filefound.rsplit('.', 2)[1]) is 2 or 3):
                         filelist.append(filefound)
                     elif file_name.lower().endswith('pt-br') and len(filefound.rsplit('.', 2)[1]) is 5:
                         filelist.append(filefound)
+                elif new_file_name.lower() == base_name.lower().replace('[[]', '[').replace('[]]', ']'): # if there's no difference in the filename add it to the filelist
+                    filelist.append(filefound)
 
         for associated_file_path in filelist:
             # only add associated to list
@@ -211,7 +223,7 @@ class PostProcessor(object):
                 continue
 
             # only list it if the only non-shared part is the extension or if it is a subtitle
-            if subtitles_only and not associated_file_path[len(associated_file_path) - 3:] in common.subtitleExtensions:
+            if subtitles_only and not associated_file_path[len(associated_file_path) - 3:] in subtitle_extensions:
                 continue
 
             # Exclude .rar files from associated list
@@ -221,7 +233,7 @@ class PostProcessor(object):
             # Add the extensions that the user doesn't allow to the 'extensions_to_delete' list
             if sickbeard.MOVE_ASSOCIATED_FILES and sickbeard.ALLOWED_EXTENSIONS:
                 allowed_extensions = sickbeard.ALLOWED_EXTENSIONS.split(",")
-                if not associated_file_path[-3:] in allowed_extensions and not associated_file_path[-3:] in common.subtitleExtensions:
+                if not associated_file_path[-3:] in allowed_extensions and not associated_file_path[-3:] in subtitle_extensions:
                     if ek(os.path.isfile, associated_file_path):
                         extensions_to_delete.append(associated_file_path)
 
@@ -324,7 +336,7 @@ class PostProcessor(object):
             cur_extension = cur_file_path[old_base_name_length + 1:]
 
             # check if file have subtitles language
-            if os.path.splitext(cur_extension)[1][1:] in common.subtitleExtensions:
+            if os.path.splitext(cur_extension)[1][1:] in subtitle_extensions:
                 cur_lang = os.path.splitext(cur_extension)[0]
                 if cur_lang:
                     cur_lang = cur_lang.lower()
@@ -333,7 +345,7 @@ class PostProcessor(object):
                     cur_extension = cur_lang + os.path.splitext(cur_extension)[1]
 
             # replace .nfo with .nfo-orig to avoid conflicts
-            if cur_extension == 'nfo' and sickbeard.NFO_RENAME == True:
+            if cur_extension == 'nfo' and sickbeard.NFO_RENAME is True:
                 cur_extension = 'nfo-orig'
 
             # If new base name then convert name
@@ -343,7 +355,7 @@ class PostProcessor(object):
             else:
                 new_file_name = helpers.replaceExtension(cur_file_name, cur_extension)
 
-            if sickbeard.SUBTITLES_DIR and cur_extension[-3:] in common.subtitleExtensions:
+            if sickbeard.SUBTITLES_DIR and cur_extension[-3:] in subtitle_extensions:
                 subs_new_path = ek(os.path.join, new_path, sickbeard.SUBTITLES_DIR)
                 dir_exists = helpers.makeDir(subs_new_path)
                 if not dir_exists:
@@ -474,7 +486,7 @@ class PostProcessor(object):
         # search the database for a possible match and return immediately if we find one
         myDB = db.DBConnection()
         for curName in names:
-            search_name = re.sub(r"[\.\-\ ]", "_", curName)
+            search_name = re.sub(r"[\.\- ]", "_", curName)
             sql_results = myDB.select("SELECT * FROM history WHERE resource LIKE ?", [search_name])
 
             if len(sql_results) == 0:
@@ -492,7 +504,7 @@ class PostProcessor(object):
 
             self.in_history = True
             self.version = version
-            to_return = (show, season, [], quality, version)
+            to_return = (str(show), season, [], quality, version)
             self._log("Found result in history: " + str(to_return), logger.DEBUG)
 
             return to_return
@@ -510,7 +522,7 @@ class PostProcessor(object):
 
         # remember whether it's a proper
         if parse_result.extra_info:
-            self.is_proper = re.search(r'(^|[\. _-])(proper|repack)([\. _-]|$)', parse_result.extra_info, re.I) != None
+            self.is_proper = re.search(r'(^|[\. _-])(proper|repack)([\. _-]|$)', parse_result.extra_info, re.I) is not None
 
         # if the result is complete then remember that for later
         # if the result is complete then set release name
@@ -648,7 +660,7 @@ class PostProcessor(object):
             if cur_version is not None:
                 version = cur_version
 
-            if cur_season != None:
+            if cur_season is not None:
                 season = cur_season
             if cur_episodes:
                 episodes = cur_episodes
@@ -685,12 +697,12 @@ class PostProcessor(object):
                         continue
 
             # if there's no season then we can hopefully just use 1 automatically
-            elif season == None and show:
+            elif season is None and show:
                 myDB = db.DBConnection()
                 numseasonsSQlResult = myDB.select(
                     "SELECT COUNT(DISTINCT season) as numseasons FROM tv_episodes WHERE showid = ? and indexer = ? and season != 0",
                     [show.indexerid, show.indexer])
-                if int(numseasonsSQlResult[0][0]) == 1 and season == None:
+                if int(numseasonsSQlResult[0][0]) == 1 and season is None:
                     self._log(
                         u"Don't have a season number, but this show appears to only have 1 season, setting season number to 1...",
                         logger.DEBUG)
@@ -727,7 +739,7 @@ class PostProcessor(object):
                 raise EpisodePostProcessingFailedException()
 
             # associate all the episodes together under a single root episode
-            if root_ep == None:
+            if root_ep is None:
                 root_ep = curEp
                 root_ep.relatedEps = []
             elif curEp not in root_ep.relatedEps:
@@ -841,6 +853,11 @@ class PostProcessor(object):
             return True
 
         _, old_ep_quality = common.Quality.splitCompositeStatus(ep_obj.status)
+        self._log(u"old_ep_quality = %s, new_ep_quality %s" % (common.Quality.qualityStrings[old_ep_quality], common.Quality.qualityStrings[new_ep_quality]), logger.DEBUG)
+
+        if old_ep_quality == common.Quality.UNKNOWN and new_ep_quality != common.Quality.UNKNOWN:
+            self._log(u"Old episode has an unknown quality, so any known quality is better", logger.DEBUG)
+            return True
 
         # if SR downloaded this on purpose we likely have a priority download
         if self.in_history or ep_obj.status in common.Quality.SNATCHED + common.Quality.SNATCHED_PROPER + common.Quality.SNATCHED_BEST:
@@ -905,7 +922,7 @@ class PostProcessor(object):
         if not show:
             self._log(u"This show isn't in your list, you need to add it to SR before post-processing an episode")
             raise EpisodePostProcessingFailedException()
-        elif season == None or not episodes:
+        elif season is None or not episodes:
             self._log(u"Not enough information to determine what episode this is. Quitting post-processing")
             return False
 
@@ -921,7 +938,7 @@ class PostProcessor(object):
         else:
             new_ep_quality = self._get_quality(ep_obj)
 
-        logger.log(u"Quality of the episode we're processing: %s" % new_ep_quality, logger.DEBUG)
+        logger.log(u"Quality of the episode we're processing: %s" % common.Quality.qualityStrings[new_ep_quality], logger.DEBUG)
 
         # see if this is a priority download (is it snatched, in history, PROPER, or BEST)
         priority_download = self._is_priority(ep_obj, new_ep_quality)
diff --git a/sickbeard/processTV.py b/sickbeard/processTV.py
index d201a5885db93295c840d7872f01f64916935a64..ec6ddd064b93a5d5a86c8a7e7975c009e8350dbb 100644
--- a/sickbeard/processTV.py
+++ b/sickbeard/processTV.py
@@ -28,7 +28,8 @@ from sickbeard import logger
 from sickbeard.name_parser.parser import NameParser, InvalidNameException, InvalidShowException
 from sickbeard import common
 from sickbeard import failedProcessor
-from sickrage.helper.encoding import ek, ss
+from sickrage.helper.common import subtitle_extensions
+from sickrage.helper.encoding import ek
 from sickrage.helper.exceptions import EpisodePostProcessingFailedException, ex, FailedPostProcessingFailedException
 
 from unrar2 import RarFile
@@ -547,15 +548,15 @@ def process_media(processPath, videoFiles, nzbName, process_method, force, is_pr
 
         try:
             processor = postProcessor.PostProcessor(cur_video_file_path, nzbName, process_method, is_priority)
-            # This feature prevents PP for files that do not have subtitle associated with the video file        
+            # This feature prevents PP for files that do not have subtitle associated with the video file
             if sickbeard.POSTPONE_IF_NO_SUBS:
                 associatedFiles = processor.list_associated_files(cur_video_file_path, subtitles_only=True)
-                if not [associatedFile for associatedFile in associatedFiles if associatedFile[-3:] in common.subtitleExtensions]:
+                if not [associatedFile for associatedFile in associatedFiles if associatedFile[-3:] in subtitle_extensions]:
                     result.output += logHelper(u"No subtitles associated. Postponing the post-process of this file: %s" % cur_video_file, logger.DEBUG)
                     continue
                 else:
-                    result.output += logHelper(u"Found subtitles associated. Continuing the post-process of this file: %s" % cur_video_file) 
-        
+                    result.output += logHelper(u"Found subtitles associated. Continuing the post-process of this file: %s" % cur_video_file)
+
             result.result = processor.process()
             process_fail_message = ""
         except EpisodePostProcessingFailedException, e:
diff --git a/sickbeard/providers/__init__.py b/sickbeard/providers/__init__.py
index d863cb052e839bd47f648cc5e858ab5cfa677b8b..60116727e358a89c64080c7c41c3c43f82b0a153 100644
--- a/sickbeard/providers/__init__.py
+++ b/sickbeard/providers/__init__.py
@@ -26,7 +26,7 @@ from sickbeard import logger
 from sickbeard.providers import btn, newznab, womble, thepiratebay, torrentleech, kat, iptorrents, torrentz, \
     omgwtfnzbs, scc, hdtorrents, torrentday, hdbits, hounddawgs, nextgen, speedcd, nyaatorrents, animenzb, bluetigers, cpasbien, fnt, xthor, torrentbytes, \
     freshontv, titansoftv, libertalia, morethantv, bitsoup, t411, tokyotoshokan, shazbat, rarbg, alpharatio, tntvillage, binsearch, torrentproject, extratorrent, \
-    scenetime, btdigg, strike, transmitthenet, tvchaosuk, bitcannon, pretome, gftracker, hdspace, newpct
+    scenetime, btdigg, strike, transmitthenet, tvchaosuk, bitcannon, pretome, gftracker, hdspace, newpct, elitetorrent
 
 __all__ = ['womble',
            'btn',
@@ -73,7 +73,8 @@ __all__ = ['womble',
            'pretome',
            'gftracker',
            'hdspace',
-           'newpct'
+           'newpct',
+           'elitetorrent'
            ]
 
 
diff --git a/sickbeard/providers/alpharatio.py b/sickbeard/providers/alpharatio.py
index d2b096faeaf39bf4d6f50670703fe57d38e57467..0e58915fa0e4e706f6c9e0334d592e71f2fb31dd 100644
--- a/sickbeard/providers/alpharatio.py
+++ b/sickbeard/providers/alpharatio.py
@@ -85,7 +85,7 @@ class AlphaRatioProvider(generic.TorrentProvider):
             logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
             for search_string in search_strings[mode]:
 
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s " % search_string, logger.DEBUG)
 
                 searchURL = self.urls['search'] % (search_string, self.categories)
@@ -125,12 +125,12 @@ class AlphaRatioProvider(generic.TorrentProvider):
 
                             # Filter unseeded torrent
                             if seeders < self.minseed or leechers < self.minleech:
-                                if mode is not 'RSS':
+                                if mode != 'RSS':
                                     logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                                 continue
 
                             item = title, download_url, size, seeders, leechers
-                            if mode is not 'RSS':
+                            if mode != 'RSS':
                                 logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                             items[mode].append(item)
diff --git a/sickbeard/providers/bitcannon.py b/sickbeard/providers/bitcannon.py
index 54ea2276879e6a796187d287a8b9addf0ce57d7a..ebdd16551107fca7e2a21f9d60234da964f5529e 100644
--- a/sickbeard/providers/bitcannon.py
+++ b/sickbeard/providers/bitcannon.py
@@ -75,7 +75,7 @@ class BitCannonProvider(generic.TorrentProvider):
 
                     # Filter unseeded torrent
                     if seeders < self.minseed or leechers < self.minleech:
-                        if mode is not 'RSS':
+                        if mode != 'RSS':
                             logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                         continue
 
@@ -83,7 +83,7 @@ class BitCannonProvider(generic.TorrentProvider):
                     download_url = 'magnet:?xt=urn:btih:%s&dn=%s&tr=%s' % (info_hash, quote_plus(title.encode('utf-8')), u'&tr='.join([quote_plus(x.encode('utf-8')) for x in trackers]))
 
                     item = title, download_url, size, seeders, leechers
-                    if mode is not 'RSS':
+                    if mode != 'RSS':
                         logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                     items[mode].append(item)
diff --git a/sickbeard/providers/bitsoup.py b/sickbeard/providers/bitsoup.py
index 162f7b3bc1fb63f8c236a443f5b28f2b7a4c9faf..e25855c9deee2c8029bee140220fb234dc4fc253 100644
--- a/sickbeard/providers/bitsoup.py
+++ b/sickbeard/providers/bitsoup.py
@@ -89,7 +89,7 @@ class BitSoupProvider(generic.TorrentProvider):
             logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
             for search_string in search_strings[mode]:
 
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s " % search_string, logger.DEBUG)
 
                 self.search_params['search'] = search_string
@@ -128,12 +128,12 @@ class BitSoupProvider(generic.TorrentProvider):
 
                                 # Filter unseeded torrent
                             if seeders < self.minseed or leechers < self.minleech:
-                                if mode is not 'RSS':
+                                if mode != 'RSS':
                                     logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                                 continue
 
                             item = title, download_url, size, seeders, leechers
-                            if mode is not 'RSS':
+                            if mode != 'RSS':
                                 logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                             items[mode].append(item)
diff --git a/sickbeard/providers/bluetigers.py b/sickbeard/providers/bluetigers.py
index bbe6e3f6e4b6324ba79b42e31d2032c8e96f10f7..561503a401732b3df83c43ef7da75950493d16fc 100644
--- a/sickbeard/providers/bluetigers.py
+++ b/sickbeard/providers/bluetigers.py
@@ -1,4 +1,4 @@
-# -*- coding: latin-1 -*-
+# coding=utf-8
 # Author: raver2046 <raver2046@gmail.com>
 # URL: http://code.google.com/p/sickbeard/
 #
@@ -18,11 +18,11 @@
 # along with Sick Beard.  If not, see <http://www.gnu.org/licenses/>.
 
 import traceback
+import requests
 import re
 
 from requests.auth import AuthBase
 from sickbeard.providers import generic
-import requests
 from sickbeard.bs4_parser import BS4Parser
 
 from sickbeard import logger
@@ -39,7 +39,6 @@ class BLUETIGERSProvider(generic.TorrentProvider):
         self.password = None
         self.ratio = None
         self.token = None
-        self.tokenLastUpdate = None
 
         self.cache = BLUETIGERSCache(self)
 
@@ -67,13 +66,16 @@ class BLUETIGERSProvider(generic.TorrentProvider):
             }
 
         response = self.getURL(self.urls['login'], post_data=login_params, timeout=30)
-        if not response:
-            logger.log(u"Unable to connect to provider", logger.WARNING)
-            return False
 
-        if re.search('/account-logout.php', response):
-            return True
-        else:
+        if not response:
+            check_login = self.getURL(self.urls['base_url'], timeout=30)
+            if re.search('account-logout.php', check_login):
+                return True
+            else:
+                logger.log(u"Unable to connect to provider", logger.WARNING)
+                return False
+
+        if re.search('account-login.php', response):
             logger.log(u"Invalid username or password. Check your settings", logger.WARNING)
             return False
 
@@ -83,8 +85,7 @@ class BLUETIGERSProvider(generic.TorrentProvider):
 
         results = []
         items = {'Season': [], 'Episode': [], 'RSS': []}
-        
-        # check for auth
+
         if not self._doLogin():
             return results
 
@@ -92,7 +93,7 @@ class BLUETIGERSProvider(generic.TorrentProvider):
             logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
             for search_string in search_strings[mode]:
 
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s " % search_string, logger.DEBUG)
 
                 self.search_params['search'] = search_string
@@ -112,7 +113,7 @@ class BLUETIGERSProvider(generic.TorrentProvider):
                         if result_linkz:
                             for link in result_linkz:
                                 title = link.text
-                                download_url = self.urls['base_url'] + "/" + link['href']
+                                download_url = self.urls['base_url'] + link['href']
                                 download_url = download_url.replace("torrents-details", "download")
                                 # FIXME
                                 size = -1
@@ -124,12 +125,12 @@ class BLUETIGERSProvider(generic.TorrentProvider):
 
                                 # Filter unseeded torrent
                                 # if seeders < self.minseed or leechers < self.minleech:
-                                #    if mode is not 'RSS':
+                                #    if mode != 'RSS':
                                 #        logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                                 #    continue
 
                                 item = title, download_url, size, seeders, leechers
-                                if mode is not 'RSS':
+                                if mode != 'RSS':
                                     logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                                 items[mode].append(item)
diff --git a/sickbeard/providers/btdigg.py b/sickbeard/providers/btdigg.py
index 553e7e320bfcd1a66395b6c964a350d9e31e5ca2..4eda679d1cdf9e9371d3566cdcf4b02ad4aaaa8f 100644
--- a/sickbeard/providers/btdigg.py
+++ b/sickbeard/providers/btdigg.py
@@ -55,11 +55,11 @@ class BTDIGGProvider(generic.TorrentProvider):
             logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
             for search_string in search_strings[mode]:
 
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s" % search_string, logger.DEBUG)
 
                 search_params['q'] = search_string.encode('utf-8')
-                search_params['order'] = '1' if mode is not 'RSS' else '2'
+                search_params['order'] = '1' if mode != 'RSS' else '2'
 
                 searchURL = self.urls['api'] + '?' + urlencode(search_params)
                 logger.log(u"Search URL: %s" %  searchURL, logger.DEBUG)
@@ -83,12 +83,12 @@ class BTDIGGProvider(generic.TorrentProvider):
 
                         # Filter unseeded torrent (Unsupported)
                         # if seeders < self.minseed or leechers < self.minleech:
-                        #    if mode is not 'RSS':
+                        #    if mode != 'RSS':
                         #        logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                         #    continue
 
                         item = title, download_url, size, seeders, leechers
-                        if mode is not 'RSS':
+                        if mode != 'RSS':
                             logger.log(u"Found result: %s" % title, logger.DEBUG)
 
                         items[mode].append(item)
diff --git a/sickbeard/providers/cpasbien.py b/sickbeard/providers/cpasbien.py
index 67641af0006547fd78cb73d998bf3508d14bd042..e91160f8db116325b9629173d66b48db37b3678e 100644
--- a/sickbeard/providers/cpasbien.py
+++ b/sickbeard/providers/cpasbien.py
@@ -49,7 +49,7 @@ class CpasbienProvider(generic.TorrentProvider):
             logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
             for search_string in search_params[mode]:
 
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s " % search_string, logger.DEBUG)
 
                 searchURL = self.url + '/recherche/'+search_string.replace('.', '-') + '.html'
@@ -101,7 +101,7 @@ class CpasbienProvider(generic.TorrentProvider):
                                 continue
 
                             item = title, download_url, size, seeders, leechers
-                            if mode is not 'RSS':
+                            if mode != 'RSS':
                                 logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                             items[mode].append(item)
diff --git a/sickbeard/providers/elitetorrent.py b/sickbeard/providers/elitetorrent.py
new file mode 100644
index 0000000000000000000000000000000000000000..63a9492e2c50d32af0d90572d04160f15075148b
--- /dev/null
+++ b/sickbeard/providers/elitetorrent.py
@@ -0,0 +1,186 @@
+# coding=utf-8
+# Author: CristianBB
+#
+# URL: http://code.google.com/p/sickbeard/
+#
+# This file is part of SickRage.
+#
+# SickRage is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# SickRage is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
+
+import traceback
+import re
+from six.moves import urllib
+
+from sickbeard import logger
+from sickbeard import tvcache
+from sickbeard.providers import generic
+from sickbeard.bs4_parser import BS4Parser
+
+
+class elitetorrentProvider(generic.TorrentProvider):
+    def __init__(self):
+
+        generic.TorrentProvider.__init__(self, "EliteTorrent")
+
+        self.supportsBacklog = True
+        self.onlyspasearch = None
+        self.minseed = None
+        self.minleech = None
+        self.cache = elitetorrentCache(self)
+
+        self.urls = {
+            'base_url': 'http://www.elitetorrent.net',
+            'search': 'http://www.elitetorrent.net/torrents.php'
+        }
+
+        self.url = self.urls['base_url']
+
+        """
+        Search query:
+        http://www.elitetorrent.net/torrents.php?cat=4&modo=listado&orden=fecha&pag=1&buscar=fringe
+        
+        cat = 4 => Shows
+        modo = listado => display results mode
+        orden = fecha => order
+        buscar => Search show
+        pag = 1 => page number
+        """
+
+        self.search_params = {
+            'cat': 4,
+            'modo': 'listado',
+            'orden': 'fecha',
+            'pag': 1,
+            'buscar': ''
+
+        }
+        
+    def _doSearch(self, search_strings, search_mode='eponly', epcount=0, age=0, epObj=None):
+
+        results = []
+        items = {'Season': [], 'Episode': [], 'RSS': []}
+
+        lang_info = '' if not epObj or not epObj.show else epObj.show.lang
+        
+        for mode in search_strings.keys():
+            logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
+            
+            # Only search if user conditions are true
+            if self.onlyspasearch and lang_info != 'es' and mode is not 'RSS':
+                logger.log(u"Show info is not spanish, skipping provider search", logger.DEBUG)
+                continue
+
+            for search_string in search_strings[mode]:
+                if mode is not 'RSS':
+                    logger.log(u"Search string: %s " % search_string, logger.DEBUG)
+                
+                search_string = re.sub(r'S0*(\d*)E(\d*)', r'\1x\2', search_string)
+                self.search_params['buscar'] = search_string.strip() if mode is not 'RSS' else ''
+                
+                searchURL = self.urls['search'] + '?' + urllib.parse.urlencode(self.search_params)
+                logger.log(u"Search URL: %s" % searchURL, logger.DEBUG)
+                
+                data = self.getURL(searchURL, timeout=30)
+                
+                if not data:
+                    continue
+
+                try:
+                    with BS4Parser(data, features=["html5lib", "permissive"]) as html:
+                        torrent_table = html.find('table', class_='fichas-listado')
+
+                        if torrent_table is None:
+                            logger.log(u"Data returned from provider does not contain any torrents", logger.DEBUG)
+                            continue
+
+                        torrent_rows = torrent_table.findAll('tr')
+                        if torrent_rows is None:
+                            logger.log(u"Torrent table does not have any rows", logger.DEBUG)
+                            continue
+
+                        for row in torrent_rows[1:]:
+                            try:
+                                seeders_raw = row.find('td', class_='semillas').text
+                                leechers_raw = row.find('td', class_='clientes').text 
+
+                                download_url = self.urls['base_url'] + row.findAll('a')[0].get('href', '')
+                                title = self._processTitle(row.findAll('a')[1].text)
+                                seeders = seeders_raw if seeders_raw.isnumeric() else 0
+                                leechers = leechers_raw if leechers_raw.isnumeric() else 0
+                                                                
+                                # FIXME: Provider does not provide size
+                                size = 0
+
+                            except (AttributeError, TypeError):
+                                continue
+
+                            if not all([title, download_url]):
+                                continue
+
+                            # Filter unseeded torrent
+                            if seeders < self.minseed or leechers < self.minleech:
+                                if mode is not 'RSS':
+                                    logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
+                                continue
+
+                            item = title, download_url, size, seeders, leechers
+                            if mode is not 'RSS':
+                                logger.log(u"Found result: %s " % title, logger.DEBUG)
+
+                            items[mode].append(item)
+
+                except Exception:
+                    logger.log(u"Failed parsing provider. Traceback: %s" % traceback.format_exc(), logger.WARNING)
+
+            # For each search mode sort all the items by seeders if available
+            items[mode].sort(key=lambda tup: tup[3], reverse=True)
+
+            results += items[mode]
+
+        return results
+
+   
+    @staticmethod
+    def _processTitle(title):
+
+        # Quality, if no literal is defined it's HDTV
+        if 'calidad' not in title:
+            title += ' HDTV x264'
+            
+        title = title.replace('(calidad baja)', 'HDTV x264')            
+        title = title.replace('(Buena calidad)', '720p HDTV x264')
+        title = title.replace('(Alta calidad)', '720p HDTV x264')
+        title = title.replace('(calidad regular)', 'DVDrip x264')
+        title = title.replace('(calidad media)', 'DVDrip x264')
+                    
+        #Language, all results from this provider have spanish audio, we append it to title (avoid to download undesired torrents)
+        title += ' SPANISH AUDIO'
+        title += '-ELITETORRENT'
+        
+        return title.strip()
+
+
+class elitetorrentCache(tvcache.TVCache):
+    def __init__(self, provider_obj):
+
+        tvcache.TVCache.__init__(self, provider_obj)
+
+        self.minTime = 20
+
+    def _getRSSData(self):
+        search_params = {'RSS': ['']}
+        return {'entries': self.provider._doSearch(search_params)}
+
+
+provider = elitetorrentProvider()
diff --git a/sickbeard/providers/extratorrent.py b/sickbeard/providers/extratorrent.py
index d16cccb75180c752bf8fccc6c906c176f44fdcde..eda874561dfa78266ab8f8fe4bd592cd64100af4 100644
--- a/sickbeard/providers/extratorrent.py
+++ b/sickbeard/providers/extratorrent.py
@@ -58,7 +58,7 @@ class ExtraTorrentProvider(generic.TorrentProvider):
             logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
             for search_string in search_strings[mode]:
 
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s " % search_string, logger.DEBUG)
 
                 try:
@@ -99,12 +99,12 @@ class ExtraTorrentProvider(generic.TorrentProvider):
 
                             # Filter unseeded torrent
                         if seeders < self.minseed or leechers < self.minleech:
-                            if mode is not 'RSS':
+                            if mode != 'RSS':
                                 logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                             continue
 
                         item = title, download_url, size, seeders, leechers
-                        if mode is not 'RSS':
+                        if mode != 'RSS':
                             logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                         items[mode].append(item)
diff --git a/sickbeard/providers/fnt.py b/sickbeard/providers/fnt.py
index 52f83e8a32985ffb9ca1129eb9efc874ea2f8fb7..78bc2d1ea74e333a8bdb062758d06ed2e91a7de2 100644
--- a/sickbeard/providers/fnt.py
+++ b/sickbeard/providers/fnt.py
@@ -90,7 +90,7 @@ class FNTProvider(generic.TorrentProvider):
             logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
             for search_string in search_strings[mode]:
 
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s " % search_string, logger.DEBUG)
 
                 self.search_params['recherche'] = search_string
@@ -135,12 +135,12 @@ class FNTProvider(generic.TorrentProvider):
 
                                     # Filter unseeded torrent
                                     if seeders < self.minseed or leechers < self.minleech:
-                                        if mode is not 'RSS':
+                                        if mode != 'RSS':
                                             logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                                         continue
 
                                     item = title, download_url, size, seeders, leechers
-                                    if mode is not 'RSS':
+                                    if mode != 'RSS':
                                         logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                                     items[mode].append(item)
diff --git a/sickbeard/providers/freshontv.py b/sickbeard/providers/freshontv.py
index 3c5b7688d2263a848cda22853a42611976029326..d0cc75351f1bbf564358aa889cd56d40f9ff5c04 100644
--- a/sickbeard/providers/freshontv.py
+++ b/sickbeard/providers/freshontv.py
@@ -117,7 +117,7 @@ class FreshOnTVProvider(generic.TorrentProvider):
             logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
             for search_string in search_params[mode]:
 
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s " % search_string, logger.DEBUG)
 
                 searchURL = self.urls['search'] % (freeleech, search_string)
@@ -190,7 +190,7 @@ class FreshOnTVProvider(generic.TorrentProvider):
                             for individual_torrent in torrent_rows:
 
                                 # skip if torrent has been nuked due to poor quality
-                                if individual_torrent.find('img', alt='Nuked') != None:
+                                if individual_torrent.find('img', alt='Nuked') is not None:
                                     continue
 
                                 try:
@@ -215,12 +215,12 @@ class FreshOnTVProvider(generic.TorrentProvider):
 
                                 # Filter unseeded torrent
                                 if seeders < self.minseed or leechers < self.minleech:
-                                    if mode is not 'RSS':
+                                    if mode != 'RSS':
                                         logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                                     continue
 
                                 item = title, download_url, size, seeders, leechers
-                                if mode is not 'RSS':
+                                if mode != 'RSS':
                                     logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                                 items[mode].append(item)
diff --git a/sickbeard/providers/gftracker.py b/sickbeard/providers/gftracker.py
index 826e42ccf49dbda3a63661c9f5cf626d89f2e161..14d6656cdda3a1050548120b441c249d78675049 100644
--- a/sickbeard/providers/gftracker.py
+++ b/sickbeard/providers/gftracker.py
@@ -94,7 +94,7 @@ class GFTrackerProvider(generic.TorrentProvider):
             logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
             for search_string in search_params[mode]:
 
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s " % search_string, logger.DEBUG)
 
                 searchURL = self.urls['search'] % (self.categories, search_string)
@@ -146,12 +146,12 @@ class GFTrackerProvider(generic.TorrentProvider):
 
                             # Filter unseeded torrent
                             if seeders < self.minseed or leechers < self.minleech:
-                                if mode is not 'RSS':
+                                if mode != 'RSS':
                                     logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                                 continue
 
                             item = title, download_url, size, seeders, leechers
-                            if mode is not 'RSS':
+                            if mode != 'RSS':
                                 logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                             items[mode].append(item)
diff --git a/sickbeard/providers/hdspace.py b/sickbeard/providers/hdspace.py
index 48eec48a7180d1cb79130ceb3e37c5efd1a39178..5a61844d13a233194b2cd0faa0800669f8b24e55 100644
--- a/sickbeard/providers/hdspace.py
+++ b/sickbeard/providers/hdspace.py
@@ -93,13 +93,13 @@ class HDSpaceProvider(generic.TorrentProvider):
             logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
             for search_string in search_strings[mode]:
 
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     searchURL = self.urls['search'] % (urllib.quote_plus(search_string.replace('.', ' ')),)
                 else:
                     searchURL = self.urls['search'] % ''
 
                 logger.log(u"Search URL: %s" %  searchURL, logger.DEBUG)
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s" %  search_string, logger.DEBUG)
 
                 data = self.getURL(searchURL)
@@ -145,12 +145,12 @@ class HDSpaceProvider(generic.TorrentProvider):
 
                         # Filter unseeded torrent
                         if seeders < self.minseed or leechers < self.minleech:
-                            if mode is not 'RSS':
+                            if mode != 'RSS':
                                 logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                             continue
 
                         item = title, download_url, size, seeders, leechers
-                        if mode is not 'RSS':
+                        if mode != 'RSS':
                             logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                         items[mode].append(item)
diff --git a/sickbeard/providers/hdtorrents.py b/sickbeard/providers/hdtorrents.py
index e2708f3cdd8db40a231cc133930e7aba15b8bfa4..4183f702686d7d68f975bb8e59e5e86fff1798b0 100644
--- a/sickbeard/providers/hdtorrents.py
+++ b/sickbeard/providers/hdtorrents.py
@@ -92,13 +92,13 @@ class HDTorrentsProvider(generic.TorrentProvider):
             logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
             for search_string in search_strings[mode]:
 
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     searchURL = self.urls['search'] % (urllib.quote_plus(search_string), self.categories)
                 else:
                     searchURL = self.urls['rss'] % self.categories
 
                 logger.log(u"Search URL: %s" %  searchURL, logger.DEBUG)
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s" %  search_string, logger.DEBUG)
 
                 data = self.getURL(searchURL)
@@ -177,12 +177,12 @@ class HDTorrentsProvider(generic.TorrentProvider):
 
                             # Filter unseeded torrent
                             if seeders < self.minseed or leechers < self.minleech:
-                                if mode is not 'RSS':
+                                if mode != 'RSS':
                                     logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                                 continue
 
                             item = title, download_url, size, seeders, leechers
-                            if mode is not 'RSS':
+                            if mode != 'RSS':
                                 logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                             items[mode].append(item)
diff --git a/sickbeard/providers/hounddawgs.py b/sickbeard/providers/hounddawgs.py
index 3736bfcb4d4bde0066dc9b69c71e5e8dfcdec5f5..704320e3e57ffd5b3cbd2374716d5e7b29ff7d3f 100644
--- a/sickbeard/providers/hounddawgs.py
+++ b/sickbeard/providers/hounddawgs.py
@@ -95,7 +95,7 @@ class HoundDawgsProvider(generic.TorrentProvider):
             logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
             for search_string in search_strings[mode]:
 
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s " % search_string, logger.DEBUG)
 
                 self.search_params['searchstr'] = search_string
@@ -157,12 +157,12 @@ class HoundDawgsProvider(generic.TorrentProvider):
 
                             # Filter unseeded torrent
                             # if seeders < self.minseed or leechers < self.minleech:
-                            #    if mode is not 'RSS':
+                            #    if mode != 'RSS':
                             #        logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                             #    continue
 
                             item = title, download_url, size, seeders, leechers
-                            if mode is not 'RSS':
+                            if mode != 'RSS':
                                 logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                             items[mode].append(item)
diff --git a/sickbeard/providers/iptorrents.py b/sickbeard/providers/iptorrents.py
index 000c9b03e42f0f19a84169d051d85f44bdcc5ac4..12c29eb991a7c1cfecb980ba437a8d78bc60f239 100644
--- a/sickbeard/providers/iptorrents.py
+++ b/sickbeard/providers/iptorrents.py
@@ -90,12 +90,12 @@ class IPTorrentsProvider(generic.TorrentProvider):
             logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
             for search_string in search_params[mode]:
 
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s " % search_string, logger.DEBUG)
 
                 # URL with 50 tv-show results, or max 150 if adjusted in IPTorrents profile
                 searchURL = self.urls['search'] % (self.categories, freeleech, search_string)
-                searchURL += ';o=seeders' if mode is not 'RSS' else ''
+                searchURL += ';o=seeders' if mode != 'RSS' else ''
                 logger.log(u"Search URL: %s" %  searchURL, logger.DEBUG)
 
                 data = self.getURL(searchURL)
@@ -136,12 +136,12 @@ class IPTorrentsProvider(generic.TorrentProvider):
 
                             # Filter unseeded torrent
                             if seeders < self.minseed or leechers < self.minleech:
-                                if mode is not 'RSS':
+                                if mode != 'RSS':
                                     logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                                 continue
 
                             item = title, download_url, size, seeders, leechers
-                            if mode is not 'RSS':
+                            if mode != 'RSS':
                                 logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                             items[mode].append(item)
diff --git a/sickbeard/providers/kat.py b/sickbeard/providers/kat.py
index 7b097de4bec31582ae8e048a1c974b236db9fc41..ec62eafa106a4d15b7d5c7f77a5c5a40d80dc51f 100755
--- a/sickbeard/providers/kat.py
+++ b/sickbeard/providers/kat.py
@@ -1,6 +1,6 @@
 # coding=utf-8
-# Author: Mr_Orange <mr_orange@hotmail.it>
-# URL: http://code.google.com/p/sickbeard/
+# Author: Dustyn Gibson <miigotu@gmail.com>
+# URL: http://sickrage.github.io
 #
 # This file is part of SickRage.
 #
@@ -18,18 +18,17 @@
 # along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
 
 
+import posixpath  # Must use posixpath
 import traceback
-
 from urllib import urlencode
-
-import xmltodict
+from bs4 import BeautifulSoup
 
 import sickbeard
 from sickbeard import logger
 from sickbeard import tvcache
 from sickbeard.common import USER_AGENT
 from sickbeard.providers import generic
-from xml.parsers.expat import ExpatError
+from sickbeard.helpers import tryInt
 
 class KATProvider(generic.TorrentProvider):
     def __init__(self):
@@ -44,14 +43,14 @@ class KATProvider(generic.TorrentProvider):
         self.minseed = None
         self.minleech = None
 
-        self.cache = KATCache(self)
-
         self.urls = {
-            'base_url': 'https://kickass.unblocked.pe/',
-            'search': 'https://kickass.unblocked.pe/%s/',
+            'base_url': 'https://kat.cr/',
+            'search': 'https://kat.cr/%s/',
         }
 
         self.url = self.urls['base_url']
+        self.custom_url = None
+
         self.headers.update({'User-Agent': USER_AGENT})
 
         self.search_params = {
@@ -62,6 +61,8 @@ class KATProvider(generic.TorrentProvider):
             'category': 'tv'
         }
 
+        self.cache = KATCache(self)
+
     def _doSearch(self, search_strings, search_mode='eponly', epcount=0, age=0, epObj=None):
         results = []
         items = {'Season': [], 'Episode': [], 'RSS': []}
@@ -74,79 +75,71 @@ class KATProvider(generic.TorrentProvider):
             logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
             for search_string in search_strings[mode]:
 
-                self.search_params['q'] = search_string.encode('utf-8') if mode is not 'RSS' else ''
-                self.search_params['field'] = 'seeders' if mode is not 'RSS' else 'time_add'
+                self.search_params['q'] = search_string.encode('utf-8') if mode != 'RSS' else ''
+                self.search_params['field'] = 'seeders' if mode != 'RSS' else 'time_add'
 
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s" % search_string, logger.DEBUG)
 
-                url_fmt_string = 'usearch' if mode is not 'RSS' else search_string
+                url_fmt_string = 'usearch' if mode != 'RSS' else search_string
                 try:
                     searchURL = self.urls['search'] % url_fmt_string + '?' + urlencode(self.search_params)
+                    if self.custom_url:
+                        searchURL = posixpath.join(self.custom_url, searchURL.split(self.url)[1].lstrip('/')) # Must use posixpath
+
                     logger.log(u"Search URL: %s" % searchURL, logger.DEBUG)
                     data = self.getURL(searchURL)
-                    # data = self.getURL(self.urls[('search', 'rss')[mode is 'RSS']], params=self.search_params)
                     if not data:
-                        logger.log(u"No data returned from provider", logger.DEBUG)
+                        logger.log(u'URL did not return data, maybe try a custom url, or a different one', logger.DEBUG)
                         continue
 
                     if not data.startswith('<?xml'):
                         logger.log(u'Expected xml but got something else, is your mirror failing?', logger.INFO)
                         continue
 
-                    try:
-                        data = xmltodict.parse(data)
-                    except ExpatError:
-                        logger.log(u"Failed parsing provider. Traceback: %r\n%r" % (traceback.format_exc(), data), logger.ERROR)
-                        continue
-
-                    if not all([data, 'rss' in data, 'channel' in data['rss'], 'item' in data['rss']['channel']]):
-                        logger.log(u"Malformed rss returned, skipping", logger.DEBUG)
-                        continue
-
-                    # https://github.com/martinblech/xmltodict/issues/111
-                    entries = data['rss']['channel']['item']
-                    entries = entries if isinstance(entries, list) else [entries]
+                    data = BeautifulSoup(data, features=["html5lib", "permissive"])
 
+                    entries = data.findAll('item')
                     for item in entries:
                         try:
-                            title = item['title']
+                            title = item.title.text
                             assert isinstance(title, unicode)
                             # Use the torcache link kat provides,
                             # unless it is not torcache or we are not using blackhole
                             # because we want to use magnets if connecting direct to client
                             # so that proxies work.
-                            download_url = item['enclosure']['@url']
+                            download_url = item.enclosure['url']
                             if sickbeard.TORRENT_METHOD != "blackhole" or 'torcache' not in download_url:
-                                download_url = item['torrent:magnetURI']
+                                download_url = item.find('torrent:magneturi').next.replace('CDATA', '').strip('[]')
+
+                            if not (title and download_url):
+                                continue
 
-                            seeders = int(item['torrent:seeds'])
-                            leechers = int(item['torrent:peers'])
-                            verified = bool(int(item['torrent:verified']) or 0)
-                            size = int(item['torrent:contentLength'])
+                            seeders = tryInt(item.find('torrent:seeds').text, 0)
+                            leechers = tryInt(item.find('torrent:peers').text, 0)
+                            verified = bool(tryInt(item.find('torrent:verified').text, 0))
+                            size = tryInt(item.find('torrent:contentlength').text)
 
-                            info_hash = item['torrent:infoHash']
+                            info_hash = item.find('torrent:infohash').text
                             # link = item['link']
 
-                        except (AttributeError, TypeError, KeyError):
+                        except (AttributeError, TypeError, KeyError, ValueError):
                             continue
 
-                        if not all([title, download_url]):
-                            continue
 
                         # Filter unseeded torrent
                         if seeders < self.minseed or leechers < self.minleech:
-                            if mode is not 'RSS':
+                            if mode != 'RSS':
                                 logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                             continue
 
                         if self.confirmed and not verified:
-                            if mode is not 'RSS':
+                            if mode != 'RSS':
                                 logger.log(u"Found result " + title + " but that doesn't seem like a verified result so I'm ignoring it", logger.DEBUG)
                             continue
 
                         item = title, download_url, size, seeders, leechers, info_hash
-                        if mode is not 'RSS':
+                        if mode != 'RSS':
                             logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                         items[mode].append(item)
diff --git a/sickbeard/providers/libertalia.py b/sickbeard/providers/libertalia.py
index dc2a9a5141570b2c3774eead9bd91bdf2a026c47..657f3be0e50398ff0d602e7d20d9beb6c4e3d386 100644
--- a/sickbeard/providers/libertalia.py
+++ b/sickbeard/providers/libertalia.py
@@ -87,7 +87,7 @@ class LibertaliaProvider(generic.TorrentProvider):
             logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
             for search_string in search_params[mode]:
 
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s " % search_string, logger.DEBUG)
 
                 searchURL = self.urlsearch % (urllib.quote(search_string), self.categories)
@@ -121,12 +121,12 @@ class LibertaliaProvider(generic.TorrentProvider):
 
                                 # Filter unseeded torrent
                                 # if seeders < self.minseed or leechers < self.minleech:
-                                #    if mode is not 'RSS':
+                                #    if mode != 'RSS':
                                 #        logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                                 #    continue
 
                                 item = title, download_url, size, seeders, leechers
-                                if mode is not 'RSS':
+                                if mode != 'RSS':
                                     logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                                 items[mode].append(item)
diff --git a/sickbeard/providers/morethantv.py b/sickbeard/providers/morethantv.py
index c89b22540ed42b04e4b01db0c55ed7a881b97bd6..82ad0842a77235dde2fa4cd4252a2eaa931a2558 100644
--- a/sickbeard/providers/morethantv.py
+++ b/sickbeard/providers/morethantv.py
@@ -106,7 +106,7 @@ class MoreThanTVProvider(generic.TorrentProvider):
             logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
             for search_string in search_params[mode]:
 
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s " % search_string, logger.DEBUG)
 
                 searchURL = self.urls['search'] % (search_string.replace('(', '').replace(')', ''))
@@ -133,7 +133,7 @@ class MoreThanTVProvider(generic.TorrentProvider):
                             link = cells[1].find('a', attrs={'title': 'Download'})
 
                             # skip if torrent has been nuked due to poor quality
-                            if cells[1].find('img', alt='Nuked') != None:
+                            if cells[1].find('img', alt='Nuked') is not None:
                                 continue
 
                             torrent_id_long = link['href'].replace('torrents.php?action=download&id=', '')
@@ -162,12 +162,12 @@ class MoreThanTVProvider(generic.TorrentProvider):
 
                             # Filter unseeded torrent
                             if seeders < self.minseed or leechers < self.minleech:
-                                if mode is not 'RSS':
+                                if mode != 'RSS':
                                     logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                                 continue
 
                             item = title, download_url, size, seeders, leechers
-                            if mode is not 'RSS':
+                            if mode != 'RSS':
                                 logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                             items[mode].append(item)
diff --git a/sickbeard/providers/newpct.py b/sickbeard/providers/newpct.py
index e4ca108dd15520ffb452be980646de8de0cd3b6b..3bcd2caaffb668c71f5d06d7670bf2259f0e0861 100644
--- a/sickbeard/providers/newpct.py
+++ b/sickbeard/providers/newpct.py
@@ -92,7 +92,7 @@ class newpctProvider(generic.TorrentProvider):
             logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
 
             for search_string in search_strings[mode]:
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s " % search_string, logger.DEBUG)
 
                 self.search_params.update({'q': search_string.strip()})
@@ -136,12 +136,12 @@ class newpctProvider(generic.TorrentProvider):
 
                             # Filter unseeded torrent (Unsupported)
                             # if seeders < self.minseed or leechers < self.minleech:
-                            #     if mode is not 'RSS':
+                            #     if mode != 'RSS':
                             #         logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                             #     continue
 
                             item = title, download_url, size, seeders, leechers
-                            if mode is not 'RSS':
+                            if mode != 'RSS':
                                 logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                             items[mode].append(item)
@@ -156,6 +156,19 @@ class newpctProvider(generic.TorrentProvider):
 
         return results
 
+    def getURL(self, url, post_data=None, params=None, timeout=30, json=False, needBytes=False):
+        """
+        needBytes=True when trying access to torrent info (For calling torrent client). Previously we must parse
+        the URL to get torrent file
+        """
+        if needBytes:
+            data = helpers.getURL(url, post_data=None, params=None, headers=self.headers, timeout=timeout,
+                              session=self.session, json=json, needBytes=False)
+            url = re.search(r'http://tumejorserie.com/descargar/.+\.torrent', data, re.DOTALL).group()
+            
+        return helpers.getURL(url, post_data=post_data, params=params, headers=self.headers, timeout=timeout,
+                              session=self.session, json=json, needBytes=needBytes)
+        
     def downloadResult(self, result):
         """
         Save the result to disk.
diff --git a/sickbeard/providers/newznab.py b/sickbeard/providers/newznab.py
index a15114d86f379536d7c649295f6ad26fe5ee7420..d8b26fe3f0b19992c9f79af6e12a68c1a75492b4 100644
--- a/sickbeard/providers/newznab.py
+++ b/sickbeard/providers/newznab.py
@@ -125,7 +125,7 @@ class NewznabProvider(generic.NZBProvider):
             return False, return_categories, error_string
 
         for category in data.caps.categories.findAll('category'):
-            if hasattr(category, 'attrs') and category.attrs['name'] == 'TV':
+            if hasattr(category, 'attrs') and 'TV' in category.attrs['name']:
                 return_categories.append({'id': category.attrs['id'], 'name': category.attrs['name']})
                 for subcat in category.findAll('subcat'):
                     return_categories.append({'id': subcat.attrs['id'], 'name': subcat.attrs['name']})
@@ -282,18 +282,21 @@ class NewznabProvider(generic.NZBProvider):
             except (AttributeError, TypeError):
                 continue
 
-            if title and download_url:
-                size = seeders = leechers = None
-                for attr in item.findAll('newznab:attr') + item.findAll('torznab:attr'):
-                    size = helpers.tryInt(attr['value'], -1) if attr['name'] == 'size' else size
-                    seeders = helpers.tryInt(attr['value'], 1) if attr['name'] == 'seeders' else seeders
-                    leechers = helpers.tryInt(attr['value'], 0) if attr['name'] == 'leechers' else leechers
+            if not (title and download_url):
+                continue
+
+            seeders = leechers = None
+            size = helpers.tryInt(item.size, -1)
+            for attr in item.findAll('newznab:attr') + item.findAll('torznab:attr'):
+                size = helpers.tryInt(attr['value'], -1) if attr['name'] == 'size' else size
+                seeders = helpers.tryInt(attr['value'], 1) if attr['name'] == 'seeders' else seeders
+                leechers = helpers.tryInt(attr['value'], 0) if attr['name'] == 'peers' else leechers
 
-                if not size or (torznab and (seeders is None or leechers is None)):
-                    continue
+            if not size or (torznab and (seeders is None or leechers is None)):
+                continue
 
-                result = {'title': title, 'link': download_url, 'size': size, 'seeders': seeders, 'leechers': leechers}
-                results.append(result)
+            result = {'title': title, 'link': download_url, 'size': size, 'seeders': seeders, 'leechers': leechers}
+            results.append(result)
 
         data.decompose()
 
diff --git a/sickbeard/providers/nextgen.py b/sickbeard/providers/nextgen.py
index 08e575da4364b18283be23c117d8dbf8ce1b67c2..5915ff6a5f999b815dc9bd88e35bb92caa5850b7 100644
--- a/sickbeard/providers/nextgen.py
+++ b/sickbeard/providers/nextgen.py
@@ -122,7 +122,7 @@ class NextGenProvider(generic.TorrentProvider):
             logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
             for search_string in search_params[mode]:
 
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s " % search_string, logger.DEBUG)
 
                 searchURL = self.urls['search'] % (urllib.quote(search_string.encode('utf-8')), self.categories)
@@ -171,12 +171,12 @@ class NextGenProvider(generic.TorrentProvider):
 
                             # Filter unseeded torrent
                             if seeders < self.minseed or leechers < self.minleech:
-                                if mode is not 'RSS':
+                                if mode != 'RSS':
                                     logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                                 continue
 
                             item = title, download_url, size, seeders, leechers
-                            if mode is not 'RSS':
+                            if mode != 'RSS':
                                 logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                             items[mode].append(item)
diff --git a/sickbeard/providers/nyaatorrents.py b/sickbeard/providers/nyaatorrents.py
index ee7ef56343002f1163f55d7743f39f6adff1d441..1b58081c1697783adab1a487496cec72168a57ac 100644
--- a/sickbeard/providers/nyaatorrents.py
+++ b/sickbeard/providers/nyaatorrents.py
@@ -55,7 +55,7 @@ class NyaaProvider(generic.TorrentProvider):
         for mode in search_strings.keys():
             logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
             for search_string in search_strings[mode]:
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s" % search_string, logger.DEBUG)
 
                 params = {
@@ -64,7 +64,7 @@ class NyaaProvider(generic.TorrentProvider):
                     "sort": 2,     # Sort Descending By Seeders
                     "order": 1
                 }
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     params["term"] = search_string.encode('utf-8')
 
                 searchURL = self.url + '?' + urllib.urlencode(params)
@@ -85,16 +85,16 @@ class NyaaProvider(generic.TorrentProvider):
 
                     # Filter unseeded torrent
                     if seeders < self.minseed or leechers < self.minleech:
-                        if mode is not 'RSS':
+                        if mode != 'RSS':
                             logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                         continue
 
-                    if self.confirmed and not verified and mode is not 'RSS':
+                    if self.confirmed and not verified and mode != 'RSS':
                         logger.log(u"Found result " + title + " but that doesn't seem like a verified result so I'm ignoring it", logger.DEBUG)
                         continue
 
                     item = title, download_url, size, seeders, leechers
-                    if mode is not 'RSS':
+                    if mode != 'RSS':
                         logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                     items[mode].append(item)
diff --git a/sickbeard/providers/pretome.py b/sickbeard/providers/pretome.py
index f165135dd6b09a1ebf9ae419bf5743b6e7edc7e4..30dbac2d2efaa2d74f989ae7ab462f28c16315f0 100644
--- a/sickbeard/providers/pretome.py
+++ b/sickbeard/providers/pretome.py
@@ -91,7 +91,7 @@ class PretomeProvider(generic.TorrentProvider):
             logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
             for search_string in search_params[mode]:
 
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s " % search_string, logger.DEBUG)
 
                 searchURL = self.urls['search'] % (urllib.quote(search_string.encode('utf-8')), self.categories)
@@ -148,12 +148,12 @@ class PretomeProvider(generic.TorrentProvider):
 
                             # Filter unseeded torrent
                             if seeders < self.minseed or leechers < self.minleech:
-                                if mode is not 'RSS':
+                                if mode != 'RSS':
                                     logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                                 continue
 
                             item = title, download_url, size, seeders, leechers
-                            if mode is not 'RSS':
+                            if mode != 'RSS':
                                 logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                             items[mode].append(item)
diff --git a/sickbeard/providers/rarbg.py b/sickbeard/providers/rarbg.py
index 38921fb59daeefb9109ed48785645f571476ecdb..f2b2bb362f26604ff8559eacfa3625d7b8456ee9 100644
--- a/sickbeard/providers/rarbg.py
+++ b/sickbeard/providers/rarbg.py
@@ -108,7 +108,7 @@ class RarbgProvider(generic.TorrentProvider):
         if not self._doLogin():
             return results
 
-        if epObj != None:
+        if epObj is not None:
             ep_indexerid = epObj.show.indexerid
             ep_indexer = epObj.show.indexer
         else:
@@ -119,7 +119,7 @@ class RarbgProvider(generic.TorrentProvider):
             logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
             for search_string in search_params[mode]:
 
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s " % search_string, logger.DEBUG)
 
                 if mode is 'RSS':
@@ -226,7 +226,7 @@ class RarbgProvider(generic.TorrentProvider):
                                 continue
 
                             item = title, download_url, size, seeders, leechers
-                            if mode is not 'RSS':
+                            if mode != 'RSS':
                                 logger.log(u"Found result: %s " % title, logger.DEBUG)
                             items[mode].append(item)
 
diff --git a/sickbeard/providers/scc.py b/sickbeard/providers/scc.py
index 67159e3e5867a320d5379ecffbbd55e513348120..c02ac02d5a63bd6fb2dc7b404fbf5d7525129953 100644
--- a/sickbeard/providers/scc.py
+++ b/sickbeard/providers/scc.py
@@ -89,10 +89,10 @@ class SCCProvider(generic.TorrentProvider):
         items = {'Season': [], 'Episode': [], 'RSS': []}
 
         for mode in search_strings.keys():
-            if mode is not 'RSS':
+            if mode != 'RSS':
                 logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
             for search_string in search_strings[mode]:
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s " % search_string, logger.DEBUG)
 
                 searchURL = self.urls['search'] % (urllib.quote(search_string), self.categories[search_mode])
@@ -140,12 +140,12 @@ class SCCProvider(generic.TorrentProvider):
 
                         # Filter unseeded torrent
                         if seeders < self.minseed or leechers < self.minleech:
-                            if mode is not 'RSS':
+                            if mode != 'RSS':
                                 logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                             continue
 
                         item = title, download_url, size, seeders, leechers
-                        if mode is not 'RSS':
+                        if mode != 'RSS':
                             logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                         items[mode].append(item)
diff --git a/sickbeard/providers/scenetime.py b/sickbeard/providers/scenetime.py
index 92dabe2a0ce0a4df2f9aa4577ff8557b626a943f..c4136a07c4fc8a385fce958b5a2b1dc36e35ceda 100644
--- a/sickbeard/providers/scenetime.py
+++ b/sickbeard/providers/scenetime.py
@@ -81,7 +81,7 @@ class SceneTimeProvider(generic.TorrentProvider):
             logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
             for search_string in search_params[mode]:
 
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s " % search_string, logger.DEBUG)
 
                 searchURL = self.urls['search'] % (urllib.quote(search_string), self.categories)
@@ -132,12 +132,12 @@ class SceneTimeProvider(generic.TorrentProvider):
 
                             # Filter unseeded torrent
                             if seeders < self.minseed or leechers < self.minleech:
-                                if mode is not 'RSS':
+                                if mode != 'RSS':
                                     logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                                 continue
 
                             item = title, download_url, size, seeders, leechers
-                            if mode is not 'RSS':
+                            if mode != 'RSS':
                                 logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                             items[mode].append(item)
diff --git a/sickbeard/providers/speedcd.py b/sickbeard/providers/speedcd.py
index a3c5377a8a474674e1f596e97e7706df4a813924..b5fe4feab1f0d11e383934e3c1e9f06172bb4cbb 100644
--- a/sickbeard/providers/speedcd.py
+++ b/sickbeard/providers/speedcd.py
@@ -80,7 +80,7 @@ class SpeedCDProvider(generic.TorrentProvider):
             logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
             for search_string in search_params[mode]:
 
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s " % search_string, logger.DEBUG)
 
                 search_string = '+'.join(search_string.split())
@@ -114,12 +114,12 @@ class SpeedCDProvider(generic.TorrentProvider):
 
                     # Filter unseeded torrent
                     if seeders < self.minseed or leechers < self.minleech:
-                        if mode is not 'RSS':
+                        if mode != 'RSS':
                             logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                         continue
 
                     item = title, download_url, size, seeders, leechers
-                    if mode is not 'RSS':
+                    if mode != 'RSS':
                         logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                     items[mode].append(item)
diff --git a/sickbeard/providers/strike.py b/sickbeard/providers/strike.py
index e64a6915130f529723e3b2c020262b833b7af1ed..a761eb5b893df139042b0afce2c2c95a6f9e05f1 100644
--- a/sickbeard/providers/strike.py
+++ b/sickbeard/providers/strike.py
@@ -1,5 +1,6 @@
-# Author: matigonkas
-# URL: https://github.com/SickRage/sickrage
+# coding=utf-8
+# Author: Gonçalo (aka duramato) <matigonkas@outlook.com>
+# URL: https://github.com/SickRage/SickRage
 #
 # This file is part of SickRage.
 #
@@ -41,7 +42,7 @@ class STRIKEProvider(generic.TorrentProvider):
             logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
             for search_string in search_strings[mode]:
 
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: " + search_string.strip(), logger.DEBUG)
 
                 searchURL = self.url + "api/v2/torrents/search/?category=TV&phrase=" + search_string
@@ -65,11 +66,11 @@ class STRIKEProvider(generic.TorrentProvider):
 
                     # Filter unseeded torrent
                     if seeders < self.minseed or leechers < self.minleech:
-                        if mode is not 'RSS':
+                        if mode != 'RSS':
                             logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                         continue
 
-                    if mode is not 'RSS':
+                    if mode != 'RSS':
                         logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                     item = title, download_url, size, seeders, leechers
@@ -91,12 +92,14 @@ class StrikeCache(tvcache.TVCache):
     def __init__(self, provider_obj):
 
         tvcache.TVCache.__init__(self, provider_obj)
-
-        # set this 0 to suppress log line, since we aren't updating it anyways
-        self.minTime = 0
+        
+        # Cache results for 10 min
+        self.minTime = 10
 
     def _getRSSData(self):
-        # no rss for getstrike.net afaik, also can't search with empty string
-        return {'entries': {}}
+        
+        # Use this hacky way for RSS search since most results will use this codec       
+        search_params = {'RSS': ['x264']}
+        return {'entries': self.provider._doSearch(search_params)}
 
 provider = STRIKEProvider()
diff --git a/sickbeard/providers/t411.py b/sickbeard/providers/t411.py
index 4a1d4d2f9aff776ad6ff46cee3d7720a1a6dc29f..55375b41a8fff87e4a3c5844b70ed59a12af272b 100644
--- a/sickbeard/providers/t411.py
+++ b/sickbeard/providers/t411.py
@@ -90,7 +90,7 @@ class T411Provider(generic.TorrentProvider):
             logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
             for search_string in search_params[mode]:
 
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s " % search_string, logger.DEBUG)
 
                 searchURLS = ([self.urls['search'] % (search_string, u) for u in self.subcategories], [self.urls['rss']])[mode is 'RSS']
@@ -101,11 +101,11 @@ class T411Provider(generic.TorrentProvider):
                         continue
 
                     try:
-                        if 'torrents' not in data and mode is not 'RSS':
+                        if 'torrents' not in data and mode != 'RSS':
                             logger.log(u"Data returned from provider does not contain any torrents", logger.DEBUG)
                             continue
 
-                        torrents = data['torrents'] if mode is not 'RSS' else data
+                        torrents = data['torrents'] if mode != 'RSS' else data
 
                         if not torrents:
                             logger.log(u"Data returned from provider does not contain any torrents", logger.DEBUG)
@@ -129,16 +129,16 @@ class T411Provider(generic.TorrentProvider):
 
                                 # Filter unseeded torrent
                                 if seeders < self.minseed or leechers < self.minleech:
-                                    if mode is not 'RSS':
+                                    if mode != 'RSS':
                                         logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                                     continue
 
-                                if self.confirmed and not verified and mode is not 'RSS':
+                                if self.confirmed and not verified and mode != 'RSS':
                                     logger.log(u"Found result " + title + " but that doesn't seem like a verified result so I'm ignoring it", logger.DEBUG)
                                     continue
 
                                 item = title, download_url, size, seeders, leechers
-                                if mode is not 'RSS':
+                                if mode != 'RSS':
                                     logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                                 items[mode].append(item)
diff --git a/sickbeard/providers/thepiratebay.py b/sickbeard/providers/thepiratebay.py
index aba4c0b2ed1997f8b8c497597a9a9766d8f2e16a..159a375cef615d116630c7d9a535c4035b50e98d 100644
--- a/sickbeard/providers/thepiratebay.py
+++ b/sickbeard/providers/thepiratebay.py
@@ -18,8 +18,8 @@
 
 
 import re
+import posixpath # Must use posixpath
 from urllib import urlencode
-
 from sickbeard import logger
 from sickbeard import tvcache
 from sickbeard.providers import generic
@@ -42,12 +42,14 @@ class ThePirateBayProvider(generic.TorrentProvider):
         self.cache = ThePirateBayCache(self)
 
         self.urls = {
-            'base_url': 'https://pirateproxy.pl/',
-            'search': 'https://pirateproxy.pl/s/',
-            'rss': 'https://pirateproxy.pl/tv/latest'
+            'base_url': 'https://thepiratebay.gd/',
+            'search': 'https://thepiratebay.gd/s/',
+            'rss': 'https://thepiratebay.gd/tv/latest'
         }
 
         self.url = self.urls['base_url']
+        self.custom_url = None
+
         self.headers.update({'User-Agent': USER_AGENT})
 
         """
@@ -75,14 +77,17 @@ class ThePirateBayProvider(generic.TorrentProvider):
 
                 self.search_params.update({'q': search_string.strip()})
 
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: " + search_string, logger.DEBUG)
 
                 searchURL = self.urls[('search', 'rss')[mode is 'RSS']] + '?' + urlencode(self.search_params)
+                if self.custom_url:
+                    searchURL = posixpath.join(self.custom_url, searchURL.split(self.url)[1].lstrip('/')) # Must use posixpath
+
                 logger.log(u"Search URL: %s" % searchURL, logger.DEBUG)
                 data = self.getURL(searchURL)
-                # data = self.getURL(self.urls[('search', 'rss')[mode is 'RSS']], params=self.search_params)
                 if not data:
+                    logger.log(u'URL did not return data, maybe try a custom url, or a different one', logger.DEBUG)
                     continue
 
                 matches = re.compile(self.re_title_url, re.DOTALL).finditer(data)
@@ -99,18 +104,18 @@ class ThePirateBayProvider(generic.TorrentProvider):
 
                     # Filter unseeded torrent
                     if seeders < self.minseed or leechers < self.minleech:
-                        if mode is not 'RSS':
+                        if mode != 'RSS':
                             logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                         continue
 
                     # Accept Torrent only from Good People for every Episode Search
                     if self.confirmed and re.search(r'(VIP|Trusted|Helper|Moderator)', torrent.group(0)) is None:
-                        if mode is not 'RSS':
+                        if mode != 'RSS':
                             logger.log(u"Found result %s but that doesn't seem like a trusted result so I'm ignoring it" % title, logger.DEBUG)
                         continue
 
                     item = title, download_url, size, seeders, leechers
-                    if mode is not 'RSS':
+                    if mode != 'RSS':
                         logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                     items[mode].append(item)
diff --git a/sickbeard/providers/titansoftv.py b/sickbeard/providers/titansoftv.py
index c94ba6ef53ec9044250e4b808cbdb45579c79295..cea172b8bd6cd41ed9a5afab296725cd490a8e14 100644
--- a/sickbeard/providers/titansoftv.py
+++ b/sickbeard/providers/titansoftv.py
@@ -51,6 +51,7 @@ class TitansOfTVProvider(generic.TorrentProvider):
 
         if 'error' in data:
             logger.log(u"Invalid api key. Check your settings", logger.WARNING)
+            return False
 
         return True
 
@@ -95,7 +96,7 @@ class TitansOfTVProvider(generic.TorrentProvider):
 
                 # Filter unseeded torrent
                 # if seeders < self.minseed or leechers < self.minleech:
-                #    if mode is not 'RSS':
+                #    if mode != 'RSS':
                 #        logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                 #    continue
 
diff --git a/sickbeard/providers/tntvillage.py b/sickbeard/providers/tntvillage.py
index 5b4fc5d4fb8036ee739ac052dc0d46535eb13c13..c00842148d894428c82c6f309e7afa44dc9d37f7 100644
--- a/sickbeard/providers/tntvillage.py
+++ b/sickbeard/providers/tntvillage.py
@@ -300,12 +300,12 @@ class TNTVillageProvider(generic.TorrentProvider):
                     if last_page:
                         break
 
-                    if mode is not 'RSS':
+                    if mode != 'RSS':
                         searchURL = (self.urls['search_page'] + '&filter={2}').format(z, self.categories, search_string)
                     else:
                         searchURL = self.urls['search_page'].format(z, self.categories)
 
-                    if mode is not 'RSS':
+                    if mode != 'RSS':
                         logger.log(u"Search string: %s " % search_string, logger.DEBUG)
 
                     logger.log(u"Search URL: %s" % searchURL, logger.DEBUG)
@@ -379,12 +379,12 @@ class TNTVillageProvider(generic.TorrentProvider):
 
                                 # Filter unseeded torrent
                                 if seeders < self.minseed or leechers < self.minleech:
-                                    if mode is not 'RSS':
+                                    if mode != 'RSS':
                                         logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                                     continue
 
                                 item = title, download_url, size, seeders, leechers
-                                if mode is not 'RSS':
+                                if mode != 'RSS':
                                     logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                                 items[mode].append(item)
diff --git a/sickbeard/providers/tokyotoshokan.py b/sickbeard/providers/tokyotoshokan.py
index ed7acce2a9950d6992d484e8cee6acf92e6ee3fd..6a078faa8de2c8459c887366350a89d79b1d4992 100644
--- a/sickbeard/providers/tokyotoshokan.py
+++ b/sickbeard/providers/tokyotoshokan.py
@@ -95,7 +95,7 @@ class TokyoToshokanProvider(generic.TorrentProvider):
 
                         # Filter unseeded torrent
                         # if seeders < self.minseed or leechers < self.minleech:
-                        #    if mode is not 'RSS':
+                        #    if mode != 'RSS':
                         #        logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                         #    continue
 
diff --git a/sickbeard/providers/torrentbytes.py b/sickbeard/providers/torrentbytes.py
index 30711eda2f6b62ddb9b4f378dc1e1e1987854f98..0a0d3a68a453a52169ef4ee46912320b052ea94d 100644
--- a/sickbeard/providers/torrentbytes.py
+++ b/sickbeard/providers/torrentbytes.py
@@ -84,7 +84,7 @@ class TorrentBytesProvider(generic.TorrentProvider):
             logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
             for search_string in search_params[mode]:
 
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s " % search_string, logger.DEBUG)
 
                 searchURL = self.urls['search'] % (urllib.quote(search_string.encode('utf-8')), self.categories)
@@ -147,12 +147,12 @@ class TorrentBytesProvider(generic.TorrentProvider):
 
                             # Filter unseeded torrent
                             if seeders < self.minseed or leechers < self.minleech:
-                                if mode is not 'RSS':
+                                if mode != 'RSS':
                                     logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                                 continue
 
                             item = title, download_url, size, seeders, leechers
-                            if mode is not 'RSS':
+                            if mode != 'RSS':
                                 logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                             items[mode].append(item)
diff --git a/sickbeard/providers/torrentday.py b/sickbeard/providers/torrentday.py
index 1d84445385dd6c11e7df7f754b65aed7db1b98dd..62550dab1691fff5d1d277d98ce36e2beb7ced46 100644
--- a/sickbeard/providers/torrentday.py
+++ b/sickbeard/providers/torrentday.py
@@ -102,7 +102,7 @@ class TorrentDayProvider(generic.TorrentProvider):
             logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
             for search_string in search_params[mode]:
 
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s " % search_string, logger.DEBUG)
 
                 search_string = '+'.join(search_string.split())
@@ -138,12 +138,12 @@ class TorrentDayProvider(generic.TorrentProvider):
 
                     # Filter unseeded torrent
                     if seeders < self.minseed or leechers < self.minleech:
-                        if mode is not 'RSS':
+                        if mode != 'RSS':
                             logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                         continue
 
                     item = title, download_url, size, seeders, leechers
-                    if mode is not 'RSS':
+                    if mode != 'RSS':
                         logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                     items[mode].append(item)
diff --git a/sickbeard/providers/torrentleech.py b/sickbeard/providers/torrentleech.py
index 2ab8987dcaa441a6788cb59c680f8442852d3d8f..7ccac63bb57895d8fd88f7fac4a85e164b373c66 100644
--- a/sickbeard/providers/torrentleech.py
+++ b/sickbeard/providers/torrentleech.py
@@ -125,12 +125,12 @@ class TorrentLeechProvider(generic.TorrentProvider):
 
                             # Filter unseeded torrent
                             if seeders < self.minseed or leechers < self.minleech:
-                                if mode is not 'RSS':
+                                if mode != 'RSS':
                                     logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                                 continue
 
                             item = title, download_url, size, seeders, leechers
-                            if mode is not 'RSS':
+                            if mode != 'RSS':
                                 logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                             items[mode].append(item)
diff --git a/sickbeard/providers/torrentproject.py b/sickbeard/providers/torrentproject.py
index b9c52266d88910d4ea7ab80137ec22b5279499cc..2f84b81c8309da3ba1b787babf7bfc507026e772 100644
--- a/sickbeard/providers/torrentproject.py
+++ b/sickbeard/providers/torrentproject.py
@@ -48,7 +48,7 @@ class TORRENTPROJECTProvider(generic.TorrentProvider):
         for mode in search_strings.keys():  # Mode = RSS, Season, Episode
             logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
             for search_string in search_strings[mode]:
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s " % search_string, logger.DEBUG)
 
 
@@ -68,7 +68,7 @@ class TORRENTPROJECTProvider(generic.TorrentProvider):
                     seeders = helpers.tryInt(torrents[i]["seeds"], 1)
                     leechers = helpers.tryInt(torrents[i]["leechs"], 0)
                     if seeders < self.minseed or leechers < self.minleech:
-                        if mode is not 'RSS':
+                        if mode != 'RSS':
                             logger.log(u"Torrent doesn't meet minimum seeds & leechers not selecting : %s" % title, logger.DEBUG)
                         continue
 
@@ -77,7 +77,7 @@ class TORRENTPROJECTProvider(generic.TorrentProvider):
 
                     try:
                         assert seeders < 10
-                        assert mode is not 'RSS'
+                        assert mode != 'RSS'
                         logger.log(u"Torrent has less than 10 seeds getting dyn trackers: " + title, logger.DEBUG)
                         trackerUrl = self.urls['api'] + "" + t_hash + "/trackers_json"
                         jdata = self.getURL(trackerUrl, json=True)
@@ -91,7 +91,7 @@ class TORRENTPROJECTProvider(generic.TorrentProvider):
 
                     item = title, download_url, size, seeders, leechers
 
-                    if mode is not 'RSS':
+                    if mode != 'RSS':
                         logger.log(u"Found result: %s" % title, logger.DEBUG)
 
                     items[mode].append(item)
diff --git a/sickbeard/providers/torrentz.py b/sickbeard/providers/torrentz.py
index f1dd3e8ffaba848dfced0340fdbe8c4e770df8c5..576a8ca114fea8a6aef3a229e2cb20761e4d8e2a 100644
--- a/sickbeard/providers/torrentz.py
+++ b/sickbeard/providers/torrentz.py
@@ -63,7 +63,7 @@ class TORRENTZProvider(generic.TorrentProvider):
         for mode in search_strings:
             for search_string in search_strings[mode]:
                 search_url = self.urls['verified'] if self.confirmed else self.urls['feed']
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     search_url += '?q=' + urllib.parse.quote_plus(search_string)
 
                 logger.log(search_url)
@@ -112,7 +112,7 @@ class TORRENTZProvider(generic.TorrentProvider):
 
                     # Filter unseeded torrent
                     if seeders < self.minseed or leechers < self.minleech:
-                        if mode is not 'RSS':
+                        if mode != 'RSS':
                             logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                         continue
 
diff --git a/sickbeard/providers/transmitthenet.py b/sickbeard/providers/transmitthenet.py
index e4e5b30e79f0ef4931a0115c74dedd36880a1d24..2049b638ddb4b600d2425a1e32268f8db2d1c2c4 100644
--- a/sickbeard/providers/transmitthenet.py
+++ b/sickbeard/providers/transmitthenet.py
@@ -90,7 +90,7 @@ class TransmitTheNetProvider(generic.TorrentProvider):
         for mode in search_strings.keys():
             for search_string in search_strings[mode]:
 
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s " % search_string, logger.DEBUG)
 
                 data = self.getURL(self.urls['index'], params=self.search_params)
@@ -133,12 +133,12 @@ class TransmitTheNetProvider(generic.TorrentProvider):
 
                             # Filter unseeded torrent
                             if seeders < self.minseed or leechers < self.minleech:
-                                if mode is not 'RSS':
+                                if mode != 'RSS':
                                     logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                                 continue
 
                             item = title, download_url, size, seeders, leechers
-                            if mode is not 'RSS':
+                            if mode != 'RSS':
                                 logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                             items[mode].append(item)
diff --git a/sickbeard/providers/tvchaosuk.py b/sickbeard/providers/tvchaosuk.py
index 49aab00075b0503218f75e6772d567116bda63eb..dee2176c41791fff58c385a2e5429c28f4b228b4 100644
--- a/sickbeard/providers/tvchaosuk.py
+++ b/sickbeard/providers/tvchaosuk.py
@@ -131,7 +131,7 @@ class TVChaosUKProvider(generic.TorrentProvider):
             logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
             for search_string in search_strings[mode]:
 
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s " % search_string, logger.DEBUG)
 
                 self.search_params['keywords'] = search_string.strip()
@@ -156,7 +156,7 @@ class TVChaosUKProvider(generic.TorrentProvider):
 
                             # Filter unseeded torrent
                             if seeders < self.minseed or leechers < self.minleech:
-                                if mode is not 'RSS':
+                                if mode != 'RSS':
                                     logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                                 continue
 
@@ -176,7 +176,7 @@ class TVChaosUKProvider(generic.TorrentProvider):
                             size = -1
 
                             item = title, download_url, size, seeders, leechers
-                            if mode is not 'RSS':
+                            if mode != 'RSS':
                                 logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                             items[mode].append(item)
diff --git a/sickbeard/providers/xthor.py b/sickbeard/providers/xthor.py
index 71cf973b5fb0310756133e72110c6c825f0b0370..2805fbe5b6270d4f4b9cf6ea7511fab4600510a4 100644
--- a/sickbeard/providers/xthor.py
+++ b/sickbeard/providers/xthor.py
@@ -80,7 +80,7 @@ class XthorProvider(generic.TorrentProvider):
             logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
             for search_string in search_params[mode]:
 
-                if mode is not 'RSS':
+                if mode != 'RSS':
                     logger.log(u"Search string: %s " % search_string, logger.DEBUG)
 
                 searchURL = self.urlsearch % (urllib.quote(search_string), self.categories)
@@ -109,12 +109,12 @@ class XthorProvider(generic.TorrentProvider):
 
                                 # Filter unseeded torrent
                                 # if seeders < self.minseed or leechers < self.minleech:
-                                #    if mode is not 'RSS':
+                                #    if mode != 'RSS':
                                 #        logger.log(u"Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})".format(title, seeders, leechers), logger.DEBUG)
                                 #    continue
 
                                 item = title, download_url, size, seeders, leechers
-                                if mode is not 'RSS':
+                                if mode != 'RSS':
                                     logger.log(u"Found result: %s " % title, logger.DEBUG)
 
                                 items[mode].append(item)
diff --git a/sickbeard/sab.py b/sickbeard/sab.py
index b22c1b7f470fc01c3f4f31ebb7f62a492016b3e7..4c1cc0e7d8952ff68d86f03c4fc8b99c6191ce9a 100644
--- a/sickbeard/sab.py
+++ b/sickbeard/sab.py
@@ -44,11 +44,11 @@ def sendNZB(nzb):
 
     # set up a dict with the URL params in it
     params = {}
-    if sickbeard.SAB_USERNAME != None:
+    if sickbeard.SAB_USERNAME is not None:
         params['ma_username'] = sickbeard.SAB_USERNAME
-    if sickbeard.SAB_PASSWORD != None:
+    if sickbeard.SAB_PASSWORD is not None:
         params['ma_password'] = sickbeard.SAB_PASSWORD
-    if sickbeard.SAB_APIKEY != None:
+    if sickbeard.SAB_APIKEY is not None:
         params['apikey'] = sickbeard.SAB_APIKEY
     category = sickbeard.SAB_CATEGORY
     if nzb.show.is_anime:
@@ -61,7 +61,7 @@ def sendNZB(nzb):
             if nzb.show.is_anime:
                 category = sickbeard.SAB_CATEGORY_ANIME_BACKLOG
 
-    if category != None:
+    if category is not None:
         params['cat'] = category
 
     # use high priority if specified (recently aired episode)
@@ -120,7 +120,7 @@ def sendNZB(nzb):
         return False
 
     # this means we couldn't open the connection or something just as bad
-    if f == None:
+    if f is None:
         logger.log(u"No data returned from SABnzbd, NZB not sent", logger.ERROR)
         return False
 
@@ -202,7 +202,7 @@ def _sabURLOpenSimple(url):
     except httplib.InvalidURL, e:
         logger.log(u"Invalid SAB host, check your config: " + ex(e), logger.ERROR)
         return False, "Invalid SAB host"
-    if f == None:
+    if f is None:
         logger.log(u"No data returned from SABnzbd", logger.ERROR)
         return False, "No data returned from SABnzbd"
     else:
diff --git a/sickbeard/search.py b/sickbeard/search.py
index 57f22ea032da0afc316f1f82b575c091344fd600..293016c7039b546878ffa48e3bb7593154954670 100644
--- a/sickbeard/search.py
+++ b/sickbeard/search.py
@@ -52,7 +52,7 @@ def _downloadResult(result):
     """
 
     resProvider = result.provider
-    if resProvider == None:
+    if resProvider is None:
         logger.log(u"Invalid provider name - this is a coding error, report it please", logger.ERROR)
         return False
 
@@ -79,7 +79,7 @@ def _downloadResult(result):
         except EnvironmentError, e:
             logger.log(u"Error trying to save NZB to black hole: " + ex(e), logger.ERROR)
             newResult = False
-    elif resProvider.providerType == "torrent":
+    elif result.resultType == "torrent":
         newResult = resProvider.downloadResult(result)
     else:
         logger.log(u"Invalid provider type - this is a coding error, report it please", logger.ERROR)
@@ -106,7 +106,7 @@ def snatchEpisode(result, endStatus=SNATCHED):
         for curEp in result.episodes:
             if datetime.date.today() - curEp.airdate <= datetime.timedelta(days=7):
                 result.priority = 1
-    if re.search(r'(^|[\. _-])(proper|repack)([\. _-]|$)', result.name, re.I) != None:
+    if re.search(r'(^|[\. _-])(proper|repack)([\. _-]|$)', result.name, re.I) is not None:
         endStatus = SNATCHED_PROPER
 
     if result.url.startswith('magnet') or result.url.endswith('torrent'):
@@ -480,7 +480,7 @@ def searchProviders(show, episodes, manualSearch=False, downCurQuality=False):
         search_mode = curProvider.search_mode
 
         # Always search for episode when manually searching when in sponly
-        if search_mode == 'sponly' and manualSearch == True:
+        if search_mode == 'sponly' and manualSearch is True:
             search_mode = 'eponly'
 
         while True:
diff --git a/sickbeard/searchBacklog.py b/sickbeard/searchBacklog.py
index b25ca0801b5400bafb6a130b44064adad3e3f0c3..5f9f8d9a48ce8d3fffd7ffe7c90cf4a483615267 100644
--- a/sickbeard/searchBacklog.py
+++ b/sickbeard/searchBacklog.py
@@ -103,8 +103,9 @@ class BacklogSearcher:
 
                 backlog_queue_item = search_queue.BacklogQueueItem(curShow, segment)
                 sickbeard.searchQueueScheduler.action.add_item(backlog_queue_item)  # @UndefinedVariable
-            else:
-                logger.log(u"Nothing needs to be downloaded for {show_name}, skipping".format(show_name=curShow.name),logger.DEBUG)
+
+            if not segments:
+                logger.log(u"Nothing needs to be downloaded for %s, skipping" % curShow.name, logger.DEBUG)
 
         # don't consider this an actual backlog search if we only did recent eps
         # or if we only did certain shows
@@ -123,7 +124,7 @@ class BacklogSearcher:
 
         if len(sqlResults) == 0:
             lastBacklog = 1
-        elif sqlResults[0]["last_backlog"] == None or sqlResults[0]["last_backlog"] == "":
+        elif sqlResults[0]["last_backlog"] is None or sqlResults[0]["last_backlog"] == "":
             lastBacklog = 1
         else:
             lastBacklog = int(sqlResults[0]["last_backlog"])
@@ -143,7 +144,7 @@ class BacklogSearcher:
         logger.log(u"Seeing if we need anything from {show_name}".format(show_name=show.name), logger.DEBUG)
 
         myDB = db.DBConnection()
-        sqlResults = myDB.select("SELECT status, season, episode FROM tv_episodes WHERE season > 0 AND airdate > ? AND showid = ?",
+        sqlResults = myDB.select("SELECT status, season, episode FROM tv_episodes WHERE airdate > ? AND showid = ?",
                 [fromDate.toordinal(), show.indexerid])
 
         # check through the list of statuses to see if we want any
diff --git a/sickbeard/show_queue.py b/sickbeard/show_queue.py
index c195832099dcb91a095eff98712106131be65b32..9cec79e7257cbdbd4844e319cfb7ffba7faf8995 100644
--- a/sickbeard/show_queue.py
+++ b/sickbeard/show_queue.py
@@ -48,7 +48,7 @@ class ShowQueue(generic_queue.GenericQueue):
         return show.indexerid in [x.show.indexerid if x.show else 0 for x in self.queue if x.action_id in actions]
 
     def _isBeingSomethinged(self, show, actions):
-        return self.currentItem != None and show == self.currentItem.show and \
+        return self.currentItem is not None and show == self.currentItem.show and \
                self.currentItem.action_id in actions
 
     def isInUpdateQueue(self, show):
@@ -79,7 +79,7 @@ class ShowQueue(generic_queue.GenericQueue):
         return self._isBeingSomethinged(show, (ShowQueueActions.SUBTITLE,))
 
     def _getLoadingShowList(self):
-        return [x for x in self.queue + [self.currentItem] if x != None and x.isLoading]
+        return [x for x in self.queue + [self.currentItem] if x is not None and x.isLoading]
 
     loadingShowList = property(_getLoadingShowList)
 
@@ -256,7 +256,7 @@ class QueueItemAdd(ShowQueueItem):
         Returns the show name if there is a show object created, if not returns
         the dir that the show is being added to.
         """
-        if self.show == None:
+        if self.show is None:
             return self.showDir
         return self.show.name
 
@@ -267,7 +267,7 @@ class QueueItemAdd(ShowQueueItem):
         Returns True if we've gotten far enough to have a show object, or False
         if we still only know the folder name.
         """
-        if self.show == None:
+        if self.show is None:
             return True
         return False
 
@@ -352,13 +352,13 @@ class QueueItemAdd(ShowQueueItem):
 
             # set up initial values
             self.show.location = self.showDir
-            self.show.subtitles = self.subtitles if self.subtitles != None else sickbeard.SUBTITLES_DEFAULT
+            self.show.subtitles = self.subtitles if self.subtitles is not None else sickbeard.SUBTITLES_DEFAULT
             self.show.quality = self.quality if self.quality else sickbeard.QUALITY_DEFAULT
-            self.show.flatten_folders = self.flatten_folders if self.flatten_folders != None else sickbeard.FLATTEN_FOLDERS_DEFAULT
-            self.show.anime = self.anime if self.anime != None else sickbeard.ANIME_DEFAULT
-            self.show.scene = self.scene if self.scene != None else sickbeard.SCENE_DEFAULT
-            self.show.archive_firstmatch = self.archive if self.archive != None else sickbeard.ARCHIVE_DEFAULT
-            self.show.paused = self.paused if self.paused != None else False
+            self.show.flatten_folders = self.flatten_folders if self.flatten_folders is not None else sickbeard.FLATTEN_FOLDERS_DEFAULT
+            self.show.anime = self.anime if self.anime is not None else sickbeard.ANIME_DEFAULT
+            self.show.scene = self.scene if self.scene is not None else sickbeard.SCENE_DEFAULT
+            self.show.archive_firstmatch = self.archive if self.archive is not None else sickbeard.ARCHIVE_DEFAULT
+            self.show.paused = self.paused if self.paused is not None else False
 
             # set up default new/missing episode status
             logger.log(u"Setting all episodes to the specified default status: " + str(self.show.default_ep_status))
@@ -479,7 +479,7 @@ class QueueItemAdd(ShowQueueItem):
         self.finish()
 
     def _finishEarly(self):
-        if self.show != None:
+        if self.show is not None:
             sickbeard.showQueueScheduler.action.removeShow(self.show)
 
         self.finish()
diff --git a/sickbeard/tv.py b/sickbeard/tv.py
index 6999fb4ef6527ecc6927084fe00b245d45650141..93ef682dbc8324b46d46c3af1cba6da17157c016 100644
--- a/sickbeard/tv.py
+++ b/sickbeard/tv.py
@@ -115,7 +115,7 @@ class TVShow(object):
         self.release_groups = None
 
         otherShow = helpers.findCertainShow(sickbeard.showList, self.indexerid)
-        if otherShow != None:
+        if otherShow is not None:
             raise MultipleShowObjectsException("Can't create a show if it already exists")
 
         self.loadFromDB()
@@ -283,7 +283,7 @@ class TVShow(object):
             else:
                 ep = TVEpisode(self, season, episode)
 
-            if ep != None:
+            if ep is not None:
                 self.episodes[season][episode] = ep
 
         return self.episodes[season][episode]
@@ -447,7 +447,7 @@ class TVShow(object):
                 curEpisode.release_name = ep_file_name
 
             # store the reference in the show
-            if curEpisode != None:
+            if curEpisode is not None:
                 if self.subtitles:
                     try:
                         curEpisode.refreshSubtitles()
@@ -644,7 +644,7 @@ class TVShow(object):
             return None
 
         # for now lets assume that any episode in the show dir belongs to that show
-        season = parse_result.season_number if parse_result.season_number != None else 1
+        season = parse_result.season_number if parse_result.season_number is not None else 1
         episodes = parse_result.episode_numbers
         rootEp = None
 
@@ -974,7 +974,7 @@ class TVShow(object):
                 "SELECT airdate, season, episode FROM tv_episodes WHERE showid = ? AND airdate >= ? AND status IN (?,?) ORDER BY airdate ASC LIMIT 1",
                 [self.indexerid, datetime.date.today().toordinal(), UNAIRED, WANTED])
 
-            if sqlResults == None or len(sqlResults) == 0:
+            if sqlResults is None or len(sqlResults) == 0:
                 logger.log(str(self.indexerid) + u": No episode found... need to implement a show status", logger.DEBUG)
                 self.nextaired = ""
             else:
@@ -1298,37 +1298,23 @@ class TVShow(object):
             return Overview.SKIPPED
         elif epStatus in Quality.ARCHIVED:
             return Overview.GOOD
-        elif epStatus in Quality.DOWNLOADED + Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.FAILED + Quality.SNATCHED_BEST:
-
-            _, bestQualities = Quality.splitQuality(self.quality)  # @UnusedVariable
-            if bestQualities:
-                maxBestQuality = max(bestQualities)
-                minBestQuality = min(bestQualities)
-            else:
-                maxBestQuality = None
-                minBestQuality = None
-
+        elif epStatus in Quality.FAILED:
+            return Overview.WANTED
+        elif epStatus in Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.SNATCHED_BEST:
+            return Overview.SNATCHED
+        elif epStatus in Quality.DOWNLOADED:
+            anyQualities, bestQualities = Quality.splitQuality(self.quality)  # @UnusedVariable
             epStatus, curQuality = Quality.splitCompositeStatus(epStatus)
 
-            if epStatus == FAILED:
-                return Overview.WANTED
-            if epStatus == DOWNLOADED and curQuality == Quality.UNKNOWN:
-                return Overview.QUAL
-            elif epStatus in (SNATCHED, SNATCHED_PROPER, SNATCHED_BEST):
-                return Overview.SNATCHED
-            # if they don't want re-downloads then we call it good if they have anything
-            elif maxBestQuality == None:
-                return Overview.GOOD
-            # if the want only first match and already have one call it good
-            elif self.archive_firstmatch and curQuality in bestQualities:
-                return Overview.GOOD
-            # if they want only first match and current quality is higher than minimal best quality call it good
-            elif self.archive_firstmatch and minBestQuality != None and curQuality > minBestQuality:
+            if curQuality not in anyQualities + bestQualities:
+                if curQuality != Quality.UNKNOWN and curQuality > max(anyQualities):
+                    return Overview.GOOD
+                else:
+                    return Overview.QUAL
+            elif self.archive_firstmatch:
                 return Overview.GOOD
-            # if they have one but it's not the best they want then mark it as qual
-            elif curQuality < maxBestQuality:
+            elif bestQualities and curQuality not in bestQualities:
                 return Overview.QUAL
-            # if it's >= maxBestQuality then it's good
             else:
                 return Overview.GOOD
 
@@ -2047,7 +2033,7 @@ class TVEpisode(object):
                     singleName = False
                     break
 
-                if curGoodName == None:
+                if curGoodName is None:
                     curGoodName = match.group(1)
                 elif curGoodName != match.group(1):
                     singleName = False
@@ -2087,7 +2073,7 @@ class TVEpisode(object):
             if name:
                 name = helpers.remove_non_release_groups(helpers.remove_extension(name))
             else:
-                return ""
+                return ''
 
             try:
                 np = NameParser(name, showObj=show, naming_pattern=True)
@@ -2098,7 +2084,7 @@ class TVEpisode(object):
 
             if not parse_result.release_group:
                 return ''
-            return parse_result.release_group
+            return parse_result.release_group.strip('.- []{}')
 
         _, epQual = Quality.splitCompositeStatus(self.status)  # @UnusedVariable
 
@@ -2115,7 +2101,7 @@ class TVEpisode(object):
             if not rel_grp['location']:
                 del rel_grp['location']
         if hasattr(self, '_release_group'):  # from the release group field in db
-            rel_grp['database'] = self._release_group
+            rel_grp['database'] = self._release_group.strip('.- []{}')
             if not rel_grp['database']:
                 del rel_grp['database']
         if hasattr(self, 'release_name'):  # from the release name field in db
@@ -2195,14 +2181,14 @@ class TVEpisode(object):
         Manipulates an episode naming pattern and then fills the template in
         """
 
-        if pattern == None:
+        if pattern is None:
             pattern = sickbeard.NAMING_PATTERN
 
-        if multi == None:
+        if multi is None:
             multi = sickbeard.NAMING_MULTI_EP
 
         if sickbeard.NAMING_CUSTOM_ANIME:
-            if anime_type == None:
+            if anime_type is None:
                 anime_type = sickbeard.NAMING_ANIME
         else:
             anime_type = 3
@@ -2406,7 +2392,7 @@ class TVEpisode(object):
         Just the filename of the episode, formatted based on the naming settings
         """
 
-        if pattern == None:
+        if pattern is None:
             # we only use ABD if it's enabled, this is an ABD show, AND this is not a multi-ep
             if self.show.air_by_date and sickbeard.NAMING_CUSTOM_ABD and not self.relatedEps:
                 pattern = sickbeard.NAMING_ABD_PATTERN
diff --git a/sickbeard/tvcache.py b/sickbeard/tvcache.py
index 6c6ce5677c29117b532d7f9dd70be46af44da64c..5a9229bffa4c9fd47f5f07c2a828592891489ac3 100644
--- a/sickbeard/tvcache.py
+++ b/sickbeard/tvcache.py
@@ -298,7 +298,7 @@ class TVCache(object):
         myDB = self._getDB()
         sql = "SELECT * FROM [" + self.providerID + "] WHERE name LIKE '%.PROPER.%' OR name LIKE '%.REPACK.%'"
 
-        if date != None:
+        if date is not None:
             sql += " AND time >= " + str(int(time.mktime(date.timetuple())))
 
         propers_results = myDB.select(sql)
diff --git a/sickbeard/ui.py b/sickbeard/ui.py
index 3d7c559bf934d59c1d188a2b4c6d22ca90b50dc2..7b62dac3f416e2d5f430a96dd3768cb0179464bb 100644
--- a/sickbeard/ui.py
+++ b/sickbeard/ui.py
@@ -128,7 +128,7 @@ class ProgressIndicators():
 
         # if any of the progress indicators are done take them off the list
         for curPI in ProgressIndicators._pi[name]:
-            if curPI != None and curPI.percentComplete() == 100:
+            if curPI is not None and curPI.percentComplete() == 100:
                 ProgressIndicators._pi[name].remove(curPI)
 
         # return the list of progress indicators associated with this name
diff --git a/sickbeard/versionChecker.py b/sickbeard/versionChecker.py
index b3dbf50cd0bf4cac5c6032f00151a6a9bdd7e88a..64a05f2f2a52d9e27698220848c3af85f8645ed5 100644
--- a/sickbeard/versionChecker.py
+++ b/sickbeard/versionChecker.py
@@ -71,7 +71,7 @@ class CheckVersion(object):
                 if sickbeard.AUTO_UPDATE:
                     logger.log(u"New update found for SickRage, starting auto-updater ...")
                     ui.notifications.message('New update found for SickRage, starting auto-updater')
-                    if self.run_backup_if_safe() is True:
+                    if self.run_backup_if_safe():
                         if sickbeard.versionCheckScheduler.action.update():
                             logger.log(u"Update was successful!")
                             ui.notifications.message('Update was successful')
@@ -96,7 +96,7 @@ class CheckVersion(object):
             if not os.path.isdir(backupDir):
                 os.mkdir(backupDir)
 
-            if self._keeplatestbackup(backupDir) == True and self._backup(backupDir) == True:
+            if self._keeplatestbackup(backupDir) and self._backup(backupDir):
                 logger.log(u"Config backup successful, updating...")
                 ui.notifications.message('Backup', 'Config backup successful, updating...')
                 return True
@@ -155,22 +155,26 @@ class CheckVersion(object):
     def safe_to_update(self):
 
         def db_safe(self):
+            message = {
+                'equal': {
+                    'type': logger.DEBUG,
+                    'text': u"We can proceed with the update. New update has same DB version"},
+                'upgrade': {
+                    'type': logger.WARNING,
+                    'text': u"We can't proceed with the update. New update has a new DB version. Please manually update"},
+                'downgrade': {
+                    'type': logger.ERROR,
+                    'text': u"We can't proceed with the update. New update has a old DB version. It's not possible to downgrade"},
+            }
             try:
                 result = self.getDBcompare()
-                if result == 'equal':
-                    logger.log(u"We can proceed with the update. New update has same DB version", logger.DEBUG)
-                    return True
-                elif result == 'upgrade':
-                    logger.log(u"We can't proceed with the update. New update has a new DB version. Please manually update", logger.WARNING)
-                    return False
-                elif result == 'downgrade':
-                    logger.log(u"We can't proceed with the update. New update has a old DB version. It's not possible to downgrade", logger.ERROR)
-                    return False
+                if result in message:
+                    logger.log(message[result]['text'], message[result]['type'])  # unpack the result message into a log entry
                 else:
                     logger.log(u"We can't proceed with the update. Unable to check remote DB version. Error: %s" % result, logger.ERROR)
-                    return False
-            except Exception as e:
-                logger.log(u"We can't proceed with the update. Unable to compare DB version. Error: %s" % repr(e), logger.ERROR)
+                return result in ['equal']  # add future True results to the list
+            except Exception as error:
+                logger.log(u"We can't proceed with the update. Unable to compare DB version. Error: %s" % repr(error), logger.ERROR)
                 return False
 
         def postprocessor_safe():
@@ -193,7 +197,7 @@ class CheckVersion(object):
         postprocessor_safe = postprocessor_safe()
         showupdate_safe = showupdate_safe()
 
-        if db_safe == True and postprocessor_safe == True and showupdate_safe == True:
+        if db_safe and postprocessor_safe and showupdate_safe:
             logger.log(u"Proceeding with auto update", logger.DEBUG)
             return True
         else:
diff --git a/sickbeard/webapi.py b/sickbeard/webapi.py
index 4a4a8bd980c7466cc8209ebffb3558696582a208..11fb7e6ae0b110f9fe738372169bebb8913b7334 100644
--- a/sickbeard/webapi.py
+++ b/sickbeard/webapi.py
@@ -1,3 +1,4 @@
+# coding=utf-8
 # Author: Dennis Lutter <lad1337@gmail.com>
 # Author: Jonathon Saine <thezoggy@gmail.com>
 # URL: http://code.google.com/p/sickbeard/
@@ -16,7 +17,10 @@
 #
 # You should have received a copy of the GNU General Public License
 # along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
-# pylint: disable=W0223,E0202
+
+# TODO: break this up into separate files
+# pylint: disable=C0301,C0302
+# pylint: disable=E1101,E0202,C0111,C0103
 
 import io
 import os
@@ -64,8 +68,10 @@ from sickbeard.common import statusStrings
 try:
     import json
 except ImportError:
+    # pylint: disable=F0401
     import simplejson as json
 
+# pylint: disable=F0401
 from tornado.web import RequestHandler
 
 indexer_ids = ["indexerid", "tvdbid"]
@@ -109,13 +115,13 @@ class ApiHandler(RequestHandler):
 
         # set the output callback
         # default json
-        outputCallbackDict = {
+        output_callback_dict = {
             'default': self._out_as_json,
             'image': self._out_as_image,
         }
 
-        accessMsg = u"API :: " + self.request.remote_ip + " - gave correct API KEY. ACCESS GRANTED"
-        logger.log(accessMsg, logger.DEBUG)
+        access_msg = u"API :: " + self.request.remote_ip + " - gave correct API KEY. ACCESS GRANTED"
+        logger.log(access_msg, logger.DEBUG)
 
         # set the original call_dispatcher as the local _call_dispatcher
         _call_dispatcher = self.call_dispatcher
@@ -127,24 +133,24 @@ class ApiHandler(RequestHandler):
             del kwargs["profile"]
 
         try:
-            outDict = _call_dispatcher(args, kwargs)
+            out_dict = _call_dispatcher(args, kwargs)
         except Exception, e:  # real internal error oohhh nooo :(
             logger.log(u"API :: " + ex(e), logger.ERROR)
-            errorData = {
+            error_data = {
                 "error_msg": ex(e),
                 "args": args,
                 "kwargs": kwargs
             }
-            outDict = _responds(RESULT_FATAL, errorData,
-                                "SickRage encountered an internal error! Please report to the Devs")
+            out_dict = _responds(RESULT_FATAL, error_data,
+                                 "SickRage encountered an internal error! Please report to the Devs")
 
-        if 'outputType' in outDict:
-            outputCallback = outputCallbackDict[outDict['outputType']]
+        if 'outputType' in out_dict:
+            output_callback = output_callback_dict[out_dict['outputType']]
         else:
-            outputCallback = outputCallbackDict['default']
+            output_callback = output_callback_dict['default']
 
         try:
-            self.finish(outputCallback(outDict))
+            self.finish(output_callback(out_dict))
         except Exception:
             pass
 
@@ -157,7 +163,7 @@ class ApiHandler(RequestHandler):
         try:
             out = json.dumps(_dict, ensure_ascii=False, sort_keys=True)
             callback = self.get_query_argument('callback', None) or self.get_query_argument('jsonp', None)
-            if callback is not None:
+            if callback:
                 out = callback + '(' + out + ');'  # wrap with JSONP call if requested
         except Exception, e:  # if we fail to generate the output fake an error
             logger.log(u"API :: " + traceback.format_exc(), logger.DEBUG)
@@ -174,101 +180,94 @@ class ApiHandler(RequestHandler):
         logger.log(u"API :: all args: '" + str(args) + "'", logger.DEBUG)
         logger.log(u"API :: all kwargs: '" + str(kwargs) + "'", logger.DEBUG)
 
-        cmds = None
+        commands = None
         if args:
-            cmds = args[0]
-            args = args[1:]
-
-        if "cmd" in kwargs:
-            cmds = kwargs["cmd"]
-            del kwargs["cmd"]
-
-        outDict = {}
-        if cmds is not None:
-            cmds = cmds.split("|")
-            multiCmds = bool(len(cmds) > 1)
-            for cmd in cmds:
-                curArgs, curKwargs = self.filter_params(cmd, args, kwargs)
-                cmdIndex = None
-                if len(cmd.split("_")) > 1:  # was a index used for this cmd ?
-                    cmd, cmdIndex = cmd.split("_")  # this gives us the clear cmd and the index
-
-                logger.log(u"API :: " + cmd + ": curKwargs " + str(curKwargs), logger.DEBUG)
-                if not (multiCmds and cmd in ('show.getbanner', 'show.getfanart', 'show.getnetworklogo', 'show.getposter')):  # skip these cmd while chaining
+            commands, args = args[0], args[1:]
+        commands = kwargs.pop("cmd", commands)
+
+        out_dict = {}
+        if commands:
+            commands = commands.split("|")
+            multi_commands = len(commands) > 1
+            for cmd in commands:
+                cur_args, cur_kwargs = self.filter_params(cmd, args, kwargs)
+
+                cmd = cmd.split("_")  # was a index used for this cmd ?
+                cmd, cmd_index = cmd[0], cmd[1:]  # this gives us the clear cmd and the index
+
+                logger.log(u"API :: " + cmd + ": cur_kwargs " + str(cur_kwargs), logger.DEBUG)
+                if not (cmd in ('show.getbanner', 'show.getfanart', 'show.getnetworklogo', 'show.getposter') and
+                        multi_commands):  # skip these cmd while chaining
                     try:
                         if cmd in function_mapper:
-                            # map function
-                            func = function_mapper.get(cmd)
-
-                            # add request handler to function
-                            func.rh = self
-
-                            # call function and get response back
-                            curOutDict = func(curArgs, curKwargs).run()
+                            func = function_mapper.get(cmd)  # map function
+                            func.rh = self  # add request handler to function
+                            cur_out_dict = func(cur_args, cur_kwargs).run()  # call function and get response
                         elif _is_int(cmd):
-                            curOutDict = TVDBShorthandWrapper(curArgs, curKwargs, cmd).run()
+                            cur_out_dict = TVDBShorthandWrapper(cur_args, cur_kwargs, cmd).run()
                         else:
-                            curOutDict = _responds(RESULT_ERROR, "No such cmd: '" + cmd + "'")
-                    except ApiError, e:  # Api errors that we raised, they are harmless
-                        curOutDict = _responds(RESULT_ERROR, msg=ex(e))
-                else:  # if someone chained one of the forbiden cmds they will get an error for this one cmd
-                    curOutDict = _responds(RESULT_ERROR, msg="The cmd '" + cmd + "' is not supported while chaining")
-
-                if multiCmds:
-                    # note: if multiple same cmds are issued but one has not an index defined it will override all others
-                    # or the other way around, this depends on the order of the cmds
-                    # this is not a bug
-                    if cmdIndex is None:  # do we need a index dict for this cmd ?
-                        outDict[cmd] = curOutDict
+                            cur_out_dict = _responds(RESULT_ERROR, "No such cmd: '" + cmd + "'")
+                    except ApiError as error:  # Api errors that we raised, they are harmless
+                        cur_out_dict = _responds(RESULT_ERROR, msg=ex(error))
+                else:  # if someone chained one of the forbidden commands they will get an error for this one cmd
+                    cur_out_dict = _responds(RESULT_ERROR, msg="The cmd '" + cmd + "' is not supported while chaining")
+
+                if multi_commands:
+                    # note: if duplicate commands are issued and one has an index defined it will override
+                    # all others or the other way around, depending on the command order
+                    # THIS IS NOT A BUG!
+                    if cmd_index:  # do we need an index dict for this cmd ?
+                        if cmd not in out_dict:
+                            out_dict[cmd] = {}
+                        out_dict[cmd][cmd_index] = cur_out_dict
                     else:
-                        if not cmd in outDict:
-                            outDict[cmd] = {}
-                        outDict[cmd][cmdIndex] = curOutDict
+                        out_dict[cmd] = cur_out_dict
                 else:
-                    outDict = curOutDict
+                    out_dict = cur_out_dict
 
-            if multiCmds:  # if we had multiple cmds we have to wrap it in a response dict
-                outDict = _responds(RESULT_SUCCESS, outDict)
+            if multi_commands:  # if we had multiple commands we have to wrap it in a response dict
+                out_dict = _responds(RESULT_SUCCESS, out_dict)
         else:  # index / no cmd given
-            outDict = CMD_SickBeard(args, kwargs).run()
+            out_dict = CMD_SickBeard(args, kwargs).run()
 
-        return outDict
+        return out_dict
 
     def filter_params(self, cmd, args, kwargs):
         """ return only params kwargs that are for cmd
             and rename them to a clean version (remove "<cmd>_")
-            args are shared across all cmds
+            args are shared across all commands
 
-            all args and kwarks are lowerd
+            all args and kwargs are lowered
 
             cmd are separated by "|" e.g. &cmd=shows|future
-            kwargs are namespaced with "." e.g. show.indexerid=101501
-            if a karg has no namespace asing it anyways (global)
+            kwargs are name-spaced with "." e.g. show.indexerid=101501
+            if a kwarg has no namespace asking it anyways (global)
 
             full e.g.
             /api?apikey=1234&cmd=show.seasonlist_asd|show.seasonlist_2&show.seasonlist_asd.indexerid=101501&show.seasonlist_2.indexerid=79488&sort=asc
 
             two calls of show.seasonlist
             one has the index "asd" the other one "2"
-            the "indexerid" kwargs / params have the indexed cmd as a namspace
+            the "indexerid" kwargs / params have the indexed cmd as a namespace
             and the kwarg / param "sort" is a used as a global
         """
-        curArgs = []
+        cur_args = []
         for arg in args:
-            curArgs.append(arg.lower())
-        curArgs = tuple(curArgs)
+            cur_args.append(arg.lower())
+        cur_args = tuple(cur_args)
 
-        curKwargs = {}
+        cur_kwargs = {}
         for kwarg in kwargs:
             if kwarg.find(cmd + ".") == 0:
-                cleanKey = kwarg.rpartition(".")[2]
-                curKwargs[cleanKey] = kwargs[kwarg].lower()
-            elif not "." in kwarg:  # the kwarg was not namespaced therefore a "global"
-                curKwargs[kwarg] = kwargs[kwarg]
-        return curArgs, curKwargs
+                clean_key = kwarg.rpartition(".")[2]
+                cur_kwargs[clean_key] = kwargs[kwarg].lower()
+            elif "." not in kwarg:  # the kwarg was not name-spaced therefore a "global"
+                cur_kwargs[kwarg] = kwargs[kwarg]
+        return cur_args, cur_kwargs
 
 
 class ApiCall(ApiHandler):
+
     _help = {"desc": "This command is not documented. Please report this to the developers."}
 
     def __init__(self, args, kwargs):
@@ -304,7 +303,7 @@ class ApiCall(ApiHandler):
 
             if paramType in self._help:
                 for paramName in paramDict:
-                    if not paramName in self._help[paramType]:
+                    if paramName not in self._help[paramType]:
                         self._help[paramType][paramName] = {}
                     if paramDict[paramName]["allowedValues"]:
                         self._help[paramType][paramName]["allowedValues"] = paramDict[paramName]["allowedValues"]
@@ -331,7 +330,7 @@ class ApiCall(ApiHandler):
             msg = "The required parameters: '" + "','".join(self._missing) + "' where not set"
         return _responds(RESULT_ERROR, msg=msg)
 
-    def check_params(self, args, kwargs, key, default, required, arg_type, allowedValues):
+    def check_params(self, args, kwargs, key, default, required, arg_type, allowed_values):
 
         """ function to check passed params for the shorthand wrapper
             and to detect missing/required params
@@ -345,10 +344,10 @@ class ApiCall(ApiHandler):
             self.indexer = indexer_ids.index(key)
 
         missing = True
-        orgDefault = default
+        org_default = default
 
         if arg_type == "bool":
-            allowedValues = [0, 1]
+            allowed_values = [0, 1]
 
         if args:
             default = args[0]
@@ -363,28 +362,27 @@ class ApiCall(ApiHandler):
                 self._requiredParams.append(key)
             except AttributeError:
                 self._missing = []
-                self._requiredParams = {key: {"allowedValues": allowedValues,
-                                              "defaultValue": orgDefault,
+                self._requiredParams = {key: {"allowed_values": allowed_values,
+                                              "defaultValue": org_default,
                                               "type": arg_type}}
 
             if missing and key not in self._missing:
                 self._missing.append(key)
         else:
             try:
-                self._optionalParams[key] = {"allowedValues": allowedValues,
-                                             "defaultValue": orgDefault,
+                self._optionalParams[key] = {"allowed_values": allowed_values,
+                                             "defaultValue": org_default,
                                              "type": arg_type}
             except AttributeError:
-                self._optionalParams = {}
-                self._optionalParams[key] = {"allowedValues": allowedValues,
-                                             "defaultValue": orgDefault,
-                                             "type": arg_type}
+                self._optionalParams = {key: {"allowed_values": allowed_values,
+                                              "defaultValue": org_default,
+                                              "type": arg_type}}
 
         if default:
             default = self._check_param_type(default, key, arg_type)
             if arg_type == "bool":
                 arg_type = []
-            self._check_param_value(default, key, allowedValues)
+            self._check_param_value(default, key, allowed_values)
 
         return default, args
 
@@ -430,25 +428,25 @@ class ApiCall(ApiHandler):
 
         return value
 
-    def _check_param_value(self, value, name, allowedValues):
+    def _check_param_value(self, value, name, allowed_values):
         """ will check if value (or all values in it ) are in allowed values
             will raise an exception if value is "out of range"
-            if bool(allowedValue) == False a check is not performed and all values are excepted
+            if bool(allowedValue) is False a check is not performed and all values are excepted
         """
-        if allowedValues:
+        if allowed_values:
             error = False
             if isinstance(value, list):
                 for item in value:
-                    if not item in allowedValues:
+                    if item not in allowed_values:
                         error = True
             else:
-                if not value in allowedValues:
+                if value not in allowed_values:
                     error = True
 
             if error:
                 # this is kinda a ApiError but raising an error is the only way of quitting here
                 raise ApiError(u"param: '" + str(name) + "' with given value: '" + str(
-                    value) + "' is out of allowed range '" + str(allowedValues) + "'")
+                    value) + "' is out of allowed range '" + str(allowed_values) + "'")
 
 
 class TVDBShorthandWrapper(ApiCall):
@@ -489,10 +487,10 @@ def _is_int(data):
         return True
 
 
-def _rename_element(dict_obj, oldKey, newKey):
+def _rename_element(dict_obj, old_key, new_key):
     try:
-        dict_obj[newKey] = dict_obj[oldKey]
-        del dict_obj[oldKey]
+        dict_obj[new_key] = dict_obj[old_key]
+        del dict_obj[old_key]
     except (ValueError, TypeError, NameError):
         pass
     return dict_obj
@@ -504,19 +502,17 @@ def _responds(result_type, data=None, msg=""):
     message is a human readable string, can be empty
     data is either a dict or a array, can be a empty dict or empty array
     """
-    if data is None:
-        data = {}
     return {"result": result_type_map[result_type],
             "message": msg,
-            "data": data}
+            "data": {} if not data else data}
 
 
-def _get_status_Strings(s):
+def _get_status_strings(s):
     return statusStrings[s]
 
 
-def _ordinal_to_dateTimeForm(ordinal):
-    # workaround for episodes with no airdate
+def _ordinal_to_datetime_form(ordinal):
+    # workaround for episodes with no air date
     if int(ordinal) != 1:
         date = datetime.date.fromordinal(ordinal)
     else:
@@ -524,7 +520,7 @@ def _ordinal_to_dateTimeForm(ordinal):
     return date.strftime(dateTimeFormat)
 
 
-def _ordinal_to_dateForm(ordinal):
+def _ordinal_to_date_form(ordinal):
     if int(ordinal) != 1:
         date = datetime.date.fromordinal(ordinal)
     else:
@@ -532,28 +528,28 @@ def _ordinal_to_dateForm(ordinal):
     return date.strftime(dateFormat)
 
 
-def _historyDate_to_dateTimeForm(timeString):
-    date = datetime.datetime.strptime(timeString, History.date_format)
+def _history_date_to_datetime_form(time_string):
+    date = datetime.datetime.strptime(time_string, History.date_format)
     return date.strftime(dateTimeFormat)
 
 
-def _mapQuality(showObj):
-    quality_map = _getQualityMap()
+def _map_quality(show_obj):
+    quality_map = _get_quality_map()
 
-    anyQualities = []
-    bestQualities = []
+    any_qualities = []
+    best_qualities = []
 
-    iqualityID, aqualityID = Quality.splitQuality(int(showObj))
-    if iqualityID:
-        for quality in iqualityID:
-            anyQualities.append(quality_map[quality])
-    if aqualityID:
-        for quality in aqualityID:
-            bestQualities.append(quality_map[quality])
-    return anyQualities, bestQualities
+    i_quality_id, a_quality_id = Quality.splitQuality(int(show_obj))
+    if i_quality_id:
+        for quality in i_quality_id:
+            any_qualities.append(quality_map[quality])
+    if a_quality_id:
+        for quality in a_quality_id:
+            best_qualities.append(quality_map[quality])
+    return any_qualities, best_qualities
 
 
-def _getQualityMap():
+def _get_quality_map():
     return {Quality.SDTV: 'sdtv',
             Quality.SDDVD: 'sddvd',
             Quality.HDTV: 'hdtv',
@@ -566,15 +562,15 @@ def _getQualityMap():
             Quality.UNKNOWN: 'unknown'}
 
 
-def _getRootDirs():
+def _get_root_dirs():
     if sickbeard.ROOT_DIRS == "":
         return {}
 
-    rootDir = {}
+    root_dir = {}
     root_dirs = sickbeard.ROOT_DIRS.split('|')
     default_index = int(sickbeard.ROOT_DIRS.split('|')[0])
 
-    rootDir["default_index"] = int(sickbeard.ROOT_DIRS.split('|')[0])
+    root_dir["default_index"] = int(sickbeard.ROOT_DIRS.split('|')[0])
     # remove default_index value from list (this fixes the offset)
     root_dirs.pop(0)
 
@@ -597,11 +593,12 @@ def _getRootDirs():
         if root_dir is default_dir:
             default = 1
 
-        curDir = {}
-        curDir['valid'] = valid
-        curDir['location'] = root_dir
-        curDir['default'] = default
-        dir_list.append(curDir)
+        cur_dir = {
+            'valid': valid,
+            'location': root_dir,
+            'default': default
+        }
+        dir_list.append(cur_dir)
 
     return dir_list
 
@@ -614,7 +611,7 @@ class ApiError(Exception):
 
 class IntParseError(Exception):
     """
-    A value could not be parsed into an int, but should be parsable to an int
+    A value could not be parsed into an int, but should be parse-able to an int
     """
 
 
@@ -722,44 +719,41 @@ class CMD_Episode(ApiCall):
 
     def run(self):
         """ Get detailed information about an episode """
-        showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
-        if not showObj:
+        show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
+        if not show_obj:
             return _responds(RESULT_FAILURE, msg="Show not found")
 
-        myDB = db.DBConnection(row_type="dict")
-        sqlResults = myDB.select(
+        my_db = db.DBConnection(row_type="dict")
+        sql_results = my_db.select(
             "SELECT name, description, airdate, status, location, file_size, release_name, subtitles FROM tv_episodes WHERE showid = ? AND episode = ? AND season = ?",
             [self.indexerid, self.e, self.s])
-        if not len(sqlResults) == 1:
+        if not len(sql_results) == 1:
             raise ApiError("Episode not found")
-        episode = sqlResults[0]
+        episode = sql_results[0]
         # handle path options
         # absolute vs relative vs broken
-        showPath = None
+        show_path = None
         try:
-            showPath = showObj.location
+            show_path = show_obj.location
         except ShowDirectoryNotFoundException:
             pass
 
-        if bool(self.fullPath) == True and showPath:
-            pass
-        elif bool(self.fullPath) == False and showPath:
-            # using the length because lstrip removes to much
-            showPathLength = len(showPath) + 1  # the / or \ yeah not that nice i know
-            episode["location"] = episode["location"][showPathLength:]
-        elif not showPath:  # show dir is broken ... episode path will be empty
+        if not show_path:  # show dir is broken ... episode path will be empty
             episode["location"] = ""
+        elif not self.fullPath:
+            # using the length because lstrip() removes to much
+            show_path_length = len(show_path) + 1  # the / or \ yeah not that nice i know
+            episode["location"] = episode["location"][show_path_length:]
 
         # convert stuff to human form
-        if helpers.tryInt(episode['airdate'], 1) > 693595: # 1900
+        if helpers.tryInt(episode['airdate'], 1) > 693595:  # 1900
             episode['airdate'] = sbdatetime.sbdatetime.sbfdate(sbdatetime.sbdatetime.convert_to_setting(
-                network_timezones.parse_date_time(int(episode['airdate']), showObj.airs, showObj.network)),
-                d_preset=dateFormat)
+                network_timezones.parse_date_time(int(episode['airdate']), show_obj.airs, show_obj.network)), d_preset=dateFormat)
         else:
             episode['airdate'] = 'Never'
 
         status, quality = Quality.splitCompositeStatus(int(episode["status"]))
-        episode["status"] = _get_status_Strings(status)
+        episode["status"] = _get_status_strings(status)
         episode["quality"] = get_quality_string(quality)
         episode["file_size_human"] = helpers.pretty_filesize(episode["file_size"])
 
@@ -790,26 +784,26 @@ class CMD_EpisodeSearch(ApiCall):
 
     def run(self):
         """ Search for an episode """
-        showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
-        if not showObj:
+        show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
+        if not show_obj:
             return _responds(RESULT_FAILURE, msg="Show not found")
 
         # retrieve the episode object and fail if we can't get one
-        epObj = showObj.getEpisode(int(self.s), int(self.e))
-        if isinstance(epObj, str):
+        ep_obj = show_obj.getEpisode(int(self.s), int(self.e))
+        if isinstance(ep_obj, str):
             return _responds(RESULT_FAILURE, msg="Episode not found")
 
         # make a queue item for it and put it on the queue
-        ep_queue_item = search_queue.ManualSearchQueueItem(showObj, epObj)
+        ep_queue_item = search_queue.ManualSearchQueueItem(show_obj, ep_obj)
         sickbeard.searchQueueScheduler.action.add_item(ep_queue_item)  # @UndefinedVariable
 
         # wait until the queue item tells us whether it worked or not
-        while ep_queue_item.success == None:  # @UndefinedVariable
+        while ep_queue_item.success is None:  # @UndefinedVariable
             time.sleep(1)
 
         # return the correct json value
         if ep_queue_item.success:
-            status, quality = Quality.splitCompositeStatus(epObj.status)  # @UnusedVariable
+            status, quality = Quality.splitCompositeStatus(ep_obj.status)  # @UnusedVariable
             # TODO: split quality and status?
             return _responds(RESULT_SUCCESS, {"quality": get_quality_string(quality)},
                              "Snatched (" + get_quality_string(quality) + ")")
@@ -846,8 +840,8 @@ class CMD_EpisodeSetStatus(ApiCall):
 
     def run(self):
         """ Set the status of an episode or a season (when no episode is provided) """
-        showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
-        if not showObj:
+        show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
+        if not show_obj:
             return _responds(RESULT_FAILURE, msg="Show not found")
 
         # convert the string status to a int
@@ -855,22 +849,22 @@ class CMD_EpisodeSetStatus(ApiCall):
             if str(statusStrings[status]).lower() == str(self.status).lower():
                 self.status = status
                 break
-        else:  # if we dont break out of the for loop we got here.
+        else:  # if we don't break out of the for loop we got here.
             # the allowed values has at least one item that could not be matched against the internal status strings
             raise ApiError("The status string could not be matched to a status. Report to Devs!")
 
         ep_list = []
         if self.e:
-            epObj = showObj.getEpisode(self.s, self.e)
-            if epObj == None:
+            ep_obj = show_obj.getEpisode(self.s, self.e)
+            if not ep_obj:
                 return _responds(RESULT_FAILURE, msg="Episode not found")
-            ep_list = [epObj]
+            ep_list = [ep_obj]
         else:
-            # get all episode numbers frome self,season
-            ep_list = showObj.getAllEpisodes(season=self.s)
+            # get all episode numbers from self, season
+            ep_list = show_obj.getAllEpisodes(season=self.s)
 
-        def _epResult(result_code, ep, msg=""):
-            return {'season': ep.season, 'episode': ep.episode, 'status': _get_status_Strings(ep.status),
+        def _ep_result(result_code, ep, msg=""):
+            return {'season': ep.season, 'episode': ep.episode, 'status': _get_status_strings(ep.status),
                     'result': result_type_map[result_code], 'message': msg}
 
         ep_results = []
@@ -879,52 +873,52 @@ class CMD_EpisodeSetStatus(ApiCall):
         segments = {}
 
         sql_l = []
-        for epObj in ep_list:
-            with epObj.lock:
+        for ep_obj in ep_list:
+            with ep_obj.lock:
                 if self.status == WANTED:
                     # figure out what episodes are wanted so we can backlog them
-                    if epObj.season in segments:
-                        segments[epObj.season].append(epObj)
+                    if ep_obj.season in segments:
+                        segments[ep_obj.season].append(ep_obj)
                     else:
-                        segments[epObj.season] = [epObj]
+                        segments[ep_obj.season] = [ep_obj]
 
-                # don't let them mess up UNAIRED episodes
-                if epObj.status == UNAIRED:
-                    if self.e != None:  # setting the status of a unaired is only considert a failure if we directly wanted this episode, but is ignored on a season request
+                # don't let them mess up UN-AIRED episodes
+                if ep_obj.status == UNAIRED:
+                    if self.e is not None:  # setting the status of an un-aired is only considered a failure if we directly wanted this episode, but is ignored on a season request
                         ep_results.append(
-                            _epResult(RESULT_FAILURE, epObj, "Refusing to change status because it is UNAIRED"))
+                            _ep_result(RESULT_FAILURE, ep_obj, "Refusing to change status because it is UN-AIRED"))
                         failure = True
                     continue
 
                 if self.status == FAILED and not sickbeard.USE_FAILED_DOWNLOADS:
-                    ep_results.append(_epResult(RESULT_FAILURE, epObj, "Refusing to change status to FAILED because failed download handling is disabled"))
+                    ep_results.append(_ep_result(RESULT_FAILURE, ep_obj, "Refusing to change status to FAILED because failed download handling is disabled"))
                     failure = True
                     continue
 
                 # allow the user to force setting the status for an already downloaded episode
-                if epObj.status in Quality.DOWNLOADED + Quality.ARCHIVED and not self.force:
-                    ep_results.append(_epResult(RESULT_FAILURE, epObj, "Refusing to change status because it is already marked as DOWNLOADED"))
+                if ep_obj.status in Quality.DOWNLOADED + Quality.ARCHIVED and not self.force:
+                    ep_results.append(_ep_result(RESULT_FAILURE, ep_obj, "Refusing to change status because it is already marked as DOWNLOADED"))
                     failure = True
                     continue
 
-                epObj.status = self.status
-                sql_l.append(epObj.get_sql())
+                ep_obj.status = self.status
+                sql_l.append(ep_obj.get_sql())
 
                 if self.status == WANTED:
                     start_backlog = True
-                ep_results.append(_epResult(RESULT_SUCCESS, epObj))
+                ep_results.append(_ep_result(RESULT_SUCCESS, ep_obj))
 
         if len(sql_l) > 0:
-            myDB = db.DBConnection()
-            myDB.mass_action(sql_l)
+            my_db = db.DBConnection()
+            my_db.mass_action(sql_l)
 
         extra_msg = ""
         if start_backlog:
             for season, segment in segments.iteritems():
-                cur_backlog_queue_item = search_queue.BacklogQueueItem(showObj, segment)
+                cur_backlog_queue_item = search_queue.BacklogQueueItem(show_obj, segment)
                 sickbeard.searchQueueScheduler.action.add_item(cur_backlog_queue_item)  # @UndefinedVariable
 
-                logger.log(u"API :: Starting backlog for " + showObj.name + " season " + str(
+                logger.log(u"API :: Starting backlog for " + show_obj.name + " season " + str(
                     season) + " because some episodes were set to WANTED")
 
             extra_msg = " Backlog started"
@@ -959,28 +953,28 @@ class CMD_SubtitleSearch(ApiCall):
 
     def run(self):
         """ Search for an episode subtitles """
-        showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
-        if not showObj:
+        show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
+        if not show_obj:
             return _responds(RESULT_FAILURE, msg="Show not found")
 
         # retrieve the episode object and fail if we can't get one
-        epObj = showObj.getEpisode(int(self.s), int(self.e))
-        if isinstance(epObj, str):
+        ep_obj = show_obj.getEpisode(int(self.s), int(self.e))
+        if isinstance(ep_obj, str):
             return _responds(RESULT_FAILURE, msg="Episode not found")
 
         # try do download subtitles for that episode
-        previous_subtitles = epObj.subtitles
+        previous_subtitles = ep_obj.subtitles
 
         try:
-            subtitles = epObj.downloadSubtitles()
+            subtitles = ep_obj.downloadSubtitles()
         except Exception:
             return _responds(RESULT_FAILURE, msg='Unable to find subtitles')
 
         # return the correct json value
-        newSubtitles = frozenset(epObj.subtitles).difference(previous_subtitles)
-        if newSubtitles:
-            newLangs = [subtitles.fromietf(newSub) for newSub in newSubtitles]
-            status = 'New subtitles downloaded: %s' % ', '.join([newLang.name for newLang in newLangs])
+        new_subtitles = frozenset(ep_obj.subtitles).difference(previous_subtitles)
+        if new_subtitles:
+            new_languages = [subtitles.fromietf(newSub) for newSub in new_subtitles]
+            status = 'New subtitles downloaded: %s' % ', '.join([new_language.name for new_language in new_languages])
             response = _responds(RESULT_SUCCESS, msg='New subtitles found')
         else:
             status = 'No subtitles downloaded'
@@ -1010,27 +1004,27 @@ class CMD_Exceptions(ApiCall):
 
     def run(self):
         """ Get the scene exceptions for all or a given show """
-        myDB = db.DBConnection("cache.db", row_type="dict")
+        my_db = db.DBConnection("cache.db", row_type="dict")
 
-        if self.indexerid == None:
-            sqlResults = myDB.select("SELECT show_name, indexer_id AS 'indexerid' FROM scene_exceptions")
+        if self.indexerid is None:
+            sql_results = my_db.select("SELECT show_name, indexer_id AS 'indexerid' FROM scene_exceptions")
             scene_exceptions = {}
-            for row in sqlResults:
+            for row in sql_results:
                 indexerid = row["indexerid"]
-                if not indexerid in scene_exceptions:
+                if indexerid not in scene_exceptions:
                     scene_exceptions[indexerid] = []
                 scene_exceptions[indexerid].append(row["show_name"])
 
         else:
-            showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
-            if not showObj:
+            show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
+            if not show_obj:
                 return _responds(RESULT_FAILURE, msg="Show not found")
 
-            sqlResults = myDB.select(
+            sql_results = my_db.select(
                 "SELECT show_name, indexer_id AS 'indexerid' FROM scene_exceptions WHERE indexer_id = ?",
                 [self.indexerid])
             scene_exceptions = []
-            for row in sqlResults:
+            for row in sql_results:
                 scene_exceptions.append(row["show_name"])
 
         return _responds(RESULT_SUCCESS, scene_exceptions)
@@ -1062,14 +1056,14 @@ class CMD_History(ApiCall):
 
         for row in data:
             status, quality = Quality.splitCompositeStatus(int(row["action"]))
-            status = _get_status_Strings(status)
+            status = _get_status_strings(status)
 
             if self.type and not status.lower() == self.type:
                 continue
 
             row["status"] = status
             row["quality"] = get_quality_string(quality)
-            row["date"] = _historyDate_to_dateTimeForm(str(row["date"]))
+            row["date"] = _history_date_to_datetime_form(str(row["date"]))
 
             del row["action"]
 
@@ -1134,15 +1128,15 @@ class CMD_Failed(ApiCall):
     def run(self):
         """ Get the failed downloads """
 
-        myDB = db.DBConnection('failed.db', row_type="dict")
+        my_db = db.DBConnection('failed.db', row_type="dict")
 
-        ulimit = min(int(self.limit), 100)
-        if ulimit == 0:
-            sqlResults = myDB.select("SELECT * FROM failed")
+        u_limit = min(int(self.limit), 100)
+        if u_limit == 0:
+            sql_results = my_db.select("SELECT * FROM failed")
         else:
-            sqlResults = myDB.select("SELECT * FROM failed LIMIT ?", [ulimit])
+            sql_results = my_db.select("SELECT * FROM failed LIMIT ?", [u_limit])
 
-        return _responds(RESULT_SUCCESS, sqlResults)
+        return _responds(RESULT_SUCCESS, sql_results)
 
 
 class CMD_Backlog(ApiCall):
@@ -1159,27 +1153,27 @@ class CMD_Backlog(ApiCall):
 
         shows = []
 
-        myDB = db.DBConnection(row_type="dict")
+        my_db = db.DBConnection(row_type="dict")
         for curShow in sickbeard.showList:
 
-            showEps = []
+            show_eps = []
 
-            sqlResults = myDB.select(
+            sql_results = my_db.select(
                 "SELECT tv_episodes.*, tv_shows.paused FROM tv_episodes INNER JOIN tv_shows ON tv_episodes.showid = tv_shows.indexer_id WHERE showid = ? and paused = 0 ORDER BY season DESC, episode DESC",
                 [curShow.indexerid])
 
-            for curResult in sqlResults:
+            for curResult in sql_results:
 
-                curEpCat = curShow.getOverview(int(curResult["status"] or -1))
-                if curEpCat and curEpCat in (Overview.WANTED, Overview.QUAL):
-                    showEps.append(curResult)
+                cur_ep_cat = curShow.getOverview(int(curResult["status"] or -1))
+                if cur_ep_cat and cur_ep_cat in (Overview.WANTED, Overview.QUAL):
+                    show_eps.append(curResult)
 
-            if showEps:
+            if show_eps:
                 shows.append({
                     "indexerid": curShow.indexerid,
                     "show_name": curShow.name,
                     "status": curShow.status,
-                    "episodes": showEps
+                    "episodes": show_eps
                 })
 
         return _responds(RESULT_SUCCESS, shows)
@@ -1208,7 +1202,7 @@ class CMD_Logs(ApiCall):
     def run(self):
         """ Get the logs """
         # 10 = Debug / 20 = Info / 30 = Warning / 40 = Error
-        minLevel = logger.reverseNames[str(self.min_level).upper()]
+        min_level = logger.reverseNames[str(self.min_level).upper()]
 
         data = []
         if os.path.isfile(logger.logFile):
@@ -1217,11 +1211,11 @@ class CMD_Logs(ApiCall):
 
         regex = r"^(\d\d\d\d)\-(\d\d)\-(\d\d)\s*(\d\d)\:(\d\d):(\d\d)\s*([A-Z]+)\s*(.+?)\s*\:\:\s*(.*)$"
 
-        finalData = []
+        final_data = []
 
-        numLines = 0
-        lastLine = False
-        numToShow = min(50, len(data))
+        num_lines = 0
+        last_line = False
+        num_to_show = min(50, len(data))
 
         for x in reversed(data):
 
@@ -1230,25 +1224,25 @@ class CMD_Logs(ApiCall):
             if match:
                 level = match.group(7)
                 if level not in logger.reverseNames:
-                    lastLine = False
+                    last_line = False
                     continue
 
-                if logger.reverseNames[level] >= minLevel:
-                    lastLine = True
-                    finalData.append(x.rstrip("\n"))
+                if logger.reverseNames[level] >= min_level:
+                    last_line = True
+                    final_data.append(x.rstrip("\n"))
                 else:
-                    lastLine = False
+                    last_line = False
                     continue
 
-            elif lastLine:
-                finalData.append("AA" + x)
+            elif last_line:
+                final_data.append("AA" + x)
 
-            numLines += 1
+            num_lines += 1
 
-            if numLines >= numToShow:
+            if num_lines >= num_to_show:
                 break
 
-        return _responds(RESULT_SUCCESS, finalData)
+        return _responds(RESULT_SUCCESS, final_data)
 
 
 class CMD_PostProcess(ApiCall):
@@ -1296,7 +1290,7 @@ class CMD_PostProcess(ApiCall):
         if not self.return_data:
             data = ""
 
-        return _responds(RESULT_SUCCESS, data=data, msg="Started postprocess for %s" % self.path)
+        return _responds(RESULT_SUCCESS, data=data, msg="Started post-process for %s" % self.path)
 
 
 class CMD_SickBeard(ApiCall):
@@ -1341,7 +1335,7 @@ class CMD_SickBeardAddRootDir(ApiCall):
         location_matched = 0
         index = 0
 
-        # dissallow adding/setting an invalid dir
+        # disallow adding/setting an invalid dir
         if not ek(os.path.isdir, self.location):
             return _responds(RESULT_FAILURE, msg="Location is invalid")
 
@@ -1373,7 +1367,7 @@ class CMD_SickBeardAddRootDir(ApiCall):
         root_dirs_new = '|'.join(unicode(x) for x in root_dirs_new)
 
         sickbeard.ROOT_DIRS = root_dirs_new
-        return _responds(RESULT_SUCCESS, _getRootDirs(), msg="Root directories updated")
+        return _responds(RESULT_SUCCESS, _get_root_dirs(), msg="Root directories updated")
 
 
 class CMD_SickBeardCheckVersion(ApiCall):
@@ -1386,21 +1380,21 @@ class CMD_SickBeardCheckVersion(ApiCall):
         ApiCall.__init__(self, args, kwargs)
 
     def run(self):
-        checkversion = CheckVersion()
-        needs_update = checkversion.check_for_new_version()
+        check_version = CheckVersion()
+        needs_update = check_version.check_for_new_version()
 
         data = {
             "current_version": {
-                "branch": checkversion.get_branch(),
-                "commit": checkversion.updater.get_cur_commit_hash(),
-                "version": checkversion.updater.get_cur_version(),
+                "branch": check_version.get_branch(),
+                "commit": check_version.updater.get_cur_commit_hash(),
+                "version": check_version.updater.get_cur_version(),
             },
             "latest_version": {
-                "branch": checkversion.get_branch(),
-                "commit": checkversion.updater.get_newest_commit_hash(),
-                "version": checkversion.updater.get_newest_version(),
+                "branch": check_version.get_branch(),
+                "commit": check_version.updater.get_newest_commit_hash(),
+                "version": check_version.updater.get_newest_version(),
             },
-            "commits_offset": checkversion.updater.get_num_commits_behind(),
+            "commits_offset": check_version.updater.get_num_commits_behind(),
             "needs_update": needs_update,
         }
 
@@ -1418,16 +1412,16 @@ class CMD_SickBeardCheckScheduler(ApiCall):
 
     def run(self):
         """ Get information about the scheduler """
-        myDB = db.DBConnection()
-        sqlResults = myDB.select("SELECT last_backlog FROM info")
+        my_db = db.DBConnection()
+        sql_results = my_db.select("SELECT last_backlog FROM info")
 
-        backlogPaused = sickbeard.searchQueueScheduler.action.is_backlog_paused()  # @UndefinedVariable
-        backlogRunning = sickbeard.searchQueueScheduler.action.is_backlog_in_progress()  # @UndefinedVariable
-        nextBacklog = sickbeard.backlogSearchScheduler.nextRun().strftime(dateFormat).decode(sickbeard.SYS_ENCODING)
+        backlog_paused = sickbeard.searchQueueScheduler.action.is_backlog_paused()  # @UndefinedVariable
+        backlog_running = sickbeard.searchQueueScheduler.action.is_backlog_in_progress()  # @UndefinedVariable
+        next_backlog = sickbeard.backlogSearchScheduler.nextRun().strftime(dateFormat).decode(sickbeard.SYS_ENCODING)
 
-        data = {"backlog_is_paused": int(backlogPaused), "backlog_is_running": int(backlogRunning),
-                "last_backlog": _ordinal_to_dateForm(sqlResults[0]["last_backlog"]),
-                "next_backlog": nextBacklog}
+        data = {"backlog_is_paused": int(backlog_paused), "backlog_is_running": int(backlog_running),
+                "last_backlog": _ordinal_to_date_form(sql_results[0]["last_backlog"]),
+                "next_backlog": next_backlog}
         return _responds(RESULT_SUCCESS, data)
 
 
@@ -1449,9 +1443,9 @@ class CMD_SickBeardDeleteRootDir(ApiCall):
     def run(self):
         """ Delete a root (parent) directory from SickRage """
         if sickbeard.ROOT_DIRS == "":
-            return _responds(RESULT_FAILURE, _getRootDirs(), msg="No root directories detected")
+            return _responds(RESULT_FAILURE, _get_root_dirs(), msg="No root directories detected")
 
-        newIndex = 0
+        new_index = 0
         root_dirs_new = []
         root_dirs = sickbeard.ROOT_DIRS.split('|')
         index = int(root_dirs[0])
@@ -1463,21 +1457,21 @@ class CMD_SickBeardDeleteRootDir(ApiCall):
             if not curRootDir == self.location:
                 root_dirs_new.append(curRootDir)
             else:
-                newIndex = 0
+                new_index = 0
 
         for curIndex, curNewRootDir in enumerate(root_dirs_new):
             if curNewRootDir is old_root_dir:
-                newIndex = curIndex
+                new_index = curIndex
                 break
 
         root_dirs_new = [urllib.unquote_plus(x) for x in root_dirs_new]
         if len(root_dirs_new) > 0:
-            root_dirs_new.insert(0, newIndex)
+            root_dirs_new.insert(0, new_index)
         root_dirs_new = "|".join(unicode(x) for x in root_dirs_new)
 
         sickbeard.ROOT_DIRS = root_dirs_new
         # what if the root dir was not found?
-        return _responds(RESULT_SUCCESS, _getRootDirs(), msg="Root directory deleted")
+        return _responds(RESULT_SUCCESS, _get_root_dirs(), msg="Root directory deleted")
 
 
 class CMD_SickBeardGetDefaults(ApiCall):
@@ -1492,11 +1486,11 @@ class CMD_SickBeardGetDefaults(ApiCall):
     def run(self):
         """ Get SickRage's user default configuration value """
 
-        anyQualities, bestQualities = _mapQuality(sickbeard.QUALITY_DEFAULT)
+        any_qualities, best_qualities = _map_quality(sickbeard.QUALITY_DEFAULT)
 
         data = {"status": statusStrings[sickbeard.STATUS_DEFAULT].lower(),
-                "flatten_folders": int(sickbeard.FLATTEN_FOLDERS_DEFAULT), "initial": anyQualities,
-                "archive": bestQualities, "future_show_paused": int(sickbeard.COMING_EPS_DISPLAY_PAUSED)}
+                "flatten_folders": int(sickbeard.FLATTEN_FOLDERS_DEFAULT), "initial": any_qualities,
+                "archive": best_qualities, "future_show_paused": int(sickbeard.COMING_EPS_DISPLAY_PAUSED)}
         return _responds(RESULT_SUCCESS, data)
 
 
@@ -1530,14 +1524,14 @@ class CMD_SickBeardGetRootDirs(ApiCall):
     def run(self):
         """ Get all root (parent) directories """
 
-        return _responds(RESULT_SUCCESS, _getRootDirs())
+        return _responds(RESULT_SUCCESS, _get_root_dirs())
 
 
 class CMD_SickBeardPauseBacklog(ApiCall):
     _help = {
-        "desc": "Pause or unpause the backlog search",
+        "desc": "Pause or un-pause the backlog search",
         "optionalParameters": {
-            "pause ": {"desc": "True to pause the backlog search, False to unpause it"}
+            "pause ": {"desc": "True to pause the backlog search, False to un-pause it"}
         }
     }
 
@@ -1549,13 +1543,13 @@ class CMD_SickBeardPauseBacklog(ApiCall):
         ApiCall.__init__(self, args, kwargs)
 
     def run(self):
-        """ Pause or unpause the backlog search """
+        """ Pause or un-pause the backlog search """
         if self.pause:
             sickbeard.searchQueueScheduler.action.pause_backlog()  # @UndefinedVariable
             return _responds(RESULT_SUCCESS, msg="Backlog paused")
         else:
             sickbeard.searchQueueScheduler.action.unpause_backlog()  # @UndefinedVariable
-            return _responds(RESULT_SUCCESS, msg="Backlog unpaused")
+            return _responds(RESULT_SUCCESS, msg="Backlog un-paused")
 
 
 class CMD_SickBeardPing(ApiCall):
@@ -1623,23 +1617,23 @@ class CMD_SickBeardSearchIndexers(ApiCall):
 
         if self.name and not self.indexerid:  # only name was given
             for _indexer in sickbeard.indexerApi().indexers if self.indexer == 0 else [int(self.indexer)]:
-                lINDEXER_API_PARMS = sickbeard.indexerApi(_indexer).api_params.copy()
+                indexer_api_params = sickbeard.indexerApi(_indexer).api_params.copy()
 
                 if self.lang and not self.lang == sickbeard.INDEXER_DEFAULT_LANGUAGE:
-                    lINDEXER_API_PARMS['language'] = self.lang
+                    indexer_api_params['language'] = self.lang
 
-                lINDEXER_API_PARMS['actors'] = False
-                lINDEXER_API_PARMS['custom_ui'] = classes.AllShowsListUI
+                indexer_api_params['actors'] = False
+                indexer_api_params['custom_ui'] = classes.AllShowsListUI
 
-                t = sickbeard.indexerApi(_indexer).indexer(**lINDEXER_API_PARMS)
+                t = sickbeard.indexerApi(_indexer).indexer(**indexer_api_params)
 
                 try:
-                    apiData = t[str(self.name).encode()]
+                    api_data = t[str(self.name).encode()]
                 except (sickbeard.indexer_shownotfound, sickbeard.indexer_showincomplete, sickbeard.indexer_error):
                     logger.log(u"API :: Unable to find show with id " + str(self.indexerid), logger.WARNING)
                     continue
 
-                for curSeries in apiData:
+                for curSeries in api_data:
                     results.append({indexer_ids[_indexer]: int(curSeries['id']),
                                     "name": curSeries['seriesname'],
                                     "first_aired": curSeries['firstaired'],
@@ -1649,31 +1643,31 @@ class CMD_SickBeardSearchIndexers(ApiCall):
 
         elif self.indexerid:
             for _indexer in sickbeard.indexerApi().indexers if self.indexer == 0 else [int(self.indexer)]:
-                lINDEXER_API_PARMS = sickbeard.indexerApi(_indexer).api_params.copy()
+                indexer_api_params = sickbeard.indexerApi(_indexer).api_params.copy()
 
                 if self.lang and not self.lang == sickbeard.INDEXER_DEFAULT_LANGUAGE:
-                    lINDEXER_API_PARMS['language'] = self.lang
+                    indexer_api_params['language'] = self.lang
 
-                lINDEXER_API_PARMS['actors'] = False
+                indexer_api_params['actors'] = False
 
-                t = sickbeard.indexerApi(_indexer).indexer(**lINDEXER_API_PARMS)
+                t = sickbeard.indexerApi(_indexer).indexer(**indexer_api_params)
 
                 try:
-                    myShow = t[int(self.indexerid)]
+                    my_show = t[int(self.indexerid)]
                 except (sickbeard.indexer_shownotfound, sickbeard.indexer_showincomplete, sickbeard.indexer_error):
                     logger.log(u"API :: Unable to find show with id " + str(self.indexerid), logger.WARNING)
                     return _responds(RESULT_SUCCESS, {"results": [], "langid": lang_id})
 
-                if not myShow.data['seriesname']:
+                if not my_show.data['seriesname']:
                     logger.log(
                         u"API :: Found show with indexerid: " + str(
                             self.indexerid) + ", however it contained no show name", logger.DEBUG)
                     return _responds(RESULT_FAILURE, msg="Show contains no name, invalid result")
 
                 # found show
-                results = [{indexer_ids[_indexer]: int(myShow.data['id']),
-                            "name": unicode(myShow.data['seriesname']),
-                            "first_aired": myShow.data['firstaired'],
+                results = [{indexer_ids[_indexer]: int(my_show.data['id']),
+                            "name": unicode(my_show.data['seriesname']),
+                            "first_aired": my_show.data['firstaired'],
                             "indexer": int(_indexer)}]
                 break
 
@@ -1763,18 +1757,18 @@ class CMD_SickBeardSetDefaults(ApiCall):
                        'fullhdbluray': Quality.FULLHDBLURAY,
                        'unknown': Quality.UNKNOWN}
 
-        iqualityID = []
-        aqualityID = []
+        i_quality_id = []
+        a_quality_id = []
 
         if self.initial:
             for quality in self.initial:
-                iqualityID.append(quality_map[quality])
+                i_quality_id.append(quality_map[quality])
         if self.archive:
             for quality in self.archive:
-                aqualityID.append(quality_map[quality])
+                a_quality_id.append(quality_map[quality])
 
-        if iqualityID or aqualityID:
-            sickbeard.QUALITY_DEFAULT = Quality.combineQualities(iqualityID, aqualityID)
+        if i_quality_id or a_quality_id:
+            sickbeard.QUALITY_DEFAULT = Quality.combineQualities(i_quality_id, a_quality_id)
 
         if self.status:
             # convert the string status to a int
@@ -1782,18 +1776,18 @@ class CMD_SickBeardSetDefaults(ApiCall):
                 if statusStrings[status].lower() == str(self.status).lower():
                     self.status = status
                     break
-            # this should be obsolete bcause of the above
-            if not self.status in statusStrings:
+            # this should be obsolete because of the above
+            if self.status not in statusStrings:
                 raise ApiError("Invalid Status")
             # only allow the status options we want
             if int(self.status) not in (3, 5, 6, 7):
                 raise ApiError("Status Prohibited")
             sickbeard.STATUS_DEFAULT = self.status
 
-        if self.flatten_folders != None:
+        if self.flatten_folders is not None:
             sickbeard.FLATTEN_FOLDERS_DEFAULT = int(self.flatten_folders)
 
-        if self.future_show_paused != None:
+        if self.future_show_paused is not None:
             sickbeard.COMING_EPS_DISPLAY_PAUSED = int(self.future_show_paused)
 
         return _responds(RESULT_SUCCESS, msg="Saved defaults")
@@ -1826,11 +1820,11 @@ class CMD_SickBeardUpdate(ApiCall):
         ApiCall.__init__(self, args, kwargs)
 
     def run(self):
-        checkversion = CheckVersion()
+        check_version = CheckVersion()
 
-        if checkversion.check_for_new_version():
-            if checkversion.run_backup_if_safe():
-                checkversion.update()
+        if check_version.check_for_new_version():
+            if check_version.run_backup_if_safe():
+                check_version.update()
 
                 return _responds(RESULT_SUCCESS, msg="SickRage is updating ...")
 
@@ -1859,76 +1853,76 @@ class CMD_Show(ApiCall):
 
     def run(self):
         """ Get detailed information about a show """
-        showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
-        if not showObj:
+        show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
+        if not show_obj:
             return _responds(RESULT_FAILURE, msg="Show not found")
 
-        showDict = {
+        show_dict = {
             "season_list": CMD_ShowSeasonList((), {"indexerid": self.indexerid}).run()["data"],
             "cache": CMD_ShowCache((), {"indexerid": self.indexerid}).run()["data"]
         }
 
-        genreList = []
-        if showObj.genre:
-            genreListTmp = showObj.genre.split("|")
-            for genre in genreListTmp:
+        genre_list = []
+        if show_obj.genre:
+            genre_list_tmp = show_obj.genre.split("|")
+            for genre in genre_list_tmp:
                 if genre:
-                    genreList.append(genre)
+                    genre_list.append(genre)
 
-        showDict["genre"] = genreList
-        showDict["quality"] = get_quality_string(showObj.quality)
+        show_dict["genre"] = genre_list
+        show_dict["quality"] = get_quality_string(show_obj.quality)
 
-        anyQualities, bestQualities = _mapQuality(showObj.quality)
-        showDict["quality_details"] = {"initial": anyQualities, "archive": bestQualities}
+        any_qualities, best_qualities = _map_quality(show_obj.quality)
+        show_dict["quality_details"] = {"initial": any_qualities, "archive": best_qualities}
 
         try:
-            showDict["location"] = showObj.location
+            show_dict["location"] = show_obj.location
         except ShowDirectoryNotFoundException:
-            showDict["location"] = ""
-
-        showDict["language"] = showObj.lang
-        showDict["show_name"] = showObj.name
-        showDict["paused"] = (0, 1)[showObj.paused]
-        showDict["subtitles"] = (0, 1)[showObj.subtitles]
-        showDict["air_by_date"] = (0, 1)[showObj.air_by_date]
-        showDict["flatten_folders"] = (0, 1)[showObj.flatten_folders]
-        showDict["sports"] = (0, 1)[showObj.sports]
-        showDict["anime"] = (0, 1)[showObj.anime]
-        showDict["airs"] = str(showObj.airs).replace('am', ' AM').replace('pm', ' PM').replace('  ', ' ')
-        showDict["dvdorder"] = (0, 1)[showObj.dvdorder]
-
-        if showObj.rls_require_words:
-            showDict["rls_require_words"] = showObj.rls_require_words.split(", ")
+            show_dict["location"] = ""
+
+        show_dict["language"] = show_obj.lang
+        show_dict["show_name"] = show_obj.name
+        show_dict["paused"] = (0, 1)[show_obj.paused]
+        show_dict["subtitles"] = (0, 1)[show_obj.subtitles]
+        show_dict["air_by_date"] = (0, 1)[show_obj.air_by_date]
+        show_dict["flatten_folders"] = (0, 1)[show_obj.flatten_folders]
+        show_dict["sports"] = (0, 1)[show_obj.sports]
+        show_dict["anime"] = (0, 1)[show_obj.anime]
+        show_dict["airs"] = str(show_obj.airs).replace('am', ' AM').replace('pm', ' PM').replace('  ', ' ')
+        show_dict["dvdorder"] = (0, 1)[show_obj.dvdorder]
+
+        if show_obj.rls_require_words:
+            show_dict["rls_require_words"] = show_obj.rls_require_words.split(", ")
         else:
-            showDict["rls_require_words"] = []
+            show_dict["rls_require_words"] = []
 
-        if showObj.rls_ignore_words:
-            showDict["rls_ignore_words"] = showObj.rls_ignore_words.split(", ")
+        if show_obj.rls_ignore_words:
+            show_dict["rls_ignore_words"] = show_obj.rls_ignore_words.split(", ")
         else:
-            showDict["rls_ignore_words"] = []
+            show_dict["rls_ignore_words"] = []
 
-        showDict["scene"] = (0, 1)[showObj.scene]
-        showDict["archive_firstmatch"] = (0, 1)[showObj.archive_firstmatch]
+        show_dict["scene"] = (0, 1)[show_obj.scene]
+        show_dict["archive_firstmatch"] = (0, 1)[show_obj.archive_firstmatch]
 
-        showDict["indexerid"] = showObj.indexerid
-        showDict["tvdbid"] = helpers.mapIndexersToShow(showObj)[1]
-        showDict["imdbid"] = showObj.imdbid
+        show_dict["indexerid"] = show_obj.indexerid
+        show_dict["tvdbid"] = helpers.mapIndexersToShow(show_obj)[1]
+        show_dict["imdbid"] = show_obj.imdbid
 
-        showDict["network"] = showObj.network
-        if not showDict["network"]:
-            showDict["network"] = ""
-        showDict["status"] = showObj.status
+        show_dict["network"] = show_obj.network
+        if not show_dict["network"]:
+            show_dict["network"] = ""
+        show_dict["status"] = show_obj.status
 
-        if helpers.tryInt(showObj.nextaired, 1) > 693595:
-            dtEpisodeAirs = sbdatetime.sbdatetime.convert_to_setting(
-                network_timezones.parse_date_time(showObj.nextaired, showDict['airs'], showDict['network']))
-            showDict['airs'] = sbdatetime.sbdatetime.sbftime(dtEpisodeAirs, t_preset=timeFormat).lstrip('0').replace(
+        if helpers.tryInt(show_obj.nextaired, 1) > 693595:
+            dt_episode_airs = sbdatetime.sbdatetime.convert_to_setting(
+                network_timezones.parse_date_time(show_obj.nextaired, show_dict['airs'], show_dict['network']))
+            show_dict['airs'] = sbdatetime.sbdatetime.sbftime(dt_episode_airs, t_preset=timeFormat).lstrip('0').replace(
                 ' 0', ' ')
-            showDict['next_ep_airdate'] = sbdatetime.sbdatetime.sbfdate(dtEpisodeAirs, d_preset=dateFormat)
+            show_dict['next_ep_airdate'] = sbdatetime.sbdatetime.sbfdate(dt_episode_airs, d_preset=dateFormat)
         else:
-            showDict['next_ep_airdate'] = ''
+            show_dict['next_ep_airdate'] = ''
 
-        return _responds(RESULT_SUCCESS, showDict)
+        return _responds(RESULT_SUCCESS, show_dict)
 
 
 class CMD_ShowAddExisting(ApiCall):
@@ -1967,27 +1961,27 @@ class CMD_ShowAddExisting(ApiCall):
 
     def run(self):
         """ Add an existing show in SickRage """
-        showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
-        if showObj:
+        show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
+        if show_obj:
             return _responds(RESULT_FAILURE, msg="An existing indexerid already exists in the database")
 
         if not ek(os.path.isdir, self.location):
             return _responds(RESULT_FAILURE, msg='Not a valid location')
 
-        indexerName = None
-        indexerResult = CMD_SickBeardSearchIndexers([], {indexer_ids[self.indexer]: self.indexerid}).run()
+        indexer_name = None
+        indexer_result = CMD_SickBeardSearchIndexers([], {indexer_ids[self.indexer]: self.indexerid}).run()
 
-        if indexerResult['result'] == result_type_map[RESULT_SUCCESS]:
-            if not indexerResult['data']['results']:
+        if indexer_result['result'] == result_type_map[RESULT_SUCCESS]:
+            if not indexer_result['data']['results']:
                 return _responds(RESULT_FAILURE, msg="Empty results returned, check indexerid and try again")
-            if len(indexerResult['data']['results']) == 1 and 'name' in indexerResult['data']['results'][0]:
-                indexerName = indexerResult['data']['results'][0]['name']
+            if len(indexer_result['data']['results']) == 1 and 'name' in indexer_result['data']['results'][0]:
+                indexer_name = indexer_result['data']['results'][0]['name']
 
-        if not indexerName:
+        if not indexer_name:
             return _responds(RESULT_FAILURE, msg="Unable to retrieve information from indexer")
 
         # set indexer so we can pass it along when adding show to SR
-        indexer = indexerResult['data']['results'][0]['indexer']
+        indexer = indexer_result['data']['results'][0]['indexer']
 
         quality_map = {'sdtv': Quality.SDTV,
                        'sddvd': Quality.SDDVD,
@@ -2000,28 +1994,28 @@ class CMD_ShowAddExisting(ApiCall):
                        'fullhdbluray': Quality.FULLHDBLURAY,
                        'unknown': Quality.UNKNOWN}
 
-        # use default quality as a failsafe
-        newQuality = int(sickbeard.QUALITY_DEFAULT)
-        iqualityID = []
-        aqualityID = []
+        # use default quality as a fail-safe
+        new_quality = int(sickbeard.QUALITY_DEFAULT)
+        i_quality_id = []
+        a_quality_id = []
 
         if self.initial:
             for quality in self.initial:
-                iqualityID.append(quality_map[quality])
+                i_quality_id.append(quality_map[quality])
         if self.archive:
             for quality in self.archive:
-                aqualityID.append(quality_map[quality])
+                a_quality_id.append(quality_map[quality])
 
-        if iqualityID or aqualityID:
-            newQuality = Quality.combineQualities(iqualityID, aqualityID)
+        if i_quality_id or a_quality_id:
+            new_quality = Quality.combineQualities(i_quality_id, a_quality_id)
 
         sickbeard.showQueueScheduler.action.addShow(
             int(indexer), int(self.indexerid), self.location, default_status=sickbeard.STATUS_DEFAULT,
-            quality=newQuality, flatten_folders=int(self.flatten_folders), subtitles=self.subtitles,
+            quality=new_quality, flatten_folders=int(self.flatten_folders), subtitles=self.subtitles,
             default_status_after=sickbeard.STATUS_DEFAULT_AFTER, archive=self.archive_firstmatch
         )
 
-        return _responds(RESULT_SUCCESS, {"name": indexerName}, indexerName + " has been queued to be added")
+        return _responds(RESULT_SUCCESS, {"name": indexer_name}, indexer_name + " has been queued to be added")
 
 
 class CMD_ShowAddNew(ApiCall):
@@ -2082,8 +2076,8 @@ class CMD_ShowAddNew(ApiCall):
 
     def run(self):
         """ Add a new show to SickRage """
-        showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
-        if showObj:
+        show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
+        if show_obj:
             return _responds(RESULT_FAILURE, msg="An existing indexerid already exists in database")
 
         if not self.location:
@@ -2109,23 +2103,23 @@ class CMD_ShowAddNew(ApiCall):
                        'fullhdbluray': Quality.FULLHDBLURAY,
                        'unknown': Quality.UNKNOWN}
 
-        # use default quality as a failsafe
-        newQuality = int(sickbeard.QUALITY_DEFAULT)
-        iqualityID = []
-        aqualityID = []
+        # use default quality as a fail-safe
+        new_quality = int(sickbeard.QUALITY_DEFAULT)
+        i_quality_id = []
+        a_quality_id = []
 
         if self.initial:
             for quality in self.initial:
-                iqualityID.append(quality_map[quality])
+                i_quality_id.append(quality_map[quality])
         if self.archive:
             for quality in self.archive:
-                aqualityID.append(quality_map[quality])
+                a_quality_id.append(quality_map[quality])
 
-        if iqualityID or aqualityID:
-            newQuality = Quality.combineQualities(iqualityID, aqualityID)
+        if i_quality_id or a_quality_id:
+            new_quality = Quality.combineQualities(i_quality_id, a_quality_id)
 
-        # use default status as a failsafe
-        newStatus = sickbeard.STATUS_DEFAULT
+        # use default status as a fail-safe
+        new_status = sickbeard.STATUS_DEFAULT
         if self.status:
             # convert the string status to a int
             for status in statusStrings:
@@ -2139,9 +2133,9 @@ class CMD_ShowAddNew(ApiCall):
             # only allow the status options we want
             if int(self.status) not in (WANTED, SKIPPED, IGNORED):
                 return _responds(RESULT_FAILURE, msg="Status prohibited")
-            newStatus = self.status
+            new_status = self.status
 
-        # use default status as a failsafe
+        # use default status as a fail-safe
         default_ep_status_after = sickbeard.STATUS_DEFAULT_AFTER
         if self.future_status:
             # convert the string status to a int
@@ -2158,43 +2152,43 @@ class CMD_ShowAddNew(ApiCall):
                 return _responds(RESULT_FAILURE, msg="Status prohibited")
             default_ep_status_after = self.future_status
 
-        indexerName = None
-        indexerResult = CMD_SickBeardSearchIndexers([], {indexer_ids[self.indexer]: self.indexerid}).run()
+        indexer_name = None
+        indexer_result = CMD_SickBeardSearchIndexers([], {indexer_ids[self.indexer]: self.indexerid}).run()
 
-        if indexerResult['result'] == result_type_map[RESULT_SUCCESS]:
-            if not indexerResult['data']['results']:
+        if indexer_result['result'] == result_type_map[RESULT_SUCCESS]:
+            if not indexer_result['data']['results']:
                 return _responds(RESULT_FAILURE, msg="Empty results returned, check indexerid and try again")
-            if len(indexerResult['data']['results']) == 1 and 'name' in indexerResult['data']['results'][0]:
-                indexerName = indexerResult['data']['results'][0]['name']
+            if len(indexer_result['data']['results']) == 1 and 'name' in indexer_result['data']['results'][0]:
+                indexer_name = indexer_result['data']['results'][0]['name']
 
-        if not indexerName:
+        if not indexer_name:
             return _responds(RESULT_FAILURE, msg="Unable to retrieve information from indexer")
 
         # set indexer for found show so we can pass it along
-        indexer = indexerResult['data']['results'][0]['indexer']
+        indexer = indexer_result['data']['results'][0]['indexer']
 
         # moved the logic check to the end in an attempt to eliminate empty directory being created from previous errors
-        showPath = ek(os.path.join, self.location, helpers.sanitizeFileName(indexerName))
+        show_path = ek(os.path.join, self.location, helpers.sanitizeFileName(indexer_name))
 
         # don't create show dir if config says not to
         if sickbeard.ADD_SHOWS_WO_DIR:
-            logger.log(u"Skipping initial creation of " + showPath + " due to config.ini setting")
+            logger.log(u"Skipping initial creation of " + show_path + " due to config.ini setting")
         else:
-            dir_exists = helpers.makeDir(showPath)
+            dir_exists = helpers.makeDir(show_path)
             if not dir_exists:
-                logger.log(u"API :: Unable to create the folder " + showPath + ", can't add the show", logger.ERROR)
-                return _responds(RESULT_FAILURE, {"path": showPath},
-                                 "Unable to create the folder " + showPath + ", can't add the show")
+                logger.log(u"API :: Unable to create the folder " + show_path + ", can't add the show", logger.ERROR)
+                return _responds(RESULT_FAILURE, {"path": show_path},
+                                 "Unable to create the folder " + show_path + ", can't add the show")
             else:
-                helpers.chmodAsParent(showPath)
+                helpers.chmodAsParent(show_path)
 
         sickbeard.showQueueScheduler.action.addShow(
-            int(indexer), int(self.indexerid), showPath, default_status=newStatus, quality=newQuality,
+            int(indexer), int(self.indexerid), show_path, default_status=new_status, quality=new_quality,
             flatten_folders=int(self.flatten_folders), lang=self.lang, subtitles=self.subtitles, anime=self.anime,
             scene=self.scene, default_status_after=default_ep_status_after, archive=self.archive_firstmatch
         )
 
-        return _responds(RESULT_SUCCESS, {"name": indexerName}, indexerName + " has been queued to be added")
+        return _responds(RESULT_SUCCESS, {"name": indexer_name}, indexer_name + " has been queued to be added")
 
 
 class CMD_ShowCache(ApiCall):
@@ -2217,8 +2211,8 @@ class CMD_ShowCache(ApiCall):
 
     def run(self):
         """ Check SickRage's cache to see if the images (poster, banner, fanart) for a show are valid """
-        showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
-        if not showObj:
+        show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
+        if not show_obj:
             return _responds(RESULT_FAILURE, msg="Show not found")
 
         # TODO: catch if cache dir is missing/invalid.. so it doesn't break show/show.cache
@@ -2229,9 +2223,9 @@ class CMD_ShowCache(ApiCall):
         has_poster = 0
         has_banner = 0
 
-        if ek(os.path.isfile, cache_obj.poster_path(showObj.indexerid)):
+        if ek(os.path.isfile, cache_obj.poster_path(show_obj.indexerid)):
             has_poster = 1
-        if ek(os.path.isfile, cache_obj.banner_path(showObj.indexerid)):
+        if ek(os.path.isfile, cache_obj.banner_path(show_obj.indexerid)):
             has_banner = 1
 
         return _responds(RESULT_SUCCESS, {"poster": has_poster, "banner": has_banner})
@@ -2263,7 +2257,7 @@ class CMD_ShowDelete(ApiCall):
         """ Delete a show in SickRage """
         error, show = Show.delete(self.indexerid, self.removefiles)
 
-        if error is not None:
+        if error:
             return _responds(RESULT_FAILURE, msg=error)
 
         return _responds(RESULT_SUCCESS, msg='%s has been queued to be deleted' % show.name)
@@ -2289,13 +2283,13 @@ class CMD_ShowGetQuality(ApiCall):
 
     def run(self):
         """ Get the quality setting of a show """
-        showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
-        if not showObj:
+        show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
+        if not show_obj:
             return _responds(RESULT_FAILURE, msg="Show not found")
 
-        anyQualities, bestQualities = _mapQuality(showObj.quality)
+        any_qualities, best_qualities = _map_quality(show_obj.quality)
 
-        return _responds(RESULT_SUCCESS, {"initial": anyQualities, "archive": bestQualities})
+        return _responds(RESULT_SUCCESS, {"initial": any_qualities, "archive": best_qualities})
 
 
 class CMD_ShowGetPoster(ApiCall):
@@ -2406,7 +2400,7 @@ class CMD_ShowGetFanArt(ApiCall):
 
 class CMD_ShowPause(ApiCall):
     _help = {
-        "desc": "Pause or unpause a show",
+        "desc": "Pause or un-pause a show",
         "requiredParameters": {
             "indexerid": {"desc": "Unique ID of a show"},
         },
@@ -2425,10 +2419,10 @@ class CMD_ShowPause(ApiCall):
         ApiCall.__init__(self, args, kwargs)
 
     def run(self):
-        """ Pause or unpause a show """
+        """ Pause or un-pause a show """
         error, show = Show.pause(self.indexerid, self.pause)
 
-        if error is not None:
+        if error:
             return _responds(RESULT_FAILURE, msg=error)
 
         return _responds(RESULT_SUCCESS, msg='%s has been %s' % (show.name, ('resumed', 'paused')[show.paused]))
@@ -2456,7 +2450,7 @@ class CMD_ShowRefresh(ApiCall):
         """ Refresh a show in SickRage """
         error, show = Show.refresh(self.indexerid)
 
-        if error is not None:
+        if error:
             return _responds(RESULT_FAILURE, msg=error)
 
         return _responds(RESULT_SUCCESS, msg='%s has queued to be refreshed' % show.name)
@@ -2484,22 +2478,22 @@ class CMD_ShowSeasonList(ApiCall):
 
     def run(self):
         """ Get the list of seasons of a show """
-        showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
-        if not showObj:
+        show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
+        if not show_obj:
             return _responds(RESULT_FAILURE, msg="Show not found")
 
-        myDB = db.DBConnection(row_type="dict")
+        my_db = db.DBConnection(row_type="dict")
         if self.sort == "asc":
-            sqlResults = myDB.select("SELECT DISTINCT season FROM tv_episodes WHERE showid = ? ORDER BY season ASC",
-                                     [self.indexerid])
+            sql_results = my_db.select("SELECT DISTINCT season FROM tv_episodes WHERE showid = ? ORDER BY season ASC",
+                                       [self.indexerid])
         else:
-            sqlResults = myDB.select("SELECT DISTINCT season FROM tv_episodes WHERE showid = ? ORDER BY season DESC",
-                                     [self.indexerid])
-        seasonList = []  # a list with all season numbers
-        for row in sqlResults:
-            seasonList.append(int(row["season"]))
+            sql_results = my_db.select("SELECT DISTINCT season FROM tv_episodes WHERE showid = ? ORDER BY season DESC",
+                                       [self.indexerid])
+        season_list = []  # a list with all season numbers
+        for row in sql_results:
+            season_list.append(int(row["season"]))
 
-        return _responds(RESULT_SUCCESS, seasonList)
+        return _responds(RESULT_SUCCESS, season_list)
 
 
 class CMD_ShowSeasons(ApiCall):
@@ -2524,57 +2518,57 @@ class CMD_ShowSeasons(ApiCall):
 
     def run(self):
         """ Get the list of episodes for one or all seasons of a show """
-        showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
-        if not showObj:
+        sho_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
+        if not sho_obj:
             return _responds(RESULT_FAILURE, msg="Show not found")
 
-        myDB = db.DBConnection(row_type="dict")
+        my_db = db.DBConnection(row_type="dict")
 
-        if self.season == None:
-            sqlResults = myDB.select(
+        if self.season is None:
+            sql_results = my_db.select(
                 "SELECT name, episode, airdate, status, release_name, season, location, file_size, subtitles FROM tv_episodes WHERE showid = ?",
                 [self.indexerid])
             seasons = {}
-            for row in sqlResults:
+            for row in sql_results:
                 status, quality = Quality.splitCompositeStatus(int(row["status"]))
-                row["status"] = _get_status_Strings(status)
+                row["status"] = _get_status_strings(status)
                 row["quality"] = get_quality_string(quality)
                 if helpers.tryInt(row['airdate'], 1) > 693595:  # 1900
-                    dtEpisodeAirs = sbdatetime.sbdatetime.convert_to_setting(
-                        network_timezones.parse_date_time(row['airdate'], showObj.airs, showObj.network))
-                    row['airdate'] = sbdatetime.sbdatetime.sbfdate(dtEpisodeAirs, d_preset=dateFormat)
+                    dt_episode_airs = sbdatetime.sbdatetime.convert_to_setting(
+                        network_timezones.parse_date_time(row['airdate'], sho_obj.airs, sho_obj.network))
+                    row['airdate'] = sbdatetime.sbdatetime.sbfdate(dt_episode_airs, d_preset=dateFormat)
                 else:
                     row['airdate'] = 'Never'
-                curSeason = int(row["season"])
-                curEpisode = int(row["episode"])
+                cur_season = int(row["season"])
+                cur_episode = int(row["episode"])
                 del row["season"]
                 del row["episode"]
-                if not curSeason in seasons:
-                    seasons[curSeason] = {}
-                seasons[curSeason][curEpisode] = row
+                if cur_season not in seasons:
+                    seasons[cur_season] = {}
+                seasons[cur_season][cur_episode] = row
 
         else:
-            sqlResults = myDB.select(
+            sql_results = my_db.select(
                 "SELECT name, episode, airdate, status, location, file_size, release_name, subtitles FROM tv_episodes WHERE showid = ? AND season = ?",
                 [self.indexerid, self.season])
-            if len(sqlResults) is 0:
+            if len(sql_results) == 0:
                 return _responds(RESULT_FAILURE, msg="Season not found")
             seasons = {}
-            for row in sqlResults:
-                curEpisode = int(row["episode"])
+            for row in sql_results:
+                cur_episode = int(row["episode"])
                 del row["episode"]
                 status, quality = Quality.splitCompositeStatus(int(row["status"]))
-                row["status"] = _get_status_Strings(status)
+                row["status"] = _get_status_strings(status)
                 row["quality"] = get_quality_string(quality)
                 if helpers.tryInt(row['airdate'], 1) > 693595:  # 1900
-                    dtEpisodeAirs = sbdatetime.sbdatetime.convert_to_setting(
-                        network_timezones.parse_date_time(row['airdate'], showObj.airs, showObj.network))
-                    row['airdate'] = sbdatetime.sbdatetime.sbfdate(dtEpisodeAirs, d_preset=dateFormat)
+                    dt_episode_airs = sbdatetime.sbdatetime.convert_to_setting(
+                        network_timezones.parse_date_time(row['airdate'], sho_obj.airs, sho_obj.network))
+                    row['airdate'] = sbdatetime.sbdatetime.sbfdate(dt_episode_airs, d_preset=dateFormat)
                 else:
                     row['airdate'] = 'Never'
-                if not curEpisode in seasons:
-                    seasons[curEpisode] = {}
-                seasons[curEpisode] = row
+                if cur_episode not in seasons:
+                    seasons[cur_episode] = {}
+                seasons[cur_episode] = row
 
         return _responds(RESULT_SUCCESS, seasons)
 
@@ -2597,7 +2591,7 @@ class CMD_ShowSetQuality(ApiCall):
         self.indexerid, args = self.check_params(args, kwargs, "indexerid", None, True, "int", [])
         # optional
         # this for whatever reason removes hdbluray not sdtv... which is just wrong. reverting to previous code.. plus we didnt use the new code everywhere.
-        # self.archive, args = self.check_params(args, kwargs, "archive", None, False, "list", _getQualityMap().values()[1:])
+        # self.archive, args = self.check_params(args, kwargs, "archive", None, False, "list", _get_quality_map().values()[1:])
         self.initial, args = self.check_params(args, kwargs, "initial", None, False, "list",
                                                ["sdtv", "sddvd", "hdtv", "rawhdtv", "fullhdtv", "hdwebdl",
                                                 "fullhdwebdl", "hdbluray", "fullhdbluray", "unknown"])
@@ -2610,8 +2604,8 @@ class CMD_ShowSetQuality(ApiCall):
 
     def run(self):
         """ Set the quality setting of a show. If no quality is provided, the default user setting is used. """
-        showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
-        if not showObj:
+        show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
+        if not show_obj:
             return _responds(RESULT_FAILURE, msg="Show not found")
 
         quality_map = {'sdtv': Quality.SDTV,
@@ -2625,24 +2619,24 @@ class CMD_ShowSetQuality(ApiCall):
                        'fullhdbluray': Quality.FULLHDBLURAY,
                        'unknown': Quality.UNKNOWN}
 
-        # use default quality as a failsafe
-        newQuality = int(sickbeard.QUALITY_DEFAULT)
-        iqualityID = []
-        aqualityID = []
+        # use default quality as a fail-safe
+        new_quality = int(sickbeard.QUALITY_DEFAULT)
+        i_quality_id = []
+        a_quality_id = []
 
         if self.initial:
             for quality in self.initial:
-                iqualityID.append(quality_map[quality])
+                i_quality_id.append(quality_map[quality])
         if self.archive:
             for quality in self.archive:
-                aqualityID.append(quality_map[quality])
+                a_quality_id.append(quality_map[quality])
 
-        if iqualityID or aqualityID:
-            newQuality = Quality.combineQualities(iqualityID, aqualityID)
-        showObj.quality = newQuality
+        if i_quality_id or a_quality_id:
+            new_quality = Quality.combineQualities(i_quality_id, a_quality_id)
+        show_obj.quality = new_quality
 
         return _responds(RESULT_SUCCESS,
-                         msg=showObj.name + " quality has been changed to " + get_quality_string(showObj.quality))
+                         msg=show_obj.name + " quality has been changed to " + get_quality_string(show_obj.quality))
 
 
 class CMD_ShowStats(ApiCall):
@@ -2665,21 +2659,19 @@ class CMD_ShowStats(ApiCall):
 
     def run(self):
         """ Get episode statistics for a given show """
-        showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
-        if not showObj:
+        show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
+        if not show_obj:
             return _responds(RESULT_FAILURE, msg="Show not found")
 
         # show stats
-        episode_status_counts_total = {}
-        episode_status_counts_total["total"] = 0
+        episode_status_counts_total = {"total": 0}
         for status in statusStrings:
             if status in [UNKNOWN, DOWNLOADED, SNATCHED, SNATCHED_PROPER, ARCHIVED]:
                 continue
             episode_status_counts_total[status] = 0
 
         # add all the downloaded qualities
-        episode_qualities_counts_download = {}
-        episode_qualities_counts_download["total"] = 0
+        episode_qualities_counts_download = {"total": 0}
         for statusCode in Quality.DOWNLOADED + Quality.ARCHIVED:
             status, quality = Quality.splitCompositeStatus(statusCode)
             if quality in [Quality.NONE]:
@@ -2687,19 +2679,18 @@ class CMD_ShowStats(ApiCall):
             episode_qualities_counts_download[statusCode] = 0
 
         # add all snatched qualities
-        episode_qualities_counts_snatch = {}
-        episode_qualities_counts_snatch["total"] = 0
+        episode_qualities_counts_snatch = {"total": 0}
         for statusCode in Quality.SNATCHED + Quality.SNATCHED_PROPER:
             status, quality = Quality.splitCompositeStatus(statusCode)
             if quality in [Quality.NONE]:
                 continue
             episode_qualities_counts_snatch[statusCode] = 0
 
-        myDB = db.DBConnection(row_type="dict")
-        sqlResults = myDB.select("SELECT status, season FROM tv_episodes WHERE season != 0 AND showid = ?",
-                                 [self.indexerid])
+        my_db = db.DBConnection(row_type="dict")
+        sql_results = my_db.select("SELECT status, season FROM tv_episodes WHERE season != 0 AND showid = ?",
+                                   [self.indexerid])
         # the main loop that goes through all episodes
-        for row in sqlResults:
+        for row in sql_results:
             status, quality = Quality.splitCompositeStatus(int(row["status"]))
 
             episode_status_counts_total["total"] += 1
@@ -2710,36 +2701,35 @@ class CMD_ShowStats(ApiCall):
             elif status in Quality.SNATCHED + Quality.SNATCHED_PROPER:
                 episode_qualities_counts_snatch["total"] += 1
                 episode_qualities_counts_snatch[int(row["status"])] += 1
-            elif status == 0:  # we dont count NONE = 0 = N/A
+            elif status == 0:  # we don't count NONE = 0 = N/A
                 pass
             else:
                 episode_status_counts_total[status] += 1
 
         # the outgoing container
-        episodes_stats = {}
-        episodes_stats["downloaded"] = {}
+        episodes_stats = {"downloaded": {}}
         # turning codes into strings
         for statusCode in episode_qualities_counts_download:
             if statusCode == "total":
                 episodes_stats["downloaded"]["total"] = episode_qualities_counts_download[statusCode]
                 continue
             status, quality = Quality.splitCompositeStatus(int(statusCode))
-            statusString = Quality.qualityStrings[quality].lower().replace(" ", "_").replace("(", "").replace(")", "")
-            episodes_stats["downloaded"][statusString] = episode_qualities_counts_download[statusCode]
+            status_string = Quality.qualityStrings[quality].lower().replace(" ", "_").replace("(", "").replace(")", "")
+            episodes_stats["downloaded"][status_string] = episode_qualities_counts_download[statusCode]
 
         episodes_stats["snatched"] = {}
-        # truning codes into strings
+        # turning codes into strings
         # and combining proper and normal
         for statusCode in episode_qualities_counts_snatch:
             if statusCode == "total":
                 episodes_stats["snatched"]["total"] = episode_qualities_counts_snatch[statusCode]
                 continue
             status, quality = Quality.splitCompositeStatus(int(statusCode))
-            statusString = Quality.qualityStrings[quality].lower().replace(" ", "_").replace("(", "").replace(")", "")
+            status_string = Quality.qualityStrings[quality].lower().replace(" ", "_").replace("(", "").replace(")", "")
             if Quality.qualityStrings[quality] in episodes_stats["snatched"]:
-                episodes_stats["snatched"][statusString] += episode_qualities_counts_snatch[statusCode]
+                episodes_stats["snatched"][status_string] += episode_qualities_counts_snatch[statusCode]
             else:
-                episodes_stats["snatched"][statusString] = episode_qualities_counts_snatch[statusCode]
+                episodes_stats["snatched"][status_string] = episode_qualities_counts_snatch[statusCode]
 
         # episodes_stats["total"] = {}
         for statusCode in episode_status_counts_total:
@@ -2747,9 +2737,9 @@ class CMD_ShowStats(ApiCall):
                 episodes_stats["total"] = episode_status_counts_total[statusCode]
                 continue
             status, quality = Quality.splitCompositeStatus(int(statusCode))
-            statusString = statusStrings[statusCode].lower().replace(" ", "_").replace("(", "").replace(
+            status_string = statusStrings[statusCode].lower().replace(" ", "_").replace("(", "").replace(
                 ")", "")
-            episodes_stats[statusString] = episode_status_counts_total[statusCode]
+            episodes_stats[status_string] = episode_status_counts_total[statusCode]
 
         return _responds(RESULT_SUCCESS, episodes_stats)
 
@@ -2774,16 +2764,16 @@ class CMD_ShowUpdate(ApiCall):
 
     def run(self):
         """ Update a show in SickRage """
-        showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
-        if not showObj:
+        show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
+        if not show_obj:
             return _responds(RESULT_FAILURE, msg="Show not found")
 
         try:
-            sickbeard.showQueueScheduler.action.updateShow(showObj, True)  # @UndefinedVariable
-            return _responds(RESULT_SUCCESS, msg=str(showObj.name) + " has queued to be updated")
+            sickbeard.showQueueScheduler.action.updateShow(show_obj, True)  # @UndefinedVariable
+            return _responds(RESULT_SUCCESS, msg=str(show_obj.name) + " has queued to be updated")
         except CantUpdateShowException as e:
             logger.log(u"API::Unable to update show: {0}".format(str(e)), logger.DEBUG)
-            return _responds(RESULT_FAILURE, msg="Unable to update " + str(showObj.name))
+            return _responds(RESULT_FAILURE, msg="Unable to update " + str(show_obj.name))
 
 
 class CMD_Shows(ApiCall):
@@ -2808,12 +2798,12 @@ class CMD_Shows(ApiCall):
         shows = {}
         for curShow in sickbeard.showList:
 
-            if self.paused is not None and bool(self.paused) != bool(curShow.paused):
+            if not self.paused and not curShow.paused:
                 continue
 
-            indexerShow = helpers.mapIndexersToShow(curShow)
+            indexer_show = helpers.mapIndexersToShow(curShow)
 
-            showDict = {
+            show_dict = {
                 "paused": (0, 1)[curShow.paused],
                 "quality": get_quality_string(curShow.quality),
                 "language": curShow.lang,
@@ -2821,7 +2811,7 @@ class CMD_Shows(ApiCall):
                 "sports": (0, 1)[curShow.sports],
                 "anime": (0, 1)[curShow.anime],
                 "indexerid": curShow.indexerid,
-                "tvdbid": indexerShow[1],
+                "tvdbid": indexer_show[1],
                 "network": curShow.network,
                 "show_name": curShow.name,
                 "status": curShow.status,
@@ -2829,19 +2819,19 @@ class CMD_Shows(ApiCall):
             }
 
             if helpers.tryInt(curShow.nextaired, 1) > 693595:  # 1900
-                dtEpisodeAirs = sbdatetime.sbdatetime.convert_to_setting(
-                    network_timezones.parse_date_time(curShow.nextaired, curShow.airs, showDict['network']))
-                showDict['next_ep_airdate'] = sbdatetime.sbdatetime.sbfdate(dtEpisodeAirs, d_preset=dateFormat)
+                dt_episode_airs = sbdatetime.sbdatetime.convert_to_setting(
+                    network_timezones.parse_date_time(curShow.nextaired, curShow.airs, show_dict['network']))
+                show_dict['next_ep_airdate'] = sbdatetime.sbdatetime.sbfdate(dt_episode_airs, d_preset=dateFormat)
             else:
-                showDict['next_ep_airdate'] = ''
+                show_dict['next_ep_airdate'] = ''
 
-            showDict["cache"] = CMD_ShowCache((), {"indexerid": curShow.indexerid}).run()["data"]
-            if not showDict["network"]:
-                showDict["network"] = ""
+            show_dict["cache"] = CMD_ShowCache((), {"indexerid": curShow.indexerid}).run()["data"]
+            if not show_dict["network"]:
+                show_dict["network"] = ""
             if self.sort == "name":
-                shows[curShow.name] = showDict
+                shows[curShow.name] = show_dict
             else:
-                shows[curShow.indexerid] = showDict
+                shows[curShow.indexerid] = show_dict
 
         return _responds(RESULT_SUCCESS, shows)
 
diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py
index b5ee0d8a09aaac682580f824844f63e588b6a432..baa9380f1c216e3f0bb78a744afdea2a91a37ccf 100644
--- a/sickbeard/webserve.py
+++ b/sickbeard/webserve.py
@@ -24,6 +24,7 @@ import time
 import urllib
 import datetime
 import traceback
+import ast
 
 import sickbeard
 from sickbeard import config, sab
@@ -995,19 +996,48 @@ class Home(WebRoot):
         data = {}
         size = 0
         for r in rows:
-            data[r['show_id']] = {'id': r['show_id'], 'name': r['show_name'], 'list': r['notify_list']}
+            NotifyList = {'emails':'', 'prowlAPIs':''}
+            if (r['notify_list'] and len(r['notify_list']) > 0):
+                # First, handle legacy format (emails only)
+                if not r['notify_list'][0] == '{':
+                    NotifyList['emails'] = r['notify_list']
+                else:
+                    NotifyList = dict(ast.literal_eval(r['notify_list']))
+
+            data[r['show_id']] = {'id': r['show_id'],'name': r['show_name'],
+                                  'list': NotifyList['emails'],
+                                  'prowl_notify_list': NotifyList['prowlAPIs']
+                                 }
             size += 1
         data['_size'] = size
         return json.dumps(data)
 
     @staticmethod
-    def saveShowNotifyList(show=None, emails=None):
+    def saveShowNotifyList(show=None, emails=None, prowlAPIs=None):
 
+        entries = {'emails':'', 'prowlAPIs':''}
         myDB = db.DBConnection()
-        if myDB.action("UPDATE tv_shows SET notify_list = ? WHERE show_id = ?", [emails, show]):
-            return 'OK'
-        else:
-            return 'ERROR'
+
+        # Get current data
+        for subs in myDB.select("SELECT notify_list FROM tv_shows WHERE show_id = ?", [show]):
+            if (subs['notify_list'] and len(subs['notify_list']) > 0):
+                # First, handle legacy format (emails only)
+                if not subs['notify_list'][0] == '{':
+                    entries['emails'] = subs['notify_list']
+                else:
+                    entries = dict(ast.literal_eval(subs['notify_list']))
+
+        if (emails is not None):
+            entries['emails'] = emails
+            if not myDB.action("UPDATE tv_shows SET notify_list = ? WHERE show_id = ?", [str(entries), show]):
+                return 'ERROR'
+
+        if (prowlAPIs is not None):
+            entries['prowlAPIs'] = prowlAPIs
+            if not myDB.action("UPDATE tv_shows SET notify_list = ? WHERE show_id = ?", [str(entries), show]):
+                return 'ERROR'
+
+        return 'OK'
 
     @staticmethod
     def testEmail(host=None, port=None, smtp_from=None, use_tls=None, user=None, pwd=None, to=None):
@@ -1149,14 +1179,15 @@ class Home(WebRoot):
             return json.dumps({"status": "error", 'message': 'General exception'})
 
     def displayShow(self, show=None):
+        # todo: add more comprehensive show validation
+        try:
+            show = int(show)  # fails if show id ends in a period SickRage/sickrage-issues#65
+            showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, show)
+        except (ValueError, TypeError):
+            return self._genericMessage("Error", "Invalid show ID: %s" % str(show))
 
-        if show is None:
-            return self._genericMessage("Error", "Invalid show ID")
-        else:
-            showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(show))
-
-            if showObj is None:
-                return self._genericMessage("Error", "Show not in show list")
+        if showObj is None:
+            return self._genericMessage("Error", "Show not in show list")
 
         myDB = db.DBConnection()
         seasonResults = myDB.select(
@@ -3020,13 +3051,13 @@ class Manage(Home, WebRoot):
             epCounts[Overview.SNATCHED] = 0
 
             sqlResults = myDB.select(
-                "SELECT * FROM tv_episodes WHERE tv_episodes.showid in (SELECT tv_shows.indexer_id FROM tv_shows WHERE tv_shows.indexer_id = ? AND paused = 0) ORDER BY tv_episodes.season DESC, tv_episodes.episode DESC",
+                "SELECT status, season, episode, name, airdate FROM tv_episodes WHERE tv_episodes.showid in (SELECT tv_shows.indexer_id FROM tv_shows WHERE tv_shows.indexer_id = ? AND paused = 0) ORDER BY tv_episodes.season DESC, tv_episodes.episode DESC",
                 [curShow.indexerid])
 
             for curResult in sqlResults:
                 curEpCat = curShow.getOverview(int(curResult["status"] or -1))
                 if curEpCat:
-                    epCats[str(curResult["season"]) + "x" + str(curResult["episode"])] = curEpCat
+                    epCats['S%02dE%02d' % (curResult['season'], curResult['episode'])] = curEpCat
                     epCounts[curEpCat] += 1
 
             showCounts[curShow.indexerid] = epCounts
@@ -3462,6 +3493,7 @@ class ManageSearches(Manage):
         return t.render(backlogPaused=sickbeard.searchQueueScheduler.action.is_backlog_paused(),
                         backlogRunning=sickbeard.searchQueueScheduler.action.is_backlog_in_progress(), dailySearchStatus=sickbeard.dailySearchScheduler.action.amActive,
                         findPropersStatus=sickbeard.properFinderScheduler.action.amActive, queueLength=sickbeard.searchQueueScheduler.action.queue_length(),
+                        subtitlesFinderStatus=sickbeard.subtitlesFinderScheduler.action.amActive,
                         title='Manage Searches', header='Manage Searches', topmenu='manage',
                         controller="manage", action="manageSearches")
 
@@ -3493,6 +3525,15 @@ class ManageSearches(Manage):
 
         return self.redirect("/manage/manageSearches/")
 
+    def forceSubtitlesFinder(self):
+        # force it to run the next time it looks
+        result = sickbeard.subtitlesFinderScheduler.forceRun()
+        if result:
+            logger.log(u"Subtitle search forced")
+            ui.notifications.message('Subtitle search started')
+
+        return self.redirect("/manage/manageSearches/")
+
     def pauseBacklog(self, paused=None):
         if paused == "1":
             sickbeard.searchQueueScheduler.action.pause_backlog()
@@ -3666,7 +3707,7 @@ class ConfigGeneral(Config):
                     handle_reverse_proxy=None, sort_article=None, auto_update=None, notify_on_update=None,
                     proxy_setting=None, proxy_indexers=None, anon_redirect=None, git_path=None, git_remote=None,
                     calendar_unprotected=None, calendar_icons=None, debug=None, ssl_verify=None, no_restart=None, coming_eps_missed_range=None,
-                    filter_row=None, fuzzy_dating=None, trim_zero=None, date_preset=None, date_preset_na=None, time_preset=None,
+                    fuzzy_dating=None, trim_zero=None, date_preset=None, date_preset_na=None, time_preset=None,
                     indexer_timeout=None, download_url=None, rootDir=None, theme_name=None, default_page=None,
                     git_reset=None, git_username=None, git_password=None, git_autoissues=None, display_all_seasons=None):
 
@@ -3722,7 +3763,6 @@ class ConfigGeneral(Config):
         sickbeard.WEB_USERNAME = web_username
         sickbeard.WEB_PASSWORD = web_password
 
-        sickbeard.FILTER_ROW = config.checkbox_to_value(filter_row)
         sickbeard.FUZZY_DATING = config.checkbox_to_value(fuzzy_dating)
         sickbeard.TRIM_ZERO = config.checkbox_to_value(trim_zero)
 
@@ -4429,6 +4469,12 @@ class ConfigProviders(Config):
         for curTorrentProvider in [curProvider for curProvider in sickbeard.providers.sortedProviderList() if
                                    curProvider.providerType == sickbeard.GenericProvider.TORRENT]:
 
+            if hasattr(curTorrentProvider, 'custom_url'):
+                try:
+                    curTorrentProvider.custom_url = str(kwargs[curTorrentProvider.getID() + '_custom_url']).strip()
+                except Exception:
+                    curTorrentProvider.custom_url = None
+
             if hasattr(curTorrentProvider, 'minseed'):
                 try:
                     curTorrentProvider.minseed = int(str(kwargs[curTorrentProvider.getID() + '_minseed']).strip())
@@ -4655,6 +4701,7 @@ class ConfigNotifications(Config):
                           freemobile_notify_onsubtitledownload=None, freemobile_id=None, freemobile_apikey=None,
                           use_prowl=None, prowl_notify_onsnatch=None, prowl_notify_ondownload=None,
                           prowl_notify_onsubtitledownload=None, prowl_api=None, prowl_priority=0,
+                          prowl_show_list=None, prowl_show=None, prowl_message_title=None,
                           use_twitter=None, twitter_notify_onsnatch=None, twitter_notify_ondownload=None,
                           twitter_notify_onsubtitledownload=None, twitter_usedm=None, twitter_dmto=None,
                           use_boxcar2=None, boxcar2_notify_onsnatch=None, boxcar2_notify_ondownload=None,
@@ -4738,6 +4785,7 @@ class ConfigNotifications(Config):
         sickbeard.PROWL_NOTIFY_ONSUBTITLEDOWNLOAD = config.checkbox_to_value(prowl_notify_onsubtitledownload)
         sickbeard.PROWL_API = prowl_api
         sickbeard.PROWL_PRIORITY = prowl_priority
+        sickbeard.PROWL_MESSAGE_TITLE = prowl_message_title
 
         sickbeard.USE_TWITTER = config.checkbox_to_value(use_twitter)
         sickbeard.TWITTER_NOTIFY_ONSNATCH = config.checkbox_to_value(twitter_notify_onsnatch)
diff --git a/sickrage/helper/common.py b/sickrage/helper/common.py
index 2ac3bf6698b7a249751a80f6185c7d95038e1fa0..e33a796a17c1bef65842b5978e2e2897c5bd06d1 100644
--- a/sickrage/helper/common.py
+++ b/sickrage/helper/common.py
@@ -18,4 +18,9 @@
 
 dateFormat = '%Y-%m-%d'
 dateTimeFormat = '%Y-%m-%d %H:%M:%S'
+media_extensions = [
+    '3gp', 'avi', 'divx', 'dvr-ms', 'f4v', 'flv', 'img', 'iso', 'm2ts', 'm4v', 'mkv', 'mov', 'mp4', 'mpeg', 'mpg',
+    'ogm', 'ogv', 'rmvb', 'tp', 'ts', 'vob', 'webm', 'wmv', 'wtv',
+]
+subtitle_extensions = ['ass', 'idx', 'srt', 'ssa', 'sub']
 timeFormat = '%A %I:%M %p'
diff --git a/sickrage/show/ComingEpisodes.py b/sickrage/show/ComingEpisodes.py
index 65b34022839127c5404f4418df8d7c7893fae493..55a5f6292445ed168c3598c3ae165a2d0b96c4ec 100644
--- a/sickrage/show/ComingEpisodes.py
+++ b/sickrage/show/ComingEpisodes.py
@@ -54,11 +54,8 @@ class ComingEpisodes:
         :return: The list of coming episodes
         """
 
-        if not isinstance(categories, list):
-            categories = categories.split('|')
-
-        if sort not in ComingEpisodes.sorts.keys():
-            sort = 'date'
+        categories = ComingEpisodes._get_categories(categories)
+        sort = ComingEpisodes._get_sort(sort)
 
         today = date.today().toordinal()
         next_week = (date.today() + timedelta(days=7)).toordinal()
@@ -124,7 +121,7 @@ class ComingEpisodes:
         if not group:
             return results
 
-        grouped_results = {category: [] for category in categories}
+        grouped_results = ComingEpisodes._get_categories_map(categories)
 
         for result in results:
             if result['paused'] and not paused:
@@ -158,3 +155,29 @@ class ComingEpisodes:
             grouped_results[category].append(result)
 
         return grouped_results
+
+    @staticmethod
+    def _get_categories(categories):
+        if not categories:
+            return []
+
+        if not isinstance(categories, list):
+            return categories.split('|')
+
+        return categories
+
+    @staticmethod
+    def _get_categories_map(categories):
+        if not categories:
+            return {}
+
+        return {category: [] for category in categories}
+
+    @staticmethod
+    def _get_sort(sort):
+        sort = sort.lower() if sort else ''
+
+        if sort not in ComingEpisodes.sorts.keys():
+            return 'date'
+
+        return sort
diff --git a/sickrage/show/History.py b/sickrage/show/History.py
index 0b04e02aa78342a8ae52053e19a140d6a0dcf36c..c015da8a8d420fe4f134205b52b3a30a5a037b0f 100644
--- a/sickrage/show/History.py
+++ b/sickrage/show/History.py
@@ -46,15 +46,8 @@ class History:
         :return: The last ``limit`` elements of type ``action`` in the history
         """
 
-        action = action.lower() if isinstance(action, str) else ''
-        limit = int(limit)
-
-        if action == 'downloaded':
-            actions = Quality.DOWNLOADED
-        elif action == 'snatched':
-            actions = Quality.SNATCHED
-        else:
-            actions = []
+        actions = History._get_actions(action)
+        limit = History._get_limit(limit)
 
         common_sql = 'SELECT action, date, episode, provider, h.quality, resource, season, show_name, showid ' \
                      'FROM history h, tv_shows s ' \
@@ -100,3 +93,27 @@ class History:
             'WHERE date < ?',
             [(datetime.today() - timedelta(days=30)).strftime(History.date_format)]
         )
+
+    @staticmethod
+    def _get_actions(action):
+        action = action.lower() if isinstance(action, str) else ''
+
+        if action == 'downloaded':
+            return Quality.DOWNLOADED
+
+        if action == 'snatched':
+            return Quality.SNATCHED
+
+        return []
+
+    @staticmethod
+    def _get_limit(limit):
+        try:
+            limit = int(limit)
+        except (TypeError, ValueError):
+            return 0
+
+        if limit < 0:
+            return 0
+
+        return int(limit)
diff --git a/sickrage/show/Show.py b/sickrage/show/Show.py
index 2e3601bce85ec339e53b1568b3307e7071e28b6a..a6824fc05054be5fdad8517d8cc0a644465bf02b 100644
--- a/sickrage/show/Show.py
+++ b/sickrage/show/Show.py
@@ -152,7 +152,9 @@ class Show:
          - the show object corresponding to ``indexer_id`` if it exists, ``None`` otherwise
         """
 
-        if indexer_id is None:
+        try:
+            indexer_id = int(indexer_id)
+        except (TypeError, ValueError):
             return 'Invalid show ID', None
 
         try:
diff --git a/tests/all_tests.py b/tests/all_tests.py
index 010946da9ea0ff772dff8a2268efc3a0c7be66a3..de388878e699e32834af523c5ecf68227e96410d 100755
--- a/tests/all_tests.py
+++ b/tests/all_tests.py
@@ -18,35 +18,60 @@
 # You should have received a copy of the GNU General Public License
 # along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
 
-import sys, os.path
+import fnmatch
+import os
+import sys
+import unittest
 
-tests_dir=os.path.abspath(__file__)[:-len(os.path.basename(__file__))]
+tests_dir = os.path.abspath(__file__)[:-len(os.path.basename(__file__))]
 
 sys.path.insert(1, os.path.join(tests_dir, '../lib'))
 sys.path.insert(1, os.path.join(tests_dir, '..'))
 
-import glob
-import unittest
 
 class AllTests(unittest.TestCase):
-    #Block issue_submitter_tests to avoid issue tracker spam on every build
+    # Block issue_submitter_tests to avoid issue tracker spam on every build
     blacklist = [tests_dir + 'all_tests.py', tests_dir + 'issue_submitter_tests.py', tests_dir + 'search_tests.py']
+
     def setUp(self):
-        self.test_file_strings = [ x for x in glob.glob(tests_dir + '*_tests.py') if not x in self.blacklist ]
-        self.module_strings = [file_string[len(tests_dir):len(file_string) - 3] for file_string in self.test_file_strings]
-        self.suites = [unittest.defaultTestLoader.loadTestsFromName(file_string) for file_string in self.module_strings]
+        self.test_file_strings = self._get_test_files()
+        self.module_strings = self._get_module_strings()
+        self.suites = self._get_test_suites()
         self.testSuite = unittest.TestSuite(self.suites)
 
     def testAll(self):
-        print "=================="
+        print "===================="
         print "STARTING - ALL TESTS"
-        print "=================="
-        for includedfiles in self.test_file_strings:
-            print "- " + includedfiles[len(tests_dir):-3]
+        print "===================="
+
+        for included_files in self.test_file_strings:
+            print "- " + included_files[len(tests_dir):-3]
 
         text_runner = unittest.TextTestRunner().run(self.testSuite)
         if not text_runner.wasSuccessful():
             sys.exit(-1)
 
+    def _get_module_strings(self):
+        modules = []
+        for file_string in self.test_file_strings:
+            modules.append(file_string[len(tests_dir):len(file_string) - 3].replace(os.sep, '.'))
+
+        return modules
+
+    def _get_test_files(self):
+        matches = []
+        for root, _, file_names in os.walk(tests_dir):
+            for filename in fnmatch.filter(file_names, '*_tests.py'):
+                filename_with_path = os.path.join(root, filename)
+
+                if filename_with_path not in self.blacklist:
+                    matches.append(filename_with_path)
+
+        return matches
+
+    def _get_test_suites(self):
+        return [unittest.defaultTestLoader.loadTestsFromName(file_string) for file_string in self.module_strings]
+
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/tests/common_tests.py b/tests/common_tests.py
index 1ba62d02d5e958dccf085fc0b2065aed7782fc1b..b0e6a1df03d3cb955f6a2afb0d9fe0b20d2dedbb 100644
--- a/tests/common_tests.py
+++ b/tests/common_tests.py
@@ -236,7 +236,7 @@ class StatusStringsTest(unittest.TestCase):
         for i in unused:
             if i is None:
                 with self.assertRaises(TypeError):
-                    status_strings[str(i)] = 1  # 'None' != None
+                    status_strings[str(i)] = 1  # 'None' is not None
                 status_strings[i] = 1  # ...but None can still be used as a key
             else:
                 status_strings[str(i)] = 1
diff --git a/tests/db_tests.py b/tests/db_tests.py
index 74383f8f5d5d28639636684053212b256fb72e0c..fca31fec0458a010af0ebd25556163befae4c912 100644
--- a/tests/db_tests.py
+++ b/tests/db_tests.py
@@ -17,42 +17,82 @@
 # You should have received a copy of the GNU General Public License
 # along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
 
-import sys, os.path
-sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '../lib')))
-sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
+"""
+Test show database functionality.
+
+Tests:
+    DBBasicTests
+    DBMultiTests
+"""
 
+import sys
+import os.path
 import unittest
-import test_lib as test
 import threading
 
+sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '../lib')))
+sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
+
+import tests.test_lib as test
+
+
 class DBBasicTests(test.SickbeardTestDBCase):
+    """
+    Perform basic database tests.
+
+    Tests:
+        test_select
+    """
+
     def setUp(self):
+        """
+        Set up test.
+        """
         super(DBBasicTests, self).setUp()
-        self.db = test.db.DBConnection()
+        self.sr_db = test.db.DBConnection()
 
     def test_select(self):
-        self.db.select("SELECT * FROM tv_episodes WHERE showid = ? AND location != ''", [0000])
+        """
+        Test selecting from the database
+        """
+        self.sr_db.select("SELECT * FROM tv_episodes WHERE showid = ? AND location != ''", [0000])
+
 
 class DBMultiTests(test.SickbeardTestDBCase):
+    """
+    Perform multi-threaded test of the database
+
+    Tests:
+        test_threaded
+    """
     def setUp(self):
+        """
+        Set up test.
+        """
         super(DBMultiTests, self).setUp()
-        self.db = test.db.DBConnection()
+        self.sr_db = test.db.DBConnection()
 
     def select(self):
-        self.db.select("SELECT * FROM tv_episodes WHERE showid = ? AND location != ''", [0000])
+        """
+        Select from the database.
+        """
+        self.sr_db.select("SELECT * FROM tv_episodes WHERE showid = ? AND location != ''", [0000])
 
     def test_threaded(self):
-        for i in xrange(4):
-            t = threading.Thread(target=self.select)
-            t.start()
+        """
+        Test multi-threaded selection from the database
+        """
+        for _ in xrange(4):
+            thread = threading.Thread(target=self.select)
+            thread.start()
 
 if __name__ == '__main__':
     print "=================="
     print "STARTING - DB TESTS"
     print "=================="
     print "######################################################################"
-    suite = unittest.TestLoader().loadTestsFromTestCase(DBBasicTests)
-    unittest.TextTestRunner(verbosity=2).run(suite)
+    SUITE = unittest.TestLoader().loadTestsFromTestCase(DBBasicTests)
+    unittest.TextTestRunner(verbosity=2).run(SUITE)
 
-    #suite = unittest.TestLoader().loadTestsFromTestCase(DBMultiTests)
-    #unittest.TextTestRunner(verbosity=2).run(suite)
+    # suite = unittest.TestLoader().loadTestsFromTestCase(DBMultiTests)
+    # unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/notifier_tests.py b/tests/notifier_tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..04701f64d6349eda7ce52a4cb834f72a5277408f
--- /dev/null
+++ b/tests/notifier_tests.py
@@ -0,0 +1,214 @@
+# coding=UTF-8
+# URL: https://github.com/SickRage/SickRage
+#
+# This file is part of SickRage.
+#
+# SickRage is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# SickRage is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+###
+# As a test case, there are instances in which it is necessary to call protected members of
+# classes in order to test those classes.  Therefore:
+# pylint: disable=W0212
+###
+
+import sys, os.path
+sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '../lib')))
+sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
+
+import unittest
+
+import tests.test_lib as test
+
+from sickbeard import db
+from sickbeard.tv import TVEpisode, TVShow
+from sickbeard.webserve import Home
+from sickbeard.notifiers.emailnotify import EmailNotifier
+from sickbeard.notifiers.prowl import ProwlNotifier
+
+from sickrage.helper.encoding import ss
+
+class NotifierTests(test.SickbeardTestDBCase):
+
+    @classmethod
+    def setUpClass(cls):
+        num_legacy_shows = 3
+        num_shows = 3
+        num_episodes_per_show = 5
+        cls.mydb = db.DBConnection()
+        cls.legacy_shows = []
+        cls.shows = []
+
+        # Per-show-notifications were originally added for email notifications only.  To add
+        # this feature to other notifiers, it was necessary to alter the way text is stored in
+        # one of the DB columns.  Therefore, to test properly, we must create some shows that
+        # store emails in the old method (legacy method) and then other shows that will use
+        # the new method.
+        for show_counter in range(100, 100+num_legacy_shows):
+            show = TVShow(1, show_counter)
+            show.name = "Show "+str(show_counter)
+            show.episodes = []
+            for episode_counter in range(0, num_episodes_per_show):
+                episode = TVEpisode(show, test.SEASON, episode_counter)
+                episode.name = "Episode "+str(episode_counter+1)
+                episode.quality = "SDTV"
+                show.episodes.append(episode)
+            show.saveToDB()
+            cls.legacy_shows.append(show)
+
+        for show_counter in range(200, 200+num_shows):
+            show = TVShow(1, show_counter)
+            show.name = "Show "+str(show_counter)
+            show.episodes = []
+            for episode_counter in range(0, num_episodes_per_show):
+                episode = TVEpisode(show, test.SEASON, episode_counter)
+                episode.name = "Episode "+str(episode_counter+1)
+                episode.quality = "SDTV"
+                show.episodes.append(episode)
+            show.saveToDB()
+            cls.shows.append(show)
+
+    def setUp(self):
+        self._debug_spew("\n\r")
+
+    #def test_boxcar(self):
+    #    pass
+
+    def test_email(self):
+        email_notifier = EmailNotifier()
+
+        # Per-show-email notifications were added early on and utilized a different format than the other notifiers.
+        # Therefore, to test properly (and ensure backwards compatibility), this routine will test shows that use
+        # both the old and the new storage methodology
+        legacy_test_emails = "email-1@address.com,email2@address.org,email_3@address.tv"
+        test_emails = "email-4@address.com,email5@address.org,email_6@address.tv"
+
+        for show in self.legacy_shows:
+            showid = self._get_showid_by_showname(show.name)
+            self.mydb.action("UPDATE tv_shows SET notify_list = ? WHERE show_id = ?", [legacy_test_emails, showid])
+
+        for show in self.shows:
+            showid = self._get_showid_by_showname(show.name)
+            Home.saveShowNotifyList(show=showid, emails=test_emails)
+
+
+        # Now, iterate through all shows using the email list generation routines that are used in the notifier proper
+        shows = self.legacy_shows+self.shows
+        for show in shows:
+            for episode in show.episodes:
+                ep_name = ss(episode._format_pattern('%SN - %Sx%0E - %EN - ')+episode.quality)
+                show_name = email_notifier._parseEp(ep_name)
+                recipients = email_notifier._generate_recipients(show_name)
+                self._debug_spew("- Email Notifications for "+show.name+" (episode: "+episode.name+") will be sent to:")
+                for email in recipients:
+                    self._debug_spew("-- "+email.strip())
+                self._debug_spew("\n\r")
+
+        return True
+
+    #def test_emby(self):
+    #    pass
+
+    #def test_freemobile(self):
+    #    pass
+
+    #def test_growl(self):
+    #    pass
+
+    #def test_kodi(self):
+    #    pass
+
+    #def test_libnotify(self):
+    #    pass
+
+    #def test_nma(self):
+    #    pass
+
+    #def test_nmj(self):
+    #    pass
+
+    #def test_nmjv2(self):
+    #    pass
+
+    #def test_plex(self):
+    #    pass
+
+    def test_prowl(self):
+        prowl_notifier = ProwlNotifier()
+
+        # Prowl per-show-notifications only utilize the new methodology for storage; therefore, the list of legacy_shows
+        # will not be altered (to preserve backwards compatibility testing)
+        test_prowl_apis = "11111111111111111111,22222222222222222222"
+
+        for show in self.shows:
+            showid = self._get_showid_by_showname(show.name)
+            Home.saveShowNotifyList(show=showid, prowlAPIs=test_prowl_apis)
+
+        # Now, iterate through all shows using the Prowl API generation routines that are used in the notifier proper
+        for show in self.shows:
+            for episode in show.episodes:
+                ep_name = ss(episode._format_pattern('%SN - %Sx%0E - %EN - ')+episode.quality)
+                show_name = prowl_notifier._parse_episode(ep_name)
+                recipients = prowl_notifier._generate_recipients(show_name)
+                self._debug_spew("- Prowl Notifications for "+show.name+" (episode: "+episode.name+") will be sent to:")
+                for api in recipients:
+                    self._debug_spew("-- "+api.strip())
+                self._debug_spew("\n\r")
+
+        return True
+
+    #def test_pushalot(self):
+    #    pass
+
+    #def test_pushbullet(self):
+    #    pass
+
+    #def test_pushover(self):
+    #    pass
+
+    #def test_pytivo(self):
+    #    pass
+
+    #def test_synoindex(self):
+    #    pass
+
+    #def test_synologynotifier(self):
+    #    pass
+
+    #def test_trakt(self):
+    #    pass
+
+    #def test_tweet(self):
+    #    pass
+
+    @staticmethod
+    def _debug_spew(text):
+        if __name__ == '__main__' and text is not None:
+            print text
+
+    def _get_showid_by_showname(self, showname):
+        if showname is not None:
+            rows = self.mydb.select("SELECT show_id FROM tv_shows WHERE show_name = ?", [showname])
+            if len(rows) == 1:
+                return rows[0]['show_id']
+        return -1
+
+if __name__ == '__main__':
+    print "=================="
+    print "STARTING - NOTIFIER TESTS"
+    print "=================="
+    print "######################################################################"
+    SUITE = unittest.TestLoader().loadTestsFromTestCase(NotifierTests)
+    unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/pp_tests.py b/tests/pp_tests.py
index 29d13985c58de8b6fc844a47681c15d8c4520b5e..c11b90338b1c8746d0b99d51bfeea538a75e016a 100644
--- a/tests/pp_tests.py
+++ b/tests/pp_tests.py
@@ -35,20 +35,20 @@ from sickbeard.name_cache import addNameToCache
 class PPInitTests(unittest.TestCase):
 
     def setUp(self):
-        self.pp = PostProcessor(test.FILEPATH)
+        self.pp = PostProcessor(test.FILE_PATH)
 
     def test_init_file_name(self):
         self.assertEqual(self.pp.file_name, test.FILENAME)
 
     def test_init_folder_name(self):
-        self.assertEqual(self.pp.folder_name, test.SHOWNAME)
+        self.assertEqual(self.pp.folder_name, test.SHOW_NAME)
 
 class PPBasicTests(test.SickbeardTestDBCase):
 
     def test_process(self):
         show = TVShow(1,3)
-        show.name = test.SHOWNAME
-        show.location = test.SHOWDIR
+        show.name = test.SHOW_NAME
+        show.location = test.SHOW_DIR
         show.saveToDB()
 
         sickbeard.showList = [show]
@@ -59,7 +59,7 @@ class PPBasicTests(test.SickbeardTestDBCase):
         addNameToCache('show name', 3)
         sickbeard.PROCESS_METHOD = 'move'
 
-        pp = PostProcessor(test.FILEPATH)
+        pp = PostProcessor(test.FILE_PATH)
         self.assertTrue(pp.process())
 
 
diff --git a/tests/search_tests.py b/tests/search_tests.py
old mode 100755
new mode 100644
diff --git a/tests/sickrage_tests/__init__.py b/tests/sickrage_tests/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..bc77c49432b6bb0bf10b6649c7b7c87e3bbbdb09
--- /dev/null
+++ b/tests/sickrage_tests/__init__.py
@@ -0,0 +1,41 @@
+# This file is part of SickRage.
+#
+# URL: https://sickrage.github.io
+# Git: https://github.com/SickRage/SickRage.git
+#
+# SickRage is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# SickRage is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
+
+from helper.quality_tests import QualityTests
+from show.coming_episodes_tests import ComingEpisodesTests
+from show.history_tests import HistoryTests
+from show.show_tests import ShowTests
+from system.restart_tests import RestartTests
+from system.shutdown_tests import ShutdownTests
+from unittest import TestLoader, TextTestRunner
+
+if __name__ == '__main__':
+    print('=====> Running all test in "sickrage_tests" <=====')
+
+    test_classes = [
+        ComingEpisodesTests,
+        HistoryTests,
+        QualityTests,
+        RestartTests,
+        ShowTests,
+        ShutdownTests,
+    ]
+
+    for test_class in test_classes:
+        suite = TestLoader().loadTestsFromTestCase(test_class)
+        TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/sickrage_tests/helper/__init__.py b/tests/sickrage_tests/helper/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/sickrage_tests/helper/quality_tests.py b/tests/sickrage_tests/helper/quality_tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..fcb7ff3ca209e478862c4679607df12dd54e0f19
--- /dev/null
+++ b/tests/sickrage_tests/helper/quality_tests.py
@@ -0,0 +1,60 @@
+# This file is part of SickRage.
+#
+# URL: https://sickrage.github.io
+# Git: https://github.com/SickRage/SickRage.git
+#
+# SickRage is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# SickRage is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import sys
+
+sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../../lib')))
+sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../..')))
+
+from sickbeard.common import ANY, HD, HD1080p, HD720p, Quality, SD
+from sickrage.helper.quality import get_quality_string
+from unittest import TestCase, TestLoader, TextTestRunner
+
+
+class QualityTests(TestCase):
+    def test_get_quality_string(self):
+        tests = {
+            ANY: 'Any',
+            HD: 'HD',
+            HD720p: 'HD720p',
+            HD1080p: 'HD1080p',
+            Quality.FULLHDBLURAY: '1080p BluRay',
+            Quality.FULLHDTV: '1080p HDTV',
+            Quality.FULLHDWEBDL: '1080p WEB-DL',
+            Quality.HDBLURAY: '720p BluRay',
+            Quality.HDTV: '720p HDTV',
+            Quality.HDWEBDL: '720p WEB-DL',
+            Quality.NONE: 'N/A',
+            Quality.RAWHDTV: 'RawHD',
+            Quality.SDDVD: 'SD DVD',
+            Quality.SDTV: 'SDTV',
+            Quality.UNKNOWN: 'Unknown',
+            SD: 'SD',
+            1000000: 'Custom',  # An invalid quality number to test the default case
+        }
+
+        for (quality, result) in tests.iteritems():
+            self.assertEqual(get_quality_string(quality), result)
+
+
+if __name__ == '__main__':
+    print('=====> Testing %s' % __file__)
+
+    suite = TestLoader().loadTestsFromTestCase(QualityTests)
+    TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/sickrage_tests/show/__init__.py b/tests/sickrage_tests/show/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/sickrage_tests/show/coming_episodes_tests.py b/tests/sickrage_tests/show/coming_episodes_tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..0bcf26f7fd68710d8a7a33506a972eebbb87b1c9
--- /dev/null
+++ b/tests/sickrage_tests/show/coming_episodes_tests.py
@@ -0,0 +1,91 @@
+# This file is part of SickRage.
+#
+# URL: https://sickrage.github.io
+# Git: https://github.com/SickRage/SickRage.git
+#
+# SickRage is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# SickRage is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import sys
+
+sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../../lib')))
+sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../..')))
+
+from sickrage.show.ComingEpisodes import ComingEpisodes
+from unittest import TestCase, TestLoader, TextTestRunner
+
+
+class ComingEpisodesTests(TestCase):
+    def test_get_categories(self):
+        categories_list = [
+            None, [], ['A', 'B'], [u'A', u'B'], '', 'A|B', u'A|B',
+        ]
+        results_list = [
+            [], [], ['A', 'B'], [u'A', u'B'], [], ['A', 'B'], ['A', 'B']
+        ]
+
+        self.assertEqual(
+                len(categories_list), len(results_list),
+                'Number of parameters (%d) and results (%d) does not match' % (len(categories_list), len(results_list))
+        )
+
+        for (index, categories) in enumerate(categories_list):
+            self.assertEqual(ComingEpisodes._get_categories(categories), results_list[index])
+
+    def test_get_categories_map(self):
+        categories_list = [
+            None, [], ['A', 'B'], [u'A', u'B']
+        ]
+        results_list = [
+            {}, {}, {'A': [], 'B': []}, {u'A': [], u'B': []}
+        ]
+
+        self.assertEqual(
+                len(categories_list), len(results_list),
+                'Number of parameters (%d) and results (%d) does not match' % (len(categories_list), len(results_list))
+        )
+
+        for (index, categories) in enumerate(categories_list):
+            self.assertEqual(ComingEpisodes._get_categories_map(categories), results_list[index])
+
+    def test_get_sort(self):
+        tests = {
+            None: 'date',
+            '': 'date',
+            u'': 'date',
+            'wrong': 'date',
+            u'wrong': 'date',
+            'date': 'date',
+            u'date': 'date',
+            'Date': 'date',
+            u'Date': 'date',
+            'network': 'network',
+            u'network': 'network',
+            'NetWork': 'network',
+            u'NetWork': 'network',
+            'show': 'show',
+            u'show': 'show',
+            'Show': 'show',
+            u'Show': 'show',
+        }
+
+        for (sort, result) in tests.iteritems():
+            self.assertEqual(ComingEpisodes._get_sort(sort), result)
+
+
+if __name__ == '__main__':
+    print('=====> Testing %s' % __file__)
+
+    suite = TestLoader().loadTestsFromTestCase(ComingEpisodesTests)
+    TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/sickrage_tests/show/history_tests.py b/tests/sickrage_tests/show/history_tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..0b8c28738f80477e7e267d98b153fe8a4a0e9045
--- /dev/null
+++ b/tests/sickrage_tests/show/history_tests.py
@@ -0,0 +1,80 @@
+# This file is part of SickRage.
+#
+# URL: https://sickrage.github.io
+# Git: https://github.com/SickRage/SickRage.git
+#
+# SickRage is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# SickRage is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import sys
+
+sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../../lib')))
+sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../..')))
+
+from sickbeard.common import Quality
+from sickrage.show.History import History
+from unittest import TestCase, TestLoader, TextTestRunner
+
+
+class HistoryTests(TestCase):
+    def test_get_actions(self):
+        tests = {
+            None: [],
+            '': [],
+            u'': [],
+            'wrong': [],
+            u'wrong': [],
+            'downloaded': Quality.DOWNLOADED,
+            u'downloaded': Quality.DOWNLOADED,
+            'Downloaded': Quality.DOWNLOADED,
+            u'Downloaded': Quality.DOWNLOADED,
+            'snatched': Quality.SNATCHED,
+            u'snatched': Quality.SNATCHED,
+            'Snatched': Quality.SNATCHED,
+            u'Snatched': Quality.SNATCHED,
+        }
+
+        for (action, result) in tests.iteritems():
+            self.assertEqual(History._get_actions(action), result)
+
+    def test_get_limit(self):
+        tests = {
+            None: 0,
+            '': 0,
+            u'': 0,
+            '0': 0,
+            u'0': 0,
+            '5': 5,
+            u'5': 5,
+            '-5': 0,
+            u'-5': 0,
+            '1.5': 0,
+            u'1.5': 0,
+            '-1.5': 0,
+            u'-1.5': 0,
+            5: 5,
+            -5: 0,
+            1.5: 1,
+            -1.5: 0,
+        }
+
+        for (action, result) in tests.iteritems():
+            self.assertEqual(History._get_limit(action), result)
+
+
+if __name__ == '__main__':
+    print('=====> Testing %s' % __file__)
+
+    suite = TestLoader().loadTestsFromTestCase(HistoryTests)
+    TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/sickrage_tests/show/show_tests.py b/tests/sickrage_tests/show/show_tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..1e6044a915e752e2582808fcf7477ed84cbbf6bb
--- /dev/null
+++ b/tests/sickrage_tests/show/show_tests.py
@@ -0,0 +1,83 @@
+# This file is part of SickRage.
+#
+# URL: https://sickrage.github.io
+# Git: https://github.com/SickRage/SickRage.git
+#
+# SickRage is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# SickRage is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import sys
+
+sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../../lib')))
+sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../..')))
+
+import sickbeard
+
+from sickbeard.common import Quality
+from sickbeard.tv import TVShow
+from sickrage.show.Show import Show
+from unittest import TestCase, TestLoader, TextTestRunner
+
+
+class ShowTests(TestCase):
+    def test_validate_indexer_id(self):
+        sickbeard.QUALITY_DEFAULT = Quality.FULLHDTV
+
+        show123 = TestTVShow(0, 123)
+        show456 = TestTVShow(0, 456)
+        show789 = TestTVShow(0, 789)
+        sickbeard.showList = [
+            show123,
+            show456,
+            show789,
+        ]
+
+        invalid_show_id = ('Invalid show ID', None)
+
+        indexer_id_list = [
+            None, '', u'', '123', u'123', '456', u'456', '789', u'789', 123, 456, 789, ['123', '456'], [u'123', u'456'],
+            [123, 456]
+        ]
+        results_list = [
+            invalid_show_id, invalid_show_id, invalid_show_id, (None, show123), (None, show123), (None, show456),
+            (None, show456), (None, show789), (None, show789), (None, show123), (None, show456), (None, show789),
+            invalid_show_id, invalid_show_id, invalid_show_id
+        ]
+
+        self.assertEqual(
+                len(indexer_id_list), len(results_list),
+                'Number of parameters (%d) and results (%d) does not match' % (len(indexer_id_list), len(results_list))
+        )
+
+        for (index, indexer_id) in enumerate(indexer_id_list):
+            self.assertEqual(Show._validate_indexer_id(indexer_id), results_list[index])
+
+
+class TestTVShow(TVShow):
+    """
+    A test ``TVShow`` object that do not need DB access.
+    """
+
+    def __init__(self, indexer, indexer_id):
+        super(TestTVShow, self).__init__(indexer, indexer_id)
+
+    def loadFromDB(self, skip_nfo=False):
+        pass
+
+
+if __name__ == '__main__':
+    print('=====> Testing %s' % __file__)
+
+    suite = TestLoader().loadTestsFromTestCase(ShowTests)
+    TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/sickrage_tests/system/__init__.py b/tests/sickrage_tests/system/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/sickrage_tests/system/restart_tests.py b/tests/sickrage_tests/system/restart_tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..70da92d250b03c43a9b5a2c96bd716704656fcf0
--- /dev/null
+++ b/tests/sickrage_tests/system/restart_tests.py
@@ -0,0 +1,57 @@
+# This file is part of SickRage.
+#
+# URL: https://sickrage.github.io
+# Git: https://github.com/SickRage/SickRage.git
+#
+# SickRage is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# SickRage is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import sys
+
+sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../../lib')))
+sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../..')))
+
+import sickbeard
+
+from sickbeard.event_queue import Events
+from sickrage.system.Restart import Restart
+from unittest import TestCase, TestLoader, TextTestRunner
+
+
+class RestartTests(TestCase):
+    def test_restart(self):
+        sickbeard.PID = 123456
+        sickbeard.events = Events(None)
+
+        tests = {
+            0: False,
+            '0': False,
+            u'0': False,
+            123: False,
+            '123': False,
+            u'123': False,
+            123456: True,
+            '123456': True,
+            u'123456': True,
+        }
+
+        for (pid, result) in tests.iteritems():
+            self.assertEqual(Restart.restart(pid), result)
+
+
+if __name__ == '__main__':
+    print('=====> Testing %s' % __file__)
+
+    suite = TestLoader().loadTestsFromTestCase(RestartTests)
+    TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/sickrage_tests/system/shutdown_tests.py b/tests/sickrage_tests/system/shutdown_tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..6c84b64cc17bff0b0b800406ee2c27c20e4d9002
--- /dev/null
+++ b/tests/sickrage_tests/system/shutdown_tests.py
@@ -0,0 +1,57 @@
+# This file is part of SickRage.
+#
+# URL: https://sickrage.github.io
+# Git: https://github.com/SickRage/SickRage.git
+#
+# SickRage is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# SickRage is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import sys
+
+sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../../lib')))
+sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../..')))
+
+import sickbeard
+
+from sickbeard.event_queue import Events
+from sickrage.system.Shutdown import Shutdown
+from unittest import TestCase, TestLoader, TextTestRunner
+
+
+class ShutdownTests(TestCase):
+    def test_shutdown(self):
+        sickbeard.PID = 123456
+        sickbeard.events = Events(None)
+
+        tests = {
+            0: False,
+            '0': False,
+            u'0': False,
+            123: False,
+            '123': False,
+            u'123': False,
+            123456: True,
+            '123456': True,
+            u'123456': True,
+        }
+
+        for (pid, result) in tests.iteritems():
+            self.assertEqual(Shutdown.stop(pid), result)
+
+
+if __name__ == '__main__':
+    print('=====> Testing %s' % __file__)
+
+    suite = TestLoader().loadTestsFromTestCase(ShutdownTests)
+    TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/test_lib.py b/tests/test_lib.py
index dd28942dcd379a2b6764f430f53b0e7b0df80845..86a12da9d5751125cad468a801835afdb8f801c7 100644
--- a/tests/test_lib.py
+++ b/tests/test_lib.py
@@ -1,5 +1,6 @@
 # coding=UTF-8
 # Author: Dennis Lutter <lad1337@gmail.com>
+
 # URL: http://code.google.com/p/sickbeard/
 #
 # This file is part of SickRage.
@@ -17,58 +18,93 @@
 # You should have received a copy of the GNU General Public License
 # along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
 
+# pylint: disable=C0301
+# Line too long
+
+"""
+Create a test database for testing.
+
+Methods:
+    create_test_log_folder
+    create_test_cache_folder
+    setup_test_db
+    teardown_test_db
+    setup_test_episode_file
+    teardown_test_episode_file
+    setup_test_show_dir
+    teardown_test_show_dir
+
+Classes:
+    SickbeardTestDBCase
+    TestDBConnection
+    TestCacheDBConnection
+"""
+
+import sys
+import os.path
+import unittest
+import shutil
 
-import sys, os.path
 sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '../lib')))
 sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
 
-import unittest
+# pylint: disable=F0401
+# Unable to import
+import shutil_custom
 
-from configobj import ConfigObj
 import sickbeard
-
 from sickbeard import db, providers
 from sickbeard.databases import mainDB
 from sickbeard.databases import cache_db, failed_db
 from sickbeard.tv import TVEpisode
 
-import shutil
-import shutil_custom
+# pylint: disable=F0401
+# Unable to import
+from configobj import ConfigObj
+
 
 shutil.copyfile = shutil_custom.copyfile_custom
 
-#=================
-# test globals
-#=================
-TESTDIR = os.path.abspath(os.path.dirname(__file__))
-TESTDBNAME = "sickbeard.db"
-TESTCACHEDBNAME = "cache.db"
-TESTFAILEDDBNAME = "failed.db"
+# =================
+#  test globals
+# =================
+TEST_DIR = os.path.abspath(os.path.dirname(__file__))
+TEST_DB_NAME = "sickbeard.db"
+TEST_CACHE_DB_NAME = "cache.db"
+TEST_FAILED_DB_NAME = "failed.db"
 
-SHOWNAME = u"show name"
+SHOW_NAME = u"show name"
 SEASON = 4
 EPISODE = 2
 FILENAME = u"show name - s0" + str(SEASON) + "e0" + str(EPISODE) + ".mkv"
-FILEDIR = os.path.join(TESTDIR, SHOWNAME)
-FILEPATH = os.path.join(FILEDIR, FILENAME)
-SHOWDIR = os.path.join(TESTDIR, SHOWNAME + " final")
-
-#=================
-# prepare env functions
-#=================
-def createTestLogFolder():
+FILE_DIR = os.path.join(TEST_DIR, SHOW_NAME)
+FILE_PATH = os.path.join(FILE_DIR, FILENAME)
+SHOW_DIR = os.path.join(TEST_DIR, SHOW_NAME + " final")
+
+
+# =================
+#  prepare env functions
+# =================
+def create_test_log_folder():
+    """
+    Create a log folder for test logs.
+    """
     if not os.path.isdir(sickbeard.LOG_DIR):
         os.mkdir(sickbeard.LOG_DIR)
 
-def createTestCacheFolder():
+
+def create_test_cache_folder():
+    """
+    Create a cache folder for caching tests.
+    """
     if not os.path.isdir(sickbeard.CACHE_DIR):
         os.mkdir(sickbeard.CACHE_DIR)
 
-# call env functions at appropriate time during sickbeard var setup
+# call env functions at appropriate time during SickBeard var setup
 
-#=================
-# sickbeard globals
-#=================
+# =================
+#  SickBeard globals
+# =================
 sickbeard.SYS_ENCODING = 'UTF-8'
 
 sickbeard.showList = []
@@ -85,168 +121,226 @@ sickbeard.PROVIDER_ORDER = ["sick_beard_index"]
 sickbeard.newznabProviderList = providers.getNewznabProviderList("'Sick Beard Index|http://lolo.sickbeard.com/|0|5030,5040|0|eponly|0|0|0!!!NZBs.org|https://nzbs.org/||5030,5040,5060,5070,5090|0|eponly|0|0|0!!!Usenet-Crawler|https://www.usenet-crawler.com/||5030,5040,5060|0|eponly|0|0|0'")
 sickbeard.providerList = providers.makeProviderList()
 
-sickbeard.PROG_DIR = os.path.abspath(os.path.join(TESTDIR, '..'))
-sickbeard.DATA_DIR = TESTDIR
+sickbeard.PROG_DIR = os.path.abspath(os.path.join(TEST_DIR, '..'))
+sickbeard.DATA_DIR = TEST_DIR
 sickbeard.CONFIG_FILE = os.path.join(sickbeard.DATA_DIR, "config.ini")
 sickbeard.CFG = ConfigObj(sickbeard.CONFIG_FILE)
 
-sickbeard.BRANCG = sickbeard.config.check_setting_str(sickbeard.CFG, 'General', 'branch', '')
+sickbeard.BRANCH = sickbeard.config.check_setting_str(sickbeard.CFG, 'General', 'branch', '')
 sickbeard.CUR_COMMIT_HASH = sickbeard.config.check_setting_str(sickbeard.CFG, 'General', 'cur_commit_hash', '')
 sickbeard.GIT_USERNAME = sickbeard.config.check_setting_str(sickbeard.CFG, 'General', 'git_username', '')
 sickbeard.GIT_PASSWORD = sickbeard.config.check_setting_str(sickbeard.CFG, 'General', 'git_password', '', censor_log=True)
 
-sickbeard.LOG_DIR = os.path.join(TESTDIR, 'Logs')
+sickbeard.LOG_DIR = os.path.join(TEST_DIR, 'Logs')
 sickbeard.logger.logFile = os.path.join(sickbeard.LOG_DIR, 'test_sickbeard.log')
-createTestLogFolder()
+create_test_log_folder()
 
-sickbeard.CACHE_DIR = os.path.join(TESTDIR, 'cache')
-createTestCacheFolder()
+sickbeard.CACHE_DIR = os.path.join(TEST_DIR, 'cache')
+create_test_cache_folder()
 
+# pylint: disable=E1103
+# Has no member
 sickbeard.logger.initLogging(False, True)
 
-#=================
-# dummy functions
-#=================
-def _dummy_saveConfig():
+
+# =================
+#  dummy functions
+# =================
+def _dummy_save_config():
+    """
+    Override the SickBeard save_config which gets called during a db upgrade.
+
+    :return: True
+    """
     return True
 
-# this overrides the sickbeard save_config which gets called during a db upgrade
+# this overrides the SickBeard save_config which gets called during a db upgrade
 # this might be considered a hack
-mainDB.sickbeard.save_config = _dummy_saveConfig
+mainDB.sickbeard.save_config = _dummy_save_config
 
 
-# the real one tries to contact tvdb just stop it from getting more info on the ep
-def _fake_specifyEP(self, season, episode):
+# pylint: disable=W0613
+# Unused arguments
+def _fake_specify_ep(self, season, episode):
+    """
+    Override contact to TVDB indexer.
+
+    :param self:
+    :param season: Season to search for  ...not used
+    :param episode: Episode to search for  ...not used
+    """
     pass
 
-TVEpisode.specifyEpisode = _fake_specifyEP
+# the real one tries to contact TVDB just stop it from getting more info on the ep
+TVEpisode.specifyEpisode = _fake_specify_ep
 
-#=================
-# test classes
-#=================
+
+# =================
+#  test classes
+# =================
 class SickbeardTestDBCase(unittest.TestCase):
+    """
+    Superclass for testing the database.
+
+    Methods:
+        setUp
+        tearDown
+    """
     def setUp(self):
         sickbeard.showList = []
-        setUp_test_db()
-        setUp_test_episode_file()
-        setUp_test_show_dir()
+        setup_test_db()
+        setup_test_episode_file()
+        setup_test_show_dir()
 
     def tearDown(self):
         sickbeard.showList = []
-        tearDown_test_db()
-        tearDown_test_episode_file()
-        tearDown_test_show_dir()
+        teardown_test_db()
+        teardown_test_episode_file()
+        teardown_test_show_dir()
+
 
 class TestDBConnection(db.DBConnection, object):
+    """
+    Test connecting to the database.
+    """
+    def __init__(self, db_file_name=TEST_DB_NAME):
+        db_file_name = os.path.join(TEST_DIR, db_file_name)
+        super(TestDBConnection, self).__init__(db_file_name)
 
-    def __init__(self, dbFileName=TESTDBNAME):
-        dbFileName = os.path.join(TESTDIR, dbFileName)
-        super(TestDBConnection, self).__init__(dbFileName)
 
 class TestCacheDBConnection(TestDBConnection, object):
-    def __init__(self, providerName):
-        db.DBConnection.__init__(self, os.path.join(TESTDIR, TESTCACHEDBNAME))
+    """
+    Test connecting to the cache database.
+    """
+    def __init__(self, provider_name):
+        # pylint: disable=W0233
+        # Init method from non-direct base class
+        db.DBConnection.__init__(self, os.path.join(TEST_DIR, TEST_CACHE_DB_NAME))
 
         # Create the table if it's not already there
         try:
-            if not self.hasTable(providerName):
-                sql = "CREATE TABLE [" + providerName + "] (name TEXT, season NUMERIC, episodes TEXT, indexerid NUMERIC, url TEXT, time NUMERIC, quality TEXT, release_group TEXT)"
+            if not self.hasTable(provider_name):
+                sql = "CREATE TABLE [" + provider_name + "] (name TEXT, season NUMERIC, episodes TEXT, indexerid NUMERIC, url TEXT, time NUMERIC, quality TEXT, release_group TEXT)"
                 self.connection.execute(sql)
                 self.connection.commit()
-        except Exception, e:
-            if str(e) != "table [" + providerName + "] already exists":
+        # pylint: disable=W0703
+        # Catching too general exception
+        except Exception as error:
+            if str(error) != "table [" + provider_name + "] already exists":
                 raise
 
             # add version column to table if missing
-            if not self.hasColumn(providerName, 'version'):
-                self.addColumn(providerName, 'version', "NUMERIC", "-1")
+            if not self.hasColumn(provider_name, 'version'):
+                self.addColumn(provider_name, 'version', "NUMERIC", "-1")
 
         # Create the table if it's not already there
         try:
             sql = "CREATE TABLE lastUpdate (provider TEXT, time NUMERIC);"
             self.connection.execute(sql)
             self.connection.commit()
-        except Exception, e:
-            if str(e) != "table lastUpdate already exists":
+        # pylint: disable=W0703
+        # Catching too general exception
+        except Exception as error:
+            if str(error) != "table lastUpdate already exists":
                 raise
 
 # this will override the normal db connection
 sickbeard.db.DBConnection = TestDBConnection
 sickbeard.tvcache.CacheDBConnection = TestCacheDBConnection
 
-#=================
-# test functions
-#=================
-def setUp_test_db():
-    """upgrades the db to the latest version
+
+# =================
+#  test functions
+# =================
+def setup_test_db():
+    """
+    Set up the test databases.
     """
+    # Upgrade the db to the latest version.
     # upgrading the db
     db.upgradeDatabase(db.DBConnection(), mainDB.InitialSchema)
 
     # fix up any db problems
     db.sanityCheckDatabase(db.DBConnection(), mainDB.MainSanityCheck)
 
-    # and for cachedb too
+    # and for cache.db too
     db.upgradeDatabase(db.DBConnection("cache.db"), cache_db.InitialSchema)
 
-    # and for faileddb too
+    # and for failed.db too
     db.upgradeDatabase(db.DBConnection("failed.db"), failed_db.InitialSchema)
 
 
-def tearDown_test_db():
+def teardown_test_db():
+    """
+    Tear down the test database.
+    """
     from sickbeard.db import db_cons
     for connection in db_cons:
         db_cons[connection].commit()
-#        db_cons[connection].close()
-
-#    for current_db in [ TESTDBNAME, TESTCACHEDBNAME, TESTFAILEDDBNAME ]:
-#        file_name = os.path.join(TESTDIR, current_db)
-#        if os.path.exists(file_name):
-#            try:
-#                os.remove(file_name)
-#            except Exception as e:
-#                print 'ERROR: Failed to remove ' + file_name
-#                print exception(e)
-
-
-def setUp_test_episode_file():
-    if not os.path.exists(FILEDIR):
-        os.makedirs(FILEDIR)
+    #     db_cons[connection].close()
+    #
+    # for current_db in [ TEST_DB_NAME, TEST_CACHE_DB_NAME, TEST_FAILED_DB_NAME ]:
+    #    file_name = os.path.join(TEST_DIR, current_db)
+    #    if os.path.exists(file_name):
+    #        try:
+    #            os.remove(file_name)
+    #        except Exception as e:
+    #            print 'ERROR: Failed to remove ' + file_name
+    #            print exception(e)
+
+
+def setup_test_episode_file():
+    """
+    Create a test episode directory with a test episode in it.
+    """
+    if not os.path.exists(FILE_DIR):
+        os.makedirs(FILE_DIR)
 
     try:
-        with open(FILEPATH, 'wb') as f:
-            f.write("foo bar")
-            f.flush()
+        with open(FILE_PATH, 'wb') as ep_file:
+            ep_file.write("foo bar")
+            ep_file.flush()
+    # pylint: disable=W0703
+    # Catching too general exception
     except Exception:
         print "Unable to set up test episode"
         raise
 
 
-def tearDown_test_episode_file():
-    if os.path.exists(FILEDIR):
-        shutil.rmtree(FILEDIR)
+def teardown_test_episode_file():
+    """
+    Remove the test episode.
+    """
+    if os.path.exists(FILE_DIR):
+        shutil.rmtree(FILE_DIR)
 
 
-def setUp_test_show_dir():
-    if not os.path.exists(SHOWDIR):
-        os.makedirs(SHOWDIR)
+def setup_test_show_dir():
+    """
+    Create a test show directory.
+    """
+    if not os.path.exists(SHOW_DIR):
+        os.makedirs(SHOW_DIR)
 
 
-def tearDown_test_show_dir():
-    if os.path.exists(SHOWDIR):
-        shutil.rmtree(SHOWDIR)
+def teardown_test_show_dir():
+    """
+    Remove the test show.
+    """
+    if os.path.exists(SHOW_DIR):
+        shutil.rmtree(SHOW_DIR)
 
 
 if __name__ == '__main__':
     print "=================="
-    print "Dont call this directly"
+    print "Don't call this directly"
     print "=================="
     print "you might want to call"
 
-    dirList = os.listdir(TESTDIR)
-    for fname in dirList:
-        if (fname.find("_test") > 0) and (fname.find("pyc") < 0):
-            print "- " + fname
+    DIR_LIST = os.listdir(TEST_DIR)
+    for filename in DIR_LIST:
+        if (filename.find("_test") > 0) and (filename.find("pyc") < 0):
+            print "- " + filename
 
     print "=================="
     print "or just call all_tests.py"