diff --git a/gui/slick/js/core.js b/gui/slick/js/core.js
index ab4d37a3e534548f49d9295c99f69d06c4830a8d..90c927e06566a073bb6836d10f3b26d882345f87 100644
--- a/gui/slick/js/core.js
+++ b/gui/slick/js/core.js
@@ -159,6 +159,15 @@ var SICKRAGE = {
                 $.get($(this).attr('href'));
                 return false;
             });
+
+            $(document.body).on('click', '.bulkCheck', function(){
+                var bulkCheck = this;
+                var whichBulkCheck = $(bulkCheck).attr('id');
+
+                $('.'+whichBulkCheck+':visible').each(function(){
+                    $(this).prop('checked', $(bulkCheck).prop('checked'));
+                });
+            });
         }
     },
     config: {
@@ -2389,7 +2398,7 @@ var SICKRAGE = {
                     }
                     row += '</td>';
                 } else {
-                	row += '<td style="width: 8%;">None</td>';
+                    row += '<td style="width: 8%;">None</td>';
                 }
                 row += '<td>' + name + '</td>';
                 row += '</tr>';
@@ -2480,15 +2489,6 @@ var SICKRAGE = {
                 window.location.href = srRoot + '/manage/failedDownloads?toRemove='+removeArr.join('|');
             });
 
