diff --git a/.build/Gruntfile.js b/.build/Gruntfile.js
index 8cb9dbda06574ae45961239741bc1daa60b2731e..ec48f3d4ceee0ad6bf425aeabd20ae85d2c4aea6 100644
--- a/.build/Gruntfile.js
+++ b/.build/Gruntfile.js
@@ -1,9 +1,22 @@
 module.exports = function(grunt) {
+    require('load-grunt-tasks')(grunt);
+
     grunt.initConfig({
-        bower_concat: {
+        clean: {
+            dist: './dist/',
+            bower_components: './bower_components' // jshint ignore:line
+        },
+        bower: {
+            install: {
+                options: {
+                    copy: false
+                }
+            }
+        },
+        bower_concat: { // jshint ignore:line
             all: {
-                dest: './_bower.js',
-                cssDest: './_bower.css',
+                dest: './dist/bower.js',
+                cssDest: './dist/bower.css',
                 exclude: [
                 ],
                 dependencies: {
@@ -32,22 +45,40 @@ module.exports = function(grunt) {
             }
         },
         uglify: {
-            my_target: {
+            bower: {
+                files: {
+                    '../gui/slick/js/vender.min.js': ['./dist/bower.js']
+                }
+            },
+            core: {
                 files: {
-                    '../gui/slick/js/vender.min.js': ['./_bower.js'],
                     '../gui/slick/js/core.min.js': ['../gui/slick/js/core.js']
                 }
             }
         },
+        sass: {
+            options: {
+                sourceMap: true
+            },
+            core: {
+                files: {
+                    './dist/core.css': ['../gui/slick/scss/core.scss']
+                }
+            }
+        },
         cssmin: {
             options: {
                 shorthandCompacting: false,
                 roundingPrecision: -1
             },
-            target: {
+            bower: {
+                files: {
+                    '../gui/slick/css/vender.min.css': ['./dist/bower.css']
+                }
+            },
+            core: {
                 files: {
-                    '../gui/slick/css/vender.min.css': ['./_bower.css'],
-                    // '../gui/slick/css/core.min.css': ['./gui/slick/css/core.css']
+                    '../gui/slick/css/core.min.css': ['./dist/core.css']
                 }
             }
         },
@@ -61,20 +92,37 @@ module.exports = function(grunt) {
                 '!../gui/slick/js/ajaxNotifications.js',
                 '!../gui/slick/js/**/*.min.js', // We use this because ignores doesn't seem to work :(
             ]
+        },
+        mocha: {
+            all: {
+                src: ['tests/testrunner.html'],
+            },
+            options: {
+                run: true
+            }
         }
     });
 
+    grunt.loadNpmTasks('grunt-contrib-clean');
+    grunt.loadNpmTasks('grunt-bower-task');
     grunt.loadNpmTasks('grunt-bower-concat');
     grunt.loadNpmTasks('grunt-contrib-uglify');
     grunt.loadNpmTasks('grunt-contrib-cssmin');
     grunt.loadNpmTasks('grunt-contrib-jshint');
+    grunt.loadNpmTasks('grunt-mocha');
 
     grunt.registerTask('default', [
+        'clean',
+        'bower',
         'bower_concat',
         'uglify',
-        'cssmin']
-    );
+        'sass',
+        'cssmin',
+        'jshint',
+        'mocha'
+    ]);
     grunt.registerTask('travis', [
-        'jshint'
+        'jshint',
+        'mocha'
     ]);
 };
diff --git a/.build/package.json b/.build/package.json
index 49d0816d9d36d12b27ea676cde6a9e567af51101..502f6995823ce8ab760914ea2cfe868ae1061b54 100644
--- a/.build/package.json
+++ b/.build/package.json
@@ -11,15 +11,24 @@
   },
   "homepage": "https://github.com/SickRage/SickRage#readme",
   "scripts": {
-    "test": "snyk test"
+    "test": "grunt travis",
+    "security": "snyk test"
   },
-  "dependencies": {
-    "snyk": "^1.3.1",
+  "devDependencies": {
+    "chai": "^3.4.1",
     "grunt": "^0.4.5",
-    "grunt-bower-concat": "^0.5.0",
+    "grunt-bower-concat": "^0.6.0",
+    "grunt-bower-task": "^0.4.0",
     "grunt-cli": "^0.1.13",
+    "grunt-contrib-clean": "^0.7.0",
     "grunt-contrib-cssmin": "^0.14.0",
     "grunt-contrib-jshint": "^0.11.3",
-    "grunt-contrib-uglify": "^0.9.2"
+    "grunt-contrib-sass": "^0.9.2",
+    "grunt-contrib-uglify": "^0.11.0",
+    "grunt-mocha": "^0.4.15",
+    "grunt-sass": "^1.1.0",
+    "load-grunt-tasks": "^3.3.0",
+    "mocha": "^2.3.4",
+    "snyk": "^1.3.1"
   }
 }
diff --git a/.build/test/testrunner.html b/.build/test/testrunner.html
new file mode 100644
index 0000000000000000000000000000000000000000..008398e6db10f4d2db4aff6063e5cc0166b70fd8
--- /dev/null
+++ b/.build/test/testrunner.html
@@ -0,0 +1,34 @@
+<html>
+    <head>
+        <meta charset="utf-8">
+        <title>Mocha Tests</title>
+        <link rel="stylesheet" href="../node_modules/mocha/mocha.css" />
+        <meta data-var="FAKE_TRUE_BOOLEAN" data-content="TRUE">
+        <meta data-var="FAKE_TRUE_NUMBER" data-content="1">
+        <meta data-var="FAKE_FALSE_BOOLEAN" data-content="FALSE">
+        <meta data-var="FAKE_FALSE_NUMBER" data-content="0">
+        <meta data-var="FAKE_CONTENT" data-content="RANDOM_CONTENT">
+    </head>
+    <body>
+        <div id="mocha"></div>
+        <script src="../node_modules/mocha/mocha.js"></script>
+        <script src="../node_modules/chai/chai.js"></script>
+        <script src="../../gui/slick/js/vender.min.js"></script>
+        <script src="../../gui/slick/js/core.js"></script>
+
+        <script>
+            mocha.setup('bdd');
+            mocha.reporter('html');
+        </script>
+
+        <!-- Tests -->
+        <script src="tests.js"></script>
+
+        <script>
+            // Only tests run in real browser, injected script run if options.run == true
+            if (navigator.userAgent.indexOf('PhantomJS') < 0) {
+                mocha.run();
+            }
+        </script>
+    </body>
+</html>
diff --git a/.build/test/tests.js b/.build/test/tests.js
new file mode 100644
index 0000000000000000000000000000000000000000..f2348856946edf92b88de9c3b8f103dcc9eb9e99
--- /dev/null
+++ b/.build/test/tests.js
@@ -0,0 +1,26 @@
+describe('metaToBool', function(){
+    it('should return true', function(){
+        chai.assert.equal(true, metaToBool('FAKE_TRUE_BOOLEAN'));
+        chai.assert.equal(true, metaToBool('FAKE_TRUE_NUMBER'));
+    });
+    it('should return false', function(){
+        chai.assert.equal(false, metaToBool('FAKE_FALSE_BOOLEAN'));
+        chai.assert.equal(false, metaToBool('FAKE_FALSE_NUMBER'));
+    });
+    it('should return undefined', function(){
+        chai.assert.equal(undefined, metaToBool('FAKE_UNDEFINED'));
+    });
+});
+describe('getMeta', function(){
+    it('should return meta variable', function(){
+        chai.assert.equal('RANDOM_CONTENT', getMeta('FAKE_CONTENT'));
+    });
+});
+describe('isMeta', function(){
+    it('should return true if any string in array is matched', function(){
+        chai.assert.equal(true, isMeta('FAKE_CONTENT', ['RANDOM_CONTENT']));
+    });
+    it('should return false if no strings in the array match', function(){
+        chai.assert.equal(false, isMeta('FAKE_CONTENT', ['a', 'b', 'c']));
+    });
+});
diff --git a/.gitignore b/.gitignore
index 77f444fa1137a7e4d2e8f273a4bb16be48468dfd..e92638ab65dc7010c470037c90fe0d103bf19123 100644
--- a/.gitignore
+++ b/.gitignore
@@ -60,8 +60,8 @@ Thumbs.db
 ######################
 lib/unrar2/UnRAR.exe
 
-# Bower #
+# Grunt #
 ######################
 .build/bower_components
 .build/node_modules
-.build/_bower*
+.build/dist
diff --git a/.jshintrc b/.jshintrc
index 1c554638c5d225598b91c26d17b6ef6ef205f8e3..189830acc77e451853115739a34afeef0b15636a 100644
--- a/.jshintrc
+++ b/.jshintrc
@@ -12,6 +12,7 @@
     "latedef": true,
     "maxerr": 100,
     "maxlen": 1000,
+    "mocha": true,
     "noarg": true,
     "noempty": true,
     "nonbsp": true,
@@ -19,6 +20,8 @@
     "unused": true,
     "node": true,
     "predef": {
+        "chai": true,
+        "mocha": true,
         "srRoot": true,
         "themeSpinner": true,
         "metaToBool": true,
diff --git a/SickBeard.py b/SickBeard.py
index 99816b58514eb500ac86e6e5e3ebb27ae934dace..21898a2eb23c4d7dbf15123b013f5f8d524a6c2d 100755
--- a/SickBeard.py
+++ b/SickBeard.py
@@ -70,6 +70,8 @@ from sickbeard.webserveInit import SRWebServer
 from sickbeard.event_queue import Events
 from configobj import ConfigObj
 
+from sickrage.helper.encoding import ek
+
 # http://bugs.python.org/issue7980#msg221094
 throwaway = datetime.datetime.strptime('20110101', '%Y%m%d')
 
@@ -138,9 +140,9 @@ class SickRage(object):
     # Too many statements
     def start(self):
         # do some preliminary stuff
-        sickbeard.MY_FULLNAME = os.path.normpath(os.path.abspath(__file__))
-        sickbeard.MY_NAME = os.path.basename(sickbeard.MY_FULLNAME)
-        sickbeard.PROG_DIR = os.path.dirname(sickbeard.MY_FULLNAME)
+        sickbeard.MY_FULLNAME = ek(os.path.normpath, ek(os.path.abspath, __file__))
+        sickbeard.MY_NAME = ek(os.path.basename, sickbeard.MY_FULLNAME)
+        sickbeard.PROG_DIR = ek(os.path.dirname, sickbeard.MY_FULLNAME)
         sickbeard.DATA_DIR = sickbeard.PROG_DIR
         sickbeard.MY_ARGS = sys.argv[1:]
 
@@ -219,16 +221,16 @@ class SickRage(object):
                 self.PIDFILE = str(a)
 
                 # If the pidfile already exists, sickbeard may still be running, so exit
-                if os.path.exists(self.PIDFILE):
+                if ek(os.path.exists, self.PIDFILE):
                     sys.exit("PID file: " + self.PIDFILE + " already exists. Exiting.")
 
             # Specify folder to load the config file from
             if o in ('--config',):
-                sickbeard.CONFIG_FILE = os.path.abspath(a)
+                sickbeard.CONFIG_FILE = ek(os.path.abspath, a)
 
             # Specify folder to use as the data dir
             if o in ('--datadir',):
-                sickbeard.DATA_DIR = os.path.abspath(a)
+                sickbeard.DATA_DIR = ek(os.path.abspath, a)
 
             # Prevent resizing of the banner/posters even if PIL is installed
             if o in ('--noresize',):
@@ -237,10 +239,10 @@ class SickRage(object):
         # The pidfile is only useful in daemon mode, make sure we can write the file properly
         if self.CREATEPID:
             if self.runAsDaemon:
-                pid_dir = os.path.dirname(self.PIDFILE)
-                if not os.access(pid_dir, os.F_OK):
+                pid_dir = ek(os.path.dirname, self.PIDFILE)
+                if not ek(os.access, pid_dir, os.F_OK):
                     sys.exit("PID dir: " + pid_dir + " doesn't exist. Exiting.")
-                if not os.access(pid_dir, os.W_OK):
+                if not ek(os.access, pid_dir, os.W_OK):
                     sys.exit("PID dir: " + pid_dir + " must be writable (write permissions). Exiting.")
 
             else:
@@ -251,38 +253,38 @@ class SickRage(object):
 
         # If they don't specify a config file then put it in the data dir
         if not sickbeard.CONFIG_FILE:
-            sickbeard.CONFIG_FILE = os.path.join(sickbeard.DATA_DIR, "config.ini")
+            sickbeard.CONFIG_FILE = ek(os.path.join, sickbeard.DATA_DIR, "config.ini")
 
         # Make sure that we can create the data dir
-        if not os.access(sickbeard.DATA_DIR, os.F_OK):
+        if not ek(os.access, sickbeard.DATA_DIR, os.F_OK):
             try:
-                os.makedirs(sickbeard.DATA_DIR, 0744)
+                ek(os.makedirs, sickbeard.DATA_DIR, 0744)
             except os.error:
                 raise SystemExit("Unable to create datadir '" + sickbeard.DATA_DIR + "'")
 
         # Make sure we can write to the data dir
-        if not os.access(sickbeard.DATA_DIR, os.W_OK):
+        if not ek(os.access, sickbeard.DATA_DIR, os.W_OK):
             raise SystemExit("Datadir must be writeable '" + sickbeard.DATA_DIR + "'")
 
         # Make sure we can write to the config file
-        if not os.access(sickbeard.CONFIG_FILE, os.W_OK):
-            if os.path.isfile(sickbeard.CONFIG_FILE):
+        if not ek(os.access, sickbeard.CONFIG_FILE, os.W_OK):
+            if ek(os.path.isfile, sickbeard.CONFIG_FILE):
                 raise SystemExit("Config file '" + sickbeard.CONFIG_FILE + "' must be writeable.")
-            elif not os.access(os.path.dirname(sickbeard.CONFIG_FILE), os.W_OK):
+            elif not ek(os.access, ek(os.path.dirname, sickbeard.CONFIG_FILE), os.W_OK):
                 raise SystemExit(
-                    "Config file root dir '" + os.path.dirname(sickbeard.CONFIG_FILE) + "' must be writeable.")
+                    "Config file root dir '" + ek(os.path.dirname, sickbeard.CONFIG_FILE) + "' must be writeable.")
 
-        os.chdir(sickbeard.DATA_DIR)
+        ek(os.chdir, sickbeard.DATA_DIR)
 
         # Check if we need to perform a restore first
-        restoreDir = os.path.join(sickbeard.DATA_DIR, 'restore')
-        if os.path.exists(restoreDir):
+        restoreDir = ek(os.path.join, sickbeard.DATA_DIR, 'restore')
+        if ek(os.path.exists, restoreDir):
             success = self.restoreDB(restoreDir, sickbeard.DATA_DIR)
             if self.consoleLogging:
                 sys.stdout.write(u"Restore: restoring DB and config.ini %s!\n" % ("FAILED", "SUCCESSFUL")[success])
 
         # Load the config and publish it to the sickbeard package
-        if self.consoleLogging and not os.path.isfile(sickbeard.CONFIG_FILE):
+        if self.consoleLogging and not ek(os.path.isfile, sickbeard.CONFIG_FILE):
             sys.stdout.write(u"Unable to find '" + sickbeard.CONFIG_FILE + "' , all settings will be default!" + "\n")
 
         sickbeard.CFG = ConfigObj(sickbeard.CONFIG_FILE)
@@ -324,15 +326,15 @@ class SickRage(object):
         self.web_options = {
             'port': int(self.startPort),
             'host': self.webhost,
-            'data_root': os.path.join(sickbeard.PROG_DIR, 'gui', sickbeard.GUI_NAME),
+            'data_root': ek(os.path.join, sickbeard.PROG_DIR, 'gui', sickbeard.GUI_NAME),
             'web_root': sickbeard.WEB_ROOT,
             'log_dir': self.log_dir,
             'username': sickbeard.WEB_USERNAME,
             'password': sickbeard.WEB_PASSWORD,
             'enable_https': sickbeard.ENABLE_HTTPS,
             'handle_reverse_proxy': sickbeard.HANDLE_REVERSE_PROXY,
-            'https_cert': os.path.join(sickbeard.PROG_DIR, sickbeard.HTTPS_CERT),
-            'https_key': os.path.join(sickbeard.PROG_DIR, sickbeard.HTTPS_KEY),
+            'https_cert': ek(os.path.join, sickbeard.PROG_DIR, sickbeard.HTTPS_CERT),
+            'https_key': ek(os.path.join, sickbeard.PROG_DIR, sickbeard.HTTPS_KEY),
         }
 
         # start web server
@@ -344,12 +346,12 @@ class SickRage(object):
 
         # Clean up after update
         if sickbeard.GIT_NEWVER:
-            toclean = os.path.join(sickbeard.CACHE_DIR, 'mako')
-            for root, dirs, files in os.walk(toclean, topdown=False):
+            toclean = ek(os.path.join, sickbeard.CACHE_DIR, 'mako')
+            for root, dirs, files in ek(os.walk, toclean, topdown=False):
                 for name in files:
-                    os.remove(os.path.join(root, name))
+                    ek(os.remove, ek(os.path.join, root, name))
                 for name in dirs:
-                    os.rmdir(os.path.join(root, name))
+                    ek(os.rmdir, ek(os.path.join, root, name))
             sickbeard.GIT_NEWVER = False
 
         # Fire up all our threads
@@ -438,8 +440,8 @@ class SickRage(object):
     @staticmethod
     def remove_pid_file(PIDFILE):
         try:
-            if os.path.exists(PIDFILE):
-                os.remove(PIDFILE)
+            if ek(os.path.exists, PIDFILE):
+                ek(os.remove, PIDFILE)
         except (IOError, OSError):
             return False
 
@@ -473,10 +475,10 @@ class SickRage(object):
             filesList = ['sickbeard.db', 'config.ini', 'failed.db', 'cache.db']
 
             for filename in filesList:
-                srcFile = os.path.join(srcDir, filename)
-                dstFile = os.path.join(dstDir, filename)
-                bakFile = os.path.join(dstDir, '{0}.bak-{1}'.format(filename, datetime.datetime.now().strftime('%Y%m%d_%H%M%S')))
-                if os.path.isfile(dstFile):
+                srcFile = ek(os.path.join, srcDir, filename)
+                dstFile = ek(os.path.join, dstDir, filename)
+                bakFile = ek(os.path.join, dstDir, '{0}.bak-{1}'.format(filename, datetime.datetime.now().strftime('%Y%m%d_%H%M%S')))
+                if ek(os.path.isfile, dstFile):
                     shutil.move(dstFile, bakFile)
                 shutil.move(srcFile, dstFile)
             return True
diff --git a/gui/slick/css/core.min.css b/gui/slick/css/core.min.css
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/gui/slick/css/dark.css b/gui/slick/css/dark.css
index 2310d85bb4c384222613e33fc38b7484648cc4e2..c4964a0678e270ccc1546c9ba739e19043002702 100644
--- a/gui/slick/css/dark.css
+++ b/gui/slick/css/dark.css
@@ -265,13 +265,13 @@ home.mako
     color: #fff;
 }
 
+.loading-spinner {
+    background-image: url("../images/loading32-dark.gif");
+}
+
 .show-container {
-    margin: 12px;
-    width: 188px;
-    height: 352px;
     background-color: #333;
-    border: 1px solid #111;
-    border-radius: 6px;
+    border-color: #333;
 }
 
 .show-title:after {
diff --git a/gui/slick/css/lib/jquery-ui-1.10.4.custom.min.css b/gui/slick/css/lib/jquery-ui-1.10.4.custom.min.css
index b84acdccdb760465bcea11ba2666f20bd9fd79cd..6b7af1c07a4c736489c232aca3575ff12d09619a 100755
Binary files a/gui/slick/css/lib/jquery-ui-1.10.4.custom.min.css and b/gui/slick/css/lib/jquery-ui-1.10.4.custom.min.css differ
diff --git a/gui/slick/css/style.css b/gui/slick/css/style.css
index dd7d31b8f57060db1a9899d235fe0527e1ced587..733539b7f3403e08db5d85f0e926aab97eabd756 100644
--- a/gui/slick/css/style.css
+++ b/gui/slick/css/style.css
@@ -623,25 +623,43 @@ home.mako
     border-radius: 3px;
 }
 
+span.show-option {
+    white-space: nowrap;
+    margin-left: 10px;
+    line-height: 40px;
+}
+
+.show-grid {
+    display: none;
+}
+
 #container, #container-anime {
     margin: 0 auto;
 }
 
+.loading-spinner {
+    background-image: url("../images/loading32.gif");
+    width: 32px;
+    height: 32px;
+    display: inline-block;
+}
+
 .show-container {
-    margin: 12px;
-    width: 188px;
-    height: 352px;
-    background-color: #DFDACF;
-    border: 1px solid #111;
-    border-radius: 6px;
+    margin: 4px;
+    background-color: #F3F3F3;
+    border: 5px solid #F3F3F3;
+    overflow: hidden;
+    box-shadow: 1px 1px 3px 0px rgba(0, 0, 0, 0.31);
+}
+
+.show-details {
+    height: 32px;
+    overflow: hidden;
 }
 
 .show-image {
     overflow: hidden;
-    height: 273px;
-    width: 186px;
-    border-top-left-radius: 5px;
-    border-top-right-radius: 5px;
+    max-width: 100%;
 }
 
 .show-container .ui-progressbar {
@@ -701,11 +719,11 @@ home.mako
     height: 100%;
     top: 0;
     right: 0;
-    background-image: -webkit-linear-gradient(right, rgba(223, 218, 207, 1), rgba(223, 218, 207, 0));
-    background-image: -moz-linear-gradient(right, rgba(223, 218, 207, 1), rgba(223, 218, 207, 0));
-    background-image: -ms-linear-gradient(right, rgba(223, 218, 207, 1), rgba(223, 218, 207, 0));
-    background-image: -o-linear-gradient(right, rgba(223, 218, 207, 1), rgba(223, 218, 207, 0));
-    background-image: linear-gradient(to left, rgba(223, 218, 207, 1), rgba(223, 218, 207, 0));
+    background-image: -webkit-linear-gradient(right, rgba(243, 243, 243, 1), rgba(243, 243, 243, 0));
+    background-image: -moz-linear-gradient(right, rgba(243, 243, 243, 1), rgba(243, 243, 243, 0));
+    background-image: -ms-linear-gradient(right, rgba(243, 243, 243, 1), rgba(243, 243, 243, 0));
+    background-image: -o-linear-gradient(right, rgba(243, 243, 243, 1), rgba(243, 243, 243, 0));
+    background-image: linear-gradient(to left, rgba(243, 243, 243, 1), rgba(243, 243, 243, 0));
 }
 
 .show-date {
@@ -714,6 +732,7 @@ home.mako
     white-space: nowrap;
     font-size: 11px;
     margin: 0px 4px 4px 4px;
+    color: #949494;
 }
 
 .show-date:after {
@@ -724,11 +743,11 @@ home.mako
     height: 100%;
     top: 0;
     right: 0;
-    background-image: -webkit-linear-gradient(right, rgba(223, 218, 207, 1), rgba(223, 218, 207, 0));
-    background-image: -moz-linear-gradient(right, rgba(223, 218, 207, 1), rgba(223, 218, 207, 0));
-    background-image: -ms-linear-gradient(right, rgba(223, 218, 207, 1), rgba(223, 218, 207, 0));
-    background-image: -o-linear-gradient(right, rgba(223, 218, 207, 1), rgba(223, 218, 207, 0));
-    background-image: linear-gradient(to left, rgba(223, 218, 207, 1), rgba(223, 218, 207, 0));
+    background-image: -webkit-linear-gradient(right, rgba(243, 243, 243, 1), rgba(243, 243, 243, 0));
+    background-image: -moz-linear-gradient(right, rgba(243, 243, 243, 1), rgba(243, 243, 243, 0));
+    background-image: -ms-linear-gradient(right, rgba(243, 243, 243, 1), rgba(243, 243, 243, 0));
+    background-image: -o-linear-gradient(right, rgba(243, 243, 243, 1), rgba(243, 243, 243, 0));
+    background-image: linear-gradient(to left, rgba(243, 243, 243, 1), rgba(243, 243, 243, 0));
 }
 
 .show-table {
@@ -3265,3 +3284,12 @@ IMDB Popular
 .popularShow p{
     margin-bottom:0px;
 }
+
+table.home-header {
+    margin-top: 20px;
+    margin-bottom: 10px;
+}
+
+input.tablesorter-filter.disabled {
+    display: none;
+}
diff --git a/gui/slick/images/providers/bitsnoop.png b/gui/slick/images/providers/bitsnoop.png
new file mode 100644
index 0000000000000000000000000000000000000000..3feed89dc113d5399d304962afbe8ff81d870724
Binary files /dev/null and b/gui/slick/images/providers/bitsnoop.png differ
diff --git a/gui/slick/images/subtitles/flags/und.png b/gui/slick/images/subtitles/flags/und.png
index af9249bc317b724a8923e1447c60977dc9a91827..be4d9f67e8f10134cc9fa330914b024c1b36dd92 100644
Binary files a/gui/slick/images/subtitles/flags/und.png and b/gui/slick/images/subtitles/flags/und.png differ
diff --git a/gui/slick/js/addExistingShow.js b/gui/slick/js/addExistingShow.js
deleted file mode 100644
index 150b5a158f41a7c0b9f15bd32e5b4f805dc0aa6b..0000000000000000000000000000000000000000
--- a/gui/slick/js/addExistingShow.js
+++ /dev/null
@@ -1,72 +0,0 @@
-$(document).ready(function() {
-    $('#tableDiv').on('click', '#checkAll', function() {
-        var seasCheck = this;
-        $('.dirCheck').each(function() {
-            this.checked = seasCheck.checked;
-        });
-    });
-
-    $('#submitShowDirs').click(function() {
-        var dirArr = [];
-        $('.dirCheck').each(function() {
-            if (this.checked === true) {
-                var show = $(this).attr('id');
-                var indexer = $(this).closest('tr').find('select').val();
-                dirArr.push(encodeURIComponent(indexer + '|' + show));
-            }
-        });
-
-        if (dirArr.length === 0) {
-            return false;
-        }
-
-        window.location.href = srRoot + '/home/addShows/addExistingShows?promptForSettings=' + ($('#promptForSettings').prop('checked') ? 'on' : 'off') + '&shows_to_add=' + dirArr.join('&shows_to_add=');
-    });
-
-
-    function loadContent() {
-        var url = '';
-        $('.dir_check').each(function(i,w) {
-            if ($(w).is(':checked')) {
-                if (url.length) {
-                    url += '&';
-                }
-                url += 'rootDir=' + encodeURIComponent($(w).attr('id'));
-            }
-        });
-
-        $('#tableDiv').html('<img id="searchingAnim" src="' + srRoot + '/images/loading32.gif" height="32" width="32" /> loading folders...');
-        $.get(srRoot + '/home/addShows/massAddTable/', url, function(data) {
-            $('#tableDiv').html(data);
-            $("#addRootDirTable").tablesorter({
-                //sortList: [[1,0]],
-                widgets: ['zebra'],
-                headers: {
-                    0: { sorter: false }
-                }
-            });
-        });
-    }
-
-    var lastTxt = '';
-    $('#rootDirText').change(function() {
-        if (lastTxt === $('#rootDirText').val()) {
-            return false;
-        } else {
-            lastTxt = $('#rootDirText').val();
-        }
-        $('#rootDirStaticList').html('');
-        $('#rootDirs option').each(function(i, w) {
-            $('#rootDirStaticList').append('<li class="ui-state-default ui-corner-all"><input type="checkbox" class="cb dir_check" id="' + $(w).val() + '" checked=checked> <label for="' + $(w).val() + '"><b>' + $(w).val() + '</b></label></li>');
-        });
-        loadContent();
-    });
-
-    $('#rootDirStaticList').on('click', '.dir_check', loadContent);
-
-    $('#tableDiv').on('click', '.showManage', function(event) {
-        event.preventDefault();
-        $("#tabs").tabs('option', 'active', 0);
-        $('html,body').animate({scrollTop:0}, 1000);
-    });
-});
diff --git a/gui/slick/js/addTrendingShow.js b/gui/slick/js/addTrendingShow.js
deleted file mode 100644
index ef5abbc17df8265c3d1b73b58d8eda01503a7024..0000000000000000000000000000000000000000
--- a/gui/slick/js/addTrendingShow.js
+++ /dev/null
@@ -1,14 +0,0 @@
-$.fn.loadContent = function(path, loadingTxt, errorTxt) {
-    $(this).html('<img id="searchingAnim" src="' + srRoot + '/images/loading32' + themeSpinner + '.gif" height="32" width="32" />&nbsp;' + loadingTxt);
-    $(this).load(srRoot + path + ' #container', function(response, status) {
-        if (status === "error") { $(this).empty().html(errorTxt); }
-    });
-};
-
-$(document).ready(function() {
-    $('#trendingShows').loadContent('/home/addShows/getTrendingShows/', 'Loading trending shows...', 'Trakt timed out, refresh page to try again');
-    $('#container').isotope({
-        itemSelector: '.trakt_show',
-        layoutMode: 'fitRows'
-    });
-});
diff --git a/gui/slick/js/browser.js b/gui/slick/js/browser.js
index 51f6f4849dc6d84d70570af1b4c3deedb9947821..b3a447d8159202e16541e6f0e109bd9a1ddeb43e 100644
--- a/gui/slick/js/browser.js
+++ b/gui/slick/js/browser.js
@@ -90,7 +90,11 @@
                 autoOpen:    false
             });
         }
-
+        else {
+            // The title may change, even if fileBrowserDialog already exists
+            fileBrowserDialog.dialog('option', 'title', options.title);   
+        }
+        
         fileBrowserDialog.dialog('option', 'buttons', [
             {
                 text: 'Ok',
diff --git a/gui/slick/js/core.js b/gui/slick/js/core.js
index 496c0cc2f8e822e0d653bc55d824d08706a5ba37..ca1c245579f54bddc152564e7b58c2d32cb36d23 100644
--- a/gui/slick/js/core.js
+++ b/gui/slick/js/core.js
@@ -22,6 +22,26 @@ var configSuccess = function(){
     $('#prowl_show').trigger('notify');
 };
 
+function metaToBool(pyVar){
+    var meta = $('meta[data-var="' + pyVar + '"]').data('content');
+    if(meta === undefined){
+        console.log(pyVar + ' is empty, did you forget to add this to main.mako?');
+        return meta;
+    } else {
+        meta = (isNaN(meta) ? meta.toLowerCase() : meta.toString());
+        return !(meta === 'false' || meta === 'none' || meta === '0');
+    }
+}
+
+function getMeta(pyVar){
+    return $('meta[data-var="' + pyVar + '"]').data('content');
+}
+
+function isMeta(pyVar, result){
+    var reg = new RegExp(result.length > 1 ? result.join('|') : result);
+    return (reg).test($('meta[data-var="' + pyVar + '"]').data('content'));
+}
+
 var SICKRAGE = {
     common: {
         init: function() {
@@ -133,6 +153,12 @@ var SICKRAGE = {
                 };
                 $("[datetime]").timeago();
             }
+
+            $(document.body).on('click', 'a[data-no-redirect]', function(e){
+                e.preventDefault();
+                $.get($(this).attr('href'));
+                return false;
+            });
         }
     },
     config: {
@@ -868,14 +894,21 @@ var SICKRAGE = {
             // we have to call this function on dom ready to create the devices select
             getPushbulletDevices();
 
-            // @TODO Find out what notify_data actually does since it doesn't seem to be a real function
             $('#email_show').on('change', function() {
                 var key = parseInt($('#email_show').val(), 10);
-                $('#email_show_list').val(key >= 0 ? notify_data[key.toString()].list : ''); // jshint ignore:line
+                $.getJSON(srRoot + "/home/loadShowNotifyLists", function(notifyData) {
+                    if (notifyData._size > 0) {
+                        $('#email_show_list').val(key >= 0 ? notifyData[key.toString()].list : '');
+                    }
+                });
             });
             $('#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
+                $.getJSON(srRoot + "/home/loadShowNotifyLists", function(notifyData) {
+                    if (notifyData._size > 0) {
+                        $('#prowl_show_list').val(key >= 0 ? notifyData[key.toString()].prowl_notify_list  : '');   // jshint ignore:line
+                    }
+                });
             });
 
             // Update the internal data struct anytime settings are saved to the server
@@ -887,10 +920,8 @@ var SICKRAGE = {
             });
 
             function loadShowNotifyLists() {
-                $.get(srRoot + "/home/loadShowNotifyLists", function(data) {
-                    var list, html, s;
-                    list = $.parseJSON(data); // @TODO The line below this is the same as the $('#email_show') function above
-                    notify_data = list; // jshint ignore:line
+                $.getJSON(srRoot + "/home/loadShowNotifyLists", function(list) {
+                    var html, s;
                     if (list._size === 0) { return; }
 
                     // Convert the 'list' object to a js array of objects so that we can sort it
@@ -1754,7 +1785,7 @@ var SICKRAGE = {
 
             // Handle filtering in the poster layout
             $('#filterShowName').on('input', _.debounce(function (e) {
-                $('#container, #container-anime').isotope({
+                $('.show-grid').isotope({
                     filter: function () {
                       var name = $('div.show-title', this).text();
                       return (name.toLowerCase().indexOf(e.target.value.toLowerCase()) !== -1);
@@ -1762,6 +1793,63 @@ var SICKRAGE = {
                 });
             }, 500));
 
+            function resizePosters(newSize) {
+                var fontSize, logoWidth, borderRadius, borderWidth;
+                if (newSize < 125) { // small
+                    borderRadius = 3;
+                    borderWidth = 4;
+                } else if (newSize < 175) { // medium
+                    fontSize = 9;
+                    logoWidth = 40;
+                    borderRadius = 4;
+                    borderWidth = 5;
+                } else { // large
+                    fontSize = 11;
+                    logoWidth = 50;
+                    borderRadius = 6;
+                    borderWidth = 6;
+                }
+
+                // If there's a poster popup, remove it before resizing
+                $('#posterPopup').remove();
+
+                if (fontSize === undefined) {
+                    $('.show-details').hide();
+                } else {
+                    $('.show-details').show();
+                    $('.show-dlstats, .show-quality').css('fontSize', fontSize);
+                    $('.show-network-image').css('width', logoWidth);
+                }
+
+                $('.show-container').css({
+                    width: newSize,
+                    borderWidth: borderWidth,
+                    borderRadius: borderRadius
+                });
+            }
+
+            var posterSize;
+            if (typeof(Storage) !== 'undefined') {
+                posterSize = parseInt(localStorage.getItem('posterSize'));
+            }
+            if (typeof(posterSize) !== 'number' || isNaN(posterSize)) {
+                posterSize = 188;
+            }
+            resizePosters(posterSize);
+
+            $('#posterSizeSlider').slider({
+                min: 75,
+                max: 250,
+                value: posterSize,
+                change: function (e, ui) {
+                    if (typeof(Storage) !== 'undefined') {
+                        localStorage.setItem('posterSize', ui.value);
+                    }
+                    resizePosters(ui.value);
+                    $('.show-grid').isotope('layout');
+                }
+            });
+
             // This needs to be refined to work a little faster.
             $('.progressbar').each(function(){
                 var percentage = $(this).data('progress-percentage');
@@ -1786,7 +1874,9 @@ var SICKRAGE = {
                     3: function(node) { return $(node).find("span").prop("title").toLowerCase(); },
                     4: function(node) { return $(node).find("span").text().toLowerCase(); },
                     5: function(node) { return $(node).find("span:first").text(); },
-                    6: function(node) { return $(node).find("img").attr("alt"); }
+                    6: function(node) { return $(node).data('show-size'); },
+                    7: function(node) { return $(node).find("img").attr("alt"); }
+
                 },
                 widgets: ['saveSort', 'zebra', 'stickyHeaders', 'filter', 'columnSelector'],
                 headers: {
@@ -1795,7 +1885,8 @@ var SICKRAGE = {
                     2: { sorter: 'loadingNames' },
                     4: { sorter: 'quality' },
                     5: { sorter: 'eps' },
-                    6: { filter: 'parsed' }
+                    6: { sorter: 'digit' },
+                    7: { filter: 'parsed' }
                 },
                 widgetOptions: {
                     filter_columnFilters: true, // jshint ignore:line
@@ -1862,43 +1953,98 @@ var SICKRAGE = {
                 sortAppend: [[2,0]]
             });
 
-            // @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({
+            $('.show-grid').imagesLoaded(function () {
+                $('.loading-spinner').hide();
+                $('.show-grid').show().isotope({
                     itemSelector: '.show-container',
                     sortBy : getMeta('sickbeard.POSTER_SORTBY'),
                     sortAscending: getMeta('sickbeard.POSTER_SORTDIR'),
                     layoutMode: 'masonry',
                     masonry: {
-                        columnWidth: 13,
                         isFitWidth: true
                     },
                     getSortData: {
-                        name: function(itemElem){
+                        name: function (itemElem) {
                             var name = $(itemElem).attr('data-name');
                             return (metaToBool('sickbeard.SORT_ARTICLE') ? (name || '') : (name || '').replace(/^(The|A|An)\s/i,''));
                         },
                         network: '[data-network]',
-                        date: function(itemElem){
+                        date: function (itemElem) {
                             var date = $(itemElem).attr('data-date');
                             return date.length && parseInt(date, 10) || Number.POSITIVE_INFINITY;
                         },
-                        progress: function(itemElem){
+                        progress: function (itemElem) {
                             var progress = $(itemElem).attr('data-progress');
                             return progress.length && parseInt(progress, 10) || Number.NEGATIVE_INFINITY;
                         }
                     }
                 });
+
+                // When posters are small enough to not display the .show-details
+                // table, display a larger poster when hovering.
+                var posterHoverTimer = null;
+                $('.show-container').on('mouseenter', function () {
+                    var poster = $(this);
+                    if (poster.find('.show-details').css('display') !== 'none') {
+                        return;
+                    }
+                    posterHoverTimer = setTimeout(function () {
+                        posterHoverTimer = null;
+                        $('#posterPopup').remove();
+                        var popup = poster.clone().attr({
+                            id: 'posterPopup'
+                        });
+                        var origLeft = poster.offset().left;
+                        var origTop  = poster.offset().top;
+                        popup.css({
+                            position: 'absolute',
+                            margin: 0,
+                            top: origTop,
+                            left: origLeft
+                        });
+                        popup.find('.show-details').show();
+                        popup.on('mouseleave', function () {
+                            $(this).remove();
+                        });
+                        popup.zIndex(9999);
+                        popup.appendTo('body');
+
+                        var height = 438, width = 250;
+                        var newTop = (origTop+poster.height()/2)-(height/2);
+                        var newLeft = (origLeft+poster.width()/2)-(width/2);
+
+                        // Make sure the popup isn't outside the viewport
+                        var margin = 5;
+                        var scrollTop = $(window).scrollTop();
+                        var scrollLeft = $(window).scrollLeft();
+                        var scrollBottom = scrollTop + $(window).innerHeight();
+                        var scrollRight = scrollLeft + $(window).innerWidth();
+                        if (newTop < scrollTop+margin) { newTop = scrollTop+margin; }
+                        if (newLeft < scrollLeft+margin) { newLeft = scrollLeft+margin; }
+                        if (newTop+height+margin > scrollBottom) { newTop = scrollBottom-height-margin; }
+                        if (newLeft+width+margin > scrollRight) { newLeft = scrollRight-width-margin; }
+
+                        popup.animate({
+                            top: newTop,
+                            left: newLeft,
+                            width: 250,
+                            height: 438
+                        });
+                    }, 300);
+                }).on('mouseleave', function () {
+                    if (posterHoverTimer !== null) {
+                        clearTimeout(posterHoverTimer);
+                    }
+                });
             });
 
             $('#postersort').on('change', function(){
-                $('#container, #container-anime').isotope({sortBy: $(this).val()});
+                $('.show-grid').isotope({sortBy: $(this).val()});
                 $.get($(this).find('option[value=' + $(this).val() +']').attr('data-sort'));
             });
 
             $('#postersortdirection').on('change', function(){
-                $('#container, #container-anime').isotope({sortAscending: ($(this).val() === 'true')});
+                $('.show-grid').isotope({sortAscending: ($(this).val() === 'true')});
                 $.get($(this).find('option[value=' + $(this).val() +']').attr('data-sort'));
             });
 
@@ -2262,9 +2408,9 @@ var SICKRAGE = {
                     8: function(node) { return $(node).find("img").attr("alt"); },
                     9: function(node) { return $(node).find("img").attr("alt"); },
                 },
-                widgets: ['zebra'],
+                widgets: ['zebra', 'filter'],
                 headers: {
-                    0: { sorter: false},
+                    0: { sorter: false, filter: false},
                     1: { sorter: 'showNames'},
                     2: { sorter: 'quality'},
                     3: { sorter: 'sports'},
@@ -2601,6 +2747,403 @@ var SICKRAGE = {
                 $.tablesorter.columnSelector.attachTo( $('#showListTable'), '#popover-target');
             });
         }
+    },
+    addShows: {
+        init: function() {
+            $('#tabs').tabs({
+                collapsible: true,
+                selected: (metaToBool('sickbeard.SORT_ARTICLE') ? -1 : 0)
+            });
+
+            $.initRemoteShowGrid = function(){
+                // Set defaults on page load
+                $('#showsort').val('original');
+                $('#showsortdirection').val('asc');
+
+                $('#showsort').on('change', function() {
+                    var sortCriteria;
+                    switch (this.value) {
+                        case 'original':
+                            sortCriteria = 'original-order';
+                            break;
+                        case 'rating':
+                            /* randomise, else the rating_votes can already
+                             * have sorted leaving this with nothing to do.
+                             */
+                            $('#container').isotope({sortBy: 'random'});
+                            sortCriteria = 'rating';
+                            break;
+                        case 'rating_votes':
+                            sortCriteria = ['rating', 'votes'];
+                            break;
+                        case 'votes':
+                            sortCriteria = 'votes';
+                            break;
+                        default:
+                            sortCriteria = 'name';
+                            break;
+                    }
+                    $('#container').isotope({
+                        sortBy: sortCriteria
+                    });
+                });
+
+                $('#showsortdirection').on('change', function() {
+                    $('#container').isotope({
+                        sortAscending: ('asc' === this.value)
+                    });
+                });
+
+                $('#container').imagesLoaded(function() {
+                    $('#container').isotope({
+                        sortBy: 'original-order',
+                        layoutMode: 'fitRows',
+                        getSortData: {
+                            name: function(itemElem) {
+                                var name = $(itemElem).attr('data-name') || '';
+                                return (metaToBool('sickbeard.SORT_ARTICLE') ? name : name.replace(/^(The|A|An)\s/i, '')).toLowerCase();
+                            },
+                            rating: '[data-rating] parseInt',
+                            votes: '[data-votes] parseInt',
+                        }
+                    });
+                });
+            };
+
+            $.fn.loadRemoteShows = function(path, loadingTxt, errorTxt) {
+                $(this).html('<img id="searchingAnim" src="' + srRoot + '/images/loading32' + themeSpinner + '.gif" height="32" width="32" />&nbsp;' + loadingTxt);
+                $(this).load(srRoot + path + ' #container', function(response, status) {
+                    if (status === "error") {
+                        $(this).empty().html(errorTxt);
+                    } else {
+                        $.initRemoteShowGrid();
+                    }
+                });
+            };
+        },
+        index: function() {
+
+        },
+        newShow: function() {
+            var searchRequestXhr = null;
+
+            function searchIndexers() {
+                if (!$('#nameToSearch').val().length) { return; }
+
+                if (searchRequestXhr) { searchRequestXhr.abort(); }
+
+                var searchingFor = $('#nameToSearch').val().trim() + ' on ' + $('#providedIndexer option:selected').text() + ' in ' + $('#indexerLangSelect').val();
+                $('#searchResults').empty().html('<img id="searchingAnim" src="' + srRoot + '/images/loading32' + themeSpinner + '.gif" height="32" width="32" /> searching ' + searchingFor + '...');
+
+                searchRequestXhr = $.ajax({
+                    url: srRoot + '/addShows/searchIndexersForShowName',
+                    data: {
+                        'search_term': $('#nameToSearch').val().trim(),
+                        'lang': $('#indexerLangSelect').val(),
+                        'indexer': $('#providedIndexer').val()},
+                    timeout: parseInt($('#indexer_timeout').val(), 10) * 1000,
+                    dataType: 'json',
+                    error: function () {
+                        $('#searchResults').empty().html('search timed out, try again or try another indexer');
+                    },
+                    success: function (data) {
+                        var firstResult = true;
+                        var resultStr = '<fieldset>\n<legend class="legendStep">Search Results:</legend>\n';
+                        var checked = '';
+
+                        if (data.results.length === 0) {
+                            resultStr += '<b>No results found, try a different search.</b>';
+                        } else {
+                            $.each(data.results, function(index, obj) {
+                                if (firstResult) {
+                                    checked = ' checked';
+                                    firstResult = false;
+                                } else {
+                                    checked = '';
+                                }
+
+                                var whichSeries = obj.join('|');
+
+                                resultStr += '<input type="radio" id="whichSeries" name="whichSeries" value="' + whichSeries.replace(/"/g, '')  + '"' + checked + ' /> ';
+                                if (data.langid && data.langid !== '') {
+                                    resultStr += '<a href="' + anonURL + obj[2] + obj[3] + '&lid=' + data.langid + '" onclick=\"window.open(this.href, \'_blank\'); return false;\" ><b>' + obj[4] + '</b></a>';
+                                } else {
+                                    resultStr += '<a href="' + anonURL + obj[2] + obj[3] + '" onclick=\"window.open(this.href, \'_blank\'); return false;\" ><b>' + obj[4] + '</b></a>';
+                                }
+
+                                if (obj[5] !== null) {
+                                    var startDate = new Date(obj[5]);
+                                    var today = new Date();
+                                    if (startDate > today) {
+                                        resultStr += ' (will debut on ' + obj[5] + ')';
+                                    } else {
+                                        resultStr += ' (started on ' + obj[5] + ')';
+                                    }
+                                }
+
+                                if (obj[0] !== null) {
+                                    resultStr += ' [' + obj[0] + ']';
+                                }
+
+                                resultStr += '<br>';
+                            });
+                            resultStr += '</ul>';
+                        }
+                        resultStr += '</fieldset>';
+                        $('#searchResults').html(resultStr);
+                        updateSampleText();
+                        myform.loadsection(0);
+                    }
+                });
+            }
+
+            $('#searchName').click(function () { searchIndexers(); });
+
+            if ($('#nameToSearch').length && $('#nameToSearch').val().length) {
+                $('#searchName').click();
+            }
+
+            $('#addShowButton').click(function () {
+                // if they haven't picked a show don't let them submit
+                if (!$('input:radio[name="whichSeries"]:checked').val() && !$('input:hidden[name="whichSeries"]').val().length) {
+                    alert('You must choose a show to continue');
+                    return false;
+                }
+                generateBlackWhiteList();
+                $('#addShowForm').submit();
+            });
+
+            $('#skipShowButton').click(function () {
+                $('#skipShow').val('1');
+                $('#addShowForm').submit();
+            });
+
+            $('#qualityPreset').change(function () {
+                myform.loadsection(2);
+            });
+
+            /***********************************************
+            * jQuery Form to Form Wizard- (c) Dynamic Drive (www.dynamicdrive.com)
+            * This notice MUST stay intact for legal use
+            * Visit http://www.dynamicdrive.com/ for this script and 100s more.
+            ***********************************************/
+
+            // @TODO we need to move to real forms instead of this
+
+            var myform = new formtowizard({ // jshint ignore:line
+                formid: 'addShowForm',
+                revealfx: ['slide', 500],
+                oninit: function () {
+                    updateSampleText();
+                    if ($('input:hidden[name=whichSeries]').length && $('#fullShowPath').length) {
+                        goToStep(3);
+                    }
+                }
+            });
+
+            function goToStep(num) {
+                $('.step').each(function () {
+                    if ($.data(this, 'section') + 1 === num) {
+                        $(this).click();
+                    }
+                });
+            }
+
+            $('#nameToSearch').focus();
+
+            function updateSampleText() {
+                // if something's selected then we have some behavior to figure out
+
+                var showName, sepChar;
+                // if they've picked a radio button then use that
+                if ($('input:radio[name=whichSeries]:checked').length) {
+                    showName = $('input:radio[name=whichSeries]:checked').val().split('|')[4];
+                } else if ($('input:hidden[name=whichSeries]').length && $('input:hidden[name=whichSeries]').val().length) { // if we provided a show in the hidden field, use that
+                    showName = $('#providedName').val();
+                } else {
+                    showName = '';
+                }
+                updateBlackWhiteList(showName);
+                var sampleText = 'Adding show <b>' + showName + '</b> into <b>';
+
+                // if we have a root dir selected, figure out the path
+                if ($('#rootDirs option:selected').length) {
+                    var rootDirectoryText = $('#rootDirs option:selected').val();
+                    if (rootDirectoryText.indexOf('/') >= 0) {
+                        sepChar = '/';
+                    } else if (rootDirectoryText.indexOf('\\') >= 0) {
+                        sepChar = '\\';
+                    } else {
+                        sepChar = '';
+                    }
+
+                    if (rootDirectoryText.substr(sampleText.length - 1) !== sepChar) {
+                        rootDirectoryText += sepChar;
+                    }
+                    rootDirectoryText += '<i>||</i>' + sepChar;
+
+                    sampleText += rootDirectoryText;
+                } else if ($('#fullShowPath').length && $('#fullShowPath').val().length) {
+                    sampleText += $('#fullShowPath').val();
+                } else {
+                    sampleText += 'unknown dir.';
+                }
+
+                sampleText += '</b>';
+
+                // if we have a show name then sanitize and use it for the dir name
+                if (showName.length) {
+                    $.get(srRoot + '/addShows/sanitizeFileName', {name: showName}, function (data) {
+                        $('#displayText').html(sampleText.replace('||', data));
+                    });
+                // if not then it's unknown
+                } else {
+                    $('#displayText').html(sampleText.replace('||', '??'));
+                }
+
+                // also toggle the add show button
+                if (($("#rootDirs option:selected").length || ($('#fullShowPath').length && $('#fullShowPath').val().length)) &&
+                    ($('input:radio[name=whichSeries]:checked').length) || ($('input:hidden[name=whichSeries]').length && $('input:hidden[name=whichSeries]').val().length)) {
+                    $('#addShowButton').attr('disabled', false);
+                } else {
+                    $('#addShowButton').attr('disabled', true);
+                }
+            }
+
+            $('#rootDirText').change(updateSampleText);
+            $('#searchResults').on('change', '#whichSeries', updateSampleText);
+
+            $('#nameToSearch').keyup(function(event) {
+                if (event.keyCode === 13) {
+                    $('#searchName').click();
+                }
+            });
+
+            $('#anime').change (function() {
+                updateSampleText();
+                myform.loadsection(2);
+            });
+
+            function updateBlackWhiteList(showName) {
+                $('#white').children().remove();
+                $('#black').children().remove();
+                $('#pool').children().remove();
+
+                if ($('#anime').prop('checked')) {
+                    $('#blackwhitelist').show();
+                    if (showName) {
+                        $.getJSON(srRoot + '/home/fetch_releasegroups', {
+                            'show_name': showName
+                        }, function (data) {
+                            if (data.result === 'success') {
+                                $.each(data.groups, function(i, group) {
+                                    var option = $("<option>");
+                                    option.attr("value", group.name);
+                                    option.html(group.name + ' | ' + group.rating + ' | ' + group.range);
+                                    option.appendTo('#pool');
+                                });
+                            }
+                        });
+                    }
+                } else {
+                    $('#blackwhitelist').hide();
+                }
+            }
+        },
+        addExistingShow: function(){
+            $('#tableDiv').on('click', '#checkAll', function() {
+                var seasCheck = this;
+                $('.dirCheck').each(function() {
+                    this.checked = seasCheck.checked;
+                });
+            });
+
+            $('#submitShowDirs').on('click', function() {
+                var dirArr = [];
+                $('.dirCheck').each(function() {
+                    if (this.checked === true) {
+                        var show = $(this).attr('id');
+                        var indexer = $(this).closest('tr').find('select').val();
+                        dirArr.push(encodeURIComponent(indexer + '|' + show));
+                    }
+                });
+
+                if (dirArr.length === 0) {
+                    return false;
+                }
+
+                window.location.href = srRoot + '/addShows/addExistingShows?promptForSettings=' + ($('#promptForSettings').prop('checked') ? 'on' : 'off') + '&shows_to_add=' + dirArr.join('&shows_to_add=');
+            });
+
+            function loadContent() {
+                var url = '';
+                $('.dir_check').each(function(i,w) {
+                    if ($(w).is(':checked')) {
+                        if (url.length) {
+                            url += '&';
+                        }
+                        url += 'rootDir=' + encodeURIComponent($(w).attr('id'));
+                    }
+                });
+
+                $('#tableDiv').html('<img id="searchingAnim" src="' + srRoot + '/images/loading32.gif" height="32" width="32" /> loading folders...');
+                $.get(srRoot + '/addShows/massAddTable/', url, function(data) {
+                    $('#tableDiv').html(data);
+                    $("#addRootDirTable").tablesorter({
+                        //sortList: [[1,0]],
+                        widgets: ['zebra'],
+                        headers: {
+                            0: { sorter: false }
+                        }
+                    });
+                });
+            }
+
+            var lastTxt = '';
+            // @TODO this needs a real name, for now this fixes the issue of the page not loading at all,
+            //       before I added this I couldn't get the directories to show in the table
+            var a = function() {
+                if (lastTxt === $('#rootDirText').val()) {
+                    return false;
+                } else {
+                    lastTxt = $('#rootDirText').val();
+                }
+                $('#rootDirStaticList').html('');
+                $('#rootDirs option').each(function(i, w) {
+                    $('#rootDirStaticList').append('<li class="ui-state-default ui-corner-all"><input type="checkbox" class="cb dir_check" id="' + $(w).val() + '" checked=checked> <label for="' + $(w).val() + '"><b>' + $(w).val() + '</b></label></li>');
+                });
+                loadContent();
+            };
+
+            a();
+
+            $('#rootDirText').on('change', a);
+
+            $('#rootDirStaticList').on('click', '.dir_check', loadContent);
+
+            $('#tableDiv').on('click', '.showManage', function(event) {
+                event.preventDefault();
+                $("#tabs").tabs('option', 'active', 0);
+                $('html,body').animate({scrollTop:0}, 1000);
+            });
+        },
+        recommendedShows: function(){
+            $('#recommendedShows').loadRemoteShows(
+                '/addShows/getRecommendedShows/',
+                'Loading recommended shows...',
+                'Trakt timed out, refresh page to try again'
+            );
+        },
+        trendingShows: function(){
+            $('#trendingShows').loadRemoteShows(
+                '/addShows/getTrendingShows/',
+                'Loading trending shows...',
+                'Trakt timed out, refresh page to try again'
+            );
+        },
+        popularShows: function(){
+            $.initRemoteShowGrid();
+        }
     }
 };
 
@@ -2623,5 +3166,6 @@ var UTIL = {
         UTIL.exec(controller, action);
     }
 };
-
-$(document).ready(UTIL.init);
+if (navigator.userAgent.indexOf('PhantomJS') === -1) {
+    $(document).ready(UTIL.init);
+}
diff --git a/gui/slick/js/core.min.js b/gui/slick/js/core.min.js
index ec512de8b35ca25d9fc76f1969820616877f9338..01fceaea955eaee788560d372e33a918b79d996f 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/home_addExistingShow.js b/gui/slick/js/home_addExistingShow.js
deleted file mode 100644
index 3e3e8dc37f7dfb8c94b377dbae7a7cf56e473d71..0000000000000000000000000000000000000000
--- a/gui/slick/js/home_addExistingShow.js
+++ /dev/null
@@ -1,6 +0,0 @@
-$(document).ready(function(){
-    $( "#tabs" ).tabs({
-        collapsible: true,
-        selected: (metaToBool('sickbeard.SORT_ARTICLE') ? -1 : 0)
-    });
-});
diff --git a/gui/slick/js/home_recommendedShows.js b/gui/slick/js/home_recommendedShows.js
deleted file mode 100644
index fa82ab85cd0eb6f55b0099621a8f6769ace9978e..0000000000000000000000000000000000000000
--- a/gui/slick/js/home_recommendedShows.js
+++ /dev/null
@@ -1,57 +0,0 @@
-$(document).ready(function(){
-    $( "#tabs" ).tabs({
-        collapsible: true,
-        selected: (metaToBool('sickbeard.SORT_ARTICLE') ? -1 : 0)
-    });
-
-    // initialise combos for dirty page refreshes
-    $('#showsort').val('original');
-    $('#showsortdirection').val('asc');
-
-    var $container = [$('#container')];
-    $.each($container, function() {
-        this.isotope({
-            itemSelector: '.trakt_show',
-            sortBy: 'original-order',
-            layoutMode: 'fitRows',
-            getSortData: {
-                name: function( itemElem ) {
-                    var name = $( itemElem ).attr('data-name');
-                    return (metaToBool('sickbeard.SORT_ARTICLE') ? (name || '') : (name || '').replace(/^(The|A|An)\s/i,''));
-                },
-                rating: '[data-rating] parseInt',
-                votes: '[data-votes] parseInt',
-            }
-        });
-    });
-
-    $('#showsort').on( 'change', function() {
-        var sortCriteria;
-        switch (this.value) {
-            case 'original':
-                sortCriteria = 'original-order';
-                break;
-            case 'rating':
-                /* randomise, else the rating_votes can already
-                 * have sorted leaving this with nothing to do.
-                 */
-                $('#container').isotope({sortBy: 'random'});
-                sortCriteria = 'rating';
-                break;
-            case 'rating_votes':
-                sortCriteria = ['rating', 'votes'];
-                break;
-            case 'votes':
-                sortCriteria = 'votes';
-                break;
-            default:
-                sortCriteria = 'name';
-                break;
-        }
-        $('#container').isotope({sortBy: sortCriteria});
-    });
-
-    $('#showsortdirection').on( 'change', function() {
-        $('#container').isotope({sortAscending: ('asc' === this.value)});
-    });
-});
diff --git a/gui/slick/js/home_trendingShows.js b/gui/slick/js/home_trendingShows.js
deleted file mode 100644
index 3f21b696a5f69b6e58891a7def52a1262fb2fa8e..0000000000000000000000000000000000000000
--- a/gui/slick/js/home_trendingShows.js
+++ /dev/null
@@ -1,54 +0,0 @@
-$(document).ready(function(){
-    $("#tabs").tabs({
-        collapsible: true,
-        // selected: ${('0', '-1')[bool(sickbeard.ROOT_DIRS)]}
-    });
-
-    // initialise combos for dirty page refreshes
-    $('#showsort').val('original');
-    $('#showsortdirection').val('asc');
-
-    $('#container').isotope({
-        itemSelector: '.trakt_show',
-        sortBy: 'original-order',
-        layoutMode: 'fitRows',
-        getSortData: {
-            name: function( itemElem ) {
-                var name = $(itemElem).attr('data-name') || '';
-                return (metaToBool('sickbeard.SORT_ARTICLE') ? name : name.replace(/^(The|A|An)\s/i, '')).toLowerCase();
-            },
-            rating: '[data-rating] parseInt',
-            votes: '[data-votes] parseInt',
-        }
-    });
-
-    $('#showsort').on( 'change', function() {
-        var sortCriteria;
-        switch (this.value) {
-            case 'original':
-                sortCriteria = 'original-order';
-                break;
-            case 'rating':
-                /* randomise, else the rating_votes can already
-                 * have sorted leaving this with nothing to do.
-                 */
-                $('#container').isotope({sortBy: 'random'});
-                sortCriteria = 'rating';
-                break;
-            case 'rating_votes':
-                sortCriteria = ['rating', 'votes'];
-                break;
-            case 'votes':
-                sortCriteria = 'votes';
-                break;
-            default:
-                sortCriteria = 'name';
-                break;
-        }
-        $('#container').isotope({sortBy: sortCriteria});
-    });
-
-    $('#showsortdirection').on( 'change', function() {
-        $('#container').isotope({sortAscending: ('asc' === this.value)});
-    });
-});
diff --git a/gui/slick/js/meta.js b/gui/slick/js/meta.js
deleted file mode 100644
index 89e1ed36cb69f6fce012d834866d9e7d65bbdaf9..0000000000000000000000000000000000000000
--- a/gui/slick/js/meta.js
+++ /dev/null
@@ -1,19 +0,0 @@
-function metaToBool(pyVar){ // jshint ignore:line
-    var meta = $('meta[data-var="' + pyVar + '"]').data('content');
-    if(meta === undefined){
-        console.log(pyVar + ' is empty, did you forget to add this to main.mako?');
-        return meta;
-    } else {
-        meta = (isNaN(meta) ? meta.toLowerCase() : meta.toString());
-        return !(meta === 'false' || meta === 'none' || meta === '0');
-    }
-}
-
-function getMeta(pyVar){ // jshint ignore:line
-    return $('meta[data-var="' + pyVar + '"]').data('content');
-}
-
-function isMeta(pyVar, result){ // jshint ignore:line
-    var reg = new RegExp(result.length > 1 ? result.join('|') : result);
-    return (reg).test($('meta[data-var="' + pyVar + '"]').data('content'));
-}
diff --git a/gui/slick/js/newShow.js b/gui/slick/js/newShow.js
deleted file mode 100644
index df8b4cce36e53207cf499f5531fd0ddb448fa1ee..0000000000000000000000000000000000000000
--- a/gui/slick/js/newShow.js
+++ /dev/null
@@ -1,223 +0,0 @@
-$(document).ready(function () {
-
-    var searchRequestXhr = null;
-
-    function searchIndexers() {
-        if (!$('#nameToSearch').val().length) { return; }
-
-        if (searchRequestXhr) { searchRequestXhr.abort(); }
-
-        var searchingFor = $('#nameToSearch').val().trim() + ' on ' + $('#providedIndexer option:selected').text() + ' in ' + $('#indexerLangSelect').val();
-        $('#searchResults').empty().html('<img id="searchingAnim" src="' + srRoot + '/images/loading32' + themeSpinner + '.gif" height="32" width="32" /> searching ' + searchingFor + '...');
-
-        searchRequestXhr = $.ajax({
-            url: srRoot + '/home/addShows/searchIndexersForShowName',
-            data: {'search_term': $('#nameToSearch').val().trim(), 'lang': $('#indexerLangSelect').val(), 'indexer': $('#providedIndexer').val()},
-            timeout: parseInt($('#indexer_timeout').val(), 10) * 1000,
-            dataType: 'json',
-            error: function () {
-                $('#searchResults').empty().html('search timed out, try again or try another indexer');
-            },
-            success: function (data) {
-                var firstResult = true;
-                var resultStr = '<fieldset>\n<legend class="legendStep">Search Results:</legend>\n';
-                var checked = '';
-
-                if (data.results.length === 0) {
-                    resultStr += '<b>No results found, try a different search.</b>';
-                } else {
-                    $.each(data.results, function (index, obj) {
-                        if (firstResult) {
-                            checked = ' checked';
-                            firstResult = false;
-                        } else {
-                            checked = '';
-                        }
-
-                        var whichSeries = obj.join('|');
-
-
-                        resultStr += '<input type="radio" id="whichSeries" name="whichSeries" value="' + whichSeries.replace(/"/g, '')  + '"' + checked + ' /> ';
-                        if (data.langid && data.langid !== '') {
-                            resultStr += '<a href="' + anonURL + obj[2] + obj[3] + '&lid=' + data.langid + '" onclick=\"window.open(this.href, \'_blank\'); return false;\" ><b>' + obj[4] + '</b></a>';
-                        } else {
-                            resultStr += '<a href="' + anonURL + obj[2] + obj[3] + '" onclick=\"window.open(this.href, \'_blank\'); return false;\" ><b>' + obj[4] + '</b></a>';
-                        }
-
-                        if (obj[5] !== null) {
-                            var startDate = new Date(obj[5]);
-                            var today = new Date();
-                            if (startDate > today) {
-                                resultStr += ' (will debut on ' + obj[5] + ')';
-                            } else {
-                                resultStr += ' (started on ' + obj[5] + ')';
-                            }
-                        }
-
-                        if (obj[0] !== null) {
-                            resultStr += ' [' + obj[0] + ']';
-                        }
-
-                        resultStr += '<br>';
-                    });
-                    resultStr += '</ul>';
-                }
-                resultStr += '</fieldset>';
-                $('#searchResults').html(resultStr);
-                updateSampleText();
-                myform.loadsection(0);
-            }
-        });
-    }
-
-    $('#searchName').click(function () { searchIndexers(); });
-
-    if ($('#nameToSearch').length && $('#nameToSearch').val().length) {
-        $('#searchName').click();
-    }
-
-    $('#addShowButton').click(function () {
-        // if they haven't picked a show don't let them submit
-        if (!$('input:radio[name="whichSeries"]:checked').val() && !$('input:hidden[name="whichSeries"]').val().length) {
-            alert('You must choose a show to continue');
-            return false;
-        }
-        generateBlackWhiteList();
-        $('#addShowForm').submit();
-    });
-
-    $('#skipShowButton').click(function () {
-        $('#skipShow').val('1');
-        $('#addShowForm').submit();
-    });
-
-    $('#qualityPreset').change(function () {
-        myform.loadsection(2);
-    });
-
-    /***********************************************
-    * jQuery Form to Form Wizard- (c) Dynamic Drive (www.dynamicdrive.com)
-    * This notice MUST stay intact for legal use
-    * Visit http://www.dynamicdrive.com/ for this script and 100s more.
-    ***********************************************/
-
-    var myform = new formtowizard({ // jshint ignore:line
-        formid: 'addShowForm',
-        revealfx: ['slide', 500],
-        oninit: function () {
-            updateSampleText();
-            if ($('input:hidden[name=whichSeries]').length && $('#fullShowPath').length) {
-                goToStep(3);
-            }
-        }
-    });
-
-    function goToStep(num) {
-        $('.step').each(function () {
-            if ($.data(this, 'section') + 1 === num) {
-                $(this).click();
-            }
-        });
-    }
-
-    $('#nameToSearch').focus();
-
-    function updateSampleText() {
-        // if something's selected then we have some behavior to figure out
-
-        var showName, sepChar;
-        // if they've picked a radio button then use that
-        if ($('input:radio[name=whichSeries]:checked').length) {
-            showName = $('input:radio[name=whichSeries]:checked').val().split('|')[4];
-        } else if ($('input:hidden[name=whichSeries]').length && $('input:hidden[name=whichSeries]').val().length) { // if we provided a show in the hidden field, use that
-            showName = $('#providedName').val();
-        } else {
-            showName = '';
-        }
-        updateBlackWhiteList(showName);
-        var sampleText = 'Adding show <b>' + showName + '</b> into <b>';
-
-        // if we have a root dir selected, figure out the path
-        if ($('#rootDirs option:selected').length) {
-            var rootDirectoryText = $('#rootDirs option:selected').val();
-            if (rootDirectoryText.indexOf('/') >= 0) {
-                sepChar = '/';
-            } else if (rootDirectoryText.indexOf('\\') >= 0) {
-                sepChar = '\\';
-            } else {
-                sepChar = '';
-            }
-
-            if (rootDirectoryText.substr(sampleText.length - 1) !== sepChar) {
-                rootDirectoryText += sepChar;
-            }
-            rootDirectoryText += '<i>||</i>' + sepChar;
-
-            sampleText += rootDirectoryText;
-        } else if ($('#fullShowPath').length && $('#fullShowPath').val().length) {
-            sampleText += $('#fullShowPath').val();
-        } else {
-            sampleText += 'unknown dir.';
-        }
-
-        sampleText += '</b>';
-
-        // if we have a show name then sanitize and use it for the dir name
-        if (showName.length) {
-            $.get(srRoot + '/home/addShows/sanitizeFileName', {name: showName}, function (data) {
-                $('#displayText').html(sampleText.replace('||', data));
-            });
-        // if not then it's unknown
-        } else {
-            $('#displayText').html(sampleText.replace('||', '??'));
-        }
-
-        // also toggle the add show button
-        if (($("#rootDirs option:selected").length || ($('#fullShowPath').length && $('#fullShowPath').val().length)) &&
-            ($('input:radio[name=whichSeries]:checked').length) || ($('input:hidden[name=whichSeries]').length && $('input:hidden[name=whichSeries]').val().length)) {
-            $('#addShowButton').attr('disabled', false);
-        } else {
-            $('#addShowButton').attr('disabled', true);
-        }
-    }
-
-    $('#rootDirText').change(updateSampleText);
-    $('#searchResults').on('change', '#whichSeries', updateSampleText);
-
-    $('#nameToSearch').keyup(function(event) {
-        if (event.keyCode === 13) {
-            $('#searchName').click();
-        }
-    });
-
-    $('#anime').change (function() {
-        updateSampleText();
-        myform.loadsection(2);
-    });
-
-    function updateBlackWhiteList(showName) {
-        $('#white').children().remove();
-        $('#black').children().remove();
-        $('#pool').children().remove();
-
-        if ($('#anime').prop('checked')) {
-            $('#blackwhitelist').show();
-            if (showName) {
-                $.getJSON(srRoot + '/home/fetch_releasegroups', {
-                    'show_name': showName
-                }, function (data) {
-                    if (data.result === 'success') {
-                        $.each(data.groups, function(i, group) {
-                            var option = $("<option>");
-                            option.attr("value", group.name);
-                            option.html(group.name + ' | ' + group.rating + ' | ' + group.range);
-                            option.appendTo('#pool');
-                        });
-                    }
-                });
-            }
-        } else {
-            $('#blackwhitelist').hide();
-        }
-    }
-});
diff --git a/gui/slick/js/qualityChooser.js b/gui/slick/js/qualityChooser.js
index 42827f4485f856e6d1683992fa7f2eb19a7e4562..81439f94f2828c25985541e82fa9171b415f1885 100644
--- a/gui/slick/js/qualityChooser.js
+++ b/gui/slick/js/qualityChooser.js
@@ -28,7 +28,7 @@ $(document).ready(function() {
         return;
     }
 
-    $('#qualityPreset').change(function() {
+    $('#qualityPreset').on('change', function() {
         setFromPresets($('#qualityPreset :selected').val());
     });
 
diff --git a/gui/slick/js/recommendedShows.js b/gui/slick/js/recommendedShows.js
deleted file mode 100644
index c64be8e6fefc1b65861f506622479ea82be846c6..0000000000000000000000000000000000000000
--- a/gui/slick/js/recommendedShows.js
+++ /dev/null
@@ -1,14 +0,0 @@
-$.fn.loadContent = function(path, loadingTxt, errorTxt) {
-    $(this).html('<img id="searchingAnim" src="' + srRoot + '/images/loading32' + themeSpinner + '.gif" height="32" width="32" />&nbsp;' + loadingTxt);
-    $(this).load(srRoot + path + ' #container', function(response, status) {
-        if (status === "error") { $(this).empty().html(errorTxt); }
-    });
-};
-
-$(document).ready(function() {
-    $('#trendingShows').loadContent('/home/addShows/getRecommendedShows/', 'Loading recommended shows...', 'Trakt timed out, refresh page to try again');
-    $('#container').isotope({
-        itemSelector: '.trakt_show',
-        layoutMode: 'fitRows'
-    });
-});
diff --git a/gui/slick/js/rootDirs.js b/gui/slick/js/rootDirs.js
index 0d1d516cf015afc54d0a3a5d78659f08fa9e5cc9..716022dbee67f0e05ed3c22061e8fc9020c009ba 100644
--- a/gui/slick/js/rootDirs.js
+++ b/gui/slick/js/rootDirs.js
@@ -63,17 +63,23 @@ $(document).ready(function() {
         }
 
         refreshRootDirs();
-        $.get(srRoot+'/config/general/saveRootDirs', {rootDirString: $('#rootDirText').val()});
+        $.get(srRoot+'/config/general/saveRootDirs', {
+            rootDirString: $('#rootDirText').val()
+        });
     }
 
-    $('#addRootDir').click(function(){$(this).nFileBrowser(addRootDir);});
-    $('#editRootDir').click(function(){$(this).nFileBrowser(editRootDir, {initialDir: $("#rootDirs option:selected").val()});});
+    $('#addRootDir').on('click', function(){
+        $(this).nFileBrowser(addRootDir);
+    });
+    $('#editRootDir').on('click', function(){
+        $(this).nFileBrowser(editRootDir, {
+            'initialDir': $("#rootDirs option:selected").val()
+        });
+    });
 
-    $('#deleteRootDir').click(function() {
+    $('#deleteRootDir').on('click', function() {
         if ($("#rootDirs option:selected").length) {
-
             var toDelete = $("#rootDirs option:selected");
-
             var newDefault = (toDelete.attr('id') === $("#whichDefaultRootDir").val());
             var deletedNum = $("#rootDirs option:selected").attr('id').substr(3);
 
@@ -81,7 +87,6 @@ $(document).ready(function() {
             syncOptionIDs();
 
             if (newDefault) {
-
                 console.log('new default when deleting');
 
                 // we deleted the default so this isn't valid anymore
@@ -101,15 +106,19 @@ $(document).ready(function() {
 
         }
         refreshRootDirs();
-        $.get(srRoot+'/config/general/saveRootDirs', {rootDirString: $('#rootDirText').val()});
+        $.get(srRoot+'/config/general/saveRootDirs', {
+            'rootDirString': $('#rootDirText').val()
+        });
     });
 
-    $('#defaultRootDir').click(function(){
+    $('#defaultRootDir').on('click', function(){
         if ($("#rootDirs option:selected").length) {
             setDefault($("#rootDirs option:selected").attr('id'));
         }
         refreshRootDirs();
-        $.get(srRoot+'/config/general/saveRootDirs', {rootDirString: $('#rootDirText').val()});
+        $.get(srRoot + '/config/general/saveRootDirs', {
+            rootDirString: $('#rootDirText').val()
+        });
     });
 
     function setDefault(which, force){
diff --git a/gui/slick/js/vender.min.js b/gui/slick/js/vender.min.js
index 9167a17446d294abb22ed0d82b51ddf6341d7d2a..eaccd1e2eca7ca8bbe64db35dfe67708146d1e6b 100644
Binary files a/gui/slick/js/vender.min.js and b/gui/slick/js/vender.min.js differ
diff --git a/gui/slick/scss/core.scss b/gui/slick/scss/core.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/gui/slick/views/home_addShows.mako b/gui/slick/views/addShows.mako
similarity index 84%
rename from gui/slick/views/home_addShows.mako
rename to gui/slick/views/addShows.mako
index a42f588421e63b2d443f5d2360babae47b8f3141..e664999eda481f35ce805f85062265f30a47c8cf 100644
--- a/gui/slick/views/home_addShows.mako
+++ b/gui/slick/views/addShows.mako
@@ -12,7 +12,7 @@
 % endif
 
 <div id="addShowPortal">
-    <a href="${srRoot}/home/addShows/newShow/" id="btnNewShow" class="btn btn-large">
+    <a href="${srRoot}/addShows/newShow/" id="btnNewShow" class="btn btn-large">
         <div class="button"><div class="icon-addnewshow"></div></div>
         <div class="buttontext">
             <h3>Add New Show</h3>
@@ -22,7 +22,7 @@
 
     <br><br>
     % if sickbeard.USE_TRAKT is True:
-    <a href="${srRoot}/home/addShows/trendingShows/" id="btnNewShow" class="btn btn-large">
+    <a href="${srRoot}/addShows/trendingShows/" id="btnNewShow" class="btn btn-large">
         <div class="button"><div class="icon-addtrendingshow"></div></div>
         <div class="buttontext">
             <h3>Add Trending Show</h3>
@@ -32,7 +32,7 @@
 
     <br><br>
 
-    <a href="${srRoot}/home/addShows/recommendedShows/" id="btnNewShow" class="btn btn-large">
+    <a href="${srRoot}/addShows/recommendedShows/" id="btnNewShow" class="btn btn-large">
         <div class="button"><div class="icon-addrecommendedshow"></div></div>
         <div class="buttontext">
             <h3>Add Recommended Shows</h3>
@@ -43,7 +43,7 @@
     <br><br>
     % endif
 
-    <a href="${srRoot}/home/addShows/popularShows/" id="btnNewShow" class="btn btn-large">
+    <a href="${srRoot}/addShows/popularShows/" id="btnNewShow" class="btn btn-large">
         <div class="button"><div class="icon-addtrendingshow"></div></div>
         <div class="buttontext">
             <h3>View Popular Shows</h3>
@@ -53,7 +53,7 @@
 
     <br><br>
 
-    <a href="${srRoot}/home/addShows/existingShows/" id="btnExistingShow" class="btn btn-large">
+    <a href="${srRoot}/addShows/existingShows/" id="btnExistingShow" class="btn btn-large">
         <div class="button"><div class="icon-addexistingshow"></div></div>
         <div class="buttontext">
             <h3>Add Existing Shows</h3>
diff --git a/gui/slick/views/home_addExistingShow.mako b/gui/slick/views/addShows_addExistingShow.mako
similarity index 83%
rename from gui/slick/views/home_addExistingShow.mako
rename to gui/slick/views/addShows_addExistingShow.mako
index 902429e6ba7945de409a708d7ef7fad849d41a70..ad6d318e312d0d7b04186d0a44a570d794ebf988 100644
--- a/gui/slick/views/home_addExistingShow.mako
+++ b/gui/slick/views/addShows_addExistingShow.mako
@@ -4,10 +4,7 @@
 %>
 <%block name="scripts">
 <script type="text/javascript" src="${srRoot}/js/qualityChooser.js?${sbPID}"></script>
-<script type="text/javascript" src="${srRoot}/js/addExistingShow.js?${sbPID}"></script>
-<script type="text/javascript" src="${srRoot}/js/rootDirs.js?${sbPID}"></script>
 <script type="text/javascript" src="${srRoot}/js/addShowOptions.js?${sbPID}"></script>
-<script type="text/javascript" src="${srRoot}/js/home_addExistingShow.js"></script>
 </%block>
 <%block name="content">
 % if not header is UNDEFINED:
@@ -22,7 +19,7 @@
 
     <div id="core-component-group1" class="tab-pane active component-group">
 
-    <form id="addShowForm" method="post" action="${srRoot}/home/addShows/addNewShow" accept-charset="utf-8">
+    <form id="addShowForm" method="post" action="${srRoot}/addShows/addNewShow" accept-charset="utf-8">
 
     <div id="tabs">
         <ul>
diff --git a/gui/slick/views/addShows_newShow.mako b/gui/slick/views/addShows_newShow.mako
new file mode 100644
index 0000000000000000000000000000000000000000..2abd76d9589d81fce8454173b8ea32ff2f2aff54
--- /dev/null
+++ b/gui/slick/views/addShows_newShow.mako
@@ -0,0 +1,101 @@
+<%inherit file="/layouts/main.mako"/>
+<%!
+    import sickbeard
+    from sickbeard.helpers import anon_url
+%>
+<%block name="scripts">
+<script type="text/javascript" src="${srRoot}/js/qualityChooser.js?${sbPID}"></script>
+<script type="text/javascript" src="${srRoot}/js/addShowOptions.js?${sbPID}"></script>
+<script type="text/javascript" src="${srRoot}/js/rootDirs.js?${sbPID}"></script>
+<script type="text/javascript" src="${srRoot}/js/blackwhite.js?${sbPID}"></script>
+</%block>
+<%block name="content">
+% if not header is UNDEFINED:
+    <h1 class="header">${header}</h1>
+% else:
+    <h1 class="title">${title}</h1>
+% endif
+
+<div id="newShowPortal">
+    <div id="config-components">
+        <ul>
+            <li><a href="#core-component-group1">Add New Show</a></li>
+        </ul>
+
+        <div id="core-component-group1" class="tab-pane active component-group">
+            <div id="displayText"></div>
+            <br>
+            <form id="addShowForm" method="post" action="${srRoot}/addShows/addNewShow" accept-charset="utf-8">
+                <fieldset class="sectionwrap">
+                    <legend class="legendStep">Find a show on theTVDB</legend>
+
+                    <div class="stepDiv">
+                        <input type="hidden" id="indexer_timeout" value="${sickbeard.INDEXER_TIMEOUT}" />
+
+                        % if use_provided_info:
+                            Show retrieved from existing metadata: <a href="${anon_url(sickbeard.indexerApi(provided_indexer).config['show_url'], provided_indexer_id)}">${provided_indexer_name}</a>
+                            <input type="hidden" id="indexerLang" name="indexerLang" value="en" />
+                            <input type="hidden" id="whichSeries" name="whichSeries" value="${provided_indexer_id}" />
+                            <input type="hidden" id="providedIndexer" name="providedIndexer" value="${provided_indexer}" />
+                            <input type="hidden" id="providedName" value="${provided_indexer_name}" />
+                        % else:
+                            <input type="text" id="nameToSearch" value="${default_show_name}" class="form-control form-control-inline input-sm input350" autocapitalize="off" />
+                            &nbsp;&nbsp;
+                            <select name="indexerLang" id="indexerLangSelect" class="form-control form-control-inline input-sm bfh-languages" data-language="${sickbeard.INDEXER_DEFAULT_LANGUAGE}" data-available="${','.join(sickbeard.indexerApi().config['valid_languages'])}">
+                            </select><b>*</b>
+                            &nbsp;
+                            <select name="providedIndexer" id="providedIndexer" class="form-control form-control-inline input-sm">
+                                <option value="0" ${('', 'selected="selected"')[provided_indexer == 0]}>All Indexers</option>
+                                % for indexer in indexers:
+                                    <option value="${indexer}" ${('', 'selected="selected"')[provided_indexer == indexer]}>
+                                        ${indexers[indexer]}
+                                    </option>
+                                % endfor
+                            </select>
+                            &nbsp;
+                            <input class="btn btn-inline" type="button" id="searchName" value="Search" />
+
+                            <br><br>
+                            <b>*</b> This will only affect the language of the retrieved metadata file contents and episode filenames.<br>
+                            This <b>DOES NOT</b> allow SickRage to download non-english TV episodes!<br><br>
+                            <div id="searchResults" style="height: 100%;"><br></div>
+                        % endif
+                    </div>
+                </fieldset>
+
+                <fieldset class="sectionwrap">
+                    <legend class="legendStep">Pick the parent folder</legend>
+                    <div class="stepDiv">
+                        % if provided_show_dir:
+                            Pre-chosen Destination Folder: <b>${provided_show_dir}</b> <br>
+                            <input type="hidden" id="fullShowPath" name="fullShowPath" value="${provided_show_dir}" /><br>
+                        % else:
+                            <%include file="/inc_rootDirs.mako"/>
+                        % endif
+                    </div>
+                </fieldset>
+
+                <fieldset class="sectionwrap">
+                    <legend class="legendStep">Customize options</legend>
+                    <div class="stepDiv">
+                        <%include file="/inc_addShowOptions.mako"/>
+                    </div>
+                </fieldset>
+
+                % for curNextDir in other_shows:
+                <input type="hidden" name="other_shows" value="${curNextDir}" />
+                % endfor
+                <input type="hidden" name="skipShow" id="skipShow" value="" />
+            </form>
+            <br>
+
+            <div style="width: 100%; text-align: center;">
+                <input class="btn" type="button" id="addShowButton" value="Add Show" disabled="disabled" />
+                % if provided_show_dir:
+                <input class="btn" type="button" id="skipShowButton" value="Skip Show" />
+                % endif
+            </div>
+        </div>
+    </div>
+</div>
+</%block>
diff --git a/gui/slick/views/addShows_popularShows.mako b/gui/slick/views/addShows_popularShows.mako
new file mode 100644
index 0000000000000000000000000000000000000000..635862674599f372469562f7bdb19e4eaa5f5bd6
--- /dev/null
+++ b/gui/slick/views/addShows_popularShows.mako
@@ -0,0 +1,82 @@
+<%inherit file="/layouts/main.mako"/>
+<%!
+    from sickbeard.helpers import anon_url
+    import sickbeard
+%>
+<%block name="metas">
+<meta data-var="sickbeard.SORT_ARTICLE" data-content="${sickbeard.SORT_ARTICLE}">
+</%block>
+<%block name="content">
+% if not header is UNDEFINED:
+    <h1 class="header">${header}</h1>
+% else:
+    <h1 class="title">${title}</h1>
+% endif
+
+<div id="tabs">
+    <span>Sort By:</span>
+    <select id="showsort" class="form-control form-control-inline input-sm">
+        <option value="name">Name</option>
+        <option value="original" selected="selected">Original</option>
+        <option value="votes">Votes</option>
+        <option value="rating">% Rating</option>
+        <option value="rating_votes">% Rating > Votes</option>
+    </select>
+
+    <span style="margin-left:12px">Sort Order:</span>
+    <select id="showsortdirection" class="form-control form-control-inline input-sm">
+        <option value="asc" selected="selected">Asc</option>
+        <option value="desc">Desc</option>
+    </select>
+</div>
+
+<% imdb_tt = [show.imdbid for show in sickbeard.showList if show.imdbid] %>
+
+<br>
+<div id="popularShows">
+    <div id="container">
+    % if not popular_shows:
+        <div class="trakt_show" style="width:100%; margin-top:20px">
+            <p class="red-text">Fetching of IMDB Data failed. Are you online?
+            <strong>Exception:</strong>
+            <p>${imdb_exception}</p>
+        </div>
+    % else:
+        % for cur_result in popular_shows:
+            % if cur_result['imdb_tt'] in imdb_tt:
+                <% continue %>
+            % endif
+
+            % if 'rating' in cur_result and cur_result['rating']:
+                <% cur_rating = cur_result['rating'] %>
+                <% cur_votes = cur_result['votes'] %>
+            % else:
+                <% cur_rating = '0' %>
+                <% cur_votes = '0' %>
+            % endif
+
+            <div class="trakt_show" data-name="${cur_result['name']}" data-rating="${cur_rating}" data-votes="${cur_votes}">
+                <div class="traktContainer">
+                    <div class="trakt-image">
+                        <a class="trakt-image" href="${anon_url(cur_result['imdb_url'])}" target="_blank"><img alt="" class="trakt-image" src="${srRoot}/cache/${cur_result['image_path']}" /></a>
+                    </div>
+
+                    <div class="show-title">
+                        ${(cur_result['name'], '<span>&nbsp;</span>')['' == cur_result['name']]}
+                    </div>
+
+                    <div class="clearfix">
+                        <p>${int(float(cur_rating)*10)}% <img src="${srRoot}/images/heart.png"></p>
+                        <i>${cur_votes} votes</i>
+                        <div class="traktShowTitleIcons">
+                            <a href="${srRoot}/addShows/addShowByID?indexer_id=${cur_result['imdb_tt']}&amp;showName=${cur_result['name']}&amp;indexer=IMDB" class="btn btn-xs" data-no-redirect>Add Show</a>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        % endfor
+    % endif
+    </div>
+</div>
+<br>
+</%block>
diff --git a/gui/slick/views/home_recommendedShows.mako b/gui/slick/views/addShows_recommendedShows.mako
similarity index 87%
rename from gui/slick/views/home_recommendedShows.mako
rename to gui/slick/views/addShows_recommendedShows.mako
index 080ecfad10833bf3d8ca1151ae32faef1c38d510..97c530e7b65a12d884ef4fe8654319ee27f6989e 100644
--- a/gui/slick/views/home_recommendedShows.mako
+++ b/gui/slick/views/addShows_recommendedShows.mako
@@ -3,10 +3,8 @@
     import sickbeard
 %>
 <%block name="scripts">
-<script type="text/javascript" src="${srRoot}/js/recommendedShows.js?${sbPID}"></script>
 <script type="text/javascript" src="${srRoot}/js/rootDirs.js?${sbPID}"></script>
 <script type="text/javascript" src="${srRoot}/js/plotTooltip.js?${sbPID}"></script>
-<script type="text/javascript" src="${srRoot}/js/home_recommendedShows.js"></script>
 </%block>
 <%block name="content">
 % if not header is UNDEFINED:
@@ -45,6 +43,6 @@
 </div>
 
 <br>
-<div id="trendingShows"></div>
+<div id="recommendedShows"></div>
 <br>
 </%block>
diff --git a/gui/slick/views/home_trendingShows.mako b/gui/slick/views/addShows_trendingShows.mako
similarity index 91%
rename from gui/slick/views/home_trendingShows.mako
rename to gui/slick/views/addShows_trendingShows.mako
index 666528478907f07bcac96e8593ff17fecdb98487..6027ffbba3e18122e5c81bff2c4bdca9263db8ac 100644
--- a/gui/slick/views/home_trendingShows.mako
+++ b/gui/slick/views/addShows_trendingShows.mako
@@ -9,10 +9,8 @@
     from sickbeard.helpers import anon_url
 %>
 <%block name="scripts">
-<script type="text/javascript" src="${srRoot}/js/addTrendingShow.js?${sbPID}"></script>
 <script type="text/javascript" src="${srRoot}/js/rootDirs.js?${sbPID}"></script>
 <script type="text/javascript" src="${srRoot}/js/plotTooltip.js?${sbPID}"></script>
-<script type="text/javascript" src="${srRoot}/js/home_trendingShows.js"></script>
 </%block>
 <%block name="content">
 % if not header is UNDEFINED:
diff --git a/gui/slick/views/apiBuilder.mako b/gui/slick/views/apiBuilder.mako
index 7077ccdb6ef5071746bc8a096030ed110399e5b7..9b8ae20b036bf8784aca317c137279de4c232285 100644
--- a/gui/slick/views/apiBuilder.mako
+++ b/gui/slick/views/apiBuilder.mako
@@ -185,7 +185,6 @@ var commands = ${sorted(commands)};
 var episodes = ${episodes};
 </script>
 <script type="text/javascript" src="${srRoot}/js/vender.min.js?${sbPID}"></script>
-<script type="text/javascript" src="${srRoot}/js/meta.js?${sbPID}"></script>
 <script type="text/javascript" src="${srRoot}/js/core.min.js?${sbPID}"></script>
 <script type="text/javascript" src="${srRoot}/js/apibuilder.js?${sbPID}"></script>
 </body>
diff --git a/gui/slick/views/config_anime.mako b/gui/slick/views/config_anime.mako
index d006a14999b465671f1074ce49a58253d13848f2..d1aa10189055911d7738293cab6d30397e16fc7e 100644
--- a/gui/slick/views/config_anime.mako
+++ b/gui/slick/views/config_anime.mako
@@ -38,7 +38,7 @@
                             <div class="field-pair">
                                 <label class="nocheck">
                                     <span class="component-title">AniDB Username</span>
-                                    <input type="text" name="anidb_username" id="anidb_username" value="${sickbeard.ANIDB_USERNAME}" class="form-control input-sm input350" />
+                                    <input type="text" name="anidb_username" id="anidb_username" value="${sickbeard.ANIDB_USERNAME}" class="form-control input-sm input350" autocapitalize="off" autocomplete="no" />
                                 </label>
                                 <label class="nocheck">
                                     <span class="component-title">&nbsp;</span>
@@ -49,7 +49,7 @@
                             <div class="field-pair">
                                 <label class="nocheck">
                                     <span class="component-title">AniDB Password</span>
-                                    <input type="password" name="anidb_password" id="anidb_password" value="${sickbeard.ANIDB_PASSWORD}" class="form-control input-sm input350" />
+                                    <input type="password" name="anidb_password" id="anidb_password" value="${sickbeard.ANIDB_PASSWORD}" class="form-control input-sm input350" autocomplete="no" autocapitalize="off" />
                                 </label>
                                 <label class="nocheck">
                                     <span class="component-title">&nbsp;</span>
diff --git a/gui/slick/views/config_backuprestore.mako b/gui/slick/views/config_backuprestore.mako
index a93bcc664cadbbb9f38bfb3dd0bd9e8b4f101a8a..c4b9569e8d576dd067bb63f63c782d0ea1ff95ee 100644
--- a/gui/slick/views/config_backuprestore.mako
+++ b/gui/slick/views/config_backuprestore.mako
@@ -43,7 +43,7 @@
 
                             <br><br>
 
-                            <input type="text" name="backupDir" id="backupDir" class="form-control input-sm input350" />
+                            <input type="text" name="backupDir" id="backupDir" class="form-control input-sm input350" autocapitalize="off" />
                             <input class="btn btn-inline" type="button" value="Backup" id="Backup" />
 
                             <br>
@@ -66,7 +66,7 @@
 
                             <br><br>
 
-                            <input type="text" name="backupFile" id="backupFile" class="form-control input-sm input350" />
+                            <input type="text" name="backupFile" id="backupFile" class="form-control input-sm input350" autocapitalize="off" />
                             <input class="btn btn-inline" type="button" value="Restore" id="Restore" />
 
                             <br>
diff --git a/gui/slick/views/config_general.mako b/gui/slick/views/config_general.mako
index b011958682c4d4035c50f5c5a7537d3bb6c83eae..f369fcce3a37c313daf47f2ada7de6494f0d47df 100644
--- a/gui/slick/views/config_general.mako
+++ b/gui/slick/views/config_general.mako
@@ -86,7 +86,7 @@
                             <label for="showupdate_hour">
                                 <span class="component-title">When to update shows</span>
                                 <span class="component-desc">
-                                    <input type="text" name="showupdate_hour" id="showupdate_hour" value="${sickbeard.SHOWUPDATE_HOUR}" class="form-control input-sm input75" />
+                                    <input type="text" name="showupdate_hour" id="showupdate_hour" value="${sickbeard.SHOWUPDATE_HOUR}" class="form-control input-sm input75" autocapitalize="off" />
                                     <p>with information such as next air dates, show ended, etc. Use 15 for 3pm, 4 for 4am etc. Anything over 23 or under 0 will be set to 0 (12am)</p>
                                 </span>
                             </label>
@@ -111,7 +111,7 @@
                             <label for="log_dir">
                                 <span class="component-title">Log file folder location</span>
                                 <span class="component-desc">
-                                    <input type="text" name="log_dir" id="log_dir" value="${sickbeard.ACTUAL_LOG_DIR}" class="form-control input-sm input350" />
+                                    <input type="text" name="log_dir" id="log_dir" value="${sickbeard.ACTUAL_LOG_DIR}" class="form-control input-sm input350" autocapitalize="off" />
                                 </span>
                             </label>
                         </div>
@@ -120,7 +120,7 @@
                             <label for="log_nr">
                                 <span class="component-title">Number of Log files saved</span>
                                 <span class="component-desc">
-                                    <input type="text" name="log_nr" id="log_nr" value="${sickbeard.LOG_NR}" class="form-control input-sm input75" />
+                                    <input type="text" name="log_nr" id="log_nr" value="${sickbeard.LOG_NR}" class="form-control input-sm input75" autocapitalize="off" />
                                     <p>number of log files saved when rotating logs (default: 5) (REQUIRES RESTART)</p>
                                 </span>
                             </label>
@@ -130,7 +130,7 @@
                             <label for="log_size">
                                 <span class="component-title">Size of Log files saved</span>
                                 <span class="component-desc">
-                                    <input type="text" name="log_size" id="log_size" value="${sickbeard.LOG_SIZE}" class="form-control input-sm input75" />
+                                    <input type="text" name="log_size" id="log_size" value="${sickbeard.LOG_SIZE}" class="form-control input-sm input75" autocapitalize="off" />
                                     <p>maximum size of a log file saved (default: 1048576 (1MB)) (REQUIRES RESTART)</p>
                                 </span>
                             </label>
@@ -155,7 +155,7 @@
                             <label for="indexer_timeout">
                                 <span class="component-title">Timeout show indexer at</span>
                                 <span class="component-desc">
-                                    <input type="text" name="indexer_timeout" id="indexer_timeout" value="${sickbeard.INDEXER_TIMEOUT}" class="form-control input-sm input75" />
+                                    <input type="text" name="indexer_timeout" id="indexer_timeout" value="${sickbeard.INDEXER_TIMEOUT}" class="form-control input-sm input75" autocapitalize="off" />
                                     <p>seconds of inactivity when finding new shows (default:10)</p>
                                 </span>
                             </label>
@@ -208,7 +208,7 @@
                             <label>
                                 <span class="component-title">Check the server every*</span>
                                 <span class="component-desc">
-                                    <input type="text" name="update_frequency" id="update_frequency" value="${sickbeard.UPDATE_FREQUENCY}" class="form-control input-sm input75" />
+                                    <input type="text" name="update_frequency" id="update_frequency" value="${sickbeard.UPDATE_FREQUENCY}" class="form-control input-sm input75" autocapitalize="off" />
                                     <p>hours for software updates (default:12)</p>
                                 </span>
                             </label>
@@ -353,7 +353,7 @@
                         <div class="field-pair">
                             <label for="download_url">
                                 <span class="component-title">Download url</span>
-                                <input type="text" name="download_url" id="download_url" value="${sickbeard.DOWNLOAD_URL}" size="35" />
+                                <input type="text" name="download_url" id="download_url" value="${sickbeard.DOWNLOAD_URL}" size="35" autocapitalize="off" />
                             </label>
                             <label>
                                 <span class="component-title">&nbsp;</span>
@@ -382,7 +382,7 @@
                             <label for="api_key">
                                 <span class="component-title">API key</span>
                                 <span class="component-desc">
-                                    <input type="text" name="api_key" id="api_key" value="${sickbeard.API_KEY}" class="form-control input-sm input300" readonly="readonly" />
+                                    <input type="text" name="api_key" id="api_key" value="${sickbeard.API_KEY}" class="form-control input-sm input300" readonly="readonly" autocapitalize="off" />
                                     <input class="btn btn-inline" type="button" id="generate_new_apikey" value="Generate">
                                     <div class="clear-left">
                                         <p>used to give 3rd party programs limited access to SickRage</p>
@@ -406,7 +406,7 @@
                             <label for="web_username">
                                 <span class="component-title">HTTP username</span>
                                 <span class="component-desc">
-                                    <input type="text" name="web_username" id="web_username" value="${sickbeard.WEB_USERNAME}" class="form-control input-sm input300" />
+                                    <input type="text" name="web_username" id="web_username" value="${sickbeard.WEB_USERNAME}" class="form-control input-sm input300" autocapitalize="off" autocomplete="no" />
                                     <p>set blank for no login</p>
                                 </span>
                             </label>
@@ -416,7 +416,7 @@
                             <label for="web_password">
                                 <span class="component-title">HTTP password</span>
                                 <span class="component-desc">
-                                    <input type="password" name="web_password" id="web_password" value="${sickbeard.WEB_PASSWORD}" class="form-control input-sm input300" />
+                                    <input type="password" name="web_password" id="web_password" value="${sickbeard.WEB_PASSWORD}" class="form-control input-sm input300" autocomplete="no" autocapitalize="off" />
                                     <p>blank = no authentication</span>
                             </label>
                         </div>
@@ -425,7 +425,7 @@
                             <label for="web_port">
                                 <span class="component-title">HTTP port</span>
                                 <span class="component-desc">
-                                    <input type="text" name="web_port" id="web_port" value="${sickbeard.WEB_PORT}" class="form-control input-sm input100" />
+                                    <input type="text" name="web_port" id="web_port" value="${sickbeard.WEB_PORT}" class="form-control input-sm input100" autocapitalize="off" />
                                     <p>web port to browse and access SickRage (default:8081)</p>
                                 </span>
                             </label>
@@ -455,7 +455,7 @@
                                 <label for="https_cert">
                                     <span class="component-title">HTTPS certificate</span>
                                     <span class="component-desc">
-                                        <input type="text" name="https_cert" id="https_cert" value="${sickbeard.HTTPS_CERT}" class="form-control input-sm input300" />
+                                        <input type="text" name="https_cert" id="https_cert" value="${sickbeard.HTTPS_CERT}" class="form-control input-sm input300" autocapitalize="off" />
                                         <div class="clear-left"><p>file name or path to HTTPS certificate</p></div>
                                     </span>
                                 </label>
@@ -464,7 +464,7 @@
                                 <label for="https_key">
                                     <span class="component-title">HTTPS key</span>
                                     <span class="component-desc">
-                                        <input type="text" name="https_key" id="https_key" value="${sickbeard.HTTPS_KEY}" class="form-control input-sm input300" />
+                                        <input type="text" name="https_key" id="https_key" value="${sickbeard.HTTPS_KEY}" class="form-control input-sm input300" autocapitalize="off" />
                                         <div class="clear-left"><p>file name or path to HTTPS key</p></div>
                                     </span>
                                 </label>
@@ -517,7 +517,7 @@
                             <label>
                                 <span class="component-title">Anonymous redirect</span>
                                 <span class="component-desc">
-                                    <input type="text" name="anon_redirect" value="${sickbeard.ANON_REDIRECT}" class="form-control input-sm input300" />
+                                    <input type="text" name="anon_redirect" value="${sickbeard.ANON_REDIRECT}" class="form-control input-sm input300" autocapitalize="off" />
                                     <div class="clear-left"><p>backlink protection via anonymizer service, must end in "?"</p></div>
                                 </span>
                             </label>
@@ -593,7 +593,7 @@
                             <label>
                                 <span class="component-title">Proxy host</span>
                                 <span class="component-desc">
-                                    <input type="text" name="proxy_setting" value="${sickbeard.PROXY_SETTING}" class="form-control input-sm input300" />
+                                    <input type="text" name="proxy_setting" value="${sickbeard.PROXY_SETTING}" class="form-control input-sm input300" autocapitalize="off" />
                                     <div class="clear-left"><p>blank to disable or proxy to use when connecting to providers</p></div>
                             </label>
                         </div>
@@ -695,7 +695,7 @@
                             <label for="git_username">
                                 <span class="component-title">GitHub username</span>
                                 <span class="component-desc">
-                                    <input type="text" name="git_username" id="git_username" value="${sickbeard.GIT_USERNAME}" class="form-control input-sm input300" />
+                                    <input type="text" name="git_username" id="git_username" value="${sickbeard.GIT_USERNAME}" class="form-control input-sm input300" autocapitalize="off" autocomplete="no" />
                                     <div class="clear-left"><p>*** (REQUIRED FOR SUBMITTING ISSUES) ***</p></div>
                                 </span>
                             </label>
@@ -705,7 +705,7 @@
                             <label for="git_password">
                                 <span class="component-title">GitHub password</span>
                                 <span class="component-desc">
-                                    <input type="password" name="git_password" id="git_password" value="${sickbeard.GIT_PASSWORD}" class="form-control input-sm input300" />
+                                    <input type="password" name="git_password" id="git_password" value="${sickbeard.GIT_PASSWORD}" class="form-control input-sm input300" autocomplete="no" autocapitalize="off" />
                                     <div class="clear-left"><p>*** (REQUIRED FOR SUBMITTING ISSUES) ***</p></div>
                                 </span>
                             </label>
@@ -715,7 +715,7 @@
                             <label for="git_remote">
                                 <span class="component-title">GitHub remote for branch</span>
                                 <span class="component-desc">
-                                    <input type="text" name="git_remote" id="git_remote" value="${sickbeard.GIT_REMOTE}" class="form-control input-sm input300" />
+                                    <input type="text" name="git_remote" id="git_remote" value="${sickbeard.GIT_REMOTE}" class="form-control input-sm input300" autocapitalize="off" />
                                     <div class="clear-left"><p>default:origin. Access repo configured remotes (save then refresh browser)</p></div>
                                 </span>
                             </label>
@@ -725,7 +725,7 @@
                             <label>
                                 <span class="component-title">Git executable path</span>
                                 <span class="component-desc">
-                                    <input type="text" name="git_path" value="${sickbeard.GIT_PATH}" class="form-control input-sm input300" />
+                                    <input type="text" name="git_path" value="${sickbeard.GIT_PATH}" class="form-control input-sm input300" autocapitalize="off" />
                                     <div class="clear-left"><p>only needed if OS is unable to locate git from env</p></div>
                                 </span>
                             </label>
diff --git a/gui/slick/views/config_notifications.mako b/gui/slick/views/config_notifications.mako
index 69f41f94d3ea1f41285f0afa1ca61469978525ea..8f5f3feb8cac847ba78605ebc105dcf60df43ca2 100644
--- a/gui/slick/views/config_notifications.mako
+++ b/gui/slick/views/config_notifications.mako
@@ -109,7 +109,7 @@
                             <div class="field-pair">
                                 <label for="kodi_host">
                                     <span class="component-title">KODI IP:Port</span>
-                                    <input type="text" name="kodi_host" id="kodi_host" value="${sickbeard.KODI_HOST}" class="form-control input-sm input350" />
+                                    <input type="text" name="kodi_host" id="kodi_host" value="${sickbeard.KODI_HOST}" class="form-control input-sm input350" autocapitalize="off" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -123,7 +123,7 @@
                             <div class="field-pair">
                                 <label for="kodi_username">
                                     <span class="component-title">KODI username</span>
-                                    <input type="text" name="kodi_username" id="kodi_username" value="${sickbeard.KODI_USERNAME}" class="form-control input-sm input250" />
+                                    <input type="text" name="kodi_username" id="kodi_username" value="${sickbeard.KODI_USERNAME}" class="form-control input-sm input250" autocapitalize="off" autocomplete="no" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -133,7 +133,7 @@
                             <div class="field-pair">
                                 <label for="kodi_password">
                                     <span class="component-title">KODI password</span>
-                                    <input type="password" name="kodi_password" id="kodi_password" value="${sickbeard.KODI_PASSWORD}" class="form-control input-sm input250" />
+                                    <input type="password" name="kodi_password" id="kodi_password" value="${sickbeard.KODI_PASSWORD}" class="form-control input-sm input250" autocomplete="no" autocapitalize="off" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -172,7 +172,7 @@
                             <div class="field-pair">
                                 <label for="plex_server_token">
                                     <span class="component-title">Plex Media Server Auth Token</span>
-                                    <input type="text" name="plex_server_token" id="plex_server_token" value="${sickbeard.PLEX_SERVER_TOKEN}" class="form-control input-sm input250" />
+                                    <input type="text" name="plex_server_token" id="plex_server_token" value="${sickbeard.PLEX_SERVER_TOKEN}" class="form-control input-sm input250" autocapitalize="off" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -188,7 +188,7 @@
                                     <label for="plex_username">
                                         <span class="component-title">Server Username</span>
                                         <span class="component-desc">
-                                            <input type="text" name="plex_username" id="plex_username" value="${sickbeard.PLEX_USERNAME}" class="form-control input-sm input250" />
+                                            <input type="text" name="plex_username" id="plex_username" value="${sickbeard.PLEX_USERNAME}" class="form-control input-sm input250" autocapitalize="off" autocomplete="no" />
                                             <p>blank = no authentication</p>
                                         </span>
                                     </label>
@@ -197,7 +197,7 @@
                                     <label for="plex_password">
                                         <span class="component-title">Server/client password</span>
                                         <span class="component-desc">
-                                            <input type="password" name="plex_password" id="plex_password" value="${'*' * len(sickbeard.PLEX_PASSWORD)}" class="form-control input-sm input250" />
+                                            <input type="password" name="plex_password" id="plex_password" value="${'*' * len(sickbeard.PLEX_PASSWORD)}" class="form-control input-sm input250" autocomplete="no" autocapitalize="off" />
                                             <p>blank = no authentication</p>
                                         </span>
                                     </label>
@@ -219,7 +219,7 @@
                                         <label for="plex_server_host">
                                             <span class="component-title">Plex Media Server IP:Port</span>
                                             <span class="component-desc">
-                                                <input type="text" name="plex_server_host" id="plex_server_host" value="${re.sub(r'\b,\b', ', ', sickbeard.PLEX_SERVER_HOST)}" class="form-control input-sm input350" />
+                                                <input type="text" name="plex_server_host" id="plex_server_host" value="${re.sub(r'\b,\b', ', ', sickbeard.PLEX_SERVER_HOST)}" class="form-control input-sm input350" autocapitalize="off" />
                                                 <div class="clear-left">
                                                     <p>one or more hosts running Plex Media Server<br>(eg. 192.168.1.1:32400, 192.168.1.2:32400)</p>
                                                 </div>
@@ -287,7 +287,7 @@
                                 <label for="plex_host">
                                     <span class="component-title">Plex Client IP:Port</span>
                                     <span class="component-desc">
-                                        <input type="text" name="plex_host" id="plex_host" value="${sickbeard.PLEX_HOST}" class="form-control input-sm input350" />
+                                        <input type="text" name="plex_host" id="plex_host" value="${sickbeard.PLEX_HOST}" class="form-control input-sm input350" autocapitalize="off" />
                                         <div class="clear-left">
                                             <p>one or more hosts running Plex client<br>(eg. 192.168.1.100:3000, 192.168.1.101:3000)</p>
                                         </div>
@@ -299,7 +299,7 @@
                                     <label for="plex_username">
                                         <span class="component-title">Server Username</span>
                                         <span class="component-desc">
-                                            <input type="text" name="plex_client_username" id="plex_client_username" value="${sickbeard.PLEX_CLIENT_USERNAME}" class="form-control input-sm input250" />
+                                            <input type="text" name="plex_client_username" id="plex_client_username" value="${sickbeard.PLEX_CLIENT_USERNAME}" class="form-control input-sm input250" autocapitalize="off" autocomplete="no" />
                                             <p>blank = no authentication</p>
                                         </span>
                                     </label>
@@ -308,7 +308,7 @@
                                     <label for="plex_client_password">
                                         <span class="component-title">Client Password</span>
                                         <span class="component-desc">
-                                            <input type="password" name="plex_client_password" id="plex_client_password" value="${'*' * len(sickbeard.PLEX_CLIENT_PASSWORD)}" class="form-control input-sm input250" />
+                                            <input type="password" name="plex_client_password" id="plex_client_password" value="${'*' * len(sickbeard.PLEX_CLIENT_PASSWORD)}" class="form-control input-sm input250" autocomplete="no" autocapitalize="off" />
                                             <p>blank = no authentication</p>
                                         </span>
                                     </label>
@@ -346,7 +346,7 @@
                             <div class="field-pair">
                                 <label for="emby_host">
                                     <span class="component-title">Emby IP:Port</span>
-                                    <input type="text" name="emby_host" id="emby_host" value="${sickbeard.EMBY_HOST}" class="form-control input-sm input250" />
+                                    <input type="text" name="emby_host" id="emby_host" value="${sickbeard.EMBY_HOST}" class="form-control input-sm input250" autocapitalize="off" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -356,7 +356,7 @@
                             <div class="field-pair">
                                 <label for="emby_apikey">
                                     <span class="component-title">Emby API Key</span>
-                                    <input type="text" name="emby_apikey" id="emby_apikey" value="${sickbeard.EMBY_APIKEY}" class="form-control input-sm input250" />
+                                    <input type="text" name="emby_apikey" id="emby_apikey" value="${sickbeard.EMBY_APIKEY}" class="form-control input-sm input250" autocapitalize="off" />
                                 </label>
                             </div>
                             <div class="testNotification" id="testEMBY-result">Click below to test.</div>
@@ -389,7 +389,7 @@
                             <div class="field-pair">
                                 <label for="nmj_host">
                                     <span class="component-title">Popcorn IP address</span>
-                                    <input type="text" name="nmj_host" id="nmj_host" value="${sickbeard.NMJ_HOST}" class="form-control input-sm input250" />
+                                    <input type="text" name="nmj_host" id="nmj_host" value="${sickbeard.NMJ_HOST}" class="form-control input-sm input250" autocapitalize="off" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -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 is 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]} autocapitalize="off" />
                                 </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 is 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]} autocapitalize="off" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -455,7 +455,7 @@
                             <div class="field-pair">
                                 <label for="nmjv2_host">
                                     <span class="component-title">Popcorn IP address</span>
-                                    <input type="text" name="nmjv2_host" id="nmjv2_host" value="${sickbeard.NMJv2_HOST}" class="form-control input-sm input250" />
+                                    <input type="text" name="nmjv2_host" id="nmjv2_host" value="${sickbeard.NMJv2_HOST}" class="form-control input-sm input250" autocapitalize="off" />
                                 </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 is 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]} autocapitalize="off" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -633,7 +633,7 @@
                             <div class="field-pair">
                                 <label for="pytivo_host">
                                     <span class="component-title">pyTivo IP:Port</span>
-                                    <input type="text" name="pytivo_host" id="pytivo_host" value="${sickbeard.PYTIVO_HOST}" class="form-control input-sm input250" />
+                                    <input type="text" name="pytivo_host" id="pytivo_host" value="${sickbeard.PYTIVO_HOST}" class="form-control input-sm input250" autocapitalize="off" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -643,7 +643,7 @@
                             <div class="field-pair">
                                 <label for="pytivo_share_name">
                                     <span class="component-title">pyTivo share name</span>
-                                    <input type="text" name="pytivo_share_name" id="pytivo_share_name" value="${sickbeard.PYTIVO_SHARE_NAME}" class="form-control input-sm input250" />
+                                    <input type="text" name="pytivo_share_name" id="pytivo_share_name" value="${sickbeard.PYTIVO_SHARE_NAME}" class="form-control input-sm input250" autocapitalize="off" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -653,7 +653,7 @@
                             <div class="field-pair">
                                 <label for="pytivo_tivo_name">
                                     <span class="component-title">Tivo name</span>
-                                    <input type="text" name="pytivo_tivo_name" id="pytivo_tivo_name" value="${sickbeard.PYTIVO_TIVO_NAME}" class="form-control input-sm input250" />
+                                    <input type="text" name="pytivo_tivo_name" id="pytivo_tivo_name" value="${sickbeard.PYTIVO_TIVO_NAME}" class="form-control input-sm input250" autocapitalize="off" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -718,7 +718,7 @@
                             <div class="field-pair">
                                 <label for="growl_host">
                                     <span class="component-title">Growl IP:Port</span>
-                                    <input type="text" name="growl_host" id="growl_host" value="${sickbeard.GROWL_HOST}" class="form-control input-sm input250" />
+                                    <input type="text" name="growl_host" id="growl_host" value="${sickbeard.GROWL_HOST}" class="form-control input-sm input250" autocapitalize="off" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -728,7 +728,7 @@
                             <div class="field-pair">
                                 <label for="growl_password">
                                     <span class="component-title">Growl password</span>
-                                    <input type="password" name="growl_password" id="growl_password" value="${sickbeard.GROWL_PASSWORD}" class="form-control input-sm input250" />
+                                    <input type="password" name="growl_password" id="growl_password" value="${sickbeard.GROWL_PASSWORD}" class="form-control input-sm input250" autocomplete="no" autocapitalize="off" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -796,13 +796,13 @@
                             <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" />
+                                     <input type="text" name="prowl_message_title" id="prowl_message_title" value="${sickbeard.PROWL_MESSAGE_TITLE}" class="form-control input-sm input250" autocapitalize="off" />
                                  </label>
                             </div>
                             <div class="field-pair">
                                 <label for="prowl_api">
                                     <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" />
+                                    <input type="text" name="prowl_api" id="prowl_api" value="${sickbeard.PROWL_API}" class="form-control input-sm input250" autocapitalize="off" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -822,7 +822,7 @@
                                 </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" />
+                                    <input type="text" name="prowl_show_list" id="prowl_show_list" class="form-control input-sm input350" autocapitalize="off" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -962,7 +962,7 @@
                             <div class="field-pair">
                                 <label for="pushover_userkey">
                                     <span class="component-title">Pushover key</span>
-                                    <input type="text" name="pushover_userkey" id="pushover_userkey" value="${sickbeard.PUSHOVER_USERKEY}" class="form-control input-sm input250" />
+                                    <input type="text" name="pushover_userkey" id="pushover_userkey" value="${sickbeard.PUSHOVER_USERKEY}" class="form-control input-sm input250" autocapitalize="off" autocomplete="no" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -972,7 +972,7 @@
                             <div class="field-pair">
                                 <label for="pushover_apikey">
                                     <span class="component-title">Pushover API key</span>
-                                    <input type="text" name="pushover_apikey" id="pushover_apikey" value="${sickbeard.PUSHOVER_APIKEY}" class="form-control input-sm input250" />
+                                    <input type="text" name="pushover_apikey" id="pushover_apikey" value="${sickbeard.PUSHOVER_APIKEY}" class="form-control input-sm input250" autocapitalize="off" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -982,7 +982,7 @@
                             <div class="field-pair">
                                 <label for="pushover_device">
                                     <span class="component-title">Pushover devices</span>
-                                    <input type="text" name="pushover_device" id="pushover_device" value="${sickbeard.PUSHOVER_DEVICE}" class="form-control input-sm input250" />
+                                    <input type="text" name="pushover_device" id="pushover_device" value="${sickbeard.PUSHOVER_DEVICE}" class="form-control input-sm input250" autocapitalize="off" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -1079,7 +1079,7 @@
                             <div class="field-pair">
                                 <label for="boxcar2_accesstoken">
                                     <span class="component-title">Boxcar2 access token</span>
-                                    <input type="text" name="boxcar2_accesstoken" id="boxcar2_accesstoken" value="${sickbeard.BOXCAR2_ACCESSTOKEN}" class="form-control input-sm input250" />
+                                    <input type="text" name="boxcar2_accesstoken" id="boxcar2_accesstoken" value="${sickbeard.BOXCAR2_ACCESSTOKEN}" class="form-control input-sm input250" autocapitalize="off" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -1142,7 +1142,7 @@
                             <div class="field-pair">
                                 <label for="nma_api">
                                        <span class="component-title">NMA API key:</span>
-                                    <input type="text" name="nma_api" id="nma_api" value="${sickbeard.NMA_API}" class="form-control input-sm input350" />
+                                    <input type="text" name="nma_api" id="nma_api" value="${sickbeard.NMA_API}" class="form-control input-sm input350" autocapitalize="off" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -1221,7 +1221,7 @@
                             <div class="field-pair">
                                 <label for="pushalot_authorizationtoken">
                                     <span class="component-title">Pushalot authorization token</span>
-                                    <input type="text" name="pushalot_authorizationtoken" id="pushalot_authorizationtoken" value="${sickbeard.PUSHALOT_AUTHORIZATIONTOKEN}" class="form-control input-sm input350" />
+                                    <input type="text" name="pushalot_authorizationtoken" id="pushalot_authorizationtoken" value="${sickbeard.PUSHALOT_AUTHORIZATIONTOKEN}" class="form-control input-sm input350" autocapitalize="off" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -1284,7 +1284,7 @@
                             <div class="field-pair">
                                 <label for="pushbullet_api">
                                     <span class="component-title">Pushbullet API key</span>
-                                    <input type="text" name="pushbullet_api" id="pushbullet_api" value="${sickbeard.PUSHBULLET_API}" class="form-control input-sm input350" />
+                                    <input type="text" name="pushbullet_api" id="pushbullet_api" value="${sickbeard.PUSHBULLET_API}" class="form-control input-sm input350" autocapitalize="off" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -1358,7 +1358,7 @@
                             <div class="field-pair">
                                 <label for="freemobile_id">
                                     <span class="component-title">Free Mobile customer ID</span>
-                                    <input type="text" name="freemobile_id" id="freemobile_id" value="${sickbeard.FREEMOBILE_ID}" class="form-control input-sm input250" />
+                                    <input type="text" name="freemobile_id" id="freemobile_id" value="${sickbeard.FREEMOBILE_ID}" class="form-control input-sm input250" autocapitalize="off" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -1368,7 +1368,7 @@
                             <div class="field-pair">
                                 <label for="freemobile_password">
                                     <span class="component-title">Free Mobile API Key</span>
-                                    <input type="text" name="freemobile_apikey" id="freemobile_apikey" value="${sickbeard.FREEMOBILE_APIKEY}" class="form-control input-sm input250" />
+                                    <input type="text" name="freemobile_apikey" id="freemobile_apikey" value="${sickbeard.FREEMOBILE_APIKEY}" class="form-control input-sm input250" autocapitalize="off" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -1447,7 +1447,7 @@
                             <div class="field-pair">
                                 <label for="twitter_dmto">
                                     <span class="component-title">Send DM to</span>
-                                    <input type="text" name="twitter_dmto" id="twitter_dmto" value="${sickbeard.TWITTER_DMTO}" class="form-control input-sm input250" />
+                                    <input type="text" name="twitter_dmto" id="twitter_dmto" value="${sickbeard.TWITTER_DMTO}" class="form-control input-sm input250" autocapitalize="off" />
                                 </label>
                                 <p>
                                     <span class="component-desc">Twitter account to send Direct Messages to (must follow you)</span>
@@ -1468,7 +1468,7 @@
                                 </label>
                                 <label>
                                     <span style="font-size: 11px;">Enter the key Twitter gave you below, and click "Verify Key".<br><br></span>
-                                    <input type="text" id="twitter_key" value="" class="form-control input-sm input350" />
+                                    <input type="text" id="twitter_key" value="" class="form-control input-sm input350" autocapitalize="off" />
                                     <input class="btn btn-inline" type="button" value="Verify Key" id="twitterStep2" />
                                 </label>
                             </div>
@@ -1509,7 +1509,7 @@
                             <div class="field-pair">
                                 <label for="trakt_username">
                                     <span class="component-title">Trakt username</span>
-                                    <input type="text" name="trakt_username" id="trakt_username" value="${sickbeard.TRAKT_USERNAME}" class="form-control input-sm input250" />
+                                    <input type="text" name="trakt_username" id="trakt_username" value="${sickbeard.TRAKT_USERNAME}" class="form-control input-sm input250" autocapitalize="off" autocomplete="no" />
                                 </label>
                                 <p>
                                     <span class="component-desc">username of your Trakt account.</span>
@@ -1520,7 +1520,7 @@
                             <div class="field-pair">
                                 <label for="trakt_pin">
                                     <span class="component-title">Trakt PIN</span>
-                                    <input type="text" name="trakt_pin" id="trakt_pin" value="" class="form-control input-sm input250" />
+                                    <input type="text" name="trakt_pin" id="trakt_pin" value="" class="form-control input-sm input250" autocapitalize="off" />
                                 </label>
                                 <p>
                                     <span class="component-desc">PIN code to authorize SickRage to access Trakt on your behalf.</span>
@@ -1530,7 +1530,7 @@
                             <div class="field-pair">
                                 <label for="trakt_timeout">
                                     <span class="component-title">API Timeout</span>
-                                    <input type="text" name="trakt_timeout" id="trakt_timeout" value="${sickbeard.TRAKT_TIMEOUT}" class="form-control input-sm input75" />
+                                    <input type="text" name="trakt_timeout" id="trakt_timeout" value="${sickbeard.TRAKT_TIMEOUT}" class="form-control input-sm input75" autocapitalize="off" />
                                 </label>
                                 <p>
                                     <span class="component-desc">
@@ -1635,7 +1635,7 @@
                             <div class="field-pair">
                                 <label for="trakt_blacklist_name">
                                     <span class="component-title">Trakt blackList name</span>
-                                    <input type="text" name="trakt_blacklist_name" id="trakt_blacklist_name" value="${sickbeard.TRAKT_BLACKLIST_NAME}" class="form-control input-sm input150" />
+                                    <input type="text" name="trakt_blacklist_name" id="trakt_blacklist_name" value="${sickbeard.TRAKT_BLACKLIST_NAME}" class="form-control input-sm input150" autocapitalize="off" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -1697,7 +1697,7 @@
                             <div class="field-pair">
                                 <label for="email_host">
                                     <span class="component-title">SMTP host</span>
-                                    <input type="text" name="email_host" id="email_host" value="${sickbeard.EMAIL_HOST}" class="form-control input-sm input250" />
+                                    <input type="text" name="email_host" id="email_host" value="${sickbeard.EMAIL_HOST}" class="form-control input-sm input250" autocapitalize="off" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -1707,7 +1707,7 @@
                             <div class="field-pair">
                                 <label for="email_port">
                                     <span class="component-title">SMTP port</span>
-                                    <input type="text" name="email_port" id="email_port" value="${sickbeard.EMAIL_PORT}" class="form-control input-sm input75" />
+                                    <input type="text" name="email_port" id="email_port" value="${sickbeard.EMAIL_PORT}" class="form-control input-sm input75" autocapitalize="off" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -1717,7 +1717,7 @@
                             <div class="field-pair">
                                 <label for="email_from">
                                     <span class="component-title">SMTP from</span>
-                                    <input type="text" name="email_from" id="email_from" value="${sickbeard.EMAIL_FROM}" class="form-control input-sm input250" />
+                                    <input type="text" name="email_from" id="email_from" value="${sickbeard.EMAIL_FROM}" class="form-control input-sm input250" autocapitalize="off" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -1736,7 +1736,7 @@
                             <div class="field-pair">
                                 <label for="email_user">
                                     <span class="component-title">SMTP user</span>
-                                    <input type="text" name="email_user" id="email_user" value="${sickbeard.EMAIL_USER}" class="form-control input-sm input250" />
+                                    <input type="text" name="email_user" id="email_user" value="${sickbeard.EMAIL_USER}" class="form-control input-sm input250" autocapitalize="off" autocomplete="no" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -1746,7 +1746,7 @@
                             <div class="field-pair">
                                 <label for="email_password">
                                     <span class="component-title">SMTP password</span>
-                                    <input type="password" name="email_password" id="email_password" value="${sickbeard.EMAIL_PASSWORD}" class="form-control input-sm input250" />
+                                    <input type="password" name="email_password" id="email_password" value="${sickbeard.EMAIL_PASSWORD}" class="form-control input-sm input250" autocomplete="no" autocapitalize="off" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -1756,7 +1756,7 @@
                             <div class="field-pair">
                                 <label for="email_list">
                                     <span class="component-title">Global email list</span>
-                                    <input type="text" name="email_list" id="email_list" value="${sickbeard.EMAIL_LIST}" class="form-control input-sm input350" />
+                                    <input type="text" name="email_list" id="email_list" value="${sickbeard.EMAIL_LIST}" class="form-control input-sm input350" autocapitalize="off" />
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
@@ -1773,7 +1773,7 @@
                                 </label>
                                 <label>
                                     <span class="component-title">&nbsp;</span>
-                                    <input type="text" name="email_show_list" id="email_show_list" class="form-control input-sm input350" />
+                                    <input type="text" name="email_show_list" id="email_show_list" class="form-control input-sm input350" autocapitalize="off" />
                                 </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 376dbe23c621601ddae41f4981c8671dc1627db7..7920b34bbbcc2c142f397dfb2a4b81d6bde3e74e 100644
--- a/gui/slick/views/config_postProcessing.mako
+++ b/gui/slick/views/config_postProcessing.mako
@@ -9,6 +9,7 @@
     from sickbeard import metadata
     from sickbeard.metadata.generic import GenericMetadata
     from sickbeard import naming
+    from sickrage.helper.encoding import ek
 %>
 <%block name="content">
 <div id="content960">
@@ -46,7 +47,7 @@
                         <div class="field-pair">
                             <label class="nocheck" for="tv_download_dir">
                                 <span class="component-title">Post Processing Dir</span>
-                                <input type="text" name="tv_download_dir" id="tv_download_dir" value="${sickbeard.TV_DOWNLOAD_DIR}" class="form-control input-sm input350" />
+                                <input type="text" name="tv_download_dir" id="tv_download_dir" value="${sickbeard.TV_DOWNLOAD_DIR}" class="form-control input-sm input350" autocapitalize="off" />
                             </label>
                             <label class="nocheck">
                                 <span class="component-title">&nbsp;</span>
@@ -98,7 +99,7 @@
                         <div class="field-pair">
                             <label class="nocheck">
                                 <span class="component-title">Sync File Extensions</span>
-                                <input type="text" name="sync_files" id="sync_files" value="${sickbeard.SYNC_FILES}" class="form-control input-sm input350" />
+                                <input type="text" name="sync_files" id="sync_files" value="${sickbeard.SYNC_FILES}" class="form-control input-sm input350" autocapitalize="off" />
                             </label>
                             <label class="nocheck">
                                 <span class="component-title">&nbsp;</span>
@@ -144,7 +145,7 @@
                         <div class="field-pair">
                             <label class="nocheck">
                                 <span class="component-title">Allowed associated file extensions</span>
-                                <input type="text" name="allowed_extensions" id="allowed_extensions" value="${sickbeard.ALLOWED_EXTENSIONS}" class="form-control input-sm input350" />
+                                <input type="text" name="allowed_extensions" id="allowed_extensions" value="${sickbeard.ALLOWED_EXTENSIONS}" class="form-control input-sm input350" autocapitalize="off" />
                             </label>
                             <label class="nocheck">
                                 <span class="component-title">&nbsp;</span>
@@ -240,7 +241,7 @@
                         <div class="field-pair">
                             <label class="nocheck">
                                 <span class="component-title">Extra Scripts</span>
-                                <input type="text" name="extra_scripts" value="${'|'.join(sickbeard.EXTRA_SCRIPTS)}" class="form-control input-sm input350" />
+                                <input type="text" name="extra_scripts" value="${'|'.join(sickbeard.EXTRA_SCRIPTS)}" class="form-control input-sm input350" autocapitalize="off" />
                             </label>
                             <label class="nocheck">
                                 <span class="component-title">&nbsp;</span>
@@ -269,7 +270,7 @@
                                             % if cur_preset == sickbeard.NAMING_PATTERN:
                                                 <% is_custom = False %>
                                             % endif
-                                            <option id="${cur_preset}" ${('', 'selected="selected"')[sickbeard.NAMING_PATTERN == cur_preset]}>${os.path.join(tmp['dir'], tmp['name'])}</option>
+                                            <option id="${cur_preset}" ${('', 'selected="selected"')[sickbeard.NAMING_PATTERN == cur_preset]}>${ek(os.path.join, tmp['dir'], tmp['name'])}</option>
                                         % endfor
                                         <option id="${sickbeard.NAMING_PATTERN}" ${('', 'selected="selected"')[bool(is_custom)]}>Custom...</option>
                                     </select>
@@ -284,7 +285,7 @@
                                         &nbsp;
                                     </span>
                                     <span class="component-desc">
-                                        <input type="text" name="naming_pattern" id="naming_pattern" value="${sickbeard.NAMING_PATTERN}" class="form-control input-sm input350" />
+                                        <input type="text" name="naming_pattern" id="naming_pattern" value="${sickbeard.NAMING_PATTERN}" class="form-control input-sm input350" autocapitalize="off" />
                                         <img src="${srRoot}/images/legend16.png" width="16" height="16" alt="[Toggle Key]" id="show_naming_key" title="Toggle Naming Legend" class="legend" class="legend" />
                                     </span>
                                 </label>
@@ -393,7 +394,7 @@
                                           <td>&nbsp;</td>
                                           <td>%Y</td>
                                           <td>${datetime.date.today().year}</td>
-                                        </tr>                                  
+                                        </tr>
                                         <tr>
                                           <td class="align-right"><b>Post-Processing Date:</b></td>
                                           <td>%CM</td>
@@ -408,7 +409,7 @@
                                           <td>&nbsp;</td>
                                           <td>%CY</td>
                                           <td>${datetime.date.today().year}</td>
-                                        </tr>                                  
+                                        </tr>
                                         <tr>
                                           <td class="align-right"><b>Quality:</b></td>
                                           <td>%QN</td>
@@ -521,7 +522,7 @@
                                                 % if cur_preset == sickbeard.NAMING_ABD_PATTERN:
                                                     <% is_abd_custom = False %>
                                                 % endif
-                                                <option id="${cur_preset}" ${('', 'selected="selected"')[sickbeard.NAMING_ABD_PATTERN == cur_preset]}>${os.path.join(tmp['dir'], tmp['name'])}</option>
+                                                <option id="${cur_preset}" ${('', 'selected="selected"')[sickbeard.NAMING_ABD_PATTERN == cur_preset]}>${ek(os.path.join, tmp['dir'], tmp['name'])}</option>
                                             % endfor
                                             <option id="${sickbeard.NAMING_ABD_PATTERN}" ${('', 'selected="selected"')[bool(is_abd_custom)]}>Custom...</option>
                                         </select>
@@ -536,7 +537,7 @@
                                             &nbsp;
                                         </span>
                                         <span class="component-desc">
-                                            <input type="text" name="naming_abd_pattern" id="naming_abd_pattern" value="${sickbeard.NAMING_ABD_PATTERN}" class="form-control input-sm input350" />
+                                            <input type="text" name="naming_abd_pattern" id="naming_abd_pattern" value="${sickbeard.NAMING_ABD_PATTERN}" class="form-control input-sm input350" autocapitalize="off" />
                                             <img src="${srRoot}/images/legend16.png" width="16" height="16" alt="[Toggle Key]" id="show_naming_abd_key" title="Toggle ABD Naming Legend" class="legend" />
                                         </span>
                                     </label>
@@ -698,7 +699,7 @@
                                                 % if cur_preset == sickbeard.NAMING_SPORTS_PATTERN:
                                                     <% is_sports_custom = False %>
                                                 % endif
-                                                <option id="${cur_preset}" ${('', 'selected="selected"')[NAMING_SPORTS_PATTERN == cur_preset]}>${os.path.join(tmp['dir'], tmp['name'])}</option>
+                                                <option id="${cur_preset}" ${('', 'selected="selected"')[NAMING_SPORTS_PATTERN == cur_preset]}>${ek(os.path.join, tmp['dir'], tmp['name'])}</option>
                                             % endfor
                                             <option id="${sickbeard.NAMING_SPORTS_PATTERN}" ${('', 'selected="selected"')[bool(is_sports_custom)]}>Custom...</option>
                                         </select>
@@ -713,7 +714,7 @@
                                             &nbsp;
                                         </span>
                                         <span class="component-desc">
-                                            <input type="text" name="naming_sports_pattern" id="naming_sports_pattern" value="${sickbeard.NAMING_SPORTS_PATTERN}" class="form-control input-sm input350" />
+                                            <input type="text" name="naming_sports_pattern" id="naming_sports_pattern" value="${sickbeard.NAMING_SPORTS_PATTERN}" class="form-control input-sm input350" autocapitalize="off" />
                                             <img src="${srRoot}/images/legend16.png" width="16" height="16" alt="[Toggle Key]" id="show_naming_sports_key" title="Toggle Sports Naming Legend" class="legend" />
                                         </span>
                                     </label>
@@ -876,7 +877,7 @@
                                                 % if cur_preset == sickbeard.NAMING_ANIME_PATTERN:
                                                     <% is_anime_custom = False %>
                                                 % endif
-                                                <option id="${cur_preset}" ${('', 'selected="selected"')[cur_preset == sickbeard.NAMING_ANIME_PATTERN]}>${os.path.join(tmp['dir'], tmp['name'])}</option>
+                                                <option id="${cur_preset}" ${('', 'selected="selected"')[cur_preset == sickbeard.NAMING_ANIME_PATTERN]}>${ek(os.path.join, tmp['dir'], tmp['name'])}</option>
                                             % endfor
                                             <option id="${sickbeard.NAMING_ANIME_PATTERN}" ${('', 'selected="selected"')[bool(is_anime_custom)]}>Custom...</option>
                                         </select>
@@ -891,7 +892,7 @@
                                             &nbsp;
                                         </span>
                                         <span class="component-desc">
-                                            <input type="text" name="naming_anime_pattern" id="naming_anime_pattern" value="${sickbeard.NAMING_ANIME_PATTERN}" class="form-control input-sm input350" />
+                                            <input type="text" name="naming_anime_pattern" id="naming_anime_pattern" value="${sickbeard.NAMING_ANIME_PATTERN}" class="form-control input-sm input350" autocapitalize="off" />
                                             <img src="${srRoot}/images/legend16.png" width="16" height="16" alt="[Toggle Key]" id="show_naming_anime_key" title="Toggle Anime Naming Legend" class="legend" />
                                         </span>
                                     </label>
diff --git a/gui/slick/views/config_providers.mako b/gui/slick/views/config_providers.mako
index 40808db9b0d223fc52864c26f4467b9b0cfc0a23..66bfc399cae32bd3f710fbd410f999083b01317d 100644
--- a/gui/slick/views/config_providers.mako
+++ b/gui/slick/views/config_providers.mako
@@ -140,7 +140,7 @@ $('#config-components').tabs();
                             <label for="${curNewznabProvider.getID()}_url">
                                 <span class="component-title">URL:</span>
                                 <span class="component-desc">
-                                    <input type="text" id="${curNewznabProvider.getID()}_url" value="${curNewznabProvider.url}" class="form-control input-sm input350" disabled/>
+                                    <input type="text" id="${curNewznabProvider.getID()}_url" value="${curNewznabProvider.url}" class="form-control input-sm input350" disabled autocapitalize="off" />
                                 </span>
                             </label>
                         </div>
@@ -148,7 +148,7 @@ $('#config-components').tabs();
                             <label for="${curNewznabProvider.getID()}_hash">
                                 <span class="component-title">API key:</span>
                                 <span class="component-desc">
-                                    <input type="text" id="${curNewznabProvider.getID()}_hash" value="${curNewznabProvider.key}" newznab_name="${curNewznabProvider.getID()}_hash" class="newznab_key form-control input-sm input350" />
+                                    <input type="text" id="${curNewznabProvider.getID()}_hash" value="${curNewznabProvider.key}" newznab_name="${curNewznabProvider.getID()}_hash" class="newznab_key form-control input-sm input350" autocapitalize="off" />
                                 </span>
                             </label>
                         </div>
@@ -223,7 +223,7 @@ $('#config-components').tabs();
                             <label for="${curNzbProvider.getID()}_username">
                                 <span class="component-title">Username:</span>
                                 <span class="component-desc">
-                                    <input type="text" name="${curNzbProvider.getID()}_username" value="${curNzbProvider.username}" class="form-control input-sm input350" />
+                                    <input type="text" name="${curNzbProvider.getID()}_username" value="${curNzbProvider.username}" class="form-control input-sm input350" autocapitalize="off" autocomplete="no" />
                                 </span>
                             </label>
                         </div>
@@ -234,7 +234,7 @@ $('#config-components').tabs();
                             <label for="${curNzbProvider.getID()}_api_key">
                                 <span class="component-title">API key:</span>
                                 <span class="component-desc">
-                                    <input type="text" name="${curNzbProvider.getID()}_api_key" value="${curNzbProvider.api_key}" class="form-control input-sm input350" />
+                                    <input type="text" name="${curNzbProvider.getID()}_api_key" value="${curNzbProvider.api_key}" class="form-control input-sm input350" autocapitalize="off" />
                                 </span>
                             </label>
                         </div>
@@ -311,7 +311,7 @@ $('#config-components').tabs();
                             <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" />
+                                    <input type="text" name="${curTorrentProvider.getID()}_custom_url" id="${curTorrentProvider.getID()}_custom_url" value="${curTorrentProvider.custom_url}" class="form-control input-sm input350" autocapitalize="off" />
                                 </span>
                             </label>
                             <label>
@@ -328,7 +328,7 @@ $('#config-components').tabs();
                             <label for="${curTorrentProvider.getID()}_api_key">
                                 <span class="component-title">Api key:</span>
                                 <span class="component-desc">
-                                    <input type="text" name="${curTorrentProvider.getID()}_api_key" id="${curTorrentProvider.getID()}_api_key" value="${curTorrentProvider.api_key}" class="form-control input-sm input350" />
+                                    <input type="text" name="${curTorrentProvider.getID()}_api_key" id="${curTorrentProvider.getID()}_api_key" value="${curTorrentProvider.api_key}" class="form-control input-sm input350" autocapitalize="off" />
                                 </span>
                             </label>
                         </div>
@@ -339,7 +339,7 @@ $('#config-components').tabs();
                             <label for="${curTorrentProvider.getID()}_digest">
                                 <span class="component-title">Digest:</span>
                                 <span class="component-desc">
-                                    <input type="text" name="${curTorrentProvider.getID()}_digest" id="${curTorrentProvider.getID()}_digest" value="${curTorrentProvider.digest}" class="form-control input-sm input350" />
+                                    <input type="text" name="${curTorrentProvider.getID()}_digest" id="${curTorrentProvider.getID()}_digest" value="${curTorrentProvider.digest}" class="form-control input-sm input350" autocapitalize="off" />
                                 </span>
                             </label>
                         </div>
@@ -350,7 +350,7 @@ $('#config-components').tabs();
                             <label for="${curTorrentProvider.getID()}_hash">
                                 <span class="component-title">Hash:</span>
                                 <span class="component-desc">
-                                    <input type="text" name="${curTorrentProvider.getID()}_hash" id="${curTorrentProvider.getID()}_hash" value="${curTorrentProvider.hash}" class="form-control input-sm input350" />
+                                    <input type="text" name="${curTorrentProvider.getID()}_hash" id="${curTorrentProvider.getID()}_hash" value="${curTorrentProvider.hash}" class="form-control input-sm input350" autocapitalize="off" />
                                 </span>
                             </label>
                         </div>
@@ -361,7 +361,7 @@ $('#config-components').tabs();
                             <label for="${curTorrentProvider.getID()}_username">
                                 <span class="component-title">Username:</span>
                                 <span class="component-desc">
-                                    <input type="text" name="${curTorrentProvider.getID()}_username" id="${curTorrentProvider.getID()}_username" value="${curTorrentProvider.username}" class="form-control input-sm input350" />
+                                    <input type="text" name="${curTorrentProvider.getID()}_username" id="${curTorrentProvider.getID()}_username" value="${curTorrentProvider.username}" class="form-control input-sm input350" autocapitalize="off" autocomplete="no" />
                                 </span>
                             </label>
                         </div>
@@ -372,7 +372,7 @@ $('#config-components').tabs();
                             <label for="${curTorrentProvider.getID()}_password">
                                 <span class="component-title">Password:</span>
                                 <span class="component-desc">
-                                    <input type="password" name="${curTorrentProvider.getID()}_password" id="${curTorrentProvider.getID()}_password" value="${curTorrentProvider.password}" class="form-control input-sm input350" />
+                                    <input type="password" name="${curTorrentProvider.getID()}_password" id="${curTorrentProvider.getID()}_password" value="${curTorrentProvider.password}" class="form-control input-sm input350" autocomplete="no" autocapitalize="off" />
                                 </span>
                             </label>
                         </div>
@@ -383,7 +383,7 @@ $('#config-components').tabs();
                             <label for="${curTorrentProvider.getID()}_passkey">
                                 <span class="component-title">Passkey:</span>
                                 <span class="component-desc">
-                                    <input type="text" name="${curTorrentProvider.getID()}_passkey" id="${curTorrentProvider.getID()}_passkey" value="${curTorrentProvider.passkey}" class="form-control input-sm input350" />
+                                    <input type="text" name="${curTorrentProvider.getID()}_passkey" id="${curTorrentProvider.getID()}_passkey" value="${curTorrentProvider.passkey}" class="form-control input-sm input350" autocapitalize="off" />
                                 </span>
                             </label>
                         </div>
@@ -394,7 +394,7 @@ $('#config-components').tabs();
                             <label for="${curTorrentProvider.getID()}_pin">
                                 <span class="component-title">Pin:</span>
                                 <span class="component-desc">
-                                    <input type="password" name="${curTorrentProvider.getID()}_pin" id="${curTorrentProvider.getID()}_pin" value="${curTorrentProvider.pin}" class="form-control input-sm input100" />
+                                    <input type="password" name="${curTorrentProvider.getID()}_pin" id="${curTorrentProvider.getID()}_pin" value="${curTorrentProvider.pin}" class="form-control input-sm input100" autocomplete="no" autocapitalize="off" />
                                 </span>
                             </label>
                         </div>
@@ -636,19 +636,19 @@ $('#config-components').tabs();
                         <div class="field-pair">
                             <label for="newznab_name">
                                 <span class="component-title">Provider name:</span>
-                                <input type="text" id="newznab_name" class="form-control input-sm input200" />
+                                <input type="text" id="newznab_name" class="form-control input-sm input200" autocapitalize="off" />
                             </label>
                         </div>
                         <div class="field-pair">
                             <label for="newznab_url">
                                 <span class="component-title">Site URL:</span>
-                                <input type="text" id="newznab_url" class="form-control input-sm input350" />
+                                <input type="text" id="newznab_url" class="form-control input-sm input350" autocapitalize="off" />
                             </label>
                         </div>
                         <div class="field-pair">
                             <label for="newznab_key">
                                 <span class="component-title">API key:</span>
-                                <input type="text" id="newznab_key" class="form-control input-sm input350" />
+                                <input type="text" id="newznab_key" class="form-control input-sm input350" autocapitalize="off" />
                             </label>
                             <label>
                                 <span class="component-title">&nbsp;</span>
@@ -712,19 +712,19 @@ $('#config-components').tabs();
                         <div class="field-pair">
                             <label for="torrentrss_name">
                                 <span class="component-title">Provider name:</span>
-                                <input type="text" id="torrentrss_name" class="form-control input-sm input200" />
+                                <input type="text" id="torrentrss_name" class="form-control input-sm input200" autocapitalize="off" />
                             </label>
                         </div>
                         <div class="field-pair">
                             <label for="torrentrss_url">
                                 <span class="component-title">RSS URL:</span>
-                                <input type="text" id="torrentrss_url" class="form-control input-sm input350" />
+                                <input type="text" id="torrentrss_url" class="form-control input-sm input350" autocapitalize="off" />
                             </label>
                         </div>
                         <div class="field-pair">
                             <label for="torrentrss_cookies">
                                 <span class="component-title">Cookies:</span>
-                                <input type="text" id="torrentrss_cookies" class="form-control input-sm input350" />
+                                <input type="text" id="torrentrss_cookies" class="form-control input-sm input350" autocapitalize="off" />
                             </label>
                             <label>
                                 <span class="component-title">&nbsp;</span>
@@ -734,7 +734,7 @@ $('#config-components').tabs();
                         <div class="field-pair">
                             <label for="torrentrss_titleTAG">
                                 <span class="component-title">Search element:</span>
-                                <input type="text" id="torrentrss_titleTAG" class="form-control input-sm input200" value="title"/>
+                                <input type="text" id="torrentrss_titleTAG" class="form-control input-sm input200" value="title" autocapitalize="off" />
                             </label>
                             <label>
                                 <span class="component-title">&nbsp;</span>
diff --git a/gui/slick/views/config_search.mako b/gui/slick/views/config_search.mako
index 61e3054dfd68e15001d3933a3efe8f28235f4c52..7df3b52c8eca25d423147d69b80fffa68ebe6610 100644
--- a/gui/slick/views/config_search.mako
+++ b/gui/slick/views/config_search.mako
@@ -65,7 +65,7 @@
                             <label>
                                 <span class="component-title">Backlog search day(s)</span>
                                 <span class="component-desc">
-                                    <input type="text" name="backlog_days" value="${sickbeard.BACKLOG_DAYS}" class="form-control input-sm input75" />
+                                    <input type="text" name="backlog_days" value="${sickbeard.BACKLOG_DAYS}" class="form-control input-sm input75" autocapitalize="off" />
                                     <p>number of day(s) that the "Forced Backlog Search" will cover (e.g. 7 Days)</p>
                                 </span>
                             </label>
@@ -75,7 +75,7 @@
                             <label>
                                 <span class="component-title">Backlog search frequency</span>
                                 <span class="component-desc">
-                                    <input type="text" name="backlog_frequency" value="${sickbeard.BACKLOG_FREQUENCY}" class="form-control input-sm input75" />
+                                    <input type="text" name="backlog_frequency" value="${sickbeard.BACKLOG_FREQUENCY}" class="form-control input-sm input75" autocapitalize="off" />
                                     <p>time in minutes between searches (min. ${sickbeard.MIN_BACKLOG_FREQUENCY})</p>
                                 </span>
                             </label>
@@ -85,7 +85,7 @@
                             <label>
                                 <span class="component-title">Daily search frequency</span>
                                 <span class="component-desc">
-                                    <input type="text" name="dailysearch_frequency" value="${sickbeard.DAILYSEARCH_FREQUENCY}" class="form-control input-sm input75" />
+                                    <input type="text" name="dailysearch_frequency" value="${sickbeard.DAILYSEARCH_FREQUENCY}" class="form-control input-sm input75" autocapitalize="off" />
                                     <p>time in minutes between searches (min. ${sickbeard.MIN_DAILYSEARCH_FREQUENCY})</p>
                                     </span>
                             </label>
@@ -95,7 +95,7 @@
                             <label>
                                 <span class="component-title">Usenet retention</span>
                                 <span class="component-desc">
-                                    <input type="text" name="usenet_retention" value="${sickbeard.USENET_RETENTION}" class="form-control input-sm input75" />
+                                    <input type="text" name="usenet_retention" value="${sickbeard.USENET_RETENTION}" class="form-control input-sm input75" autocapitalize="off" />
                                     <p>age limit in days for usenet articles to be used (e.g. 500)</p>
                                 </span>
                             </label>
@@ -105,7 +105,7 @@
                             <label>
                                 <span class="component-title">Ignore words</span>
                                 <span class="component-desc">
-                                    <input type="text" name="ignore_words" value="${sickbeard.IGNORE_WORDS}" class="form-control input-sm input350" />
+                                    <input type="text" name="ignore_words" value="${sickbeard.IGNORE_WORDS}" class="form-control input-sm input350" autocapitalize="off" />
                                     <div class="clear-left">results with one or more word from this list will be ignored<br>
                                     separate words with a comma, e.g. "word1,word2,word3"
                                     </div>
@@ -117,7 +117,7 @@
                             <label>
                                 <span class="component-title">Require words</span>
                                 <span class="component-desc">
-                                    <input type="text" name="require_words" value="${sickbeard.REQUIRE_WORDS}" class="form-control input-sm input350" />
+                                    <input type="text" name="require_words" value="${sickbeard.REQUIRE_WORDS}" class="form-control input-sm input350" autocapitalize="off" />
                                     <div class="clear-left">results with no word from this list will be ignored<br>
                                     separate words with a comma, e.g. "word1,word2,word3"
                                     </div>
@@ -129,7 +129,7 @@
                             <label>
                                 <span class="component-title">Trackers list</span>
                                 <span class="component-desc">
-                                    <input type="text" name="trackers_list" value="${sickbeard.TRACKERS_LIST}" class="form-control input-sm input350" />
+                                    <input type="text" name="trackers_list" value="${sickbeard.TRACKERS_LIST}" class="form-control input-sm input350" autocapitalize="off" />
                                     <div class="clear-left">Trackers that will be added to magnets without trackers<br>
                                     separate trackers with a comma, e.g. "tracker1,tracker2,tracker3"
                                     </div>
@@ -141,7 +141,7 @@
                             <label>
                                 <span class="component-title">Ignore language names in subbed results</span>
                                 <span class="component-desc">
-                                    <input type="text" name="ignored_subs_list" value="${sickbeard.IGNORED_SUBS_LIST}" class="form-control input-sm input350" />
+                                    <input type="text" name="ignored_subs_list" value="${sickbeard.IGNORED_SUBS_LIST}" class="form-control input-sm input350" autocapitalize="off" />
                                     <div class="clear-left">Ignore subbed releases based on language names <br>
                                     Example: "dk" will ignore words: dksub, dksubs, dksubbed, dksubed <br>
                                     separate languages with a comma, e.g. "lang1,lang2,lang3
@@ -229,7 +229,7 @@
                                 <label>
                                     <span class="component-title">Black hole folder location</span>
                                     <span class="component-desc">
-                                        <input type="text" name="nzb_dir" id="nzb_dir" value="${sickbeard.NZB_DIR}" class="form-control input-sm input350" />
+                                        <input type="text" name="nzb_dir" id="nzb_dir" value="${sickbeard.NZB_DIR}" class="form-control input-sm input350" autocapitalize="off" />
                                         <div class="clear-left"><p><b>.nzb</b> files are stored at this location for external software to find and use</p></div>
                                     </span>
                                 </label>
@@ -241,7 +241,7 @@
                                 <label>
                                     <span class="component-title">SABnzbd server URL</span>
                                     <span class="component-desc">
-                                        <input type="text" id="sab_host" name="sab_host" value="${sickbeard.SAB_HOST}" class="form-control input-sm input350" />
+                                        <input type="text" id="sab_host" name="sab_host" value="${sickbeard.SAB_HOST}" class="form-control input-sm input350" autocapitalize="off" />
                                         <div class="clear-left"><p>URL to your SABnzbd server (e.g. http://localhost:8080/)</p></div>
                                     </span>
                                 </label>
@@ -251,7 +251,7 @@
                                 <label>
                                     <span class="component-title">SABnzbd username</span>
                                     <span class="component-desc">
-                                        <input type="text" name="sab_username" id="sab_username" value="${sickbeard.SAB_USERNAME}" class="form-control input-sm input200" />
+                                        <input type="text" name="sab_username" id="sab_username" value="${sickbeard.SAB_USERNAME}" class="form-control input-sm input200" autocapitalize="off" autocomplete="no" />
                                         <p>(blank for none)</p>
                                     </span>
                                 </label>
@@ -261,7 +261,7 @@
                                 <label>
                                     <span class="component-title">SABnzbd password</span>
                                     <span class="component-desc">
-                                        <input type="password" name="sab_password" id="sab_password" value="${sickbeard.SAB_PASSWORD}" class="form-control input-sm input200" />
+                                        <input type="password" name="sab_password" id="sab_password" value="${sickbeard.SAB_PASSWORD}" class="form-control input-sm input200" autocomplete="no" autocapitalize="off" />
                                         <p>(blank for none)</p>
                                     </span>
                                 </label>
@@ -271,7 +271,7 @@
                                 <label>
                                     <span class="component-title">SABnzbd API key</span>
                                     <span class="component-desc">
-                                        <input type="text" name="sab_apikey" id="sab_apikey" value="${sickbeard.SAB_APIKEY}" class="form-control input-sm input350" />
+                                        <input type="text" name="sab_apikey" id="sab_apikey" value="${sickbeard.SAB_APIKEY}" class="form-control input-sm input350" autocapitalize="off" />
                                         <div class="clear-left"><p>locate at... SABnzbd Config -> General -> API Key</p></div>
                                     </span>
                                 </label>
@@ -281,7 +281,7 @@
                                 <label>
                                     <span class="component-title">Use SABnzbd category</span>
                                     <span class="component-desc">
-                                        <input type="text" name="sab_category" id="sab_category" value="${sickbeard.SAB_CATEGORY}" class="form-control input-sm input200" />
+                                        <input type="text" name="sab_category" id="sab_category" value="${sickbeard.SAB_CATEGORY}" class="form-control input-sm input200" autocapitalize="off" />
                                         <p>add downloads to this category (e.g. TV)</p>
                                     </span>
                                 </label>
@@ -291,7 +291,7 @@
                                 <label>
                                     <span class="component-title">Use SABnzbd category (backlog episodes)</span>
                                     <span class="component-desc">
-                                        <input type="text" name="sab_category_backlog" id="sab_category_backlog" value="${sickbeard.SAB_CATEGORY_BACKLOG}" class="form-control input-sm input200" />
+                                        <input type="text" name="sab_category_backlog" id="sab_category_backlog" value="${sickbeard.SAB_CATEGORY_BACKLOG}" class="form-control input-sm input200" autocapitalize="off" />
                                         <p>add downloads of old episodes to this category (e.g. TV)</p>
                                     </span>
                                 </label>
@@ -301,7 +301,7 @@
                                 <label>
                                     <span class="component-title">Use SABnzbd category for anime</span>
                                     <span class="component-desc">
-                                        <input type="text" name="sab_category_anime" id="sab_category_anime" value="${sickbeard.SAB_CATEGORY_ANIME}" class="form-control input-sm input200" />
+                                        <input type="text" name="sab_category_anime" id="sab_category_anime" value="${sickbeard.SAB_CATEGORY_ANIME}" class="form-control input-sm input200" autocapitalize="off" />
                                         <p>add anime downloads to this category (e.g. anime)</p>
                                     </span>
                                 </label>
@@ -312,7 +312,7 @@
                                 <label>
                                     <span class="component-title">Use SABnzbd category for anime (backlog episodes)</span>
                                     <span class="component-desc">
-                                        <input type="text" name="sab_category_anime_backlog" id="sab_category_anime_backlog" value="${sickbeard.SAB_CATEGORY_ANIME_BACKLOG}" class="form-control input-sm input200" />
+                                        <input type="text" name="sab_category_anime_backlog" id="sab_category_anime_backlog" value="${sickbeard.SAB_CATEGORY_ANIME_BACKLOG}" class="form-control input-sm input200" autocapitalize="off" />
                                         <p>add anime downloads of old episodes to this category (e.g. anime)</p>
                                     </span>
                                 </label>
@@ -346,7 +346,7 @@
                                 <label>
                                     <span class="component-title">NZBget host:port</span>
                                     <span class="component-desc">
-                                        <input type="text" name="nzbget_host" id="nzbget_host" value="${sickbeard.NZBGET_HOST}" class="form-control input-sm input350" />
+                                        <input type="text" name="nzbget_host" id="nzbget_host" value="${sickbeard.NZBGET_HOST}" class="form-control input-sm input350" autocapitalize="off" />
                                         <p>(e.g. localhost:6789)</p>
                                         <div class="clear-left"><p>NZBget RPC host name and port number (not NZBgetweb!)</p></div>
                                     </span>
@@ -357,7 +357,7 @@
                                 <label>
                                     <span class="component-title">NZBget username</span>
                                     <span class="component-desc">
-                                        <input type="text" name="nzbget_username" value="${sickbeard.NZBGET_USERNAME}" class="form-control input-sm input200" />
+                                        <input type="text" name="nzbget_username" value="${sickbeard.NZBGET_USERNAME}" class="form-control input-sm input200" autocapitalize="off" autocomplete="no" />
                                         <p>locate in nzbget.conf (default:nzbget)</p>
                                     </span>
                                 </label>
@@ -367,7 +367,7 @@
                                 <label>
                                     <span class="component-title">NZBget password</span>
                                     <span class="component-desc">
-                                        <input type="password" name="nzbget_password" id="nzbget_password" value="${sickbeard.NZBGET_PASSWORD}" class="form-control input-sm input200" />
+                                        <input type="password" name="nzbget_password" id="nzbget_password" value="${sickbeard.NZBGET_PASSWORD}" class="form-control input-sm input200" autocomplete="no" autocapitalize="off" />
                                         <p>locate in nzbget.conf (default:tegbzn6789)</p>
                                     </span>
                                 </label>
@@ -377,7 +377,7 @@
                                 <label>
                                     <span class="component-title">Use NZBget category</span>
                                     <span class="component-desc">
-                                        <input type="text" name="nzbget_category" id="nzbget_category" value="${sickbeard.NZBGET_CATEGORY}" class="form-control input-sm input200" />
+                                        <input type="text" name="nzbget_category" id="nzbget_category" value="${sickbeard.NZBGET_CATEGORY}" class="form-control input-sm input200" autocapitalize="off" />
                                         <p>send downloads marked this category (e.g. TV)</p>
                                     </span>
                                 </label>
@@ -387,7 +387,7 @@
                                 <label>
                                     <span class="component-title">Use NZBget category (backlog episodes)</span>
                                     <span class="component-desc">
-                                        <input type="text" name="nzbget_category_backlog" id="nzbget_category_backlog" value="${sickbeard.NZBGET_CATEGORY_BACKLOG}" class="form-control input-sm input200" />
+                                        <input type="text" name="nzbget_category_backlog" id="nzbget_category_backlog" value="${sickbeard.NZBGET_CATEGORY_BACKLOG}" class="form-control input-sm input200" autocapitalize="off" />
                                         <p>send downloads of old episodes marked this category (e.g. TV)</p>
                                     </span>
                                 </label>
@@ -397,7 +397,7 @@
                                 <label>
                                     <span class="component-title">Use NZBget category for anime</span>
                                     <span class="component-desc">
-                                        <input type="text" name="nzbget_category_anime" id="nzbget_category_anime" value="${sickbeard.NZBGET_CATEGORY_ANIME}" class="form-control input-sm input200" />
+                                        <input type="text" name="nzbget_category_anime" id="nzbget_category_anime" value="${sickbeard.NZBGET_CATEGORY_ANIME}" class="form-control input-sm input200" autocapitalize="off" />
                                         <p>send anime downloads marked this category (e.g. anime)</p>
                                     </span>
                                 </label>
@@ -407,7 +407,7 @@
                                 <label>
                                     <span class="component-title">Use NZBget category for anime (backlog episodes)</span>
                                     <span class="component-desc">
-                                        <input type="text" name="nzbget_category_anime_backlog" id="nzbget_category_anime_backlog" value="${sickbeard.NZBGET_CATEGORY_ANIME_BACKLOG}" class="form-control input-sm input200" />
+                                        <input type="text" name="nzbget_category_anime_backlog" id="nzbget_category_anime_backlog" value="${sickbeard.NZBGET_CATEGORY_ANIME_BACKLOG}" class="form-control input-sm input200" autocapitalize="off" />
                                         <p>send anime downloads of old episodes marked this category (e.g. anime)</p>
                                     </span>
                                 </label>
@@ -475,7 +475,7 @@
                                         <label>
                                             <span class="component-title">Black hole folder location</span>
                                             <span class="component-desc">
-                                                <input type="text" name="torrent_dir" id="torrent_dir" value="${sickbeard.TORRENT_DIR}" class="form-control input-sm input350" />
+                                                <input type="text" name="torrent_dir" id="torrent_dir" value="${sickbeard.TORRENT_DIR}" class="form-control input-sm input350" autocapitalize="off" />
                                                 <div class="clear-left"><p><b>.torrent</b> files are stored at this location for external software to find and use</p></div>
                                             </span>
                                         </label>
@@ -491,7 +491,7 @@
                                     <label>
                                         <span class="component-title" id="host_title">Torrent host:port</span>
                                         <span class="component-desc">
-                                            <input type="text" name="torrent_host" id="torrent_host" value="${sickbeard.TORRENT_HOST}" class="form-control input-sm input350" />
+                                            <input type="text" name="torrent_host" id="torrent_host" value="${sickbeard.TORRENT_HOST}" class="form-control input-sm input350" autocapitalize="off" />
                                             <div class="clear-left">
                                                 <p id="host_desc_torrent">URL to your torrent client (e.g. http://localhost:8000/)</p>
                                             </div>
@@ -503,7 +503,7 @@
                                     <label>
                                         <span class="component-title" id="rpcurl_title">Torrent RPC URL</span>
                                         <span class="component-desc">
-                                            <input type="text" name="torrent_rpcurl" id="torrent_rpcurl" value="${sickbeard.TORRENT_RPCURL}" class="form-control input-sm input350"/>
+                                            <input type="text" name="torrent_rpcurl" id="torrent_rpcurl" value="${sickbeard.TORRENT_RPCURL}" class="form-control input-sm input350" autocapitalize="off" />
                                             <div class="clear-left">
                                                 <p id="rpcurl_desc_">The path without leading and trailing slashes (e.g. transmission)</p>
                                             </div>
@@ -541,7 +541,7 @@
                                     <label>
                                         <span class="component-title" id="username_title">Client username</span>
                                         <span class="component-desc">
-                                            <input type="text" name="torrent_username" id="torrent_username" value="${sickbeard.TORRENT_USERNAME}" class="form-control input-sm input200" />
+                                            <input type="text" name="torrent_username" id="torrent_username" value="${sickbeard.TORRENT_USERNAME}" class="form-control input-sm input200" autocapitalize="off" autocomplete="no" />
                                             <p>(blank for none)</p>
                                         </span>
                                     </label>
@@ -551,7 +551,7 @@
                                     <label>
                                         <span class="component-title" id="password_title">Client password</span>
                                         <span class="component-desc">
-                                            <input type="password" name="torrent_password" id="torrent_password" value="${sickbeard.TORRENT_PASSWORD}" class="form-control input-sm input200" />
+                                            <input type="password" name="torrent_password" id="torrent_password" value="${sickbeard.TORRENT_PASSWORD}" class="form-control input-sm input200" autocomplete="no" autocapitalize="off" />
                                             <p>(blank for none)</p>
                                         </span>
                                     </label>
@@ -561,7 +561,7 @@
                                     <label>
                                         <span class="component-title">Add label to torrent</span>
                                         <span class="component-desc">
-                                            <input type="text" name="torrent_label" id="torrent_label" value="${sickbeard.TORRENT_LABEL}" class="form-control input-sm input200" />
+                                            <input type="text" name="torrent_label" id="torrent_label" value="${sickbeard.TORRENT_LABEL}" class="form-control input-sm input200" autocapitalize="off" />
                                             <span id="label_warning_deluge" style="display:none"><p>(blank spaces are not allowed)</p>
                                                 <div class="clear-left"><p>note: label plugin must be enabled in Deluge clients</p></div>
                                             </span>
@@ -573,7 +573,7 @@
                                     <label>
                                         <span class="component-title">Add label to torrent for anime</span>
                                         <span class="component-desc">
-                                            <input type="text" name="torrent_label_anime" id="torrent_label_anime" value="${sickbeard.TORRENT_LABEL_ANIME}" class="form-control input-sm input200" />
+                                            <input type="text" name="torrent_label_anime" id="torrent_label_anime" value="${sickbeard.TORRENT_LABEL_ANIME}" class="form-control input-sm input200" autocapitalize="off" />
                                             <span id="label_anime_warning_deluge" style="display:none"><p>(blank spaces are not allowed)</p>
                                                 <div class="clear-left"><p>note: label plugin must be enabled in Deluge clients</p></div>
                                             </span>
@@ -585,7 +585,7 @@
                                     <label>
                                         <span class="component-title" id="directory_title">Downloaded files location</span>
                                         <span class="component-desc">
-                                            <input type="text" name="torrent_path" id="torrent_path" value="${sickbeard.TORRENT_PATH}" class="form-control input-sm input350" />
+                                            <input type="text" name="torrent_path" id="torrent_path" value="${sickbeard.TORRENT_PATH}" class="form-control input-sm input350" autocapitalize="off" />
                                             <div class="clear-left"><p>where <span id="torrent_client">the torrent client</span> will save downloaded files (blank for client default)
                                                 <span id="path_synology"> <b>note:</b> the destination has to be a shared folder for Synology DS</span></p>
                                             </div>
diff --git a/gui/slick/views/config_subtitles.mako b/gui/slick/views/config_subtitles.mako
index bf1f208908a51c7ade978639f69bbc2ef6608233..01ea0fd718fc8d9f731011428fccc58cb81b20ee 100644
--- a/gui/slick/views/config_subtitles.mako
+++ b/gui/slick/views/config_subtitles.mako
@@ -7,13 +7,13 @@
 <%block name="scripts">
 <script>
 $(document).ready(function() {
-    $("#subtitles_languages").tokenInput([${','.join("{\"id\": \"" + lang.opensubtitles + "\", name: \"" + lang.name + "\"}" for lang in subtitles.subtitleLanguageFilter())}], {
+    $("#subtitles_languages").tokenInput([${','.join("{\"id\": \"" + code + "\", name: \"" + subtitles.name_from_code(code) + "\"}" for code in subtitles.subtitle_code_filter())}], {
         method: "POST",
         hintText: "Write to search a language and select it",
         preventDuplicates: true,
-        prePopulate: [${','.join("{\"id\": \"" + subtitles.fromietf(lang).opensubtitles + "\", name: \"" + subtitles.fromietf(lang).name + "\"}" for lang in subtitles.wantedLanguages()) if subtitles.wantedLanguages() else ''}],
-        resultsFormatter: function(item){ return "<li><img src='${srRoot}/images/subtitles/flags/" + item.id + ".png' /> " + item.name + "</li>" },
-        tokenFormatter: function(item)  { return "<li><img src='${srRoot}/images/subtitles/flags/" + item.id + ".png' /> " + item.name + "</li>" },
+        prePopulate: [${','.join("{\"id\": \"" + code + "\", name: \"" + subtitles.name_from_code(code) + "\"}" for code in subtitles.wanted_languages())}],
+        resultsFormatter: function(item){ return "<li><img src='${srRoot}/images/subtitles/flags/" + item.id + ".png' onError='this.onerror=null;this.src=\"${srRoot}/images/flags/unknown.png\";' style='vertical-align: middle !important;' /> " + item.name + "</li>" },
+        tokenFormatter: function(item)  { return "<li><img src='${srRoot}/images/subtitles/flags/" + item.id + ".png' onError='this.onerror=null;this.src=\"${srRoot}/images/flags/unknown.png\";' style='vertical-align: middle !important;' /> " + item.name + "</li>" },
     });
 });
 $('#config-components').tabs();
@@ -59,7 +59,7 @@ $('#subtitles_dir').fileBrowser({ title: 'Select Subtitles Download Directory' }
                                 <div class="field-pair">
                                     <label>
                                         <span class="component-title">Subtitle Languages</span>
-                                        <span class="component-desc"><input type="text" id="subtitles_languages" name="subtitles_languages" /></span>
+                                        <span class="component-desc"><input type="text" id="subtitles_languages" name="subtitles_languages" autocapitalize="off" /></span>
                                     </label>
                                 </div>
                                 <div class="field-pair">
@@ -134,7 +134,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" autocapitalize="off" />
                                     </label>
                                     <label class="nocheck">
                                         <span class="component-title">&nbsp;</span>
@@ -173,7 +173,7 @@ $('#subtitles_dir').fileBrowser({ title: 'Select Subtitles Download Directory' }
 
                     <fieldset class="component-group-list" style="margin-left: 50px; margin-top:36px">
                         <ul id="service_order_list">
-                        % for curService in sickbeard.subtitles.sortedServiceList():
+                        % for curService in sickbeard.subtitles.sorted_service_list():
                             <li class="ui-state-default" id="${curService['name']}">
                                 <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">
@@ -184,7 +184,7 @@ $('#subtitles_dir').fileBrowser({ title: 'Select Subtitles Download Directory' }
                           </li>
                         % endfor
                         </ul>
-                        <input type="hidden" name="service_order" id="service_order" value="<%" ".join(['%s:%d' % (x['name'], x['enabled']) for x in sickbeard.subtitles.sortedServiceList()])%>"/>
+                        <input type="hidden" name="service_order" id="service_order" value="<%" ".join(['%s:%d' % (x['name'], x['enabled']) for x in sickbeard.subtitles.sorted_service_list()])%>"/>
 
                         <br><input type="submit" class="btn config_submitter" value="Save Changes" /><br>
                     </fieldset>
@@ -202,7 +202,7 @@ $('#subtitles_dir').fileBrowser({ title: 'Select Subtitles Download Directory' }
                                 'addic7ed': {'user': sickbeard.ADDIC7ED_USER, 'pass': sickbeard.ADDIC7ED_PASS},
                                 'opensubtitles': {'user': sickbeard.OPENSUBTITLES_USER, 'pass': sickbeard.OPENSUBTITLES_PASS}}
                         %>
-                        % for curService in sickbeard.subtitles.sortedServiceList():
+                        % for curService in sickbeard.subtitles.sorted_service_list():
                             % if curService['name'] not in providerLoginDict.keys():
                                 <% continue %>
                             % endif
@@ -211,13 +211,13 @@ $('#subtitles_dir').fileBrowser({ title: 'Select Subtitles Download Directory' }
                                 <label class="nocheck" for="${curService['name']}_user">
                                     <span class="component-title">${curService['name'].capitalize()} User Name</span>
                                     <span class="component-desc">
-                                        <input type="text" name="${curService['name']}_user" id="${curService['name']}_user" value="${providerLoginDict[curService['name']]['user']}" class="form-control input-sm input300" />
+                                        <input type="text" name="${curService['name']}_user" id="${curService['name']}_user" value="${providerLoginDict[curService['name']]['user']}" class="form-control input-sm input300" autocapitalize="off" autocomplete="no" />
                                     </span>
                                 </label>
                                 <label class="nocheck" for="${curService['name']}_pass">
                                     <span class="component-title">${curService['name'].capitalize()} Password</span>
                                     <span class="component-desc">
-                                        <input type="password" name="${curService['name']}_pass" id="${curService['name']}_pass" value="${providerLoginDict[curService['name']]['pass']}" class="form-control input-sm input300" />
+                                        <input type="password" name="${curService['name']}_pass" id="${curService['name']}_pass" value="${providerLoginDict[curService['name']]['pass']}" class="form-control input-sm input300" autocomplete="no" autocapitalize="off" />
                                     </span>
                                 </label>
                             </div>
diff --git a/gui/slick/views/displayShow.mako b/gui/slick/views/displayShow.mako
index 3ac9d1361d68324415c10ad14af4862f602f95fd..fd52eb56b48bdacefe2b24b7f4aa5499e0e79b39 100644
--- a/gui/slick/views/displayShow.mako
+++ b/gui/slick/views/displayShow.mako
@@ -207,7 +207,7 @@
                 </table>
 
                 <table style="width:180px; float: right; vertical-align: middle; height: 100%;">
-                    <% info_flag = subtitles.fromietf(show.lang).opensubtitles if show.lang else '' %>
+                    <% info_flag = subtitles.code_from_code(show.lang) if show.lang else '' %>
                     <tr><td class="showLegend">Info Language:</td><td><img src="${srRoot}/images/subtitles/flags/${info_flag}.png" width="16" height="11" alt="${show.lang}" title="${show.lang}" onError="this.onerror=null;this.src='${srRoot}/images/flags/unknown.png';"/></td></tr>
                     % if sickbeard.USE_SUBTITLES:
                     <tr><td class="showLegend">Subtitles: </td><td><img src="${srRoot}/images/${("no16.png", "yes16.png")[bool(show.subtitles)]}" alt="${("N", "Y")[bool(show.subtitles)]}" width="16" height="16" /></td></tr>
@@ -445,7 +445,7 @@
                     % else:
                         value="${str(scSeas)}x${str(scEpis)}"
                     % endif
-                        style="padding: 0; text-align: center; max-width: 60px;" />
+                        style="padding: 0; text-align: center; max-width: 60px;" autocapitalize="off" />
             </td>
             <td align="center">
                 <input type="text" placeholder="${str(dfltAbsolute)}" size="6" maxlength="8"
@@ -457,7 +457,7 @@
                     % else:
                         value="${str(scAbsolute)}"
                     % endif
-                        style="padding: 0; text-align: center; max-width: 60px;" />
+                        style="padding: 0; text-align: center; max-width: 60px;" autocapitalize="off" />
             </td>
             <td class="col-name">
             % if epResult["description"] != "" and epResult["description"] is not None:
@@ -499,12 +499,10 @@
                 % endif
             </td>
             <td class="col-subtitles" align="center">
-            % for sub_lang in [subtitles.fromietf(x) for x in epResult["subtitles"].split(',') if epResult["subtitles"]]:
-                <% flag = sub_lang.opensubtitles %>
-                % if (not sickbeard.SUBTITLES_MULTI and len(subtitles.wantedLanguages()) is 1) and subtitles.wantedLanguages()[0] in sub_lang.opensubtitles:
-                    <% flag = 'checkbox' %>
+            % for flag in (epResult["subtitles"] or '').split(','):
+                % if flag.strip():
+                    <img src="${srRoot}/images/subtitles/flags/${flag}.png" width="16" height="11" alt="${subtitles.name_from_code(flag)}" onError="this.onerror=null;this.src='${srRoot}/images/flags/unknown.png';" />
                 % endif
-                <img src="${srRoot}/images/subtitles/flags/${flag}.png" width="16" height="11" alt="${sub_lang.name}" onError="this.onerror=null;this.src='${srRoot}/images/flags/unknown.png';" />
             % endfor
             </td>
                 <% curStatus, curQuality = Quality.splitCompositeStatus(int(epResult["status"])) %>
@@ -521,7 +519,7 @@
                         <a class="epSearch" id="${str(show.indexerid)}x${str(epResult["season"])}x${str(epResult["episode"])}" name="${str(show.indexerid)}x${str(epResult["season"])}x${str(epResult["episode"])}" href="searchEpisode?show=${show.indexerid}&amp;season=${epResult["season"]}&amp;episode=${epResult["episode"]}"><img src="${srRoot}/images/search16.png" width="16" height="16" alt="search" title="Manual Search" /></a>
                     % endif
                 % endif
-                % if sickbeard.USE_SUBTITLES and show.subtitles and epResult["location"] and frozenset(subtitles.wantedLanguages()).difference(epResult["subtitles"].split(',')):
+                % if sickbeard.USE_SUBTITLES and show.subtitles and epResult["location"] and subtitles.needs_subtitles(epResult['subtitles']):
                     <a class="epSubtitlesSearch" href="searchEpisodeSubtitles?show=${show.indexerid}&amp;season=${epResult["season"]}&amp;episode=${epResult["episode"]}"><img src="${srRoot}/images/closed_captioning.png" height="16" alt="search subtitles" title="Search Subtitles" /></a>
                 % endif
             </td>
diff --git a/gui/slick/views/editShow.mako b/gui/slick/views/editShow.mako
index 299168570c6e184e613eb6e108dfe120d375dc2f..9b3be1c6814317af095969c450b549e226a7df27 100644
--- a/gui/slick/views/editShow.mako
+++ b/gui/slick/views/editShow.mako
@@ -50,7 +50,7 @@
                                 <span class="component-title">Show Location</span>
                                 <span class="component-desc">
                                     <input type="hidden" name="show" value="${show.indexerid}" />
-                                    <input type="text" name="location" id="location" value="${show._location}" class="form-control form-control-inline input-sm input350" />
+                                    <input type="text" name="location" id="location" value="${show._location}" class="form-control form-control-inline input-sm input350" autocapitalize="off" />
                                 </span>
                             </label>
                         </div>
@@ -202,7 +202,7 @@
                             <label for="rls_ignore_words">
                                 <span class="component-title">Ignored Words</span>
                                 <span class="component-desc">
-                                    <input type="text" id="rls_ignore_words" name="rls_ignore_words" id="rls_ignore_words" value="${show.rls_ignore_words}" class="form-control form-control-inline input-sm input350" /><br>
+                                    <input type="text" id="rls_ignore_words" name="rls_ignore_words" id="rls_ignore_words" value="${show.rls_ignore_words}" class="form-control form-control-inline input-sm input350" autocapitalize="off" /><br>
                                     <div class="clear-left">
                                         <p>comma-separated <i>e.g. "word1,word2,word3"</i></>
                                         <p>Search results with one or more words from this list will be ignored.</p>
@@ -215,7 +215,7 @@
                             <label for="rls_require_words">
                                 <span class="component-title">Required Words</span>
                                 <span class="component-desc">
-                                    <input type="text" id="rls_require_words" name="rls_require_words" id="rls_require_words" value="${show.rls_require_words}" class="form-control form-control-inline input-sm input350" /><br>
+                                    <input type="text" id="rls_require_words" name="rls_require_words" id="rls_require_words" value="${show.rls_require_words}" class="form-control form-control-inline input-sm input350" autocapitalize="off" /><br>
                                     <div class="clear-left">
                                         <p>comma-separated <i>e.g. "word1,word2,word3"</i></p>
                                         <p>Search results with no words from this list will be ignored.</p>
@@ -228,7 +228,7 @@
                             <label for="SceneName">
                                 <span class="component-title">Scene Exception</span>
                                 <span class="component-desc">
-                                    <input type="text" id="SceneName" class="form-control form-control-inline input-sm input200" /><input class="btn btn-inline" type="button" value="Add" id="addSceneName" /><br><br>
+                                    <input type="text" id="SceneName" class="form-control form-control-inline input-sm input200" autocapitalize="off" /><input class="btn btn-inline" type="button" value="Add" id="addSceneName" /><br><br>
                                     <div class="pull-left">
                                         <select id="exceptions_list" name="exceptions_list" multiple="multiple" style="min-width:200px;height:99px;">
                                         % for cur_exception in show.exceptions:
diff --git a/gui/slick/views/history.mako b/gui/slick/views/history.mako
index 8895b5b044e84ddc132f883bc49daa0c6fe38929..385f707b1da2f7ecca9a41bd26f11774d04f574b 100644
--- a/gui/slick/views/history.mako
+++ b/gui/slick/views/history.mako
@@ -14,6 +14,7 @@
     from sickbeard.common import Quality, statusStrings, Overview
 
     from sickrage.show.History import History
+    from sickrage.helper.encoding import ek
 %>
 <%block name="content">
 <%namespace file="/inc_defs.mako" import="renderQualityPill"/>
@@ -76,7 +77,7 @@
                 % if curStatus == SUBTITLED:
                     <img width="16" height="11" style="vertical-align:middle;" src="${srRoot}/images/subtitles/flags/${hItem['resource']}.png" onError="this.onerror=null;this.src='${srRoot}/images/flags/unknown.png';">
                 % endif
-                    <span style="cursor: help; vertical-align:middle;" title="${os.path.basename(hItem['resource'])}">${statusStrings[curStatus]}</span>
+                    <span style="cursor: help; vertical-align:middle;" title="${ek(os.path.basename, hItem['resource'])}">${statusStrings[curStatus]}</span>
                 </td>
                 <td align="center">
                 % if curStatus in [DOWNLOADED, ARCHIVED]:
@@ -143,7 +144,7 @@
                         % if curStatus in [SNATCHED, FAILED]:
                             <% provider = providers.getProviderClass(generic.GenericProvider.makeID(action["provider"])) %>
                             % 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"])}"/>
+                                <img src="${srRoot}/images/providers/${provider.imageName()}" width="16" height="16" style="vertical-align:middle;" alt="${provider.name}" style="cursor: help;" title="${provider.name}: ${ek(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"/>
                             % endif
@@ -155,9 +156,9 @@
                         <% curStatus, curQuality = Quality.splitCompositeStatus(int(action["action"])) %>
                         % if curStatus in [DOWNLOADED, ARCHIVED]:
                             % if action["provider"] != "-1":
-                                <span style="cursor: help;" title="${os.path.basename(action["resource"])}"><i>${action["provider"]}</i></span>
+                                <span style="cursor: help;" title="${ek(os.path.basename, action["resource"])}"><i>${action["provider"]}</i></span>
                             % else:
-                                <span style="cursor: help;" title="${os.path.basename(action["resource"])}"></span>
+                                <span style="cursor: help;" title="${ek(os.path.basename, action["resource"])}"></span>
                             % endif
                         % endif
                     % endfor
@@ -167,7 +168,7 @@
                     % for action in sorted(hItem["actions"]):
                         <% curStatus, curQuality = Quality.splitCompositeStatus(int(action["action"])) %>
                         % if curStatus == SUBTITLED:
-                            <img src="${srRoot}/images/subtitles/${action['provider']}.png" width="16" height="16" style="vertical-align:middle;" alt="${action["provider"]}" title="${action["provider"].capitalize()}: ${os.path.basename(action["resource"])}"/>
+                            <img src="${srRoot}/images/subtitles/${action['provider']}.png" width="16" height="16" style="vertical-align:middle;" alt="${action["provider"]}" title="${action["provider"].capitalize()}: ${ek(os.path.basename, action["resource"])}"/>
                             <span style="vertical-align:middle;"> / </span>
                             <img width="16" height="11" style="vertical-align:middle;" src="${srRoot}/images/subtitles/flags/${action['resource']}.png" onError="this.onerror=null;this.src='${srRoot}/images/flags/unknown.png';" style="vertical-align: middle !important;">
                             &nbsp;
diff --git a/gui/slick/views/home.mako b/gui/slick/views/home.mako
index df431a375c11b71f216b57c55e2505ee9f4555cd..efa1090e2b9c669870dfd19dee6b892ce586babb 100644
--- a/gui/slick/views/home.mako
+++ b/gui/slick/views/home.mako
@@ -12,65 +12,82 @@
 </%block>
 <%block name="content">
 <%namespace file="/inc_defs.mako" import="renderQualityPill"/>
-% if not header is UNDEFINED:
-    <h1 class="header">${header}</h1>
-% else:
-    <h1 class="title">${title}</h1>
-% endif
 
-<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>
-    </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>
-            <option value="date" data-sort="${srRoot}/setPosterSortBy/?sort=date" ${('', 'selected="selected"')[sickbeard.POSTER_SORTBY == 'date']}>Next Episode</option>
-            <option value="network" data-sort="${srRoot}/setPosterSortBy/?sort=network" ${('', 'selected="selected"')[sickbeard.POSTER_SORTBY == 'network']}>Network</option>
-            <option value="progress" data-sort="${srRoot}/setPosterSortBy/?sort=progress" ${('', 'selected="selected"')[sickbeard.POSTER_SORTBY == 'progress']}>Progress</option>
-        </select>
-    </span>
-    &nbsp;
-    <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]}>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>
-    % endif
+<table style="width: 100%;" class="home-header">
+    <tr>
+        <td nowrap>
+            % if not header is UNDEFINED:
+                <h1 class="header" style="margin: 0;">${header}</h1>
+            % else:
+                <h1 class="title" style="margin: 0;">${title}</h1>
+            % endif
+        </td>
 
-    &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>
+        <td align="right">
+            <div>
+                % if sickbeard.HOME_LAYOUT != 'poster':
+                    <span class="show-option">
+                        <button id="popover" type="button" class="btn btn-inline">Select Columns <b class="caret"></b></button>
+                    </span>
+
+                    <span class="show-option">
+                        <button type="button" class="resetsorting btn btn-inline">Clear Filter(s)</button>
+                    </span>
+                % endif
+
+                % if sickbeard.HOME_LAYOUT == 'poster':
+                    <span class="show-option"> Poster Size:
+                        <div style="width: 100px; display: inline-block; margin-left: 7px;" id="posterSizeSlider"></div>
+                    </span>
+
+                    <span class="show-option">
+                        <input id="filterShowName" class="form-control form-control-inline input-sm input200" type="search" placeholder="Filter Show Name">
+                    </span>
+
+                    <span class="show-option"> 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>
+                            <option value="date" data-sort="${srRoot}/setPosterSortBy/?sort=date" ${('', 'selected="selected"')[sickbeard.POSTER_SORTBY == 'date']}>Next Episode</option>
+                            <option value="network" data-sort="${srRoot}/setPosterSortBy/?sort=network" ${('', 'selected="selected"')[sickbeard.POSTER_SORTBY == 'network']}>Network</option>
+                            <option value="progress" data-sort="${srRoot}/setPosterSortBy/?sort=progress" ${('', 'selected="selected"')[sickbeard.POSTER_SORTBY == 'progress']}>Progress</option>
+                        </select>
+                    </span>
+
+                    <span class="show-option"> 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]}>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>
+                % endif
+
+                <span class="show-option"> 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>
+        </td>
+    </tr>
+</table>
+% if sickbeard.HOME_LAYOUT == 'poster':
+    <div class="loading-spinner"></div>
+% endif
 
 % for curShowlist in showlists:
     <% curListType = curShowlist[0] %>
     <% myShowList = list(curShowlist[1]) %>
     % if curListType == "Anime":
         <h1 class="header">Anime List</h1>
+        % if sickbeard.HOME_LAYOUT == 'poster':
+            <div class="loading-spinner"></div>
+    % endif
     % endif
 % if sickbeard.HOME_LAYOUT == 'poster':
-<div id="${('container', 'container-anime')[curListType == 'Anime' and sickbeard.HOME_LAYOUT == 'poster']}" class="clearfix">
+<div id="${('container', 'container-anime')[curListType == 'Anime' and sickbeard.HOME_LAYOUT == 'poster']}" class="show-grid clearfix">
 <div class="posterview">
 % for curLoadingShow in sickbeard.showQueueScheduler.action.loadingShowList:
     % if curLoadingShow.show is None:
@@ -116,23 +133,22 @@
         if not cur_total:
             cur_total = 0
 
-    if cur_total != 0:
-        download_stat = str(cur_downloaded)
-        download_stat_tip = "Downloaded: " + str(cur_downloaded)
-        if cur_snatched > 0:
-            download_stat = download_stat
-            download_stat_tip = download_stat_tip + "&#013;" + "Snatched: " + str(cur_snatched)
+    download_stat = str(cur_downloaded)
+    download_stat_tip = "Downloaded: " + str(cur_downloaded)
 
-        download_stat = download_stat + " / " + str(cur_total)
-        download_stat_tip = download_stat_tip + "&#013;" + "Total: " + str(cur_total)
-    else:
-        download_stat = '?'
-        download_stat_tip = "no data"
+    if cur_snatched:
+        download_stat = download_stat + "+" + str(cur_snatched)
+        download_stat_tip = download_stat_tip + "&#013;" + "Snatched: " + str(cur_snatched)
+
+    download_stat = download_stat + " / " + str(cur_total)
+    download_stat_tip = download_stat_tip + "&#013;" + "Total: " + str(cur_total)
 
     nom = cur_downloaded
-    den = cur_total
-    if den == 0:
+    if cur_total:
+        den = cur_total
+    else:
         den = 1
+        download_stat_tip = "Unaired"
 
     progressbar_percent = nom * 100 / den
 
@@ -183,29 +199,31 @@
 % endif
         </div>
 
-        <table width="100%" cellspacing="1" border="0" cellpadding="0">
-            <tr>
-                <td class="show-table">
-                    <span class="show-dlstats" title="${download_stat_tip}">${download_stat}</span>
-                </td>
-
-                <td class="show-table">
-                    % if sickbeard.HOME_LAYOUT != 'simple':
-                        % if curShow.network:
-                            <span title="${curShow.network}"><img class="show-network-image" src="${srRoot}/showPoster/?show=${curShow.indexerid}&amp;which=network" alt="${curShow.network}" title="${curShow.network}" /></span>
+        <div class="show-details">
+            <table class="show-details" width="100%" cellspacing="1" border="0" cellpadding="0">
+                <tr>
+                    <td class="show-table">
+                        <span class="show-dlstats" title="${download_stat_tip}">${download_stat}</span>
+                    </td>
+
+                    <td class="show-table">
+                        % if sickbeard.HOME_LAYOUT != 'simple':
+                            % if curShow.network:
+                                <span title="${curShow.network}"><img class="show-network-image" src="${srRoot}/showPoster/?show=${curShow.indexerid}&amp;which=network" alt="${curShow.network}" title="${curShow.network}" /></span>
+                            % else:
+                                <span title="No Network"><img class="show-network-image" src="${srRoot}/images/network/nonetwork.png" alt="No Network" title="No Network" /></span>
+                            % endif
                         % else:
-                            <span title="No Network"><img class="show-network-image" src="${srRoot}/images/network/nonetwork.png" alt="No Network" title="No Network" /></span>
+                            <span title="${curShow.network}">${curShow.network}</span>
                         % endif
-                    % else:
-                        <span title="${curShow.network}">${curShow.network}</span>
-                    % endif
-                </td>
+                    </td>
 
-                <td class="show-table">
-                    ${renderQualityPill(curShow.quality, showTitle=True, overrideClass="show-quality")}
-                </td>
-            </tr>
-        </table>
+                    <td class="show-table">
+                        ${renderQualityPill(curShow.quality, showTitle=True, overrideClass="show-quality")}
+                    </td>
+                </tr>
+            </table>
+        </div>
 
     </div>
 
@@ -225,7 +243,7 @@
             <th>Network</th>
             <th>Quality</th>
             <th>Downloads</th>
-            ## <th>Size</th>
+            <th>Size</th>
             <th>Active</th>
             <th>Status</th>
         </tr>
@@ -233,13 +251,13 @@
 
     <tfoot class="hidden-print">
         <tr>
-            <th rowspan="1" colspan="1" align="center"><a href="${srRoot}/home/addShows/">Add ${('Show', 'Anime')[curListType == 'Anime']}</a></th>
+            <th rowspan="1" colspan="1" align="center"><a href="${srRoot}/addShows/">Add ${('Show', 'Anime')[curListType == 'Anime']}</a></th>
+            <th>&nbsp;</th>
             <th>&nbsp;</th>
             <th>&nbsp;</th>
             <th>&nbsp;</th>
             <th>&nbsp;</th>
             <th>&nbsp;</th>
-            ## <th>&nbsp;</th> // This is needed for size
             <th>&nbsp;</th>
             <th>&nbsp;</th>
         </tr>
@@ -282,6 +300,7 @@
     cur_snatched = 0
     cur_downloaded = 0
     cur_total = 0
+    show_size = 0
     download_stat_tip = ''
 
     if curShow.indexerid in show_stat:
@@ -300,8 +319,11 @@
         if not cur_total:
             cur_total = 0
 
+        show_size = show_stat[curShow.indexerid]['show_size']
+
     download_stat = str(cur_downloaded)
     download_stat_tip = "Downloaded: " + str(cur_downloaded)
+
     if cur_snatched:
         download_stat = download_stat + "+" + str(cur_snatched)
         download_stat_tip = download_stat_tip + "&#013;" + "Snatched: " + str(cur_snatched)
@@ -391,8 +413,7 @@
             <span class="visible-print-inline">${download_stat}</span>
         </td>
 
-        ## <% show_size = sickbeard.helpers.get_size(curShow._location) %>
-        ## <td align="center" data-show-size="${show_size}">${pretty_file_size(show_size)}</td>
+        <td align="center" data-show-size="${show_size}">${pretty_file_size(show_size)}</td>
 
         <td align="center">
             <% paused = int(curShow.paused) == 0 and curShow.status == 'Continuing' %>
diff --git a/gui/slick/views/home_newShow.mako b/gui/slick/views/home_newShow.mako
deleted file mode 100644
index 6905df264290f57e0007e556907b0bdcca2105b7..0000000000000000000000000000000000000000
--- a/gui/slick/views/home_newShow.mako
+++ /dev/null
@@ -1,106 +0,0 @@
-<%inherit file="/layouts/main.mako"/>
-<%!
-    import sickbeard
-    from sickbeard.helpers import anon_url
-%>
-<%block name="scripts">
-<script type="text/javascript" src="${srRoot}/js/qualityChooser.js?${sbPID}"></script>
-<script type="text/javascript" src="${srRoot}/js/newShow.js?${sbPID}"></script>
-<script type="text/javascript" src="${srRoot}/js/addShowOptions.js?${sbPID}"></script>
-<script type="text/javascript" src="${srRoot}/js/rootDirs.js?${sbPID}"></script>
-<script type="text/javascript" src="${srRoot}/js/blackwhite.js?${sbPID}"></script>
-</%block>
-<%block name="content">
-% if not header is UNDEFINED:
-    <h1 class="header">${header}</h1>
-% else:
-    <h1 class="title">${title}</h1>
-% endif
-
-<div id="newShowPortal">
-    <div id="config-components">
-        <ul>
-            <li><a href="#core-component-group1">Add New Show</a></li>
-        </ul>
-
-    <div id="core-component-group1" class="tab-pane active component-group">
-
-    <div id="displayText">aoeu</div>
-    <br>
-
-    <form id="addShowForm" method="post" action="${srRoot}/home/addShows/addNewShow" accept-charset="utf-8">
-
-    <fieldset class="sectionwrap">
-        <legend class="legendStep">Find a show on theTVDB</legend>
-
-        <div class="stepDiv">
-            <input type="hidden" id="indexer_timeout" value="${sickbeard.INDEXER_TIMEOUT}" />
-
-            % if use_provided_info:
-                Show retrieved from existing metadata: <a href="${anon_url(sickbeard.indexerApi(provided_indexer).config['show_url'], provided_indexer_id)}">${provided_indexer_name}</a>
-                <input type="hidden" id="indexerLang" name="indexerLang" value="en" />
-                <input type="hidden" id="whichSeries" name="whichSeries" value="${provided_indexer_id}" />
-                <input type="hidden" id="providedIndexer" name="providedIndexer" value="${provided_indexer}" />
-                <input type="hidden" id="providedName" value="${provided_indexer_name}" />
-            % else:
-                <input type="text" id="nameToSearch" value="${default_show_name}" class="form-control form-control-inline input-sm input350" />
-                &nbsp;&nbsp;
-                <select name="indexerLang" id="indexerLangSelect" class="form-control form-control-inline input-sm bfh-languages" data-language="${sickbeard.INDEXER_DEFAULT_LANGUAGE}" data-available="${','.join(sickbeard.indexerApi().config['valid_languages'])}">
-                </select><b>*</b>
-                &nbsp;
-                <select name="providedIndexer" id="providedIndexer" class="form-control form-control-inline input-sm">
-                    <option value="0" ${('', 'selected="selected"')[provided_indexer == 0]}>All Indexers</option>
-                    % for indexer in indexers:
-                        <option value="${indexer}" ${('', 'selected="selected"')[provided_indexer == indexer]}>
-                            ${indexers[indexer]}
-                        </option>
-                    % endfor
-                </select>
-                &nbsp;
-                <input class="btn btn-inline" type="button" id="searchName" value="Search" />
-
-                <br><br>
-                <b>*</b> This will only affect the language of the retrieved metadata file contents and episode filenames.<br>
-                This <b>DOES NOT</b> allow SickRage to download non-english TV episodes!<br><br>
-                <div id="searchResults" style="height: 100%;"><br></div>
-            % endif
-
-        </div>
-    </fieldset>
-
-    <fieldset class="sectionwrap">
-        <legend class="legendStep">Pick the parent folder</legend>
-
-        <div class="stepDiv">
-            % if provided_show_dir:
-                Pre-chosen Destination Folder: <b>${provided_show_dir}</b> <br>
-                <input type="hidden" id="fullShowPath" name="fullShowPath" value="${provided_show_dir}" /><br>
-            % else:
-                <%include file="/inc_rootDirs.mako"/>
-            % endif
-        </div>
-    </fieldset>
-
-    <fieldset class="sectionwrap">
-        <legend class="legendStep">Customize options</legend>
-            <div class="stepDiv">
-                <%include file="/inc_addShowOptions.mako"/>
-            </div>
-    </fieldset>
-
-    % for curNextDir in other_shows:
-    <input type="hidden" name="other_shows" value="${curNextDir}" />
-    % endfor
-    <input type="hidden" name="skipShow" id="skipShow" value="" />
-    </form>
-
-<br>
-
-<div style="width: 100%; text-align: center;">
-<input class="btn" type="button" id="addShowButton" value="Add Show" disabled="disabled" />
-% if provided_show_dir:
-<input class="btn" type="button" id="skipShowButton" value="Skip Show" />
-% endif
-</div>
-</div></div></div></div>
-</%block>
diff --git a/gui/slick/views/home_popularShows.mako b/gui/slick/views/home_popularShows.mako
deleted file mode 100644
index 0da617745e709194ec03fd230d1ad13f01a7bfc6..0000000000000000000000000000000000000000
--- a/gui/slick/views/home_popularShows.mako
+++ /dev/null
@@ -1,45 +0,0 @@
-<%inherit file="/layouts/main.mako"/>
-<%!
-    from sickbeard.helpers import anon_url
-    import sickbeard
-%>
-<%block name="content">
-<h2>Popular Shows</h2>
-<br>
-
-<% imdb_tt = [show.imdbid for show in sickbeard.showList if show.imdbid] %>
-% if not popular_shows:
-    <h3>Fetching of IMDB Data failed. Are you online?</h3>
-
-    <strong>Exception:</strong>
-    <p>${imdb_exception}</p>
-
-% else:
-    % for cur_result in popular_shows:
-        <div class="popularShow">
-            <div class="left">
-                <img class="coverImage" src="${srRoot}/cache/${cur_result['image_path']}" />
-            </div>
-            <div class="right">
-                <h3>${cur_result['name']}</h3>
-
-                % if 'rating' in cur_result and cur_result['rating']:
-                    <span class="rating">${cur_result['rating']}/10 (${cur_result['votes']})</span>
-                % endif
-
-                <p>${cur_result['outline']}<span class="year"> - Released ${cur_result['year']}<span></p>
-                <span class="imdb_url"><a href="${anon_url(cur_result['imdb_url'])}">View on IMDB</a></span>&nbsp;&nbsp;|&nbsp;&nbsp;
-                % if cur_result['imdb_tt'] not in imdb_tt:
-                <span class="imdb_sickrage_search"><a href="${srRoot}/home/addShows/newShow/?search_string=${cur_result['name']}">
-                    Add Show</a></span>
-                % else:
-                <span> Already added </span>
-                % endif
-
-            </div>
-            <br style="clear:both" />
-
-        </div>
-    % endfor
-% endif
-</%block>
diff --git a/gui/slick/views/home_postprocess.mako b/gui/slick/views/home_postprocess.mako
index 8876ebd9c2ed11848b9bd1ff332edab78b8c3755..a66fe4c3aea54ce624e91ed678d83ae92848ead8 100644
--- a/gui/slick/views/home_postprocess.mako
+++ b/gui/slick/views/home_postprocess.mako
@@ -19,7 +19,7 @@
                 <b>Enter the folder containing the episode:</b>
             </td>
             <td>
-                <input type="text" name="dir" id="episodeDir" class="form-control form-control-inline input-sm input350" />
+                <input type="text" name="dir" id="episodeDir" class="form-control form-control-inline input-sm input350" autocapitalize="off" />
             </td>
         </tr>
         <tr>
diff --git a/gui/slick/views/inc_blackwhitelist.mako b/gui/slick/views/inc_blackwhitelist.mako
index 37cc9fb65d8669432e430faccc6c1b555ee52844..86793b5db79b2056537388f4de28605ea82f5208 100644
--- a/gui/slick/views/inc_blackwhitelist.mako
+++ b/gui/slick/views/inc_blackwhitelist.mako
@@ -50,7 +50,7 @@
             </div>
             <br style="clear:both" />
             <div class="blackwhitelist manual">
-                <input type="text" id="addToPoolText" class="form-control form-control-inline input-sm input250" />
+                <input type="text" id="addToPoolText" class="form-control form-control-inline input-sm input250" autocapitalize="off" />
                 <input class="btn btn-inline" type="button" value="Add to Whitelist" id="addToWhite">
                 <input class="btn btn-inline" type="button" value="Add to Blacklist" id="addToBlack">
             </div>
diff --git a/gui/slick/views/inc_rootDirs.mako b/gui/slick/views/inc_rootDirs.mako
index ecdec42255be6864b5c5397236fde1cb664ad22a..11493a365b22fb0c585b9f5dc062e4a0f6799371 100644
--- a/gui/slick/views/inc_rootDirs.mako
+++ b/gui/slick/views/inc_rootDirs.mako
@@ -26,4 +26,4 @@
     <input class="btn" type="button" id="deleteRootDir" value="Delete" />
     <input class="btn" type="button" id="defaultRootDir" value="Set as Default *" />
 </div>
-<input type="text" style="display: none" id="rootDirText" />
+<input type="text" style="display: none" id="rootDirText" autocapitalize="off" />
diff --git a/gui/slick/views/layouts/main.mako b/gui/slick/views/layouts/main.mako
index 3b850185cad01a60fd8ad8c7d9dacccadb4e611c..9da4508649d58ef1f425dbfef31bd897daf1fe8f 100644
--- a/gui/slick/views/layouts/main.mako
+++ b/gui/slick/views/layouts/main.mako
@@ -112,7 +112,7 @@
                             </a>
                             <ul class="dropdown-menu">
                                 <li><a href="${srRoot}/home/"><i class="menu-icon-home"></i>&nbsp;Show List</a></li>
-                                <li><a href="${srRoot}/home/addShows/"><i class="menu-icon-addshow"></i>&nbsp;Add Shows</a></li>
+                                <li><a href="${srRoot}/addShows/"><i class="menu-icon-addshow"></i>&nbsp;Add Shows</a></li>
                                 <li><a href="${srRoot}/home/postprocess/"><i class="menu-icon-postprocess"></i>&nbsp;Manual Post-Processing</a></li>
                                 % if sickbeard.SHOWS_RECENT:
                                     <li role="separator" class="divider"></li>
@@ -317,7 +317,7 @@
         <script type="text/javascript" src="${srRoot}/js/lib/jquery.selectboxes.min.js?${sbPID}"></script>
         <script type="text/javascript" src="${srRoot}/js/lib/formwizard.js?${sbPID}"></script><!-- Can't be added to bower -->
         <script type="text/javascript" src="${srRoot}/js/parsers.js?${sbPID}"></script>
-        <script type="text/javascript" src="${srRoot}/js/meta.js?${sbPID}"></script>
+        <script type="text/javascript" src="${srRoot}/js/rootDirs.js?${sbPID}"></script>
         % if sickbeard.DEVELOPER:
         <script type="text/javascript" src="${srRoot}/js/core.js?${sbPID}"></script>
         % else:
diff --git a/gui/slick/views/login.mako b/gui/slick/views/login.mako
index 57a20f0cc9a353810446bacf5db65fd484b7c9f1..a2388f883940e5cc2761398494e29b0ea918ff77 100644
--- a/gui/slick/views/login.mako
+++ b/gui/slick/views/login.mako
@@ -3,7 +3,7 @@
 <div class="login">
     <form action="" method="post">
         <h1>SickRage</h1>
-        <div class="ctrlHolder"><input class="inlay" name="username" type="text" placeholder="Username" autocomplete="off" /></div>
+        <div class="ctrlHolder"><input class="inlay" name="username" type="text" placeholder="Username" autocomplete="off" autocomplete="no" /></div>
         <div class="ctrlHolder"><input class="inlay" name="password" type="password" placeholder="Password" autocomplete="off" /></div>
         <div class="ctrlHolder">
             <label class="remember_me" title="for 30 days"><input class="inlay" id="remember_me" name="remember_me" type="checkbox" value="1"checked="checked" /> Remember me</label>
diff --git a/gui/slick/views/manage.mako b/gui/slick/views/manage.mako
index bcac1e4bd9a5cbad61c83e517c6d20c9e0be3aeb..f2891bf374bb6c424ca4761ea4fe1e78ead6d084 100644
--- a/gui/slick/views/manage.mako
+++ b/gui/slick/views/manage.mako
@@ -16,7 +16,7 @@
 % endif
 <form name="massUpdateForm" method="post" action="massUpdate">
 
-<table id="massUpdateTable" class="sickbeardTable tablesorter" cellspacing="1" border="0" cellpadding="0">
+<table id="massUpdateTable" class="tablesorter" cellspacing="1" border="0" cellpadding="0">
     <thead>
         <tr>
             <th class="col-checkbox">Edit<br><input type="checkbox" class="bulkCheck" id="editCheck" /></th>
@@ -29,7 +29,7 @@
             <th class="col-legend">Archive first match</th>
             <th class="col-legend">Paused</th>
             <th class="col-legend">Subtitle</th>
-            <th class="col-legend">Default Ep<br>Status</th>
+            <th class="col-legend">Default Ep Status</th>
             <th class="col-legend">Status</th>
             <th width="1%">Update<br><input type="checkbox" class="bulkCheck" id="updateCheck" /></th>
             <th width="1%">Rescan<br><input type="checkbox" class="bulkCheck" id="refreshCheck" /></th>
diff --git a/gui/slick/views/manage_massEdit.mako b/gui/slick/views/manage_massEdit.mako
index 0892016a97e75f1e78b853719456eb3d2fefc9cf..a1cbd763d5d9d11c87b066b5a8ff0f3eb052da0a 100644
--- a/gui/slick/views/manage_massEdit.mako
+++ b/gui/slick/views/manage_massEdit.mako
@@ -75,7 +75,7 @@
                                                     <a href="#" class="btn edit_root_dir" class="edit_root_dir" id="edit_root_dir_${cur_index}">Edit</a>
                                                     <a href="#" class="btn delete_root_dir" class="delete_root_dir" id="delete_root_dir_${cur_index}">Delete</a>
                                                     <input type="hidden" name="orig_root_dir_${cur_index}" value="${cur_dir}" />
-                                                    <input type="text" style="display: none" name="new_root_dir_${cur_index}" id="new_root_dir_${cur_index}" class="new_root_dir" value="${cur_dir}" />
+                                                    <input type="text" style="display: none" name="new_root_dir_${cur_index}" id="new_root_dir_${cur_index}" class="new_root_dir" value="${cur_dir}" autocapitalize="off" />
                                                 </td>
                                             </tr>
                                         % endfor
diff --git a/gui/slick/views/manage_subtitleMissed.mako b/gui/slick/views/manage_subtitleMissed.mako
index d488199aa6787ab8213e2a97c0c81c04ddfa9278..25d8442888f380f71b78b1f8afad353b544da957 100644
--- a/gui/slick/views/manage_subtitleMissed.mako
+++ b/gui/slick/views/manage_subtitleMissed.mako
@@ -6,56 +6,53 @@
     from sickbeard import common
 %>
 <%block name="content">
-<div id="content960">
-% if not header is UNDEFINED:
-    <h1 class="header">${header}</h1>
-% else:
-    <h1 class="title">${title}</h1>
-% endif
-% if whichSubs:
-<% subsLanguage = subtitles.fromietf(whichSubs).name if not whichSubs == 'all' else 'All' %>
-% endif
-% if not whichSubs or (whichSubs and not ep_counts):
+    <div id="content960">
+    % if not header is UNDEFINED:
+        <h1 class="header">${header}</h1>
+    % else:
+        <h1 class="title">${title}</h1>
+    % endif
+    % if whichSubs:
+        <% subsLanguage = subtitles.name_from_code(whichSubs) if not whichSubs == 'all' else 'All' %>
+    % endif
+    % if not whichSubs or (whichSubs and not ep_counts):
+        % if whichSubs:
+        <h2>All of your episodes have ${subsLanguage} subtitles.</h2>
+        <br>
+        % endif
 
-% if whichSubs:
-<h2>All of your episodes have ${subsLanguage} subtitles.</h2>
-<br>
-% endif
+        <form action="${srRoot}/manage/subtitleMissed" method="get">
+            Manage episodes without <select name="whichSubs" class="form-control form-control-inline input-sm">
+            <option value="all">All</option>
+            % for sub_code in subtitles.wanted_languages():
+                <option value="${sub_code}">${subtitles.name_from_code(sub_code)}</option>
+            % endfor
+            </select>
 
-<form action="${srRoot}/manage/subtitleMissed" method="get">
-Manage episodes without <select name="whichSubs" class="form-control form-control-inline input-sm">
-<option value="all">All</option>
-<% sub_langs = [subtitles.fromietf(x) for x in subtitles.wantedLanguages()] %>
-% for sub_lang in sub_langs:
-<option value="${sub_lang.opensubtitles}">${sub_lang.name}</option>
-% endfor
-</select>
-subtitles
-<input class="btn" type="submit" value="Manage" />
-</form>
+            <input class="btn" type="submit" value="Manage" />
+        </form>
 
-% else:
-<input type="hidden" id="selectSubLang" name="selectSubLang" value="${whichSubs}" />
-
-<form action="${srRoot}/manage/downloadSubtitleMissed" method="post">
-<h2>Episodes without ${subsLanguage} subtitles.</h2>
-<br>
-Download missed subtitles for selected episodes <input class="btn btn-inline" type="submit" value="Go" />
-<div>
-    <button type="button" class="btn btn-xs selectAllShows">Select all</a></button>
-    <button type="button" class="btn btn-xs unselectAllShows">Clear all</a></button>
-</div>
-<br>
-<table class="sickbeardTable manageTable" cellspacing="1" border="0" cellpadding="0">
-% for cur_indexer_id in sorted_show_ids:
- <tr id="${cur_indexer_id}">
-  <th><input type="checkbox" class="allCheck" id="allCheck-${cur_indexer_id}" name="${cur_indexer_id}-all"checked="checked" /></th>
-  <th colspan="3" style="width: 100%; text-align: left;"><a class="whitelink" href="${srRoot}/home/displayShow?show=${cur_indexer_id}">${show_names[cur_indexer_id]}</a> (${ep_counts[cur_indexer_id]}) <input type="button" class="pull-right get_more_eps btn" id="${cur_indexer_id}" value="Expand" /></th>
- </tr>
-% endfor
-</table>
-</form>
-
-% endif
-</div>
+    % else:
+        ##Strange that this is used by js but is an input outside of any form?
+        <input type="hidden" id="selectSubLang" name="selectSubLang" value="${whichSubs}" />
+        <form action="${srRoot}/manage/downloadSubtitleMissed" method="post">
+            <h2>Episodes without ${subsLanguage} subtitles.</h2>
+            <br>
+            Download missed subtitles for selected episodes <input class="btn btn-inline" type="submit" value="Go" />
+            <div>
+                <button type="button" class="btn btn-xs selectAllShows">Select all</a></button>
+                <button type="button" class="btn btn-xs unselectAllShows">Clear all</a></button>
+            </div>
+            <br>
+            <table class="sickbeardTable manageTable" cellspacing="1" border="0" cellpadding="0">
+            % for cur_indexer_id in sorted_show_ids:
+                <tr id="${cur_indexer_id}">
+                    <th><input type="checkbox" class="allCheck" id="allCheck-${cur_indexer_id}" name="${cur_indexer_id}-all"checked="checked" /></th>
+                    <th colspan="3" style="width: 100%; text-align: left;"><a class="whitelink" href="${srRoot}/home/displayShow?show=${cur_indexer_id}">${show_names[cur_indexer_id]}</a> (${ep_counts[cur_indexer_id]}) <input type="button" class="pull-right get_more_eps btn" id="${cur_indexer_id}" value="Expand" /></th>
+                </tr>
+            % endfor
+            </table>
+        </form>
+    % endif
+    </div>
 </%block>
diff --git a/gui/slick/views/schedule.mako b/gui/slick/views/schedule.mako
index ff76a17c3b8ca220b86e46a71de968345f04a844..215d12dc495b1b49fc6c9d26792e940b5c05e4d0 100644
--- a/gui/slick/views/schedule.mako
+++ b/gui/slick/views/schedule.mako
@@ -288,7 +288,7 @@
     <div class="tvshowDiv">
         <table width="100%" border="0" cellpadding="0" cellspacing="0">
         <tr>
-            <th ${('class="nobg"', 'rowspan="2"')[banner == layout]} valign="top">
+            <th ${('class="nobg"', 'rowspan="2"')[layout == 'poster']} valign="top">
                 <a href="${srRoot}/home/displayShow?show=${cur_result['showid']}">
                     <img alt="" class="${('posterThumb', 'bannerThumb')[layout == 'banner']}" src="${srRoot}/showPoster/?show=${cur_result['showid']}&amp;which=${(layout, 'poster_thumb')[layout == 'poster']}" />
                 </a>
@@ -324,10 +324,10 @@
                     <span class="title">Quality:</span>
                     ${renderQualityPill(cur_result['quality'], showTitle=True)}
                 </div>
-            </td>
-        </tr>
-        <tr>
-            <td style="vertical-align: top;">
+            ##</td>
+        ##</tr>
+        ##<tr>
+            ##<td style="vertical-align: top;">
                 <div>
 % if cur_result['description']:
                         <span class="title" style="vertical-align:middle;">Plot:</span>
diff --git a/gui/slick/views/trendingShows.mako b/gui/slick/views/trendingShows.mako
index 0a8ab311793775dbadf734c5b8602db28402722e..ccd207d0cd06cfd5a6c63440b58d690a3103011d 100644
--- a/gui/slick/views/trendingShows.mako
+++ b/gui/slick/views/trendingShows.mako
@@ -38,9 +38,9 @@
             <p>${int(cur_show['show']['rating']*10)}% <img src="${srRoot}/images/heart.png"></p>
             <i>${cur_show['show']['votes']} votes</i>
             <div class="traktShowTitleIcons">
-                <a href="${srRoot}/home/addShows/addTraktShow?indexer_id=${cur_show['show']['ids']['tvdb']}&amp;showName=${cur_show['show']['title']}" class="btn btn-xs">Add Show</a>
+                <a href="${srRoot}/addShows/addShowByID?indexer_id=${cur_show['show']['ids']['tvdb']}&amp;showName=${cur_show['show']['title']}" class="btn btn-xs" data-no-redirect>Add Show</a>
                 % if blacklist:
-                <a href="${srRoot}/home/addShows/addShowToBlacklist?indexer_id=${cur_show['show']['ids']['tvdb'] or cur_show['show']['ids']['tvrage']}" class="btn btn-xs">Remove Show</a>
+                <a href="${srRoot}/addShows/addShowToBlacklist?indexer_id=${cur_show['show']['ids']['tvdb'] or cur_show['show']['ids']['tvrage']}" class="btn btn-xs">Remove Show</a>
                 % endif
             </div>
         </div>
diff --git a/gui/slick/views/viewlogs.mako b/gui/slick/views/viewlogs.mako
index 73e7200c149333126a653410ef38d3306ff1ac0e..3c54383446dab0abaf325a97417b5e9eb9322736 100644
--- a/gui/slick/views/viewlogs.mako
+++ b/gui/slick/views/viewlogs.mako
@@ -37,7 +37,7 @@ Filter log by: <select name="logFilter" id="logFilter" class="form-control form-
 % endfor
 </select>
 Search log by:
-<input type="text" name="logSearch" placeholder="clear to reset" id="logSearch" value="${('', logSearch)[bool(logSearch)]}" class="form-control form-control-inline input-sm" />
+<input type="text" name="logSearch" placeholder="clear to reset" id="logSearch" value="${('', logSearch)[bool(logSearch)]}" class="form-control form-control-inline input-sm" autocapitalize="off" />
 </div>
 <br>
 <div class="align-left"><pre>
diff --git a/lib/github/CommitStatus.py b/lib/github/CommitStatus.py
index dad911dd912e8baf87a3afc04833625ded8777d0..d6074ea8546e36f11cc8d7059e9577be904e5991 100644
--- a/lib/github/CommitStatus.py
+++ b/lib/github/CommitStatus.py
@@ -31,7 +31,7 @@ import github.NamedUser
 
 class CommitStatus(github.GithubObject.NonCompletableGithubObject):
     """
-    This class represents CommitStatuss as returned for example by http://developer.github.com/v3/todo
+    This class represents CommitStatuss as returned for example by https://developer.github.com/v3/repos/statuses/
     """
 
     @property
diff --git a/lib/github/PaginatedList.py b/lib/github/PaginatedList.py
index a777654aff9d3036d6d279cd328d715445a28dc4..12e1acc7e15f89a4b9f92dc9136e6822331053c7 100644
--- a/lib/github/PaginatedList.py
+++ b/lib/github/PaginatedList.py
@@ -106,7 +106,7 @@ class PaginatedList(PaginatedListBase):
         some_other_repos = user.get_repos().get_page(3)
     """
 
-    def __init__(self, contentClass, requester, firstUrl, firstParams):
+    def __init__(self, contentClass, requester, firstUrl, firstParams, headers=None):
         PaginatedListBase.__init__(self)
         self.__requester = requester
         self.__contentClass = contentClass
@@ -114,6 +114,7 @@ class PaginatedList(PaginatedListBase):
         self.__firstParams = firstParams or ()
         self.__nextUrl = firstUrl
         self.__nextParams = firstParams or {}
+        self.__headers = headers
         if self.__requester.per_page != 30:
             self.__nextParams["per_page"] = self.__requester.per_page
         self._reversed = False
@@ -130,7 +131,8 @@ class PaginatedList(PaginatedListBase):
         headers, data = self.__requester.requestJsonAndCheck(
             "GET",
             self.__firstUrl,
-            parameters=self.__nextParams
+            parameters=self.__nextParams,
+            headers=self.__headers
         )
         links = self.__parseLinkHeader(headers)
         lastUrl = links.get("last")
@@ -155,7 +157,8 @@ class PaginatedList(PaginatedListBase):
         headers, data = self.__requester.requestJsonAndCheck(
             "GET",
             self.__nextUrl,
-            parameters=self.__nextParams
+            parameters=self.__nextParams,
+            headers=self.__headers
         )
         data = data if data else []
 
@@ -201,7 +204,8 @@ class PaginatedList(PaginatedListBase):
         headers, data = self.__requester.requestJsonAndCheck(
             "GET",
             self.__firstUrl,
-            parameters=params
+            parameters=params,
+            headers=self.__headers
         )
 
         if 'items' in data:
diff --git a/lib/github/Repository.py b/lib/github/Repository.py
index 640bb857879cae41f25f8b4f28eba45c7d8e6bae..64c96e3eabac67388688cdd70b1e2775565878ef 100644
--- a/lib/github/Repository.py
+++ b/lib/github/Repository.py
@@ -11,6 +11,7 @@
 # Copyright 2013 Mark Roddy <markroddy@gmail.com>                              #
 # Copyright 2013 Vincent Jacques <vincent@vincent-jacques.net>                 #
 # Copyright 2013 martinqt <m.ki2@laposte.net>                                  #
+# Copyright 2015 Jannis Gebauer <ja.geb@me.com>                                #
 #                                                                              #
 # This file is part of PyGithub. http://jacquev6.github.com/PyGithub/          #
 #                                                                              #
@@ -67,6 +68,7 @@ import github.StatsCommitActivity
 import github.StatsCodeFrequency
 import github.StatsParticipation
 import github.StatsPunchCard
+import github.Stargazer
 
 
 class Repository(github.GithubObject.CompletableGithubObject):
@@ -1430,7 +1432,7 @@ class Repository(github.GithubObject.CompletableGithubObject):
         )
         return github.Issue.Issue(self._requester, headers, data, completed=True)
 
-    def get_issues(self, milestone=github.GithubObject.NotSet, state=github.GithubObject.NotSet, assignee=github.GithubObject.NotSet, mentioned=github.GithubObject.NotSet, labels=github.GithubObject.NotSet, sort=github.GithubObject.NotSet, direction=github.GithubObject.NotSet, since=github.GithubObject.NotSet):
+    def get_issues(self, milestone=github.GithubObject.NotSet, state=github.GithubObject.NotSet, assignee=github.GithubObject.NotSet, mentioned=github.GithubObject.NotSet, labels=github.GithubObject.NotSet, sort=github.GithubObject.NotSet, direction=github.GithubObject.NotSet, since=github.GithubObject.NotSet, creator=github.GithubObject.NotSet):
         """
         :calls: `GET /repos/:owner/:repo/issues <http://developer.github.com/v3/issues>`_
         :param milestone: :class:`github.Milestone.Milestone` or "none" or "*"
@@ -1441,6 +1443,7 @@ class Repository(github.GithubObject.CompletableGithubObject):
         :param sort: string
         :param direction: string
         :param since: datetime.datetime
+        :param creator: string or :class:`github.NamedUser.NamedUser`
         :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.Issue.Issue`
         """
         assert milestone is github.GithubObject.NotSet or milestone == "*" or milestone == "none" or isinstance(milestone, github.Milestone.Milestone), milestone
@@ -1451,6 +1454,7 @@ class Repository(github.GithubObject.CompletableGithubObject):
         assert sort is github.GithubObject.NotSet or isinstance(sort, (str, unicode)), sort
         assert direction is github.GithubObject.NotSet or isinstance(direction, (str, unicode)), direction
         assert since is github.GithubObject.NotSet or isinstance(since, datetime.datetime), since
+        assert creator is github.GithubObject.NotSet or isinstance(creator, github.NamedUser.NamedUser) or isinstance(creator, (str, unicode)), creator
         url_parameters = dict()
         if milestone is not github.GithubObject.NotSet:
             if isinstance(milestone, str):
@@ -1474,6 +1478,11 @@ class Repository(github.GithubObject.CompletableGithubObject):
             url_parameters["direction"] = direction
         if since is not github.GithubObject.NotSet:
             url_parameters["since"] = since.strftime("%Y-%m-%dT%H:%M:%SZ")
+        if creator is not github.GithubObject.NotSet:
+            if isinstance(creator, str):
+                url_parameters["creator"] = creator
+            else:
+                url_parameters["creator"] = creator._identity
         return github.PaginatedList.PaginatedList(
             github.Issue.Issue,
             self._requester,
@@ -1655,19 +1664,28 @@ class Repository(github.GithubObject.CompletableGithubObject):
         )
         return github.PullRequest.PullRequest(self._requester, headers, data, completed=True)
 
-    def get_pulls(self, state=github.GithubObject.NotSet, sort=github.GithubObject.NotSet):
+    def get_pulls(self, state=github.GithubObject.NotSet, sort=github.GithubObject.NotSet, direction=github.GithubObject.NotSet, base=github.GithubObject.NotSet):
         """
         :calls: `GET /repos/:owner/:repo/pulls <http://developer.github.com/v3/pulls>`_
         :param state: string
+        :param sort: string
+        :param direction: string
+        :param base: string
         :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.PullRequest.PullRequest`
         """
         assert state is github.GithubObject.NotSet or isinstance(state, (str, unicode)), state
         assert sort is github.GithubObject.NotSet or isinstance(sort, (str, unicode)), sort
+        assert direction is github.GithubObject.NotSet or isinstance(direction, (str, unicode)), direction
+        assert base is github.GithubObject.NotSet or isinstance(base, (str, unicode)), base
         url_parameters = dict()
         if state is not github.GithubObject.NotSet:
             url_parameters["state"] = state
         if sort is not github.GithubObject.NotSet:
             url_parameters["sort"] = sort
+        if direction is not github.GithubObject.NotSet:
+            url_parameters["direction"] = direction
+        if base is not github.GithubObject.NotSet:
+            url_parameters["base"] = base
         return github.PaginatedList.PaginatedList(
             github.PullRequest.PullRequest,
             self._requester,
@@ -1739,6 +1757,19 @@ class Repository(github.GithubObject.CompletableGithubObject):
             None
         )
 
+    def get_stargazers_with_dates(self):
+        """
+        :calls: `GET /repos/:owner/:repo/stargazers <http://developer.github.com/v3/activity/starring>`_
+        :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.Stargazer.Stargazer`
+        """
+        return github.PaginatedList.PaginatedList(
+            github.Stargazer.Stargazer,
+            self._requester,
+            self.url + "/stargazers",
+            None,
+            headers={'Accept': 'application/vnd.github.v3.star+json'}
+        )
+
     def get_stats_contributors(self):
         """
         :calls: `GET /repos/:owner/:repo/stats/contributors <http://developer.github.com/v3/repos/statistics/#get-contributors-list-with-additions-deletions-and-commit-counts>`_
diff --git a/lib/github/Stargazer.py b/lib/github/Stargazer.py
new file mode 100644
index 0000000000000000000000000000000000000000..4f261c64cd8538457c165bced148a495d62bd1a7
--- /dev/null
+++ b/lib/github/Stargazer.py
@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+
+# ########################## Copyrights and license ############################
+#                                                                              #
+# Copyright 2012 Christopher Gilbert <christopher.john.gilbert@gmail.com>      #
+# Copyright 2012 Steve English <steve.english@navetas.com>                     #
+# Copyright 2012 Vincent Jacques <vincent@vincent-jacques.net>                 #
+# Copyright 2012 Zearin <zearin@gonk.net>                                      #
+# Copyright 2013 AKFish <akfish@gmail.com>                                     #
+# Copyright 2013 Adrian Petrescu <adrian.petrescu@maluuba.com>                 #
+# Copyright 2013 Mark Roddy <markroddy@gmail.com>                              #
+# Copyright 2013 Vincent Jacques <vincent@vincent-jacques.net>                 #
+# Copyright 2013 martinqt <m.ki2@laposte.net>                                  #
+# Copyright 2015 Dan Vanderkam <danvdk@gmail.com>                              #
+#                                                                              #
+# This file is part of PyGithub. http://jacquev6.github.com/PyGithub/          #
+#                                                                              #
+# PyGithub is free software: you can redistribute it and/or modify it under    #
+# the terms of the GNU Lesser General Public License as published by the Free  #
+# Software Foundation, either version 3 of the License, or (at your option)    #
+# any later version.                                                           #
+#                                                                              #
+# PyGithub 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 Lesser General Public License for more #
+# details.                                                                     #
+#                                                                              #
+# You should have received a copy of the GNU Lesser General Public License     #
+# along with PyGithub. If not, see <http://www.gnu.org/licenses/>.             #
+#                                                                              #
+# ##############################################################################
+
+import github
+
+
+class Stargazer(github.GithubObject.NonCompletableGithubObject):
+    """
+    This class represents Stargazers with the date of starring as returned by
+    https://developer.github.com/v3/activity/starring/#alternative-response-with-star-creation-timestamps
+    """
+    @property
+    def starred_at(self):
+        """
+        :type: datetime.datetime
+        """
+        return self._starred_at.value
+
+    @property
+    def user(self):
+        """
+        :type: :class:`github.NamedUser`
+        """
+        return self._user.value
+
+    def _initAttributes(self):
+        self._starred_at = github.GithubObject.NotSet
+        self._user = github.GithubObject.NotSet
+        self._url = github.GithubObject.NotSet
+
+    def _useAttributes(self, attributes):
+        if 'starred_at' in attributes:
+            self._starred_at = self._makeDatetimeAttribute(attributes['starred_at'])
+        if 'user' in attributes:
+            self._user = self._makeClassAttribute(github.NamedUser.NamedUser, attributes['user'])
diff --git a/lib/github/StatsCodeFrequency.py b/lib/github/StatsCodeFrequency.py
old mode 100644
new mode 100755
diff --git a/lib/github/StatsCommitActivity.py b/lib/github/StatsCommitActivity.py
old mode 100644
new mode 100755
diff --git a/lib/github/StatsContributor.py b/lib/github/StatsContributor.py
old mode 100644
new mode 100755
diff --git a/lib/github/StatsParticipation.py b/lib/github/StatsParticipation.py
old mode 100644
new mode 100755
diff --git a/lib/github/StatsPunchCard.py b/lib/github/StatsPunchCard.py
old mode 100644
new mode 100755
diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py
index 4079965bb78696d3e66592d570f0e4e89ccf99bd..61330544b7c66c5beed6ca74e4b1d109353e7e25 100644
--- a/sickbeard/__init__.py
+++ b/sickbeard/__init__.py
@@ -55,7 +55,10 @@ from sickbeard.common import SD
 from sickbeard.common import SKIPPED
 from sickbeard.common import WANTED
 from sickbeard.databases import mainDB, cache_db, failed_db
+
+from sickrage.helper.encoding import ek
 from sickrage.helper.exceptions import ex
+from sickrage.show.Show import Show
 from sickrage.system.Shutdown import Shutdown
 
 from configobj import ConfigObj
@@ -663,7 +666,7 @@ def initialize(consoleLogging=True):
             DEFAULT_PAGE = 'home'
 
         ACTUAL_LOG_DIR = check_setting_str(CFG, 'General', 'log_dir', 'Logs')
-        LOG_DIR = os.path.normpath(os.path.join(DATA_DIR, ACTUAL_LOG_DIR))
+        LOG_DIR = ek(os.path.normpath, ek(os.path.join, DATA_DIR, ACTUAL_LOG_DIR))
         LOG_NR = check_setting_int(CFG, 'General', 'log_nr', 5)  # Default to 5 backup file (sickrage.log.x)
         LOG_SIZE = check_setting_int(CFG, 'General', 'log_size', 1048576)  # Default to max 1MB per logfile
         fileLogging = True
@@ -682,7 +685,7 @@ def initialize(consoleLogging=True):
                 gh = Github(login_or_token=GIT_USERNAME, password=GIT_PASSWORD, user_agent="SiCKRAGE").get_organization(GIT_ORG).get_repo(GIT_REPO)
         except Exception as e:
             gh = None
-            logger.log(u'Unable to setup GitHub properly. GitHub will not be available. Error: %s' % ex(e), logger.WARNING)
+            logger.log(u'Unable to setup GitHub properly. GitHub will not be available. Error: %s' % str(e), logger.WARNING)
 
         # git reset on update
         GIT_RESET = bool(check_setting_int(CFG, 'General', 'git_reset', 1))
@@ -711,8 +714,8 @@ def initialize(consoleLogging=True):
             ACTUAL_CACHE_DIR = 'cache'
 
         # unless they specify, put the cache dir inside the data dir
-        if not os.path.isabs(ACTUAL_CACHE_DIR):
-            CACHE_DIR = os.path.join(DATA_DIR, ACTUAL_CACHE_DIR)
+        if not ek(os.path.isabs, ACTUAL_CACHE_DIR):
+            CACHE_DIR = ek(os.path.join, DATA_DIR, ACTUAL_CACHE_DIR)
         else:
             CACHE_DIR = ACTUAL_CACHE_DIR
 
@@ -722,36 +725,36 @@ def initialize(consoleLogging=True):
 
         # Check if we need to perform a restore of the cache folder
         try:
-            restoreDir = os.path.join(DATA_DIR, 'restore')
-            if os.path.exists(restoreDir) and os.path.exists(os.path.join(restoreDir, 'cache')):
+            restoreDir = ek(os.path.join, DATA_DIR, 'restore')
+            if ek(os.path.exists, restoreDir) and ek(os.path.exists, ek(os.path.join, restoreDir, 'cache')):
                 def restoreCache(srcDir, dstDir):
                     def path_leaf(path):
-                        head, tail = os.path.split(path)
-                        return tail or os.path.basename(head)
+                        head, tail = ek(os.path.split, path)
+                        return tail or ek(os.path.basename, head)
 
                     try:
-                        if os.path.isdir(dstDir):
+                        if ek(os.path.isdir, dstDir):
                             bakFilename = '{0}-{1}'.format(path_leaf(dstDir), datetime.datetime.strftime(datetime.datetime.now(), '%Y%m%d_%H%M%S'))
-                            shutil.move(dstDir, os.path.join(os.path.dirname(dstDir), bakFilename))
+                            shutil.move(dstDir, ek(os.path.join, ek(os.path.dirname, dstDir), bakFilename))
 
                         shutil.move(srcDir, dstDir)
                         logger.log(u"Restore: restoring cache successful", logger.INFO)
                     except Exception as e:
                         logger.log(u"Restore: restoring cache failed: {0}".format(str(e)), logger.ERROR)
 
-                restoreCache(os.path.join(restoreDir, 'cache'), CACHE_DIR)
+                restoreCache(ek(os.path.join, restoreDir, 'cache'), CACHE_DIR)
         except Exception as e:
             logger.log(u"Restore: restoring cache failed: {0}".format(ex(e)), logger.ERROR)
         finally:
-            if os.path.exists(os.path.join(DATA_DIR, 'restore')):
+            if ek(os.path.exists, ek(os.path.join, DATA_DIR, 'restore')):
                 try:
-                    shutil.rmtree(os.path.join(DATA_DIR, 'restore'))
+                    shutil.rmtree(ek(os.path.join, DATA_DIR, 'restore'))
                 except Exception as e:
                     logger.log(u"Restore: Unable to remove the restore directory: {0}".format(ex(e)), logger.ERROR)
 
                 for cleanupDir in ['mako', 'sessions', 'indexers']:
                     try:
-                        shutil.rmtree(os.path.join(CACHE_DIR, cleanupDir))
+                        shutil.rmtree(ek(os.path.join, CACHE_DIR, cleanupDir))
                     except Exception as e:
                         logger.log(u"Restore: Unable to remove the cache/{0} directory: {1}".format(cleanupDir, ex(e)), logger.WARNING)
 
@@ -1229,7 +1232,7 @@ def initialize(consoleLogging=True):
                                                                 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)                                           
+                                                                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)
@@ -1338,7 +1341,7 @@ def initialize(consoleLogging=True):
                                                                        curNzbProvider.getID() + '_enable_backlog',
                                                                        curNzbProvider.supportsBacklog))
 
-        if not os.path.isfile(CONFIG_FILE):
+        if not ek(os.path.isfile, CONFIG_FILE):
             logger.log(u"Unable to find '" + CONFIG_FILE + "', all settings will be default!", logger.DEBUG)
             save_config()
 
@@ -2205,7 +2208,7 @@ def getEpList(epIDs, showid=None):
     epList = []
 
     for curEp in sqlResults:
-        curShowObj = helpers.findCertainShow(showList, int(curEp["showid"]))
+        curShowObj = Show.find(showList, int(curEp["showid"]))
         curEpObj = curShowObj.getEpisode(int(curEp["season"]), int(curEp["episode"]))
         epList.append(curEpObj)
 
diff --git a/sickbeard/browser.py b/sickbeard/browser.py
index 9288465ba51d605edf1c7d20e6e07dac5bb61590..021adc81c76fa8e063d450141b1f737cae20afb9 100644
--- a/sickbeard/browser.py
+++ b/sickbeard/browser.py
@@ -79,12 +79,12 @@ def foldersAtPath(path, includeParent=False, includeFiles=False):
     """
 
     # walk up the tree until we find a valid path
-    while path and not os.path.isdir(path):
-        if path == os.path.dirname(path):
+    while path and not ek(os.path.isdir, path):
+        if path == ek(os.path.dirname, path):
             path = ''
             break
         else:
-            path = os.path.dirname(path)
+            path = ek(os.path.dirname, path)
 
     if path == "":
         if os.name == 'nt':
@@ -97,8 +97,8 @@ def foldersAtPath(path, includeParent=False, includeFiles=False):
             path = '/'
 
     # fix up the path and find the parent
-    path = os.path.abspath(os.path.normpath(path))
-    parentPath = os.path.dirname(path)
+    path = ek(os.path.abspath, ek(os.path.normpath, path))
+    parentPath = ek(os.path.dirname, path)
 
     # if we're at the root then the next step is the meta-node showing our drive letters
     if path == parentPath and os.name == 'nt':
@@ -111,7 +111,7 @@ def foldersAtPath(path, includeParent=False, includeFiles=False):
         fileList = getFileList(parentPath, includeFiles)
 
     fileList = sorted(fileList,
-                      lambda x, y: cmp(os.path.basename(x['name']).lower(), os.path.basename(y['path']).lower()))
+                      lambda x, y: cmp(ek(os.path.basename, x['name']).lower(), ek(os.path.basename, y['path']).lower()))
 
     entries = [{'currentPath': path}]
     if includeParent and parentPath != path:
diff --git a/sickbeard/clients/__init__.py b/sickbeard/clients/__init__.py
index ddc11114af7adfac01cef01991ef12ae81616aa5..f1e67523c4484d193c3389757158f6dd91d8fd3f 100644
--- a/sickbeard/clients/__init__.py
+++ b/sickbeard/clients/__init__.py
@@ -28,82 +28,6 @@ __all__ = [
     'mlnet'
 ]
 
-# Mapping error status codes to official W3C names
-http_error_code = {
-    # todo: Handle error codes with duplicates (e.g. 451, 499)
-    300: 'Multiple Choices',
-    301: 'Moved Permanently',
-    302: 'Found',
-    303: 'See Other',
-    304: 'Not Modified',
-    305: 'Use Proxy',
-    306: 'Switch Proxy',
-    307: 'Temporary Redirect',
-    308: 'Permanent Redirect',
-    400: 'Bad Request',
-    401: 'Unauthorized',
-    402: 'Payment Required',
-    403: 'Forbidden',
-    404: 'Not Found',
-    405: 'Method Not Allowed',
-    406: 'Not Acceptable',
-    407: 'Proxy Authentication Required',
-    408: 'Request Timeout',
-    409: 'Conflict',
-    410: 'Gone',
-    411: 'Length Required',
-    412: 'Precondition Failed',
-    413: 'Request Entity Too Large',
-    414: 'Request-URI Too Long',
-    415: 'Unsupported Media Type',
-    416: 'Requested Range Not Satisfiable',
-    417: 'Expectation Failed',
-    418: 'Im a teapot',
-    419: 'Authentication Timeout',
-    420: 'Enhance Your Calm',
-    422: 'Unprocessable Entity',
-    423: 'Locked',
-    424: 'Failed Dependency',
-    426: 'Upgrade Required',
-    428: 'Precondition Required',
-    429: 'Too Many Requests',
-    431: 'Request Header Fields Too Large',
-    440: 'Login Timeout',
-    444: 'No Response',
-    449: 'Retry With',
-    450: 'Blocked by Windows Parental Controls',
-    451: 'Redirect',
-    451: 'Unavailable For Legal Reasons',
-    494: 'Request Header Too Large',
-    495: 'Cert Error',
-    496: 'No Cert',
-    497: 'HTTP to HTTPS',
-    498: 'Token expired/invalid',
-    499: 'Client Closed Request',
-    499: 'Token required',
-    500: 'Internal Server Error',
-    501: 'Not Implemented',
-    502: 'Bad Gateway',
-    503: 'Service Unavailable',
-    504: 'Gateway Timeout',
-    505: 'HTTP Version Not Supported',
-    506: 'Variant Also Negotiates',
-    507: 'Insufficient Storage',
-    508: 'Loop Detected',
-    509: 'Bandwidth Limit Exceeded',
-    510: 'Not Extended',
-    511: 'Network Authentication Required',
-    520: 'Cloudfare - Web server is returning an unknown error',
-    521: 'Cloudfare - Web server is down',
-    522: 'Cloudfare - Connection timed out',
-    523: 'Cloudfare - Origin is unreachable',
-    524: 'Cloudfare - A timeout occurred',
-    525: 'Cloudfare - SSL handshake failed',
-    526: 'Cloudfare - Invalid SSL certificate',
-    598: 'Network read timeout error',
-    599: 'Network connect timeout error '
-}
-
 default_host = {
     'utorrent': 'http://localhost:8000',
     'transmission': 'http://localhost:9091',
diff --git a/sickbeard/clients/generic.py b/sickbeard/clients/generic.py
index 945554403af1025bdf6606f78c9b2f0a8946c00d..2109da630a568984ea4cb3c5c78d92541ee165b3 100644
--- a/sickbeard/clients/generic.py
+++ b/sickbeard/clients/generic.py
@@ -7,10 +7,10 @@ from base64 import b16encode, b32decode
 
 import sickbeard
 from sickbeard import logger
-from sickbeard.clients import http_error_code
 from bencode import bencode, bdecode
 import requests
 from bencode.BTL import BTFailure
+from sickrage.helper.common import http_code_description
 
 
 class GenericClient(object):
@@ -66,8 +66,10 @@ class GenericClient(object):
             logger.log(self.name + u': Invalid Username or Password, check your config', logger.ERROR)
             return False
 
-        if self.response.status_code in http_error_code.keys():
-            logger.log(self.name + u': ' + http_error_code[self.response.status_code], logger.DEBUG)
+        code_description = http_code_description(self.response.status_code)
+
+        if code_description is not None:
+            logger.log(self.name + u': ' + code_description, logger.DEBUG)
             return False
 
         logger.log(self.name + u': Response to ' + method.upper() + ' request is ' + self.response.text, logger.DEBUG)
diff --git a/sickbeard/common.py b/sickbeard/common.py
index bbc240b237cd36023f2339ab3e76a6bdd09700f6..731aaa84896b266d875e8d8e09217072db5e7e74 100644
--- a/sickbeard/common.py
+++ b/sickbeard/common.py
@@ -1,3 +1,4 @@
+# coding=utf-8
 # Author: Nic Wolfe <nic@wolfeden.ca>
 # URL: https://sickrage.github.io/
 # Git: https://github.com/SickRage/SickRage.git
@@ -17,34 +18,42 @@
 # 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.path
+"""
+Common interface for Quality and Status
+"""
+
+# pylint: disable=line-too-long
+
+
 import operator
+from os import path
 import platform
+from random import shuffle
 import re
 import uuid
-from sickbeard.numdict import NumDict
-
-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 hachoir_parser import createParser  # pylint: disable=import-error
+from hachoir_metadata import extractMetadata  # pylint: disable=import-error
+from hachoir_core.log import log  # pylint: disable=import-error
 
-from random import shuffle
+from sickbeard.numdict import NumDict
+from sickrage.helper.encoding import ek
 
 SPOOF_USER_AGENT = False
 
 # If some provider has an issue with functionality of SR, other than user agents, it's best to come talk to us rather than block.
 # It is no different than us going to a provider if we have questions or issues. Be a team player here.
 # This is disabled, was only added for testing, and has no config.ini or web ui setting. To enable, set SPOOF_USER_AGENT = True
-user_agents = ['Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36'
-               'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.1 Safari/537.36'
-               'Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.0'
-               'Mozilla/5.0 (X11; Linux i586; rv:31.0) Gecko/20100101 Firefox/31.0'
-               'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/7046A194A'
-               'Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25'
-               'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko'
-               'Mozilla/5.0 (compatible, MSIE 11, Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko'
-              ]
+user_agents = [
+    'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36'
+    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.1 Safari/537.36'
+    'Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.0'
+    'Mozilla/5.0 (X11; Linux i586; rv:31.0) Gecko/20100101 Firefox/31.0'
+    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/7046A194A'
+    'Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25'
+    'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko'
+    'Mozilla/5.0 (compatible, MSIE 11, Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko'
+]
 
 INSTANCE_ID = str(uuid.uuid1())
 USER_AGENT = ('SickRage/(' + platform.system() + '; ' + platform.release() + '; ' + INSTANCE_ID + ')')
@@ -53,10 +62,11 @@ if SPOOF_USER_AGENT:
     shuffle(user_agents)
     USER_AGENT = user_agents[0]
 
-cpu_presets = {'HIGH': 5,
-               'NORMAL': 2,
-               'LOW': 1
-              }
+cpu_presets = {
+    'HIGH': 5,
+    'NORMAL': 2,
+    'LOW': 1
+}
 
 # Other constants
 MULTI_EP_RESULT = -1
@@ -69,12 +79,13 @@ NOTIFY_SUBTITLE_DOWNLOAD = 3
 NOTIFY_GIT_UPDATE = 4
 NOTIFY_GIT_UPDATE_TEXT = 5
 
-notifyStrings = {}
-notifyStrings[NOTIFY_SNATCH] = "Started Download"
-notifyStrings[NOTIFY_DOWNLOAD] = "Download Finished"
-notifyStrings[NOTIFY_SUBTITLE_DOWNLOAD] = "Subtitle Download Finished"
-notifyStrings[NOTIFY_GIT_UPDATE] = "SickRage Updated"
-notifyStrings[NOTIFY_GIT_UPDATE_TEXT] = "SickRage Updated To Commit#: "
+notifyStrings = NumDict({
+    NOTIFY_SNATCH: "Started Download",
+    NOTIFY_DOWNLOAD: "Download Finished",
+    NOTIFY_SUBTITLE_DOWNLOAD: "Subtitle Download Finished",
+    NOTIFY_GIT_UPDATE: "SickRage Updated",
+    NOTIFY_GIT_UPDATE_TEXT: "SickRage Updated To Commit#: "
+})
 
 # Episode statuses
 UNKNOWN = -1  # should never happen
@@ -88,7 +99,7 @@ IGNORED = 7  # episodes that you don't want included in your download stats
 SNATCHED_PROPER = 9  # qualified with quality
 SUBTITLED = 10  # qualified with quality
 FAILED = 11  # episode downloaded or snatched we don't want
-SNATCHED_BEST = 12  # episode redownloaded using best quality
+SNATCHED_BEST = 12  # episode re-downloaded using best quality
 
 NAMING_REPEAT = 1
 NAMING_EXTEND = 2
@@ -97,16 +108,20 @@ NAMING_LIMITED_EXTEND = 8
 NAMING_SEPARATED_REPEAT = 16
 NAMING_LIMITED_EXTEND_E_PREFIXED = 32
 
-multiEpStrings = {}
-multiEpStrings[NAMING_REPEAT] = "Repeat"
-multiEpStrings[NAMING_SEPARATED_REPEAT] = "Repeat (Separated)"
-multiEpStrings[NAMING_DUPLICATE] = "Duplicate"
-multiEpStrings[NAMING_EXTEND] = "Extend"
-multiEpStrings[NAMING_LIMITED_EXTEND] = "Extend (Limited)"
-multiEpStrings[NAMING_LIMITED_EXTEND_E_PREFIXED] = "Extend (Limited, E-prefixed)"
+MULTI_EP_STRINGS = NumDict({
+    NAMING_REPEAT: "Repeat",
+    NAMING_SEPARATED_REPEAT: "Repeat (Separated)",
+    NAMING_DUPLICATE: "Duplicate",
+    NAMING_EXTEND: "Extend",
+    NAMING_LIMITED_EXTEND: "Extend (Limited)",
+    NAMING_LIMITED_EXTEND_E_PREFIXED: "Extend (Limited, E-prefixed)"
+})
+
 
-# pylint: disable=W0232
 class Quality(object):
+    """
+    Determine quality and set status codes
+    """
     NONE = 0  # 0
     SDTV = 1  # 1
     SDDVD = 1 << 1  # 2
@@ -124,55 +139,66 @@ class Quality(object):
     # put these bits at the other end of the spectrum, far enough out that they shouldn't interfere
     UNKNOWN = 1 << 15  # 32768
 
-    qualityStrings = {NONE: "N/A",
-                      UNKNOWN: "Unknown",
-                      SDTV: "SDTV",
-                      SDDVD: "SD DVD",
-                      HDTV: "720p HDTV",
-                      RAWHDTV: "RawHD",
-                      FULLHDTV: "1080p HDTV",
-                      HDWEBDL: "720p WEB-DL",
-                      FULLHDWEBDL: "1080p WEB-DL",
-                      HDBLURAY: "720p BluRay",
-                      FULLHDBLURAY: "1080p BluRay"}
-
-    sceneQualityStrings = {NONE: "N/A",
-                           UNKNOWN: "Unknown",
-                           SDTV: "HDTV",
-                           SDDVD: "",
-                           HDTV: "720p HDTV",
-                           RAWHDTV: "1080i HDTV",
-                           FULLHDTV: "1080p HDTV",
-                           HDWEBDL: "720p WEB-DL",
-                           FULLHDWEBDL: "1080p WEB-DL",
-                           HDBLURAY: "720p BluRay",
-                           FULLHDBLURAY: "1080p BluRay"}
-
-    combinedQualityStrings = {ANYHDTV: "HDTV",
-                              ANYWEBDL: "WEB-DL",
-                              ANYBLURAY: "BluRay"}
-
-    cssClassStrings = {NONE: "N/A",
-                       UNKNOWN: "Unknown",
-                       SDTV: "SDTV",
-                       SDDVD: "SDDVD",
-                       HDTV: "HD720p",
-                       RAWHDTV: "RawHD",
-                       FULLHDTV: "HD1080p",
-                       HDWEBDL: "HD720p",
-                       FULLHDWEBDL: "HD1080p",
-                       HDBLURAY: "HD720p",
-                       FULLHDBLURAY: "HD1080p",
-                       ANYHDTV: "any-hd",
-                       ANYWEBDL: "any-hd",
-                       ANYBLURAY: "any-hd"}
-
-    statusPrefixes = {DOWNLOADED: "Downloaded",
-                      SNATCHED: "Snatched",
-                      SNATCHED_PROPER: "Snatched (Proper)",
-                      FAILED: "Failed",
-                      SNATCHED_BEST: "Snatched (Best)",
-                      ARCHIVED: "Archived"}
+    qualityStrings = NumDict({
+        NONE: "N/A",
+        UNKNOWN: "Unknown",
+        SDTV: "SDTV",
+        SDDVD: "SD DVD",
+        HDTV: "720p HDTV",
+        RAWHDTV: "RawHD",
+        FULLHDTV: "1080p HDTV",
+        HDWEBDL: "720p WEB-DL",
+        FULLHDWEBDL: "1080p WEB-DL",
+        HDBLURAY: "720p BluRay",
+        FULLHDBLURAY: "1080p BluRay"
+    })
+
+    sceneQualityStrings = NumDict({
+        NONE: "N/A",
+        UNKNOWN: "Unknown",
+        SDTV: "HDTV",
+        SDDVD: "",
+        HDTV: "720p HDTV",
+        RAWHDTV: "1080i HDTV",
+        FULLHDTV: "1080p HDTV",
+        HDWEBDL: "720p WEB-DL",
+        FULLHDWEBDL: "1080p WEB-DL",
+        HDBLURAY: "720p BluRay",
+        FULLHDBLURAY: "1080p BluRay"
+    })
+
+    combinedQualityStrings = NumDict({
+        ANYHDTV: "HDTV",
+        ANYWEBDL: "WEB-DL",
+        ANYBLURAY: "BluRay"
+    })
+
+    cssClassStrings = NumDict({
+        NONE: "N/A",
+        UNKNOWN: "Unknown",
+        SDTV: "SDTV",
+        SDDVD: "SDDVD",
+        HDTV: "HD720p",
+        RAWHDTV: "RawHD",
+        FULLHDTV: "HD1080p",
+        HDWEBDL: "HD720p",
+        FULLHDWEBDL: "HD1080p",
+        HDBLURAY: "HD720p",
+        FULLHDBLURAY: "HD1080p",
+        ANYHDTV: "any-hd",
+        ANYWEBDL: "any-hd",
+        ANYBLURAY: "any-hd"
+    })
+
+    statusPrefixes = NumDict({
+        DOWNLOADED: "Downloaded",
+        SNATCHED: "Snatched",
+        SNATCHED_PROPER: "Snatched (Proper)",
+        FAILED: "Failed",
+        SNATCHED_BEST: "Snatched (Best)",
+        ARCHIVED: "Archived"
+    })
+
     @staticmethod
     def _getStatusStrings(status):
         """
@@ -181,33 +207,33 @@ class Quality(object):
         :param status: Status prefix to resolve
         :return: Human readable status value
         """
-        toReturn = {}
-        for q in Quality.qualityStrings.keys():
-            toReturn[Quality.compositeStatus(status, q)] = Quality.statusPrefixes[status] + " (" + \
-                                                           Quality.qualityStrings[q] + ")"
-        return toReturn
+        to_return = {}
+        for quality in Quality.qualityStrings:
+            to_return[Quality.compositeStatus(status, quality)] = Quality.statusPrefixes[status] + " (" + \
+                                                           Quality.qualityStrings[quality] + ")"
+        return to_return
 
     @staticmethod
-    def combineQualities(anyQualities, bestQualities):
-        anyQuality = 0
-        bestQuality = 0
-        if anyQualities:
-            anyQuality = reduce(operator.or_, anyQualities)
-        if bestQualities:
-            bestQuality = reduce(operator.or_, bestQualities)
-        return anyQuality | (bestQuality << 16)
+    def combineQualities(any_qualities, best_qualities):
+        any_quality = 0
+        best_quality = 0
+        if any_qualities:
+            any_quality = reduce(operator.or_, any_qualities)
+        if best_qualities:
+            best_quality = reduce(operator.or_, best_qualities)
+        return any_quality | (best_quality << 16)
 
     @staticmethod
     def splitQuality(quality):
-        anyQualities = []
-        bestQualities = []
-        for curQual in Quality.qualityStrings.keys():
-            if curQual & quality:
-                anyQualities.append(curQual)
-            if curQual << 16 & quality:
-                bestQualities.append(curQual)
+        any_qualities = []
+        best_qualities = []
+        for cur_qual in Quality.qualityStrings:
+            if cur_qual & quality:
+                any_qualities.append(cur_qual)
+            if cur_qual << 16 & quality:
+                best_qualities.append(cur_qual)
 
-        return (sorted(anyQualities), sorted(bestQualities))
+        return sorted(any_qualities), sorted(best_qualities)
 
     @staticmethod
     def nameQuality(name, anime=False):
@@ -215,6 +241,7 @@ class Quality(object):
         Return The quality from an episode File renamed by SickRage
         If no quality is achieved it will try sceneQuality regex
 
+        :param name: to parse
         :param anime: Boolean to indicate if the show we're resolving is Anime
         :return: Quality prefix
         """
@@ -230,9 +257,8 @@ class Quality(object):
 
         return Quality.UNKNOWN
 
-
     @staticmethod
-    def sceneQuality(name, anime=False):
+    def sceneQuality(name, anime=False):  # pylint: disable=too-many-branches
         """
         Return The quality from the scene episode File
 
@@ -241,60 +267,55 @@ class Quality(object):
         :return: Quality prefix
         """
 
-        # pylint: disable=R0912
-
         ret = Quality.UNKNOWN
         if not name:
             return ret
 
-        name = os.path.basename(name)
+        name = ek(path.basename, name)
 
-        checkName = lambda list, func: func([re.search(x, name, re.I) for x in list])
+        check_name = lambda regex_list, func: func([re.search(regex, name, re.I) for regex in regex_list])
 
         if anime:
-            dvdOptions = checkName([r"dvd", r"dvdrip"], any)
-            blueRayOptions = checkName([r"BD", r"blue?-?ray"], any)
-            sdOptions = checkName([r"360p", r"480p", r"848x480", r"XviD"], any)
-            hdOptions = checkName([r"720p", r"1280x720", r"960x720"], any)
-            fullHD = checkName([r"1080p", r"1920x1080"], any)
+            dvd_options = check_name([r"dvd", r"dvdrip"], any)
+            bluray_options = check_name([r"BD", r"blue?-?ray"], any)
+            sd_options = check_name([r"360p", r"480p", r"848x480", r"XviD"], any)
+            hd_options = check_name([r"720p", r"1280x720", r"960x720"], any)
+            full_hd = check_name([r"1080p", r"1920x1080"], any)
 
-            if sdOptions and not blueRayOptions and not dvdOptions:
+            if sd_options and not bluray_options and not dvd_options:
                 ret = Quality.SDTV
-            elif dvdOptions:
+            elif dvd_options:
                 ret = Quality.SDDVD
-            elif hdOptions and not blueRayOptions and not fullHD:
+            elif hd_options and not bluray_options and not full_hd:
                 ret = Quality.HDTV
-            elif fullHD and not blueRayOptions and not hdOptions:
+            elif full_hd and not bluray_options and not hd_options:
                 ret = Quality.FULLHDTV
-            elif hdOptions and not blueRayOptions and not fullHD:
+            elif hd_options and not bluray_options and not full_hd:
                 ret = Quality.HDWEBDL
-            elif blueRayOptions and hdOptions and not fullHD:
+            elif bluray_options and hd_options and not full_hd:
                 ret = Quality.HDBLURAY
-            elif blueRayOptions and fullHD and not hdOptions:
+            elif bluray_options and full_hd and not hd_options:
                 ret = Quality.FULLHDBLURAY
 
             return ret
 
-        if (checkName([r"480p|web.?dl|web(rip|mux|hd)|[sph]d.?tv|dsr|tv(rip|mux)|satrip", r"xvid|divx|[xh].?26[45]"], all)
-                and not checkName([r"(720|1080)[pi]"], all) and not checkName([r"hr.ws.pdtv.[xh].?26[45]"], any)):
+        if check_name([r"480p|web.?dl|web(rip|mux|hd)|[sph]d.?tv|dsr|tv(rip|mux)|satrip", r"xvid|divx|[xh].?26[45]"], all) and not check_name([r"(720|1080)[pi]"], all) and not check_name([r"hr.ws.pdtv.[xh].?26[45]"], any):
             ret = Quality.SDTV
-        elif (checkName([r"dvd(rip|mux)|b[rd](rip|mux)|blue?-?ray", r"xvid|divx|[xh].?26[45]"], all)
-              and not checkName([r"(720|1080)[pi]"], all) and not checkName([r"hr.ws.pdtv.[xh].?26[45]"], any)):
+        elif check_name([r"dvd(rip|mux)|b[rd](rip|mux)|blue?-?ray", r"xvid|divx|[xh].?26[45]"], all) and not check_name([r"(720|1080)[pi]"], all) and not check_name([r"hr.ws.pdtv.[xh].?26[45]"], any):
             ret = Quality.SDDVD
-        elif (checkName([r"720p", r"hd.?tv", r"[xh].?26[45]"], all) or checkName([r"hr.ws.pdtv.[xh].?26[45]"], any)
-              and not checkName([r"1080[pi]"], all)):
+        elif check_name([r"720p", r"hd.?tv", r"[xh].?26[45]"], all) or check_name([r"hr.ws.pdtv.[xh].?26[45]"], any) and not check_name([r"1080[pi]"], all):
             ret = Quality.HDTV
-        elif checkName([r"720p|1080i", r"hd.?tv", r"mpeg-?2"], all) or checkName([r"1080[pi].hdtv", r"h.?26[45]"], all):
+        elif check_name([r"720p|1080i", r"hd.?tv", r"mpeg-?2"], all) or check_name([r"1080[pi].hdtv", r"h.?26[45]"], all):
             ret = Quality.RAWHDTV
-        elif checkName([r"1080p", r"hd.?tv", r"[xh].?26[45]"], all):
+        elif check_name([r"1080p", r"hd.?tv", r"[xh].?26[45]"], all):
             ret = Quality.FULLHDTV
-        elif checkName([r"720p", r"web.?dl|web(rip|mux|hd)"], all) or checkName([r"720p", r"itunes", r"[xh].?26[45]"], all):
+        elif check_name([r"720p", r"web.?dl|web(rip|mux|hd)"], all) or check_name([r"720p", r"itunes", r"[xh].?26[45]"], all):
             ret = Quality.HDWEBDL
-        elif checkName([r"1080p", r"web.?dl|web(rip|mux|hd)"], all) or checkName([r"1080p", r"itunes", r"[xh].?26[45]"], all):
+        elif check_name([r"1080p", r"web.?dl|web(rip|mux|hd)"], all) or check_name([r"1080p", r"itunes", r"[xh].?26[45]"], all):
             ret = Quality.FULLHDWEBDL
-        elif checkName([r"720p", r"blue?-?ray|hddvd|b[rd](rip|mux)", r"[xh].?26[45]"], all):
+        elif check_name([r"720p", r"blue?-?ray|hddvd|b[rd](rip|mux)", r"[xh].?26[45]"], all):
             ret = Quality.HDBLURAY
-        elif checkName([r"1080p", r"blue?-?ray|hddvd|b[rd](rip|mux)", r"[xh].?26[45]"], all):
+        elif check_name([r"1080p", r"blue?-?ray|hddvd|b[rd](rip|mux)", r"[xh].?26[45]"], all):
             ret = Quality.FULLHDBLURAY
 
         return ret
@@ -317,7 +338,7 @@ class Quality(object):
             return Quality.UNKNOWN
 
     @staticmethod
-    def qualityFromFileMeta(filename):
+    def qualityFromFileMeta(filename):  # pylint: disable=too-many-branches
         """
         Get quality file file metadata
 
@@ -325,17 +346,11 @@ class Quality(object):
         :return: Quality prefix
         """
 
-        # pylint: disable=R0912
-
-        from hachoir_parser import createParser
-        from hachoir_metadata import extractMetadata
-        from hachoir_core.log import log
         log.use_print = False
 
         try:
             parser = createParser(filename)
-        # pylint: disable=W0703
-        except Exception:
+        except Exception:  # pylint: disable=broad-except
             parser = None
 
         if not parser:
@@ -343,15 +358,12 @@ class Quality(object):
 
         try:
             metadata = extractMetadata(parser)
-        # pylint: disable=W0703
-        except Exception:
+        except Exception:  # pylint: disable=broad-except
             metadata = None
 
         try:
-            # pylint: disable=W0212
-            parser.stream._input.close()
-        # pylint: disable=W0703
-        except Exception:
+            parser.stream._input.close()  # pylint: disable=protected-access
+        except Exception:  # pylint: disable=broad-except
             pass
 
         if not metadata:
@@ -370,14 +382,14 @@ class Quality(object):
         if not height:
             return Quality.UNKNOWN
 
-        base_filename = os.path.basename(filename)
+        base_filename = ek(path.basename, filename)
         bluray = re.search(r"blue?-?ray|hddvd|b[rd](rip|mux)", base_filename, re.I) is not None
         webdl = re.search(r"web.?dl|web(rip|mux|hd)", base_filename, re.I) is not None
 
         ret = Quality.UNKNOWN
         if height > 1000:
             ret = ((Quality.FULLHDTV, Quality.FULLHDBLURAY)[bluray], Quality.FULLHDWEBDL)[webdl]
-        elif height > 680 and height < 800:
+        elif 680 < height < 800:
             ret = ((Quality.HDTV, Quality.HDBLURAY)[bluray], Quality.HDWEBDL)[webdl]
         elif height < 680:
             ret = (Quality.SDTV, Quality.SDDVD)[re.search(r'dvd|b[rd]rip|blue?-?ray', base_filename, re.I) is not None]
@@ -394,18 +406,23 @@ class Quality(object):
 
     @staticmethod
     def splitCompositeStatus(status):
-        """Returns a tuple containing (status, quality)"""
+        """
+        Split a composite status code into a status and quality.
+
+        :param status: to split
+        :returns: a tuple containing (status, quality)
+        """
         if status == UNKNOWN:
-            return (UNKNOWN, Quality.UNKNOWN)
+            return UNKNOWN, Quality.UNKNOWN
 
         for q in sorted(Quality.qualityStrings.keys(), reverse=True):
             if status > q * 100:
-                return (status - q * 100, q)
+                return status - q * 100, q
 
-        return (status, Quality.NONE)
+        return status, Quality.NONE
 
     @staticmethod
-    def sceneQualityFromName(name, quality): # pylint: disable=R0912
+    def sceneQualityFromName(name, quality):  # pylint: disable=too-many-branches
         """
         Get scene naming parameters from filename and quality
 
@@ -413,17 +430,17 @@ class Quality(object):
         :param quality: int of quality to make sure we get the right rip type
         :return: encoder type for scene quality naming
         """
-        codecList = ['xvid', 'divx']
-        x264List = ['x264', 'x 264', 'x.264']
-        h264List = ['h264', 'h 264', 'h.264', 'avc']
-        x265List = ['x265', 'x 265', 'x.265']
-        h265List = ['h265', 'h 265', 'h.265', 'hevc']
-        codecList.extend(x264List + h264List + x265List + h265List)
+        codec_list = ['xvid', 'divx']
+        x264_list = ['x264', 'x 264', 'x.264']
+        h264_list = ['h264', 'h 264', 'h.264', 'avc']
+        x265_list = ['x265', 'x 265', 'x.265']
+        h265_list = ['h265', 'h 265', 'h.265', 'hevc']
+        codec_list += x264_list + h264_list + x265_list + h265_list
 
         found_codecs = {}
         found_codec = None
 
-        for codec in codecList:
+        for codec in codec_list:
             if codec in name.lower():
                 found_codecs[name.lower().rfind(codec)] = codec
 
@@ -441,18 +458,18 @@ class Quality(object):
                 rip_type = ""
 
         if found_codec:
-            if codecList[0] in found_codec:
+            if codec_list[0] in found_codec:
                 found_codec = 'XviD'
-            elif codecList[1] in found_codec:
+            elif codec_list[1] in found_codec:
                 found_codec = 'DivX'
-            elif found_codec in x264List:
-                found_codec = x264List[0]
-            elif found_codec in h264List:
-                found_codec = h264List[0]
-            elif found_codec in x265List:
-                found_codec = x265List[0]
-            elif found_codec in h265List:
-                found_codec = h265List[0]
+            elif found_codec in x264_list:
+                found_codec = x264_list[0]
+            elif found_codec in h264_list:
+                found_codec = h264_list[0]
+            elif found_codec in x265_list:
+                found_codec = x265_list[0]
+            elif found_codec in h265_list:
+                found_codec = h265_list[0]
 
             if quality == 2:
                 return rip_type + " " + found_codec
@@ -485,12 +502,12 @@ class Quality(object):
     SNATCHED_BEST = None
     ARCHIVED = None
 
-Quality.DOWNLOADED = [Quality.compositeStatus(DOWNLOADED, x) for x in Quality.qualityStrings.keys()]
-Quality.SNATCHED = [Quality.compositeStatus(SNATCHED, x) for x in Quality.qualityStrings.keys()]
-Quality.SNATCHED_PROPER = [Quality.compositeStatus(SNATCHED_PROPER, x) for x in Quality.qualityStrings.keys()]
-Quality.FAILED = [Quality.compositeStatus(FAILED, x) for x in Quality.qualityStrings.keys()]
-Quality.SNATCHED_BEST = [Quality.compositeStatus(SNATCHED_BEST, x) for x in Quality.qualityStrings.keys()]
-Quality.ARCHIVED = [Quality.compositeStatus(ARCHIVED, x) for x in Quality.qualityStrings.keys()]
+Quality.DOWNLOADED = [Quality.compositeStatus(DOWNLOADED, x) for x in Quality.qualityStrings]
+Quality.SNATCHED = [Quality.compositeStatus(SNATCHED, x) for x in Quality.qualityStrings]
+Quality.SNATCHED_PROPER = [Quality.compositeStatus(SNATCHED_PROPER, x) for x in Quality.qualityStrings]
+Quality.FAILED = [Quality.compositeStatus(FAILED, x) for x in Quality.qualityStrings]
+Quality.SNATCHED_BEST = [Quality.compositeStatus(SNATCHED_BEST, x) for x in Quality.qualityStrings]
+Quality.ARCHIVED = [Quality.compositeStatus(ARCHIVED, x) for x in Quality.qualityStrings]
 
 HD720p = Quality.combineQualities([Quality.HDTV, Quality.HDWEBDL, Quality.HDBLURAY], [])
 HD1080p = Quality.combineQualities([Quality.FULLHDTV, Quality.FULLHDWEBDL, Quality.FULLHDBLURAY], [])
@@ -503,28 +520,23 @@ ANY = Quality.combineQualities([SD, HD], [])
 BEST = Quality.combineQualities([Quality.SDTV, Quality.HDTV, Quality.HDWEBDL], [Quality.HDTV])
 
 qualityPresets = (SD, HD, HD720p, HD1080p, ANY)
-qualityPresetStrings = {SD: "SD",
-                        HD: "HD",
-                        HD720p: "HD720p",
-                        HD1080p: "HD1080p",
-                        ANY: "Any"}
+qualityPresetStrings = NumDict({
+    SD: "SD",
+    HD: "HD",
+    HD720p: "HD720p",
+    HD1080p: "HD1080p",
+    ANY: "Any"
+})
 
 
 class StatusStrings(NumDict):
     """
     Dictionary containing strings for status codes
     """
-    # todo: Deprecate StatusStrings().statusStrings and use StatusStrings() directly
-    # todo: Deprecate .has_key and switch to 'x in y'
     # todo: Make views return Qualities too
-    # todo:
 
     qualities = Quality.DOWNLOADED + Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.SNATCHED_BEST + Quality.ARCHIVED + Quality.FAILED
 
-    @property
-    def statusStrings(self):  # for backwards compatibility
-        return self.data
-
     def __missing__(self, key):
         """
         If the key is not found try to determine a status from Quality
@@ -563,30 +575,30 @@ statusStrings = StatusStrings({
 })
 
 
-# pylint: disable=R0903
-class Overview(object):
+class Overview(object):  # pylint: disable=too-few-public-methods
     UNAIRED = UNAIRED  # 1
-    SNATCHED = SNATCHED # 2
+    SNATCHED = SNATCHED  # 2
     WANTED = WANTED  # 3
-    GOOD = DOWNLOADED # 4
+    GOOD = DOWNLOADED  # 4
     SKIPPED = SKIPPED  # 5
     SNATCHED_PROPER = SNATCHED_PROPER  # 9
-    SNATCHED_BEST = SNATCHED_BEST # 12
+    SNATCHED_BEST = SNATCHED_BEST  # 12
 
     # Should suffice!
     QUAL = 50
 
-    overviewStrings = {
+    overviewStrings = NumDict({
         SKIPPED: "skipped",
         WANTED: "wanted",
         QUAL: "qual",
         GOOD: "good",
         UNAIRED: "unaired",
         SNATCHED: "snatched",
-        # we can give these a different class later, otherwise breaks checkboxes in displayshow for showing different statuses
+        # we can give these a different class later, otherwise
+        # breaks checkboxes in displayShow for showing different statuses
         SNATCHED_BEST: "snatched",
         SNATCHED_PROPER: "snatched"
-    }
+    })
 
 countryList = {
     'Australia': 'AU',
diff --git a/sickbeard/config.py b/sickbeard/config.py
index 670835e10682f47eb762dc66d286d3941e2769b6..920bf2dceba6e1c38b1e82573f8a6cba21b96136 100644
--- a/sickbeard/config.py
+++ b/sickbeard/config.py
@@ -28,6 +28,9 @@ from sickbeard import logger
 from sickbeard import naming
 from sickbeard import db
 
+from sickrage.helper.common import try_int
+from sickrage.helper.encoding import ek
+
 # Address poor support for scgi over unix domain sockets
 # this is not nicely handled by python currently
 # http://bugs.python.org/issue23636
@@ -65,9 +68,9 @@ def change_HTTPS_CERT(https_cert):
         sickbeard.HTTPS_CERT = ''
         return True
 
-    if os.path.normpath(sickbeard.HTTPS_CERT) != os.path.normpath(https_cert):
-        if helpers.makeDir(os.path.dirname(os.path.abspath(https_cert))):
-            sickbeard.HTTPS_CERT = os.path.normpath(https_cert)
+    if ek(os.path.normpath, sickbeard.HTTPS_CERT) != ek(os.path.normpath, https_cert):
+        if helpers.makeDir(ek(os.path.dirname, ek(os.path.abspath, https_cert))):
+            sickbeard.HTTPS_CERT = ek(os.path.normpath, https_cert)
             logger.log(u"Changed https cert path to " + https_cert)
         else:
             return False
@@ -86,9 +89,9 @@ def change_HTTPS_KEY(https_key):
         sickbeard.HTTPS_KEY = ''
         return True
 
-    if os.path.normpath(sickbeard.HTTPS_KEY) != os.path.normpath(https_key):
-        if helpers.makeDir(os.path.dirname(os.path.abspath(https_key))):
-            sickbeard.HTTPS_KEY = os.path.normpath(https_key)
+    if ek(os.path.normpath, sickbeard.HTTPS_KEY) != ek(os.path.normpath, https_key):
+        if helpers.makeDir(ek(os.path.dirname, ek(os.path.abspath, https_key))):
+            sickbeard.HTTPS_KEY = ek(os.path.normpath, https_key)
             logger.log(u"Changed https key path to " + https_key)
         else:
             return False
@@ -105,12 +108,12 @@ def change_LOG_DIR(log_dir, web_log):
     :return: True on success, False on failure
     """
     log_dir_changed = False
-    abs_log_dir = os.path.normpath(os.path.join(sickbeard.DATA_DIR, log_dir))
+    abs_log_dir = ek(os.path.normpath, ek(os.path.join, sickbeard.DATA_DIR, log_dir))
     web_log_value = checkbox_to_value(web_log)
 
-    if os.path.normpath(sickbeard.LOG_DIR) != abs_log_dir:
+    if ek(os.path.normpath, sickbeard.LOG_DIR) != abs_log_dir:
         if helpers.makeDir(abs_log_dir):
-            sickbeard.ACTUAL_LOG_DIR = os.path.normpath(log_dir)
+            sickbeard.ACTUAL_LOG_DIR = ek(os.path.normpath, log_dir)
             sickbeard.LOG_DIR = abs_log_dir
 
             logger.initLogging()
@@ -137,9 +140,9 @@ def change_NZB_DIR(nzb_dir):
         sickbeard.NZB_DIR = ''
         return True
 
-    if os.path.normpath(sickbeard.NZB_DIR) != os.path.normpath(nzb_dir):
+    if ek(os.path.normpath, sickbeard.NZB_DIR) != ek(os.path.normpath, nzb_dir):
         if helpers.makeDir(nzb_dir):
-            sickbeard.NZB_DIR = os.path.normpath(nzb_dir)
+            sickbeard.NZB_DIR = ek(os.path.normpath, nzb_dir)
             logger.log(u"Changed NZB folder to " + nzb_dir)
         else:
             return False
@@ -158,9 +161,9 @@ def change_TORRENT_DIR(torrent_dir):
         sickbeard.TORRENT_DIR = ''
         return True
 
-    if os.path.normpath(sickbeard.TORRENT_DIR) != os.path.normpath(torrent_dir):
+    if ek(os.path.normpath, sickbeard.TORRENT_DIR) != ek(os.path.normpath, torrent_dir):
         if helpers.makeDir(torrent_dir):
-            sickbeard.TORRENT_DIR = os.path.normpath(torrent_dir)
+            sickbeard.TORRENT_DIR = ek(os.path.normpath, torrent_dir)
             logger.log(u"Changed torrent folder to " + torrent_dir)
         else:
             return False
@@ -179,9 +182,9 @@ def change_TV_DOWNLOAD_DIR(tv_download_dir):
         sickbeard.TV_DOWNLOAD_DIR = ''
         return True
 
-    if os.path.normpath(sickbeard.TV_DOWNLOAD_DIR) != os.path.normpath(tv_download_dir):
+    if ek(os.path.normpath, sickbeard.TV_DOWNLOAD_DIR) != ek(os.path.normpath, tv_download_dir):
         if helpers.makeDir(tv_download_dir):
-            sickbeard.TV_DOWNLOAD_DIR = os.path.normpath(tv_download_dir)
+            sickbeard.TV_DOWNLOAD_DIR = ek(os.path.normpath, tv_download_dir)
             logger.log(u"Changed TV download folder to " + tv_download_dir)
         else:
             return False
@@ -196,7 +199,7 @@ def change_AUTOPOSTPROCESSER_FREQUENCY(freq):
 
     :param freq: New frequency
     """
-    sickbeard.AUTOPOSTPROCESSER_FREQUENCY = to_int(freq, default=sickbeard.DEFAULT_AUTOPOSTPROCESSER_FREQUENCY)
+    sickbeard.AUTOPOSTPROCESSER_FREQUENCY = try_int(freq, sickbeard.DEFAULT_AUTOPOSTPROCESSER_FREQUENCY)
 
     if sickbeard.AUTOPOSTPROCESSER_FREQUENCY < sickbeard.MIN_AUTOPOSTPROCESSER_FREQUENCY:
         sickbeard.AUTOPOSTPROCESSER_FREQUENCY = sickbeard.MIN_AUTOPOSTPROCESSER_FREQUENCY
@@ -210,7 +213,7 @@ def change_DAILYSEARCH_FREQUENCY(freq):
 
     :param freq: New frequency
     """
-    sickbeard.DAILYSEARCH_FREQUENCY = to_int(freq, default=sickbeard.DEFAULT_DAILYSEARCH_FREQUENCY)
+    sickbeard.DAILYSEARCH_FREQUENCY = try_int(freq, sickbeard.DEFAULT_DAILYSEARCH_FREQUENCY)
 
     if sickbeard.DAILYSEARCH_FREQUENCY < sickbeard.MIN_DAILYSEARCH_FREQUENCY:
         sickbeard.DAILYSEARCH_FREQUENCY = sickbeard.MIN_DAILYSEARCH_FREQUENCY
@@ -224,7 +227,7 @@ def change_BACKLOG_FREQUENCY(freq):
 
     :param freq: New frequency
     """
-    sickbeard.BACKLOG_FREQUENCY = to_int(freq, default=sickbeard.DEFAULT_BACKLOG_FREQUENCY)
+    sickbeard.BACKLOG_FREQUENCY = try_int(freq, sickbeard.DEFAULT_BACKLOG_FREQUENCY)
 
     sickbeard.MIN_BACKLOG_FREQUENCY = sickbeard.get_backlog_cycle_time()
     if sickbeard.BACKLOG_FREQUENCY < sickbeard.MIN_BACKLOG_FREQUENCY:
@@ -239,7 +242,7 @@ def change_UPDATE_FREQUENCY(freq):
 
     :param freq: New frequency
     """
-    sickbeard.UPDATE_FREQUENCY = to_int(freq, default=sickbeard.DEFAULT_UPDATE_FREQUENCY)
+    sickbeard.UPDATE_FREQUENCY = try_int(freq, sickbeard.DEFAULT_UPDATE_FREQUENCY)
 
     if sickbeard.UPDATE_FREQUENCY < sickbeard.MIN_UPDATE_FREQUENCY:
         sickbeard.UPDATE_FREQUENCY = sickbeard.MIN_UPDATE_FREQUENCY
@@ -253,7 +256,7 @@ def change_SHOWUPDATE_HOUR(freq):
 
     :param freq: New frequency
     """
-    sickbeard.SHOWUPDATE_HOUR = to_int(freq, default=sickbeard.DEFAULT_SHOWUPDATE_HOUR)
+    sickbeard.SHOWUPDATE_HOUR = try_int(freq, sickbeard.DEFAULT_SHOWUPDATE_HOUR)
 
     if sickbeard.SHOWUPDATE_HOUR > 23:
         sickbeard.SHOWUPDATE_HOUR = 0
@@ -272,7 +275,7 @@ def change_SUBTITLES_FINDER_FREQUENCY(subtitles_finder_frequency):
     if subtitles_finder_frequency == '' or subtitles_finder_frequency is None:
         subtitles_finder_frequency = 1
 
-    sickbeard.SUBTITLES_FINDER_FREQUENCY = to_int(subtitles_finder_frequency, 1)
+    sickbeard.SUBTITLES_FINDER_FREQUENCY = try_int(subtitles_finder_frequency, 1)
 
 
 def change_VERSION_NOTIFY(version_notify):
@@ -504,24 +507,13 @@ def clean_url(url):
     return cleaned_url
 
 
-def to_int(val, default=0):
-    """ Return int value of val or default on error """
-
-    try:
-        val = int(val)
-    except Exception:
-        val = default
-
-    return val
-
-
 ################################################################################
 # Check_setting_int                                                            #
 ################################################################################
 def minimax(val, default, low, high):
     """ Return value forced within range """
 
-    val = to_int(val, default=default)
+    val = try_int(val, default)
 
     if val < low:
         return low
diff --git a/sickbeard/dailysearcher.py b/sickbeard/dailysearcher.py
index 89abc9ca1695d15409959450414420f082026496..fd6f8443b22b50fe3663b4aff0d2fbcb89e8d5b8 100644
--- a/sickbeard/dailysearcher.py
+++ b/sickbeard/dailysearcher.py
@@ -17,7 +17,6 @@
 # You should have received a copy of the GNU General Public License
 # along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
 
-
 import datetime
 import threading
 
@@ -25,8 +24,8 @@ import sickbeard
 from sickbeard import logger
 from sickbeard import db
 from sickbeard import common
-from sickbeard import helpers
 from sickbeard import network_timezones
+from sickrage.show.Show import Show
 from sickrage.helper.exceptions import MultipleShowObjectsException
 
 
@@ -68,7 +67,7 @@ class DailySearcher():
         for sqlEp in sqlResults:
             try:
                 if not show or int(sqlEp["showid"]) != show.indexerid:
-                    show = helpers.findCertainShow(sickbeard.showList, int(sqlEp["showid"]))
+                    show = Show.find(sickbeard.showList, int(sqlEp["showid"]))
 
                 # for when there is orphaned series in the database but not loaded into our showlist
                 if not show or show.paused:
diff --git a/sickbeard/databases/mainDB.py b/sickbeard/databases/mainDB.py
index 131406cef8fbef13dc99d894d90ac7eb5e77797f..4707b685a4758bee94a1b2ab9d5ff0e968606a52 100644
--- a/sickbeard/databases/mainDB.py
+++ b/sickbeard/databases/mainDB.py
@@ -29,7 +29,7 @@ from sickbeard.name_parser.parser import NameParser, InvalidNameException, Inval
 from sickrage.helper.common import dateTimeFormat
 from sickrage.helper.encoding import ek
 
-from babelfish import language_converters, Language
+from sickbeard import subtitles
 
 MIN_DB_VERSION = 9  # oldest db version we support migrating from
 MAX_DB_VERSION = 42
@@ -268,8 +268,6 @@ class MainSanityCheck(db.DBSanityCheck):
             [datetime.datetime(2015, 7, 15, 17, 20, 44, 326380).strftime(dateTimeFormat)]
         )
 
-        validLanguages = [Language.fromopensubtitles(language).opensubtitles for language in language_converters['opensubtitles'].codes if len(language) == 3]
-
         if not sqlResults:
             return
 
@@ -280,7 +278,7 @@ class MainSanityCheck(db.DBSanityCheck):
                        (sqlResult['episode_id'], sqlResult['subtitles']), logger.DEBUG)
 
             for subcode in sqlResult['subtitles'].split(','):
-                if not len(subcode) is 3 or subcode not in validLanguages:
+                if not len(subcode) is 3 or subcode not in subtitles.subtitle_code_filter():
                     logger.log(u"Fixing subtitle codes for episode_id: %s, invalid code: %s" %
                                (sqlResult['episode_id'], subcode), logger.DEBUG)
                     continue
@@ -442,7 +440,7 @@ class AddSizeAndSceneNameFields(InitialSchema):
         for cur_result in empty_results:
 
             ep_file_name = ek(os.path.basename, cur_result["location"])
-            ep_file_name = os.path.splitext(ep_file_name)[0]
+            ep_file_name = ek(os.path.splitext, ep_file_name)[0]
 
             # only want to find real scene names here so anything with a space in it is out
             if ' ' in ep_file_name:
diff --git a/sickbeard/failed_history.py b/sickbeard/failed_history.py
index 177fed1c3e69d4d248920b95f1cc5428319ce908..a3608b153ecb39c6c33ffea8285b7e2873792d07 100644
--- a/sickbeard/failed_history.py
+++ b/sickbeard/failed_history.py
@@ -132,8 +132,8 @@ def revertEpisode(epObj):
                 logger.log(u"Found in history")
                 epObj.status = history_eps[epObj.episode]['old_status']
             else:
-                logger.log(u"WARNING: Episode not found in history. Setting it back to WANTED",
-                           logger.WARNING)
+                logger.log(u"Episode don't have a previous snatched status to revert. Setting it back to WANTED",
+                           logger.DEBUG)
                 epObj.status = WANTED
                 epObj.saveToDB()
 
@@ -246,4 +246,4 @@ def findRelease(epObj):
 
     # Release was not found
     logger.log(u"No releases found for season (%s) of (%s)" % (epObj.season, epObj.show.indexerid), logger.DEBUG)
-    return (release, provider)
\ No newline at end of file
+    return (release, provider)
diff --git a/sickbeard/helpers.py b/sickbeard/helpers.py
index 9e78f997a4578437779ac68dd97fede5d962ecfc..4c21d00bd49bf44ebc20b523e2a3ba66a242669f 100644
--- a/sickbeard/helpers.py
+++ b/sickbeard/helpers.py
@@ -52,17 +52,19 @@ from sickbeard import logger, classes
 from sickbeard.common import USER_AGENT
 from sickbeard import db
 from sickbeard.notifiers.synoindex import notifier as synoindex_notifier
-from sickbeard import clients
-from sickrage.helper.common import media_extensions, pretty_file_size, subtitle_extensions
+from sickrage.helper.common import http_code_description, media_extensions, pretty_file_size, subtitle_extensions
 from sickrage.helper.encoding import ek
-from sickrage.helper.exceptions import ex, MultipleShowObjectsException
+from sickrage.helper.exceptions import ex
+from sickrage.show.Show import Show
 from cachecontrol import CacheControl, caches
-from babelfish import Language
 from itertools import izip, cycle
 
 import shutil
 import shutil_custom
 
+import xml.etree.ElementTree as ET
+import json
+
 shutil.copyfile = shutil_custom.copyfile_custom
 
 # pylint: disable=W0212
@@ -70,14 +72,6 @@ shutil.copyfile = shutil_custom.copyfile_custom
 urllib._urlopener = classes.SickBeardURLopener()
 
 
-def isValidLanguage(language):
-    try:
-        Language.fromopensubtitles(language)
-    except Exception:
-        return False
-    return True
-
-
 def fixGlob(path):
     path = re.sub(r'\[', '[[]', path)
     return re.sub(r'(?<!\[)\]', '[]]', path)
@@ -101,6 +95,7 @@ def indentXML(elem, level=0):
         if level and (not elem.tail or not elem.tail.strip()):
             elem.tail = i
 
+
 def remove_non_release_groups(name):
     """
     Remove non release groups from name
@@ -237,32 +232,6 @@ def remove_file_failed(failed_file):
         pass
 
 
-def findCertainShow(showList, indexerid):
-    """
-    Find a show by indexer ID in the show list
-
-    :param showList: List of shows to search in (needle)
-    :param indexerid: Show to look for
-    :return: result list
-    """
-
-    results = []
-
-    if not isinstance(indexerid, list):
-        indexerid = [indexerid]
-
-    if showList and indexerid:
-        results = [show for show in showList if show.indexerid in indexerid]
-
-    if not results:
-        return None
-
-    if len(results) == 1:
-        return results[0]
-    elif len(results) > 1:
-        raise MultipleShowObjectsException()
-
-
 def makeDir(path):
     """
     Make a directory on the filesystem
@@ -367,7 +336,7 @@ def searchIndexerForShowID(regShowName, indexer=None, indexer_id=None, ui=None):
 
             if not (seriesname and series_id):
                 continue
-            ShowObj = findCertainShow(sickbeard.showList, int(series_id))
+            ShowObj = Show.find(sickbeard.showList, int(series_id))
             # Check if we can find the show in our list (if not, it's not the right show)
             if (indexer_id is None) and (ShowObj is not None) and (ShowObj.indexerid == int(series_id)):
                 return seriesname, i, int(series_id)
@@ -449,7 +418,7 @@ def link(src, dst):
         if ctypes.windll.kernel32.CreateHardLinkW(unicode(dst), unicode(src), 0) == 0:
             raise ctypes.WinError()
     else:
-        os.link(src, dst)
+        ek(os.link, src, dst)
 
 
 def hardlinkFile(srcFile, destFile):
@@ -478,10 +447,10 @@ def symlink(src, dst):
     """
 
     if os.name == 'nt':
-        if ctypes.windll.kernel32.CreateSymbolicLinkW(unicode(dst), unicode(src), 1 if os.path.isdir(src) else 0) in [0, 1280]:
+        if ctypes.windll.kernel32.CreateSymbolicLinkW(unicode(dst), unicode(src), 1 if ek(os.path.isdir, src) else 0) in [0, 1280]:
             raise ctypes.WinError()
     else:
-        os.symlink(src, dst)
+        ek(os.symlink, src, dst)
 
 
 def moveAndSymlinkFile(srcFile, destFile):
@@ -558,11 +527,11 @@ def rename_ep_file(cur_path, new_path, old_path_length=0):
     :param old_path_length: The length of media file path (old name) WITHOUT THE EXTENSION
     """
 
-    # new_dest_dir, new_dest_name = os.path.split(new_path)  # @UnusedVariable
+    # new_dest_dir, new_dest_name = ek(os.path.split, new_path)  # @UnusedVariable
 
     if old_path_length == 0 or old_path_length > len(cur_path):
         # approach from the right
-        cur_file_name, cur_file_ext = os.path.splitext(cur_path)  # @UnusedVariable
+        cur_file_name, cur_file_ext = ek(os.path.splitext, cur_path)  # @UnusedVariable
     else:
         # approach from the left
         cur_file_ext = cur_path[old_path_length:]
@@ -570,16 +539,16 @@ def rename_ep_file(cur_path, new_path, old_path_length=0):
 
     if cur_file_ext[1:] in subtitle_extensions:
         # Extract subtitle language from filename
-        sublang = os.path.splitext(cur_file_name)[1][1:]
+        sublang = ek(os.path.splitext, cur_file_name)[1][1:]
 
         # Check if the language extracted from filename is a valid language
-        if isValidLanguage(sublang):
+        if sublang in sickbeard.subtitles.subtitle_code_filter():
             cur_file_ext = '.' + sublang + cur_file_ext
 
     # put the extension on the incoming file
     new_path += cur_file_ext
 
-    make_dirs(os.path.dirname(new_path))
+    make_dirs(ek(os.path.dirname, new_path))
 
     # move the file
     try:
@@ -661,12 +630,12 @@ def chmodAsParent(childPath):
         logger.log(u"No parent path provided in " + childPath + ", unable to get permissions from it", logger.DEBUG)
         return
 
-    childPath = os.path.join(parentPath, ek(os.path.basename, childPath))
+    childPath = ek(os.path.join, parentPath, ek(os.path.basename, childPath))
 
-    parentPathStat = os.stat(parentPath)
+    parentPathStat = ek(os.stat, parentPath)
     parentMode = stat.S_IMODE(parentPathStat[stat.ST_MODE])
 
-    childPathStat = os.stat(childPath.encode(sickbeard.SYS_ENCODING))
+    childPathStat = ek(os.stat, childPath.encode(sickbeard.SYS_ENCODING))
     childPath_mode = stat.S_IMODE(childPathStat[stat.ST_MODE])
 
     if ek(os.path.isfile, childPath):
@@ -685,7 +654,7 @@ def chmodAsParent(childPath):
         return
 
     try:
-        os.chmod(childPath, childMode)
+        ek(os.chmod, childPath, childMode)
         logger.log(u"Setting permissions for %s to %o as parent directory has %o" % (childPath, childMode, parentMode),
                    logger.DEBUG)
     except OSError:
@@ -704,14 +673,14 @@ def fixSetGroupID(childPath):
         return
 
     parentPath = ek(os.path.dirname, childPath)
-    parentStat = os.stat(parentPath)
+    parentStat = ek(os.stat, parentPath)
     parentMode = stat.S_IMODE(parentStat[stat.ST_MODE])
 
-    childPath = os.path.join(parentPath, ek(os.path.basename, childPath))
+    childPath = ek(os.path.join, parentPath, ek(os.path.basename, childPath))
 
     if parentMode & stat.S_ISGID:
         parentGID = parentStat[stat.ST_GID]
-        childStat = os.stat(childPath.encode(sickbeard.SYS_ENCODING))
+        childStat = ek(os.stat, childPath.encode(sickbeard.SYS_ENCODING))
         childGID = childStat[stat.ST_GID]
 
         if childGID == parentGID:
@@ -785,7 +754,7 @@ def get_all_episodes_from_absolute_number(show, absolute_numbers, indexer_id=Non
 
     if len(absolute_numbers):
         if not show and indexer_id:
-            show = findCertainShow(sickbeard.showList, indexer_id)
+            show = Show.find(sickbeard.showList, indexer_id)
 
         for absolute_number in absolute_numbers if show else []:
             ep = show.getEpisode(None, None, absolute_number=absolute_number)
@@ -948,7 +917,7 @@ def restoreVersionedFile(backup_file, version):
 
     numTries = 0
 
-    new_file, _ = os.path.splitext(backup_file)
+    new_file, _ = ek(os.path.splitext, backup_file)
     restore_file = new_file + '.' + 'v' + str(version)
 
     if not ek(os.path.isfile, new_file):
@@ -988,22 +957,6 @@ def restoreVersionedFile(backup_file, version):
     return True
 
 
-# try to convert to int, if it fails the default will be returned
-def tryInt(s, s_default=0):
-    """
-    Try to convert to int, if it fails, the default will be returned
-
-    :param s: Value to attempt to convert to int
-    :param s_default: Default value to return on failure (defaults to 0)
-    :return: integer, or default value on failure
-    """
-
-    try:
-        return int(s)
-    except Exception:
-        return s_default
-
-
 # generates a md5 hash of a file
 def md5_for_file(filename, block_size=2 ** 16):
     """
@@ -1140,18 +1093,18 @@ def get_show(name, tryIndexers=False):
         cache = sickbeard.name_cache.retrieveNameFromCache(name)
         if cache:
             fromCache = True
-            showObj = findCertainShow(sickbeard.showList, int(cache))
+            showObj = Show.find(sickbeard.showList, int(cache))
 
         # try indexers
         if not showObj and tryIndexers:
-            showObj = findCertainShow(sickbeard.showList,
+            showObj = Show.find(sickbeard.showList,
                                       searchIndexerForShowID(full_sanitizeSceneName(name), ui=classes.ShowListUI)[2])
 
         # try scene exceptions
         if not showObj:
             ShowID = sickbeard.scene_exceptions.get_scene_exception_by_name(name)[0]
             if ShowID:
-                showObj = findCertainShow(sickbeard.showList, int(ShowID))
+                showObj = Show.find(sickbeard.showList, int(ShowID))
 
         # add show to cache
         if showObj and not fromCache:
@@ -1169,7 +1122,7 @@ def is_hidden_folder(folder):
     :param folder: Full path of folder to check
     """
     def is_hidden(filepath):
-        name = os.path.basename(os.path.abspath(filepath))
+        name = ek(os.path.basename, ek(os.path.abspath, filepath))
         return name.startswith('.') or has_hidden_attribute(filepath)
 
     def has_hidden_attribute(filepath):
@@ -1277,19 +1230,19 @@ def extractZip(archive, targetDir):
     """
 
     try:
-        if not os.path.exists(targetDir):
-            os.mkdir(targetDir)
+        if not ek(os.path.exists, targetDir):
+            ek(os.mkdir, targetDir)
 
         zip_file = zipfile.ZipFile(archive, 'r', allowZip64=True)
         for member in zip_file.namelist():
-            filename = os.path.basename(member)
+            filename = ek(os.path.basename, member)
             # skip directories
             if not filename:
                 continue
 
             # copy file (taken from zipfile's extract)
             source = zip_file.open(member)
-            target = file(os.path.join(targetDir, filename), "wb")
+            target = file(ek(os.path.join, targetDir, filename), "wb")
             shutil.copyfileobj(source, target)
             source.close()
             target.close()
@@ -1313,7 +1266,7 @@ def backupConfigZip(fileList, archive, arcname=None):
     try:
         a = zipfile.ZipFile(archive, 'w', zipfile.ZIP_DEFLATED, allowZip64=True)
         for f in fileList:
-            a.write(f, os.path.relpath(f, arcname))
+            a.write(f, ek(os.path.relpath, f, arcname))
         a.close()
         return True
     except Exception as e:
@@ -1331,14 +1284,14 @@ def restoreConfigZip(archive, targetDir):
     """
 
     try:
-        if not os.path.exists(targetDir):
-            os.mkdir(targetDir)
+        if not ek(os.path.exists, targetDir):
+            ek(os.mkdir, targetDir)
         else:
             def path_leaf(path):
-                head, tail = os.path.split(path)
-                return tail or os.path.basename(head)
+                head, tail = ek(os.path.split, path)
+                return tail or ek(os.path.basename, head)
             bakFilename = '{0}-{1}'.format(path_leaf(targetDir), datetime.datetime.now().strftime('%Y%m%d_%H%M%S'))
-            shutil.move(targetDir, os.path.join(os.path.dirname(targetDir), bakFilename))
+            shutil.move(targetDir, ek(os.path.join, ek(os.path.dirname, targetDir), bakFilename))
 
         zip_file = zipfile.ZipFile(archive, 'r', allowZip64=True)
         for member in zip_file.namelist():
@@ -1448,20 +1401,9 @@ def _getTempDir():
         try:
             uid = getpass.getuser()
         except ImportError:
-            return os.path.join(tempfile.gettempdir(), "sickrage")
+            return ek(os.path.join, tempfile.gettempdir(), "sickrage")
 
-    return os.path.join(tempfile.gettempdir(), "sickrage-%s" % uid)
-
-
-def codeDescription(status_code):
-    """
-    Returns the description of the URL error code
-    """
-    if status_code in clients.http_error_code:
-        return clients.http_error_code[status_code]
-    else:
-        logger.log(u"Unknown error code: %s. Please submit an issue" % status_code, logger.ERROR)
-        return 'unknown'
+    return ek(os.path.join, tempfile.gettempdir(), "sickrage-%s" % uid)
 
 
 def _setUpSession(session, headers):
@@ -1475,7 +1417,7 @@ def _setUpSession(session, headers):
 
     # request session
     cache_dir = sickbeard.CACHE_DIR or _getTempDir()
-    session = CacheControl(sess=session, cache=caches.FileCache(os.path.join(cache_dir, 'sessions'), use_dir_lock=True), cache_etags=False)
+    session = CacheControl(sess=session, cache=caches.FileCache(ek(os.path.join, cache_dir, 'sessions'), use_dir_lock=True), cache_etags=False)
 
     # request session clear residual referer
     # pylint: disable=C0325
@@ -1537,7 +1479,7 @@ def getURL(url, post_data=None, params=None, headers=None, timeout=30, session=N
 
         if not resp.ok:
             logger.log(u"Requested getURL %s returned status code is %s: %s"
-                       % (url, resp.status_code, codeDescription(resp.status_code)), logger.DEBUG)
+                       % (url, resp.status_code, http_code_description(resp.status_code)), logger.DEBUG)
             return None
 
     except (SocketTimeout, TypeError) as e:
@@ -1582,7 +1524,7 @@ def download_file(url, filename, session=None, headers=None):
         with closing(session.get(url, allow_redirects=True, verify=session.verify)) as resp:
             if not resp.ok:
                 logger.log(u"Requested download url %s returned status code is %s: %s"
-                           % (url, resp.status_code, codeDescription(resp.status_code)), logger.DEBUG)
+                           % (url, resp.status_code, http_code_description(resp.status_code)), logger.DEBUG)
                 return False
 
             try:
@@ -1699,7 +1641,7 @@ def verify_freespace(src, dest, oldfile=None):
 
     if hasattr(os, 'statvfs'):  # POSIX
         def disk_usage(path):
-            st = os.statvfs(path)
+            st = ek(os.statvfs, path)
             free = st.f_bavail * st.f_frsize
             return free
 
@@ -1792,12 +1734,12 @@ def isFileLocked(checkfile, writeLockCheck=False):
 
     if writeLockCheck:
         lockFile = checkfile + ".lckchk"
-        if os.path.exists(lockFile):
-            os.remove(lockFile)
+        if ek(os.path.exists, lockFile):
+            ek(os.remove, lockFile)
         try:
-            os.rename(checkfile, lockFile)
+            ek(os.rename, checkfile, lockFile)
             time.sleep(1)
-            os.rename(lockFile, checkfile)
+            ek(os.rename, lockFile, checkfile)
         except (OSError, IOError):
             return True
 
@@ -1809,13 +1751,49 @@ def getDiskSpaceUsage(diskPath=None):
     returns the free space in human readable bytes for a given path or False if no path given
     :param diskPath: the filesystem path being checked
     """
-    if diskPath and os.path.exists(diskPath):
+    if diskPath and ek(os.path.exists, diskPath):
         if platform.system() == 'Windows':
             free_bytes = ctypes.c_ulonglong(0)
             ctypes.windll.kernel32.GetDiskFreeSpaceExW(ctypes.c_wchar_p(diskPath), None, None, ctypes.pointer(free_bytes))
             return pretty_file_size(free_bytes.value)
         else:
-            st = os.statvfs(diskPath)
+            st = ek(os.statvfs, diskPath)
             return pretty_file_size(st.f_bavail * st.f_frsize)
     else:
         return False
+
+
+def getTVDBFromID(indexer_id, indexer):
+    tvdb_id = ''
+    if indexer == 'IMDB':
+        url = "http://www.thetvdb.com/api/GetSeriesByRemoteID.php?imdbid=%s" % (indexer_id)
+        data = urllib.urlopen(url)
+        try:
+            tree = ET.parse(data)
+            for show in tree.getiterator("Series"):
+                tvdb_id = show.findtext("seriesid")
+
+        except SyntaxError:
+            pass
+
+        return tvdb_id
+    elif indexer == 'ZAP2IT':
+        url = "http://www.thetvdb.com/api/GetSeriesByRemoteID.php?zap2it=%s" % (indexer_id)
+        data = urllib.urlopen(url)
+        try:
+            tree = ET.parse(data)
+            for show in tree.getiterator("Series"):
+                tvdb_id = show.findtext("seriesid")
+
+        except SyntaxError:
+            pass
+
+        return tvdb_id
+    elif indexer == 'TVMAZE':
+        url = "http://api.tvmaze.com/shows/%s" % (indexer_id)
+        response = urllib2.urlopen(url)
+        data = json.load(response)
+        tvdb_id = data['externals']['thetvdb']
+        return tvdb_id
+    else:
+        return tvdb_id
diff --git a/sickbeard/image_cache.py b/sickbeard/image_cache.py
index 7ab282fee4c344aba20106db3e39662aab38a6fd..6569c6e81ac0c72864b8ce63fb41800f2591268e 100644
--- a/sickbeard/image_cache.py
+++ b/sickbeard/image_cache.py
@@ -286,7 +286,7 @@ class ImageCache:
                     logger.log(u"Checking if we can use the show image from the " + cur_provider.name + " metadata",
                                logger.DEBUG)
                     if ek(os.path.isfile, cur_provider.get_poster_path(show_obj)):
-                        cur_file_name = os.path.abspath(cur_provider.get_poster_path(show_obj))
+                        cur_file_name = ek(os.path.abspath, cur_provider.get_poster_path(show_obj))
                         cur_file_type = self.which_type(cur_file_name)
 
                         if cur_file_type is None:
diff --git a/sickbeard/imdbPopular.py b/sickbeard/imdbPopular.py
index 26af7f89548299e1b901d1f413e28885da5c44e0..540e3eee57e226513ae258c0d60323f4e850d8a0 100644
--- a/sickbeard/imdbPopular.py
+++ b/sickbeard/imdbPopular.py
@@ -45,7 +45,7 @@ class imdbPopular(object):
             if image_td:
                 image = image_td.find("img")
                 show['image_url_large'] = self.change_size(image['src'], 3)
-                show['image_path'] = os.path.join('images', 'imdb_popular', os.path.basename(show['image_url_large']))
+                show['image_path'] = ek(os.path.join, 'images', 'imdb_popular', ek(os.path.basename, show['image_url_large']))
 
                 self.cache_image(show['image_url_large'])
 
@@ -91,7 +91,7 @@ class imdbPopular(object):
 
         if match:
             matches = match.groups()
-            os.path.basename(image_url)
+            ek(os.path.basename, image_url)
             matches = list(matches)
             matches[2] = int(matches[2]) * factor
             matches[4] = int(matches[4]) * factor
@@ -111,12 +111,12 @@ class imdbPopular(object):
         """
         path = ek(os.path.abspath, ek(os.path.join, sickbeard.CACHE_DIR, 'images', 'imdb_popular'))
 
-        if not os.path.exists(path):
-            os.makedirs(path)
+        if not ek(os.path.exists, path):
+            ek(os.makedirs, path)
 
-        full_path = os.path.join(path, os.path.basename(image_url))
+        full_path = ek(os.path.join, path, ek(os.path.basename, image_url))
 
-        if not os.path.isfile(full_path):
+        if not ek(os.path.isfile, full_path):
             helpers.download_file(image_url, full_path, session=self.session)
 
 imdb_popular = imdbPopular()
diff --git a/sickbeard/indexers/__init__.py b/sickbeard/indexers/__init__.py
index bdbdcca31206955d112052f32f86dbab0cec2093..eb63e387f91550866b4e3a5959a0022b65c85bac 100644
--- a/sickbeard/indexers/__init__.py
+++ b/sickbeard/indexers/__init__.py
@@ -18,4 +18,5 @@
 # You should have received a copy of the GNU General Public License
 # along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
 
-from . import indexer_api, indexer_exceptions
+from . import indexer_api
+from . import indexer_exceptions
diff --git a/sickbeard/indexers/indexer_api.py b/sickbeard/indexers/indexer_api.py
index bf9f09ab6bb74a0081e42869c38edbdee630c934..a8c5e92e86a755b4bcc21a5a2125a4e175511b46 100644
--- a/sickbeard/indexers/indexer_api.py
+++ b/sickbeard/indexers/indexer_api.py
@@ -17,15 +17,19 @@
 #
 # 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 sickbeard
+from sickrage.helper.common import try_int
+from sickrage.helper.encoding import ek
 
-from indexer_config import initConfig, indexerConfig
+from indexer_config import initConfig
+from indexer_config import indexerConfig
 
 
 class indexerApi(object):
     def __init__(self, indexerID=None):
-        self.indexerID = int(indexerID) if indexerID else None
+        self.indexerID = try_int(indexerID, None)
 
     def __del__(self):
         pass
@@ -54,7 +58,7 @@ class indexerApi(object):
     def api_params(self):
         if self.indexerID:
             if sickbeard.CACHE_DIR:
-                indexerConfig[self.indexerID]['api_params']['cache'] = os.path.join(sickbeard.CACHE_DIR, 'indexers', self.name)
+                indexerConfig[self.indexerID]['api_params']['cache'] = ek(os.path.join, sickbeard.CACHE_DIR, 'indexers', self.name)
             if sickbeard.PROXY_SETTING and sickbeard.PROXY_INDEXERS:
                 indexerConfig[self.indexerID]['api_params']['proxy'] = sickbeard.PROXY_SETTING
 
diff --git a/sickbeard/logger.py b/sickbeard/logger.py
index 0a14f5f36face2b9ae1eddccf184a4b0dd0e24fb..8d8f5ac3118b16159db0b195168cb5e9a7e0e879 100644
--- a/sickbeard/logger.py
+++ b/sickbeard/logger.py
@@ -33,10 +33,11 @@ from github import Github, InputFileContent
 
 import sickbeard
 from sickbeard import classes
-from sickrage.helper.common import dateTimeFormat
-from sickrage.helper.exceptions import ex
-from sickrage.helper.encoding import ss
 
+from sickrage.helper.encoding import ss
+from sickrage.helper.encoding import ek
+from sickrage.helper.exceptions import ex
+from sickrage.helper.common import dateTimeFormat
 
 # log levels
 ERROR = logging.ERROR
@@ -102,7 +103,7 @@ class Logger(object):
         self.submitter_running = False
 
     def initLogging(self, consoleLogging=False, fileLogging=False, debugLogging=False):
-        self.logFile = self.logFile or os.path.join(sickbeard.LOG_DIR, 'sickrage.log')
+        self.logFile = self.logFile or ek(os.path.join, sickbeard.LOG_DIR, 'sickrage.log')
         self.debugLogging = debugLogging
         self.consoleLogging = consoleLogging
         self.fileLogging = fileLogging
@@ -213,12 +214,12 @@ class Logger(object):
             # read log file
             log_data = None
 
-            if os.path.isfile(self.logFile):
+            if ek(os.path.isfile, self.logFile):
                 with io.open(self.logFile, 'r', encoding='utf-8') as f:
                     log_data = f.readlines()
 
             for i in range(1, int(sickbeard.LOG_NR)):
-                if os.path.isfile(self.logFile + ".%i" % i) and (len(log_data) <= 500):
+                if ek(os.path.isfile, self.logFile + ".%i" % i) and (len(log_data) <= 500):
                     with io.open(self.logFile + ".%i" % i, 'r', encoding='utf-8') as f:
                         log_data += f.readlines()
 
diff --git a/sickbeard/metadata/generic.py b/sickbeard/metadata/generic.py
index 0a2cc1a414d709ff6f160523906b579d6bebcd42..c9d0c8eeb0baba2757d10989b2b33eebebecd177 100644
--- a/sickbeard/metadata/generic.py
+++ b/sickbeard/metadata/generic.py
@@ -35,6 +35,7 @@ from sickbeard.metadata import helpers as metadata_helpers
 from sickbeard.show_name_helpers import allPossibleShowNames
 from sickrage.helper.common import replace_extension
 from sickrage.helper.exceptions import ex
+from sickrage.helper.encoding import ek
 
 from tmdb_api.tmdb_api import TMDB
 
@@ -118,7 +119,7 @@ class GenericMetadata(object):
     def _check_exists(location):
         if location:
             assert isinstance(location, unicode)
-            result = os.path.isfile(location)
+            result = ek(os.path.isfile, location)
             logger.log(u"Checking if " + location + " exists: " + str(result), logger.DEBUG)
             return result
         return False
@@ -154,19 +155,19 @@ class GenericMetadata(object):
         return self._check_exists(self.get_season_all_banner_path(show_obj))
 
     def get_show_file_path(self, show_obj):
-        return os.path.join(show_obj.location, self._show_metadata_filename)
+        return ek(os.path.join, show_obj.location, self._show_metadata_filename)
 
     def get_episode_file_path(self, ep_obj):
         return replace_extension(ep_obj.location, self._ep_nfo_extension)
 
     def get_fanart_path(self, show_obj):
-        return os.path.join(show_obj.location, self.fanart_name)
+        return ek(os.path.join, show_obj.location, self.fanart_name)
 
     def get_poster_path(self, show_obj):
-        return os.path.join(show_obj.location, self.poster_name)
+        return ek(os.path.join, show_obj.location, self.poster_name)
 
     def get_banner_path(self, show_obj):
-        return os.path.join(show_obj.location, self.banner_name)
+        return ek(os.path.join, show_obj.location, self.banner_name)
 
     @staticmethod
     def get_episode_thumb_path(ep_obj):
@@ -175,7 +176,7 @@ class GenericMetadata(object):
         ep_obj: a TVEpisode instance for which to create the thumbnail
         """
         assert isinstance(ep_obj.location, unicode)
-        if os.path.isfile(ep_obj.location):
+        if ek(os.path.isfile, ep_obj.location):
 
             tbn_filename = ep_obj.location.rpartition(".")
 
@@ -204,7 +205,7 @@ class GenericMetadata(object):
         else:
             season_poster_filename = 'season' + str(season).zfill(2)
 
-        return os.path.join(show_obj.location, season_poster_filename + '-poster.jpg')
+        return ek(os.path.join, show_obj.location, season_poster_filename + '-poster.jpg')
 
     @staticmethod
     def get_season_banner_path(show_obj, season):
@@ -222,13 +223,13 @@ class GenericMetadata(object):
         else:
             season_banner_filename = 'season' + str(season).zfill(2)
 
-        return os.path.join(show_obj.location, season_banner_filename + '-banner.jpg')
+        return ek(os.path.join, show_obj.location, season_banner_filename + '-banner.jpg')
 
     def get_season_all_poster_path(self, show_obj):
-        return os.path.join(show_obj.location, self.season_all_poster_name)
+        return ek(os.path.join, show_obj.location, self.season_all_poster_name)
 
     def get_season_all_banner_path(self, show_obj):
-        return os.path.join(show_obj.location, self.season_all_banner_name)
+        return ek(os.path.join, show_obj.location, self.season_all_banner_name)
 
     # pylint: disable=W0613,R0201
     def _show_data(self, show_obj):
@@ -400,12 +401,12 @@ class GenericMetadata(object):
         nfo_file_path = self.get_show_file_path(show_obj)
         assert isinstance(nfo_file_path, unicode)
 
-        nfo_file_dir = os.path.dirname(nfo_file_path)
+        nfo_file_dir = ek(os.path.dirname, nfo_file_path)
 
         try:
-            if not os.path.isdir(nfo_file_dir):
+            if not ek(os.path.isdir, nfo_file_dir):
                 logger.log(u"Metadata dir didn't exist, creating it at " + nfo_file_dir, logger.DEBUG)
-                os.makedirs(nfo_file_dir)
+                ek(os.makedirs, nfo_file_dir)
                 helpers.chmodAsParent(nfo_file_dir)
 
             logger.log(u"Writing show nfo file to " + nfo_file_path, logger.DEBUG)
@@ -445,12 +446,12 @@ class GenericMetadata(object):
 
         nfo_file_path = self.get_episode_file_path(ep_obj)
         assert isinstance(nfo_file_path, unicode)
-        nfo_file_dir = os.path.dirname(nfo_file_path)
+        nfo_file_dir = ek(os.path.dirname, nfo_file_path)
 
         try:
-            if not os.path.isdir(nfo_file_dir):
+            if not ek(os.path.isdir, nfo_file_dir):
                 logger.log(u"Metadata dir didn't exist, creating it at " + nfo_file_dir, logger.DEBUG)
-                os.makedirs(nfo_file_dir)
+                ek(os.makedirs, nfo_file_dir)
                 helpers.chmodAsParent(nfo_file_dir)
 
             logger.log(u"Writing episode nfo file to " + nfo_file_path, logger.DEBUG)
@@ -686,20 +687,20 @@ class GenericMetadata(object):
         assert isinstance(image_path, unicode)
 
         # don't bother overwriting it
-        if os.path.isfile(image_path):
+        if ek(os.path.isfile, image_path):
             logger.log(u"Image already exists, not downloading", logger.DEBUG)
             return False
 
-        image_dir = os.path.dirname(image_path)
+        image_dir = ek(os.path.dirname, image_path)
 
         if not image_data:
             logger.log(u"Unable to retrieve image to save in %s, skipping" % image_path, logger.DEBUG)
             return False
 
         try:
-            if not os.path.isdir(image_dir):
+            if not ek(os.path.isdir, image_dir):
                 logger.log(u"Metadata dir didn't exist, creating it at " + image_dir, logger.DEBUG)
-                os.makedirs(image_dir)
+                ek(os.makedirs, image_dir)
                 helpers.chmodAsParent(image_dir)
 
             outFile = io.open(image_path, 'wb')
@@ -905,9 +906,9 @@ class GenericMetadata(object):
 
         assert isinstance(folder, unicode)
 
-        metadata_path = os.path.join(folder, self._show_metadata_filename)
+        metadata_path = ek(os.path.join, folder, self._show_metadata_filename)
 
-        if not os.path.isdir(folder) or not os.path.isfile(metadata_path):
+        if not ek(os.path.isdir, folder) or not ek(os.path.isfile, metadata_path):
             logger.log(u"Can't load the metadata file from " + metadata_path + ", it doesn't exist", logger.DEBUG)
             return empty_return
 
diff --git a/sickbeard/name_parser/parser.py b/sickbeard/name_parser/parser.py
index 1394505ac1b8c158a3df531bf66ef45eb7dcc0a0..5284c09d8c28149ea91e898213952c1d5d55d64a 100644
--- a/sickbeard/name_parser/parser.py
+++ b/sickbeard/name_parser/parser.py
@@ -421,7 +421,7 @@ class NameParser(object):
         file_name_result = self._parse_string(base_file_name)
 
         # use only the direct parent dir
-        dir_name = os.path.basename(dir_name)
+        dir_name = ek(os.path.basename, dir_name)
 
         # parse the dirname for extra info if needed
         dir_name_result = self._parse_string(dir_name)
diff --git a/sickbeard/name_parser/regexes.py b/sickbeard/name_parser/regexes.py
index 211375f92273c29b139ab8b1048abbccd63b2591..b12fffb4c420114ca4f56b43d8ee8ffe87438c91 100644
--- a/sickbeard/name_parser/regexes.py
+++ b/sickbeard/name_parser/regexes.py
@@ -340,6 +340,24 @@ anime_regexes = [
     .*?
     '''),
 
+    ('anime_SxxExx',
+     # Show.Name.S01E02.Source.Quality.Etc-Group
+     # Show Name - S01E02 - My Ep Name
+     # Show.Name.S01.E03.My.Ep.Name
+     # Show.Name.S01E02E03.Source.Quality.Etc-Group
+     # Show Name - S01E02-03 - My Ep Name
+     # Show.Name.S01.E02.E03
+     r'''
+     ^((?P<series_name>.+?)[. _-]+)?             # Show_Name and separator
+     (\()?s(?P<season_num>\d+)[. _-]*            # S01 and optional separator
+     e(?P<ep_num>\d+)(\))?                       # E02 and separator
+     (([. _-]*e|-)                               # linking e/- char
+     (?P<extra_ep_num>(?!(1080|720|480)[pi])\d+)(\))?)*   # additional E03/etc
+     [. _-]*((?P<extra_info>.+?)                 # Source_Quality_Etc-
+     ((?<![. _-])(?<!WEB)                        # Make sure this is really the release group
+     -(?P<release_group>[^- ]+([. _-]\[.*\])?))?)?$              # Group
+     '''),
+
     ('anime_and_normal',
      # Bleach - s16e03-04 - 313-314
      # Bleach.s16e03-04.313-314
diff --git a/sickbeard/network_timezones.py b/sickbeard/network_timezones.py
index 15f09d15b2df9a4e9e2ec56596c2cbcd42e28da4..ae1b2eafeaee808dc8886f6f7457b30abad172fc 100644
--- a/sickbeard/network_timezones.py
+++ b/sickbeard/network_timezones.py
@@ -25,6 +25,7 @@ from dateutil import tz
 from sickbeard import db
 from sickbeard import helpers
 from sickbeard import logger
+from sickrage.helper.common import try_int
 
 # regex to parse time (12/24 hour format)
 time_regex = re.compile(r'(\d{1,2})(([:.](\d{2,2}))? ?([PA][. ]? ?M)|[:.](\d{2,2}))\b', flags=re.IGNORECASE)
@@ -136,8 +137,8 @@ def parse_date_time(d, t, network):
     if mo is not None and len(mo.groups()) >= 5:
         if mo.group(5) is not None:
             try:
-                hr = helpers.tryInt(mo.group(1))
-                m = helpers.tryInt(mo.group(4))
+                hr = try_int(mo.group(1))
+                m = try_int(mo.group(4))
                 ap = mo.group(5)
                 # convert am/pm to 24 hour clock
                 if ap is not None:
@@ -150,8 +151,8 @@ def parse_date_time(d, t, network):
                 m = 0
         else:
             try:
-                hr = helpers.tryInt(mo.group(1))
-                m = helpers.tryInt(mo.group(6))
+                hr = try_int(mo.group(1))
+                m = try_int(mo.group(6))
             except Exception:
                 hr = 0
                 m = 0
@@ -162,7 +163,7 @@ def parse_date_time(d, t, network):
         hr = 0
         m = 0
 
-    te = datetime.datetime.fromordinal(helpers.tryInt(d) or 1)
+    te = datetime.datetime.fromordinal(try_int(d) or 1)
     try:
         foreign_timezone = get_network_timezone(network, network_dict)
         return datetime.datetime(te.year, te.month, te.day, hr, m, tzinfo=foreign_timezone)
diff --git a/sickbeard/notifiers/libnotify.py b/sickbeard/notifiers/libnotify.py
index 162dfe2189890cce139041396d2034fcacb1858e..ab4efff673a9e51ba30a22db6e920d880d56381a 100644
--- a/sickbeard/notifiers/libnotify.py
+++ b/sickbeard/notifiers/libnotify.py
@@ -23,7 +23,7 @@ import cgi
 import sickbeard
 
 from sickbeard import logger, common
-
+from sickrage.helper.encoding import ek
 
 def diagnose():
     """
@@ -95,7 +95,7 @@ class LibnotifyNotifier(object):
     def notify_subtitle_download(self, ep_name, lang):
         if sickbeard.LIBNOTIFY_NOTIFY_ONSUBTITLEDOWNLOAD:
             self._notify(common.notifyStrings[common.NOTIFY_SUBTITLE_DOWNLOAD], ep_name + ": " + lang)
-            
+
     def notify_git_update(self, new_version="??"):
         if sickbeard.USE_LIBNOTIFY:
             update_text = common.notifyStrings[common.NOTIFY_GIT_UPDATE_TEXT]
@@ -113,7 +113,7 @@ class LibnotifyNotifier(object):
 
         # Can't make this a global constant because PROG_DIR isn't available
         # when the module is imported.
-        icon_path = os.path.join(sickbeard.PROG_DIR, 'gui', 'slick', 'images', 'ico', 'favicon-120.png')
+        icon_path = ek(os.path.join, sickbeard.PROG_DIR, 'gui', 'slick', 'images', 'ico', 'favicon-120.png')
 
         # If the session bus can't be acquired here a bunch of warning messages
         # will be printed but the call to show() will still return True.
diff --git a/sickbeard/nzbget.py b/sickbeard/nzbget.py
index 5e12b521aac631e9e36b8b9290dd5dbaf6860891..d5ea16b16663353cbf76284df0966f9d94e473f1 100644
--- a/sickbeard/nzbget.py
+++ b/sickbeard/nzbget.py
@@ -17,7 +17,6 @@
 # You should have received a copy of the GNU General Public License
 # along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
 
-
 import httplib
 import datetime
 from base64 import standard_b64encode
@@ -25,8 +24,9 @@ import xmlrpclib
 
 import sickbeard
 from sickbeard.providers.generic import GenericProvider
-from sickbeard import logger, helpers
+from sickbeard import logger
 from sickbeard.common import Quality
+from sickrage.helper.common import try_int
 
 
 def sendNZB(nzb, proper=False):
@@ -108,7 +108,7 @@ def sendNZB(nzb, proper=False):
     try:
         # Find out if nzbget supports priority (Version 9.0+), old versions beginning with a 0.x will use the old command
         nzbget_version_str = nzbGetRPC.version()
-        nzbget_version = helpers.tryInt(nzbget_version_str[:nzbget_version_str.find(".")])
+        nzbget_version = try_int(nzbget_version_str[:nzbget_version_str.find(".")])
         if nzbget_version == 0:
             if nzbcontent64 is not None:
                 nzbget_result = nzbGetRPC.append(nzb.name + ".nzb", category, addToTop, nzbcontent64)
diff --git a/sickbeard/postProcessor.py b/sickbeard/postProcessor.py
index 0dee5ce8001386351f736d265ecfa954e12bc3d3..0e33b65de3d96861666ffd15c0249c4070749584 100644
--- a/sickbeard/postProcessor.py
+++ b/sickbeard/postProcessor.py
@@ -39,6 +39,7 @@ from sickrage.helper.common import remove_extension, replace_extension, subtitle
 from sickrage.helper.encoding import ek
 from sickrage.helper.exceptions import EpisodeNotFoundException, EpisodePostProcessingFailedException, ex
 from sickrage.helper.exceptions import ShowDirectoryNotFoundException
+from sickrage.show.Show import Show
 from babelfish import language_converters
 
 import adba
@@ -158,9 +159,9 @@ class PostProcessor(object):
         """
         def recursive_glob(treeroot, pattern):
             results = []
-            for base, _, files in os.walk(treeroot.encode(sickbeard.SYS_ENCODING)):
+            for base, _, files in ek(os.walk, treeroot.encode(sickbeard.SYS_ENCODING)):
                 goodfiles = fnmatch.filter(files, pattern)
-                results.extend(os.path.join(base, f) for f in goodfiles)
+                results.extend(ek(os.path.join, base, f) for f in goodfiles)
             return results
 
         if not file_path:
@@ -335,13 +336,13 @@ 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 subtitle_extensions:
-                cur_lang = os.path.splitext(cur_extension)[0]
+            if ek(os.path.splitext, cur_extension)[1][1:] in subtitle_extensions:
+                cur_lang = ek(os.path.splitext, cur_extension)[0]
                 if cur_lang:
                     cur_lang = cur_lang.lower()
                     if cur_lang == 'pt-br':
                         cur_lang = 'pt-BR'
-                    cur_extension = cur_lang + os.path.splitext(cur_extension)[1]
+                    cur_extension = cur_lang + ek(os.path.splitext, cur_extension)[1]
 
             # replace .nfo with .nfo-orig to avoid conflicts
             if cur_extension == 'nfo' and sickbeard.NFO_RENAME is True:
@@ -499,7 +500,7 @@ class PostProcessor(object):
             if quality == common.Quality.UNKNOWN:
                 quality = None
 
-            show = helpers.findCertainShow(sickbeard.showList, indexer_id)
+            show = Show.find(sickbeard.showList, indexer_id)
 
             self.in_history = True
             self.version = version
@@ -1139,7 +1140,7 @@ class PostProcessor(object):
                 with cur_ep.lock:
                     cur_ep.location = ek(os.path.join, dest_path, new_file_name)
                     cur_ep.refreshSubtitles()
-                    cur_ep.downloadSubtitles(force=True)
+                    cur_ep.download_subtitles(force=True)
 
         # now that processing has finished, we can put the info in the DB. If we do it earlier, then when processing fails, it won't try again.
         if len(sql_l) > 0:
diff --git a/sickbeard/processTV.py b/sickbeard/processTV.py
index 9e2ed2431096cda5cc396d8f0a95bcb3ccd7f644..9e03f21d49f014feba9759be0836f75dfd426319 100644
--- a/sickbeard/processTV.py
+++ b/sickbeard/processTV.py
@@ -79,7 +79,7 @@ def delete_folder(folder, check_empty=True):
 
         try:
             logger.log(u"Deleting folder (if it's empty): " + folder)
-            os.rmdir(folder)
+            ek(os.rmdir, folder)
         except (OSError, IOError), e:
             logger.log(u"Warning: unable to delete folder: " + folder + ": " + ex(e), logger.WARNING)
             return False
@@ -331,18 +331,18 @@ def validateDir(path, dirName, nzbNameOriginal, failed, result):
         return False
 
     if failed:
-        process_failed(os.path.join(path, dirName), nzbNameOriginal, result)
+        process_failed(ek(os.path.join, path, dirName), nzbNameOriginal, result)
         result.missedfiles.append(dirName + " : Failed download")
         return False
 
-    if helpers.is_hidden_folder(os.path.join(path, dirName)):
+    if helpers.is_hidden_folder(ek(os.path.join, path, dirName)):
         result.output += logHelper(u"Ignoring hidden folder: " + dirName, logger.DEBUG)
         result.missedfiles.append(dirName + " : Hidden folder")
         return False
 
     # make sure the dir isn't inside a show dir
     myDB = db.DBConnection()
-    sqlResults = myDB.select("SELECT * FROM tv_shows")
+    sqlResults = myDB.select("SELECT location FROM tv_shows")
 
     for sqlShow in sqlResults:
         if dirName.lower().startswith(ek(os.path.realpath, sqlShow["location"]).lower() + os.sep) or \
@@ -414,11 +414,11 @@ def unRAR(path, rarFiles, force, result):
             result.output += logHelper(u"Unpacking archive: " + archive, logger.DEBUG)
 
             try:
-                rar_handle = RarFile(os.path.join(path, archive))
+                rar_handle = RarFile(ek(os.path.join, path, archive))
 
                 # Skip extraction if any file in archive has previously been extracted
                 skip_file = False
-                for file_in_archive in [os.path.basename(x.filename) for x in rar_handle.infolist() if not x.isdir]:
+                for file_in_archive in [ek(os.path.basename, x.filename) for x in rar_handle.infolist() if not x.isdir]:
                     if already_postprocessed(path, file_in_archive, force, result):
                         result.output += logHelper(
                             u"Archive file already post-processed, extraction skipped: " + file_in_archive,
@@ -432,7 +432,7 @@ def unRAR(path, rarFiles, force, result):
                 rar_handle.extract(path=path, withSubpath=False, overwrite=False)
                 for x in rar_handle.infolist():
                     if not x.isdir:
-                        basename = os.path.basename(x.filename)
+                        basename = ek(os.path.basename, x.filename)
                         if basename not in unpacked_files:
                             unpacked_files.append(basename)
                 del rar_handle
@@ -488,38 +488,31 @@ def already_postprocessed(dirName, videofile, force, result):
 
     # Avoid processing the same dir again if we use a process method <> move
     myDB = db.DBConnection()
-    sqlResult = myDB.select("SELECT * FROM tv_episodes WHERE release_name = ?", [dirName])
+    sqlResult = myDB.select("SELECT release_name FROM tv_episodes WHERE release_name in (?, ?) LIMIT 1", [dirName, videofile.rpartition('.')[0]])
     if sqlResult:
         # result.output += logHelper(u"You're trying to post process a dir that's already been processed, skipping", logger.DEBUG)
         return True
 
-    else:
-        sqlResult = myDB.select("SELECT * FROM tv_episodes WHERE release_name = ?", [videofile.rpartition('.')[0]])
-        if sqlResult:
-            # result.output += logHelper(u"You're trying to post process a video that's already been processed, skipping", logger.DEBUG)
-            return True
-
-        # Needed if we have downloaded the same episode @ different quality
-        # But we need to make sure we check the history of the episode we're going to PP, and not others
-        np = NameParser(dirName, tryIndexers=True)
-        try:  # if it fails to find any info (because we're doing an unparsable folder (like the TV root dir) it will throw an exception, which we want to ignore
-            parse_result = np.parse(dirName)
-        except Exception:  # ignore the exception, because we kind of expected it, but create parse_result anyway so we can perform a check on it.
-            parse_result = False
-
-
-        search_sql = "SELECT tv_episodes.indexerid, history.resource FROM tv_episodes INNER JOIN history ON history.showid=tv_episodes.showid"  # This part is always the same
-        search_sql += " WHERE history.season=tv_episodes.season and history.episode=tv_episodes.episode"
-        # If we find a showid, a season number, and one or more episode numbers then we need to use those in the query
-        if parse_result and (parse_result.show.indexerid and parse_result.episode_numbers and parse_result.season_number):
-            search_sql += " and tv_episodes.showid = '" + str(parse_result.show.indexerid) + "' and tv_episodes.season = '" + str(parse_result.season_number) + "' and tv_episodes.episode = '" + str(parse_result.episode_numbers[0]) + "'"
-
-        search_sql += " and tv_episodes.status IN (" + ",".join([str(x) for x in common.Quality.DOWNLOADED]) + ")"
-        search_sql += " and history.resource LIKE ?"
-        sqlResult = myDB.select(search_sql, ['%' + videofile])
-        if sqlResult:
-            # result.output += logHelper(u"You're trying to post process a video that's already been processed, skipping", logger.DEBUG)
-            return True
+    # Needed if we have downloaded the same episode @ different quality
+    # But we need to make sure we check the history of the episode we're going to PP, and not others
+    np = NameParser(dirName, tryIndexers=True)
+    try:  # if it fails to find any info (because we're doing an unparsable folder (like the TV root dir) it will throw an exception, which we want to ignore
+        parse_result = np.parse(dirName)
+    except Exception:  # ignore the exception, because we kind of expected it, but create parse_result anyway so we can perform a check on it.
+        parse_result = False
+
+    search_sql = "SELECT tv_episodes.indexerid, history.resource FROM tv_episodes INNER JOIN history ON history.showid=tv_episodes.showid"  # This part is always the same
+    search_sql += " WHERE history.season=tv_episodes.season and history.episode=tv_episodes.episode"
+    # If we find a showid, a season number, and one or more episode numbers then we need to use those in the query
+    if parse_result and (parse_result.show.indexerid and parse_result.episode_numbers and parse_result.season_number):
+        search_sql += " and tv_episodes.showid = '" + str(parse_result.show.indexerid) + "' and tv_episodes.season = '" + str(parse_result.season_number) + "' and tv_episodes.episode = '" + str(parse_result.episode_numbers[0]) + "'"
+
+    search_sql += " and tv_episodes.status IN (" + ",".join([str(x) for x in common.Quality.DOWNLOADED]) + ")"
+    search_sql += " and history.resource LIKE ? LIMIT 1"
+    sqlResult = myDB.select(search_sql, ['%' + videofile])
+    if sqlResult:
+        # result.output += logHelper(u"You're trying to post process a video that's already been processed, skipping", logger.DEBUG)
+        return True
 
     return False
 
@@ -592,10 +585,10 @@ def get_path_dir_files(dirName, nzbName, proc_type):
             break
     else:
         path, dirs = ek(os.path.split, dirName)  # Script Post Processing
-        if not nzbName is None and not nzbName.endswith('.nzb') and os.path.isfile(
-                os.path.join(dirName, nzbName)):  # For single torrent file without Dir
+        if not nzbName is None and not nzbName.endswith('.nzb') and ek(os.path.isfile,
+                ek(os.path.join, dirName, nzbName)):  # For single torrent file without Dir
             dirs = []
-            files = [os.path.join(dirName, nzbName)]
+            files = [ek(os.path.join, dirName, nzbName)]
         else:
             dirs = [dirs]
             files = []
diff --git a/sickbeard/providers/__init__.py b/sickbeard/providers/__init__.py
index 60116727e358a89c64080c7c41c3c43f82b0a153..1d838ce81f302b50738ec923c46b9bcb68bfed77 100644
--- a/sickbeard/providers/__init__.py
+++ b/sickbeard/providers/__init__.py
@@ -26,56 +26,18 @@ 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, elitetorrent
-
-__all__ = ['womble',
-           'btn',
-           'thepiratebay',
-           'kat',
-           'torrentleech',
-           'scc',
-           'hdtorrents',
-           'torrentday',
-           'hdbits',
-           'hounddawgs',
-           'iptorrents',
-           'omgwtfnzbs',
-           'nextgen',
-           'speedcd',
-           'nyaatorrents',
-           'animenzb',
-           'torrentbytes',
-           'freshontv',
-           'titansoftv',
-           'libertalia',
-           'morethantv',
-           'bitsoup',
-           't411',
-           'tokyotoshokan',
-           'alpharatio',
-           'shazbat',
-           'rarbg',
-           'tntvillage',
-           'binsearch',
-           'bluetigers',
-           'cpasbien',
-           'fnt',
-           'xthor',
-           'scenetime',
-           'btdigg',
-           'strike',
-           'transmitthenet',
-           'tvchaosuk',
-           'torrentproject',
-           'extratorrent',
-           'bitcannon',
-           'torrentz',
-           'pretome',
-           'gftracker',
-           'hdspace',
-           'newpct',
-           'elitetorrent'
-           ]
+    scenetime, btdigg, strike, transmitthenet, tvchaosuk, bitcannon, pretome, gftracker, hdspace, newpct, elitetorrent, bitsnoop
+
+__all__ = [
+    'womble', 'btn', 'thepiratebay', 'kat', 'torrentleech', 'scc', 'hdtorrents',
+    'torrentday', 'hdbits', 'hounddawgs', 'iptorrents', 'omgwtfnzbs', 'nextgen',
+    'speedcd', 'nyaatorrents', 'animenzb', 'torrentbytes', 'freshontv', 'titansoftv',
+    'libertalia', 'morethantv', 'bitsoup', 't411', 'tokyotoshokan', 'alpharatio',
+    'shazbat', 'rarbg', 'tntvillage', 'binsearch', 'bluetigers', 'cpasbien',
+    'fnt', 'xthor', 'scenetime', 'btdigg', 'strike', 'transmitthenet', 'tvchaosuk',
+    'torrentproject', 'extratorrent', 'bitcannon', 'torrentz', 'pretome', 'gftracker',
+    'hdspace', 'newpct', 'elitetorrent', 'bitsnoop'
+]
 
 
 def sortedProviderList(randomize=False):
diff --git a/sickbeard/providers/alpharatio.py b/sickbeard/providers/alpharatio.py
index 0e58915fa0e4e706f6c9e0334d592e71f2fb31dd..c8549a7a3b813576d5c2533f201146037d098e82 100644
--- a/sickbeard/providers/alpharatio.py
+++ b/sickbeard/providers/alpharatio.py
@@ -33,7 +33,6 @@ class AlphaRatioProvider(generic.TorrentProvider):
 
         generic.TorrentProvider.__init__(self, "AlphaRatio")
 
-        self.supportsBacklog = True
 
         self.username = None
         self.password = None
diff --git a/sickbeard/providers/animenzb.py b/sickbeard/providers/animenzb.py
index f06300540ef867677d43a1db47833a792efbe351..db95585cd5f91e6cfa03b366af461179c1d6a5d5 100644
--- a/sickbeard/providers/animenzb.py
+++ b/sickbeard/providers/animenzb.py
@@ -42,12 +42,11 @@ class animenzb(generic.NZBProvider):
         self.supportsAbsoluteNumbering = True
         self.anime_only = True
 
-        self.cache = animenzbCache(self)
-
         self.urls = {'base_url': 'http://animenzb.com//'}
-
         self.url = self.urls['base_url']
 
+        self.cache = animenzbCache(self)
+
     def _get_season_search_strings(self, ep_obj):
         return [x for x in show_name_helpers.makeSceneSeasonSearchString(self.show, ep_obj)]
 
diff --git a/sickbeard/providers/binsearch.py b/sickbeard/providers/binsearch.py
index c71d6e3123cb63f09dff32b5806b8b660eb326e7..9c911276d5d5d2a4fe1161ab6f34d6b78ec14e78 100644
--- a/sickbeard/providers/binsearch.py
+++ b/sickbeard/providers/binsearch.py
@@ -31,7 +31,7 @@ class BinSearchProvider(generic.NZBProvider):
         self.cache = BinSearchCache(self)
         self.urls = {'base_url': 'https://www.binsearch.info/'}
         self.url = self.urls['base_url']
-
+        self.supportsBacklog = False
 
 class BinSearchCache(tvcache.TVCache):
     def __init__(self, provider_obj):
diff --git a/sickbeard/providers/bitcannon.py b/sickbeard/providers/bitcannon.py
index a3b9481d1c8c0a50c731bd8c6d247308f43452f9..a4984066b41f8d7ca6c50837c1bbe0592583b97c 100644
--- a/sickbeard/providers/bitcannon.py
+++ b/sickbeard/providers/bitcannon.py
@@ -29,7 +29,6 @@ class BitCannonProvider(generic.TorrentProvider):
 
         generic.TorrentProvider.__init__(self, "BitCannon")
 
-        self.supportsBacklog = True
         self.public = True
 
         self.minseed = None
diff --git a/sickbeard/providers/bitsnoop.py b/sickbeard/providers/bitsnoop.py
new file mode 100644
index 0000000000000000000000000000000000000000..831a763a6ac71a8dabac3e3597a42d339b9e4d69
--- /dev/null
+++ b/sickbeard/providers/bitsnoop.py
@@ -0,0 +1,143 @@
+# coding=utf-8
+# Author: Gonçalo (aka duramato) <matigonkas@outlook.com>
+# 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/>.
+
+import traceback
+from bs4 import BeautifulSoup
+
+import sickbeard
+from sickbeard import logger
+from sickbeard import tvcache
+from sickbeard.providers import generic
+from sickrage.helper.common import try_int
+
+
+class BitSnoopProvider(generic.TorrentProvider): # pylint: disable=R0902,R0913
+    def __init__(self):
+        generic.TorrentProvider.__init__(self, "BitSnoop")
+
+        self.urls = {
+            'index': 'http://bitsnoop.com',
+            'search': 'http://bitsnoop.com/search/video/',
+            'rss': 'http://bitsnoop.com/new_video.html?fmt=rss'
+            }
+
+        self.url = self.urls['index']
+
+        self.public = True
+        self.ratio = None
+        self.minseed = None
+        self.minleech = None
+
+        self.proper_strings = ['PROPER', 'REPACK']
+
+        self.cache = BitSnoopCache(self)
+
+    def _doSearch(self, search_strings, search_mode='eponly', epcount=0, age=0, epObj=None): # pylint: disable=R0912,R0913,R0914
+
+        results = []
+        items = {'Season': [], 'Episode': [], 'RSS': []}
+
+        for mode in search_strings.keys():
+            logger.log(u"Search Mode: %s" % mode, logger.DEBUG)
+            for search_string in search_strings[mode]:
+
+                if mode != 'RSS':
+                    logger.log(u"Search string: %s " % search_string, logger.DEBUG)
+
+                try:
+                    url = (self.urls['rss'], self.urls['search'] + search_string + '/s/d/1/?fmt=rss')[mode != 'RSS']
+                    data = self.getURL(url)
+                    if not data:
+                        logger.log(u"No data returned from provider", logger.DEBUG)
+                        continue
+
+                    if not data.startswith('<?xml'):
+                        logger.log(u'Expected xml but got something else, is your mirror failing?', logger.INFO)
+                        continue
+
+                    data = BeautifulSoup(data, features=["html5lib", "permissive"])
+
+                    entries = entries = data.findAll('item')
+
+                    for item in entries:
+                        try:
+                            if not item.category.text.endswith(('TV', 'Anime')):
+                                continue
+
+                            title = item.title.text
+                            assert isinstance(title, unicode)
+                            # Use the torcache link bitsnoop 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']
+                            if sickbeard.TORRENT_METHOD != "blackhole" or 'torcache' not in download_url:
+                                download_url = item.find('magneturi').next.replace('CDATA', '').strip('[]') + self._custom_trackers
+
+                            if not (title and download_url):
+                                continue
+
+                            seeders = try_int(item.find('numseeders').text, 0)
+                            leechers = try_int(item.find('numleechers').text, 0)
+                            size = try_int(item.find('size').text, -1)
+
+                            info_hash = item.find('infohash').text
+
+                        except (AttributeError, TypeError, KeyError, ValueError):
+                            continue
+
+                            # Filter unseeded torrent
+                        if seeders < self.minseed or leechers < self.minleech:
+                            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, info_hash
+                        if mode != 'RSS':
+                            logger.log(u"Found result: %s " % title, logger.DEBUG)
+
+                        items[mode].append(item)
+
+                except (AttributeError, TypeError, KeyError, ValueError):
+                    logger.log(u"Failed parsing provider. Traceback: %r" % traceback.format_exc(), logger.ERROR)
+
+            # 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
+
+
+    def seedRatio(self):
+        return self.ratio
+
+
+class BitSnoopCache(tvcache.TVCache):
+    def __init__(self, provider_obj):
+
+        tvcache.TVCache.__init__(self, provider_obj)
+
+        self.minTime = 20
+
+    def _getRSSData(self):
+        search_strings = {'RSS': ['rss']}
+        return {'entries': self.provider._doSearch(search_strings)}
+
+
+provider = BitSnoopProvider()
diff --git a/sickbeard/providers/bitsoup.py b/sickbeard/providers/bitsoup.py
index e25855c9deee2c8029bee140220fb234dc4fc253..a3e10b6e8e94211b171de77b944da663949470b3 100644
--- a/sickbeard/providers/bitsoup.py
+++ b/sickbeard/providers/bitsoup.py
@@ -38,7 +38,6 @@ class BitSoupProvider(generic.TorrentProvider):
 
         self.url = self.urls['base_url']
 
-        self.supportsBacklog = True
 
         self.username = None
         self.password = None
diff --git a/sickbeard/providers/bluetigers.py b/sickbeard/providers/bluetigers.py
index 561503a401732b3df83c43ef7da75950493d16fc..c6d2902bd59ffe8e868214be9a6de7ce9f12acef 100644
--- a/sickbeard/providers/bluetigers.py
+++ b/sickbeard/providers/bluetigers.py
@@ -33,7 +33,6 @@ class BLUETIGERSProvider(generic.TorrentProvider):
     def __init__(self):
         generic.TorrentProvider.__init__(self, "BLUETIGERS")
 
-        self.supportsBacklog = True
 
         self.username = None
         self.password = None
diff --git a/sickbeard/providers/btdigg.py b/sickbeard/providers/btdigg.py
index 4eda679d1cdf9e9371d3566cdcf4b02ad4aaaa8f..c97d225e9986c154083d66cd08a1ba9cef0ec599 100644
--- a/sickbeard/providers/btdigg.py
+++ b/sickbeard/providers/btdigg.py
@@ -31,12 +31,13 @@ class BTDIGGProvider(generic.TorrentProvider):
     def __init__(self):
         generic.TorrentProvider.__init__(self, "BTDigg")
 
-        self.supportsBacklog = True
         self.public = True
         self.ratio = 0
         self.urls = {'url': u'https://btdigg.org/',
                      'api': u'https://api.btdigg.org/api/private-341ada3245790954/s02'}
 
+        self.proper_strings = ['PROPER', 'REPACK']
+
         self.url = self.urls['url']
 
         # Unsupported
diff --git a/sickbeard/providers/btn.py b/sickbeard/providers/btn.py
index 6b3b767289e6ea00e8d6e80c2d7f97cf65274513..47f825d497ad9b9a3df299fa4c7c2c12b8c6c95d 100644
--- a/sickbeard/providers/btn.py
+++ b/sickbeard/providers/btn.py
@@ -38,7 +38,6 @@ class BTNProvider(generic.TorrentProvider):
     def __init__(self):
         generic.TorrentProvider.__init__(self, "BTN")
 
-        self.supportsBacklog = True
 
         self.supportsAbsoluteNumbering = True
 
diff --git a/sickbeard/providers/cpasbien.py b/sickbeard/providers/cpasbien.py
index e91160f8db116325b9629173d66b48db37b3678e..a2456108c1b1484e899192c8b5e49da59ec7769d 100644
--- a/sickbeard/providers/cpasbien.py
+++ b/sickbeard/providers/cpasbien.py
@@ -31,7 +31,6 @@ class CpasbienProvider(generic.TorrentProvider):
 
         generic.TorrentProvider.__init__(self, "Cpasbien")
 
-        self.supportsBacklog = True
         self.public = True
         self.ratio = None
         self.url = "http://www.cpasbien.io"
diff --git a/sickbeard/providers/elitetorrent.py b/sickbeard/providers/elitetorrent.py
index 63a9492e2c50d32af0d90572d04160f15075148b..f63582e602226b411215c4946d1349f0f97d9118 100644
--- a/sickbeard/providers/elitetorrent.py
+++ b/sickbeard/providers/elitetorrent.py
@@ -33,7 +33,6 @@ class elitetorrentProvider(generic.TorrentProvider):
 
         generic.TorrentProvider.__init__(self, "EliteTorrent")
 
-        self.supportsBacklog = True
         self.onlyspasearch = None
         self.minseed = None
         self.minleech = None
@@ -77,16 +76,16 @@ class elitetorrentProvider(generic.TorrentProvider):
             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':
+            if self.onlyspasearch and lang_info != 'es' and mode != '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':
+                if mode != '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 ''
+                self.search_params['buscar'] = search_string.strip() if mode != 'RSS' else ''
                 
                 searchURL = self.urls['search'] + '?' + urllib.parse.urlencode(self.search_params)
                 logger.log(u"Search URL: %s" % searchURL, logger.DEBUG)
@@ -130,12 +129,12 @@ class elitetorrentProvider(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/extratorrent.py b/sickbeard/providers/extratorrent.py
index eda874561dfa78266ab8f8fe4bd592cd64100af4..e760fbbb4526351cf5867a08b72b867c0f1ec864 100644
--- a/sickbeard/providers/extratorrent.py
+++ b/sickbeard/providers/extratorrent.py
@@ -23,9 +23,9 @@ from xml.parsers.expat import ExpatError
 
 from sickbeard import logger
 from sickbeard import tvcache
-from sickbeard import helpers
 from sickbeard.common import USER_AGENT
 from sickbeard.providers import generic
+from sickrage.helper.common import try_int
 
 
 class ExtraTorrentProvider(generic.TorrentProvider):
@@ -39,7 +39,6 @@ class ExtraTorrentProvider(generic.TorrentProvider):
 
         self.url = self.urls['index']
 
-        self.supportsBacklog = True
         self.public = True
         self.ratio = None
         self.minseed = None
@@ -90,8 +89,8 @@ class ExtraTorrentProvider(generic.TorrentProvider):
                         title = item['title'].decode('utf-8')
                        # info_hash = item['info_hash']
                         size = int(item['size'])
-                        seeders = helpers.tryInt(item['seeders'], 0)
-                        leechers = helpers.tryInt(item['leechers'], 0)
+                        seeders = try_int(item['seeders'], 0)
+                        leechers = try_int(item['leechers'], 0)
                         download_url = item['enclosure']['@url'] if 'enclosure' in item else self._magnet_from_details(item['link'])
 
                         if not all([title, download_url]):
diff --git a/sickbeard/providers/fnt.py b/sickbeard/providers/fnt.py
index 78bc2d1ea74e333a8bdb062758d06ed2e91a7de2..adae3518cb659b3dbb1c6eafaada3d72466d2920 100644
--- a/sickbeard/providers/fnt.py
+++ b/sickbeard/providers/fnt.py
@@ -31,7 +31,6 @@ class FNTProvider(generic.TorrentProvider):
     def __init__(self):
         generic.TorrentProvider.__init__(self, "FNT")
 
-        self.supportsBacklog = True
 
         self.username = None
         self.password = None
diff --git a/sickbeard/providers/freshontv.py b/sickbeard/providers/freshontv.py
index d0cc75351f1bbf564358aa889cd56d40f9ff5c04..c875e022d2fd4e7c8ee2ae2dffb60e69108e65e8 100644
--- a/sickbeard/providers/freshontv.py
+++ b/sickbeard/providers/freshontv.py
@@ -23,17 +23,16 @@ import traceback
 
 from sickbeard import logger
 from sickbeard import tvcache
-from sickbeard.helpers import tryInt
 from sickbeard.providers import generic
 from sickbeard.bs4_parser import BS4Parser
+from sickrage.helper.common import try_int
 
-class FreshOnTVProvider(generic.TorrentProvider):
 
+class FreshOnTVProvider(generic.TorrentProvider):
     def __init__(self):
 
         generic.TorrentProvider.__init__(self, "FreshOnTV")
 
-        self.supportsBacklog = True
 
 
         self._uid = None
@@ -203,8 +202,8 @@ class FreshOnTVProvider(generic.TorrentProvider):
                                     details_url = individual_torrent.find('a', {'class': 'torrent_name_link'})['href']
                                     torrent_id = int((re.match('.*?([0-9]+)$', details_url).group(1)).strip())
                                     download_url = self.urls['download'] % (str(torrent_id))
-                                    seeders = tryInt(individual_torrent.find('td', {'class': 'table_seeders'}).find('span').text.strip(), 1)
-                                    leechers = tryInt(individual_torrent.find('td', {'class': 'table_leechers'}).find('a').text.strip(), 0)
+                                    seeders = try_int(individual_torrent.find('td', {'class': 'table_seeders'}).find('span').text.strip(), 1)
+                                    leechers = try_int(individual_torrent.find('td', {'class': 'table_leechers'}).find('a').text.strip(), 0)
                                     # FIXME
                                     size = -1
                                 except Exception:
diff --git a/sickbeard/providers/generic.py b/sickbeard/providers/generic.py
index 89f1526be4a1656e2e8d578d6493e40140bd76ca..99b80f6e7148bc331683fab26b61923c95bd8e0e 100644
--- a/sickbeard/providers/generic.py
+++ b/sickbeard/providers/generic.py
@@ -37,6 +37,7 @@ from sickbeard.common import user_agents
 from sickrage.helper.common import sanitize_filename
 from sickrage.helper.encoding import ek
 from sickrage.helper.exceptions import ex
+from sickrage.show.Show import Show
 from sickbeard import show_name_helpers
 
 
@@ -57,7 +58,7 @@ class GenericProvider(object):
 
         self.show = None
 
-        self.supportsBacklog = False
+        self.supportsBacklog = True
         self.supportsAbsoluteNumbering = False
         self.anime_only = False
 
@@ -655,7 +656,7 @@ class TorrentProvider(GenericProvider):
         )
 
         for sqlshow in sqlResults or []:
-            show = helpers.findCertainShow(sickbeard.showList, int(sqlshow["showid"]))
+            show = Show.find(sickbeard.showList, int(sqlshow["showid"]))
             if show:
                 curEp = show.getEpisode(int(sqlshow["season"]), int(sqlshow["episode"]))
                 for term in self.proper_strings:
diff --git a/sickbeard/providers/gftracker.py b/sickbeard/providers/gftracker.py
index 14d6656cdda3a1050548120b441c249d78675049..c709b14a20e8345ff44ff2485cf9e2b9fd20cef9 100644
--- a/sickbeard/providers/gftracker.py
+++ b/sickbeard/providers/gftracker.py
@@ -32,7 +32,6 @@ class GFTrackerProvider(generic.TorrentProvider):
 
         generic.TorrentProvider.__init__(self, "GFTracker")
 
-        self.supportsBacklog = True
 
         self.username = None
         self.password = None
diff --git a/sickbeard/providers/hdbits.py b/sickbeard/providers/hdbits.py
index fe2f6b91f45375a80100c93172804fa6cfa4c8d0..33c37dbd1582fd92ed902512ab33563b243cbe1e 100644
--- a/sickbeard/providers/hdbits.py
+++ b/sickbeard/providers/hdbits.py
@@ -33,7 +33,6 @@ class HDBitsProvider(generic.TorrentProvider):
 
         generic.TorrentProvider.__init__(self, "HDBits")
 
-        self.supportsBacklog = True
 
 
         self.username = None
diff --git a/sickbeard/providers/hdspace.py b/sickbeard/providers/hdspace.py
index 5a61844d13a233194b2cd0faa0800669f8b24e55..c91255e07c9258f3699036fa5013d349e62353e2 100644
--- a/sickbeard/providers/hdspace.py
+++ b/sickbeard/providers/hdspace.py
@@ -31,7 +31,6 @@ class HDSpaceProvider(generic.TorrentProvider):
     def __init__(self):
         generic.TorrentProvider.__init__(self, "HDSpace")
 
-        self.supportsBacklog = True
 
         self.username = None
         self.password = None
diff --git a/sickbeard/providers/hdtorrents.py b/sickbeard/providers/hdtorrents.py
index 4183f702686d7d68f975bb8e59e5e86fff1798b0..2428c1932048522d1664f34ff4323fe0f78ed4bc 100644
--- a/sickbeard/providers/hdtorrents.py
+++ b/sickbeard/providers/hdtorrents.py
@@ -32,7 +32,6 @@ class HDTorrentsProvider(generic.TorrentProvider):
 
         generic.TorrentProvider.__init__(self, "HDTorrents")
 
-        self.supportsBacklog = True
 
         self.username = None
         self.password = None
diff --git a/sickbeard/providers/hounddawgs.py b/sickbeard/providers/hounddawgs.py
index 704320e3e57ffd5b3cbd2374716d5e7b29ff7d3f..48067378a5032eb4d54ad541d9c9572d859444d8 100644
--- a/sickbeard/providers/hounddawgs.py
+++ b/sickbeard/providers/hounddawgs.py
@@ -30,7 +30,6 @@ class HoundDawgsProvider(generic.TorrentProvider):
 
         generic.TorrentProvider.__init__(self, "HoundDawgs")
 
-        self.supportsBacklog = True
 
         self.username = None
         self.password = None
diff --git a/sickbeard/providers/iptorrents.py b/sickbeard/providers/iptorrents.py
index 12c29eb991a7c1cfecb980ba437a8d78bc60f239..dc412eb108d29daa376543e889d7c7011a1528b4 100644
--- a/sickbeard/providers/iptorrents.py
+++ b/sickbeard/providers/iptorrents.py
@@ -28,7 +28,6 @@ class IPTorrentsProvider(generic.TorrentProvider):
 
         generic.TorrentProvider.__init__(self, "IPTorrents")
 
-        self.supportsBacklog = True
 
 
         self.username = None
diff --git a/sickbeard/providers/kat.py b/sickbeard/providers/kat.py
index ec62eafa106a4d15b7d5c7f77a5c5a40d80dc51f..0c580a7ad7120454fd7fbac45c2817581382bd06 100755
--- a/sickbeard/providers/kat.py
+++ b/sickbeard/providers/kat.py
@@ -28,14 +28,14 @@ from sickbeard import logger
 from sickbeard import tvcache
 from sickbeard.common import USER_AGENT
 from sickbeard.providers import generic
-from sickbeard.helpers import tryInt
+from sickrage.helper.common import try_int
+
 
 class KATProvider(generic.TorrentProvider):
     def __init__(self):
 
         generic.TorrentProvider.__init__(self, "KickAssTorrents")
 
-        self.supportsBacklog = True
         self.public = True
 
         self.confirmed = True
@@ -115,10 +115,10 @@ class KATProvider(generic.TorrentProvider):
                             if not (title and download_url):
                                 continue
 
-                            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)
+                            seeders = try_int(item.find('torrent:seeds').text, 0)
+                            leechers = try_int(item.find('torrent:peers').text, 0)
+                            verified = bool(try_int(item.find('torrent:verified').text, 0))
+                            size = try_int(item.find('torrent:contentlength').text)
 
                             info_hash = item.find('torrent:infohash').text
                             # link = item['link']
@@ -126,7 +126,6 @@ class KATProvider(generic.TorrentProvider):
                         except (AttributeError, TypeError, KeyError, ValueError):
                             continue
 
-
                         # Filter unseeded torrent
                         if seeders < self.minseed or leechers < self.minleech:
                             if mode != 'RSS':
diff --git a/sickbeard/providers/libertalia.py b/sickbeard/providers/libertalia.py
index 657f3be0e50398ff0d602e7d20d9beb6c4e3d386..a329f52ccca6db91ac06d22ec92e38aa63b4fd7e 100644
--- a/sickbeard/providers/libertalia.py
+++ b/sickbeard/providers/libertalia.py
@@ -35,7 +35,6 @@ class LibertaliaProvider(generic.TorrentProvider):
 
         generic.TorrentProvider.__init__(self, "Libertalia")
 
-        self.supportsBacklog = True
 
         self.cj = cookielib.CookieJar()
 
diff --git a/sickbeard/providers/morethantv.py b/sickbeard/providers/morethantv.py
index 82ad0842a77235dde2fa4cd4252a2eaa931a2558..253a166f109cc0df4ba60dac6c779e57d684261d 100644
--- a/sickbeard/providers/morethantv.py
+++ b/sickbeard/providers/morethantv.py
@@ -37,7 +37,6 @@ class MoreThanTVProvider(generic.TorrentProvider):
 
         generic.TorrentProvider.__init__(self, "MoreThanTV")
 
-        self.supportsBacklog = True
 
         self._uid = None
         self._hash = None
diff --git a/sickbeard/providers/newpct.py b/sickbeard/providers/newpct.py
index 6365f9640944200b244cf2126e9ac22e88d67dd6..e4350ccfc8dc4906a4af58bd5a1e7dc55e2c1a4b 100644
--- a/sickbeard/providers/newpct.py
+++ b/sickbeard/providers/newpct.py
@@ -35,7 +35,6 @@ class newpctProvider(generic.TorrentProvider):
 
         generic.TorrentProvider.__init__(self, "Newpct")
 
-        self.supportsBacklog = True
         self.onlyspasearch = None
         self.cache = newpctCache(self)
 
@@ -52,19 +51,19 @@ class newpctProvider(generic.TorrentProvider):
 
         """
         Search query:
-        http://www.newpct.com/index.php?l=doSearch&q=fringe&category_=767&idioma_=1&bus_de_=All
+        http://www.newpct.com/index.php?l=doSearch&q=fringe&category_=All&idioma_=1&bus_de_=All
 
         q => Show name
         category_ = Category "Shows" (767)
         idioma_ = Language Spanish (1)
-        bus_de_ = Date from (All, Semana)
+        bus_de_ = Date from (All, hoy)
 
         """
 
         self.search_params = {
             'l': 'doSearch',
             'q': '',
-            'category_': 767,
+            'category_': 'All',
             'idioma_': 1,
             'bus_de_': 'All'
         }
@@ -81,7 +80,7 @@ class newpctProvider(generic.TorrentProvider):
             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':
+            if self.onlyspasearch and lang_info != 'es' and mode != 'RSS':
                 logger.log(u"Show info is not spanish, skipping provider search", logger.DEBUG)
                 continue
 
@@ -89,8 +88,8 @@ class newpctProvider(generic.TorrentProvider):
                 if mode != 'RSS':
                     logger.log(u"Search string: %s " % search_string, logger.DEBUG)
 
-                self.search_params['q'] = search_string.strip() if mode is not 'RSS' else ''
-                self.search_params['bus_de_'] = 'All' if mode is not 'RSS' else 'semana'
+                self.search_params['q'] = search_string.strip() if mode != 'RSS' else ''
+                self.search_params['bus_de_'] = 'All' if mode != 'RSS' else 'hoy'
 
                 searchURL = self.urls['search'] + '?' + urllib.parse.urlencode(self.search_params)
                 logger.log(u"Search URL: %s" % searchURL, logger.DEBUG)
@@ -221,21 +220,23 @@ class newpctProvider(generic.TorrentProvider):
         title = title[22:]
 
         # Quality - Use re module to avoid case sensitive problems with replace
-        title = re.sub('\[HDTV 1080p.*]', '1080p HDTV x264', title, flags=re.IGNORECASE)
-        title = re.sub('\[HDTV 720p.*]', '720p HDTV x264', title, flags=re.IGNORECASE)
+        title = re.sub('\[HDTV 1080p[^\[]*]', '1080p HDTV x264', title, flags=re.IGNORECASE)
+        title = re.sub('\[HDTV 720p[^\[]*]', '720p HDTV x264', title, flags=re.IGNORECASE)
+        title = re.sub('\[ALTA DEFINICION 720p[^\[]*]', '720p HDTV x264', title, flags=re.IGNORECASE)
         title = re.sub('\[HDTV]', 'HDTV x264', title, flags=re.IGNORECASE)
-        title = re.sub('\[DVD.*]', 'DVDrip x264', title, flags=re.IGNORECASE)
-        title = re.sub('\[BluRay 1080p.*]', '1080p BlueRay x264', title, flags=re.IGNORECASE)
-        title = re.sub('\[BluRay MicroHD.*]', '1080p BlueRay x264', title, flags=re.IGNORECASE)
-        title = re.sub('\[MicroHD 1080p.*]', '1080p BlueRay x264', title, flags=re.IGNORECASE)
-        title = re.sub('\[BLuRay.*]', '720p BlueRay x264', title, flags=re.IGNORECASE)
-        title = re.sub('\[BRrip.*]', '720p BlueRay x264', title, flags=re.IGNORECASE)
-        title = re.sub('\[BDrip.*]', '720p BlueRay x264', title, flags=re.IGNORECASE)
+        title = re.sub('\[DVD[^\[]*]', 'DVDrip x264', title, flags=re.IGNORECASE)
+        title = re.sub('\[BluRay 1080p[^\[]*]', '1080p BlueRay x264', title, flags=re.IGNORECASE)
+        title = re.sub('\[BluRay MicroHD[^\[]*]', '1080p BlueRay x264', title, flags=re.IGNORECASE)
+        title = re.sub('\[MicroHD 1080p[^\[]*]', '1080p BlueRay x264', title, flags=re.IGNORECASE)
+        title = re.sub('\[BLuRay[^\[]*]', '720p BlueRay x264', title, flags=re.IGNORECASE)
+        title = re.sub('\[BRrip[^\[]*]', '720p BlueRay x264', title, flags=re.IGNORECASE)
+        title = re.sub('\[BDrip[^\[]*]', '720p BlueRay x264', title, flags=re.IGNORECASE)
 
         #Language
-        title = re.sub('\[Spanish.*]', 'SPANISH AUDIO', title, flags=re.IGNORECASE)
-        title = re.sub(ur'\[Español.*]', 'SPANISH AUDIO', title, flags=re.IGNORECASE)
-        title = re.sub(u'\[AC3 5\.1 Español.*]', 'SPANISH AUDIO', title, flags=re.IGNORECASE)
+        title = re.sub('\[Spanish[^\[]*]', 'SPANISH AUDIO', title, flags=re.IGNORECASE)
+        title = re.sub('\[Castellano[^\[]*]', 'SPANISH AUDIO', title, flags=re.IGNORECASE)
+        title = re.sub(ur'\[Español[^\[]*]', 'SPANISH AUDIO', title, flags=re.IGNORECASE)
+        title = re.sub(u'\[AC3 5\.1 Español[^\[]*]', 'SPANISH AUDIO', title, flags=re.IGNORECASE)
 
         title += '-NEWPCT'
 
diff --git a/sickbeard/providers/newznab.py b/sickbeard/providers/newznab.py
index d8b26fe3f0b19992c9f79af6e12a68c1a75492b4..a250100f4ac03d750b0d67d5fb1a097d02fea98d 100644
--- a/sickbeard/providers/newznab.py
+++ b/sickbeard/providers/newznab.py
@@ -37,6 +37,8 @@ from sickbeard import db
 from sickbeard.common import Quality
 from sickbeard.providers import generic
 from sickrage.helper.encoding import ek
+from sickrage.show.Show import Show
+from sickrage.helper.common import try_int
 from sickbeard.common import USER_AGENT
 
 
@@ -63,7 +65,6 @@ class NewznabProvider(generic.NZBProvider):
         self.enable_daily = enable_daily
         self.enable_backlog = enable_backlog
 
-        self.supportsBacklog = True
 
         # 0 in the key spot indicates that no key is needed
         self.needs_auth = self.key != '0'
@@ -110,7 +111,7 @@ class NewznabProvider(generic.NZBProvider):
         if self.needs_auth and self.key:
             params['apikey'] = self.key
 
-        url = os.path.join(self.url, 'api?') +  urllib.urlencode(params)
+        url = ek(os.path.join, self.url, 'api?') +  urllib.urlencode(params)
         data = self.getURL(url)
         if not data:
             error_string = u"Error getting xml for [%s]" % url
@@ -258,7 +259,7 @@ class NewznabProvider(generic.NZBProvider):
 
         params['maxage'] = min(params['maxage'], sickbeard.USENET_RETENTION)
 
-        search_url = os.path.join(self.url, 'api?') + urllib.urlencode(params)
+        search_url = ek(os.path.join, self.url, 'api?') + urllib.urlencode(params)
         logger.log(u"Search url: %s" % search_url, logger.DEBUG)
         data = self.getURL(search_url)
         if not data:
@@ -286,11 +287,11 @@ class NewznabProvider(generic.NZBProvider):
                 continue
 
             seeders = leechers = None
-            size = helpers.tryInt(item.size, -1)
+            size = try_int(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
+                size = try_int(attr['value'], -1) if attr['name'] == 'size' else size
+                seeders = try_int(attr['value'], 1) if attr['name'] == 'seeders' else seeders
+                leechers = try_int(attr['value'], 0) if attr['name'] == 'peers' else leechers
 
             if not size or (torznab and (seeders is None or leechers is None)):
                 continue
@@ -305,14 +306,12 @@ class NewznabProvider(generic.NZBProvider):
 
         return results
 
-
     def _get_size(self, item):
         """
         Gets size info from a result item
         Returns int size or -1
         """
-        return helpers.tryInt(item.get('size', -1), -1)
-
+        return try_int(item.get('size', -1), -1)
 
     def findPropers(self, search_date=datetime.datetime.today()):
         """
@@ -334,7 +333,7 @@ class NewznabProvider(generic.NZBProvider):
             return results
 
         for sqlshow in sqlResults:
-            self.show = helpers.findCertainShow(sickbeard.showList, int(sqlshow["showid"]))
+            self.show = Show.find(sickbeard.showList, int(sqlshow["showid"]))
             if self.show:
                 curEp = self.show.getEpisode(int(sqlshow["season"]), int(sqlshow["episode"]))
                 searchStrings = self._get_episode_search_strings(curEp, add_string='PROPER|REPACK')
diff --git a/sickbeard/providers/nextgen.py b/sickbeard/providers/nextgen.py
index 5915ff6a5f999b815dc9bd88e35bb92caa5850b7..172af632dbd7f6b7c752a48ac8869f008304f65b 100644
--- a/sickbeard/providers/nextgen.py
+++ b/sickbeard/providers/nextgen.py
@@ -32,7 +32,6 @@ class NextGenProvider(generic.TorrentProvider):
 
         generic.TorrentProvider.__init__(self, "NextGen")
 
-        self.supportsBacklog = True
 
 
         self.username = None
diff --git a/sickbeard/providers/nyaatorrents.py b/sickbeard/providers/nyaatorrents.py
index 1b58081c1697783adab1a487496cec72168a57ac..75943032c72c0aade3e564aaf48b7ce01ca7f6d3 100644
--- a/sickbeard/providers/nyaatorrents.py
+++ b/sickbeard/providers/nyaatorrents.py
@@ -29,7 +29,6 @@ class NyaaProvider(generic.TorrentProvider):
 
         generic.TorrentProvider.__init__(self, "NyaaTorrents")
 
-        self.supportsBacklog = True
         self.public = True
         self.supportsAbsoluteNumbering = True
         self.anime_only = True
diff --git a/sickbeard/providers/omgwtfnzbs.py b/sickbeard/providers/omgwtfnzbs.py
index 775061259bcb461e15cb85774571944815fc0d3b..edf5b00dc91a60e34ae252568491e1de557e33fc 100644
--- a/sickbeard/providers/omgwtfnzbs.py
+++ b/sickbeard/providers/omgwtfnzbs.py
@@ -25,6 +25,7 @@ from sickbeard import classes
 from sickbeard import logger
 from sickbeard import show_name_helpers
 from sickbeard.providers import generic
+from sickrage.helper.common import try_int
 
 
 class OmgwtfnzbsProvider(generic.NZBProvider):
@@ -38,7 +39,6 @@ class OmgwtfnzbsProvider(generic.NZBProvider):
         self.urls = {'base_url': 'https://omgwtfnzbs.org/'}
         self.url = self.urls['base_url']
 
-        self.supportsBacklog = True
 
     def _checkAuth(self):
 
@@ -83,12 +83,7 @@ class OmgwtfnzbsProvider(generic.NZBProvider):
         return (item['release'], item['getnzb'])
 
     def _get_size(self, item):
-        try:
-            size = int(item['sizebytes'])
-        except (ValueError, TypeError, AttributeError, KeyError):
-            return -1
-
-        return size
+        return try_int(item['sizebytes'], -1)
 
     def _doSearch(self, search, search_mode='eponly', epcount=0, retention=0, epObj=None):
 
diff --git a/sickbeard/providers/pretome.py b/sickbeard/providers/pretome.py
index 30dbac2d2efaa2d74f989ae7ab462f28c16315f0..4164e1826f1e5b9ebda4db91651798ef10e4fedb 100644
--- a/sickbeard/providers/pretome.py
+++ b/sickbeard/providers/pretome.py
@@ -32,7 +32,6 @@ class PretomeProvider(generic.TorrentProvider):
 
         generic.TorrentProvider.__init__(self, "Pretome")
 
-        self.supportsBacklog = True
 
         self.username = None
         self.password = None
diff --git a/sickbeard/providers/rarbg.py b/sickbeard/providers/rarbg.py
index f2b2bb362f26604ff8559eacfa3625d7b8456ee9..6227b7362bd1b85f29d298bf757870a81e7819fd 100644
--- a/sickbeard/providers/rarbg.py
+++ b/sickbeard/providers/rarbg.py
@@ -39,7 +39,6 @@ class RarbgProvider(generic.TorrentProvider):
     def __init__(self):
         generic.TorrentProvider.__init__(self, "Rarbg")
 
-        self.supportsBacklog = True
         self.public = True
         self.ratio = None
         self.minseed = None
diff --git a/sickbeard/providers/rsstorrent.py b/sickbeard/providers/rsstorrent.py
index 434174d3654945b27a5c5e8f8acca359e1470317..deb0d2e681e808eb841cdf977d4552cef50dd2b0 100644
--- a/sickbeard/providers/rsstorrent.py
+++ b/sickbeard/providers/rsstorrent.py
@@ -65,7 +65,7 @@ class TorrentRssProvider(generic.TorrentProvider):
         )
 
     def imageName(self):
-        if os.path.isfile(ek(os.path.join, sickbeard.PROG_DIR, 'gui', sickbeard.GUI_NAME, 'images', 'providers', self.getID() + '.png')):
+        if ek(os.path.isfile, ek(os.path.join, sickbeard.PROG_DIR, 'gui', sickbeard.GUI_NAME, 'images', 'providers', self.getID() + '.png')):
             return self.getID() + '.png'
         return 'torrentrss.png'
 
diff --git a/sickbeard/providers/scc.py b/sickbeard/providers/scc.py
index c02ac02d5a63bd6fb2dc7b404fbf5d7525129953..7fcfa6138696c60dbe19019ee94d182e0f292d5e 100644
--- a/sickbeard/providers/scc.py
+++ b/sickbeard/providers/scc.py
@@ -34,7 +34,6 @@ class SCCProvider(generic.TorrentProvider):
 
         generic.TorrentProvider.__init__(self, "SceneAccess")
 
-        self.supportsBacklog = True
 
 
         self.username = None
diff --git a/sickbeard/providers/scenetime.py b/sickbeard/providers/scenetime.py
index c4136a07c4fc8a385fce958b5a2b1dc36e35ceda..139e8873ea9a0d5d7eb7f2250868bd1a88c29344 100644
--- a/sickbeard/providers/scenetime.py
+++ b/sickbeard/providers/scenetime.py
@@ -32,7 +32,6 @@ class SceneTimeProvider(generic.TorrentProvider):
 
         generic.TorrentProvider.__init__(self, "SceneTime")
 
-        self.supportsBacklog = True
 
 
         self.username = None
diff --git a/sickbeard/providers/speedcd.py b/sickbeard/providers/speedcd.py
index b5fe4feab1f0d11e383934e3c1e9f06172bb4cbb..21498130ad4b6fa305f2f2522a5fa014e73b7d36 100644
--- a/sickbeard/providers/speedcd.py
+++ b/sickbeard/providers/speedcd.py
@@ -29,7 +29,6 @@ class SpeedCDProvider(generic.TorrentProvider):
 
         generic.TorrentProvider.__init__(self, "Speedcd")
 
-        self.supportsBacklog = True
 
         self.username = None
         self.password = None
diff --git a/sickbeard/providers/strike.py b/sickbeard/providers/strike.py
index a761eb5b893df139042b0afce2c2c95a6f9e05f1..478746cf5599a199869054f7967ff2d384f21f5d 100644
--- a/sickbeard/providers/strike.py
+++ b/sickbeard/providers/strike.py
@@ -26,7 +26,6 @@ class STRIKEProvider(generic.TorrentProvider):
     def __init__(self):
         generic.TorrentProvider.__init__(self, "Strike")
 
-        self.supportsBacklog = True
         self.public = True
         self.url = 'https://getstrike.net/'
         self.ratio = 0
diff --git a/sickbeard/providers/t411.py b/sickbeard/providers/t411.py
index 55375b41a8fff87e4a3c5844b70ed59a12af272b..9c45f29275d416126c784025c527b4ce3307c7de 100644
--- a/sickbeard/providers/t411.py
+++ b/sickbeard/providers/t411.py
@@ -30,7 +30,6 @@ class T411Provider(generic.TorrentProvider):
     def __init__(self):
         generic.TorrentProvider.__init__(self, "T411")
 
-        self.supportsBacklog = True
 
         self.username = None
         self.password = None
diff --git a/sickbeard/providers/thepiratebay.py b/sickbeard/providers/thepiratebay.py
index 159a375cef615d116630c7d9a535c4035b50e98d..c0cdcf9fb3640ee68b341ea3bc6cd643a3a04690 100644
--- a/sickbeard/providers/thepiratebay.py
+++ b/sickbeard/providers/thepiratebay.py
@@ -31,7 +31,6 @@ class ThePirateBayProvider(generic.TorrentProvider):
 
         generic.TorrentProvider.__init__(self, "ThePirateBay")
 
-        self.supportsBacklog = True
         self.public = True
 
         self.ratio = None
diff --git a/sickbeard/providers/titansoftv.py b/sickbeard/providers/titansoftv.py
index cea172b8bd6cd41ed9a5afab296725cd490a8e14..7f77f7c88ed438ad44943e30a10c3af245422249 100644
--- a/sickbeard/providers/titansoftv.py
+++ b/sickbeard/providers/titansoftv.py
@@ -29,7 +29,6 @@ from sickrage.helper.exceptions import AuthException
 class TitansOfTVProvider(generic.TorrentProvider):
     def __init__(self):
         generic.TorrentProvider.__init__(self, 'TitansOfTV')
-        self.supportsBacklog = True
 
         self.supportsAbsoluteNumbering = True
         self.api_key = None
diff --git a/sickbeard/providers/tntvillage.py b/sickbeard/providers/tntvillage.py
index c00842148d894428c82c6f309e7afa44dc9d37f7..047aa36ac09f14748c06d92469e73df6c751d711 100644
--- a/sickbeard/providers/tntvillage.py
+++ b/sickbeard/providers/tntvillage.py
@@ -60,7 +60,6 @@ class TNTVillageProvider(generic.TorrentProvider):
     def __init__(self):
         generic.TorrentProvider.__init__(self, "TNTVillage")
 
-        self.supportsBacklog = True
 
         self._uid = None
         self._hash = None
diff --git a/sickbeard/providers/tokyotoshokan.py b/sickbeard/providers/tokyotoshokan.py
index 6a078faa8de2c8459c887366350a89d79b1d4992..17f6cd66505889cf3a88dfc5877e5e4225d773f1 100644
--- a/sickbeard/providers/tokyotoshokan.py
+++ b/sickbeard/providers/tokyotoshokan.py
@@ -31,7 +31,6 @@ class TokyoToshokanProvider(generic.TorrentProvider):
 
         generic.TorrentProvider.__init__(self, "TokyoToshokan")
 
-        self.supportsBacklog = True
         self.public = True
         self.supportsAbsoluteNumbering = True
         self.anime_only = True
diff --git a/sickbeard/providers/torrentbytes.py b/sickbeard/providers/torrentbytes.py
index 0a0d3a68a453a52169ef4ee46912320b052ea94d..0f32e03d32870443fe998e12139ab368471f2c89 100644
--- a/sickbeard/providers/torrentbytes.py
+++ b/sickbeard/providers/torrentbytes.py
@@ -32,7 +32,6 @@ class TorrentBytesProvider(generic.TorrentProvider):
 
         generic.TorrentProvider.__init__(self, "TorrentBytes")
 
-        self.supportsBacklog = True
 
         self.username = None
         self.password = None
diff --git a/sickbeard/providers/torrentday.py b/sickbeard/providers/torrentday.py
index 62550dab1691fff5d1d277d98ce36e2beb7ced46..ea1b67e6b56555a623241814a89551c369867f9e 100644
--- a/sickbeard/providers/torrentday.py
+++ b/sickbeard/providers/torrentday.py
@@ -27,7 +27,6 @@ class TorrentDayProvider(generic.TorrentProvider):
 
         generic.TorrentProvider.__init__(self, "TorrentDay")
 
-        self.supportsBacklog = True
 
 
         self._uid = None
diff --git a/sickbeard/providers/torrentleech.py b/sickbeard/providers/torrentleech.py
index 7ccac63bb57895d8fd88f7fac4a85e164b373c66..a969138c694c4e66fbe65446a21db0be6db1a91f 100644
--- a/sickbeard/providers/torrentleech.py
+++ b/sickbeard/providers/torrentleech.py
@@ -32,7 +32,6 @@ class TorrentLeechProvider(generic.TorrentProvider):
 
         generic.TorrentProvider.__init__(self, "TorrentLeech")
 
-        self.supportsBacklog = True
 
         self.username = None
         self.password = None
diff --git a/sickbeard/providers/torrentproject.py b/sickbeard/providers/torrentproject.py
index 2f84b81c8309da3ba1b787babf7bfc507026e772..b7df83d7e62b533561d7f71044ba2e62584b50d1 100644
--- a/sickbeard/providers/torrentproject.py
+++ b/sickbeard/providers/torrentproject.py
@@ -20,17 +20,15 @@ from urllib import quote_plus
 
 from sickbeard import logger
 from sickbeard import tvcache
-from sickbeard import helpers
 from sickbeard.providers import generic
 from sickbeard.common import USER_AGENT
+from sickrage.helper.common import try_int
 
 
 class TORRENTPROJECTProvider(generic.TorrentProvider):
-
     def __init__(self):
         generic.TorrentProvider.__init__(self, "TorrentProject")
 
-        self.supportsBacklog = True
         self.public = True
         self.ratio = 0
         self.urls = {'api': u'https://torrentproject.se/',}
@@ -65,8 +63,8 @@ class TORRENTPROJECTProvider(generic.TorrentProvider):
                 results = []
                 for i in torrents:
                     title = torrents[i]["title"]
-                    seeders = helpers.tryInt(torrents[i]["seeds"], 1)
-                    leechers = helpers.tryInt(torrents[i]["leechs"], 0)
+                    seeders = try_int(torrents[i]["seeds"], 1)
+                    leechers = try_int(torrents[i]["leechs"], 0)
                     if seeders < self.minseed or leechers < self.minleech:
                         if mode != 'RSS':
                             logger.log(u"Torrent doesn't meet minimum seeds & leechers not selecting : %s" % title, logger.DEBUG)
diff --git a/sickbeard/providers/torrentz.py b/sickbeard/providers/torrentz.py
index 576a8ca114fea8a6aef3a229e2cb20761e4d8e2a..6282d965c60ade8d7670ea2cfc92d000d436de80 100644
--- a/sickbeard/providers/torrentz.py
+++ b/sickbeard/providers/torrentz.py
@@ -37,7 +37,6 @@ class TORRENTZProvider(generic.TorrentProvider):
 
         generic.TorrentProvider.__init__(self, "Torrentz")
         self.public = True
-        self.supportsBacklog = True
         self.confirmed = True
         self.ratio = None
         self.minseed = None
diff --git a/sickbeard/providers/transmitthenet.py b/sickbeard/providers/transmitthenet.py
index 2049b638ddb4b600d2425a1e32268f8db2d1c2c4..bd534091e190ddb2271122308758200bdb8bc629 100644
--- a/sickbeard/providers/transmitthenet.py
+++ b/sickbeard/providers/transmitthenet.py
@@ -22,7 +22,7 @@ from sickbeard import tvcache
 from sickbeard.bs4_parser import BS4Parser
 from sickbeard.providers import generic
 from sickrage.helper.exceptions import AuthException
-
+from sickrage.helper.common import try_int
 
 class TransmitTheNetProvider(generic.TorrentProvider):
     def __init__(self):
@@ -31,27 +31,21 @@ class TransmitTheNetProvider(generic.TorrentProvider):
 
         self.urls = {
             'base_url': 'https://transmithe.net/',
-            'index': 'https://transmithe.net/index.php',
+            'login': 'https://transmithe.net/login.php',
+            'search': 'https://transmithe.net/torrents.php',
         }
 
         self.url = self.urls['base_url']
 
-        self.supportsBacklog = True
-
         self.username = None
         self.password = None
         self.ratio = None
         self.minseed = None
         self.minleech = None
+        self.freeleech = None
 
         self.cache = TransmitTheNetCache(self)
 
-        self.search_params = {
-            "page": 'torrents',
-            "category": 0,
-            "active": 1
-        }
-
     def _checkAuth(self):
 
         if not self.username or not self.password:
@@ -62,13 +56,13 @@ class TransmitTheNetProvider(generic.TorrentProvider):
     def _doLogin(self):
 
         login_params = {
-            'uid': self.username,
-            'pwd': self.password,
-            'remember_me': 'on',
-            'login': 'submit'
+            'username': self.username,
+            'password': self.password,
+            'keeplogged': 'on',
+            'login': 'Login'
         }
 
-        response = self.getURL(self.urls['index'], params={'page': 'login'}, post_data=login_params, timeout=30)
+        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
@@ -93,40 +87,48 @@ class TransmitTheNetProvider(generic.TorrentProvider):
                 if mode != 'RSS':
                     logger.log(u"Search string: %s " % search_string, logger.DEBUG)
 
-                data = self.getURL(self.urls['index'], params=self.search_params)
-                searchURL = self.urls['index'] + "?" + urlencode(self.search_params)
+                search_params = {
+                    'searchtext': search_string,
+                    'filter_freeleech': (0, 1)[self.freeleech is True],
+                    'order_by': ('seeders', 'time')[mode == 'RSS'],
+                    "order_way": "desc"
+                }
+
+                if not search_string:
+                    del search_params['searchtext']
+
+                searchURL = self.urls['search'] + "?" + urlencode(search_params)
                 logger.log(u"Search URL: %s" %  searchURL, logger.DEBUG)
 
+                data = self.getURL(self.urls['search'], params=search_params)
                 if not data:
                     logger.log(u"No data returned from provider", logger.DEBUG)
                     continue
 
                 try:
-                    with BS4Parser(data) as html:
-
-                        torrent_rows = []
-
-                        down_elems = html.findAll("img", {"alt": "Download Torrent"})
-                        for down_elem in down_elems:
-                            if down_elem:
-                                torr_row = down_elem.findParent('tr')
-                                if torr_row:
-                                    torrent_rows.append(torr_row)
-
+                    with BS4Parser(data, features=["html5lib", "permissive"]) as html:
+                        torrent_table = html.find('table', {'id':'torrent_table'})
+                        torrent_rows = torrent_table.findAll('tr', {'class':'torrent'})
                         # Continue only if one Release is found
-                        if len(torrent_rows) < 1:
-                            logger.log(u"Data returned from provider does not contain any torrents", logger.DEBUG)
+                        if not torrent_rows:
+                            logger.log(u"Data returned from %s does not contain any torrents" % self.name, logger.DEBUG)
                             continue
 
                         for torrent_row in torrent_rows:
+                            freeleech = torrent_row.find('img', alt="Freeleech") is not None
+                            if self.freeleech and not freeleech:
+                                continue
+
+                            download_url = self.urls['base_url'] + torrent_row.find('a', {"title": 'Download Torrent'})['href']
+
+                            temp_anchor = torrent_row.find('a', {"data-src": True})
+                            title = temp_anchor['data-src'].rsplit('.', 1)[0]
+                            size = try_int(temp_anchor['data-filesize'])
+
+                            temp_anchor = torrent_row.find('span', class_='time').parent.find_next_sibling()
+                            seeders = try_int(temp_anchor.text.strip())
+                            leechers = try_int(temp_anchor.find_next_sibling().text.strip())
 
-                            title = torrent_row.find('a', {"data-src": True})['data-src'].rsplit('.', 1)[0]
-                            download_href = torrent_row.find('img', {"alt": 'Download Torrent'}).findParent()['href']
-                            seeders = int(torrent_row.findAll('a', {'title': 'Click here to view peers details'})[0].text.strip())
-                            leechers = int(torrent_row.findAll('a', {'title': 'Click here to view peers details'})[1].text.strip())
-                            download_url = self.urls['base_url'] + download_href
-                            # FIXME
-                            size = -1
 
                             if not all([title, download_url]):
                                 continue
diff --git a/sickbeard/providers/tvchaosuk.py b/sickbeard/providers/tvchaosuk.py
index dee2176c41791fff58c385a2e5429c28f4b228b4..fac4c38c23a3f2d83ac43cd7a35a055896afb8ba 100644
--- a/sickbeard/providers/tvchaosuk.py
+++ b/sickbeard/providers/tvchaosuk.py
@@ -37,7 +37,6 @@ class TVChaosUKProvider(generic.TorrentProvider):
 
         self.url = self.urls['base_url']
 
-        self.supportsBacklog = True
 
         self.username = None
         self.password = None
diff --git a/sickbeard/providers/womble.py b/sickbeard/providers/womble.py
index 136d649fc061ebb986ef2ad728c054bf5ed9772f..7a3d1ef902359dd7df7341d1505b617f05efc8f0 100644
--- a/sickbeard/providers/womble.py
+++ b/sickbeard/providers/womble.py
@@ -29,7 +29,7 @@ class WombleProvider(generic.NZBProvider):
         self.cache = WombleCache(self)
         self.urls = {'base_url': 'http://newshost.co.za/'}
         self.url = self.urls['base_url']
-
+        self.supportsBacklog = False
 
 class WombleCache(tvcache.TVCache):
     def __init__(self, provider_obj):
diff --git a/sickbeard/providers/xthor.py b/sickbeard/providers/xthor.py
index 2805fbe5b6270d4f4b9cf6ea7511fab4600510a4..bc40a10e342f5d4303451ced417115c1af80ddb9 100644
--- a/sickbeard/providers/xthor.py
+++ b/sickbeard/providers/xthor.py
@@ -33,7 +33,6 @@ class XthorProvider(generic.TorrentProvider):
 
         generic.TorrentProvider.__init__(self, "Xthor")
 
-        self.supportsBacklog = True
 
         self.cj = cookielib.CookieJar()
 
diff --git a/sickbeard/scene_numbering.py b/sickbeard/scene_numbering.py
index d4bb071d542420c676dcd2580a7dfd04b1b50f4a..23ba5f06c53477973a852cad0cc4817f54039453 100644
--- a/sickbeard/scene_numbering.py
+++ b/sickbeard/scene_numbering.py
@@ -29,8 +29,8 @@ import traceback
 import sickbeard
 from sickbeard import logger
 from sickbeard import db
-from sickbeard import helpers
 from sickrage.helper.exceptions import ex
+from sickrage.show.Show import Show
 
 
 def get_scene_numbering(indexer_id, indexer, season, episode, fallback_to_xem=True):
@@ -49,7 +49,7 @@ def get_scene_numbering(indexer_id, indexer, season, episode, fallback_to_xem=Tr
     if indexer_id is None or season is None or episode is None:
         return (season, episode)
 
-    showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(indexer_id))
+    showObj = Show.find(sickbeard.showList, int(indexer_id))
     if showObj and not showObj.is_scene:
         return (season, episode)
 
@@ -101,7 +101,7 @@ def get_scene_absolute_numbering(indexer_id, indexer, absolute_number, fallback_
     indexer_id = int(indexer_id)
     indexer = int(indexer)
 
-    showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, indexer_id)
+    showObj = Show.find(sickbeard.showList, indexer_id)
     if showObj and not showObj.is_scene:
         return absolute_number
 
@@ -219,7 +219,7 @@ def set_scene_numbering(indexer_id, indexer, season=None, episode=None, absolute
             [sceneAbsolute, indexer, indexer_id, absolute_number])
 
     # Reload data from DB so that cache and db are in sync
-    show = helpers.findCertainShow(sickbeard.showList, indexer_id)
+    show = Show.find(sickbeard.showList, indexer_id)
     show.flushEpisodes()
 
 
diff --git a/sickbeard/show_queue.py b/sickbeard/show_queue.py
index 9cec79e7257cbdbd4844e319cfb7ffba7faf8995..457b6f8ed5da22b3de640cbbcbcb3af2defa75b6 100644
--- a/sickbeard/show_queue.py
+++ b/sickbeard/show_queue.py
@@ -133,7 +133,7 @@ class ShowQueue(generic_queue.GenericQueue):
 
         return queueItemObj
 
-    def downloadSubtitles(self, show, force=False):
+    def download_subtitles(self, show, force=False):
 
         queueItemObj = QueueItemSubtitle(show)
 
@@ -560,7 +560,7 @@ class QueueItemSubtitle(ShowQueueItem):
 
         logger.log(u"Downloading subtitles for " + self.show.name)
 
-        self.show.downloadSubtitles()
+        self.show.download_subtitles()
         self.finish()
 
 class QueueItemUpdate(ShowQueueItem):
diff --git a/sickbeard/subtitles.py b/sickbeard/subtitles.py
index 402e6a61683c88b1faa4e771fba34ccbe26c9812..b862646d75683cfb7934f954b633cfc0ae987d54 100644
--- a/sickbeard/subtitles.py
+++ b/sickbeard/subtitles.py
@@ -1,5 +1,7 @@
-# Author: Nyaran <nyayukko@gmail.com>, based on Antoine Bertin <diaoulael@gmail.com> work
-# URL: http://code.google.com/p/sickbeard/
+# Author: medariox <dariox@gmx.com>,
+# based on Antoine Bertin's <diaoulael@gmail.com> work
+# and originally written by Nyaran <nyayukko@gmail.com>
+# URL: https://github.com/SickRage/SickRage/
 #
 # This file is part of SickRage.
 #
@@ -16,7 +18,6 @@
 # You should have received a copy of the GNU General Public License
 # along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
 
-import io
 import os
 import re
 import datetime
@@ -25,9 +26,8 @@ import subliminal
 import subprocess
 import pkg_resources
 import sickbeard
-from enzyme import MKV, MalformedMKVError
 from subliminal.api import provider_manager
-from babelfish import Error as BabelfishError, Language, language_converters
+from babelfish import Language, language_converters
 from sickbeard import logger
 from sickbeard import history
 from sickbeard import db
@@ -35,11 +35,12 @@ from sickbeard import processTV
 from sickrage.helper.common import media_extensions, dateTimeFormat
 from sickrage.helper.encoding import ek
 from sickrage.helper.exceptions import ex
+from sickrage.show.Show import Show
 
-distribution = pkg_resources.Distribution(location=os.path.dirname(os.path.dirname(__file__)),
+DISTRIBUTION = pkg_resources.Distribution(location=os.path.dirname(os.path.dirname(__file__)),
                                           project_name='fake_entry_points', version='1.0.0')
 
-entry_points = {
+ENTRY_POINTS = {
     'subliminal.providers': [
         'addic7ed = subliminal.providers.addic7ed:Addic7edProvider',
         'legendastv = subliminal.providers.legendastv:LegendasTvProvider',
@@ -59,14 +60,14 @@ entry_points = {
 
 # pylint: disable=W0212
 # Access to a protected member of a client class
-distribution._ep_map = pkg_resources.EntryPoint.parse_map(entry_points, distribution)
-pkg_resources.working_set.add(distribution)
+DISTRIBUTION._ep_map = pkg_resources.EntryPoint.parse_map(ENTRY_POINTS, DISTRIBUTION)
+pkg_resources.working_set.add(DISTRIBUTION)
 
 provider_manager.ENTRY_POINT_CACHE.pop('subliminal.providers')
 
 subliminal.region.configure('dogpile.cache.memory')
 
-provider_urls = {
+PROVIDER_URLS = {
     'addic7ed': 'http://www.addic7ed.com',
     'legendastv': 'http://www.legendas.tv',
     'napiprojekt': 'http://www.napiprojekt.pl',
@@ -77,154 +78,195 @@ provider_urls = {
 }
 
 
-def sortedServiceList():
-    newList = []
+def sorted_service_list():
+    new_list = []
     lmgtfy = 'http://lmgtfy.com/?q=%s'
 
-    curIndex = 0
-    for curService in sickbeard.SUBTITLES_SERVICES_LIST:
-        if curService in subliminal.provider_manager.names():
-            newList.append({'name': curService,
-                            'url': provider_urls[curService] if curService in provider_urls else lmgtfy % curService,
-                            'image': curService + '.png',
-                            'enabled': sickbeard.SUBTITLES_SERVICES_ENABLED[curIndex] == 1
-                           })
-        curIndex += 1
-
-    for curService in subliminal.provider_manager.names():
-        if curService not in [x['name'] for x in newList]:
-            newList.append({'name': curService,
-                            'url': provider_urls[curService] if curService in provider_urls else lmgtfy % curService,
-                            'image': curService + '.png',
-                            'enabled': False,
-                           })
-
-    return newList
-
-def getEnabledServiceList():
-    return [x['name'] for x in sortedServiceList() if x['enabled']]
+    current_index = 0
+    for current_service in sickbeard.SUBTITLES_SERVICES_LIST:
+        if current_service in subliminal.provider_manager.names():
+            new_list.append({'name': current_service,
+                             'url': PROVIDER_URLS[current_service] if current_service in PROVIDER_URLS else
+                                    lmgtfy % current_service,
+                             'image': current_service + '.png',
+                             'enabled': sickbeard.SUBTITLES_SERVICES_ENABLED[current_index] == 1
+                            })
+        current_index += 1
+
+    for current_service in subliminal.provider_manager.names():
+        if current_service not in [service['name'] for service in new_list]:
+            new_list.append({'name': current_service,
+                             'url': PROVIDER_URLS[current_service] if current_service in PROVIDER_URLS else
+                                    lmgtfy % current_service,
+                             'image': current_service + '.png',
+                             'enabled': False,
+                            })
+
+    return new_list
+
+
+def enabled_service_list():
+    return [service['name'] for service in sorted_service_list() if service['enabled']]
+
+
+def wanted_languages(sql_like=None):
+    wanted = frozenset(sickbeard.SUBTITLES_LANGUAGES).intersection(subtitle_code_filter())
+    return (wanted, '%' + ','.join(wanted) + '%')[bool(sql_like)]
+
+
+def get_needed_languages(subtitles):
+    return {from_code(language) for language in wanted_languages().difference(subtitles)}
+
+
+def subtitle_code_filter():
+    return {code for code in language_converters['opensubtitles'].codes if len(code) == 3}
+
+
+def needs_subtitles(subtitles):
+    if isinstance(subtitles, basestring):
+        subtitles = {subtitle.strip() for subtitle in subtitles.split(',')}
+
+    if sickbeard.SUBTITLES_MULTI:
+        return len(wanted_languages().difference(subtitles)) > 0
+    else:
+        return len(subtitles) == 0
+
 
 # Hack around this for now.
-def fromietf(language):
-    return Language.fromopensubtitles(language)
+def from_code(language):
+    language = language.strip()
+    if language not in language_converters['opensubtitles'].codes:
+        return Language('und')
 
-def isValidLanguage(language):
-    try:
-        fromietf(language)
-    except Exception:
-        return False
-    return True
+    return Language.fromopensubtitles(language)  # pylint: disable=no-member
 
-def getLanguageName(language):
-    return fromietf(language).name
 
-def downloadSubtitles(subtitles_info):
+def name_from_code(code):
+    return from_code(code).name
+
+
+def code_from_code(code):
+    return from_code(code).opensubtitles
+
+
+def download_subtitles(subtitles_info):
     existing_subtitles = subtitles_info['subtitles']
-    # First of all, check if we need subtitles
-    languages = getNeededLanguages(existing_subtitles)
+
+    if not needs_subtitles(existing_subtitles):
+        logger.log(u'Episode already has all needed subtitles, skipping  episode %dx%d of show %s'
+                   % (subtitles_info['season'], subtitles_info['episode'], subtitles_info['show_name']), logger.DEBUG)
+        return (existing_subtitles, None)
+
+    # Check if we really need subtitles
+    languages = get_needed_languages(existing_subtitles)
     if not languages:
-        logger.log(u'%s: No missing subtitles for S%02dE%02d' % (subtitles_info['show.indexerid'], subtitles_info['season'], subtitles_info['episode']), logger.DEBUG)
+        logger.log(u'No subtitles needed for %s S%02dE%02d'
+                   % (subtitles_info['show_name'], subtitles_info['season'],
+                      subtitles_info['episode']), logger.DEBUG)
         return (existing_subtitles, None)
 
-    subtitles_path = getSubtitlesPath(subtitles_info['location']).encode(sickbeard.SYS_ENCODING)
+    subtitles_path = get_subtitles_path(subtitles_info['location']).encode(sickbeard.SYS_ENCODING)
     video_path = subtitles_info['location'].encode(sickbeard.SYS_ENCODING)
-    providers = getEnabledServiceList()
 
-    try:
-        video = subliminal.scan_video(video_path, subtitles=False, embedded_subtitles=False)
-    except Exception:
-        logger.log(u'%s: Exception caught in subliminal.scan_video for S%02dE%02d' %
-                   (subtitles_info['show.indexerid'], subtitles_info['season'], subtitles_info['episode']), logger.DEBUG)
+    video = get_video(video_path, subtitles_path=subtitles_path)
+    if not video:
+        logger.log(u'Exception caught in subliminal.scan_video for %s S%02dE%02d'
+                   % (subtitles_info['show_name'], subtitles_info['season'],
+                      subtitles_info['episode']), logger.DEBUG)
         return (existing_subtitles, None)
 
-    provider_configs = {'addic7ed': {'username': sickbeard.ADDIC7ED_USER, 'password': sickbeard.ADDIC7ED_PASS},
-                        'legendastv': {'username': sickbeard.LEGENDASTV_USER, 'password': sickbeard.LEGENDASTV_PASS},
-                        'opensubtitles': {'username': sickbeard.OPENSUBTITLES_USER, 'password': sickbeard.OPENSUBTITLES_PASS}}
+    providers = enabled_service_list()
+    provider_configs = {'addic7ed': {'username': sickbeard.ADDIC7ED_USER,
+                                     'password': sickbeard.ADDIC7ED_PASS},
+                        'legendastv': {'username': sickbeard.LEGENDASTV_USER,
+                                       'password': sickbeard.LEGENDASTV_PASS},
+                        'opensubtitles': {'username': sickbeard.OPENSUBTITLES_USER,
+                                          'password': sickbeard.OPENSUBTITLES_PASS}}
 
     pool = subliminal.api.ProviderPool(providers=providers, provider_configs=provider_configs)
 
     try:
         subtitles_list = pool.list_subtitles(video, languages)
         if not subtitles_list:
-            logger.log(u'%s: No subtitles found for S%02dE%02d on any provider' % (subtitles_info['show.indexerid'], subtitles_info['season'], subtitles_info['episode']), logger.DEBUG)
+            logger.log(u'No subtitles found for %s S%02dE%02d on any provider'
+                       % (subtitles_info['show_name'], subtitles_info['season'],
+                          subtitles_info['episode']), logger.DEBUG)
             return (existing_subtitles, None)
 
-        found_subtitles = pool.download_best_subtitles(subtitles_list, video, languages=languages, hearing_impaired=sickbeard.SUBTITLES_HEARING_IMPAIRED, only_one=not sickbeard.SUBTITLES_MULTI)
-
-        save_subtitles(video, found_subtitles, directory=subtitles_path, single=not sickbeard.SUBTITLES_MULTI)
+        found_subtitles = pool.download_best_subtitles(subtitles_list, video, languages=languages,
+                                                       hearing_impaired=sickbeard.SUBTITLES_HEARING_IMPAIRED,
+                                                       only_one=not sickbeard.SUBTITLES_MULTI)
 
-        if not sickbeard.EMBEDDED_SUBTITLES_ALL and sickbeard.SUBTITLES_EXTRA_SCRIPTS and video_path.rsplit(".", 1)[1] in media_extensions:
-            run_subs_extra_scripts(subtitles_info, found_subtitles, video, single=not sickbeard.SUBTITLES_MULTI)
-
-        current_subtitles = subtitlesLanguages(video_path)[0]
-        new_subtitles = frozenset(current_subtitles).difference(existing_subtitles)
+        subliminal.save_subtitles(video, found_subtitles, directory=subtitles_path,
+                                  single=not sickbeard.SUBTITLES_MULTI)
 
     except Exception:
         logger.log(u"Error occurred when downloading subtitles for: %s" % video_path)
         logger.log(traceback.format_exc(), logger.ERROR)
         return (existing_subtitles, None)
 
+    for subtitle in found_subtitles:
+        subtitle_path = subliminal.subtitle.get_subtitle_path(video.name,
+                                                              None if not sickbeard.SUBTITLES_MULTI else
+                                                              subtitle.language)
+        if subtitles_path is not None:
+            subtitle_path = ek(os.path.join, subtitles_path, ek(os.path.split, subtitle_path)[1])
+        sickbeard.helpers.chmodAsParent(subtitle_path)
+        sickbeard.helpers.fixSetGroupID(subtitle_path)
+
+    if (not sickbeard.EMBEDDED_SUBTITLES_ALL and sickbeard.SUBTITLES_EXTRA_SCRIPTS and
+            video_path.rsplit(".", 1)[1] in media_extensions):
+        run_subs_extra_scripts(subtitles_info, found_subtitles, video, single=not sickbeard.SUBTITLES_MULTI)
+
+    current_subtitles = [subtitle.language.opensubtitles for subtitle in found_subtitles]
+    new_subtitles = frozenset(current_subtitles).difference(existing_subtitles)
+    current_subtitles += existing_subtitles
+
     if sickbeard.SUBTITLES_HISTORY:
         for subtitle in found_subtitles:
-            logger.log(u'history.logSubtitle %s, %s' % (subtitle.provider_name, subtitle.language.opensubtitles), logger.DEBUG)
-            history.logSubtitle(subtitles_info['show.indexerid'], subtitles_info['season'], subtitles_info['episode'], subtitles_info['status'], subtitle)
+            logger.log(u'history.logSubtitle %s, %s' %
+                       (subtitle.provider_name, subtitle.language.opensubtitles), logger.DEBUG)
 
-    return (current_subtitles, new_subtitles)
+            history.logSubtitle(subtitles_info['show_indexerid'], subtitles_info['season'],
+                                subtitles_info['episode'], subtitles_info['status'], subtitle)
 
-def save_subtitles(video, subtitles, single=False, directory=None):
-    saved_subtitles = []
-    for subtitle in subtitles:
-        # check content
-        if subtitle.content is None:
-            logger.log(u"Skipping subtitle for %s: no content" % video.name, logger.DEBUG)
-            continue
-
-        # check language
-        if subtitle.language in set(s.language for s in saved_subtitles):
-            logger.log(u"Skipping subtitle for %s: language already saved" % video.name, logger.DEBUG)
-            continue
-
-        # create subtitle path
-        subtitle_path = subliminal.subtitle.get_subtitle_path(video.name, None if single else subtitle.language)
-        if directory is not None:
-            subtitle_path = os.path.join(directory, os.path.split(subtitle_path)[1])
-
-        # save content as is or in the specified encoding
-        logger.log(u"Saving subtitle for %s to %s" % (video.name, subtitle_path), logger.DEBUG)
-        if subtitle.encoding:
-            with io.open(subtitle_path, 'w', encoding=subtitle.encoding) as f:
-                f.write(subtitle.text)
-        else:
-            with io.open(subtitle_path, 'wb') as f:
-                f.write(subtitle.content)
+    return (current_subtitles, new_subtitles)
 
-        # chmod and set group for the saved subtitle
-        sickbeard.helpers.chmodAsParent(subtitle_path)
-        sickbeard.helpers.fixSetGroupID(subtitle_path)
 
-        # check single
-        if single:
-            break
+def refresh_subtitles(episode_info, existing_subtitles):
+    video = get_video(episode_info['location'].encode(sickbeard.SYS_ENCODING))
+    if not video:
+        logger.log(u"Exception caught in subliminal.scan_video, subtitles couldn't be refreshed", logger.DEBUG)
+        return (existing_subtitles, None)
+    current_subtitles = get_subtitles(video)
+    if existing_subtitles == current_subtitles:
+        logger.log(u'No changed subtitles for %s S%02dE%02d'
+                   % (episode_info['show_name'], episode_info['season'],
+                      episode_info['episode']), logger.DEBUG)
+        return (existing_subtitles, None)
+    else:
+        return (current_subtitles, True)
 
-    return saved_subtitles
 
-def getNeededLanguages(current_subtitles):
-    languages = set()
-    for language in frozenset(wantedLanguages()).difference(current_subtitles):
-        languages.add(fromietf(language))
+def get_video(video_path, subtitles_path=None):
+    if not subtitles_path:
+        subtitles_path = get_subtitles_path(video_path).encode(sickbeard.SYS_ENCODING)
 
-    return languages
+    try:
+        if not sickbeard.EMBEDDED_SUBTITLES_ALL and video_path.endswith('.mkv'):
+            video = subliminal.scan_video(video_path, subtitles=True, embedded_subtitles=True,
+                                          subtitles_dir=subtitles_path)
+        else:
+            video = subliminal.scan_video(video_path, subtitles=True, embedded_subtitles=False,
+                                          subtitles_dir=subtitles_path)
+    except Exception:
+        return None
 
-# TODO: Filter here for non-languages in sickbeard.SUBTITLES_LANGUAGES
-def wantedLanguages(sqlLike=False):
-    wanted = [x for x in sorted(sickbeard.SUBTITLES_LANGUAGES) if x in subtitleCodeFilter()]
-    if sqlLike:
-        return '%' + ','.join(wanted) + '%'
+    return video
 
-    return wanted
 
-def getSubtitlesPath(video_path):
-    if os.path.isabs(sickbeard.SUBTITLES_DIR):
+def get_subtitles_path(video_path):
+    if ek(os.path.isabs, sickbeard.SUBTITLES_DIR):
         new_subtitles_path = sickbeard.SUBTITLES_DIR
     elif sickbeard.SUBTITLES_DIR:
         new_subtitles_path = ek(os.path.join, ek(os.path.dirname, video_path), sickbeard.SUBTITLES_DIR)
@@ -238,110 +280,21 @@ def getSubtitlesPath(video_path):
 
     return new_subtitles_path
 
-def subtitlesLanguages(video_path):
-    """Return a list detected subtitles for the given video file"""
-    resultList = []
-    should_save_subtitles = None
 
-    if not sickbeard.EMBEDDED_SUBTITLES_ALL and video_path.endswith('.mkv'):
-        embedded_subtitle_languages = getEmbeddedLanguages(video_path.encode(sickbeard.SYS_ENCODING))
+def get_subtitles(video):
+    """Return a sorted list of detected subtitles for the given video file"""
 
-    # Search subtitles with the absolute path
-    if os.path.isabs(sickbeard.SUBTITLES_DIR):
-        video_path = ek(os.path.join, sickbeard.SUBTITLES_DIR, ek(os.path.basename, video_path))
-    # Search subtitles with the relative path
-    elif sickbeard.SUBTITLES_DIR:
-        check_subtitles_path = ek(os.path.join, ek(os.path.dirname, video_path), sickbeard.SUBTITLES_DIR)
-        if not os.path.exists(check_subtitles_path):
-            getSubtitlesPath(video_path)
-        video_path = ek(os.path.join, ek(os.path.dirname, video_path), sickbeard.SUBTITLES_DIR, ek(os.path.basename, video_path))
-    else:
-        video_path = ek(os.path.join, ek(os.path.dirname, video_path), ek(os.path.basename, video_path))
-
-    if not sickbeard.EMBEDDED_SUBTITLES_ALL and video_path.endswith('.mkv'):
-        external_subtitle_languages = scan_subtitle_languages(video_path)
-        subtitle_languages = external_subtitle_languages.union(embedded_subtitle_languages)
-        if not sickbeard.SUBTITLES_MULTI:
-            currentWantedLanguages = wantedLanguages()
-            if len(currentWantedLanguages) == 1 and Language('und') in external_subtitle_languages:
-                if embedded_subtitle_languages not in currentWantedLanguages and Language('und') in embedded_subtitle_languages:
-                    subtitle_languages.add(fromietf(currentWantedLanguages[0]))
-                    should_save_subtitles = True
-                elif embedded_subtitle_languages not in currentWantedLanguages and Language('und') not in embedded_subtitle_languages:
-                    subtitle_languages.remove(Language('und'))
-                    subtitle_languages.add(fromietf(currentWantedLanguages[0]))
-                    should_save_subtitles = True
-    else:
-        subtitle_languages = scan_subtitle_languages(video_path)
-        if not sickbeard.SUBTITLES_MULTI:
-            if len(wantedLanguages()) == 1 and Language('und') in subtitle_languages:
-                subtitle_languages.remove(Language('und'))
-                subtitle_languages.add(fromietf(wantedLanguages()[0]))
-                should_save_subtitles = True
-
-    for language in subtitle_languages:
+    result_list = []
+
+    if not video.subtitle_languages:
+        return result_list
+
+    for language in video.subtitle_languages:
         if hasattr(language, 'opensubtitles') and language.opensubtitles:
-            resultList.append(language.opensubtitles)
-        elif hasattr(language, 'alpha3b') and language.alpha3b:
-            resultList.append(language.alpha3b)
-        elif hasattr(language, 'alpha3t') and language.alpha3t:
-            resultList.append(language.alpha3t)
-        elif hasattr(language, 'alpha2') and language.alpha2:
-            resultList.append(language.alpha2)
-
-    return (sorted(resultList), should_save_subtitles)
-
-def getEmbeddedLanguages(video_path):
-    embedded_subtitle_languages = set()
-    try:
-        with io.open(video_path, 'rb') as f:
-            mkv = MKV(f)
-            if mkv.subtitle_tracks:
-                for st in mkv.subtitle_tracks:
-                    if st.language:
-                        try:
-                            embedded_subtitle_languages.add(Language.fromalpha3b(st.language))
-                        except BabelfishError:
-                            logger.log(u'Embedded subtitle track is not a valid language', logger.DEBUG)
-                            embedded_subtitle_languages.add(Language('und'))
-                    elif st.name:
-                        try:
-                            embedded_subtitle_languages.add(Language.fromname(st.name))
-                        except BabelfishError:
-                            logger.log(u'Embedded subtitle track is not a valid language', logger.DEBUG)
-                            embedded_subtitle_languages.add(Language('und'))
-                    else:
-                        embedded_subtitle_languages.add(Language('und'))
-            else:
-                logger.log(u'MKV has no subtitle track', logger.DEBUG)
-    except MalformedMKVError:
-        logger.log(u'MKV seems to be malformed ( %s ), ignoring embedded subtitles' % video_path, logger.INFO)
-
-    return embedded_subtitle_languages
-
-def scan_subtitle_languages(path):
-    language_extensions = tuple('.' + c for c in language_converters['opensubtitles'].codes)
-    dirpath, filename = os.path.split(path)
-    subtitles = set()
-    for p in os.listdir(dirpath):
-        if not isinstance(p, bytes) and p.startswith(os.path.splitext(filename)[0]) and p.endswith(subliminal.video.SUBTITLE_EXTENSIONS):
-            if os.path.splitext(p)[0].endswith(language_extensions) and len(os.path.splitext(p)[0].rsplit('.', 1)[1]) is 2:
-                subtitles.add(Language.fromopensubtitles(os.path.splitext(p)[0][-2:]))
-            elif os.path.splitext(p)[0].endswith(language_extensions) and len(os.path.splitext(p)[0].rsplit('.', 1)[1]) is 3:
-                subtitles.add(Language.fromopensubtitles(os.path.splitext(p)[0][-3:]))
-            elif os.path.splitext(p)[0].endswith('pt-BR') and len(os.path.splitext(p)[0].rsplit('.', 1)[1]) is 5:
-                subtitles.add(Language.fromopensubtitles('pob'))
-            else:
-                subtitles.add(Language('und'))
-
-    return subtitles
-
-# TODO: Return only languages our providers allow
-def subtitleLanguageFilter():
-    return [Language.fromopensubtitles(language) for language in language_converters['opensubtitles'].codes if len(language) == 3]
-
-def subtitleCodeFilter():
-    return [Language.fromopensubtitles(language).opensubtitles for language in language_converters['opensubtitles'].codes if len(language) == 3]
+            result_list.append(language.opensubtitles)
+
+    return sorted(result_list)
+
 
 class SubtitlesFinder(object):
     """
@@ -350,57 +303,84 @@ class SubtitlesFinder(object):
     """
     def __init__(self):
         self.amActive = False
-    
-    def subtitles_download_in_pp(self):
+
+    @staticmethod
+    def subtitles_download_in_pp():  # pylint: disable=R0914
         logger.log(u'Checking for needed subtitles in Post-Process folder', logger.INFO)
 
-        providers = getEnabledServiceList()
-        provider_configs = {'addic7ed': {'username': sickbeard.ADDIC7ED_USER, 'password': sickbeard.ADDIC7ED_PASS},
-                        'legendastv': {'username': sickbeard.LEGENDASTV_USER, 'password': sickbeard.LEGENDASTV_PASS},
-                        'opensubtitles': {'username': sickbeard.OPENSUBTITLES_USER, 'password': sickbeard.OPENSUBTITLES_PASS}}
+        providers = enabled_service_list()
+        provider_configs = {'addic7ed': {'username': sickbeard.ADDIC7ED_USER,
+                                         'password': sickbeard.ADDIC7ED_PASS},
+                            'legendastv': {'username': sickbeard.LEGENDASTV_USER,
+                                           'password': sickbeard.LEGENDASTV_PASS},
+                            'opensubtitles': {'username': sickbeard.OPENSUBTITLES_USER,
+                                              'password': sickbeard.OPENSUBTITLES_PASS}}
 
         pool = subliminal.api.ProviderPool(providers=providers, provider_configs=provider_configs)
 
         # Search for all wanted languages
-        languages = set()
-        for language in frozenset(wantedLanguages()):
-            languages.add(fromietf(language))
+        languages = {from_code(language) for language in wanted_languages()}
         if not languages:
             return
-        
-        runPostProcess = False
+
+        run_post_process = False
         # Check if PP folder is set
-        if sickbeard.TV_DOWNLOAD_DIR and os.path.isdir(sickbeard.TV_DOWNLOAD_DIR):
-            for root, _, files in os.walk(sickbeard.TV_DOWNLOAD_DIR, topdown=False):
-                for videoFilename in sorted(files):
-                    if videoFilename.rsplit(".", 1)[1] in media_extensions:
+        if sickbeard.TV_DOWNLOAD_DIR and ek(os.path.isdir, sickbeard.TV_DOWNLOAD_DIR):
+            for root, _, files in ek(os.walk, sickbeard.TV_DOWNLOAD_DIR, topdown=False):
+                for video_filename in sorted(files):
+                    if video_filename.rsplit(".", 1)[1] in media_extensions:
                         try:
-                            video = subliminal.scan_video(os.path.join(root, videoFilename), subtitles=False, embedded_subtitles=False)
+                            video = subliminal.scan_video(os.path.join(root, video_filename),
+                                                          subtitles=False, embedded_subtitles=False)
                             subtitles_list = pool.list_subtitles(video, languages)
-                            
+
                             if not subtitles_list:
-                                logger.log(u'No subtitles found for %s' % os.path.join(root, videoFilename), logger.DEBUG)
+                                logger.log(u'No subtitles found for %s'
+                                           % ek(os.path.join, root, video_filename), logger.DEBUG)
                                 continue
-        
-                            found_subtitles = pool.download_best_subtitles(subtitles_list, video, languages=languages, hearing_impaired=sickbeard.SUBTITLES_HEARING_IMPAIRED, only_one=not sickbeard.SUBTITLES_MULTI)
-                            
+
+                            hearing_impaired = sickbeard.SUBTITLES_HEARING_IMPAIRED
+                            found_subtitles = pool.download_best_subtitles(subtitles_list, video, languages=languages,
+                                                                           hearing_impaired=hearing_impaired,
+                                                                           only_one=not sickbeard.SUBTITLES_MULTI)
+
+                            downloaded_languages = set()
                             for subtitle in found_subtitles:
-                                logger.log(u"Found subtitle for %s in %s provider with language %s" % (os.path.join(root, videoFilename), subtitle.provider_name, subtitle.language.opensubtitles), logger.DEBUG)
-                                save_subtitles(video, found_subtitles, directory=root, single=not sickbeard.SUBTITLES_MULTI)
-                                runPostProcess = True
-                        except Exception as e:
-                            logger.log(u"Error occurred when downloading subtitles for: %s. Error: %r" % (os.path.join(root, videoFilename), ex(e)))
-            if runPostProcess:
-                logger.log(u"Starting post-process with defaults settings now that we found subtitles")
+                                logger.log(u"Found subtitle for %s in %s provider with language %s"
+                                           % (os.path.join(root, video_filename), subtitle.provider_name,
+                                              subtitle.language.opensubtitles), logger.DEBUG)
+                                subliminal.save_subtitles(video, found_subtitles, directory=root,
+                                                          single=not sickbeard.SUBTITLES_MULTI)
+
+                                subtitles_multi = not sickbeard.SUBTITLES_MULTI
+                                subtitle_path = subliminal.subtitle.get_subtitle_path(video.name,
+                                                                                      None if subtitles_multi else
+                                                                                      subtitle.language)
+                                if root is not None:
+                                    subtitle_path = ek(os.path.join, root, ek(os.path.split, subtitle_path)[1])
+                                sickbeard.helpers.chmodAsParent(subtitle_path)
+                                sickbeard.helpers.fixSetGroupID(subtitle_path)
+
+                                downloaded_languages.add(subtitle.language.opensubtitles)
+
+                            # Don't run post processor unless at least one file has all of the needed subtitles
+                            if not needs_subtitles(downloaded_languages):
+                                run_post_process = True
+                        except Exception as error:
+                            logger.log(u"Error occurred when downloading subtitles for: %s. Error: %r"
+                                       % (os.path.join(root, video_filename), ex(error)))
+            if run_post_process:
+                logger.log(u"Starting post-process with default settings now that we found subtitles")
                 processTV.processDir(sickbeard.TV_DOWNLOAD_DIR)
 
-    def run(self, force=False):
+    def run(self, force=False):  # pylint: disable=unused-argument
 
         if not sickbeard.USE_SUBTITLES:
             return
 
-        if len(sickbeard.subtitles.getEnabledServiceList()) < 1:
-            logger.log(u'Not enough services selected. At least 1 service is required to search subtitles in the background', logger.WARNING)
+        if len(sickbeard.subtitles.enabled_service_list()) < 1:
+            logger.log(u'Not enough services selected. At least 1 service is required to '
+                       'search subtitles in the background', logger.WARNING)
             return
 
         self.amActive = True
@@ -418,70 +398,80 @@ class SubtitlesFinder(object):
         #  - search count < 7 and diff(airdate, now) <= 1 week : now -> 4h -> 8h -> 16h -> 1d -> 1d -> 1d
 
         today = datetime.date.today().toordinal()
+        database = db.DBConnection()
 
-        # you have 5 minutes to understand that one. Good luck
-        myDB = db.DBConnection()
-
-        sqlResults = myDB.select(
+        sql_results = database.select(
             'SELECT s.show_name, e.showid, e.season, e.episode, e.status, e.subtitles, ' +
-            'e.subtitles_searchcount AS searchcount, e.subtitles_lastsearch AS lastsearch, e.location, (? - e.airdate) AS airdate_daydiff ' +
+            'e.subtitles_searchcount AS searchcount, e.subtitles_lastsearch AS lastsearch, e.location, '
+            '(? - e.airdate) AS airdate_daydiff ' +
             'FROM tv_episodes AS e INNER JOIN tv_shows AS s ON (e.showid = s.indexer_id) ' +
             'WHERE s.subtitles = 1 AND e.subtitles NOT LIKE (?) ' +
             'AND (e.subtitles_searchcount <= 2 OR (e.subtitles_searchcount <= 7 AND airdate_daydiff <= 7)) ' +
-            'AND e.location != ""', [today, wantedLanguages(True)])
+            'AND e.location != ""', [today, wanted_languages(True)])
 
-        if len(sqlResults) == 0:
+        if len(sql_results) == 0:
             logger.log(u'No subtitles to download', logger.INFO)
             self.amActive = False
             return
 
-        rules = self._getRules()
+        rules = self._get_rules()
         now = datetime.datetime.now()
-        for epToSub in sqlResults:
+        for ep_to_sub in sql_results:
 
-            if not ek(os.path.isfile, epToSub['location']):
-                logger.log(u'Episode file does not exist, cannot download subtitles for episode %dx%d of show %s' % (epToSub['season'], epToSub['episode'], epToSub['show_name']), logger.DEBUG)
+            if not ek(os.path.isfile, ep_to_sub['location']):
+                logger.log(u'Episode file does not exist, cannot download subtitles for episode %dx%d of show %s'
+                           % (ep_to_sub['season'], ep_to_sub['episode'], ep_to_sub['show_name']), logger.DEBUG)
+                continue
+
+            if not needs_subtitles(ep_to_sub['subtitles']):
+                logger.log(u'Episode already has all needed subtitles, skipping  episode %dx%d of show %s'
+                           % (ep_to_sub['season'], ep_to_sub['episode'], ep_to_sub['show_name']), logger.DEBUG)
                 continue
 
             # http://bugs.python.org/issue7980#msg221094
-            # I dont think this needs done here, but keeping to be safe
+            # I dont think this needs done here, but keeping to be safe (Recent shows rule)
             datetime.datetime.strptime('20110101', '%Y%m%d')
-            if ((epToSub['airdate_daydiff'] > 7 and epToSub['searchcount'] < 2 and now - datetime.datetime.strptime(epToSub['lastsearch'], dateTimeFormat) > datetime.timedelta(hours=rules['old'][epToSub['searchcount']])) or
-                # Recent shows rule
-                (epToSub['airdate_daydiff'] <= 7 and epToSub['searchcount'] < 7 and now - datetime.datetime.strptime(epToSub['lastsearch'], dateTimeFormat) > datetime.timedelta(hours=rules['new'][epToSub['searchcount']]))):
-
-                logger.log(u'Downloading subtitles for episode %dx%d of show %s' % (epToSub['season'], epToSub['episode'], epToSub['show_name']), logger.DEBUG)
-
-                showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(epToSub['showid']))
-                if not showObj:
+            if ((ep_to_sub['airdate_daydiff'] > 7 and ep_to_sub['searchcount'] < 2 and
+                 now - datetime.datetime.strptime(ep_to_sub['lastsearch'], dateTimeFormat) >
+                 datetime.timedelta(hours=rules['old'][ep_to_sub['searchcount']])) or
+                    (ep_to_sub['airdate_daydiff'] <= 7 and ep_to_sub['searchcount'] < 7 and
+                     now - datetime.datetime.strptime(ep_to_sub['lastsearch'], dateTimeFormat) >
+                     datetime.timedelta(hours=rules['new'][ep_to_sub['searchcount']]))):
+
+                logger.log(u'Downloading subtitles for episode %dx%d of show %s'
+                           % (ep_to_sub['season'], ep_to_sub['episode'], ep_to_sub['show_name']), logger.DEBUG)
+
+                show_object = Show.find(sickbeard.showList, int(ep_to_sub['showid']))
+                if not show_object:
                     logger.log(u'Show not found', logger.DEBUG)
                     self.amActive = False
                     return
 
-                epObj = showObj.getEpisode(int(epToSub["season"]), int(epToSub["episode"]))
-                if isinstance(epObj, str):
+                episode_object = show_object.getEpisode(int(ep_to_sub["season"]), int(ep_to_sub["episode"]))
+                if isinstance(episode_object, str):
                     logger.log(u'Episode not found', logger.DEBUG)
                     self.amActive = False
                     return
 
-                existing_subtitles = epObj.subtitles
+                existing_subtitles = episode_object.subtitles
 
                 try:
-                    epObj.downloadSubtitles()
-                except Exception as e:
+                    episode_object.download_subtitles()
+                except Exception as error:
                     logger.log(u'Unable to find subtitles', logger.DEBUG)
-                    logger.log(str(e), logger.DEBUG)
+                    logger.log(str(error), logger.DEBUG)
                     self.amActive = False
                     return
 
-                newSubtitles = frozenset(epObj.subtitles).difference(existing_subtitles)
-                if newSubtitles:
-                    logger.log(u'Downloaded subtitles for S%02dE%02d in %s' % (epToSub["season"], epToSub["episode"], ', '.join(newSubtitles)))
+                new_subtitles = frozenset(episode_object.subtitles).difference(existing_subtitles)
+                if new_subtitles:
+                    logger.log(u'Downloaded subtitles for S%02dE%02d in %s'
+                               % (ep_to_sub["season"], ep_to_sub["episode"], ', '.join(new_subtitles)))
 
         self.amActive = False
 
     @staticmethod
-    def _getRules():
+    def _get_rules():
         """
         Define the hours to wait between 2 subtitles search depending on:
         - the episode: new or old
@@ -490,26 +480,28 @@ class SubtitlesFinder(object):
         return {'old': [0, 24], 'new': [0, 4, 8, 4, 16, 24, 24]}
 
 
-def run_subs_extra_scripts(epObj, found_subtitles, video, single=False):
+def run_subs_extra_scripts(episode_object, found_subtitles, video, single=False):
 
-    for curScriptName in sickbeard.SUBTITLES_EXTRA_SCRIPTS:
-        script_cmd = [piece for piece in re.split("( |\\\".*?\\\"|'.*?')", curScriptName) if piece.strip()]
+    for script_name in sickbeard.SUBTITLES_EXTRA_SCRIPTS:
+        script_cmd = [piece for piece in re.split("( |\\\".*?\\\"|'.*?')", script_name) if piece.strip()]
         script_cmd[0] = ek(os.path.abspath, script_cmd[0])
         logger.log(u"Absolute path to script: " + script_cmd[0], logger.DEBUG)
 
         for subtitle in found_subtitles:
             subtitle_path = subliminal.subtitle.get_subtitle_path(video.name, None if single else subtitle.language)
 
-            inner_cmd = script_cmd + [video.name, subtitle_path, subtitle.language.opensubtitles, epObj['show.name'],
-                                      str(epObj['season']), str(epObj['episode']), epObj['name'], str(epObj['show.indexerid'])]
+            inner_cmd = script_cmd + [video.name, subtitle_path, subtitle.language.opensubtitles,
+                                      episode_object['show_name'], str(episode_object['season']),
+                                      str(episode_object['episode']), episode_object['name'],
+                                      str(episode_object['show_indexerid'])]
 
             # use subprocess to run the command and capture output
             logger.log(u"Executing command: %s" % inner_cmd)
             try:
-                p = subprocess.Popen(inner_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-                                     stderr=subprocess.STDOUT, cwd=sickbeard.PROG_DIR)
-                out, _ = p.communicate()  # @UnusedVariable
+                process = subprocess.Popen(inner_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+                                           stderr=subprocess.STDOUT, cwd=sickbeard.PROG_DIR)
+                out, _ = process.communicate()  # @UnusedVariable
                 logger.log(u"Script result: %s" % out, logger.DEBUG)
 
-            except Exception as e:
-                logger.log(u"Unable to run subs_extra_script: " + ex(e))
+            except Exception as error:
+                logger.log(u"Unable to run subs_extra_script: " + ex(error))
diff --git a/sickbeard/traktChecker.py b/sickbeard/traktChecker.py
index 530673a228e351b55e971255f569d212ba008b64..651dcb08f51f65b4de70f924360f8a308663205e 100644
--- a/sickbeard/traktChecker.py
+++ b/sickbeard/traktChecker.py
@@ -36,6 +36,7 @@ from sickbeard.common import Quality
 from sickrage.helper.common import sanitize_filename
 from sickrage.helper.encoding import ek
 from sickrage.helper.exceptions import ex
+from sickrage.show.Show import Show
 
 
 def setEpisodeToWanted(show, s, e):
@@ -388,7 +389,7 @@ class TraktChecker(object):
                 self.addDefaultShow(indexer, indexer_id, show['title'], WANTED)
 
             if int(sickbeard.TRAKT_METHOD_ADD) == 1:
-                newShow = helpers.findCertainShow(sickbeard.showList, indexer_id)
+                newShow = Show.find(sickbeard.showList, indexer_id)
 
                 if newShow is not None:
                     setEpisodeToWanted(newShow, 1, 1)
@@ -415,7 +416,7 @@ class TraktChecker(object):
             indexer_id = int(show_el)
             show = self.EpisodeWatchlist[trakt_id][show_el]
 
-            newShow = helpers.findCertainShow(sickbeard.showList, indexer_id)
+            newShow = Show.find(sickbeard.showList, indexer_id)
 
             try:
                 if newShow is None:
@@ -444,7 +445,7 @@ class TraktChecker(object):
         """
         Adds a new show with the default settings
         """
-        if not helpers.findCertainShow(sickbeard.showList, int(indexer_id)):
+        if not Show.find(sickbeard.showList, int(indexer_id)):
             logger.log(u"Adding show " + str(indexer_id))
             root_dirs = sickbeard.ROOT_DIRS.split('|')
 
diff --git a/sickbeard/tv.py b/sickbeard/tv.py
index 6559e9c6a7797c9102aa3f11965a7bb47a0257c9..c831f1ea0057e872a95876ee084c9c4dc2729caf 100644
--- a/sickbeard/tv.py
+++ b/sickbeard/tv.py
@@ -47,12 +47,13 @@ from sickbeard.blackandwhitelist import BlackAndWhiteList
 from sickbeard import network_timezones
 from sickbeard.indexers.indexer_config import INDEXER_TVRAGE
 from sickbeard.name_parser.parser import NameParser, InvalidNameException, InvalidShowException
-from sickrage.helper.common import dateTimeFormat, remove_extension, replace_extension, sanitize_filename
+from sickrage.helper.common import dateTimeFormat, remove_extension, replace_extension, sanitize_filename, try_int
 from sickrage.helper.encoding import ek
 from sickrage.helper.exceptions import EpisodeDeletedException, EpisodeNotFoundException, ex
 from sickrage.helper.exceptions import MultipleEpisodesInDatabaseException, MultipleShowsInDatabaseException
 from sickrage.helper.exceptions import MultipleShowObjectsException, NoNFOException, ShowDirectoryNotFoundException
 from sickrage.helper.exceptions import ShowNotFoundException
+from sickrage.show.Show import Show
 
 from sickbeard.common import Quality, Overview, statusStrings
 from sickbeard.common import DOWNLOADED, SNATCHED, SNATCHED_PROPER, ARCHIVED, IGNORED, UNAIRED, WANTED, SKIPPED, UNKNOWN
@@ -112,7 +113,7 @@ class TVShow(object):
         self.nextaired = ""
         self.release_groups = None
 
-        otherShow = helpers.findCertainShow(sickbeard.showList, self.indexerid)
+        otherShow = Show.find(sickbeard.showList, self.indexerid)
         if otherShow is not None:
             raise MultipleShowObjectsException("Can't create a show if it already exists")
 
@@ -464,13 +465,16 @@ class TVShow(object):
     def loadEpisodesFromDB(self):
 
         logger.log(u"Loading all episodes from the DB", logger.DEBUG)
-
-        myDB = db.DBConnection()
-        sql = "SELECT * FROM tv_episodes WHERE showid = ?"
-        sqlResults = myDB.select(sql, [self.indexerid])
-
         scannedEps = {}
 
+        try:
+            myDB = db.DBConnection()
+            sql = "SELECT season, episode, showid, show_name FROM tv_episodes JOIN tv_shows WHERE showid = indexer_id and showid = ?"
+            sqlResults = myDB.select(sql, [self.indexerid])
+        except Exception as error:
+            logger.log(u"Could not load episodes from the DB. Error: %s" % error, logger.ERROR)
+            return scannedEps
+
         lINDEXER_API_PARMS = sickbeard.indexerApi(self.indexer).api_params.copy()
 
         if self.lang:
@@ -492,23 +496,24 @@ class TVShow(object):
             curSeason = int(curResult["season"])
             curEpisode = int(curResult["episode"])
             curShowid = int(curResult['showid'])
+            curShowName = str(curResult['show_name'])
 
-            logger.log(u"%s: loading Episodes from DB" % curShowid, logger.DEBUG)
+            logger.log(u"%s: Loading %s episodes from DB" % (curShowid, curShowName), logger.DEBUG)
             deleteEp = False
 
             if curSeason not in cachedSeasons:
                 try:
                     cachedSeasons[curSeason] = cachedShow[curSeason]
-                except sickbeard.indexer_seasonnotfound, e:
-                    logger.log(u"%s: Error when trying to load the episode from %s. Message: %s " %
-                               (curShowid, sickbeard.indexerApi(self.indexer).name, e.message), logger.WARNING)
+                except sickbeard.indexer_seasonnotfound as error:
+                    logger.log(u"%s: %s (unaired/deleted) in the indexer %s for %s. Removing existing records from database" %
+                               (curShowid, error.message, sickbeard.indexerApi(self.indexer).name, curShowName), logger.DEBUG)
                     deleteEp = True
 
             if not curSeason in scannedEps:
-                logger.log(u"Not curSeason in scannedEps", logger.DEBUG)
+                logger.log(u"%s: Not curSeason in scannedEps" % curShowid, logger.DEBUG)
                 scannedEps[curSeason] = {}
 
-            logger.log(u"%s: Loading episode S%02dE%02d from the DB" % (curShowid, curSeason or 0, curEpisode or 0), logger.DEBUG)
+            logger.log(u"%s: Loading %s S%02dE%02d from the DB" % (curShowid, curShowName, curSeason or 0, curEpisode or 0), logger.DEBUG)
 
             try:
                 curEp = self.getEpisode(curSeason, curEpisode)
@@ -523,11 +528,11 @@ class TVShow(object):
                 curEp.loadFromIndexer(tvapi=t, cachedSeason=cachedSeasons[curSeason])
                 scannedEps[curSeason][curEpisode] = True
             except EpisodeDeletedException:
-                logger.log(u"Tried loading an episode from the DB that should have been deleted, skipping it",
+                logger.log(u"%s: Tried loading %s S%02dE%02d from the DB that should have been deleted, skipping it" % (curShowid, curShowName, curSeason or 0, curEpisode or 0),
                            logger.DEBUG)
                 continue
 
-        logger.log(u"Finished loading all episodes from the DB", logger.DEBUG)
+        logger.log(u"%s: Finished loading all episodes for %s from the DB" % (curShowName, curShowid), logger.DEBUG)
 
         return scannedEps
 
@@ -1005,7 +1010,7 @@ class TVShow(object):
                 if sickbeard.TRASH_REMOVE_SHOW:
                     send2trash(cache_file)
                 else:
-                    os.remove(cache_file)
+                    ek(os.remove, cache_file)
 
             except OSError, e:
                 logger.log(u'Unable to %s %s: %s / %s' % (action, cache_file, repr(e), str(e)), logger.WARNING)
@@ -1065,7 +1070,7 @@ class TVShow(object):
 
         sql_l = []
         for ep in sqlResults:
-            curLoc = os.path.normpath(ep["location"])
+            curLoc = ek(os.path.normpath, ep["location"])
             season = int(ep["season"])
             episode = int(ep["episode"])
 
@@ -1079,8 +1084,8 @@ class TVShow(object):
                 continue
 
             # if the path doesn't exist or if it's not in our show dir
-            if not ek(os.path.isfile, curLoc) or not os.path.normpath(curLoc).startswith(
-                    os.path.normpath(self.location)):
+            if not ek(os.path.isfile, curLoc) or not ek(os.path.normpath, curLoc).startswith(
+                    ek(os.path.normpath, self.location)):
 
                 # check if downloaded files still exist, update our data if this has changed
                 if not sickbeard.SKIP_REMOVED_FILES:
@@ -1116,7 +1121,7 @@ class TVShow(object):
             myDB = db.DBConnection()
             myDB.mass_action(sql_l)
 
-    def downloadSubtitles(self, force=False):
+    def download_subtitles(self, force=False):
         # TODO: Add support for force option
         if not ek(os.path.isdir, self._location):
             logger.log(str(self.indexerid) + ": Show dir doesn't exist, can't download subtitles", logger.DEBUG)
@@ -1131,7 +1136,7 @@ class TVShow(object):
                 return
 
             for episode in episodes:
-                episode.downloadSubtitles(force=force)
+                episode.download_subtitles(force=force)
 
         except Exception:
             logger.log(u"%s: Error occurred when downloading subtitles for %s" % (self.indexerid, self.name), logger.DEBUG)
@@ -1413,41 +1418,46 @@ class TVEpisode(object):
 
     def refreshSubtitles(self):
         """Look for subtitles files and refresh the subtitles property"""
-        self.subtitles, save_subtitles = subtitles.subtitlesLanguages(self.location)
+        episode_info = {'show_name': self.show.name, 'location': self.location,
+                        'season': self.season, 'episode': self.episode}
+        self.subtitles, save_subtitles = subtitles.refresh_subtitles(episode_info, self.subtitles)
         if save_subtitles:
             self.saveToDB()
 
-    def downloadSubtitles(self, force=False):
+    def download_subtitles(self, force=False):
         if not ek(os.path.isfile, self.location):
             logger.log(u"%s: Episode file doesn't exist, can't download subtitles for S%02dE%02d" %
                        (self.show.indexerid, self.season or 0, self.episode or 0), logger.DEBUG)
             return
 
-        logger.log(u"%s: Downloading subtitles for S%02dE%02d" % (self.show.indexerid, self.season or 0, self.episode or 0), logger.DEBUG)
+        if not subtitles.needs_subtitles(self.subtitles):
+            logger.log(u'Episode already has all needed subtitles, skipping  episode %dx%d of show %s'
+                         % (self.season or 0,  self.episode or 0, self.show.name), logger.DEBUG)
+            return
 
-        # logging.getLogger('subliminal.api').addHandler(logging.StreamHandler())
-        # logging.getLogger('subliminal.api').setLevel(logging.DEBUG)
-        # logging.getLogger('subliminal').addHandler(logging.StreamHandler())
-        # logging.getLogger('subliminal').setLevel(logging.DEBUG)
+        logger.log(u"%s: Downloading subtitles for %s S%02dE%02d"
+                   % (self.show.indexerid, self.show.name, self.season or 0, self.episode or 0), logger.DEBUG)
 
-        subtitles_info = {'location': self.location, 'subtitles': self.subtitles, 'show.indexerid': self.show.indexerid, 'season': self.season,
-                          'episode': self.episode, 'name': self.name, 'show.name': self.show.name, 'status': self.status}
+        subtitles_info = {'location': self.location, 'subtitles': self.subtitles, 'season': self.season,
+                          'episode': self.episode, 'name': self.name, 'show_name': self.show.name,
+                          'show_indexerid': self.show.indexerid, 'status': self.status}
 
-        self.subtitles, newSubtitles = subtitles.downloadSubtitles(subtitles_info)
+        self.subtitles, new_subtitles = subtitles.download_subtitles(subtitles_info)
 
         self.subtitles_searchcount += 1 if self.subtitles_searchcount else 1
         self.subtitles_lastsearch = datetime.datetime.now().strftime(dateTimeFormat)
         self.saveToDB()
 
-        if newSubtitles:
-            subtitleList = ", ".join([subtitles.fromietf(newSub).name for newSub in newSubtitles])
-            logger.log(u"%s: Downloaded %s subtitles for S%02dE%02d" %
-                       (self.show.indexerid, subtitleList, self.season or 0, self.episode or 0), logger.DEBUG)
+        if new_subtitles:
+            subtitle_list = ", ".join([subtitles.name_from_code(code) for code in new_subtitles])
+            logger.log(u"%s: Downloaded %s subtitles for %s S%02dE%02d" %
+                       (self.show.indexerid, subtitle_list, self.show.name, self.season or 0,
+                        self.episode or 0), logger.DEBUG)
 
-            notifiers.notify_subtitle_download(self.prettyName(), subtitleList)
+            notifiers.notify_subtitle_download(self.prettyName(), subtitle_list)
         else:
-            logger.log(u"%s: No subtitles downloaded for S%02dE%02d" %
-                       (self.show.indexerid, self.season or 0, self.episode or 0), logger.DEBUG)
+            logger.log(u"%s: No subtitles downloaded for %s S%02dE%02d" %
+                       (self.show.indexerid, self.show.name, self.season or 0, self.episode or 0), logger.DEBUG)
 
     def checkForMetaFiles(self):
 
@@ -1502,7 +1512,7 @@ class TVEpisode(object):
                         raise EpisodeNotFoundException("Couldn't find episode S%02dE%02d" % (season or 0, episode or 0))
 
     def loadFromDB(self, season, episode):
-        logger.log(u"%s: Loading episode details from DB for episode %s S%02dE%02d" % (self.show.indexerid, self.show.name, season or 0, episode or 0), logger.DEBUG)
+        logger.log(u"%s: Loading episode details for %s S%02dE%02d from DB" % (self.show.indexerid, self.show.name, season or 0, episode or 0), logger.DEBUG)
 
         myDB = db.DBConnection()
         sqlResults = myDB.select("SELECT * FROM tv_episodes WHERE showid = ? AND season = ? AND episode = ?",
@@ -1534,7 +1544,7 @@ class TVEpisode(object):
 
             # don't overwrite my location
             if sqlResults[0]["location"] and sqlResults[0]["location"]:
-                self.location = os.path.normpath(sqlResults[0]["location"])
+                self.location = ek(os.path.normpath, sqlResults[0]["location"])
             if sqlResults[0]["file_size"]:
                 self.file_size = int(sqlResults[0]["file_size"])
             else:
@@ -1545,9 +1555,9 @@ class TVEpisode(object):
 
             sickbeard.scene_numbering.xem_refresh(self.show.indexerid, self.show.indexer)
 
-            self.scene_season = helpers.tryInt(sqlResults[0]["scene_season"], 0)
-            self.scene_episode = helpers.tryInt(sqlResults[0]["scene_episode"], 0)
-            self.scene_absolute_number = helpers.tryInt(sqlResults[0]["scene_absolute_number"], 0)
+            self.scene_season = try_int(sqlResults[0]["scene_season"], 0)
+            self.scene_episode = try_int(sqlResults[0]["scene_episode"], 0)
+            self.scene_absolute_number = try_int(sqlResults[0]["scene_absolute_number"], 0)
 
             if self.scene_absolute_number == 0:
                 self.scene_absolute_number = sickbeard.scene_numbering.get_scene_absolute_numbering(
@@ -1585,8 +1595,8 @@ class TVEpisode(object):
         if episode is None:
             episode = self.episode
 
-        logger.log(u"%s: Loading episode details from %s for episode S%02dE%02d" %
-                   (self.show.indexerid, sickbeard.indexerApi(self.show.indexer).name, season or 0, episode or 0), logger.DEBUG)
+        logger.log(u"%s: Loading episode details for %s S%02dE%02d from %s" %
+                   (self.show.indexerid, self.show.name, season or 0, episode or 0, sickbeard.indexerApi(self.show.indexer).name), logger.DEBUG)
 
         indexer_lang = self.show.lang
 
@@ -1640,7 +1650,7 @@ class TVEpisode(object):
             # return False
 
         if getattr(myEp, 'absolute_number', None) is None:
-            logger.log(u"This episode %s - S%02dE%02d has no absolute number on %s" %(self.show.name, season or 0, episode or 0, sickbeard.indexerApi(self.indexer).name), logger.DEBUG)
+            logger.log(u"%s: This episode %s - S%02dE%02d has no absolute number on %s" %(self.show.indexerid, self.show.name, season or 0, episode or 0, sickbeard.indexerApi(self.indexer).name), logger.DEBUG)
         else:
             logger.log(u"%s: The absolute_number for S%02dE%02d is: %s " % (self.show.indexerid, season or 0, episode or 0, myEp["absolute_number"]), logger.DEBUG)
             self.absolute_number = int(myEp["absolute_number"])
@@ -1698,7 +1708,7 @@ class TVEpisode(object):
 
         if not ek(os.path.isfile, self.location):
             if self.airdate >= datetime.date.today() or self.airdate == datetime.date.fromordinal(1):
-                logger.log(u"Episode airs in the future or has no airdate, marking it %s" % statusStrings[UNAIRED], logger.DEBUG)
+                logger.log(u"%s: Episode airs in the future or has no airdate, marking it %s" % (self.show.indexerid, statusStrings[UNAIRED]), logger.DEBUG)
                 self.status = UNAIRED
             elif self.status in [UNAIRED, UNKNOWN]:
                 # Only do UNAIRED/UNKNOWN, it could already be snatched/ignored/skipped, or downloaded/archived to disconnected media
@@ -2524,7 +2534,7 @@ class TVEpisode(object):
         if sickbeard.FILE_TIMESTAMP_TIMEZONE == 'local':
             airdatetime = airdatetime.astimezone(network_timezones.sb_timezone)
 
-        filemtime = datetime.datetime.fromtimestamp(os.path.getmtime(self.location)).replace(tzinfo=network_timezones.sb_timezone)
+        filemtime = datetime.datetime.fromtimestamp(ek(os.path.getmtime, self.location)).replace(tzinfo=network_timezones.sb_timezone)
 
         if filemtime != airdatetime:
             import time
@@ -2534,13 +2544,13 @@ class TVEpisode(object):
                        "' to show air date " + time.strftime("%b %d,%Y (%H:%M)", airdatetime), logger.DEBUG)
             try:
                 if helpers.touchFile(self.location, time.mktime(airdatetime)):
-                    logger.log(str(self.show.indexerid) + u": Changed modify date of " + os.path.basename(self.location)
+                    logger.log(str(self.show.indexerid) + u": Changed modify date of " + ek(os.path.basename, self.location)
                                + " to show air date " + time.strftime("%b %d,%Y (%H:%M)", airdatetime))
                 else:
-                    logger.log(str(self.show.indexerid) + u": Unable to modify date of " + os.path.basename(self.location)
+                    logger.log(str(self.show.indexerid) + u": Unable to modify date of " + ek(os.path.basename, self.location)
                                + " to show air date " + time.strftime("%b %d,%Y (%H:%M)", airdatetime), logger.WARNING)
             except Exception as e:
-                logger.log(str(self.show.indexerid) + u": Failed to modify date of '" + os.path.basename(self.location)
+                logger.log(str(self.show.indexerid) + u": Failed to modify date of '" + ek(os.path.basename, self.location)
                            + "' to show air date " + time.strftime("%b %d,%Y (%H:%M)", airdatetime) + ". Error: %s" % ex(e), logger.WARNING)
 
     def __getstate__(self):
diff --git a/sickbeard/tvcache.py b/sickbeard/tvcache.py
index 5a9229bffa4c9fd47f5f07c2a828592891489ac3..6ace5591a924705406e53fc3e043c9489fef182b 100644
--- a/sickbeard/tvcache.py
+++ b/sickbeard/tvcache.py
@@ -16,7 +16,6 @@
 # You should have received a copy of the GNU General Public License
 # along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
 
-
 import time
 import datetime
 import itertools
@@ -25,12 +24,12 @@ import urllib2
 import sickbeard
 from sickbeard import db
 from sickbeard import logger
-from sickbeard import helpers
 from sickbeard.common import Quality
 from sickbeard.rssfeeds import getFeed
 from sickbeard import show_name_helpers
 from sickrage.helper.encoding import ss
 from sickrage.helper.exceptions import AuthException, ex
+from sickrage.show.Show import Show
 from sickbeard.name_parser.parser import NameParser, InvalidNameException, InvalidShowException
 
 
@@ -246,7 +245,7 @@ class TVCache(object):
             # create showObj from indexer_id if available
             showObj = None
             if indexer_id:
-                showObj = helpers.findCertainShow(sickbeard.showList, indexer_id)
+                showObj = Show.find(sickbeard.showList, indexer_id)
 
             try:
                 myParser = NameParser(showObj=showObj)
@@ -333,7 +332,7 @@ class TVCache(object):
                 continue
 
             # get the show object, or if it's not one of our shows then ignore it
-            showObj = helpers.findCertainShow(sickbeard.showList, int(curResult["indexerid"]))
+            showObj = Show.find(sickbeard.showList, int(curResult["indexerid"]))
             if not showObj:
                 continue
 
diff --git a/sickbeard/versionChecker.py b/sickbeard/versionChecker.py
index 64a05f2f2a52d9e27698220848c3af85f8645ed5..1a9d96168aeb5d165e95b1561a4a1a40eb4e0acd 100644
--- a/sickbeard/versionChecker.py
+++ b/sickbeard/versionChecker.py
@@ -92,9 +92,9 @@ class CheckVersion(object):
         logger.log(u"Config backup in progress...")
         ui.notifications.message('Backup', 'Config backup in progress...')
         try:
-            backupDir = os.path.join(sickbeard.DATA_DIR, 'backup')
-            if not os.path.isdir(backupDir):
-                os.mkdir(backupDir)
+            backupDir = ek(os.path.join, sickbeard.DATA_DIR, 'backup')
+            if not ek(os.path.isdir, backupDir):
+                ek(os.mkdir, backupDir)
 
             if self._keeplatestbackup(backupDir) and self._backup(backupDir):
                 logger.log(u"Config backup successful, updating...")
@@ -115,20 +115,20 @@ class CheckVersion(object):
             return False
 
         import glob
-        files = glob.glob(os.path.join(backupDir, '*.zip'))
+        files = glob.glob(ek(os.path.join, backupDir, '*.zip'))
         if not files:
             return True
 
         now = time.time()
-        newest = files[0], now - os.path.getctime(files[0])
+        newest = files[0], now - ek(os.path.getctime, files[0])
         for f in files[1:]:
-            age = now - os.path.getctime(f)
+            age = now - ek(os.path.getctime, f)
             if age < newest[1]:
                 newest = f, age
         files.remove(newest[0])
 
         for f in files:
-            os.remove(f)
+            ek(os.remove, f)
 
         return True
 
@@ -138,17 +138,17 @@ class CheckVersion(object):
     def _backup(backupDir=None):
         if not backupDir:
             return False
-        source = [os.path.join(sickbeard.DATA_DIR, 'sickbeard.db'), sickbeard.CONFIG_FILE]
-        source.append(os.path.join(sickbeard.DATA_DIR, 'failed.db'))
-        source.append(os.path.join(sickbeard.DATA_DIR, 'cache.db'))
-        target = os.path.join(backupDir, 'sickrage-' + time.strftime('%Y%m%d%H%M%S') + '.zip')
+        source = [ek(os.path.join, sickbeard.DATA_DIR, 'sickbeard.db'), sickbeard.CONFIG_FILE]
+        source.append(ek(os.path.join, sickbeard.DATA_DIR, 'failed.db'))
+        source.append(ek(os.path.join, sickbeard.DATA_DIR, 'cache.db'))
+        target = ek(os.path.join, backupDir, 'sickrage-' + time.strftime('%Y%m%d%H%M%S') + '.zip')
 
-        for (path, dirs, files) in os.walk(sickbeard.CACHE_DIR, topdown=True):
+        for (path, dirs, files) in ek(os.walk, sickbeard.CACHE_DIR, topdown=True):
             for dirname in dirs:
                 if path == sickbeard.CACHE_DIR and dirname not in ['images']:
                     dirs.remove(dirname)
             for filename in files:
-                source.append(os.path.join(path, filename))
+                source.append(ek(os.path.join, path, filename))
 
         return helpers.backupConfigZip(source, target, sickbeard.DATA_DIR)
 
@@ -241,7 +241,7 @@ class CheckVersion(object):
         # check if we're a windows build
         if sickbeard.BRANCH.startswith('build '):
             install_type = 'win'
-        elif os.path.isdir(ek(os.path.join, sickbeard.PROG_DIR, u'.git')):
+        elif ek(os.path.isdir, ek(os.path.join, sickbeard.PROG_DIR, u'.git')):
             install_type = 'git'
         else:
             install_type = 'source'
@@ -813,16 +813,16 @@ class SourceUpdateManager(UpdateManager):
             # prepare the update dir
             sr_update_dir = ek(os.path.join, sickbeard.PROG_DIR, u'sr-update')
 
-            if os.path.isdir(sr_update_dir):
+            if ek(os.path.isdir, sr_update_dir):
                 logger.log(u"Clearing out update folder " + sr_update_dir + " before extracting")
                 shutil.rmtree(sr_update_dir)
 
             logger.log(u"Creating update folder " + sr_update_dir + " before extracting")
-            os.makedirs(sr_update_dir)
+            ek(os.makedirs, sr_update_dir)
 
             # retrieve file
             logger.log(u"Downloading update from " + repr(tar_download_url))
-            tar_download_path = os.path.join(sr_update_dir, u'sr-update.tar')
+            tar_download_path = ek(os.path.join, sr_update_dir, u'sr-update.tar')
             helpers.download_file(tar_download_url, tar_download_path, session=self.session)
 
             if not ek(os.path.isfile, tar_download_path):
@@ -841,40 +841,40 @@ class SourceUpdateManager(UpdateManager):
 
             # delete .tar.gz
             logger.log(u"Deleting file " + tar_download_path)
-            os.remove(tar_download_path)
+            ek(os.remove, tar_download_path)
 
             # find update dir name
-            update_dir_contents = [x for x in os.listdir(sr_update_dir) if
-                                   os.path.isdir(os.path.join(sr_update_dir, x))]
+            update_dir_contents = [x for x in ek(os.listdir, sr_update_dir) if
+                                   ek(os.path.isdir, ek(os.path.join, sr_update_dir, x))]
             if len(update_dir_contents) != 1:
                 logger.log(u"Invalid update data, update failed: " + str(update_dir_contents), logger.ERROR)
                 return False
-            content_dir = os.path.join(sr_update_dir, update_dir_contents[0])
+            content_dir = ek(os.path.join, sr_update_dir, update_dir_contents[0])
 
             # walk temp folder and move files to main folder
             logger.log(u"Moving files from " + content_dir + " to " + sickbeard.PROG_DIR)
-            for dirname, _, filenames in os.walk(content_dir):  # @UnusedVariable
+            for dirname, _, filenames in ek(os.walk, content_dir):  # @UnusedVariable
                 dirname = dirname[len(content_dir) + 1:]
                 for curfile in filenames:
-                    old_path = os.path.join(content_dir, dirname, curfile)
-                    new_path = os.path.join(sickbeard.PROG_DIR, dirname, curfile)
+                    old_path = ek(os.path.join, content_dir, dirname, curfile)
+                    new_path = ek(os.path.join, sickbeard.PROG_DIR, dirname, curfile)
 
                     # Avoid DLL access problem on WIN32/64
                     # These files needing to be updated manually
                     # or find a way to kill the access from memory
                     if curfile in ('unrar.dll', 'unrar64.dll'):
                         try:
-                            os.chmod(new_path, stat.S_IWRITE)
-                            os.remove(new_path)
-                            os.renames(old_path, new_path)
+                            ek(os.chmod, new_path, stat.S_IWRITE)
+                            ek(os.remove, new_path)
+                            ek(os.renames, old_path, new_path)
                         except Exception, e:
                             logger.log(u"Unable to update " + new_path + ': ' + ex(e), logger.DEBUG)
-                            os.remove(old_path)  # Trash the updated file without moving in new path
+                            ek(os.remove, old_path)  # Trash the updated file without moving in new path
                         continue
 
-                    if os.path.isfile(new_path):
-                        os.remove(new_path)
-                    os.renames(old_path, new_path)
+                    if ek(os.path.isfile, new_path):
+                        ek(os.remove, new_path)
+                    ek(os.renames, old_path, new_path)
 
             sickbeard.CUR_COMMIT_HASH = self._newest_commit_hash
             sickbeard.CUR_COMMIT_BRANCH = self.branch
diff --git a/sickbeard/webapi.py b/sickbeard/webapi.py
index e3092be6c6a6cea6d955207dec19a7dbf8742d42..ad07a1c54f75525f27978bd6549e22fe250ec1e7 100644
--- a/sickbeard/webapi.py
+++ b/sickbeard/webapi.py
@@ -31,7 +31,7 @@ import datetime
 import traceback
 
 import sickbeard
-from sickrage.helper.common import dateFormat, dateTimeFormat, pretty_file_size, sanitize_filename, timeFormat
+from sickrage.helper.common import dateFormat, dateTimeFormat, pretty_file_size, sanitize_filename, timeFormat, try_int
 from sickrage.helper.encoding import ek
 from sickrage.helper.exceptions import CantUpdateShowException, ex, ShowDirectoryNotFoundException
 from sickrage.helper.quality import get_quality_string
@@ -719,7 +719,7 @@ class CMD_Episode(ApiCall):
 
     def run(self):
         """ Get detailed information about an episode """
-        show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
+        show_obj = Show.find(sickbeard.showList, int(self.indexerid))
         if not show_obj:
             return _responds(RESULT_FAILURE, msg="Show not found")
 
@@ -746,7 +746,7 @@ class CMD_Episode(ApiCall):
             episode["location"] = episode["location"][show_path_length:]
 
         # convert stuff to human form
-        if helpers.tryInt(episode['airdate'], 1) > 693595:  # 1900
+        if try_int(episode['airdate'], 1) > 693595:  # 1900
             episode['airdate'] = sbdatetime.sbdatetime.sbfdate(sbdatetime.sbdatetime.convert_to_setting(
                 network_timezones.parse_date_time(int(episode['airdate']), show_obj.airs, show_obj.network)), d_preset=dateFormat)
         else:
@@ -784,7 +784,7 @@ class CMD_EpisodeSearch(ApiCall):
 
     def run(self):
         """ Search for an episode """
-        show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
+        show_obj = Show.find(sickbeard.showList, int(self.indexerid))
         if not show_obj:
             return _responds(RESULT_FAILURE, msg="Show not found")
 
@@ -840,7 +840,7 @@ class CMD_EpisodeSetStatus(ApiCall):
 
     def run(self):
         """ Set the status of an episode or a season (when no episode is provided) """
-        show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
+        show_obj = Show.find(sickbeard.showList, int(self.indexerid))
         if not show_obj:
             return _responds(RESULT_FAILURE, msg="Show not found")
 
@@ -953,7 +953,7 @@ class CMD_SubtitleSearch(ApiCall):
 
     def run(self):
         """ Search for an episode subtitles """
-        show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
+        show_obj = Show.find(sickbeard.showList, int(self.indexerid))
         if not show_obj:
             return _responds(RESULT_FAILURE, msg="Show not found")
 
@@ -966,15 +966,15 @@ class CMD_SubtitleSearch(ApiCall):
         previous_subtitles = ep_obj.subtitles
 
         try:
-            subtitles = ep_obj.downloadSubtitles()
+            subtitles = ep_obj.download_subtitles()
         except Exception:
             return _responds(RESULT_FAILURE, msg='Unable to find subtitles')
 
         # return the correct json value
         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])
+            new_languages = [subtitles.name_from_code(code) for code in new_subtitles]
+            status = 'New subtitles downloaded: %s' % ', '.join(new_languages)
             response = _responds(RESULT_SUCCESS, msg='New subtitles found')
         else:
             status = 'No subtitles downloaded'
@@ -1016,7 +1016,7 @@ class CMD_Exceptions(ApiCall):
                 scene_exceptions[indexerid].append(row["show_name"])
 
         else:
-            show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
+            show_obj = Show.find(sickbeard.showList, int(self.indexerid))
             if not show_obj:
                 return _responds(RESULT_FAILURE, msg="Show not found")
 
@@ -1068,8 +1068,8 @@ class CMD_History(ApiCall):
             del row["action"]
 
             _rename_element(row, "show_id", "indexerid")
-            row["resource_path"] = os.path.dirname(row["resource"])
-            row["resource"] = os.path.basename(row["resource"])
+            row["resource_path"] = ek(os.path.dirname, row["resource"])
+            row["resource"] = ek(os.path.basename, row["resource"])
 
             # Add tvdbid for backward compatibility
             row['tvdbid'] = row['indexerid']
@@ -1205,7 +1205,7 @@ class CMD_Logs(ApiCall):
         min_level = logger.reverseNames[str(self.min_level).upper()]
 
         data = []
-        if os.path.isfile(logger.logFile):
+        if ek(os.path.isfile, logger.logFile):
             with io.open(logger.logFile, 'r', encoding='utf-8') as f:
                 data = f.readlines()
 
@@ -1853,7 +1853,7 @@ class CMD_Show(ApiCall):
 
     def run(self):
         """ Get detailed information about a show """
-        show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
+        show_obj = Show.find(sickbeard.showList, int(self.indexerid))
         if not show_obj:
             return _responds(RESULT_FAILURE, msg="Show not found")
 
@@ -1913,7 +1913,7 @@ class CMD_Show(ApiCall):
             show_dict["network"] = ""
         show_dict["status"] = show_obj.status
 
-        if helpers.tryInt(show_obj.nextaired, 1) > 693595:
+        if try_int(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(
@@ -1961,7 +1961,7 @@ class CMD_ShowAddExisting(ApiCall):
 
     def run(self):
         """ Add an existing show in SickRage """
-        show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
+        show_obj = Show.find(sickbeard.showList, int(self.indexerid))
         if show_obj:
             return _responds(RESULT_FAILURE, msg="An existing indexerid already exists in the database")
 
@@ -2076,7 +2076,7 @@ class CMD_ShowAddNew(ApiCall):
 
     def run(self):
         """ Add a new show to SickRage """
-        show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
+        show_obj = Show.find(sickbeard.showList, int(self.indexerid))
         if show_obj:
             return _responds(RESULT_FAILURE, msg="An existing indexerid already exists in database")
 
@@ -2211,7 +2211,7 @@ class CMD_ShowCache(ApiCall):
 
     def run(self):
         """ Check SickRage's cache to see if the images (poster, banner, fanart) for a show are valid """
-        show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
+        show_obj = Show.find(sickbeard.showList, int(self.indexerid))
         if not show_obj:
             return _responds(RESULT_FAILURE, msg="Show not found")
 
@@ -2283,7 +2283,7 @@ class CMD_ShowGetQuality(ApiCall):
 
     def run(self):
         """ Get the quality setting of a show """
-        show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
+        show_obj = Show.find(sickbeard.showList, int(self.indexerid))
         if not show_obj:
             return _responds(RESULT_FAILURE, msg="Show not found")
 
@@ -2478,7 +2478,7 @@ class CMD_ShowSeasonList(ApiCall):
 
     def run(self):
         """ Get the list of seasons of a show """
-        show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
+        show_obj = Show.find(sickbeard.showList, int(self.indexerid))
         if not show_obj:
             return _responds(RESULT_FAILURE, msg="Show not found")
 
@@ -2518,7 +2518,7 @@ class CMD_ShowSeasons(ApiCall):
 
     def run(self):
         """ Get the list of episodes for one or all seasons of a show """
-        sho_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
+        sho_obj = Show.find(sickbeard.showList, int(self.indexerid))
         if not sho_obj:
             return _responds(RESULT_FAILURE, msg="Show not found")
 
@@ -2533,7 +2533,7 @@ class CMD_ShowSeasons(ApiCall):
                 status, quality = Quality.splitCompositeStatus(int(row["status"]))
                 row["status"] = _get_status_strings(status)
                 row["quality"] = get_quality_string(quality)
-                if helpers.tryInt(row['airdate'], 1) > 693595:  # 1900
+                if try_int(row['airdate'], 1) > 693595:  # 1900
                     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)
@@ -2560,7 +2560,7 @@ class CMD_ShowSeasons(ApiCall):
                 status, quality = Quality.splitCompositeStatus(int(row["status"]))
                 row["status"] = _get_status_strings(status)
                 row["quality"] = get_quality_string(quality)
-                if helpers.tryInt(row['airdate'], 1) > 693595:  # 1900
+                if try_int(row['airdate'], 1) > 693595:  # 1900
                     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)
@@ -2604,7 +2604,7 @@ 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. """
-        show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
+        show_obj = Show.find(sickbeard.showList, int(self.indexerid))
         if not show_obj:
             return _responds(RESULT_FAILURE, msg="Show not found")
 
@@ -2659,7 +2659,7 @@ class CMD_ShowStats(ApiCall):
 
     def run(self):
         """ Get episode statistics for a given show """
-        show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
+        show_obj = Show.find(sickbeard.showList, int(self.indexerid))
         if not show_obj:
             return _responds(RESULT_FAILURE, msg="Show not found")
 
@@ -2764,7 +2764,7 @@ class CMD_ShowUpdate(ApiCall):
 
     def run(self):
         """ Update a show in SickRage """
-        show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.indexerid))
+        show_obj = Show.find(sickbeard.showList, int(self.indexerid))
         if not show_obj:
             return _responds(RESULT_FAILURE, msg="Show not found")
 
@@ -2818,7 +2818,7 @@ class CMD_Shows(ApiCall):
                 "subtitles": (0, 1)[curShow.subtitles],
             }
 
-            if helpers.tryInt(curShow.nextaired, 1) > 693595:  # 1900
+            if try_int(curShow.nextaired, 1) > 693595:  # 1900
                 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)
diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py
index 79d20f81c6a9612f013c5dbc3fc228249ff2d0a5..2ad46f506e5bbbbd589225ba887f5295e5bb31ed 100644
--- a/sickbeard/webserve.py
+++ b/sickbeard/webserve.py
@@ -53,7 +53,7 @@ from unrar2 import RarFile
 import adba
 from libtrakt import TraktAPI
 from libtrakt.exceptions import traktException
-from sickrage.helper.common import sanitize_filename
+from sickrage.helper.common import sanitize_filename, try_int
 from sickrage.helper.encoding import ek, ss
 from sickrage.helper.exceptions import CantRefreshShowException, CantUpdateShowException, ex
 from sickrage.helper.exceptions import MultipleShowObjectsException, NoNFOException, ShowDirectoryNotFoundException
@@ -98,9 +98,9 @@ def get_lookup():
     global mako_path  # pylint: disable=W0603
 
     if mako_path is None:
-        mako_path = os.path.join(sickbeard.PROG_DIR, "gui/" + sickbeard.GUI_NAME + "/views/")
+        mako_path = ek(os.path.join, sickbeard.PROG_DIR, "gui/" + sickbeard.GUI_NAME + "/views/")
     if mako_cache is None:
-        mako_cache = os.path.join(sickbeard.CACHE_DIR, 'mako')
+        mako_cache = ek(os.path.join, sickbeard.CACHE_DIR, 'mako')
     if mako_lookup is None:
         mako_lookup = TemplateLookup(directories=[mako_path], module_directory=mako_cache, format_exceptions=True)
     return mako_lookup
@@ -557,7 +557,7 @@ class CalendarHandler(BaseHandler):
                 air_date_time = network_timezones.parse_date_time(episode['airdate'], show["airs"],
                                                                   show['network']).astimezone(utc)
                 air_date_time_end = air_date_time + datetime.timedelta(
-                    minutes=helpers.tryInt(show["runtime"], 60))
+                    minutes=try_int(show["runtime"], 60))
 
                 # Create event for episode
                 ical += 'BEGIN:VEVENT\r\n'
@@ -631,7 +631,7 @@ class WebFileBrowser(WebRoot):
     def complete(self, term, includeFiles=0, *args, **kwargs):
         self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')
         self.set_header("Content-Type", "application/json")
-        paths = [entry['path'] for entry in foldersAtPath(os.path.dirname(term), includeFiles=bool(int(includeFiles)))
+        paths = [entry['path'] for entry in foldersAtPath(ek(os.path.dirname, term), includeFiles=bool(int(includeFiles)))
                  if 'path' in entry]
 
         return json.dumps(paths)
@@ -651,7 +651,7 @@ class Home(WebRoot):
         if show is None:
             return "Invalid show parameters"
 
-        showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(show))
+        showObj = Show.find(sickbeard.showList, int(show))
 
         if showObj is None:
             return "Invalid show paramaters"
@@ -702,7 +702,8 @@ class Home(WebRoot):
         sql_statement += ' OR (status IN ' + status_quality + ') OR (status IN ' + status_download + '))) AS ep_total, '
 
         sql_statement += ' (SELECT airdate FROM tv_episodes WHERE showid=tv_eps.showid AND airdate >= ' + today + ' AND (status = ' + str(UNAIRED) + ' OR status = ' + str(WANTED) + ') ORDER BY airdate ASC LIMIT 1) AS ep_airs_next, '
-        sql_statement += ' (SELECT airdate FROM tv_episodes WHERE showid=tv_eps.showid AND airdate > 1 AND status <> ' + str(UNAIRED) + ' ORDER BY airdate DESC LIMIT 1) AS ep_airs_prev '
+        sql_statement += ' (SELECT airdate FROM tv_episodes WHERE showid=tv_eps.showid AND airdate > 1 AND status <> ' + str(UNAIRED) + ' ORDER BY airdate DESC LIMIT 1) AS ep_airs_prev, '
+        sql_statement += ' (SELECT SUM(file_size) FROM tv_episodes WHERE showid=tv_eps.showid) AS show_size'
         sql_statement += ' FROM tv_episodes tv_eps GROUP BY showid'
 
         sql_result = myDB.select(sql_statement)
@@ -1141,7 +1142,7 @@ class Home(WebRoot):
             if branch:
                 checkversion.updater.branch = branch
 
-            if checkversion.updater.update():
+            if checkversion.updater.need_update() and checkversion.updater.update():
                 # do a hard restart
                 sickbeard.events.put(sickbeard.events.SystemEvent.RESTART)
 
@@ -1185,7 +1186,7 @@ class Home(WebRoot):
         # 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)
+            showObj = Show.find(sickbeard.showList, show)
         except (ValueError, TypeError):
             return self._genericMessage("Error", "Invalid show ID: %s" % str(show))
 
@@ -1358,7 +1359,7 @@ class Home(WebRoot):
             else:
                 return self._genericMessage("Error", errString)
 
-        showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(show))
+        showObj = Show.find(sickbeard.showList, int(show))
 
         if not showObj:
             errString = "Unable to find the specified show: " + str(show)
@@ -1369,7 +1370,7 @@ class Home(WebRoot):
 
         showObj.exceptions = sickbeard.scene_exceptions.get_scene_exceptions(showObj.indexerid)
 
-        if helpers.tryInt(quality_preset, None):
+        if try_int(quality_preset, None):
             bestQualities = []
 
         if not location and not anyQualities and not bestQualities and not flatten_folders:
@@ -1491,8 +1492,8 @@ class Home(WebRoot):
 
             location = location.decode('UTF-8')
             # if we change location clear the db of episodes, change it, write to db, and rescan
-            if os.path.normpath(showObj._location) != os.path.normpath(location):
-                logger.log(os.path.normpath(showObj._location) + " != " + os.path.normpath(location), logger.DEBUG)
+            if ek(os.path.normpath, showObj._location) != ek(os.path.normpath, location):
+                logger.log(ek(os.path.normpath, showObj._location) + " != " + ek(os.path.normpath, location), logger.DEBUG)
                 if not ek(os.path.isdir, location) and not sickbeard.CREATE_MISSING_SHOW_DIRS:
                     errors.append("New location <tt>%s</tt> does not exist" % location)
 
@@ -1597,7 +1598,7 @@ class Home(WebRoot):
         if show is None:
             return self._genericMessage("Error", "Invalid show ID")
 
-        showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(show))
+        showObj = Show.find(sickbeard.showList, int(show))
 
         if showObj is None:
             return self._genericMessage("Error", "Unable to find the specified show")
@@ -1618,13 +1619,13 @@ class Home(WebRoot):
         if show is None:
             return self._genericMessage("Error", "Invalid show ID")
 
-        showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(show))
+        showObj = Show.find(sickbeard.showList, int(show))
 
         if showObj is None:
             return self._genericMessage("Error", "Unable to find the specified show")
 
         # search and download subtitles
-        sickbeard.showQueueScheduler.action.downloadSubtitles(showObj, bool(force))
+        sickbeard.showQueueScheduler.action.download_subtitles(showObj, bool(force))
 
         time.sleep(cpu_presets[sickbeard.CPU_PRESET])
 
@@ -1635,7 +1636,7 @@ class Home(WebRoot):
         showObj = None
 
         if show:
-            showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(show))
+            showObj = Show.find(sickbeard.showList, int(show))
             if showObj:
                 showName = urllib.quote_plus(showObj.name.encode('utf-8'))
 
@@ -1666,7 +1667,7 @@ class Home(WebRoot):
         showObj = None
 
         if show:
-            showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(show))
+            showObj = Show.find(sickbeard.showList, int(show))
 
         if notifiers.emby_notifier.update_library(showObj):
             ui.notifications.message(
@@ -1698,7 +1699,7 @@ class Home(WebRoot):
             else:
                 return self._genericMessage("Error", errMsg)
 
-        showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(show))
+        showObj = Show.find(sickbeard.showList, int(show))
 
         if not showObj:
             errMsg = "Error", "Show not in show list"
@@ -1828,7 +1829,7 @@ class Home(WebRoot):
         if show is None:
             return self._genericMessage("Error", "You must specify a show")
 
-        showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(show))
+        showObj = Show.find(sickbeard.showList, int(show))
 
         if showObj is None:
             return self._genericMessage("Error", "Show not in show list")
@@ -1874,7 +1875,7 @@ class Home(WebRoot):
             errMsg = "You must specify a show and at least one episode"
             return self._genericMessage("Error", errMsg)
 
-        show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(show))
+        show_obj = Show.find(sickbeard.showList, int(show))
 
         if show_obj is None:
             errMsg = "Error", "Show not in show list"
@@ -1941,7 +1942,7 @@ class Home(WebRoot):
     def getManualSearchStatus(self, show=None):
         def getEpisodes(searchThread, searchstatus):
             results = []
-            showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(searchThread.show.indexerid))
+            showObj = Show.find(sickbeard.showList, int(searchThread.show.indexerid))
 
             if not showObj:
                 logger.log(u'No Show Object found for show with indexerID: ' + str(searchThread.show.indexerid), logger.ERROR)
@@ -2025,15 +2026,15 @@ class Home(WebRoot):
         # try do download subtitles for that episode
         previous_subtitles = ep_obj.subtitles
         try:
-            ep_obj.downloadSubtitles()
+            ep_obj.download_subtitles()
         except Exception:
             return json.dumps({'result': 'failure'})
 
         # return the correct json value
-        newSubtitles = frozenset(ep_obj.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.name_from_code(code) for code in new_subtitles]
+            status = 'New subtitles downloaded: %s' % ', '.join(new_languages)
         else:
             status = 'No subtitles downloaded'
         ui.notifications.message(ep_obj.show.name, status)
@@ -2056,7 +2057,7 @@ class Home(WebRoot):
         if sceneAbsolute in ['null', '']:
             sceneAbsolute = None
 
-        showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(show))
+        showObj = Show.find(sickbeard.showList, int(show))
 
         if showObj.is_anime:
             result = {
@@ -2143,10 +2144,13 @@ class Home(WebRoot):
     def fetch_releasegroups(show_name):
         logger.log(u'ReleaseGroups: %s' % show_name, logger.INFO)
         if helpers.set_up_anidb_connection():
-            anime = adba.Anime(sickbeard.ADBA_CONNECTION, name=show_name)
-            groups = anime.get_groups()
-            logger.log(u'ReleaseGroups: %s' % groups, logger.INFO)
-            return json.dumps({'result': 'success', 'groups': groups})
+            try:
+                anime = adba.Anime(sickbeard.ADBA_CONNECTION, name=show_name)
+                groups = anime.get_groups()
+                logger.log(u'ReleaseGroups: %s' % groups, logger.INFO)
+                return json.dumps({'result': 'success', 'groups': groups})
+            except AttributeError as error:
+                logger.log(u'Unable to get ReleaseGroups: %s' % error, logger.DEBUG)
 
         return json.dumps({'result': 'failure'})
 
@@ -2246,14 +2250,14 @@ class HomePostProcess(Home):
             return self._genericMessage("Postprocessing results", result)
 
 
-@route('/home/addShows(/?.*)')
+@route('/addShows(/?.*)')
 class HomeAddShows(Home):
     def __init__(self, *args, **kwargs):
         super(HomeAddShows, self).__init__(*args, **kwargs)
 
     def index(self):
-        t = PageTemplate(rh=self, filename="home_addShows.mako")
-        return t.render(title='Add Shows', header='Add Shows', topmenu='home', controller="home", action="addShows")
+        t = PageTemplate(rh=self, filename="addShows.mako")
+        return t.render(title='Add Shows', header='Add Shows', topmenu='home', controller="addShows", action="index")
 
     @staticmethod
     def getIndexerLanguages():
@@ -2393,7 +2397,7 @@ class HomeAddShows(Home):
 
                 cur_dir['existing_info'] = (indexer_id, show_name, indexer)
 
-                if indexer_id and helpers.findCertainShow(sickbeard.showList, indexer_id):
+                if indexer_id and Show.find(sickbeard.showList, indexer_id):
                     cur_dir['added_already'] = True
         return t.render(dirList=dir_list)
 
@@ -2402,7 +2406,7 @@ class HomeAddShows(Home):
         Display the new show page which collects a tvdb id, folder, and extra options and
         posts them to addNewShow
         """
-        t = PageTemplate(rh=self, filename="home_newShow.mako")
+        t = PageTemplate(rh=self, filename="addShows_newShow.mako")
 
         indexer, show_dir, indexer_id, show_name = self.split_extra_show(show_to_add)
 
@@ -2441,7 +2445,8 @@ class HomeAddShows(Home):
             provided_show_dir=show_dir, provided_indexer_id=provided_indexer_id,
             provided_indexer_name=provided_indexer_name, provided_indexer=provided_indexer,
             indexers=sickbeard.indexerApi().indexers, whitelist=[], blacklist=[], groups=[],
-            title='New Show', header='New Show', topmenu='home'
+            title='New Show', header='New Show', topmenu='home',
+            controller="addShows", action="newShow"
         )
 
     def recommendedShows(self):
@@ -2449,8 +2454,9 @@ class HomeAddShows(Home):
         Display the new show page which collects a tvdb id, folder, and extra options and
         posts them to addNewShow
         """
-        t = PageTemplate(rh=self, filename="home_recommendedShows.mako")
-        return t.render(title="Recommended Shows", header="Recommended Shows", enable_anime_options=False)
+        t = PageTemplate(rh=self, filename="addShows_recommendedShows.mako")
+        return t.render(title="Recommended Shows", header="Recommended Shows", enable_anime_options=False,
+                controller="addShows", action="recommendedShows")
 
     def getRecommendedShows(self):
         t = PageTemplate(rh=self, filename="trendingShows.mako")
@@ -2473,7 +2479,7 @@ class HomeAddShows(Home):
             for show_detail in shows:
                 show = {'show': show_detail}
                 try:
-                    if not helpers.findCertainShow(sickbeard.showList, [int(show['show']['ids']['tvdb'])]):
+                    if not Show.find(sickbeard.showList, [int(show['show']['ids']['tvdb'])]):
                         if show['show']['ids']['tvdb'] not in (lshow['show']['ids']['tvdb'] for lshow in library_shows):
                             if not_liked_show:
                                 if show['show']['ids']['tvdb'] not in (show['show']['ids']['tvdb'] for show in not_liked_show if show['type'] == 'show'):
@@ -2499,8 +2505,9 @@ class HomeAddShows(Home):
         Display the new show page which collects a tvdb id, folder, and extra options and
         posts them to addNewShow
         """
-        t = PageTemplate(rh=self, filename="home_trendingShows.mako")
-        return t.render(title="Trending Shows", header="Trending Shows", enable_anime_options=False)
+        t = PageTemplate(rh=self, filename="addShows_trendingShows.mako")
+        return t.render(title="Trending Shows", header="Trending Shows", enable_anime_options=False,
+                controller="addShows", action="trendingShows")
 
     def getTrendingShows(self):
         """
@@ -2527,7 +2534,7 @@ class HomeAddShows(Home):
             library_shows = trakt_api.traktRequest("sync/collection/shows?extended=full") or []
             for show in shows:
                 try:
-                    if not helpers.findCertainShow(sickbeard.showList, [int(show['show']['ids']['tvdb'])]):
+                    if not Show.find(sickbeard.showList, [int(show['show']['ids']['tvdb'])]):
                         if show['show']['ids']['tvdb'] not in (lshow['show']['ids']['tvdb'] for lshow in library_shows):
                             if not_liked_show:
                                 if show['show']['ids']['tvdb'] not in (show['show']['ids']['tvdb'] for show in not_liked_show if show['type'] == 'show'):
@@ -2552,7 +2559,7 @@ class HomeAddShows(Home):
         """
         Fetches data from IMDB to show a list of popular shows.
         """
-        t = PageTemplate(rh=self, filename="home_popularShows.mako")
+        t = PageTemplate(rh=self, filename="addShows_popularShows.mako")
         e = None
 
         try:
@@ -2561,7 +2568,8 @@ class HomeAddShows(Home):
             # print traceback.format_exc()
             popular_shows = None
 
-        return t.render(title="Popular Shows", header="Popular Shows", popular_shows=popular_shows, imdb_exception=e, topmenu="home")
+        return t.render(title="Popular Shows", header="Popular Shows", popular_shows=popular_shows, imdb_exception=e, topmenu="home",
+                controller="addShows", action="popularShows")
 
     def addShowToBlacklist(self, indexer_id):
         # URL parameters
@@ -2571,17 +2579,24 @@ class HomeAddShows(Home):
 
         trakt_api.traktRequest("users/" + sickbeard.TRAKT_USERNAME + "/lists/" + sickbeard.TRAKT_BLACKLIST_NAME + "/items", data, method='POST')
 
-        return self.redirect('/home/addShows/trendingShows/')
+        return self.redirect('/addShows/trendingShows/')
 
     def existingShows(self):
         """
         Prints out the page to add existing shows from a root dir
         """
-        t = PageTemplate(rh=self, filename="home_addExistingShow.mako")
-        return t.render(enable_anime_options=False, title='Existing Show', header='Existing Show', topmenu="home")
+        t = PageTemplate(rh=self, filename="addShows_addExistingShow.mako")
+        return t.render(enable_anime_options=False, title='Existing Show', header='Existing Show', topmenu="home",
+                controller="addShows", action="addExistingShow")
+
+    def addShowByID(self, indexer_id, showName, indexer="TVDB"):
+
+        if indexer is not "TVDB":
+            tvdb_id = helpers.getTVDBFromID(indexer_id, indexer.upper())
+            if tvdb_id is not '':
+                indexer_id = tvdb_id
 
-    def addTraktShow(self, indexer_id, showName):
-        if helpers.findCertainShow(sickbeard.showList, int(indexer_id)):
+        if Show.find(sickbeard.showList, int(indexer_id)):
             return
 
         if sickbeard.ROOT_DIRS:
@@ -2663,7 +2678,7 @@ class HomeAddShows(Home):
                 logger.log(u"Unable to add show due to show selection. Not anough arguments: %s" % (repr(series_pieces)),
                            logger.ERROR)
                 ui.notifications.error("Unknown error. Unable to add show due to problem with show selection.")
-                return self.redirect('/home/addShows/existingShows/')
+                return self.redirect('/addShows/existingShows/')
 
             indexer = int(series_pieces[1])
             indexer_id = int(series_pieces[3])
@@ -2676,7 +2691,7 @@ class HomeAddShows(Home):
 
             indexer = int(providedIndexer)
             indexer_id = int(whichSeries)
-            show_name = os.path.basename(os.path.normpath(fullShowPath))
+            show_name = ek(os.path.basename, ek(os.path.normpath, fullShowPath))
 
         # use the whole path if it's given, or else append the show name to the root dir to get the full show path
         if fullShowPath:
@@ -2687,7 +2702,7 @@ class HomeAddShows(Home):
         # blanket policy - if the dir exists you should have used "add existing show" numbnuts
         if ek(os.path.isdir, show_dir) and not fullShowPath:
             ui.notifications.error("Unable to add show", "Folder " + show_dir + " exists already")
-            return self.redirect('/home/addShows/existingShows/')
+            return self.redirect('/addShows/existingShows/')
 
         # don't create show dir if config says not to
         if sickbeard.ADD_SHOWS_WO_DIR:
@@ -2717,7 +2732,7 @@ class HomeAddShows(Home):
 
         if not anyQualities:
             anyQualities = []
-        if not bestQualities or helpers.tryInt(quality_preset, None):
+        if not bestQualities or try_int(quality_preset, None):
             bestQualities = []
         if not isinstance(anyQualities, list):
             anyQualities = [anyQualities]
@@ -2937,7 +2952,7 @@ class Manage(Home, WebRoot):
         result = {}
         for cur_result in cur_show_results:
             if whichSubs == 'all':
-                if not frozenset(subtitles.wantedLanguages()).difference(cur_result["subtitles"].split(',')):
+                if not frozenset(subtitles.wanted_languages()).difference(cur_result["subtitles"].split(',')):
                     continue
             elif whichSubs in cur_result["subtitles"]:
                 continue
@@ -2977,7 +2992,7 @@ class Manage(Home, WebRoot):
         sorted_show_ids = []
         for cur_status_result in status_results:
             if whichSubs == 'all':
-                if not frozenset(subtitles.wantedLanguages()).difference(cur_status_result["subtitles"].split(',')):
+                if not frozenset(subtitles.wanted_languages()).difference(cur_status_result["subtitles"].split(',')):
                     continue
             elif whichSubs in cur_status_result["subtitles"]:
                 continue
@@ -3024,13 +3039,13 @@ class Manage(Home, WebRoot):
             for epResult in to_download[cur_indexer_id]:
                 season, episode = epResult.split('x')
 
-                show = sickbeard.helpers.findCertainShow(sickbeard.showList, int(cur_indexer_id))
-                show.getEpisode(int(season), int(episode)).downloadSubtitles()
+                show = Show.find(sickbeard.showList, int(cur_indexer_id))
+                show.getEpisode(int(season), int(episode)).download_subtitles()
 
         return self.redirect('/manage/subtitleMissed/')
 
     def backlogShow(self, indexer_id):
-        show_obj = helpers.findCertainShow(sickbeard.showList, int(indexer_id))
+        show_obj = Show.find(sickbeard.showList, int(indexer_id))
 
         if show_obj:
             sickbeard.backlogSearchScheduler.action.searchBacklog([show_obj])
@@ -3086,7 +3101,7 @@ class Manage(Home, WebRoot):
         showNames = []
         for curID in showIDs:
             curID = int(curID)
-            showObj = helpers.findCertainShow(sickbeard.showList, curID)
+            showObj = Show.find(sickbeard.showList, curID)
             if showObj:
                 showList.append(showObj)
                 showNames.append(showObj.name)
@@ -3226,7 +3241,7 @@ class Manage(Home, WebRoot):
         errors = []
         for curShow in showIDs:
             curErrors = []
-            showObj = helpers.findCertainShow(sickbeard.showList, int(curShow))
+            showObj = Show.find(sickbeard.showList, int(curShow))
             if not showObj:
                 continue
 
@@ -3295,7 +3310,7 @@ class Manage(Home, WebRoot):
 
             if quality_preset == 'keep':
                 anyQualities, bestQualities = Quality.splitQuality(showObj.quality)
-            elif helpers.tryInt(quality_preset, None):
+            elif try_int(quality_preset, None):
                 bestQualities = []
 
             exceptions_list = []
@@ -3369,7 +3384,7 @@ class Manage(Home, WebRoot):
             if curShowID == '':
                 continue
 
-            showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(curShowID))
+            showObj = Show.find(sickbeard.showList, int(curShowID))
 
             if showObj is None:
                 continue
@@ -3404,7 +3419,7 @@ class Manage(Home, WebRoot):
                 renames.append(showObj.name)
 
             if curShowID in toSubtitle:
-                sickbeard.showQueueScheduler.action.downloadSubtitles(showObj)
+                sickbeard.showQueueScheduler.action.download_subtitles(showObj)
                 subtitles.append(showObj.name)
 
         if errors:
@@ -3758,10 +3773,10 @@ class ConfigGeneral(Config):
         sickbeard.DEBUG = config.checkbox_to_value(debug)
         sickbeard.SSL_VERIFY = config.checkbox_to_value(ssl_verify)
         # sickbeard.LOG_DIR is set in config.change_LOG_DIR()
-        sickbeard.COMING_EPS_MISSED_RANGE = config.to_int(coming_eps_missed_range, default=7)
+        sickbeard.COMING_EPS_MISSED_RANGE = try_int(coming_eps_missed_range, 7)
         sickbeard.DISPLAY_ALL_SEASONS = config.checkbox_to_value(display_all_seasons)
 
-        sickbeard.WEB_PORT = config.to_int(web_port)
+        sickbeard.WEB_PORT = try_int(web_port)
         sickbeard.WEB_IPV6 = config.checkbox_to_value(web_ipv6)
         # sickbeard.WEB_LOG is set in config.change_LOG_DIR()
         if config.checkbox_to_value(encryption_version) == 1:
@@ -3778,10 +3793,10 @@ class ConfigGeneral(Config):
             sickbeard.DATE_PRESET = date_preset
 
         if indexer_default:
-            sickbeard.INDEXER_DEFAULT = config.to_int(indexer_default)
+            sickbeard.INDEXER_DEFAULT = try_int(indexer_default)
 
         if indexer_timeout:
-            sickbeard.INDEXER_TIMEOUT = config.to_int(indexer_timeout)
+            sickbeard.INDEXER_TIMEOUT = try_int(indexer_timeout)
 
         if time_preset:
             sickbeard.TIME_PRESET_W_SECONDS = time_preset
@@ -3790,7 +3805,7 @@ class ConfigGeneral(Config):
         sickbeard.TIMEZONE_DISPLAY = timezone_display
 
         if not config.change_LOG_DIR(log_dir, web_log):
-            results += ["Unable to create directory " + os.path.normpath(log_dir) + ", log directory not changed."]
+            results += ["Unable to create directory " + ek(os.path.normpath, log_dir) + ", log directory not changed."]
 
         sickbeard.API_KEY = api_key
 
@@ -3798,11 +3813,11 @@ class ConfigGeneral(Config):
 
         if not config.change_HTTPS_CERT(https_cert):
             results += [
-                "Unable to create directory " + os.path.normpath(https_cert) + ", https cert directory not changed."]
+                "Unable to create directory " + ek(os.path.normpath, https_cert) + ", https cert directory not changed."]
 
         if not config.change_HTTPS_KEY(https_key):
             results += [
-                "Unable to create directory " + os.path.normpath(https_key) + ", https key directory not changed."]
+                "Unable to create directory " + ek(os.path.normpath, https_key) + ", https key directory not changed."]
 
         sickbeard.HANDLE_REVERSE_PROXY = config.checkbox_to_value(handle_reverse_proxy)
 
@@ -3841,17 +3856,17 @@ class ConfigBackupRestore(Config):
         finalResult = ''
 
         if backupDir:
-            source = [os.path.join(sickbeard.DATA_DIR, 'sickbeard.db'), sickbeard.CONFIG_FILE,
-                      os.path.join(sickbeard.DATA_DIR, 'failed.db'),
-                      os.path.join(sickbeard.DATA_DIR, 'cache.db')]
-            target = os.path.join(backupDir, 'sickrage-' + time.strftime('%Y%m%d%H%M%S') + '.zip')
+            source = [ek(os.path.join, sickbeard.DATA_DIR, 'sickbeard.db'), sickbeard.CONFIG_FILE,
+                      ek(os.path.join, sickbeard.DATA_DIR, 'failed.db'),
+                      ek(os.path.join, sickbeard.DATA_DIR, 'cache.db')]
+            target = ek(os.path.join, backupDir, 'sickrage-' + time.strftime('%Y%m%d%H%M%S') + '.zip')
 
-            for (path, dirs, files) in os.walk(sickbeard.CACHE_DIR, topdown=True):
+            for (path, dirs, files) in ek(os.walk, sickbeard.CACHE_DIR, topdown=True):
                 for dirname in dirs:
                     if path == sickbeard.CACHE_DIR and dirname not in ['images']:
                         dirs.remove(dirname)
                 for filename in files:
-                    source.append(os.path.join(path, filename))
+                    source.append(ek(os.path.join, path, filename))
 
             if helpers.backupConfigZip(source, target, sickbeard.DATA_DIR):
                 finalResult += "Successful backup to " + target
@@ -3871,7 +3886,7 @@ class ConfigBackupRestore(Config):
 
         if backupFile:
             source = backupFile
-            target_dir = os.path.join(sickbeard.DATA_DIR, 'restore')
+            target_dir = ek(os.path.join, sickbeard.DATA_DIR, 'restore')
 
             if helpers.restoreConfigZip(source, target_dir):
                 finalResult += "Successfully extracted restore files to " + target_dir
@@ -3913,22 +3928,22 @@ class ConfigSearch(Config):
         results = []
 
         if not config.change_NZB_DIR(nzb_dir):
-            results += ["Unable to create directory " + os.path.normpath(nzb_dir) + ", dir not changed."]
+            results += ["Unable to create directory " + ek(os.path.normpath, nzb_dir) + ", dir not changed."]
 
         if not config.change_TORRENT_DIR(torrent_dir):
-            results += ["Unable to create directory " + os.path.normpath(torrent_dir) + ", dir not changed."]
+            results += ["Unable to create directory " + ek(os.path.normpath, torrent_dir) + ", dir not changed."]
 
         config.change_DAILYSEARCH_FREQUENCY(dailysearch_frequency)
 
         config.change_BACKLOG_FREQUENCY(backlog_frequency)
-        sickbeard.BACKLOG_DAYS = config.to_int(backlog_days, default=7)
+        sickbeard.BACKLOG_DAYS = try_int(backlog_days, 7)
 
         sickbeard.USE_NZBS = config.checkbox_to_value(use_nzbs)
         sickbeard.USE_TORRENTS = config.checkbox_to_value(use_torrents)
 
         sickbeard.NZB_METHOD = nzb_method
         sickbeard.TORRENT_METHOD = torrent_method
-        sickbeard.USENET_RETENTION = config.to_int(usenet_retention, default=500)
+        sickbeard.USENET_RETENTION = try_int(usenet_retention, 500)
 
         sickbeard.IGNORE_WORDS = ignore_words if ignore_words else ""
         sickbeard.TRACKERS_LIST = trackers_list if trackers_list else ""
@@ -3964,7 +3979,7 @@ class ConfigSearch(Config):
         sickbeard.NZBGET_CATEGORY_ANIME_BACKLOG = nzbget_category_anime_backlog
         sickbeard.NZBGET_HOST = config.clean_host(nzbget_host)
         sickbeard.NZBGET_USE_HTTPS = config.checkbox_to_value(nzbget_use_https)
-        sickbeard.NZBGET_PRIORITY = config.to_int(nzbget_priority, default=100)
+        sickbeard.NZBGET_PRIORITY = try_int(nzbget_priority, 100)
 
         sickbeard.TORRENT_USERNAME = torrent_username
         sickbeard.TORRENT_PASSWORD = torrent_password
@@ -4023,7 +4038,7 @@ class ConfigPostProcessing(Config):
         results = []
 
         if not config.change_TV_DOWNLOAD_DIR(tv_download_dir):
-            results += ["Unable to create directory " + os.path.normpath(tv_download_dir) + ", dir not changed."]
+            results += ["Unable to create directory " + ek(os.path.normpath, tv_download_dir) + ", dir not changed."]
 
         config.change_AUTOPOSTPROCESSER_FREQUENCY(autopostprocesser_frequency)
         config.change_PROCESS_AUTOMATICALLY(process_automatically)
@@ -4179,7 +4194,7 @@ class ConfigPostProcessing(Config):
         """
 
         try:
-            rar_path = os.path.join(sickbeard.PROG_DIR, 'lib', 'unrar2', 'test.rar')
+            rar_path = ek(os.path.join, sickbeard.PROG_DIR, 'lib', 'unrar2', 'test.rar')
             testing = RarFile(rar_path).read_files('*test.txt')
             if testing[0][1] == 'This is only a test.':
                 return 'supported'
@@ -4454,7 +4469,7 @@ class ConfigProviders(Config):
         # do the enable/disable
         for curProviderStr in provider_str_list:
             curProvider, curEnabled = curProviderStr.split(':')
-            curEnabled = config.to_int(curEnabled)
+            curEnabled = try_int(curEnabled)
 
             curProvObj = [x for x in sickbeard.providers.sortedProviderList() if
                           x.getID() == curProvider and hasattr(x, 'enabled')]
@@ -4860,7 +4875,7 @@ class ConfigNotifications(Config):
         sickbeard.EMAIL_NOTIFY_ONDOWNLOAD = config.checkbox_to_value(email_notify_ondownload)
         sickbeard.EMAIL_NOTIFY_ONSUBTITLEDOWNLOAD = config.checkbox_to_value(email_notify_onsubtitledownload)
         sickbeard.EMAIL_HOST = config.clean_host(email_host)
-        sickbeard.EMAIL_PORT = config.to_int(email_port, default=25)
+        sickbeard.EMAIL_PORT = try_int(email_port, 25)
         sickbeard.EMAIL_FROM = email_from
         sickbeard.EMAIL_TLS = config.checkbox_to_value(email_tls)
         sickbeard.EMAIL_USER = email_user
@@ -4931,7 +4946,7 @@ class ConfigSubtitles(Config):
         config.change_SUBTITLES_FINDER_FREQUENCY(subtitles_finder_frequency)
         config.change_USE_SUBTITLES(use_subtitles)
 
-        sickbeard.SUBTITLES_LANGUAGES = [lang.strip() for lang in subtitles_languages.split(',') if subtitles.isValidLanguage(lang.strip())] if subtitles_languages else []
+        sickbeard.SUBTITLES_LANGUAGES = [code.strip() for code in subtitles_languages.split(',') if code.strip() in subtitles.subtitle_code_filter()] if subtitles_languages else []
         sickbeard.SUBTITLES_DIR = subtitles_dir
         sickbeard.SUBTITLES_HISTORY = config.checkbox_to_value(subtitles_history)
         sickbeard.EMBEDDED_SUBTITLES_ALL = config.checkbox_to_value(embedded_subtitles_all)
@@ -5128,12 +5143,12 @@ class ErrorLogs(WebRoot):
 
         data = []
 
-        if os.path.isfile(logger.logFile):
+        if ek(os.path.isfile, logger.logFile):
             with io.open(logger.logFile, 'r', encoding='utf-8') as f:
                 data = Get_Data(minLevel, f.readlines(), 0, regex, logFilter, logSearch, maxLines)
 
         for i in range(1, int(sickbeard.LOG_NR)):
-            if os.path.isfile(logger.logFile + "." + str(i)) and (len(data) <= maxLines):
+            if ek(os.path.isfile, logger.logFile + "." + str(i)) and (len(data) <= maxLines):
                 with io.open(logger.logFile + "." + str(i), 'r', encoding='utf-8') as f:
                     data += Get_Data(minLevel, f.readlines(), len(data), regex, logFilter, logSearch, maxLines)
 
diff --git a/sickbeard/webserveInit.py b/sickbeard/webserveInit.py
index c9636d67580e87192f6b0d5badec46b60efc66fc..dd709e2e3bd87f7b7769725ae9691b138da33d4b 100644
--- a/sickbeard/webserveInit.py
+++ b/sickbeard/webserveInit.py
@@ -6,6 +6,8 @@ from sickbeard.webserve import LoginHandler, LogoutHandler, KeyHandler, Calendar
 from sickbeard.webapi import ApiHandler
 from sickbeard import logger
 from sickbeard.helpers import create_https_certificates, generateApiKey
+from sickrage.helper.encoding import ek
+
 from tornado.web import Application, StaticFileHandler, RedirectHandler
 from tornado.httpserver import HTTPServer
 from tornado.ioloop import IOLoop
@@ -52,14 +54,14 @@ class SRWebServer(threading.Thread):
 
         if self.enable_https:
             # If either the HTTPS certificate or key do not exist, make some self-signed ones.
-            if not (self.https_cert and os.path.exists(self.https_cert)) or not (
-                        self.https_key and os.path.exists(self.https_key)):
+            if not (self.https_cert and ek(os.path.exists, self.https_cert)) or not (
+                        self.https_key and ek(os.path.exists, self.https_key)):
                 if not create_https_certificates(self.https_cert, self.https_key):
                     logger.log(u"Unable to create CERT/KEY files, disabling HTTPS")
                     sickbeard.ENABLE_HTTPS = False
                     self.enable_https = False
 
-            if not (os.path.exists(self.https_cert) and os.path.exists(self.https_key)):
+            if not (ek(os.path.exists, self.https_cert) and ek(os.path.exists, self.https_key)):
                 logger.log(u"Disabled HTTPS because of missing CERT and KEY files", logger.WARNING)
                 sickbeard.ENABLE_HTTPS = False
                 self.enable_https = False
@@ -101,23 +103,23 @@ class SRWebServer(threading.Thread):
         self.app.add_handlers(".*$", [
             # favicon
             (r'%s/(favicon\.ico)' % self.options['web_root'], StaticFileHandler,
-             {"path": os.path.join(self.options['data_root'], 'images/ico/favicon.ico')}),
+             {"path": ek(os.path.join, self.options['data_root'], 'images/ico/favicon.ico')}),
 
             # images
             (r'%s/images/(.*)' % self.options['web_root'], StaticFileHandler,
-             {"path": os.path.join(self.options['data_root'], 'images')}),
+             {"path": ek(os.path.join, self.options['data_root'], 'images')}),
 
             # cached images
             (r'%s/cache/images/(.*)' % self.options['web_root'], StaticFileHandler,
-             {"path": os.path.join(sickbeard.CACHE_DIR, 'images')}),
+             {"path": ek(os.path.join, sickbeard.CACHE_DIR, 'images')}),
 
             # css
             (r'%s/css/(.*)' % self.options['web_root'], StaticFileHandler,
-             {"path": os.path.join(self.options['data_root'], 'css')}),
+             {"path": ek(os.path.join, self.options['data_root'], 'css')}),
 
             # javascript
             (r'%s/js/(.*)' % self.options['web_root'], StaticFileHandler,
-             {"path": os.path.join(self.options['data_root'], 'js')}),
+             {"path": ek(os.path.join, self.options['data_root'], 'js')}),
 
             # videos
         ] + [(r'%s/videos/(.*)' % self.options['web_root'], StaticFileHandler,
diff --git a/sickrage/helper/common.py b/sickrage/helper/common.py
index f63111cbd498632dc5dac61c7c5b42e60a1fbed5..34e0f5a107321a11dbdca0fd3748ad6fde85f481 100644
--- a/sickrage/helper/common.py
+++ b/sickrage/helper/common.py
@@ -21,6 +21,84 @@ import sickbeard
 
 dateFormat = '%Y-%m-%d'
 dateTimeFormat = '%Y-%m-%d %H:%M:%S'
+# Mapping HTTP status codes to official W3C names
+http_status_code = {
+    300: 'Multiple Choices',
+    301: 'Moved Permanently',
+    302: 'Found',
+    303: 'See Other',
+    304: 'Not Modified',
+    305: 'Use Proxy',
+    306: 'Switch Proxy',
+    307: 'Temporary Redirect',
+    308: 'Permanent Redirect',
+    400: 'Bad Request',
+    401: 'Unauthorized',
+    402: 'Payment Required',
+    403: 'Forbidden',
+    404: 'Not Found',
+    405: 'Method Not Allowed',
+    406: 'Not Acceptable',
+    407: 'Proxy Authentication Required',
+    408: 'Request Timeout',
+    409: 'Conflict',
+    410: 'Gone',
+    411: 'Length Required',
+    412: 'Precondition Failed',
+    413: 'Request Entity Too Large',
+    414: 'Request-URI Too Long',
+    415: 'Unsupported Media Type',
+    416: 'Requested Range Not Satisfiable',
+    417: 'Expectation Failed',
+    418: 'Im a teapot',
+    419: 'Authentication Timeout',
+    420: 'Enhance Your Calm',
+    422: 'Unprocessable Entity',
+    423: 'Locked',
+    424: 'Failed Dependency',
+    426: 'Upgrade Required',
+    428: 'Precondition Required',
+    429: 'Too Many Requests',
+    431: 'Request Header Fields Too Large',
+    440: 'Login Timeout',
+    444: 'No Response',
+    449: 'Retry With',
+    450: 'Blocked by Windows Parental Controls',
+    451: [
+        'Redirect',
+        'Unavailable For Legal Reasons',
+    ],
+    494: 'Request Header Too Large',
+    495: 'Cert Error',
+    496: 'No Cert',
+    497: 'HTTP to HTTPS',
+    498: 'Token expired/invalid',
+    499: [
+        'Client Closed Request',
+        'Token required',
+    ],
+    500: 'Internal Server Error',
+    501: 'Not Implemented',
+    502: 'Bad Gateway',
+    503: 'Service Unavailable',
+    504: 'Gateway Timeout',
+    505: 'HTTP Version Not Supported',
+    506: 'Variant Also Negotiates',
+    507: 'Insufficient Storage',
+    508: 'Loop Detected',
+    509: 'Bandwidth Limit Exceeded',
+    510: 'Not Extended',
+    511: 'Network Authentication Required',
+    520: 'Cloudfare - Web server is returning an unknown error',
+    521: 'Cloudfare - Web server is down',
+    522: 'Cloudfare - Connection timed out',
+    523: 'Cloudfare - Origin is unreachable',
+    524: 'Cloudfare - A timeout occurred',
+    525: 'Cloudfare - SSL handshake failed',
+    526: 'Cloudfare - Invalid SSL certificate',
+    598: 'Network read timeout error',
+    599: 'Network connect timeout error',
+}
 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',
@@ -29,6 +107,27 @@ subtitle_extensions = ['ass', 'idx', 'srt', 'ssa', 'sub']
 timeFormat = '%A %I:%M %p'
 
 
+def http_code_description(http_code):
+    """
+    Get the description of the provided HTTP status code.
+    :param http_code: The HTTP status code
+    :return: The description of the provided ``http_code``
+    """
+
+    if http_code in http_status_code:
+        description = http_status_code[http_code]
+
+        if isinstance(description, list):
+            return '(%s)' % ', '.join(description)
+
+        return description
+
+    # TODO Restore logger import
+    # logger.log(u'Unknown HTTP status code %s. Please submit an issue' % http_code, logger.ERROR)
+
+    return None
+
+
 def is_sync_file(filename):
     """
     Check if the provided ``filename`` is a sync file, based on its name.
@@ -63,11 +162,9 @@ def pretty_file_size(size):
     :param size: The size to convert
     :return: The converted size
     """
-
-    if isinstance(size, str) and size.isdigit():
+    if isinstance(size, (str, unicode)) and size.isdigit():
         size = float(size)
-
-    if not isinstance(size, (int, long, float)):
+    elif not isinstance(size, (int, long, float)):
         return ''
 
     remaining_size = size
@@ -133,3 +230,17 @@ def sanitize_filename(filename):
         return filename
 
     return ''
+
+
+def try_int(candidate, default_value=0):
+    """
+    Try to convert ``candidate`` to int, or return the ``default_value``.
+    :param candidate: The value to convert to int
+    :param default_value: The value to return if the conversion fails
+    :return: ``candidate`` as int, or ``default_value`` if the conversion fails
+    """
+
+    try:
+        return int(candidate)
+    except Exception:
+        return default_value
diff --git a/sickrage/media/GenericMedia.py b/sickrage/media/GenericMedia.py
index d323e2909e113b35aa824c2aa14c6932919ee865..968aac52cb74997069d78048ec7e9e5bf1d85867 100644
--- a/sickrage/media/GenericMedia.py
+++ b/sickrage/media/GenericMedia.py
@@ -21,9 +21,10 @@ import sickbeard
 from abc import abstractmethod
 from mimetypes import guess_type
 from os.path import isfile, join, normpath
-from sickbeard.helpers import findCertainShow
+from sickrage.helper.common import try_int
 from sickrage.helper.encoding import ek
 from sickrage.helper.exceptions import MultipleShowObjectsException
+from sickrage.show.Show import Show
 
 
 class GenericMedia:
@@ -33,16 +34,13 @@ class GenericMedia:
         :param media_format: The format of the media to get. Must be either 'normal' or 'thumb'
         """
 
+        self.indexer_id = try_int(indexer_id, 0)
+
         if media_format in ('normal', 'thumb'):
             self.media_format = media_format
         else:
             self.media_format = 'normal'
 
-        try:
-            self.indexer_id = int(indexer_id)
-        except ValueError:
-            self.indexer_id = 0
-
     @abstractmethod
     def get_default_media_name(self):
         """
@@ -98,7 +96,7 @@ class GenericMedia:
         """
 
         try:
-            return findCertainShow(sickbeard.showList, self.indexer_id)
+            return Show.find(sickbeard.showList, self.indexer_id)
         except MultipleShowObjectsException:
             return None
 
diff --git a/sickrage/show/History.py b/sickrage/show/History.py
index c015da8a8d420fe4f134205b52b3a30a5a037b0f..b46d0024e10fc0a617af1a078d49cb7d82557bf2 100644
--- a/sickrage/show/History.py
+++ b/sickrage/show/History.py
@@ -20,6 +20,7 @@ from datetime import datetime
 from datetime import timedelta
 from sickbeard.common import Quality
 from sickbeard.db import DBConnection
+from sickrage.helper.common import try_int
 
 
 class History:
@@ -96,7 +97,7 @@ class History:
 
     @staticmethod
     def _get_actions(action):
-        action = action.lower() if isinstance(action, str) else ''
+        action = action.lower() if isinstance(action, (str, unicode)) else ''
 
         if action == 'downloaded':
             return Quality.DOWNLOADED
@@ -108,12 +109,6 @@ class History:
 
     @staticmethod
     def _get_limit(limit):
-        try:
-            limit = int(limit)
-        except (TypeError, ValueError):
-            return 0
+        limit = try_int(limit, 0)
 
-        if limit < 0:
-            return 0
-
-        return int(limit)
+        return max(limit, 0)
diff --git a/sickrage/show/Show.py b/sickrage/show/Show.py
index 0634beb0175d985ffeb154750e812d9e5672d66c..441695a405cb95886b22a83ca58efb75d14b2e79 100644
--- a/sickrage/show/Show.py
+++ b/sickrage/show/Show.py
@@ -21,7 +21,6 @@ import sickbeard
 from datetime import date
 from sickbeard.common import Quality, SKIPPED, WANTED
 from sickbeard.db import DBConnection
-from sickbeard.helpers import findCertainShow
 from sickrage.helper.exceptions import CantRefreshShowException, CantRemoveShowException, ex
 from sickrage.helper.exceptions import MultipleShowObjectsException
 
@@ -53,6 +52,30 @@ class Show:
 
         return None, show
 
+    @staticmethod
+    def find(shows, indexer_id):
+        """
+        Find a show by its indexer id in the provided list of shows
+        :param shows: The list of shows to search in
+        :param indexer_id: The indexer id of the desired show
+        :return: The desired show if found, ``None`` if not found
+        :throw: ``MultipleShowObjectsException`` if multiple shows match the provided ``indexer_id``
+        """
+
+        if indexer_id is None or shows is None or len(shows) == 0:
+            return None
+
+        indexer_ids = [indexer_id] if not isinstance(indexer_id, list) else indexer_id
+        results = [show for show in shows if show.indexerid in indexer_ids]
+
+        if len(results) == 0:
+            return None
+
+        if len(results) == 1:
+            return results[0]
+
+        raise MultipleShowObjectsException()
+
     @staticmethod
     def overall_stats():
         db = DBConnection()
@@ -158,7 +181,7 @@ class Show:
             return 'Invalid show ID', None
 
         try:
-            show = findCertainShow(sickbeard.showList, int(indexer_id))
+            show = Show.find(sickbeard.showList, indexer_id)
         except MultipleShowObjectsException:
             return 'Unable to find the specified show', None
 
diff --git a/tests/__init__.py b/tests/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..78b5f9dff0d33aff7981ffad3aca187949df3161 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -0,0 +1,4 @@
+# coding=utf-8
+"""
+Test all modules for SickRage
+"""
\ No newline at end of file
diff --git a/tests/all_tests.py b/tests/all_tests.py
index ca1533cfc9da4e89a6fb1b45c2f1fc89026b9402..47aea896f0f49e69e44dfb3cf51770a417364551 100755
--- a/tests/all_tests.py
+++ b/tests/all_tests.py
@@ -18,12 +18,11 @@
 # 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=line-too-long
-
 """
 Perform all tests in tests/
 """
 
+# pylint: disable=line-too-long
 
 import fnmatch
 import os
diff --git a/tests/common_tests.py b/tests/common_tests.py
index 83a77e752553f881eefff862f6b292916ec5eaf9..9dd29d9d17808fbbc0bd1d6d494c62bf20f67216 100644
--- a/tests/common_tests.py
+++ b/tests/common_tests.py
@@ -27,9 +27,8 @@ Classes:
 
 # TODO: Implement skipped tests
 
-
-import sys
 import os.path
+import sys
 import unittest
 
 sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '../lib')))
diff --git a/tests/config_tests.py b/tests/config_tests.py
index b396896919ec140558c9f4799f4f90d7770ba1f2..e52c082fe223671ab42a87819f4f02ae00543b53 100644
--- a/tests/config_tests.py
+++ b/tests/config_tests.py
@@ -37,7 +37,6 @@ Methods
     clean_host
     clean_hosts
     clean_url
-    to_int
     minimax
     check_setting_int
     check_setting_float
@@ -46,11 +45,11 @@ Methods
 
 # pylint: disable=line-too-long
 
-import sys
+from collections import namedtuple
+import logging
 import os.path
+import sys
 import unittest
-import logging
-from collections import namedtuple
 
 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__), '..')))
@@ -119,18 +118,11 @@ class ConfigTestBasic(unittest.TestCase):
                     self.assertEqual(config.clean_url(test_url.dirty), test_url.clean)
             elif test_url.expected_result is True:
                 self.assertEqual(config.clean_url(test_url.dirty), test_url.clean)
-            elif not test_url.expected_result is False:
+            elif test_url.expected_result is False:
                 self.assertNotEqual(config.clean_url(test_url.dirty), test_url.clean)
             else:
                 log.error('Test not defined for %s', test_url)
 
-    @unittest.skip('Test not implemented')
-    def test_to_int(self):
-        """
-        Test to_int
-        """
-        pass
-
     @unittest.skip('Test not implemented')
     def test_mini_max(self):
         """
diff --git a/tests/db_tests.py b/tests/db_tests.py
index fca31fec0458a010af0ebd25556163befae4c912..6088dc342ed987a1c75f344b353a5267c80aa02c 100644
--- a/tests/db_tests.py
+++ b/tests/db_tests.py
@@ -25,10 +25,10 @@ Tests:
     DBMultiTests
 """
 
-import sys
 import os.path
-import unittest
+import sys
 import threading
+import unittest
 
 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__), '..')))
diff --git a/tests/encoding_tests.py b/tests/encoding_tests.py
index b6d042f4aa37dfb0e7b7b18dd256b6ed6167b2ed..626ea831d67f40a66ce5198026d7b72f94096a1a 100644
--- a/tests/encoding_tests.py
+++ b/tests/encoding_tests.py
@@ -6,18 +6,17 @@ Test encoding
 
 # pylint: disable=line-too-long
 
-import sys
-import os.path
 import locale
+import os.path
+import sys
 import unittest
 
 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 import ek, ex
 from sickrage.helper.common import sanitize_filename
-from sickrage.helper.encoding import ek
-from sickrage.helper.exceptions import ex
 
 
 class EncodingTests(unittest.TestCase):
diff --git a/tests/exceptions_helper_tests.py b/tests/exceptions_helper_tests.py
index a1d4e4d29ff9030b41ea5968535de6c0ded513f7..a603893809819be6914cc817a81e179b1607becf 100644
--- a/tests/exceptions_helper_tests.py
+++ b/tests/exceptions_helper_tests.py
@@ -1,42 +1,75 @@
 # -*- coding: utf-8 -*-
 
-import sys, os.path
+"""
+Test exceptions helpers
+"""
+
+# pylint: disable=line-too-long
+
+import os.path
+import sys
+import unittest
+
 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
+from sickbeard import ex
 
-import test_lib as test
-from sickrage.helper.exceptions import ex
 
 class ExceptionsHelperTestCase(unittest.TestCase):
+    """
+    Test exceptions helper
+    """
 
     def test_none_returns_empty(self):
+        """
+        Test none returns empty
+        """
         self.assertEqual(ex(None), u'')
 
     def test_empty_args_returns_empty(self):
+        """
+        Test empty args returns empty
+        """
         self.assertEqual(ex(Exception()), u'')
 
     def test_args_of_none_returns_empty(self):
+        """
+        Test args of none returns empty
+        """
         self.assertEqual(ex(Exception(None, None)), u'')
 
-    def test_Exception_returns_args_string(self):
+    def test_ex_ret_args_string(self):
+        """
+        Test exception returns args strings
+        :return:
+        """
         self.assertEqual(ex(Exception('hi')), 'hi')
 
-# TODO why doesn't this work?
-#    def test_Exception_returns_args_ustring(self):
-#        self.assertEqual(ex(Exception('\xc3\xa4h')), u'äh')
+    # TODO why doesn't this work?@
+    @unittest.skip('Errors with unicode conversion')
+    def test_ex_ret_args_ustring(self):
+        """
+        Test exception returns args ustring
+        """
+        self.assertEqual(ex(Exception('\xc3\xa4h')), u'äh')
 
-    def test_Exception_returns_concatenated_args_strings(self):
+    def test_ex_ret_concat_args_strings(self):
+        """
+        Test exception returns concatenated args and strings
+        """
         self.assertEqual(ex(Exception('lots', 'of', 'strings')), 'lots : of : strings')
 
-    def test_Exception_returns_stringified_args(self):
+    def test_ex_ret_stringed_args(self):
+        """
+        Test exception returns stringed args
+        """
         self.assertEqual(ex(Exception(303)), 'error 303')
 
 if __name__ == '__main__':
     if len(sys.argv) > 1:
-        suite = unittest.TestLoader().loadTestsFromName('exceptions_helper_tests.ExceptionsHelperTestCase.test_' + sys.argv[1])
-        unittest.TextTestRunner(verbosity=2).run(suite)
+        SUITE = unittest.TestLoader().loadTestsFromName('exceptions_helper_tests.ExceptionsHelperTestCase.test_' + sys.argv[1])
+        unittest.TextTestRunner(verbosity=2).run(SUITE)
     else:
-        suite = unittest.TestLoader().loadTestsFromTestCase(ExceptionsHelperTestCase)
-        unittest.TextTestRunner(verbosity=2).run(suite)
+        SUITE = unittest.TestLoader().loadTestsFromTestCase(ExceptionsHelperTestCase)
+        unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/feedparser_tests.py b/tests/feedparser_tests.py
index 701c23a421edfb8c09f558a02878be9596a20b31..95b64de3988d854069b0379e1b6cea2343563399 100644
--- a/tests/feedparser_tests.py
+++ b/tests/feedparser_tests.py
@@ -4,8 +4,8 @@
 Test Feed Parser
 """
 
-import sys
 import os.path
+import sys
 import unittest
 
 sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '../lib')))
diff --git a/tests/helpers_tests.py b/tests/helpers_tests.py
index 87b9e1c5dc9c1fbc9b3a4b4c365542d4120cca6b..b605837d48c8903fdfb6f1c426a95b96c9d3b2f3 100755
--- a/tests/helpers_tests.py
+++ b/tests/helpers_tests.py
@@ -1,6 +1,7 @@
 #!/usr/bin/env python2.7
+# coding=utf-8
 # Author: Dustyn Gibson <miigotu@gmail.com>
-# URL: http://github.com/SiCKRAGETV/SickRage
+# URL: http://github.com/SickRage/SickRage
 #
 # This file is part of SickRage.
 #
@@ -21,7 +22,6 @@
 Test sickbeard.helpers
 
 Methods:
-    isValidLanguage
     fixGlob
     indentXML
     remove_non_release_groups
@@ -29,7 +29,6 @@ Methods:
     isRarFile
     isBeingWritten
     remove_file_failed
-    findCertainShow
     makeDir
     searchDBForShow
     searchIndexerForShowID
@@ -55,7 +54,6 @@ Methods:
     create_https_certificates
     backupVersionedFile
     restoreVersionedFile
-    tryInt
     md5_for_file
     get_lan_ip
     check_url
@@ -76,7 +74,6 @@ Methods:
     mapIndexersToShow
     touchFile
     _getTempDir
-    codeDescription
     _setUpSession
     getURL
     download_file
@@ -90,16 +87,14 @@ Methods:
     getDiskSpaceUsage
 """
 
-import sys
 import os.path
+import sys
+import unittest
 
 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
-
-from sickbeard.helpers import isValidLanguage, remove_non_release_groups
-from babelfish import language  # pylint: disable=import-error
+from sickbeard.helpers import remove_non_release_groups
 
 TEST_RESULT = 'Show.Name.S01E01.HDTV.x264-RLSGROUP'
 TEST_CASES = {
@@ -461,13 +456,6 @@ class HelpersShowTests(unittest.TestCase):
     """
     Test show methods
     """
-    @unittest.skip('Not yet implemented')
-    def test_find_certain_show(self):
-        """
-        Test findCertainShow
-        """
-        pass
-
     @unittest.skip('Not yet implemented')
     def test_search_db_for_show(self):
         """
@@ -590,26 +578,6 @@ class HelpersMiscTests(unittest.TestCase):
     """
     Test misc helper methods
     """
-    @unittest.expectedFailure
-    def test_is_valid_language(self):
-        # TODO: Determine why this fails and at such a high failure %
-        """
-        Test isValidLanguage
-        """
-        exception_count = 0
-        total = 0
-        for lang in language.LANGUAGES:
-            total += 1
-            try:
-                self.assertTrue(isValidLanguage(lang), lang)
-            except NameError:
-                exception_count += 1
-            except Exception as error:
-                raise error
-        if exception_count > 0:
-            raise Exception('Language failure ratio: %s [%s/%s]' %
-                            (100.0 * exception_count/total, exception_count, total))
-
     @unittest.skip('Not yet implemented')
     def test_fix_glob(self):
         """
@@ -659,13 +627,6 @@ class HelpersMiscTests(unittest.TestCase):
         """
         pass
 
-    @unittest.skip('Not yet implemented')
-    def test_try_int(self):
-        """
-        Test tryInt
-        """
-        pass
-
     @unittest.skip('Not yet implemented')
     def test_full_sanitize_scene_name(self):
         """
@@ -673,13 +634,6 @@ class HelpersMiscTests(unittest.TestCase):
         """
         pass
 
-    @unittest.skip('Not yet implemented')
-    def test_code_description(self):
-        """
-        Test codeDescription
-        """
-        pass
-
     @unittest.skip('Not yet implemented')
     def test_remove_article(self):
         """
diff --git a/tests/issue_submitter_tests.py b/tests/issue_submitter_tests.py
index 79829f5b45821a1084e969a3e7e7f0422a4bad2c..c929ee284587eae0e1d75a51c9cfacbeff794631 100644
--- a/tests/issue_submitter_tests.py
+++ b/tests/issue_submitter_tests.py
@@ -21,15 +21,14 @@
 Test exception logging
 """
 
-import sys
 import os.path
+import sys
 import unittest
 
 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 import logger
-from sickrage.helper.exceptions import ex
+from sickbeard import logger, ex
 
 
 def exception_generator():
diff --git a/tests/name_parser_tests.py b/tests/name_parser_tests.py
index 50293a9078039849bb8b8a4ddb0cb57c0e838987..c71a904e2f680cb00e4af8f686303860f62dfb35 100644
--- a/tests/name_parser_tests.py
+++ b/tests/name_parser_tests.py
@@ -1,23 +1,23 @@
+# coding=utf-8
 """
 Test name parsing
 """
 
 # pylint: disable=line-too-long
 
-import sys
-import os.path
 import datetime
+import os.path
+import sys
 import unittest
 
 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 tests import test_lib as test
-import sickbeard
 from sickbeard import tv
 from sickbeard.name_parser import parser
+import tests.test_lib as test
 
-sickbeard.SYS_ENCODING = 'UTF-8'
+SYS_ENCODING = 'UTF-8'
 
 DEBUG = VERBOSE = False
 
diff --git a/tests/notifier_tests.py b/tests/notifier_tests.py
index b6b2b9d99bb3c096e66dd63429325838807c789a..9d72f62f30523a2ca355bd5640deb3be5d2853bd 100644
--- a/tests/notifier_tests.py
+++ b/tests/notifier_tests.py
@@ -30,22 +30,20 @@ Test notifiers
 """
 
 
-import sys
 import os.path
+import sys
 import unittest
 
 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
-
 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
+import tests.test_lib as test
 
 
 class NotifierTests(test.SickbeardTestDBCase):  # pylint: disable=too-many-public-methods
diff --git a/tests/numdict_tests.py b/tests/numdict_tests.py
index 4802550bf9ee0c8ccfd5eafb7c1980d198e1145f..54f752a20288fb6082fd60abb32d2044a400b8d4 100644
--- a/tests/numdict_tests.py
+++ b/tests/numdict_tests.py
@@ -6,8 +6,8 @@ Unit Tests for sickbeard/numdict.py
 
 # pylint: disable=line-too-long
 
-import sys
 import os.path
+import sys
 import unittest
 
 sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '../lib')))
diff --git a/tests/pp_tests.py b/tests/pp_tests.py
index c11b90338b1c8746d0b99d51bfeea538a75e016a..30ecdc4750928f9d253f526fca0e0708a17aac96 100644
--- a/tests/pp_tests.py
+++ b/tests/pp_tests.py
@@ -17,50 +17,70 @@
 # 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 post processing
+"""
 
-import random
+import os.path
+import sys
 import unittest
 
-import test_lib as test
+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.name_cache import addNameToCache
 from sickbeard.postProcessor import PostProcessor
-import sickbeard
 from sickbeard.tv import TVEpisode, TVShow
-from sickbeard.name_cache import addNameToCache
+import sickbeard
+import tests.test_lib as test
 
 
 class PPInitTests(unittest.TestCase):
-
+    """
+    Init tests
+    """
     def setUp(self):
-        self.pp = PostProcessor(test.FILE_PATH)
+        """
+        Set up tests
+        """
+        self.post_processor = PostProcessor(test.FILE_PATH)
 
     def test_init_file_name(self):
-        self.assertEqual(self.pp.file_name, test.FILENAME)
+        """
+        Test file name
+        """
+        self.assertEqual(self.post_processor.file_name, test.FILENAME)
 
     def test_init_folder_name(self):
-        self.assertEqual(self.pp.folder_name, test.SHOW_NAME)
+        """
+        Test folder name
+        """
+        self.assertEqual(self.post_processor.folder_name, test.SHOW_NAME)
 
-class PPBasicTests(test.SickbeardTestDBCase):
 
+class PPBasicTests(test.SickbeardTestDBCase):
+    """
+    Basic tests
+    """
     def test_process(self):
-        show = TVShow(1,3)
+        """
+        Test process
+        """
+        show = TVShow(1, 3)
         show.name = test.SHOW_NAME
         show.location = test.SHOW_DIR
         show.saveToDB()
 
         sickbeard.showList = [show]
-        ep = TVEpisode(show, test.SEASON, test.EPISODE)
-        ep.name = "some ep name"
-        ep.saveToDB()
+        episode = TVEpisode(show, test.SEASON, test.EPISODE)
+        episode.name = "some episode name"
+        episode.saveToDB()
 
         addNameToCache('show name', 3)
         sickbeard.PROCESS_METHOD = 'move'
 
-        pp = PostProcessor(test.FILE_PATH)
-        self.assertTrue(pp.process())
+        post_processor = PostProcessor(test.FILE_PATH)
+        self.assertTrue(post_processor.process())
 
 
 if __name__ == '__main__':
@@ -68,8 +88,11 @@ if __name__ == '__main__':
     print "STARTING - PostProcessor TESTS"
     print "=================="
     print "######################################################################"
-    suite = unittest.TestLoader().loadTestsFromTestCase(PPInitTests)
-    unittest.TextTestRunner(verbosity=2).run(suite)
+
+    SUITE = unittest.TestLoader().loadTestsFromTestCase(PPInitTests)
+    unittest.TextTestRunner(verbosity=2).run(SUITE)
+
     print "######################################################################"
-    suite = unittest.TestLoader().loadTestsFromTestCase(PPBasicTests)
-    unittest.TextTestRunner(verbosity=2).run(suite)
+
+    SUITE = unittest.TestLoader().loadTestsFromTestCase(PPBasicTests)
+    unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/scene_helpers_tests.py b/tests/scene_helpers_tests.py
index f064bf44bcf10fe7879ad2326ced11755b3aab10..9241c69fa22de4eb087c89b0370173a40decd4bd 100644
--- a/tests/scene_helpers_tests.py
+++ b/tests/scene_helpers_tests.py
@@ -1,20 +1,20 @@
+# coding=utf-8
 """
 Test scene helpers
 """
 
 # pylint: disable=line-too-long
 
-import sys
 import os.path
+import sys
 import unittest
-import tests.test_lib as test
 
 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 import show_name_helpers, scene_exceptions, common, name_cache
-from sickbeard import db
+from sickbeard import show_name_helpers, scene_exceptions, common, name_cache, db
 from sickbeard.tv import TVShow as Show
+import tests.test_lib as test
 
 
 class SceneTests(test.SickbeardTestDBCase):
diff --git a/tests/search_tests.py b/tests/search_tests.py
index c90378db4430547c0011d79c593374f3d20c85f0..56601e158f017946195131442a69c79ae85a37a3 100644
--- a/tests/search_tests.py
+++ b/tests/search_tests.py
@@ -18,60 +18,81 @@
 # 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__), '..')))
+# pylint: disable=line-too-long
+
+"""
+Test searches
+"""
 
-import random
+import os.path
+import sys
 import unittest
 
-import test_lib as test
+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.search as search
-import sickbeard
 from sickbeard.tv import TVEpisode, TVShow
-import sickbeard.common as c
-
 from sickbeard.providers.generic import GenericProvider
+import sickbeard
+import sickbeard.common as common
+import tests.test_lib as test
 
-tests = {"Game of Thrones":
-               {"tvdbid": 121361, "s": 5, "e": [10],
-                "s_strings": [{"Season": [u"Game of Thrones S05"]}],
-                "e_strings": [{"Episode": [u"Game of Thrones S05E10"]}]}}
+TESTS = {
+    "Game of Thrones": {
+        "tvdbid": 121361, "s": 5, "e": [10],
+        "s_strings": [{"Season": [u"Game of Thrones S05"]}],
+        "e_strings": [{"Episode": [u"Game of Thrones S05E10"]}]
+    }
+}
 
-class SearchTest(test.SickbeardTestDBCase):
 
+class SearchTest(test.SickbeardTestDBCase):
+    """
+    Test search
+    """
     def __init__(self, something):
         super(SearchTest, self).__init__(something)
 
 
-def test_generator(curData, name, provider, forceSearch):
+def test_generator(cur_data, cur_name, cur_provider):
+    """
+    Generate test
+
+    :param cur_data:
+    :param cur_name:
+    :param cur_provider:
+    :return:
+    """
 
-    def test(self):
-        show = TVShow(1, int(curData["tvdbid"]))
-        show.name = name
-        show.quality = c.ANY | c.Quality.UNKNOWN | c.Quality.RAWHDTV
+    def do_test():
+        """
+        Test to perform
+        """
+        show = TVShow(1, int(cur_data["tvdbid"]))
+        show.name = cur_name
+        show.quality = common.ANY | common.Quality.UNKNOWN | common.Quality.RAWHDTV
         show.saveToDB()
         sickbeard.showList.append(show)
 
-        for epNumber in curData["e"]:
-            episode = TVEpisode(show, curData["s"], epNumber)
-            episode.status = c.WANTED
+        for ep_number in cur_data["e"]:
+            episode = TVEpisode(show, cur_data["s"], ep_number)
+            episode.status = common.WANTED
 
-            # We arent updating scene numbers, so fake it here
-            episode.scene_season = curData["s"]
-            episode.scene_episode = epNumber
+            # We aren't updating scene numbers, so fake it here
+            episode.scene_season = cur_data["s"]
+            episode.scene_episode = ep_number
 
             episode.saveToDB()
 
-            provider.show = show
-            season_strings = provider._get_season_search_strings(episode)
-            episode_strings = provider._get_episode_search_strings(episode)
+            cur_provider.show = show
+            season_strings = cur_provider._get_season_search_strings(episode)  # pylint: disable=protected-access
+            episode_strings = cur_provider._get_episode_search_strings(episode)  # pylint: disable=protected-access
 
             fail = False
+            cur_string = ''
             for cur_string in season_strings, episode_strings:
                 if not all([isinstance(cur_string, list), isinstance(cur_string[0], dict)]):
-                    print " %s is using a wrong string format!" % provider.name
+                    print " %s is using a wrong string format!" % cur_provider.name
                     print cur_string
                     fail = True
                     continue
@@ -80,44 +101,45 @@ def test_generator(curData, name, provider, forceSearch):
                 continue
 
             try:
-                assert(season_strings == curData["s_strings"])
-                assert(episode_strings == curData["e_strings"])
+                assert season_strings == cur_data["s_strings"]
+                assert episode_strings == cur_data["e_strings"]
             except AssertionError:
-                print " %s is using a wrong string format!" % provider.name
+                print " %s is using a wrong string format!" % cur_provider.name
                 print cur_string
                 continue
 
             search_strings = episode_strings[0]
-            #search_strings.update(season_strings[0])
-            #search_strings.update({"RSS":['']})
+            # search_strings.update(season_strings[0])
+            # search_strings.update({"RSS":['']})
 
-            #print search_strings
+            # print search_strings
 
-            if not provider.public:
+            if not cur_provider.public:
                 continue
 
-            items = provider._doSearch(search_strings)
+            items = cur_provider._doSearch(search_strings)  # pylint: disable=protected-access
             if not items:
-                print "No results from provider?"
+                print "No results from cur_provider?"
                 continue
 
-            title, url = provider._get_title_and_url(items[0])
+            title, url = cur_provider._get_title_and_url(items[0])  # pylint: disable=protected-access
             for word in show.name.split(" "):
                 if not word.lower() in title.lower():
-                    print "Show name not in title: %s. URL: %s" % (title, url)
+                    print "Show cur_name not in title: %s. URL: %s" % (title, url)
                     continue
 
             if not url:
                 print "url is empty"
                 continue
 
-            quality = provider.getQuality(items[0])
-            size = provider._get_size(items[0])
+            quality = cur_provider.getQuality(items[0])
+            size = cur_provider._get_size(items[0])  # pylint: disable=protected-access
+
             if not show.quality & quality:
-                print "Quality not in common.ANY, %r" % quality
+                print "Quality not in common.ANY, %r %s" % (quality, size)
                 continue
 
-    return test
+    return do_test
 
 if __name__ == '__main__':
     print "=================="
@@ -126,17 +148,17 @@ if __name__ == '__main__':
     print "######################################################################"
     # create the test methods
     for forceSearch in (True, False):
-        for name, curData in tests.items():
-            fname = name.replace(' ', '_')
+        for name, data in TESTS.items():
+            filename = name.replace(' ', '_')
 
             for provider in sickbeard.providers.sortedProviderList():
                 if provider.providerType == GenericProvider.TORRENT:
                     if forceSearch:
-                        test_name = 'test_manual_%s_%s_%s' % (fname, curData["tvdbid"], provider.name)
+                        test_name = 'test_manual_%s_%s_%s' % (filename, data["tvdbid"], provider.name)
                     else:
-                        test_name = 'test_%s_%s_%s' % (fname, curData["tvdbid"], provider.name)
-                    test = test_generator(curData, name, provider, forceSearch)
+                        test_name = 'test_%s_%s_%s' % (filename, data["tvdbid"], provider.name)
+                    test = test_generator(data, name, provider)
                     setattr(SearchTest, test_name, test)
 
-    suite = unittest.TestLoader().loadTestsFromTestCase(SearchTest)
-    unittest.TextTestRunner(verbosity=2).run(suite)
+    SUITE = unittest.TestLoader().loadTestsFromTestCase(SearchTest)
+    unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/sickrage_tests/__init__.py b/tests/sickrage_tests/__init__.py
index f250b9e1d019f65aea1bae0aa142457a68e294ab..65f1d24f7a0f64e3e96303df7d9349c13fe99e93 100644
--- a/tests/sickrage_tests/__init__.py
+++ b/tests/sickrage_tests/__init__.py
@@ -1,6 +1,7 @@
+# coding=utf-8
 # This file is part of SickRage.
 #
-# URL: https://sickrage.github.io
+# URL: https://SickRage.GitHub.io
 # Git: https://github.com/SickRage/SickRage.git
 #
 # SickRage is free software: you can redistribute it and/or modify
@@ -16,28 +17,28 @@
 # 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.common_tests import CommonTests
-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
+"""
+Tests for SickRage
+"""
+
+from __future__ import print_function
+
+import helper
+import media
+import show
+import system
+import unittest
 
 if __name__ == '__main__':
     print('=====> Running all test in "sickrage_tests" <=====')
 
-    test_classes = [
-        ComingEpisodesTests,
-        CommonTests,
-        HistoryTests,
-        QualityTests,
-        RestartTests,
-        ShowTests,
-        ShutdownTests,
+    TEST_MODULES = [
+        helper,
+        media,
+        show,
+        system,
     ]
 
-    for test_class in test_classes:
-        suite = TestLoader().loadTestsFromTestCase(test_class)
-        TextTestRunner(verbosity=2).run(suite)
+    for test_module in TEST_MODULES:
+        SUITE = unittest.TestLoader().loadTestsFromModule(test_module)
+        unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/sickrage_tests/helper/__init__.py b/tests/sickrage_tests/helper/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..21e2b67f8fc095b5b8ccb5f4471c2ed2983d30f3 100644
--- a/tests/sickrage_tests/helper/__init__.py
+++ b/tests/sickrage_tests/helper/__init__.py
@@ -0,0 +1,21 @@
+# coding=utf-8
+"""
+Tests for SickRage helpers
+"""
+
+import unittest
+
+from common_tests import CommonTests
+from quality_tests import QualityTests
+
+if __name__ == '__main__':
+    print('=====> Running all test in "sickrage_tests.helper" <=====')
+
+    TEST_CLASSES = [
+        CommonTests,
+        QualityTests,
+    ]
+
+    for test_class in TEST_CLASSES:
+        SUITE = unittest.TestLoader().loadTestsFromTestCase(test_class)
+        unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/sickrage_tests/helper/common_tests.py b/tests/sickrage_tests/helper/common_tests.py
index 8991ab3ef7cb98dc474c6be241a0aded27f6bbef..2f4eb47cc193cc96b53e82157c0c52ff0bfae49d 100644
--- a/tests/sickrage_tests/helper/common_tests.py
+++ b/tests/sickrage_tests/helper/common_tests.py
@@ -1,6 +1,7 @@
+# coding=utf-8
 # This file is part of SickRage.
 #
-# URL: https://sickrage.github.io
+# URL: https://SickRage.GitHub.io
 # Git: https://github.com/SickRage/SickRage.git
 #
 # SickRage is free software: you can redistribute it and/or modify
@@ -16,8 +17,15 @@
 # You should have received a copy of the GNU General Public License
 # along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
 
-from unittest import TestCase, TestLoader, TextTestRunner
+# pylint: disable=line-too-long
 
+"""
+Test sickrage.common
+"""
+
+from __future__ import print_function
+
+import unittest
 import os
 import sys
 
@@ -25,85 +33,136 @@ sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '../.
 sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../..')))
 
 import sickbeard
+from sickrage.helper.common import http_code_description, is_sync_file, is_torrent_or_nzb_file, pretty_file_size
+from sickrage.helper.common import remove_extension, replace_extension, sanitize_filename, try_int
+
 
-from sickrage.helper.common import is_sync_file, is_torrent_or_nzb_file, pretty_file_size, remove_extension
-from sickrage.helper.common import replace_extension, sanitize_filename
+class CommonTests(unittest.TestCase):
+    """
+    Test common
+    """
 
+    def test_http_code_description(self):
+        test_cases = {
+            None: None,
+            '': None,
+            '123': None,
+            '12.3': None,
+            '-123': None,
+            '-12.3': None,
+            '300': None,
+            0: None,
+            123: None,
+            12.3: None,
+            -123: None,
+            -12.3: None,
+            300: 'Multiple Choices',
+            451: '(Redirect, Unavailable For Legal Reasons)',
+            497: 'HTTP to HTTPS',
+            499: '(Client Closed Request, Token required)',
+            600: None,
+        }
+
+        unicode_test_cases = {
+            u'': None,
+            u'123': None,
+            u'12.3': None,
+            u'-123': None,
+            u'-12.3': None,
+            u'300': None,
+        }
+
+        for test in test_cases, unicode_test_cases:
+            for (http_code, result) in test.iteritems():
+                self.assertEqual(http_code_description(http_code), result)
 
-class CommonTests(TestCase):
     def test_is_sync_file(self):
+        """
+        Test is sync file
+        """
         sickbeard.SYNC_FILES = '!sync,lftp-pget-status,part'
 
-        tests = {
+        test_cases = {
             None: False,
             42: False,
             '': False,
-            u'': False,
             'filename': False,
-            u'filename': False,
             '.syncthingfilename': True,
-            u'.syncthingfilename': True,
             '.syncthing.filename': True,
-            u'.syncthing.filename': True,
             '.syncthing-filename': True,
-            u'.syncthing-filename': True,
             '.!sync': True,
-            u'.!sync': True,
             'file.!sync': True,
-            u'file.!sync': True,
             'file.!sync.ext': False,
-            u'file.!sync.ext': False,
             '.lftp-pget-status': True,
-            u'.lftp-pget-status': True,
             'file.lftp-pget-status': True,
-            u'file.lftp-pget-status': True,
             'file.lftp-pget-status.ext': False,
-            u'file.lftp-pget-status.ext': False,
             '.part': True,
-            u'.part': True,
             'file.part': True,
-            u'file.part': True,
             'file.part.ext': False,
+        }
+
+        unicode_test_cases = {
+            u'': False,
+            u'filename': False,
+            u'.syncthingfilename': True,
+            u'.syncthing.filename': True,
+            u'.syncthing-filename': True,
+            u'.!sync': True,
+            u'file.!sync': True,
+            u'file.!sync.ext': False,
+            u'.lftp-pget-status': True,
+            u'file.lftp-pget-status': True,
+            u'file.lftp-pget-status.ext': False,
+            u'.part': True,
+            u'file.part': True,
             u'file.part.ext': False,
         }
 
-        for (filename, result) in tests.iteritems():
-            self.assertEqual(is_sync_file(filename), result)
+        for tests in test_cases, unicode_test_cases:
+            for (filename, result) in tests.iteritems():
+                self.assertEqual(is_sync_file(filename), result)
 
     def test_is_torrent_or_nzb_file(self):
-        tests = {
+        """
+        Test is torrent or nzb file
+        """
+        test_cases = {
             None: False,
             42: False,
             '': False,
-            u'': False,
             'filename': False,
-            u'filename': False,
             '.nzb': True,
-            u'.nzb': True,
             'file.nzb': True,
-            u'file.nzb': True,
             'file.nzb.part': False,
-            u'file.nzb.part': False,
             '.torrent': True,
-            u'.torrent': True,
             'file.torrent': True,
-            u'file.torrent': True,
             'file.torrent.part': False,
+        }
+
+        unicode_test_cases = {
+            u'': False,
+            u'filename': False,
+            u'.nzb': True,
+            u'file.nzb': True,
+            u'file.nzb.part': False,
+            u'.torrent': True,
+            u'file.torrent': True,
             u'file.torrent.part': False,
         }
 
-        for (filename, result) in tests.iteritems():
-            self.assertEqual(is_torrent_or_nzb_file(filename), result)
+        for tests in test_cases, unicode_test_cases:
+            for (filename, result) in tests.iteritems():
+                self.assertEqual(is_torrent_or_nzb_file(filename), result)
 
     def test_pretty_file_size(self):
-        tests = {
+        """
+        Test pretty file size
+        """
+        test_cases = {
             None: '',
             '': '',
-            u'': '',
             '1024': '1.00 KB',
-            u'1024': '1.00 KB',
             '1024.5': '',
-            u'1024.5': '',
             -42.5: '-42.50 B',
             -42: '-42.00 B',
             0: '0.00 B',
@@ -122,102 +181,121 @@ class CommonTests(TestCase):
             2 ** 60: 2 ** 60,
         }
 
-        for (size, result) in tests.iteritems():
-            self.assertEqual(pretty_file_size(size), result)
+        unicode_test_cases = {
+            u'': '',
+            u'1024': '1.00 KB',
+            u'1024.5': '',
+        }
+
+        for tests in test_cases, unicode_test_cases:
+            for (size, result) in tests.iteritems():
+                self.assertEqual(pretty_file_size(size), result)
 
     def test_remove_extension(self):
-        tests = {
+        """
+        Test remove extension
+        """
+        test_cases = {
             None: None,
             42: 42,
             '': '',
-            u'': u'',
             '.': '.',
-            u'.': u'.',
             'filename': 'filename',
-            u'filename': u'filename',
             '.bashrc': '.bashrc',
-            u'.bashrc': u'.bashrc',
             '.nzb': '.nzb',
-            u'.nzb': u'.nzb',
             'file.nzb': 'file',
-            u'file.nzb': u'file',
             'file.name.nzb': 'file.name',
-            u'file.name.nzb': u'file.name',
             '.torrent': '.torrent',
-            u'.torrent': u'.torrent',
             'file.torrent': 'file',
-            u'file.torrent': u'file',
             'file.name.torrent': 'file.name',
-            u'file.name.torrent': u'file.name',
             '.avi': '.avi',
-            u'.avi': u'.avi',
             'file.avi': 'file',
-            u'file.avi': u'file',
             'file.name.avi': 'file.name',
-            u'file.name.avi': u'file.name',
         }
 
-        for (extension, result) in tests.iteritems():
-            self.assertEqual(remove_extension(extension), result)
+        unicode_test_cases = {
+            u'': u'',
+            u'.': u'.',
+            u'filename': u'filename',
+            u'.bashrc': u'.bashrc',
+            u'.nzb': u'.nzb',
+            u'file.nzb': u'file',
+            u'file.name.nzb': u'file.name',
+            u'.torrent': u'.torrent',
+            u'file.torrent': u'file',
+            u'file.name.torrent': u'file.name',
+            u'.avi': u'.avi',
+            u'file.avi': u'file',
+            u'file.name.avi': u'file.name',
+        }
+        for tests in test_cases, unicode_test_cases:
+            for (extension, result) in tests.iteritems():
+                self.assertEqual(remove_extension(extension), result)
 
     def test_replace_extension(self):
-        tests = {
+        """
+        Test replace extension
+        """
+        test_cases = {
             (None, None): None,
             (None, ''): None,
-            (None, u''): None,
             (42, None): 42,
             (42, ''): 42,
-            (42, u''): 42,
             ('', None): '',
             ('', ''): '',
+            ('.', None): '.',
+            ('.', ''): '.',
+            ('.', 'avi'): '.',
+            ('filename', None): 'filename',
+            ('filename', ''): 'filename',
+            ('filename', 'avi'): 'filename',
+            ('.bashrc', None): '.bashrc',
+            ('.bashrc', ''): '.bashrc',
+            ('.bashrc', 'avi'): '.bashrc',
+            ('file.mkv', None): 'file.None',
+            ('file.mkv', ''): 'file.',
+            ('file.mkv', 'avi'): 'file.avi',
+            ('file.name.mkv', None): 'file.name.None',
+            ('file.name.mkv', ''): 'file.name.',
+            ('file.name.mkv', 'avi'): 'file.name.avi',
+        }
+
+        unicode_test_cases = {
+            (None, u''): None,
+            (42, u''): 42,
             ('', u''): '',
             (u'', None): u'',
             (u'', ''): u'',
             (u'', u''): u'',
-            ('.', None): '.',
-            ('.', ''): '.',
             ('.', u''): '.',
-            ('.', 'avi'): '.',
             ('.', u'avi'): '.',
             (u'.', None): u'.',
             (u'.', ''): u'.',
             (u'.', u''): u'.',
             (u'.', 'avi'): u'.',
             (u'.', u'avi'): u'.',
-            ('filename', None): 'filename',
-            ('filename', ''): 'filename',
             ('filename', u''): 'filename',
-            ('filename', 'avi'): 'filename',
             ('filename', u'avi'): 'filename',
             (u'filename', None): u'filename',
             (u'filename', ''): u'filename',
             (u'filename', u''): u'filename',
             (u'filename', 'avi'): u'filename',
             (u'filename', u'avi'): u'filename',
-            ('.bashrc', None): '.bashrc',
-            ('.bashrc', ''): '.bashrc',
             ('.bashrc', u''): '.bashrc',
-            ('.bashrc', 'avi'): '.bashrc',
             ('.bashrc', u'avi'): '.bashrc',
             (u'.bashrc', None): u'.bashrc',
             (u'.bashrc', ''): u'.bashrc',
             (u'.bashrc', u''): u'.bashrc',
             (u'.bashrc', 'avi'): u'.bashrc',
             (u'.bashrc', u'avi'): u'.bashrc',
-            ('file.mkv', None): 'file.None',
-            ('file.mkv', ''): 'file.',
             ('file.mkv', u''): 'file.',
-            ('file.mkv', 'avi'): 'file.avi',
             ('file.mkv', u'avi'): 'file.avi',
             (u'file.mkv', None): u'file.None',
             (u'file.mkv', ''): u'file.',
             (u'file.mkv', u''): u'file.',
             (u'file.mkv', 'avi'): u'file.avi',
             (u'file.mkv', u'avi'): u'file.avi',
-            ('file.name.mkv', None): 'file.name.None',
-            ('file.name.mkv', ''): 'file.name.',
             ('file.name.mkv', u''): 'file.name.',
-            ('file.name.mkv', 'avi'): 'file.name.avi',
             ('file.name.mkv', u'avi'): 'file.name.avi',
             (u'file.name.mkv', None): u'file.name.None',
             (u'file.name.mkv', ''): u'file.name.',
@@ -226,33 +304,102 @@ class CommonTests(TestCase):
             (u'file.name.mkv', u'avi'): u'file.name.avi',
         }
 
-        for ((filename, extension), result) in tests.iteritems():
-            self.assertEqual(replace_extension(filename, extension), result)
+        for tests in test_cases, unicode_test_cases:
+            for ((filename, extension), result) in tests.iteritems():
+                self.assertEqual(replace_extension(filename, extension), result)
 
     def test_sanitize_filename(self):
-        tests = {
+        """
+        Test sanitize filename
+        """
+        test_cases = {
             None: '',
             42: '',
             '': '',
-            u'': u'',
             'filename': 'filename',
-            u'filename': u'filename',
             'fi\\le/na*me': 'fi-le-na-me',
-            u'fi\\le/na*me': u'fi-le-na-me',
             'fi:le"na<me': 'filename',
-            u'fi:le"na<me': u'filename',
             'fi>le|na?me': 'filename',
+            ' . file\u2122name. .': 'file-u2122name',  # pylint: disable=anomalous-unicode-escape-in-string
+        }
+
+        unicode_test_cases = {
+            u'': u'',
+            u'filename': u'filename',
+            u'fi\\le/na*me': u'fi-le-na-me',
+            u'fi:le"na<me': u'filename',
             u'fi>le|na?me': u'filename',
-            ' . file\u2122name. .': 'file-u2122name',
             u' . file\u2122name. .': u'filename',
         }
 
-        for (filename, result) in tests.iteritems():
-            self.assertEqual(sanitize_filename(filename), result)
+        for tests in test_cases, unicode_test_cases:
+            for (filename, result) in tests.iteritems():
+                self.assertEqual(sanitize_filename(filename), result)
+
+    def test_try_int(self):
+        """
+        Test try int
+        """
+        test_cases = {
+            None: 0,
+            '': 0,
+            '123': 123,
+            '-123': -123,
+            '12.3': 0,
+            '-12.3': 0,
+            0: 0,
+            123: 123,
+            -123: -123,
+            12.3: 12,
+            -12.3: -12,
+        }
+
+        unicode_test_cases = {
+            u'': 0,
+            u'123': 123,
+            u'-123': -123,
+            u'12.3': 0,
+            u'-12.3': 0,
+        }
+
+        for test in test_cases, unicode_test_cases:
+            for (candidate, result) in test.iteritems():
+                self.assertEqual(try_int(candidate), result)
+
+    def test_try_int_with_default(self):
+        """
+        Test try int
+        """
+        default_value = 42
+        test_cases = {
+            None: default_value,
+            '': default_value,
+            '123': 123,
+            '-123': -123,
+            '12.3': default_value,
+            '-12.3': default_value,
+            0: 0,
+            123: 123,
+            -123: -123,
+            12.3: 12,
+            -12.3: -12,
+        }
+
+        unicode_test_cases = {
+            u'': default_value,
+            u'123': 123,
+            u'-123': -123,
+            u'12.3': default_value,
+            u'-12.3': default_value,
+        }
+
+        for test in test_cases, unicode_test_cases:
+            for (candidate, result) in test.iteritems():
+                self.assertEqual(try_int(candidate, default_value), result)
 
 
 if __name__ == '__main__':
     print('=====> Testing %s' % __file__)
 
-    suite = TestLoader().loadTestsFromTestCase(CommonTests)
-    TextTestRunner(verbosity=2).run(suite)
+    SUITE = unittest.TestLoader().loadTestsFromTestCase(CommonTests)
+    unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/sickrage_tests/helper/quality_tests.py b/tests/sickrage_tests/helper/quality_tests.py
index fcb7ff3ca209e478862c4679607df12dd54e0f19..be0021fadabd078fac6f21b8a46a0a1af93a5278 100644
--- a/tests/sickrage_tests/helper/quality_tests.py
+++ b/tests/sickrage_tests/helper/quality_tests.py
@@ -1,6 +1,7 @@
+# coding=utf-8
 # This file is part of SickRage.
 #
-# URL: https://sickrage.github.io
+# URL: https://SickRage.GitHub.io
 # Git: https://github.com/SickRage/SickRage.git
 #
 # SickRage is free software: you can redistribute it and/or modify
@@ -16,19 +17,31 @@
 # You should have received a copy of the GNU General Public License
 # along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
 
+"""
+Test qualities
+"""
+
+from __future__ import print_function
+
 import os
 import sys
+import unittest
 
 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):
+class QualityTests(unittest.TestCase):
+    """
+    Test qualities
+    """
     def test_get_quality_string(self):
+        """
+        Test get quality string
+        """
         tests = {
             ANY: 'Any',
             HD: 'HD',
@@ -56,5 +69,5 @@ class QualityTests(TestCase):
 if __name__ == '__main__':
     print('=====> Testing %s' % __file__)
 
-    suite = TestLoader().loadTestsFromTestCase(QualityTests)
-    TextTestRunner(verbosity=2).run(suite)
+    SUITE = unittest.TestLoader().loadTestsFromTestCase(QualityTests)
+    unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/sickrage_tests/media/__init__.py b/tests/sickrage_tests/media/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..914787236e6e1997f2d376b4b8ea9ff8e45e852f
--- /dev/null
+++ b/tests/sickrage_tests/media/__init__.py
@@ -0,0 +1,27 @@
+# coding=utf-8
+"""
+Tests for SickRage media
+"""
+
+import unittest
+
+from generic_media_tests import GenericMediaTests
+from show_banner_tests import ShowBannerTests
+from show_fan_art_tests import ShowFanArtTests
+from show_network_logo_tests import ShowNetworkLogoTests
+from show_poster_tests import ShowPosterTests
+
+if __name__ == '__main__':
+    print('=====> Running all test in "sickrage_tests.media" <=====')
+
+    TEST_CLASSES = [
+        GenericMediaTests,
+        ShowBannerTests,
+        ShowFanArtTests,
+        ShowNetworkLogoTests,
+        ShowPosterTests,
+    ]
+
+    for test_class in TEST_CLASSES:
+        SUITE = unittest.TestLoader().loadTestsFromTestCase(test_class)
+        unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/sickrage_tests/media/generic_media_tests.py b/tests/sickrage_tests/media/generic_media_tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..172d191fb45bec9b6deff7f4b9f9b3ee57bd3199
--- /dev/null
+++ b/tests/sickrage_tests/media/generic_media_tests.py
@@ -0,0 +1,145 @@
+# coding=utf-8
+# 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/>.
+
+"""
+Test GenericMedia
+"""
+
+from __future__ import print_function
+
+import os
+import sys
+import unittest
+
+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 sickrage.media.GenericMedia import GenericMedia
+
+
+class GenericMediaTests(unittest.TestCase):
+    """
+    Test GenericMedia
+    """
+
+    def test___init__(self):
+        """
+        Test __init__
+        """
+        test_cases = {
+            (None, None): (0, 'normal'),
+            ('', None): (0, 'normal'),
+            ('123', None): (123, 'normal'),
+            ('12.3', None): (0, 'normal'),
+            (123, None): (123, 'normal'),
+            (12.3, None): (12, 'normal'),
+            (None, ''): (0, 'normal'),
+            ('', ''): (0, 'normal'),
+            ('123', ''): (123, 'normal'),
+            ('12.3', ''): (0, 'normal'),
+            (123, ''): (123, 'normal'),
+            (12.3, ''): (12, 'normal'),
+            (None, 'normal'): (0, 'normal'),
+            ('', 'normal'): (0, 'normal'),
+            ('123', 'normal'): (123, 'normal'),
+            ('12.3', 'normal'): (0, 'normal'),
+            (123, 'normal'): (123, 'normal'),
+            (12.3, 'normal'): (12, 'normal'),
+            (None, 'thumb'): (0, 'thumb'),
+            ('', 'thumb'): (0, 'thumb'),
+            ('123', 'thumb'): (123, 'thumb'),
+            ('12.3', 'thumb'): (0, 'thumb'),
+            (123, 'thumb'): (123, 'thumb'),
+            (12.3, 'thumb'): (12, 'thumb'),
+            (None, 'foo'): (0, 'normal'),
+            ('', 'foo'): (0, 'normal'),
+            ('123', 'foo'): (123, 'normal'),
+            ('12.3', 'foo'): (0, 'normal'),
+            (123, 'foo'): (123, 'normal'),
+            (12.3, 'foo'): (12, 'normal'),
+        }
+
+        unicode_test_cases = {
+            (u'', None): (0, 'normal'),
+            (u'123', None): (123, 'normal'),
+            (u'12.3', None): (0, 'normal'),
+            (None, u''): (0, 'normal'),
+            (u'', u''): (0, 'normal'),
+            (u'123', u''): (123, 'normal'),
+            (u'12.3', u''): (0, 'normal'),
+            (123, u''): (123, 'normal'),
+            (12.3, u''): (12, 'normal'),
+            (None, u'normal'): (0, 'normal'),
+            (u'', u'normal'): (0, 'normal'),
+            (u'123', u'normal'): (123, 'normal'),
+            (u'12.3', u'normal'): (0, 'normal'),
+            (123, u'normal'): (123, 'normal'),
+            (12.3, u'normal'): (12, 'normal'),
+            (None, u'thumb'): (0, 'thumb'),
+            (u'', u'thumb'): (0, 'thumb'),
+            (u'123', u'thumb'): (123, 'thumb'),
+            (u'12.3', u'thumb'): (0, 'thumb'),
+            (123, u'thumb'): (123, 'thumb'),
+            (12.3, u'thumb'): (12, 'thumb'),
+            (None, u'foo'): (0, 'normal'),
+            (u'', u'foo'): (0, 'normal'),
+            (u'123', u'foo'): (123, 'normal'),
+            (u'12.3', u'foo'): (0, 'normal'),
+            (123, u'foo'): (123, 'normal'),
+            (12.3, u'foo'): (12, 'normal'),
+        }
+
+        for test in test_cases, unicode_test_cases:
+            for ((indexer_id, media_format), (expected_indexer_id, expected_media_format)) in test.iteritems():
+                generic_media = GenericMedia(indexer_id, media_format)
+
+                self.assertEqual(generic_media.indexer_id, expected_indexer_id)
+                self.assertEqual(generic_media.media_format, expected_media_format)
+
+    def test_get_default_media_name(self):
+        """
+        Test get_default_media_name
+        """
+
+        self.assertEqual(GenericMedia(0, '').get_default_media_name(), '')
+
+    def test_get_media_path(self):
+        """
+        Test get_media_path
+        """
+
+        self.assertEqual(GenericMedia(0, '').get_media_path(), '')
+
+    def test_get_media_root(self):
+        """
+        Test get_media_root
+        """
+
+        sickbeard.PROG_DIR = '/home/SickRage/'
+
+        self.assertEqual(GenericMedia.get_media_root(), '/home/SickRage/gui/slick')
+
+
+if __name__ == '__main__':
+    print('=====> Testing %s' % __file__)
+
+    SUITE = unittest.TestLoader().loadTestsFromTestCase(GenericMediaTests)
+    unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/sickrage_tests/media/show_banner_tests.py b/tests/sickrage_tests/media/show_banner_tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..68233b51e015edd236746bfbfba8f2d920739f70
--- /dev/null
+++ b/tests/sickrage_tests/media/show_banner_tests.py
@@ -0,0 +1,53 @@
+# coding=utf-8
+# 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/>.
+
+"""
+Test ShowBanner
+"""
+
+from generic_media_tests import GenericMediaTests
+
+import os
+import sys
+import unittest
+
+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.media.ShowBanner import ShowBanner
+
+
+class ShowBannerTests(GenericMediaTests):
+    """
+    Test ShowBanner
+    """
+
+    def test_get_default_media_name(self):
+        """
+        Test get_default_media_name
+        """
+
+        self.assertEqual(ShowBanner(0, '').get_default_media_name(), 'banner.png')
+
+
+if __name__ == '__main__':
+    print('=====> Testing %s' % __file__)
+
+    SUITE = unittest.TestLoader().loadTestsFromTestCase(ShowBannerTests)
+    unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/sickrage_tests/media/show_fan_art_tests.py b/tests/sickrage_tests/media/show_fan_art_tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..6e5be0582097ab9f9a9711894ceedd86d0e2053c
--- /dev/null
+++ b/tests/sickrage_tests/media/show_fan_art_tests.py
@@ -0,0 +1,53 @@
+# coding=utf-8
+# 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/>.
+
+"""
+Test ShowFanArt
+"""
+
+from generic_media_tests import GenericMediaTests
+
+import os
+import sys
+import unittest
+
+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.media.ShowFanArt import ShowFanArt
+
+
+class ShowFanArtTests(GenericMediaTests):
+    """
+    Test ShowFanArt
+    """
+
+    def test_get_default_media_name(self):
+        """
+        Test get_default_media_name
+        """
+
+        self.assertEqual(ShowFanArt(0, '').get_default_media_name(), 'fanart.png')
+
+
+if __name__ == '__main__':
+    print('=====> Testing %s' % __file__)
+
+    SUITE = unittest.TestLoader().loadTestsFromTestCase(ShowFanArtTests)
+    unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/sickrage_tests/media/show_network_logo_tests.py b/tests/sickrage_tests/media/show_network_logo_tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..0cb3ee9b3e6e2f1cb33886aa3cbe93c1d18c2c92
--- /dev/null
+++ b/tests/sickrage_tests/media/show_network_logo_tests.py
@@ -0,0 +1,53 @@
+# coding=utf-8
+# 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/>.
+
+"""
+Test ShowNetworkLogo
+"""
+
+from generic_media_tests import GenericMediaTests
+
+import os
+import sys
+import unittest
+
+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.media.ShowNetworkLogo import ShowNetworkLogo
+
+
+class ShowNetworkLogoTests(GenericMediaTests):
+    """
+    Test ShowNetworkLogo
+    """
+
+    def test_get_default_media_name(self):
+        """
+        Test get_default_media_name
+        """
+
+        self.assertEqual(ShowNetworkLogo(0, '').get_default_media_name(), 'network/nonetwork.png')
+
+
+if __name__ == '__main__':
+    print('=====> Testing %s' % __file__)
+
+    SUITE = unittest.TestLoader().loadTestsFromTestCase(ShowNetworkLogoTests)
+    unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/sickrage_tests/media/show_poster_tests.py b/tests/sickrage_tests/media/show_poster_tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..16a8a8bc21eb7e2d24760c5d7a1603d9496f56d7
--- /dev/null
+++ b/tests/sickrage_tests/media/show_poster_tests.py
@@ -0,0 +1,53 @@
+# coding=utf-8
+# 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/>.
+
+"""
+Test ShowPoster
+"""
+
+from generic_media_tests import GenericMediaTests
+
+import os
+import sys
+import unittest
+
+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.media.ShowPoster import ShowPoster
+
+
+class ShowPosterTests(GenericMediaTests):
+    """
+    Test ShowPoster
+    """
+
+    def test_get_default_media_name(self):
+        """
+        Test get_default_media_name
+        """
+
+        self.assertEqual(ShowPoster(0, '').get_default_media_name(), 'poster.png')
+
+
+if __name__ == '__main__':
+    print('=====> Testing %s' % __file__)
+
+    SUITE = unittest.TestLoader().loadTestsFromTestCase(ShowPosterTests)
+    unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/sickrage_tests/show/__init__.py b/tests/sickrage_tests/show/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..906cca08b7eb2dc00af58e2bec32302db2f55cff 100644
--- a/tests/sickrage_tests/show/__init__.py
+++ b/tests/sickrage_tests/show/__init__.py
@@ -0,0 +1,23 @@
+# coding=utf-8
+"""
+Tests for SickRage show
+"""
+
+import unittest
+
+from coming_episodes_tests import ComingEpisodesTests
+from history_tests import HistoryTests
+from show_tests import ShowTests
+
+if __name__ == '__main__':
+    print('=====> Running all test in "sickrage_tests.show" <=====')
+
+    TEST_CLASSES = [
+        ComingEpisodesTests,
+        HistoryTests,
+        ShowTests,
+    ]
+
+    for test_class in TEST_CLASSES:
+        SUITE = unittest.TestLoader().loadTestsFromTestCase(test_class)
+        unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/sickrage_tests/show/coming_episodes_tests.py b/tests/sickrage_tests/show/coming_episodes_tests.py
index 0bcf26f7fd68710d8a7a33506a972eebbb87b1c9..387f90839a1c9b6b65ee36a5594a8ced99ea5a3b 100644
--- a/tests/sickrage_tests/show/coming_episodes_tests.py
+++ b/tests/sickrage_tests/show/coming_episodes_tests.py
@@ -1,6 +1,7 @@
+# coding=utf-8
 # This file is part of SickRage.
 #
-# URL: https://sickrage.github.io
+# URL: https://SickRage.GitHub.io
 # Git: https://github.com/SickRage/SickRage.git
 #
 # SickRage is free software: you can redistribute it and/or modify
@@ -16,18 +17,32 @@
 # You should have received a copy of the GNU General Public License
 # along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
 
+"""
+Test coming episodes
+"""
+
+# pylint: disable=line-too-long
+
+from __future__ import print_function
+
 import os
 import sys
+import unittest
 
 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):
+class ComingEpisodesTests(unittest.TestCase):
+    """
+    Test comping episodes
+    """
     def test_get_categories(self):
+        """
+        Test get categories
+        """
         categories_list = [
             None, [], ['A', 'B'], [u'A', u'B'], '', 'A|B', u'A|B',
         ]
@@ -36,14 +51,17 @@ class ComingEpisodesTests(TestCase):
         ]
 
         self.assertEqual(
-                len(categories_list), len(results_list),
-                'Number of parameters (%d) and results (%d) does not match' % (len(categories_list), len(results_list))
+            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])
+            self.assertEqual(ComingEpisodes._get_categories(categories), results_list[index])  # pylint: disable=protected-access
 
     def test_get_categories_map(self):
+        """
+        Test get categories map
+        """
         categories_list = [
             None, [], ['A', 'B'], [u'A', u'B']
         ]
@@ -52,40 +70,47 @@ class ComingEpisodesTests(TestCase):
         ]
 
         self.assertEqual(
-                len(categories_list), len(results_list),
-                'Number of parameters (%d) and results (%d) does not match' % (len(categories_list), len(results_list))
+            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])
+            self.assertEqual(ComingEpisodes._get_categories_map(categories), results_list[index])  # pylint: disable=protected-access
 
     def test_get_sort(self):
-        tests = {
+        """
+        Test get sort
+        """
+        test_cases = {
             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',
+        }
+
+        unicode_test_cases = {
+            u'': 'date',
+            u'wrong': 'date',
+            u'date': 'date',
+            u'Date': 'date',
+            u'network': 'network',
+            u'NetWork': 'network',
+            u'show': 'show',
             u'Show': 'show',
         }
 
-        for (sort, result) in tests.iteritems():
-            self.assertEqual(ComingEpisodes._get_sort(sort), result)
+        for tests in test_cases, unicode_test_cases:
+            for (sort, result) in tests.iteritems():
+                self.assertEqual(ComingEpisodes._get_sort(sort), result)  # pylint: disable=protected-access
 
 
 if __name__ == '__main__':
     print('=====> Testing %s' % __file__)
 
-    suite = TestLoader().loadTestsFromTestCase(ComingEpisodesTests)
-    TextTestRunner(verbosity=2).run(suite)
+    SUITE = unittest.TestLoader().loadTestsFromTestCase(ComingEpisodesTests)
+    unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/sickrage_tests/show/history_tests.py b/tests/sickrage_tests/show/history_tests.py
index 0b8c28738f80477e7e267d98b153fe8a4a0e9045..74aac381b2ffbfe2398e934325d66549d383bb41 100644
--- a/tests/sickrage_tests/show/history_tests.py
+++ b/tests/sickrage_tests/show/history_tests.py
@@ -1,6 +1,7 @@
+# coding=utf-8
 # This file is part of SickRage.
 #
-# URL: https://sickrage.github.io
+# URL: https://SickRage.GitHub.io
 # Git: https://github.com/SickRage/SickRage.git
 #
 # SickRage is free software: you can redistribute it and/or modify
@@ -16,65 +17,88 @@
 # You should have received a copy of the GNU General Public License
 # along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
 
+"""
+Test history
+"""
+
+from __future__ import print_function
+
 import os
 import sys
+import unittest
 
 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):
+class HistoryTests(unittest.TestCase):
+    """
+    Test history
+    """
     def test_get_actions(self):
-        tests = {
+        """
+        Test get actions
+        """
+        test_cases = {
             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,
+        }
+
+        unicode_test_cases = {
+            u'': [],
+            u'wrong': [],
+            u'downloaded': Quality.DOWNLOADED,
+            u'Downloaded': Quality.DOWNLOADED,
+            u'snatched': Quality.SNATCHED,
             u'Snatched': Quality.SNATCHED,
         }
 
-        for (action, result) in tests.iteritems():
-            self.assertEqual(History._get_actions(action), result)
+        for tests in test_cases, unicode_test_cases:
+            for (action, result) in tests.iteritems():
+                self.assertEqual(History._get_actions(action), result)  # pylint: disable=protected-access
 
     def test_get_limit(self):
-        tests = {
+        """
+        Test get limit
+        """
+        test_cases = {
             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)
+        unicode_test_cases = {
+            u'': 0,
+            u'0': 0,
+            u'5': 5,
+            u'-5': 0,
+            u'1.5': 0,
+            u'-1.5': 0,
+        }
+
+        for tests in test_cases, unicode_test_cases:
+            for (action, result) in tests.iteritems():
+                self.assertEqual(History._get_limit(action), result)  # pylint: disable=protected-access
 
 
 if __name__ == '__main__':
     print('=====> Testing %s' % __file__)
 
-    suite = TestLoader().loadTestsFromTestCase(HistoryTests)
-    TextTestRunner(verbosity=2).run(suite)
+    SUITE = unittest.TestLoader().loadTestsFromTestCase(HistoryTests)
+    unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/sickrage_tests/show/show_tests.py b/tests/sickrage_tests/show/show_tests.py
index 1e6044a915e752e2582808fcf7477ed84cbbf6bb..ab53550f74df3e19ee60403ec98af27044d6c335 100644
--- a/tests/sickrage_tests/show/show_tests.py
+++ b/tests/sickrage_tests/show/show_tests.py
@@ -1,6 +1,7 @@
+# coding=utf-8
 # This file is part of SickRage.
 #
-# URL: https://sickrage.github.io
+# URL: https://SickRage.GitHub.io
 # Git: https://github.com/SickRage/SickRage.git
 #
 # SickRage is free software: you can redistribute it and/or modify
@@ -16,8 +17,17 @@
 # You should have received a copy of the GNU General Public License
 # along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
 
+"""
+Test shows
+"""
+
+# pylint: disable=line-too-long
+
+from __future__ import print_function
+
 import os
 import sys
+import unittest
 
 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__), '../../..')))
@@ -26,12 +36,63 @@ import sickbeard
 
 from sickbeard.common import Quality
 from sickbeard.tv import TVShow
+from sickrage.helper.exceptions import MultipleShowObjectsException
 from sickrage.show.Show import Show
-from unittest import TestCase, TestLoader, TextTestRunner
 
 
-class ShowTests(TestCase):
+class ShowTests(unittest.TestCase):
+    """
+    Test shows
+    """
+
+    def test_find(self):
+        """
+        Test find
+        """
+        sickbeard.QUALITY_DEFAULT = Quality.FULLHDTV
+
+        show123 = TestTVShow(0, 123)
+        show456 = TestTVShow(0, 456)
+        show789 = TestTVShow(0, 789)
+        shows = [show123, show456, show789]
+        shows_duplicate = shows + shows
+
+        test_cases = {
+            (False, None): None,
+            (False, ''): None,
+            (False, '123'): None,
+            (False, 123): None,
+            (False, 12.3): None,
+            (True, None): None,
+            (True, ''): None,
+            (True, '123'): None,
+            (True, 123): show123,
+            (True, 12.3): None,
+            (True, 456): show456,
+            (True, 789): show789,
+        }
+
+        unicode_test_cases = {
+            (False, u''): None,
+            (False, u'123'): None,
+            (True, u''): None,
+            (True, u'123'): None,
+        }
+
+        for tests in test_cases, unicode_test_cases:
+            for ((use_shows, indexer_id), result) in tests.iteritems():
+                if use_shows:
+                    self.assertEqual(Show.find(shows, indexer_id), result)
+                else:
+                    self.assertEqual(Show.find(None, indexer_id), result)
+
+        with self.assertRaises(MultipleShowObjectsException):
+            Show.find(shows_duplicate, 456)
+
     def test_validate_indexer_id(self):
+        """
+        Test validate indexer id
+        """
         sickbeard.QUALITY_DEFAULT = Quality.FULLHDTV
 
         show123 = TestTVShow(0, 123)
@@ -56,28 +117,33 @@ class ShowTests(TestCase):
         ]
 
         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))
+            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])
+            self.assertEqual(Show._validate_indexer_id(indexer_id), results_list[index])  # pylint: disable=protected-access
 
 
 class TestTVShow(TVShow):
     """
-    A test ``TVShow`` object that do not need DB access.
+    A test `TVShow` object that does not need DB access.
     """
 
     def __init__(self, indexer, indexer_id):
         super(TestTVShow, self).__init__(indexer, indexer_id)
 
     def loadFromDB(self, skip_nfo=False):
+        """
+        Override TVShow.loadFromDB to avoid DB access during testing
+
+        :param skip_nfo: ...not used
+        """
         pass
 
 
 if __name__ == '__main__':
     print('=====> Testing %s' % __file__)
 
-    suite = TestLoader().loadTestsFromTestCase(ShowTests)
-    TextTestRunner(verbosity=2).run(suite)
+    SUITE = unittest.TestLoader().loadTestsFromTestCase(ShowTests)
+    unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/sickrage_tests/system/__init__.py b/tests/sickrage_tests/system/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..816646d0bbaf1e058d9fc9ecdf4e8c82f482e576 100644
--- a/tests/sickrage_tests/system/__init__.py
+++ b/tests/sickrage_tests/system/__init__.py
@@ -0,0 +1,21 @@
+# coding=utf-8
+"""
+Tests for SickRage system
+"""
+
+import unittest
+
+from restart_tests import RestartTests
+from shutdown_tests import ShutdownTests
+
+if __name__ == '__main__':
+    print('=====> Running all test in "sickrage_tests.system" <=====')
+
+    TEST_CLASSES = [
+        RestartTests,
+        ShutdownTests,
+    ]
+
+    for test_class in TEST_CLASSES:
+        SUITE = unittest.TestLoader().loadTestsFromTestCase(test_class)
+        unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/sickrage_tests/system/restart_tests.py b/tests/sickrage_tests/system/restart_tests.py
index 70da92d250b03c43a9b5a2c96bd716704656fcf0..e0b6b6c47cf4e80a0c1ebf8824d7d73d250dfe29 100644
--- a/tests/sickrage_tests/system/restart_tests.py
+++ b/tests/sickrage_tests/system/restart_tests.py
@@ -1,6 +1,7 @@
+# coding=utf-8
 # This file is part of SickRage.
 #
-# URL: https://sickrage.github.io
+# URL: https://SickRage.GitHub.io
 # Git: https://github.com/SickRage/SickRage.git
 #
 # SickRage is free software: you can redistribute it and/or modify
@@ -16,42 +17,57 @@
 # You should have received a copy of the GNU General Public License
 # along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
 
+"""
+Test restart
+"""
+
+from __future__ import print_function
+
 import os
 import sys
+import unittest
 
 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):
+class RestartTests(unittest.TestCase):
+    """
+    Test restart
+    """
     def test_restart(self):
+        """
+        Test restart
+        """
         sickbeard.PID = 123456
         sickbeard.events = Events(None)
 
-        tests = {
+        test_cases = {
             0: False,
             '0': False,
-            u'0': False,
             123: False,
             '123': False,
-            u'123': False,
             123456: True,
             '123456': True,
+        }
+
+        unicode_test_cases = {
+            u'0': False,
+            u'123': False,
             u'123456': True,
         }
 
-        for (pid, result) in tests.iteritems():
-            self.assertEqual(Restart.restart(pid), result)
+        for tests in test_cases, unicode_test_cases:
+            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)
+    SUITE = unittest.TestLoader().loadTestsFromTestCase(RestartTests)
+    unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/sickrage_tests/system/shutdown_tests.py b/tests/sickrage_tests/system/shutdown_tests.py
index 6c84b64cc17bff0b0b800406ee2c27c20e4d9002..7b425c0d4669104fe86c66de23018064f4b721d7 100644
--- a/tests/sickrage_tests/system/shutdown_tests.py
+++ b/tests/sickrage_tests/system/shutdown_tests.py
@@ -1,6 +1,7 @@
+# coding=utf-8
 # This file is part of SickRage.
 #
-# URL: https://sickrage.github.io
+# URL: https://SickRage.GitHub.io
 # Git: https://github.com/SickRage/SickRage.git
 #
 # SickRage is free software: you can redistribute it and/or modify
@@ -16,42 +17,57 @@
 # You should have received a copy of the GNU General Public License
 # along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
 
+"""
+Test shutdown
+"""
+
+from __future__ import print_function
+
 import os
 import sys
+import unittest
 
 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):
+class ShutdownTests(unittest.TestCase):
+    """
+    Test shutdown
+    """
     def test_shutdown(self):
+        """
+        Test shutdown
+        """
         sickbeard.PID = 123456
         sickbeard.events = Events(None)
 
-        tests = {
+        test_cases = {
             0: False,
             '0': False,
-            u'0': False,
             123: False,
             '123': False,
-            u'123': False,
             123456: True,
             '123456': True,
+        }
+
+        unicode_test_cases = {
+            u'0': False,
+            u'123': False,
             u'123456': True,
         }
 
-        for (pid, result) in tests.iteritems():
-            self.assertEqual(Shutdown.stop(pid), result)
+        for tests in test_cases, unicode_test_cases:
+            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)
+    SUITE = unittest.TestLoader().loadTestsFromTestCase(ShutdownTests)
+    unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/snatch_tests.py b/tests/snatch_tests.py
index d22926594e1c73c0b25c84c8192e876af4f12aed..fee4517c031d207aa00c30b6d0346d9a87c46511 100644
--- a/tests/snatch_tests.py
+++ b/tests/snatch_tests.py
@@ -17,27 +17,39 @@
 # 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__), '..')))
+# pylint: disable=line-too-long
 
-import random
+"""
+Test snatching
+"""
+
+import os.path
+import sys
 import unittest
 
-import test_lib as test
+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.search as search
-import sickbeard
 from sickbeard.tv import TVEpisode, TVShow
-import sickbeard.common as c
+import sickbeard
+import sickbeard.search as search
+import sickbeard.common as common
+import tests.test_lib as test
 
-tests = {"Dexter": {"a": 1, "q": c.HD, "s": 5, "e": [7], "b": 'Dexter.S05E07.720p.BluRay.X264-REWARD', "i": ['Dexter.S05E07.720p.BluRay.X264-REWARD', 'Dexter.S05E07.720p.X264-REWARD']},
-         "House": {"a": 1, "q": c.HD, "s": 4, "e": [5], "b": 'House.4x5.720p.BluRay.X264-REWARD', "i": ['Dexter.S05E04.720p.X264-REWARD', 'House.4x5.720p.BluRay.X264-REWARD']},
-         "Hells Kitchen": {"a": 1, "q": c.SD, "s": 6, "e": [14, 15], "b": 'Hells.Kitchen.s6e14e15.HDTV.XviD-ASAP', "i": ['Hells.Kitchen.S06E14.HDTV.XviD-ASAP', 'Hells.Kitchen.6x14.HDTV.XviD-ASAP', 'Hells.Kitchen.s6e14e15.HDTV.XviD-ASAP']}
-       }
+TESTS = {
+    "Dexter": {"a": 1, "q": common.HD, "s": 5, "e": [7], "b": 'Dexter.S05E07.720p.BluRay.X264-REWARD', "i": ['Dexter.S05E07.720p.BluRay.X264-REWARD', 'Dexter.S05E07.720p.X264-REWARD']},
+    "House": {"a": 1, "q": common.HD, "s": 4, "e": [5], "b": 'House.4x5.720p.BluRay.X264-REWARD', "i": ['Dexter.S05E04.720p.X264-REWARD', 'House.4x5.720p.BluRay.X264-REWARD']},
+    "Hells Kitchen": {"a": 1, "q": common.SD, "s": 6, "e": [14, 15], "b": 'Hells.Kitchen.s6e14e15.HDTV.XviD-ASAP', "i": ['Hells.Kitchen.S06E14.HDTV.XviD-ASAP', 'Hells.Kitchen.6x14.HDTV.XviD-ASAP', 'Hells.Kitchen.s6e14e15.HDTV.XviD-ASAP']}
+}
 
 
 def _create_fake_xml(items):
+    """
+    Create fake xml
+
+    :param items:
+    :return:
+    """
     xml = '<?xml version="1.0" encoding="UTF-8" ?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:newznab="http://www.newznab.com/DTD/2010/feeds/attributes/" encoding="utf-8"><channel>'
     for item in items:
         xml += '<item><title>' + item + '</title>\n'
@@ -45,49 +57,83 @@ def _create_fake_xml(items):
     xml += '</channel></rss>'
     return xml
 
-
-searchItems = []
+# pylint: disable=invalid-name
+search_items = []
 
 
 class SearchTest(test.SickbeardTestDBCase):
-
-    def _fake_getURL(self, url, headers=None):
-        global searchItems
-        return _create_fake_xml(searchItems)
-
-    def _fake_isActive(self):
+    """
+    Perform search tests
+    """
+
+    @staticmethod
+    def _fake_get_url(url, headers=None):
+        """
+        Fake getting a url
+
+        :param url:
+        :param headers:
+        :return:
+        """
+        _ = url, headers
+        return _create_fake_xml(search_items)
+
+    @staticmethod
+    def _fake_is_active():
+        """
+        Fake is active
+        """
         return True
 
     def __init__(self, something):
+        """
+        Initialize tests
+
+        :param something:
+        :return:
+        """
+
         for provider in sickbeard.providers.sortedProviderList():
-            provider.getURL = self._fake_getURL
-            #provider.isActive = self._fake_isActive
+            provider.getURL = self._fake_get_url
+            # provider.isActive = self._fake_is_active
 
         super(SearchTest, self).__init__(something)
 
 
-def test_generator(tvdbdid, show_name, curData, forceSearch):
-
-    def test(self):
-        global searchItems
-        searchItems = curData["i"]
-        show = TVShow(1, tvdbdid)
+def test_generator(tvdb_id, show_name, cur_data, force_search):
+    """
+    Generate tests
+
+    :param tvdb_id:
+    :param show_name:
+    :param cur_data:
+    :param force_search:
+    :return:
+    """
+    def do_test():
+        """
+        Test to perform
+        """
+        global search_items  # pylint: disable=global-statement
+        search_items = cur_data["i"]
+        show = TVShow(1, tvdb_id)
         show.name = show_name
-        show.quality = curData["q"]
+        show.quality = cur_data["q"]
         show.saveToDB()
         sickbeard.showList.append(show)
         episode = None
 
-        for epNumber in curData["e"]:
-            episode = TVEpisode(show, curData["s"], epNumber)
-            episode.status = c.WANTED
+        for epNumber in cur_data["e"]:
+            episode = TVEpisode(show, cur_data["s"], epNumber)
+            episode.status = common.WANTED
             episode.saveToDB()
 
-        bestResult = search.searchProviders(show, episode.episode, forceSearch)
-        if not bestResult:
-            self.assertEqual(curData["b"], bestResult)
-        self.assertEqual(curData["b"], bestResult.name) #first is expected, second is choosen one
-    return test
+        best_result = search.searchProviders(show, episode.episode, force_search)
+        if not best_result:
+            assert cur_data["b"] == best_result
+        # pylint: disable=no-member
+        assert cur_data["b"] == best_result.name  # first is expected, second is chosen one
+    return do_test
 
 if __name__ == '__main__':
     print "=================="
@@ -95,20 +141,20 @@ if __name__ == '__main__':
     print "=================="
     print "######################################################################"
     # create the test methods
-    tvdbdid = 1
+    cur_tvdb_id = 1
     for forceSearch in (True, False):
-        for name, curData in tests.items():
-            if not curData["a"]:
+        for name, data in TESTS.items():
+            if not data["a"]:
                 continue
-            fname = name.replace(' ', '_')
+            filename = name.replace(' ', '_')
             if forceSearch:
-                test_name = 'test_manual_%s_%s' % (fname, tvdbdid)
+                test_name = 'test_manual_%s_%s' % (filename, cur_tvdb_id)
             else:
-                test_name = 'test_%s_%s' % (fname, tvdbdid)
+                test_name = 'test_%s_%s' % (filename, cur_tvdb_id)
 
-            test = test_generator(tvdbdid, name, curData, forceSearch)
+            test = test_generator(cur_tvdb_id, name, data, forceSearch)
             setattr(SearchTest, test_name, test)
-            tvdbdid += 1
+            cur_tvdb_id += 1
 
     suite = unittest.TestLoader().loadTestsFromTestCase(SearchTest)
     unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/tests/ssl_sni_tests.py b/tests/ssl_sni_tests.py
index 59d58f6557c3c62b26562dac070ee829f1ba9ad0..b4c6e0b380c2e31ffb67be25704b479ab2dbf133 100644
--- a/tests/ssl_sni_tests.py
+++ b/tests/ssl_sni_tests.py
@@ -17,23 +17,38 @@
 # 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__), '..')))
+# pylint: disable=line-too-long
 
+"""
+Test SNI and SSL
+"""
+
+import os.path
+import sys
 import unittest
-import requests
 
+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 import ex
+import certifi  # pylint: disable=import-error
+import requests  # pylint: disable=import-error
 import sickbeard.providers as providers
-import certifi
-from sickrage.helper.exceptions import ex
 
 
-class SNI_Tests(unittest.TestCase):
+class SniTests(unittest.TestCase):
+    """
+    Test SNI
+    """
     self_signed_cert_providers = ["Womble's Index", "Libertalia"]
-    def test_SNI_URLS(self):
+
+    def test_sni_urls(self):
+        """
+        Test SNI urls
+        :return:
+        """
         print ''
-        #Just checking all providers - we should make this error on non-existent urls.
+        # Just checking all providers - we should make this error on non-existent urls.
         for provider in [provider for provider in providers.makeProviderList() if provider.name not in self.self_signed_cert_providers]:
             print 'Checking %s' % provider.name
             try:
@@ -45,10 +60,10 @@ class SNI_Tests(unittest.TestCase):
                     print 'SSLError on %s: %s' % (provider.name, ex(error.message))
                     raise
                 else:
-                    print  'Cannot verify certificate for %s' % provider.name
-            except Exception:
+                    print 'Cannot verify certificate for %s' % provider.name
+            except Exception:  # pylint: disable=broad-except
                 pass
 
 if __name__ == "__main__":
-    suite = unittest.TestLoader().loadTestsFromTestCase(SNI_Tests)
-    unittest.TextTestRunner(verbosity=2).run(suite)
+    SUITE = unittest.TestLoader().loadTestsFromTestCase(SniTests)
+    unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/test_lib.py b/tests/test_lib.py
index 86a12da9d5751125cad468a801835afdb8f801c7..caf136fa5951b669e5400a2bd142980d7f6961d0 100644
--- a/tests/test_lib.py
+++ b/tests/test_lib.py
@@ -40,27 +40,23 @@ Classes:
     TestCacheDBConnection
 """
 
-import sys
 import os.path
-import unittest
 import shutil
+import sys
+import unittest
 
 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__), '..')))
 
-# pylint: disable=F0401
-# Unable to import
-import shutil_custom
-
-import sickbeard
+from configobj import ConfigObj
 from sickbeard import db, providers
-from sickbeard.databases import mainDB
-from sickbeard.databases import cache_db, failed_db
+from sickbeard.databases import cache_db, failed_db, mainDB
 from sickbeard.tv import TVEpisode
+import shutil_custom  # pylint: disable=import-error
+import sickbeard
 
 # pylint: disable=F0401
 # Unable to import
-from configobj import ConfigObj
 
 
 shutil.copyfile = shutil_custom.copyfile_custom
@@ -159,16 +155,15 @@ def _dummy_save_config():
 mainDB.sickbeard.save_config = _dummy_save_config
 
 
-# pylint: disable=W0613
-# Unused arguments
 def _fake_specify_ep(self, season, episode):
     """
     Override contact to TVDB indexer.
 
-    :param self:
+    :param self: ...not used
     :param season: Season to search for  ...not used
     :param episode: Episode to search for  ...not used
     """
+    _ = self, season, episode  # throw away unused variables
     pass
 
 # the real one tries to contact TVDB just stop it from getting more info on the ep
diff --git a/tests/torrent_tests.py b/tests/torrent_tests.py
index 7ebe155953aefee44b452c1e004f53ead971c170..62fd6a970f78e209c174b93f7759323e70fc2cb6 100644
--- a/tests/torrent_tests.py
+++ b/tests/torrent_tests.py
@@ -17,24 +17,32 @@
 # You should have received a copy of the GNU General Public License
 # along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
 
+"""
+Test torrents
+"""
 
-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__), '..')))
+# pylint: disable=line-too-long
 
+import os.path
+import sys
 import unittest
 
-import urlparse
-import tests.test_lib as test
+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 bs4 import BeautifulSoup
 from sickbeard.helpers import getURL
+from sickbeard.providers.bitcannon import BitCannonProvider
 from sickbeard.tv import TVEpisode, TVShow
-import requests
+import requests  # pylint: disable=import-error
+import tests.test_lib as test
+import urlparse
 
-from sickbeard.providers.bitcannon import BitCannonProvider
 
 class TorrentBasicTests(test.SickbeardTestDBCase):
-
+    """
+    Test torrents
+    """
     @classmethod
     def setUpClass(cls):
         cls.shows = []
@@ -50,18 +58,26 @@ class TorrentBasicTests(test.SickbeardTestDBCase):
         cls.shows.append(show)
 
     def test_bitcannon(self):
+        """
+        Test bitcannon
+        """
         bitcannon = BitCannonProvider()
         bitcannon.custom_url = ""        # true testing requires a valid URL here (e.g., "http://localhost:3000/")
         bitcannon.api_key = ""
 
         if len(bitcannon.custom_url) > 0:
+            # pylint: disable=protected-access
             search_strings_list = bitcannon._get_episode_search_strings(self.shows[0].episodes[0])  # [{'Episode': ['Italian Works S05E10']}]
             for search_strings in search_strings_list:
-                bitcannon._doSearch(search_strings)   # {'Episode': ['Italian Works S05E10']}
+                bitcannon._doSearch(search_strings)   # {'Episode': ['Italian Works S05E10']} # pylint: disable=protected-access
 
         return True
 
-    def test_search(self):
+    @staticmethod
+    def test_search():  # pylint: disable=too-many-locals
+        """
+        Test searching
+        """
         url = 'http://kickass.to/'
         search_url = 'http://kickass.to/usearch/American%20Dad%21%20S08%20-S08E%20category%3Atv/?field=seeders&sorder=desc'
 
@@ -77,23 +93,23 @@ class TorrentBasicTests(test.SickbeardTestDBCase):
         # cleanup memory
         soup.clear(True)
 
-        #Continue only if one Release is found
+        # Continue only if one Release is found
         if len(torrent_rows) < 2:
             print "The data returned does not contain any torrents"
             return
 
-        for tr in torrent_rows[1:]:
-
+        for row in torrent_rows[1:]:
             try:
-                link = urlparse.urljoin(url, (tr.find('div', {'class': 'torrentname'}).find_all('a')[1])['href'])
-                _id = tr.get('id')[-7:]
-                title = (tr.find('div', {'class': 'torrentname'}).find_all('a')[1]).text \
-                    or (tr.find('div', {'class': 'torrentname'}).find_all('a')[2]).text
-                url = tr.find('a', 'imagnet')['href']
-                verified = True if tr.find('a', 'iverify') else False
-                trusted = True if tr.find('img', {'alt': 'verified'}) else False
-                seeders = int(tr.find_all('td')[-2].text)
-                leechers = int(tr.find_all('td')[-1].text)
+                link = urlparse.urljoin(url, (row.find('div', {'class': 'torrentname'}).find_all('a')[1])['href'])
+                _id = row.get('id')[-7:]
+                title = (row.find('div', {'class': 'torrentname'}).find_all('a')[1]).text \
+                    or (row.find('div', {'class': 'torrentname'}).find_all('a')[2]).text
+                url = row.find('a', 'imagnet')['href']
+                verified = True if row.find('a', 'iverify') else False
+                trusted = True if row.find('img', {'alt': 'verified'}) else False
+                seeders = int(row.find_all('td')[-2].text)
+                leechers = int(row.find_all('td')[-1].text)
+                _ = link, _id, verified, trusted, seeders, leechers
             except (AttributeError, TypeError):
                 continue
 
@@ -104,5 +120,5 @@ if __name__ == "__main__":
     print "STARTING - Torrent Basic TESTS"
     print "=================="
     print "######################################################################"
-    suite = unittest.TestLoader().loadTestsFromTestCase(TorrentBasicTests)
-    unittest.TextTestRunner(verbosity=2).run(suite)
+    SUITE = unittest.TestLoader().loadTestsFromTestCase(TorrentBasicTests)
+    unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/tv_tests.py b/tests/tv_tests.py
index cefe61d6891877cdb03fae4e845e43f3d96c56dc..810d95c99a7bf0f435a02d8711d44a1168e624dc 100644
--- a/tests/tv_tests.py
+++ b/tests/tv_tests.py
@@ -17,28 +17,44 @@
 # 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 tv
+"""
 
+import os.path
+import sys
 import unittest
 
-import test_lib as test
+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.tv import TVEpisode, TVShow
+import sickbeard
+import tests.test_lib as test
 
-class TVShowTests(test.SickbeardTestDBCase):
 
+class TVShowTests(test.SickbeardTestDBCase):
+    """
+    Test tv shows
+    """
     def setUp(self):
+        """
+        Set up tests
+        """
         super(TVShowTests, self).setUp()
         sickbeard.showList = []
 
     def test_init_indexerid(self):
+        """
+        test init indexer id
+        """
         show = TVShow(1, 0001, "en")
         self.assertEqual(show.indexerid, 0001)
 
     def test_change_indexerid(self):
+        """
+        test change indexer id
+        """
         show = TVShow(1, 0001, "en")
         show.name = "show name"
         show.network = "cbs"
@@ -59,6 +75,9 @@ class TVShowTests(test.SickbeardTestDBCase):
         self.assertEqual(show.indexerid, 0002)
 
     def test_set_name(self):
+        """
+        test set name
+        """
         show = TVShow(1, 0001, "en")
         show.name = "newName"
         show.saveToDB()
@@ -67,27 +86,44 @@ class TVShowTests(test.SickbeardTestDBCase):
 
 
 class TVEpisodeTests(test.SickbeardTestDBCase):
-
+    """
+    Test tv episode
+    """
     def setUp(self):
+        """
+        Set up
+        """
         super(TVEpisodeTests, self).setUp()
         sickbeard.showList = []
 
     def test_init_empty_db(self):
+        """
+        test init empty db
+        """
         show = TVShow(1, 0001, "en")
-        ep = TVEpisode(show, 1, 1)
-        ep.name = "asdasdasdajkaj"
-        ep.saveToDB()
-        ep.loadFromDB(1, 1)
-        self.assertEqual(ep.name, "asdasdasdajkaj")
+        episode = TVEpisode(show, 1, 1)
+        episode.name = "asdasdasdajkaj"
+        episode.saveToDB()
+        episode.loadFromDB(1, 1)
+        self.assertEqual(episode.name, "asdasdasdajkaj")
 
 
 class TVTests(test.SickbeardTestDBCase):
-
+    """
+    Test tv
+    """
     def setUp(self):
+        """
+        Set up
+        """
         super(TVTests, self).setUp()
         sickbeard.showList = []
 
-    def test_getEpisode(self):
+    @staticmethod
+    def test_get_episode():
+        """
+        Test get episodes
+        """
         show = TVShow(1, 0001, "en")
         show.name = "show name"
         show.network = "cbs"
@@ -99,7 +135,7 @@ class TVTests(test.SickbeardTestDBCase):
         show.startyear = 1987
         show.saveToDB()
         sickbeard.showList = [show]
-        #TODO: implement
+        # TODO: implement
 
 
 if __name__ == '__main__':
@@ -107,11 +143,11 @@ if __name__ == '__main__':
     print "STARTING - TV TESTS"
     print "=================="
     print "######################################################################"
-    suite = unittest.TestLoader().loadTestsFromTestCase(TVShowTests)
-    unittest.TextTestRunner(verbosity=2).run(suite)
+    SUITE = unittest.TestLoader().loadTestsFromTestCase(TVShowTests)
+    unittest.TextTestRunner(verbosity=2).run(SUITE)
     print "######################################################################"
-    suite = unittest.TestLoader().loadTestsFromTestCase(TVEpisodeTests)
-    unittest.TextTestRunner(verbosity=2).run(suite)
+    SUITE = unittest.TestLoader().loadTestsFromTestCase(TVEpisodeTests)
+    unittest.TextTestRunner(verbosity=2).run(SUITE)
     print "######################################################################"
-    suite = unittest.TestLoader().loadTestsFromTestCase(TVTests)
-    unittest.TextTestRunner(verbosity=2).run(suite)
+    SUITE = unittest.TestLoader().loadTestsFromTestCase(TVTests)
+    unittest.TextTestRunner(verbosity=2).run(SUITE)
diff --git a/tests/xem_tests.py b/tests/xem_tests.py
index 97fa2d425c1c48b176eafa6fdf5c62abead2f874..4341c6625bf49cbedf285643a8e0028e03c13b9c 100644
--- a/tests/xem_tests.py
+++ b/tests/xem_tests.py
@@ -17,65 +17,81 @@
 # You should have received a copy of the GNU General Public License
 # along with SickRage.  If not, see <http://www.gnu.org/licenses/>.
 
+"""
+Test XEM
+"""
 
-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__), '..')))
+# pylint: disable=line-too-long
 
-import datetime
-import unittest
+import os.path
 import re
+import sys
+import unittest
+
+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 test_lib as test
-import sickbeard
-from sickbeard.helpers import sanitizeSceneName
-from sickbeard.name_parser.parser import NameParser
 from sickbeard.tv import TVShow
+import sickbeard
+import tests.test_lib as test
+
 
 class XEMBasicTests(test.SickbeardTestDBCase):
-    def loadShowsFromDB(self):
+    """
+    Perform basic xem tests
+    """
+
+    @staticmethod
+    def load_shows_from_db():
         """
         Populates the showList with shows from the database
         """
 
-        myDB = test.db.DBConnection()
-        sqlResults = myDB.select("SELECT * FROM tv_shows")
+        my_db = test.db.DBConnection()
+        sql_results = my_db.select("SELECT * FROM tv_shows")
 
-        for sqlShow in sqlResults:
+        for sql_show in sql_results:
             try:
-                curShow = TVShow(int(sqlShow["indexer"]), int(sqlShow["indexer_id"]))
-                sickbeard.showList.append(curShow)
-            except Exception:
+                cur_show = TVShow(int(sql_show["indexer"]), int(sql_show["indexer_id"]))
+                sickbeard.showList.append(cur_show)
+            except Exception:  # pylint: disable=broad-except
                 pass
 
-    def loadFromDB(self):
+    @staticmethod
+    def load_from_db():
         """
         Populates the showList with shows from the database
         """
+        my_db = test.db.DBConnection()
+        sql_results = my_db.select("SELECT * FROM tv_shows")
 
-        myDB = test.db.DBConnection()
-        sqlResults = myDB.select("SELECT * FROM tv_shows")
-
-        for sqlShow in sqlResults:
+        for sql_show in sql_results:
             try:
-                curShow = TVShow(int(sqlShow["indexer"]), int(sqlShow["indexer_id"]))
-                sickbeard.showList.append(curShow)
-            except Exception, e:
-                print "There was an error creating the show"
+                cur_show = TVShow(int(sql_show["indexer"]), int(sql_show["indexer_id"]))
+                sickbeard.showList.append(cur_show)
+            except Exception as error:  # pylint: disable=broad-except
+                print "There was an error creating the show %s" % error
 
-    def test_formating(self):
+    @staticmethod
+    def test_formatting():
+        """
+        Test formatting
+        """
         name = "Game.of.Thrones.S03.720p.HDTV.x264-CtrlHD"
         release = "Game of Thrones"
 
         # m = re.match('(?P<ep_ab_num>(?>\d{1,3})(?![ip])).+', name)
 
+        # pylint: disable=anomalous-backslash-in-string
         escaped_name = re.sub('\\\\[\\s.-]', '\W+', re.escape(release))
-        curRegex = '^' + escaped_name + '\W+(?:(?:S\d[\dE._ -])|(?:\d\d?x)|(?:\d{4}\W\d\d\W\d\d)|(?:(?:part|pt)[\._ -]?(\d|[ivx]))|Season\W+\d+\W+|E\d+\W+|(?:\d{1,3}.+\d{1,}[a-zA-Z]{2}\W+[a-zA-Z]{3,}\W+\d{4}.+))'
+        # pylint: disable=anomalous-backslash-in-string
+        cur_regex = '^' + escaped_name + '\W+(?:(?:S\d[\dE._ -])|(?:\d\d?x)|(?:\d{4}\W\d\d\W\d\d)|(?:(?:part|pt)[\._ -]?(\d|[ivx]))|Season\W+\d+\W+|E\d+\W+|(?:\d{1,3}.+\d{1,}[a-zA-Z]{2}\W+[a-zA-Z]{3,}\W+\d{4}.+))'
         # print(u"Checking if show " + name + " matches " + curRegex)
 
-        match = re.search(curRegex, name, re.I)
-        # if match:
-        #     print(u"Matched " + curRegex + " to " + name)
+        match = re.search(cur_regex, name, re.I)
+        if match:
+            # print(u"Matched " + curRegex + " to " + name)
+            pass
 
 
 if __name__ == "__main__":
@@ -83,5 +99,6 @@ if __name__ == "__main__":
     print "STARTING - XEM Scene Numbering TESTS"
     print "=================="
     print "######################################################################"
-    suite = unittest.TestLoader().loadTestsFromTestCase(XEMBasicTests)
-    unittest.TextTestRunner(verbosity=2).run(suite)
+
+    SUITE = unittest.TestLoader().loadTestsFromTestCase(XEMBasicTests)
+    unittest.TextTestRunner(verbosity=2).run(SUITE)