-            $('.bulkCheck').on('click', function(){
-                var bulkCheck = this;
-                var whichBulkCheck = $(bulkCheck).attr('id');
-
-                $('.'+whichBulkCheck+':visible').each(function(){
-                    this.checked = bulkCheck.checked;
-                });
-            });
-
             if($('.removeCheck').length){
                 $('.removeCheck').each(function(name) {
                     var lastCheck = null;
diff --git a/gui/slick/js/core.min.js b/gui/slick/js/core.min.js
index fd0fb136dc61acbdbe7e384df326b3ae49cce5bd..3b682619b9711aad0da2b28bf9d569bff5ddc0e6 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/massUpdate.js b/gui/slick/js/massUpdate.js
index 032c9256f367d4121c8bf4a389b5333ed7040b9b..9539cf53a482c5c05184e77a4fac3dd7e04c8714 100644
--- a/gui/slick/js/massUpdate.js
+++ b/gui/slick/js/massUpdate.js
@@ -65,15 +65,6 @@ $(document).ready(function(){
         }
     });
 
-    $('.bulkCheck').on('click', function(){
-        var bulkCheck = this;
-        var whichBulkCheck = $(bulkCheck).attr('id');
-
-        $('.'+whichBulkCheck).each(function(){
-            if(!this.disabled) { this.checked = !this.checked; }
-        });
-    });
-
     ['.editCheck', '.updateCheck', '.refreshCheck', '.renameCheck', '.deleteCheck', '.removeCheck'].forEach(function(name) {
         var lastCheck = null;
 
diff --git a/gui/slick/views/config_subtitles.mako b/gui/slick/views/config_subtitles.mako
index df6c0246bc93ccd0f37616548a54ef0536a5d716..720b10cc23e5274acc53a981bf0a75dfaa3651bc 100644
--- a/gui/slick/views/config_subtitles.mako
+++ b/gui/slick/views/config_subtitles.mako
@@ -79,7 +79,7 @@ $('#subtitles_dir').fileBrowser({ title: 'Select Subtitles Download Directory' }
                                 <div class="field-pair">
                                     <label>
                                         <span class="component-title">Subtitle Find Frequency</span>
-                                        <input type="number" name="subtitles_finder_frequency" value="${sickbeard.SUBTITLES_FINDER_FREQUENCY}" hours="1" class="form-control input-sm input75" />
+                                        <input type="number" name="subtitles_finder_frequency" value="${sickbeard.SUBTITLES_FINDER_FREQUENCY}" hours="1" min="1" step="1" class="form-control input-sm input75" />
                                         <span class="component-desc">time in hours between scans (default: 1)</span>
                                     </label>
                                 </div>
@@ -100,6 +100,10 @@ $('#subtitles_dir').fileBrowser({ title: 'Select Subtitles Download Directory' }
                                             <p>Append language codes to subtitle filenames?</p>
                                         </span>
                                     </label>
+                                    <label>
+                                        <span class="component-title">&nbsp;</span>
+                                        <span class="component-desc"><b>NOTE:</b> This option is required if you use multiple subtitle languages.</span>
+                                    </label>
                                 </div>
                                 <div class="field-pair">
                                     <label class="clearfix" for="subtitles_download_in_pp">
diff --git a/gui/slick/views/displayShow.mako b/gui/slick/views/displayShow.mako
index 8e78f7332e10a33aa9301dbcbdcb566d434e2e23..9b7515d7ff7681da6e9bb316a590fcb9c70c674d 100644
--- a/gui/slick/views/displayShow.mako
+++ b/gui/slick/views/displayShow.mako
@@ -519,8 +519,10 @@
                         <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 (len(epResult["subtitles"]) == 0 or 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>
+                % if sickbeard.USE_SUBTITLES and show.subtitles and epResult["location"]:
+                    % if (sickbeard.SUBTITLES_MULTI and subtitles.needs_subtitles(epResult["subtitles"])) or (not sickbeard.SUBTITLES_MULTI and len(subtitles.wanted_languages()) > 0 and "und" not in epResult["subtitles"] and list(subtitles.wanted_languages())[0] not in 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
                 % endif
             </td>
         </tr>
diff --git a/gui/slick/views/manage.mako b/gui/slick/views/manage.mako
index d667d1e3518ebdd79f39591b11ced9e399c991b1..710a003647ba02702fba8f8f8d3e0a93d6012d39 100644
--- a/gui/slick/views/manage.mako
+++ b/gui/slick/views/manage.mako
@@ -10,7 +10,7 @@
 <%block name="content">
 <%namespace file="/inc_defs.mako" import="renderQualityPill"/>
 
-
+<form name="massUpdateForm" method="post" action="massUpdate">
 <table style="width: 100%;" class="home-header">
     <tr>
         <td nowrap>
@@ -35,8 +35,6 @@
     </tr>
 </table>
 
-<form name="massUpdateForm" method="post" action="massUpdate">
-
 <table id="massUpdateTable" class="tablesorter" cellspacing="1" border="0" cellpadding="0">
     <thead>
         <tr>
diff --git a/gui/slick/views/manage_subtitleMissed.mako b/gui/slick/views/manage_subtitleMissed.mako
index 25d8442888f380f71b78b1f8afad353b544da957..41e8b838c69de444d242615f88c3c352e8b7a569 100644
--- a/gui/slick/views/manage_subtitleMissed.mako
+++ b/gui/slick/views/manage_subtitleMissed.mako
@@ -22,13 +22,25 @@
         % 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
+            % if sickbeard.SUBTITLES_MULTI:
+                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
+            % else:
+                Manage episodes without <select name="whichSubs" class="form-control form-control-inline input-sm">
+                % if not subtitles.wanted_languages():
+                    <option value="all">All</option>
+                % else:
+                    % for index, sub_code in enumerate(subtitles.wanted_languages()):
+                        % if index == 0:
+                            <option value="und">${subtitles.name_from_code(sub_code)}</option>
+                        % endif
+                    % endfor
+                % endif
             </select>
-
+            % endif
             <input class="btn" type="submit" value="Manage" />
         </form>
 
@@ -36,7 +48,15 @@
         ##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>
+            % if sickbeard.SUBTITLES_MULTI:
+                <h2>Episodes without ${subsLanguage} subtitles.</h2>
+            % else:
+                % for index, sub_code in enumerate(subtitles.wanted_languages()):
+                    % if index == 0:
+                        <h2>Episodes without ${subtitles.name_from_code(sub_code)} (undefined) subtitles.</h2>
+                    % endif
+                % endfor
+            % endif
             <br>
             Download missed subtitles for selected episodes <input class="btn btn-inline" type="submit" value="Go" />
             <div>
@@ -47,8 +67,8 @@
             <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>
+                    <th style="width: 1%;"><input type="checkbox" class="allCheck" id="allCheck-${cur_indexer_id}" name="${cur_indexer_id}-all"checked="checked" /></th>
+                    <th colspan="3" style="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>
diff --git a/sickbeard/helpers.py b/sickbeard/helpers.py
index 4d86695a3450e09029aa603aaf8d3f43c9ed2814..e1e191302e6455060090e11b006be55d29df5fa2 100644
--- a/sickbeard/helpers.py
+++ b/sickbeard/helpers.py
@@ -168,25 +168,29 @@ def isMediaFile(filename):
     """
 
     # ignore samples
-    if re.search(r'(^|[\W_])(?<!shomin.)(sample\d*)[\W_]', filename, re.I):
-        return False
+    try:
+        if re.search(r'(^|[\W_])(?<!shomin.)(sample\d*)[\W_]', filename, re.I):
+            return False
 
-    # ignore RARBG release intro
-    if re.search(r'^RARBG\.\w+\.(mp4|avi|txt)$', filename, re.I):
-        return False
+        # ignore RARBG release intro
+        if re.search(r'^RARBG\.\w+\.(mp4|avi|txt)$', filename, re.I):
+            return False
 
-    # ignore MAC OS's retarded "resource fork" files
-    if filename.startswith('._'):
-        return False
+        # ignore MAC OS's retarded "resource fork" files
+        if filename.startswith('._'):
+            return False
 
-    sepFile = filename.rpartition(".")
+        sepFile = filename.rpartition(".")
 
-    if re.search('extras?$', sepFile[0], re.I):
-        return False
+        if re.search('extras?$', sepFile[0], re.I):
+            return False
 
-    if sepFile[2].lower() in media_extensions:
-        return True
-    else:
+        if sepFile[2].lower() in media_extensions:
+            return True
+        else:
+            return False
+    except TypeError as error:  # Not a string
+        logger.log('Invalid filename. Filename must be a string. %s' % error, logger.DEBUG)  # pylint: disable=no-member
         return False
 
 
@@ -1282,7 +1286,6 @@ def mapIndexersToShow(showObj):
             logger.log(u"Found indexer mapping in cache for show: " + showObj.name, logger.DEBUG)
             mapped[int(curResult['mindexer'])] = int(curResult['mindexer_id'])
             return mapped
-            break
     else:
         sql_l = []
         for indexer in sickbeard.indexerApi().indexers:
@@ -1758,6 +1761,7 @@ def getTVDBFromID(indexer_id, indexer):
     else:
         return tvdb_id
 
+
 def is_ip_private(ip):
     priv_lo = re.compile("^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$")
     priv_24 = re.compile("^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$")
diff --git a/sickbeard/notifiers/pushover.py b/sickbeard/notifiers/pushover.py
index a0914e7dd4b392738c51899a0f3d6b1c0952d70a..6c3e22a4ed2933c8290506b61d857b48a1ab9711 100644
--- a/sickbeard/notifiers/pushover.py
+++ b/sickbeard/notifiers/pushover.py
@@ -25,7 +25,7 @@ import time
 
 import sickbeard
 from sickbeard import logger
-from sickbeard.common import notifyStrings, NOTIFY_SNATCH, NOTIFY_DOWNLOAD, NOTIFY_SUBTITLE_DOWNLOAD, NOTIFY_GIT_UPDATE, NOTIFY_GIT_UPDATE_TEXT
+from sickbeard.common import notifyStrings, NOTIFY_SNATCH, NOTIFY_DOWNLOAD, NOTIFY_SUBTITLE_DOWNLOAD, NOTIFY_GIT_UPDATE, NOTIFY_GIT_UPDATE_TEXT, NOTIFY_LOGIN_TEXT, NOTIFY_LOGIN
 from sickrage.helper.exceptions import ex
 
 API_URL = "https://api.pushover.net/1/messages.json"
@@ -153,8 +153,8 @@ class PushoverNotifier(object):
 
     def notify_login(self, ipaddress=""):
         if sickbeard.USE_PUSHOVER:
-            update_text = common.notifyStrings[common.NOTIFY_LOGIN_TEXT]
-            title = common.notifyStrings[common.NOTIFY_LOGIN]
+            update_text = notifyStrings[NOTIFY_LOGIN_TEXT]
+            title = notifyStrings[NOTIFY_LOGIN]
             self._notifyPushover(title, update_text.format(ipaddress))
 
     def _notifyPushover(self, title, message, sound=None, userKey=None, apiKey=None, force=False):
diff --git a/sickbeard/processTV.py b/sickbeard/processTV.py
index 9e03f21d49f014feba9759be0836f75dfd426319..7943e1b53d226e67d7411af900b9d2a289b4867a 100644
--- a/sickbeard/processTV.py
+++ b/sickbeard/processTV.py
@@ -28,7 +28,7 @@ from sickbeard.name_parser.parser import NameParser, InvalidNameException, Inval
 from sickbeard import common
 from sickbeard import failedProcessor
 from sickrage.helper.common import is_sync_file, is_torrent_or_nzb_file, subtitle_extensions
-from sickrage.helper.encoding import ek
+from sickrage.helper.encoding import ek, ss
 from sickrage.helper.exceptions import EpisodePostProcessingFailedException, ex, FailedPostProcessingFailedException
 
 from unrar2 import RarFile
@@ -44,7 +44,7 @@ import shutil_custom
 shutil.copyfile = shutil_custom.copyfile_custom
 
 
-class ProcessResult(object):
+class ProcessResult(object):  # pylint: disable=too-few-public-methods
     def __init__(self):
         self.result = True
         self.output = ''
@@ -139,7 +139,7 @@ def logHelper(logMessage, logLevel=logger.INFO):
     return logMessage + u"\n"
 
 
-def processDir(dirName, nzbName=None, process_method=None, force=False, is_priority=None, delete_on=False, failed=False, proc_type="auto"):
+def processDir(dirName, nzbName=None, process_method=None, force=False, is_priority=None, delete_on=False, failed=False, proc_type="auto"):  # pylint: disable=too-many-arguments,too-many-branches,too-many-statements,too-many-locals
     """
     Scans through the files in dirName and processes whatever media files it finds
 
@@ -300,7 +300,7 @@ def processDir(dirName, nzbName=None, process_method=None, force=False, is_prior
     return result.output
 
 
-def validateDir(path, dirName, nzbNameOriginal, failed, result):
+def validateDir(path, dirName, nzbNameOriginal, failed, result):  # pylint: disable=too-many-locals,too-many-branches,too-many-return-statements
     """
     Check if directory is valid for processing
 
@@ -312,6 +312,8 @@ def validateDir(path, dirName, nzbNameOriginal, failed, result):
     :return: True if dir is valid for processing, False if not
     """
 
+    dirName = ss(dirName)
+
     IGNORED_FOLDERS = ['.AppleDouble', '.@__thumb', '@eaDir']
     folder_name = ek(os.path.basename, dirName)
     if folder_name in IGNORED_FOLDERS:
@@ -392,7 +394,7 @@ def validateDir(path, dirName, nzbNameOriginal, failed, result):
     result.output += logHelper(dirName + " : No processable items found in folder", logger.DEBUG)
     return False
 
-def unRAR(path, rarFiles, force, result):
+def unRAR(path, rarFiles, force, result):  # pylint: disable=too-many-branches,too-many-statements
     """
     Extracts RAR files
 
@@ -473,7 +475,7 @@ def unRAR(path, rarFiles, force, result):
     return unpacked_files
 
 
-def already_postprocessed(dirName, videofile, force, result):
+def already_postprocessed(dirName, videofile, force, result):  # pylint: disable=unused-argument
     """
     Check if we already post processed a file
 
@@ -517,7 +519,7 @@ def already_postprocessed(dirName, videofile, force, result):
     return False
 
 
-def process_media(processPath, videoFiles, nzbName, process_method, force, is_priority, result):
+def process_media(processPath, videoFiles, nzbName, process_method, force, is_priority, result):  # pylint: disable=too-many-arguments
     """
     Postprocess mediafiles
 
@@ -585,8 +587,7 @@ 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 ek(os.path.isfile,
-                ek(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 = [ek(os.path.join, dirName, nzbName)]
         else:
diff --git a/sickbeard/providers/hounddawgs.py b/sickbeard/providers/hounddawgs.py
index 8bb7c8a1454545e19c1f280317864277c2d00a8d..c5f1fb6202a7b083b2e74c3ac2fc792a455ca6f5 100644
--- a/sickbeard/providers/hounddawgs.py
+++ b/sickbeard/providers/hounddawgs.py
@@ -21,10 +21,11 @@ import traceback
 from sickbeard import logger
 from sickbeard import tvcache
 from sickbeard.bs4_parser import BS4Parser
+from sickrage.helper.common import try_int
 from sickrage.providers.TorrentProvider import TorrentProvider
 
 
-class HoundDawgsProvider(TorrentProvider):
+class HoundDawgsProvider(TorrentProvider):  # pylint: disable=too-many-instance-attributes
 
     def __init__(self):
 
@@ -35,12 +36,15 @@ class HoundDawgsProvider(TorrentProvider):
         self.ratio = None
         self.minseed = None
         self.minleech = None
+        self.freeleech = None
+        self.ranked = None
 
-        self.cache = HoundDawgsCache(self)
 
-        self.urls = {'base_url': 'https://hounddawgs.org/',
-                     'search': 'https://hounddawgs.org/torrents.php',
-                     'login': 'https://hounddawgs.org/login.php'}
+        self.urls = {
+            'base_url': 'https://hounddawgs.org/',
+            'search': 'https://hounddawgs.org/torrents.php',
+            'login': 'https://hounddawgs.org/login.php'
+        }
 
         self.url = self.urls['base_url']
 
@@ -60,12 +64,16 @@ class HoundDawgsProvider(TorrentProvider):
             "searchtags": ''
         }
 
+        self.cache = HoundDawgsCache(self)
+
     def login(self):
 
-        login_params = {'username': self.username,
-                        'password': self.password,
-                        'keeplogged': 'on',
-                        'login': 'Login'}
+        login_params = {
+            'username': self.username,
+            'password': self.password,
+            'keeplogged': 'on',
+            'login': 'Login'
+        }
 
         self.get_url(self.urls['base_url'], timeout=30)
         response = self.get_url(self.urls['login'], post_data=login_params, timeout=30)
@@ -81,7 +89,7 @@ class HoundDawgsProvider(TorrentProvider):
 
         return True
 
-    def search(self, search_strings, age=0, ep_obj=None):
+    def search(self, search_strings, age=0, ep_obj=None):  # pylint: disable=too-many-locals,too-many-branches,too-many-statements
 
         results = []
         items = {'Season': [], 'Episode': [], 'RSS': []}
@@ -127,25 +135,20 @@ class HoundDawgsProvider(TorrentProvider):
                             allAs = (torrent[1]).find_all('a')
 
                             try:
-                                # link = self.urls['base_url'] + allAs[2].attrs['href']
-                                # url = result.find('td', attrs={'class': 'quickdownload'}).find('a')
+                                notinternal = result.find('img', src='/static//common/user_upload.png')
+                                if self.ranked and notinternal:
+                                    logger.log(u"Found a user uploaded release, Ignoring it..", logger.DEBUG)
+                                    continue
+                                freeleech = result.find('img', src='/static//common/browse/freeleech.png')
+                                if self.freeleech and not freeleech:
+                                    continue
                                 title = allAs[2].string
-                                # Trimming title so accepted by scene check(Feature has been rewuestet i forum)
-                                title = title.replace("custom.", "")
-                                title = title.replace("CUSTOM.", "")
-                                title = title.replace("Custom.", "")
-                                title = title.replace("dk", "")
-                                title = title.replace("DK", "")
-                                title = title.replace("Dk", "")
-                                title = title.replace("subs.", "")
-                                title = title.replace("SUBS.", "")
-                                title = title.replace("Subs.", "")
-
                                 download_url = self.urls['base_url']+allAs[0].attrs['href']
-                                # FIXME
-                                size = -1
-                                seeders = 1
-                                leechers = 0
+                                torrent_size = result.find("td", class_="nobr").find_next_sibling("td").string
+                                if torrent_size:
+                                    size = self._convertSize(torrent_size)
+                                seeders = try_int((result.findAll('td')[6]).text)
+                                leechers = try_int((result.findAll('td')[7]).text)
 
                             except (AttributeError, TypeError):
                                 continue
@@ -154,10 +157,10 @@ class HoundDawgsProvider(TorrentProvider):
                                 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
+                            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
                             if mode != 'RSS':
@@ -165,7 +168,7 @@ class HoundDawgsProvider(TorrentProvider):
 
                             items[mode].append(item)
 
-                except Exception, e:
+                except Exception:
                     logger.log(u"Failed parsing provider. Traceback: %s" % traceback.format_exc(), logger.ERROR)
 
             # For each search mode sort all the items by seeders if available
@@ -175,6 +178,21 @@ class HoundDawgsProvider(TorrentProvider):
 
         return results
 
+
+    @staticmethod
+    def _convertSize(size):
+        size = re.sub(r'[i, ]+', '', size)
+        matches = re.match(r'([\d.]+)([TGMK])', size.strip().upper())
+        if not matches:
+            return -1
+
+        size = matches.group(1)
+        modifier = matches.group(2)
+
+        mod = {'K': 1, 'M': 2, 'G': 3, 'T': 4}
+        return float(size) * 1024**mod[modifier]
+
+
     def seed_ratio(self):
         return self.ratio
 
diff --git a/sickbeard/providers/tvchaosuk.py b/sickbeard/providers/tvchaosuk.py
index 7542bb2c611f02b0fb53216185e435a3e46fee7b..f719454084ec318dcc8690702efca5e8d8b23c07 100644
--- a/sickbeard/providers/tvchaosuk.py
+++ b/sickbeard/providers/tvchaosuk.py
@@ -154,7 +154,7 @@ class TVChaosUKProvider(TorrentProvider):
                             freeleech = torrent.find('img', alt=re.compile('Free Torrent'))
                             if self.freeleech and not freeleech:
                                 continue
-                            title = torrent.find(attrs={'class':'tooltip-target'}).text.strip()
+                            title = (torrent.find(attrs={'class':'tooltip-target'}).text.strip()).replace("mp4", "x264")
                             download_url = torrent.find(title="Click to Download this Torrent!").parent['href'].strip()
                             seeders = int(torrent.find(title='Seeders').text.strip())
                             leechers = int(torrent.find(title='Leechers').text.strip())
diff --git a/sickbeard/subtitles.py b/sickbeard/subtitles.py
index d568aa6d0e27abe3370753b7c9e823de401b135b..a7fab4ccdc17f97f44658ff57e6286aac2444dc8 100644
--- a/sickbeard/subtitles.py
+++ b/sickbeard/subtitles.py
@@ -32,6 +32,7 @@ from sickbeard import logger
 from sickbeard import history
 from sickbeard import db
 from sickbeard import processTV
+from sickbeard.helpers import remove_non_release_groups
 from sickrage.helper.common import media_extensions, dateTimeFormat
 from sickrage.helper.encoding import ek
 from sickrage.helper.exceptions import ex
@@ -123,13 +124,13 @@ def subtitle_code_filter():
 
 
 def needs_subtitles(subtitles):
-    if isinstance(subtitles, basestring):
+    if isinstance(subtitles, basestring) and sickbeard.SUBTITLES_MULTI:
         subtitles = {subtitle.strip() for subtitle in subtitles.split(',')}
 
     if sickbeard.SUBTITLES_MULTI:
         return len(wanted_languages().difference(subtitles)) > 0
-    else:
-        return len(subtitles) == 0
+    elif 'und' not in subtitles:
+        return True
 
 
 # Hack around this for now.
@@ -153,8 +154,8 @@ def download_subtitles(subtitles_info):
     existing_subtitles = subtitles_info['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)
+        logger.log(u'Episode already has all needed subtitles, skipping %s S%02dE%02d'
+                   % (subtitles_info['show_name'], subtitles_info['season'], subtitles_info['episode']), logger.DEBUG)
         return (existing_subtitles, None)
 
     # Check if we really need subtitles
@@ -333,6 +334,14 @@ class SubtitlesFinder(object):
         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):
+                    try:
+                        # Remove non release groups from video file. Needed to match subtitles
+                        new_video_filename = remove_non_release_groups(video_filename)
+                        if new_video_filename != video_filename:
+                            os.rename(video_filename, new_video_filename)
+                            video_filename = new_video_filename
+                    except Exception as e:
+                        logger.log(u'Could not remove non release groups from video file. Error: %r' % ex(e), logger.DEBUG)
                     if video_filename.rsplit(".", 1)[1] in media_extensions:
                         try:
                             video = subliminal.scan_video(os.path.join(root, video_filename),
@@ -429,13 +438,13 @@ class SubtitlesFinder(object):
         for ep_to_sub in sql_results:
 
             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)
+                logger.log(u'Episode file does not exist, cannot download subtitles for %s S%02dE%02d'
+                           % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode']), 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)
+                logger.log(u'Episode already has all needed subtitles, skipping %s S%02dE%02d'
+                           % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode']), logger.DEBUG)
                 continue
 
             # http://bugs.python.org/issue7980#msg221094
@@ -448,18 +457,19 @@ class SubtitlesFinder(object):
                      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)
+                logger.log(u'Downloading subtitles for %s S%02dE%02d'
+                           % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode']), 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)
+                    logger.log(u'Show with ID %s not found in the database' % ep_to_sub['showid'], logger.DEBUG)
                     self.amActive = False
                     return
 
                 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)
+                    logger.log(u'%s S%02dE%02d not found in the database'
+                               % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode']), logger.DEBUG)
                     self.amActive = False
                     return
 
@@ -468,15 +478,16 @@ class SubtitlesFinder(object):
                 try:
                     episode_object.download_subtitles()
                 except Exception as error:
-                    logger.log(u'Unable to find subtitles', logger.DEBUG)
+                    logger.log(u'Unable to find subtitles for %s S%02dE%02d'
+                               % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode']), logger.DEBUG)
                     logger.log(str(error), logger.DEBUG)
                     self.amActive = False
                     return
 
                 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)))
+                    logger.log(u'Downloaded %s subtitles for %s S%02dE%02d'
+                               % (', '.join(new_subtitles), ep_to_sub['show_name'], ep_to_sub["season"], ep_to_sub["episode"]))
 
         self.amActive = False
 
diff --git a/tests/helpers_tests.py b/tests/helpers_tests.py
index 85b3aac84369ea63070136f864bd727ea407885d..1ec1cb4605e726317f0c03b068c1dc6b1846f8a0 100755
--- a/tests/helpers_tests.py
+++ b/tests/helpers_tests.py
@@ -21,7 +21,7 @@
 """
 Test sickbeard.helpers
 
-Methods:
+Public Methods:
     fixGlob
     indentXML
     remove_non_release_groups
@@ -60,7 +60,6 @@ Methods:
     encrypt
     decrypt
     full_sanitizeSceneName
-    _check_against_names
     get_show
     is_hidden_folder
     real_path
@@ -72,8 +71,6 @@ Methods:
     restoreConfigZip
     mapIndexersToShow
     touchFile
-    _getTempDir
-    _setUpSession
     getURL
     download_file
     get_size
@@ -84,8 +81,13 @@ Methods:
     pretty_time_delta
     isFileLocked
     getDiskSpaceUsage
+Private Methods:
+    _check_against_names
+    _getTempDir
+    _setUpSession
 """
 
+from __future__ import print_function
 import os.path
 import sys
 import unittest
@@ -93,7 +95,8 @@ 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.helpers import remove_non_release_groups
+from sickbeard import helpers
+from sickrage.helper.common import media_extensions, subtitle_extensions
 
 TEST_RESULT = 'Show.Name.S01E01.HDTV.x264-RLSGROUP'
 TEST_CASES = {
@@ -163,7 +166,7 @@ def test_generator(test_strings):
         :return: test to run
         """
         for test_string in test_strings:
-            self.assertEqual(remove_non_release_groups(test_string), TEST_RESULT)
+            self.assertEqual(helpers.remove_non_release_groups(test_string), TEST_RESULT)
     return _test
 
 
@@ -258,12 +261,53 @@ class HelpersFileTests(unittest.TestCase):
     """
     Test file helpers
     """
-    @unittest.skip('Not yet implemented')
+
     def test_is_media_file(self):
         """
         Test isMediaFile
         """
-        pass
+        # TODO: Add unicode tests
+        # TODO: Add MAC OS resource fork tests
+        # TODO: Add RARBG release tests
+        # RARBG release intros should be ignored
+        # MAC OS's "resource fork" files should be ignored
+        # Extras should be ignored
+        # and the file extension should be in the list of media extensions
+
+        # Test all valid media extensions
+        temp_name = 'Show.Name.S01E01.HDTV.x264-RLSGROUP'
+        extension_tests = {'.'.join((temp_name, ext)): True for ext in media_extensions}
+        # ...and some invalid ones
+        other_extensions = ['txt', 'sfv', 'srr', 'rar', 'nfo', 'zip']
+        extension_tests.update({'.'.join((temp_name, ext)): False for ext in other_extensions + subtitle_extensions})
+
+        # Samples should be ignored
+        sample_tests = {  # Samples should be ignored, valid samples will return False
+            'Show.Name.S01E01.HDTV.sample.mkv': False,  # default case
+            'Show.Name.S01E01.HDTV.sAmPle.mkv': False,  # Ignore case
+            'Show.Name.S01E01.HDTV.samples.mkv': True,  # sample should not be plural
+            'Show.Name.S01E01.HDTVsample.mkv': True,  # no separation, can't identify as sample
+            'Sample.Show.Name.S01E01.HDTV.mkv': False,  # location doesn't matter
+            'Show.Name.Sample.S01E01.HDTV.sample.mkv': False,  # location doesn't matter
+            'Show.Name.S01E01.HDTV.sample1.mkv': False,  # numbered samples are ok
+            'Show.Name.S01E01.HDTV.sample12.mkv': False,  # numbered samples are ok
+            'Show.Name.S01E01.HDTV.sampleA.mkv': True,  # samples should not be indexed alphabetically
+        }
+
+        edge_cases = {
+            None: False,
+            '': False,
+            0: False,
+            1: False,
+            42: False,
+            123189274981274: False,
+            12.23: False,
+            ('this', 'is', 'a tuple'): False,
+        }
+
+        for cur_test in extension_tests, sample_tests, edge_cases:
+            for cur_name, expected_result in cur_test.items():
+                self.assertEqual(helpers.isMediaFile(cur_name), expected_result, cur_name)
 
     @unittest.skip('Not yet implemented')
     def test_is_file_locked(self):
@@ -640,12 +684,11 @@ class HelpersMiscTests(unittest.TestCase):
         """
         pass
 
-
 if __name__ == '__main__':
-    print "=================="
-    print "STARTING - Helpers TESTS"
-    print "=================="
-    print "######################################################################"
+    print("==================")
+    print("STARTING - Helpers TESTS")
+    print("==================")
+    print("######################################################################")
     for name, test_data in TEST_CASES.items():
         test_name = 'test_%s' % name
         test = test_generator(test_data)