diff --git a/SickBeard.py b/SickBeard.py
index 2beb507455319dd660f8e3f3c5a12601cddc144f..ae44059e17fedc6f6eb35a0f99ff7b8d85e486c2 100644
--- a/SickBeard.py
+++ b/SickBeard.py
@@ -65,6 +65,9 @@ def loadShowsFromDB():
 
 def main():
 
+	# use this pid for everything
+	sickbeard.PID = os.getpid()
+
 	# do some preliminary stuff
 	sickbeard.MY_FULLNAME = os.path.normpath(os.path.abspath(sys.argv[0]))
 	sickbeard.MY_NAME = os.path.basename(sickbeard.MY_FULLNAME)
@@ -153,10 +156,10 @@ def main():
 		        'password':  sickbeard.WEB_PASSWORD,
 		})
 	except IOError:
-		logger.log(u"Unable to start web server, is something else running on port %d?" % sickbeard.WEB_PORT, logger.ERROR)
+		logger.log(u"Unable to start web server, is something else running on port %d?" % startPort, logger.ERROR)
 		if sickbeard.LAUNCH_BROWSER:
 			logger.log(u"Launching browser and exiting", logger.ERROR)
-			sickbeard.launchBrowser()
+			sickbeard.launchBrowser(startPort)
 		sys.exit()
 
 	# build from the DB to start with
@@ -172,7 +175,7 @@ def main():
 
 	# launch browser if we're supposed to
 	if sickbeard.LAUNCH_BROWSER:
-		sickbeard.launchBrowser()
+		sickbeard.launchBrowser(startPort)
 
 	# start an update if we're supposed to
 	if forceUpdate:
diff --git a/data/css/tablesorter.css b/data/css/tablesorter.css
index b88f1dd1179e347b2b6315a11ec62ff8e7bb3867..30259c19c173656c0adcc4e88aba93bcc8ba7e66 100644
--- a/data/css/tablesorter.css
+++ b/data/css/tablesorter.css
@@ -1,15 +1,15 @@
 /* tables */
 table.tablesorter thead tr .header {
-	background-image: url(/images/tablesorter/bg.gif);
+	background-image: url(../images/tablesorter/bg.gif);
 	background-repeat: no-repeat;
 	background-position: center right;
 	cursor: pointer;
 }
 table.tablesorter thead tr .headerSortUp {
-	background-image: url(/images/tablesorter/asc.gif);
+	background-image: url(../images/tablesorter/asc.gif);
 }
 table.tablesorter thead tr .headerSortDown {
-	background-image: url(/images/tablesorter/desc.gif);
+	background-image: url(../images/tablesorter/desc.gif);
 }
 table.tablesorter thead tr .headerSortDown, table.tablesorter thead tr .headerSortUp {
     background-color: #57442B;
diff --git a/data/interfaces/default/config_general.tmpl b/data/interfaces/default/config_general.tmpl
index ab67e23942c640bb76b782d9f5937ca9e14a43b8..d2fdb0b28f9517df5de0786bc06c7af25fa77b9d 100644
--- a/data/interfaces/default/config_general.tmpl
+++ b/data/interfaces/default/config_general.tmpl
@@ -203,6 +203,12 @@ the settings in your existing shows!</strong><br />
 
 <legend>Episode Naming</legend>
 
+<strong>Season Folders format</strong><br />
+Format to use when creating season folders<br />
+Eg. 'Season %0d' or 'season%02d'<br />
+<input type="text" name="season_folders_format" value="$sickbeard.SEASON_FOLDERS_FORMAT" size="10"><br />
+<br />
+
 <label for="naming_show_name"><input type="checkbox" name="naming_show_name" id="naming_show_name" #if $sickbeard.NAMING_SHOW_NAME then "CHECKED" else ""#> <strong>Include show name</strong></label><br />
 <br />
 <label for="naming_ep_name"><input type="checkbox" name="naming_ep_name" id="naming_ep_name" #if $sickbeard.NAMING_EP_NAME then "CHECKED" else ""#> <strong>Include episode name</strong></label><br />
diff --git a/data/interfaces/default/inc_bottom.tmpl b/data/interfaces/default/inc_bottom.tmpl
index 7303bf7d16ca5c106969602f630842995ce611b2..8e268b372280bebe9c2457328f7d2a155351ea95 100644
--- a/data/interfaces/default/inc_bottom.tmpl
+++ b/data/interfaces/default/inc_bottom.tmpl
@@ -13,7 +13,7 @@
 <br />
 <b>Search</b>: <%=str(sickbeard.currentSearchScheduler.timeLeft()).split('.')[0]%> |
 <!--<b>Update</b>: <a%a=str(sickbeard.updateScheduler.timeLeft()).split('.')[0]%> | -->
-<b>Backlog</b>: $sickbeard.backlogSearchScheduler.nextRun().strftime("%A") <br />
+<b>Backlog</b>: $sickbeard.backlogSearchScheduler.nextRun().strftime("%a %b %d") <br />
 </div>
 </body>
 </html>
diff --git a/lib/tvnamer/utils.py b/lib/tvnamer/utils.py
index 8e9761e801632f7d3b26fcefec240f76578af7b0..c572dc0e29539653449b8d85548d775fe60cf606 100644
--- a/lib/tvnamer/utils.py
+++ b/lib/tvnamer/utils.py
@@ -312,7 +312,10 @@ class FileParser(object):
                         month = day
                         day = tmp_month
 
-                    episodenumbers = [datetime.date(int(match.group('year')), month, day)]
+                    try:
+                        episodenumbers = [datetime.date(int(match.group('year')), month, day)]
+                    except ValueError, e:
+                        raise InvalidFilename(str(e))
 
                 else:
                     raise ConfigValueError(
diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py
index 133e7e75db94ff5498b10d6571ee13bf919d0d4c..da8587cae09cd557319b76811542365c067ec375 100644
--- a/sickbeard/__init__.py
+++ b/sickbeard/__init__.py
@@ -43,6 +43,8 @@ from lib.configobj import ConfigObj
 
 SOCKET_TIMEOUT = 30
 
+PID = None
+
 CFG = None
 CONFIG_FILE = None
 
@@ -99,6 +101,7 @@ ART_THUMBNAILS = None
 ART_SEASON_THUMBNAILS = None
 
 QUALITY_DEFAULT = None
+SEASON_FOLDERS_FORMAT = None
 SEASON_FOLDERS_DEFAULT = None
 PROVIDER_ORDER = []
 
@@ -289,7 +292,7 @@ def initialize(consoleLogging=True):
                 airingList, comingList, loadingShowList, SOCKET_TIMEOUT, \
                 NZBS, NZBS_UID, NZBS_HASH, USE_NZB, USE_TORRENT, TORRENT_DIR, USENET_RETENTION, \
                 SEARCH_FREQUENCY, DEFAULT_SEARCH_FREQUENCY, BACKLOG_SEARCH_FREQUENCY, \
-                DEFAULT_BACKLOG_SEARCH_FREQUENCY, QUALITY_DEFAULT, SEASON_FOLDERS_DEFAULT, \
+                DEFAULT_BACKLOG_SEARCH_FREQUENCY, QUALITY_DEFAULT, SEASON_FOLDERS_FORMAT, SEASON_FOLDERS_DEFAULT, \
                 USE_GROWL, GROWL_HOST, GROWL_PASSWORD, PROG_DIR, NZBMATRIX, NZBMATRIX_USERNAME, \
                 NZBMATRIX_APIKEY, versionCheckScheduler, VERSION_NOTIFY, PROCESS_AUTOMATICALLY, \
                 KEEP_PROCESSED_DIR, TV_DOWNLOAD_DIR, TVDB_BASE_URL, MIN_SEARCH_FREQUENCY, \
@@ -339,6 +342,9 @@ def initialize(consoleLogging=True):
         LAUNCH_BROWSER = bool(check_setting_int(CFG, 'General', 'launch_browser', 1))
 
         CACHE_DIR = check_setting_str(CFG, 'General', 'cache_dir', 'cache')
+        # fix bad configs due to buggy code
+        if CACHE_DIR == 'None':
+            CACHE_DIR = 'cache'
         if not helpers.makeDir(CACHE_DIR):
             logger.log(u"!!! Creating local cache dir failed, using system default", logger.ERROR)
             CACHE_DIR = None
@@ -353,6 +359,7 @@ def initialize(consoleLogging=True):
 
         QUALITY_DEFAULT = check_setting_int(CFG, 'General', 'quality_default', SD)
         VERSION_NOTIFY = check_setting_int(CFG, 'General', 'version_notify', 1)
+        SEASON_FOLDERS_FORMAT = check_setting_str(CFG, 'General', 'season_folders_format', 'Season %02d')
         SEASON_FOLDERS_DEFAULT = bool(check_setting_int(CFG, 'General', 'season_folders_default', 0))
 
         PROVIDER_ORDER = check_setting_str(CFG, 'General', 'provider_order', '').split()
@@ -670,10 +677,10 @@ def saveAndShutdown(restart=False):
         elif install_type == 'win':
             if hasattr(sys, 'frozen'):
                 # c:\dir\to\updater.exe 12345 c:\dir\to\sickbeard.exe
-                popen_list = [os.path.join(sickbeard.PROG_DIR, 'updater.exe'), str(os.getpid()), sys.executable]
+                popen_list = [os.path.join(sickbeard.PROG_DIR, 'updater.exe'), str(sickbeard.PID), sys.executable]
             else:
                 logger.log(u"Unknown SB launch method, please file a bug report about this", logger.ERROR)
-                popen_list = [sys.executable, os.path.join(sickbeard.PROG_DIR, 'updater.py'), str(os.getpid()), sys.executable, sickbeard.MY_FULLNAME ]
+                popen_list = [sys.executable, os.path.join(sickbeard.PROG_DIR, 'updater.py'), str(sickbeard.PID), sys.executable, sickbeard.MY_FULLNAME ]
 
         if popen_list:
             popen_list += sickbeard.MY_ARGS
@@ -719,6 +726,7 @@ def save_config():
     new_config['General']['use_nzb'] = int(USE_NZB)
     new_config['General']['download_propers'] = int(DOWNLOAD_PROPERS)
     new_config['General']['quality_default'] = int(QUALITY_DEFAULT)
+    new_config['General']['season_folders_format'] = SEASON_FOLDERS_FORMAT
     new_config['General']['season_folders_default'] = int(SEASON_FOLDERS_DEFAULT)
     new_config['General']['provider_order'] = ' '.join([x.getID() for x in providers.sortedProviderList()])
     new_config['General']['version_notify'] = int(VERSION_NOTIFY)
@@ -739,7 +747,7 @@ def save_config():
     new_config['General']['art_fanart'] = int(ART_FANART)
     new_config['General']['art_thumbnails'] = int(ART_THUMBNAILS)
     new_config['General']['art_season_thumbnails'] = int(ART_SEASON_THUMBNAILS)
-    new_config['General']['cache_dir'] = CACHE_DIR
+    new_config['General']['cache_dir'] = CACHE_DIR if CACHE_DIR else 'cache'
     new_config['General']['tv_download_dir'] = TV_DOWNLOAD_DIR
     new_config['General']['keep_processed_dir'] = int(KEEP_PROCESSED_DIR)
     new_config['General']['process_automatically'] = int(PROCESS_AUTOMATICALLY)
@@ -817,8 +825,10 @@ def save_config():
     new_config.write()
 
 
-def launchBrowser():
-    browserURL = 'http://localhost:%d%s' % (WEB_PORT, WEB_ROOT)
+def launchBrowser(startPort=None):
+    if not startPort:
+        startPort = WEB_PORT
+    browserURL = 'http://localhost:%d%s' % (startPort, WEB_ROOT)
     try:
         webbrowser.open(browserURL, 2, 1)
     except:
diff --git a/sickbeard/common.py b/sickbeard/common.py
index a0bb08dbca68744b9a29e56817ad635c21048be1..bd5f2e934f4d37b57501b69618f185165bebd9bc 100644
--- a/sickbeard/common.py
+++ b/sickbeard/common.py
@@ -275,6 +275,7 @@ sceneExceptions = {72546: ['CSI'],
                    194751: ['Conan', 'Conan (2010)'],
                    164451: ['Carlos (2010)'],
                    70726: ['Babylon 5', 'Babylon5'],
+                   83714: ['Genius', 'Genius With Dave  Gormand'],
                    }
 
 countryList = {'Australia': 'AU',
diff --git a/sickbeard/helpers.py b/sickbeard/helpers.py
index 6767a5d5d549b71af5e257e3ce5edff0f2a77738..ab9b5caf4d229d25d1823aac1fea6a214f196258 100644
--- a/sickbeard/helpers.py
+++ b/sickbeard/helpers.py
@@ -292,7 +292,7 @@ def buildNFOXML(myShow):
 
 def searchDBForShow(regShowName):
 
-	showNames = set([regShowName+'%', regShowName.replace(' ','_')+'%'])
+	showNames = [regShowName.replace(' ','_')]
 
 	# if tvdb fails then try looking it up in the db
 	myDB = db.DBConnection()
diff --git a/sickbeard/logger.py b/sickbeard/logger.py
index e0c8d88fc67abcca75cc11432b43da7af55a4bc4..864f3167e44cb45084770c001d619c4e160bfd01 100644
--- a/sickbeard/logger.py
+++ b/sickbeard/logger.py
@@ -83,17 +83,20 @@ def log(toLog, logLevel=MESSAGE):
         outLine = message.encode('utf-8')
     
         sbLogger = logging.getLogger('sickbeard')
-    
-        if logLevel == DEBUG:
-            sbLogger.debug(outLine)
-        elif logLevel == MESSAGE:
-            sbLogger.info(outLine)
-        elif logLevel == WARNING:
-            sbLogger.warning(outLine)
-        elif logLevel == ERROR:
-            sbLogger.error(outLine)
-    
-            # add errors to the UI logger
-            classes.ErrorViewer.add(classes.UIError(message))
-        else:
-            sbLogger.log(logLevel, outLine)
+
+        try:
+            if logLevel == DEBUG:
+                sbLogger.debug(outLine)
+            elif logLevel == MESSAGE:
+                sbLogger.info(outLine)
+            elif logLevel == WARNING:
+                sbLogger.warning(outLine)
+            elif logLevel == ERROR:
+                sbLogger.error(outLine)
+        
+                # add errors to the UI logger
+                classes.ErrorViewer.add(classes.UIError(message))
+            else:
+                sbLogger.log(logLevel, outLine)
+        except ValueError, e:
+            pass
\ No newline at end of file
diff --git a/sickbeard/metadata/__init__.py b/sickbeard/metadata/__init__.py
index d135fa35a8ea6a950f48dd6d406d6791ed0092a4..f471ad632a42dd1e2ca8d7c638919303061cc6d6 100644
--- a/sickbeard/metadata/__init__.py
+++ b/sickbeard/metadata/__init__.py
@@ -1,7 +1,7 @@
-__all__ = ['generic', 'helpers', 'xbmc', 'mediabrowser']
+__all__ = ['generic', 'helpers', 'xbmc', 'mediabrowser', 'ps3']
 
 import sys
-import xbmc, mediabrowser
+import xbmc, mediabrowser, ps3
 
 def available_generators():
     return filter(lambda x: x not in ('generic', 'helpers'), __all__)
@@ -32,4 +32,4 @@ def getMetadataGeneratorList():
         result[cur_generator_id] = cur_module.name
     
     return result
-        
\ No newline at end of file
+        
diff --git a/sickbeard/metadata/ps3.py b/sickbeard/metadata/ps3.py
new file mode 100644
index 0000000000000000000000000000000000000000..67a775032b9baf8330103dbdbd4e2db509cf2bcc
--- /dev/null
+++ b/sickbeard/metadata/ps3.py
@@ -0,0 +1,64 @@
+# Author: Nic Wolfe <nic@wolfeden.ca>
+# URL: http://code.google.com/p/sickbeard/
+#
+# This file is part of Sick Beard.
+#
+# Sick Beard 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.
+#
+# Sick Beard 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 Sick Beard.  If not, see <http://www.gnu.org/licenses/>.
+
+import datetime
+
+import sickbeard
+
+import generic
+
+from sickbeard.common import *
+from sickbeard import logger, exceptions, helpers
+from lib.tvdb_api import tvdb_api, tvdb_exceptions
+
+from sickbeard import encodingKludge as ek
+
+class PS3Metadata(generic.GenericMetadata):
+    """
+    Metadata generation class for Sony PS3.
+
+    The following file structure is used:
+    
+    show_root/cover.jpg                                      (poster)
+    show_root/Season 01/show - 1x01 - episode.avi            (existing video)
+    show_root/Season 01/show - 1x01 - episode.avi.cover.jpg  (episode thumb)
+    """
+    
+    def __init__(self):
+        generic.GenericMetadata.__init__(self)
+
+        self.poster_name = 'cover.jpg'
+        self.name = 'PS3'
+
+    def get_episode_thumb_path(self, ep_obj):
+        """
+        Returns the path where the episode thumbnail should be stored. Defaults to
+        the same path as the episode file but with a .cover.jpg extension.
+        
+        ep_obj: a TVEpisode instance for which to create the thumbnail
+        """
+        if ek.ek(os.path.isfile, ep_obj.location):
+            tbn_filename = ep_obj.location + '.cover.jpg'
+        else:
+            return None
+        
+        return tbn_filename
+    
+# present a standard "interface"
+metadata_class = PS3Metadata
+
diff --git a/sickbeard/processTV.py b/sickbeard/processTV.py
index d3edbd605bf3446bc4cfd85bf8495bef1245de57..9800bc94a6c5ddd73b56973cf38c0285e8c3e2df 100644
--- a/sickbeard/processTV.py
+++ b/sickbeard/processTV.py
@@ -20,12 +20,10 @@ from __future__ import with_statement
 
 import os, subprocess, shlex, os.path
 import shutil
-import sys
 import re
 import glob
 from shutil import Error
 
-from sickbeard import notifiers
 from sickbeard import exceptions
 from sickbeard import notifiers
 from sickbeard import db, classes, helpers, sceneHelpers
@@ -58,7 +56,7 @@ def renameFile(movedFilePath, newName):
     try:
         ek.ek(os.rename, movedFilePath, renamedFilePathname)
     except (OSError, IOError), e:
-        logger.log(u"Failed renaming " + movedFilePath + " to " + os.path.basename(renamedFilePathname) + ": " + str(e), logger.ERROR)
+        logger.log(u"Failed renaming " + movedFilePath + " to " + ek.ek(os.path.basename, renamedFilePathname) + ": " + str(e), logger.ERROR)
         return False
 
     return renamedFilePathname
@@ -87,9 +85,9 @@ def deleteAssociatedFiles(file):
     for associatedFilePath in ek.ek(glob.glob, baseName+'*'):
         # only delete it if the only non-shared part is the extension
         if '.' in associatedFilePath[len(baseName):]:
-            logger.log(u"Not deleting file "+associatedFilePath+" because it looks like it's not related", logger.DEBUG)
+            logger.log(u"Not deleting file "+associatedFilePath+u" because it looks like it's not related", logger.DEBUG)
             continue
-        logger.log(u"Deleting file "+associatedFilePath+" because it is associated with "+file, logger.DEBUG)
+        logger.log(u"Deleting file "+associatedFilePath+u" because it is associated with "+file, logger.DEBUG)
         ek.ek(os.remove, associatedFilePath)
 
 def _checkForExistingFile(renamedFilePath, oldFile):
@@ -130,14 +128,14 @@ def findInHistory(nzbName):
         for cur_result in sqlResults:
             episodes.append(int(cur_result["episode"]))            
 
-        return (tvdb_id, season, episodes)
+        return (tvdb_id, season, list(set(episodes)))
 
     return None
 
 
 def logHelper (logMessage, logLevel=logger.MESSAGE):
     logger.log(logMessage, logLevel)
-    return logMessage + "\n"
+    return logMessage + u"\n"
 
 
 def processDir (dirName, nzbName=None, recurse=False):
@@ -151,9 +149,9 @@ def processDir (dirName, nzbName=None, recurse=False):
         dirName = ek.ek(os.path.realpath, dirName)
 
     # if they've got a download dir configured then use it
-    elif sickbeard.TV_DOWNLOAD_DIR and os.path.isdir(sickbeard.TV_DOWNLOAD_DIR) \
-            and os.path.normpath(dirName) != os.path.normpath(sickbeard.TV_DOWNLOAD_DIR):
-        dirName = ek.ek(os.path.join, sickbeard.TV_DOWNLOAD_DIR, os.path.abspath(dirName).split(os.path.sep)[-1])
+    elif sickbeard.TV_DOWNLOAD_DIR and ek.ek(os.path.isdir, sickbeard.TV_DOWNLOAD_DIR) \
+            and ek.ek(os.path.normpath, dirName) != ek.ek(os.path.normpath, sickbeard.TV_DOWNLOAD_DIR):
+        dirName = ek.ek(os.path.join, sickbeard.TV_DOWNLOAD_DIR, ek.ek(os.path.abspath, dirName).split(os.path.sep)[-1])
         returnStr += logHelper(u"Trying to use folder "+dirName, logger.DEBUG)
 
     # if we didn't find a real dir then quit
@@ -162,13 +160,13 @@ def processDir (dirName, nzbName=None, recurse=False):
         return returnStr
 
     # TODO: check if it's failed and deal with it if it is
-    if os.path.basename(dirName).startswith('_FAILED_'):
+    if ek.ek(os.path.basename, dirName).startswith('_FAILED_'):
         returnStr += logHelper(u"The directory name indicates it failed to extract, cancelling", logger.DEBUG)
         return returnStr
-    elif os.path.basename(dirName).startswith('_UNDERSIZED_'):
+    elif ek.ek(os.path.basename, dirName).startswith('_UNDERSIZED_'):
         returnStr += logHelper(u"The directory name indicates that it was previously rejected for being undersized, cancelling", logger.DEBUG)
         return returnStr
-    elif os.path.basename(dirName).startswith('_UNPACK_'):
+    elif ek.ek(os.path.basename, dirName).startswith('_UNPACK_'):
         returnStr += logHelper(u"The directory name indicates that this release is in the process of being unpacked, skipping", logger.DEBUG)
         return returnStr
 
@@ -200,7 +198,7 @@ def processDir (dirName, nzbName=None, recurse=False):
 
         # if there's only one video file in the dir we can use the dirname to process too
         if len(videoFiles) == 1:
-            returnStr += logHelper(u"Auto processing file: "+movedFilePath+" ("+dirName+")")
+            returnStr += logHelper(u"Auto processing file: "+movedFilePath+u" ("+dirName+u")")
             result = processFile(movedFilePath, dirName, nzbName)
 
             # as long as the postprocessing was successful delete the old folder unless the config wants us not to
@@ -208,7 +206,7 @@ def processDir (dirName, nzbName=None, recurse=False):
                 returnStr += result[0]
 
                 if not sickbeard.KEEP_PROCESSED_DIR and \
-                    os.path.normpath(dirName) != os.path.normpath(sickbeard.TV_DOWNLOAD_DIR) and \
+                    ek.ek(os.path.normpath, dirName) != ek.ek(os.path.normpath, sickbeard.TV_DOWNLOAD_DIR) and \
                     len(remainingFolders) == 0:
 
                     returnStr += logHelper(u"Deleting folder " + dirName, logger.DEBUG)
@@ -239,13 +237,18 @@ def processDir (dirName, nzbName=None, recurse=False):
 
 def processFile(fileName, downloadDir=None, nzbName=None, multi_file=False):
 
+    logger.log(u"fileName: "+repr(fileName)+u" downloadDir: "+repr(downloadDir)+u" nzbName: "+repr(nzbName), logger.DEBUG)
+
     returnStr = ''
 
-    folderName = None
+    folderName = ''
     if downloadDir != None:
         folderName = downloadDir.split(os.path.sep)[-1]
 
-    returnStr += logHelper(u"Processing file "+fileName+" (with folder name "+str(folderName)+" and NZB name "+str(nzbName)+")", logger.DEBUG)
+    if not nzbName:
+        nzbName = ''
+
+    returnStr += logHelper(u"Processing file "+fileName+u" (with folder name "+folderName+u" and NZB name "+nzbName+u")", logger.DEBUG)
 
     finalNameList = []
 
@@ -267,14 +270,14 @@ def processFile(fileName, downloadDir=None, nzbName=None, multi_file=False):
 
         historyResult = findInHistory(curName)
         if historyResult:
-            returnStr += logHelper(u"Result from history: "+str(historyResult)+" from "+curName, logger.DEBUG)
+            returnStr += logHelper(u"Result from history: "+str(historyResult)+u" from "+curName, logger.DEBUG)
             (tvdb_id, season, episodes) = historyResult
             showResults = helpers.findCertainShow(sickbeard.showList, tvdb_id)
             break
 
     # if we're parsing a multi-file folder then the folder name doesn't reflect the correct episode so ignore it
     if multi_file and episodes:
-        returnStr += logHelper(u"Multi-file dir "+downloadDir+" doesn't reflect all episode names, only using name & season", logger.DEBUG)
+        returnStr += logHelper(u"Multi-file dir "+downloadDir+u" doesn't reflect all episode names, only using name & season", logger.DEBUG)
         episodes = []
 
     # if that didn't work then try manually parsing and searching them on TVDB
@@ -299,18 +302,18 @@ def processFile(fileName, downloadDir=None, nzbName=None, multi_file=False):
             season = result.seasonnumber if result.seasonnumber != None else 1
             episodes = result.episodenumbers
 
-            returnStr += logHelper(u"Ended up with season "+str(season)+" and episodes "+str(episodes), logger.DEBUG)
+            returnStr += logHelper(u"Ended up with season "+str(season)+u" and episodes "+str(episodes), logger.DEBUG)
 
         except tvnamer_exceptions.InvalidFilename:
-            returnStr += logHelper(u"Unable to parse the filename "+curName+" into a valid episode", logger.DEBUG)
+            returnStr += logHelper(u"Unable to parse the filename "+curName+u" into a valid episode", logger.DEBUG)
             continue
 
         if not result.seriesname:
-            returnStr += logHelper(u"Filename "+curName+" has no series name, unable to use this name for processing", logger.DEBUG)
+            returnStr += logHelper(u"Filename "+curName+u" has no series name, unable to use this name for processing", logger.DEBUG)
             continue
 
         if not episodes:
-            returnStr += logHelper(u"Unable to find an episode number in the filename "+curName+", skipping", logger.DEBUG)
+            returnStr += logHelper(u"Unable to find an episode number in the filename "+curName+u", skipping", logger.DEBUG)
             continue
 
         # reverse-lookup the scene exceptions
@@ -322,7 +325,7 @@ def processFile(fileName, downloadDir=None, nzbName=None, multi_file=False):
                     sceneID = exceptionID
                     break
             if sceneID:
-                returnStr += logHelper(u"Scene exception lookup got tvdb id "+str(sceneID)+", using that", logger.DEBUG)
+                returnStr += logHelper(u"Scene exception lookup got tvdb id "+str(sceneID)+u", using that", logger.DEBUG)
                 break
 
         if sceneID:
@@ -334,13 +337,13 @@ def processFile(fileName, downloadDir=None, nzbName=None, multi_file=False):
 
             # get the tvdb object from either the scene exception ID or the series name
             if tvdb_id:
-                returnStr += logHelper(u"Looking up ID "+str(tvdb_id)+" on TVDB", logger.DEBUG)
+                returnStr += logHelper(u"Looking up ID "+str(tvdb_id)+u" on TVDB", logger.DEBUG)
                 showObj = t[tvdb_id]
             else:
-                returnStr += logHelper(u"Looking up name "+result.seriesname+" on TVDB", logger.DEBUG)
+                returnStr += logHelper(u"Looking up name "+result.seriesname+u" on TVDB", logger.DEBUG)
                 showObj = t[result.seriesname]
 
-            returnStr += logHelper(u"Got tvdb_id "+str(showObj["id"])+" and series name "+showObj["seriesname"].decode('utf-8')+" from TVDB", logger.DEBUG)
+            returnStr += logHelper(u"Got tvdb_id "+str(showObj["id"])+u" and series name "+showObj["seriesname"].decode('utf-8')+u" from TVDB", logger.DEBUG)
 
             showInfo = (int(showObj["id"]), showObj["seriesname"])
 
@@ -369,12 +372,12 @@ def processFile(fileName, downloadDir=None, nzbName=None, multi_file=False):
                 season = int(epObj["seasonnumber"])
                 episodes = [int(epObj["episodenumber"])]
             except tvdb_exceptions.tvdb_episodenotfound, e:
-                returnStr += logHelper(u"Unable to find episode with date "+str(episodes[0])+" for show "+showObj["seriesname"]+", skipping", logger.DEBUG)
+                returnStr += logHelper(u"Unable to find episode with date "+str(episodes[0])+u" for show "+showObj["seriesname"]+u", skipping", logger.DEBUG)
                 continue
 
         # if we couldn't get the necessary info from either of the above methods, try the next name
         if tvdb_id == None or season == None or episodes == []:
-            returnStr += logHelper(u"Unable to get all the necessary info, ended up with tvdb_id "+str(tvdb_id)+", season "+str(season)+", and episodes "+str(episodes)+". Skipping to the next name...", logger.DEBUG)
+            returnStr += logHelper(u"Unable to get all the necessary info, ended up with tvdb_id "+str(tvdb_id)+u", season "+str(season)+u", and episodes "+str(episodes)+u". Skipping to the next name...", logger.DEBUG)
             continue
 
         # find the show in the showlist
@@ -393,7 +396,7 @@ def processFile(fileName, downloadDir=None, nzbName=None, multi_file=False):
     if tvdb_id == None or season == None or episodes == []:
         # if we have a good enough result then fine, use it
 
-        returnStr += logHelper(u"Unable to figure out what this episode is, giving up.  Ended up with tvdb_id "+str(tvdb_id)+", season "+str(season)+", and episodes "+str(episodes)+".", logger.DEBUG)
+        returnStr += logHelper(u"Unable to figure out what this episode is, giving up.  Ended up with tvdb_id "+str(tvdb_id)+u", season "+str(season)+u", and episodes "+str(episodes)+u".", logger.DEBUG)
         return returnStr
 
     # if we found enough info but it wasn't a show we know about, give up
@@ -402,7 +405,7 @@ def processFile(fileName, downloadDir=None, nzbName=None, multi_file=False):
         return returnStr
 
     # if we DO know about the show but its dir is offline, give up
-    if not os.path.isdir(showResults._location):
+    if not ek.ek(os.path.isdir, showResults._location):
         returnStr += logHelper(u"The show dir doesn't exist, canceling postprocessing", logger.DEBUG)
         return returnStr
 
@@ -413,7 +416,7 @@ def processFile(fileName, downloadDir=None, nzbName=None, multi_file=False):
     newQuality = Quality.UNKNOWN
     for curName in finalNameList:
         curNewQuality = Quality.nameQuality(curName)
-        returnStr += logHelper(u"Looking up quality for name "+curName+", got "+Quality.qualityStrings[curNewQuality], logger.DEBUG)
+        returnStr += logHelper(u"Looking up quality for name "+curName+u", got "+Quality.qualityStrings[curNewQuality], logger.DEBUG)
         # just remember if we find a good quality
         if curNewQuality != Quality.UNKNOWN and newQuality == Quality.UNKNOWN:
             newQuality = curNewQuality
@@ -424,7 +427,7 @@ def processFile(fileName, downloadDir=None, nzbName=None, multi_file=False):
         if newQuality != Quality.UNKNOWN:
             break
         newQuality = Quality.assumeQuality(curName)
-        returnStr += logHelper(u"Guessing quality for name "+curName+", got "+Quality.qualityStrings[curNewQuality], logger.DEBUG)
+        returnStr += logHelper(u"Guessing quality for name "+curName+u", got "+Quality.qualityStrings[curNewQuality], logger.DEBUG)
         if newQuality != Quality.UNKNOWN:
             break
 
@@ -459,8 +462,8 @@ def processFile(fileName, downloadDir=None, nzbName=None, multi_file=False):
             curEp.status = Quality.compositeStatus(SNATCHED, newQuality)
 
     # figure out the new filename
-    biggestFileName = os.path.basename(fileName)
-    biggestFileExt = os.path.splitext(biggestFileName)[1]
+    biggestFileName = ek.ek(os.path.basename, fileName)
+    biggestFileExt = ek.ek(os.path.splitext, biggestFileName)[1]
 
     # if we're supposed to put it in a season folder then figure out what folder to use
     seasonFolder = ''
@@ -469,7 +472,7 @@ def processFile(fileName, downloadDir=None, nzbName=None, multi_file=False):
         # search the show dir for season folders
         for curDir in os.listdir(rootEp.show.location):
 
-            if not os.path.isdir(os.path.join(rootEp.show.location, curDir)):
+            if not ek.ek(os.path.isdir, ek.ek(os.path.join, rootEp.show.location, curDir)):
                 continue
 
             # if it's a season folder, check if it's the one we want
@@ -486,18 +489,18 @@ def processFile(fileName, downloadDir=None, nzbName=None, multi_file=False):
             if rootEp.show.is_air_by_date:
                 seasonFolder = str(rootEp.airdate.year)
             else:
-                seasonFolder = 'Season ' + str(rootEp.season)
+                seasonFolder = sickbeard.SEASON_FOLDERS_FORMAT % (rootEp.season)
 
     returnStr += logHelper(u"Season folders were " + str(rootEp.show.seasonfolders) + " which gave " + seasonFolder, logger.DEBUG)
 
-    destDir = os.path.join(rootEp.show.location, seasonFolder)
+    destDir = ek.ek(os.path.join, rootEp.show.location, seasonFolder)
 
     # movedFilePath is the full path to where we will move the file
-    movedFilePath = os.path.join(destDir, biggestFileName)
+    movedFilePath = ek.ek(os.path.join, destDir, biggestFileName)
 
     # renamedFilePath is the full path to the renamed file's eventual location
     if sickbeard.RENAME_EPISODES:
-        renamedFilePath = os.path.join(destDir, helpers.sanitizeFileName(rootEp.prettyName())+biggestFileExt)
+        renamedFilePath = ek.ek(os.path.join, destDir, helpers.sanitizeFileName(rootEp.prettyName())+biggestFileExt)
     else:
         renamedFilePath = movedFilePath
     returnStr += logHelper(u"The ultimate destination for " + fileName + " is " + renamedFilePath, logger.DEBUG)
@@ -517,20 +520,20 @@ def processFile(fileName, downloadDir=None, nzbName=None, multi_file=False):
     # see if the existing file is bigger - if it is, bail (unless it's a proper or better quality in which case we're forcing an overwrite)
     if existingResult > 0:
         if rootEp.status in Quality.SNATCHED_PROPER:
-            returnStr += logHelper(u"There is already a file that's bigger at "+renamedFilePath+" but I'm going to overwrite it with a PROPER", logger.DEBUG)
+            returnStr += logHelper(u"There is already a file that's bigger at "+renamedFilePath+u" but I'm going to overwrite it with a PROPER", logger.DEBUG)
         elif oldStatus != None:
-            returnStr += logHelper(u"There is already a file that's bigger at "+renamedFilePath+" but I'm going to overwrite it because this one seems to have been downloaded on purpose", logger.DEBUG)
+            returnStr += logHelper(u"There is already a file that's bigger at "+renamedFilePath+u" but I'm going to overwrite it because this one seems to have been downloaded on purpose", logger.DEBUG)
         else:
-            returnStr += logHelper(u"There is already a file that's bigger at "+renamedFilePath+" - not processing this episode.", logger.DEBUG)
+            returnStr += logHelper(u"There is already a file that's bigger at "+renamedFilePath+u" - not processing this episode.", logger.DEBUG)
 
             # tag the dir so we know what happened
             if downloadDir:
                 try:
-                    oldDirName = os.path.abspath(downloadDir)
-                    baseDirPath = os.path.dirname(oldDirName)
-                    endDirPath = os.path.basename(oldDirName)
+                    oldDirName = ek.ek(os.path.abspath, downloadDir)
+                    baseDirPath = ek.ek(os.path.dirname, oldDirName)
+                    endDirPath = ek.ek(os.path.basename, oldDirName)
                     newDirPath = ek.ek(os.path.join, baseDirPath, '_UNDERSIZED_'+endDirPath)
-                    returnStr += logHelper(u"Renaming the parent folder to indicate that the post process was failed: "+downloadDir+" -> "+newDirPath, logger.DEBUG)
+                    returnStr += logHelper(u"Renaming the parent folder to indicate that the post process was failed: "+downloadDir+u" -> "+newDirPath, logger.DEBUG)
                     os.rename(oldDirName, newDirPath)
                 except (OSError, IOError), e:
                     returnStr += logHelper(u"Failed renaming " + oldDirName + " to " + newDirPath + ": " + str(e), logger.ERROR)
@@ -538,7 +541,7 @@ def processFile(fileName, downloadDir=None, nzbName=None, multi_file=False):
             return returnStr
 
     # if the dir doesn't exist (new season folder) then make it
-    if not os.path.isdir(destDir):
+    if not ek.ek(os.path.isdir, destDir):
         returnStr += logHelper(u"Season folder didn't exist, creating it", logger.DEBUG)
         os.mkdir(destDir)
 
@@ -622,7 +625,7 @@ def processFile(fileName, downloadDir=None, nzbName=None, multi_file=False):
     for curScriptName in sickbeard.EXTRA_SCRIPTS:
         script_cmd = shlex.split(curScriptName) + [rootEp.location, biggestFileName, str(tvdb_id), str(season), str(episode), str(rootEp.airdate)]
         returnStr += logHelper(u"Executing command "+str(script_cmd))
-        logger.log(u"Absolute path to script: "+os.path.abspath(script_cmd[0]), logger.DEBUG)
+        logger.log(u"Absolute path to script: "+ek.ek(os.path.abspath, script_cmd[0]), logger.DEBUG)
         try:
             p = subprocess.Popen(script_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=sickbeard.PROG_DIR)
             out, err = p.communicate()
diff --git a/sickbeard/providers/generic.py b/sickbeard/providers/generic.py
index e3c67a9342e9362c22cc4b6e87ea3cb46caa74d2..43316771f7f0b7dfebdcde04f3da002151d153f4 100644
--- a/sickbeard/providers/generic.py
+++ b/sickbeard/providers/generic.py
@@ -188,9 +188,21 @@ class GenericProvider:
             title = item.findtext('title')
             url = item.findtext('link').replace('&amp;','&')
 
+            # parse the file name
+            try:
+                myParser = FileParser(title)
+                epInfo = myParser.parse()
+            except tvnamer_exceptions.InvalidFilename:
+                logger.log(u"Unable to parse the filename "+title+" into a valid episode", logger.WARNING)
+                continue
+
+            if epInfo.seasonnumber != episode.season or episode.episode not in epInfo.episodenumbers:
+                logger.log("Episode "+title+" isn't "+str(episode.season)+"x"+str(episode.episode)+", skipping it", logger.DEBUG)
+                continue
+
             quality = self.getQuality(item)
 
-            if not episode.show.wantEpisode(episode.season, episode.episode, quality, manualSearch):
+            if not episode.show.wantEpisode(epInfo.seasonnumber, epInfo.episodenumbers[0], quality, manualSearch):
                 logger.log(u"Ignoring result "+title+" because we don't want an episode that is "+Quality.qualityStrings[quality], logger.DEBUG)
                 continue
 
diff --git a/sickbeard/providers/newzbin.py b/sickbeard/providers/newzbin.py
index cc2102c9e74909944e3f6719ca96e237880ae808..a160e615333c5d824f0a0179b31de5ed38fbe6d0 100644
--- a/sickbeard/providers/newzbin.py
+++ b/sickbeard/providers/newzbin.py
@@ -56,9 +56,9 @@ class NewzbinDownloader(urllib.FancyURLopener):
             elif newzbinErrCode == 402:
                 raise exceptions.AuthException("Newzbin account not premium status, can't download NZBs")
 
-            logger.log("Newzbin throttled our NZB downloading, pausing for " + result.group() + "seconds")
+            logger.log("Newzbin throttled our NZB downloading, pausing for " + result.group(1) + "seconds")
 
-            time.sleep(int(result.group()))
+            time.sleep(int(result.group(1)))
 
             raise exceptions.NewzbinAPIThrottled()
 
diff --git a/sickbeard/providers/newznab.py b/sickbeard/providers/newznab.py
index 61d374c9440390ce286863c8ffe387693e4c11c3..c381514e33121f0a8a9bb476eb9d35934df76e36 100644
--- a/sickbeard/providers/newznab.py
+++ b/sickbeard/providers/newznab.py
@@ -126,6 +126,10 @@ class NewznabProvider(generic.NZBProvider):
 		logger.log(u"Search url: " + searchURL, logger.DEBUG)
 
 		data = self.getURL(searchURL)
+		
+		# hack this in until it's fixed server side
+		if not data.startswith('<?xml'):
+			data = '<?xml version="1.0" encoding="ISO-8859-1" ?>' + data
 
 		if data == None:
 			return []
@@ -213,6 +217,10 @@ class NewznabCache(tvcache.TVCache):
 
 		data = self.provider.getURL(url)
 
+		# hack this in until it's fixed server side
+		if not data.startswith('<?xml'):
+			data = '<?xml version="1.0" encoding="ISO-8859-1" ?>' + data
+
 		return data
 
 	def _checkAuth(self, data):
diff --git a/sickbeard/queue.py b/sickbeard/queue.py
index 8205501a7680f4c76a434674ff57c3592b594ba4..8fa878f3f4eb9bfa41e33f5efb6715a54098bdef 100644
--- a/sickbeard/queue.py
+++ b/sickbeard/queue.py
@@ -12,7 +12,7 @@ from sickbeard.tv import TVShow
 from sickbeard import exceptions
 from sickbeard import helpers
 from sickbeard import logger
-from sickbeard import webserve
+from sickbeard import ui
 
 
 class ShowQueue:
@@ -234,15 +234,15 @@ class QueueItemAdd(QueueItem):
         except tvdb_exceptions.tvdb_exception, e:
             logger.log(u"Unable to add show due to an error with TVDB: "+str(e).decode('utf-8'), logger.ERROR)
             if self.show:
-                webserve.flash.error("Unable to add "+str(self.show.name)+" due to an error with TVDB")
+                ui.flash.error("Unable to add "+str(self.show.name)+" due to an error with TVDB")
             else:
-                webserve.flash.error("Unable to add show due to an error with TVDB")
+                ui.flash.error("Unable to add show due to an error with TVDB")
             self._finishEarly()
             return
 
         except exceptions.MultipleShowObjectsException:
             logger.log(u"The show in " + self.showDir + " is already in your show list, skipping", logger.ERROR)
-            webserve.flash.error("The show in " + self.showDir + " is already in your show list, skipping")
+            ui.flash.error("The show in " + self.showDir + " is already in your show list, skipping")
             self._finishEarly()
             return
 
diff --git a/sickbeard/sceneHelpers.py b/sickbeard/sceneHelpers.py
index ffa0e5231fbf9b37eba6caa5daae2d8ecf5b8d31..386b1c0747255bb4c521ce9b96e426666410e598 100644
--- a/sickbeard/sceneHelpers.py
+++ b/sickbeard/sceneHelpers.py
@@ -66,7 +66,12 @@ def sanitizeSceneName (name):
 
 def sceneToNormalShowNames(name):
 
-    return [name, name.replace(".and.", ".&.")]
+    results = [name]
+    
+    if '.and.' in name:
+        results.append(name.replace('.and.', '.&.'))
+
+    return results
 
 def makeSceneShowSearchStrings(show):
 
@@ -88,7 +93,7 @@ def makeSceneSeasonSearchString (show, segment, extraSearchType=None):
     
     else:
         numseasonsSQlResult = myDB.select("SELECT COUNT(DISTINCT season) as numseasons FROM tv_episodes WHERE showid = ? and season != 0", [show.tvdbid])
-        numseasons = numseasonsSQlResult[0][0]
+        numseasons = int(numseasonsSQlResult[0][0])
 
         seasonStrings = ["S%02d" % segment]
         # since nzbmatrix allows more than one search per request we search SxEE results too
@@ -127,6 +132,10 @@ def makeSceneSeasonSearchString (show, segment, extraSearchType=None):
 
 def makeSceneSearchString (episode):
 
+    myDB = db.DBConnection()
+    numseasonsSQlResult = myDB.select("SELECT COUNT(DISTINCT season) as numseasons FROM tv_episodes WHERE showid = ? and season != 0", [episode.show.tvdbid])
+    numseasons = int(numseasonsSQlResult[0][0])
+
     # see if we should use dates instead of episodes
     if episode.show.is_air_by_date and episode.airdate != datetime.date.fromordinal(1):
         epStrings = [str(episode.airdate)]
@@ -134,6 +143,10 @@ def makeSceneSearchString (episode):
         epStrings = ["S%02iE%02i" % (int(episode.season), int(episode.episode)),
                     "%ix%02i" % (int(episode.season), int(episode.episode))]
 
+    # for single-season shows just search for the show name
+    if numseasons == 1:
+        epStrings = ['']
+
     showNames = set(makeSceneShowSearchStrings(episode.show))
 
     toReturn = []
@@ -157,16 +170,20 @@ def allPossibleShowNames(show):
 
     newShowNames = []
 
+    country_list = countryList
+    country_list.update(dict(zip(countryList.values(), countryList.keys())))
+
     # if we have "Show Name Australia" or "Show Name (Australia)" this will add "Show Name (AU)" for
     # any countries defined in common.countryList
+    # (and vice versa)
     for curName in showNames:
-        for curCountry in countryList:
+        for curCountry in country_list:
             if curName.endswith(' '+curCountry):
-                logger.log(u"Show names ends with "+curCountry+", so trying to add ("+countryList[curCountry]+") to it as well", logger.DEBUG)
-                newShowNames.append(curName.replace(' '+curCountry, ' ('+countryList[curCountry]+')'))
+                logger.log(u"Show names ends with "+curCountry+", so trying to add ("+country_list[curCountry]+") to it as well", logger.DEBUG)
+                newShowNames.append(curName.replace(' '+curCountry, ' ('+country_list[curCountry]+')'))
             elif curName.endswith(' ('+curCountry+')'):
-                logger.log(u"Show names ends with "+curCountry+", so trying to add ("+countryList[curCountry]+") to it as well", logger.DEBUG)
-                newShowNames.append(curName.replace(' ('+curCountry+')', ' ('+countryList[curCountry]+')'))
+                logger.log(u"Show names ends with "+curCountry+", so trying to add ("+country_list[curCountry]+") to it as well", logger.DEBUG)
+                newShowNames.append(curName.replace(' ('+curCountry+')', ' ('+country_list[curCountry]+')'))
 
     showNames += newShowNames
 
diff --git a/sickbeard/searchCurrent.py b/sickbeard/searchCurrent.py
index 3defa6512e0111465e732a72187ce626040fd143..c78902b62f82d5777eaf67e4fffa535bc6e1fd0b 100644
--- a/sickbeard/searchCurrent.py
+++ b/sickbeard/searchCurrent.py
@@ -38,14 +38,16 @@ class CurrentSearcher():
 
         self.amActive = True
 
-        # pause the backlog to prevent race conditions downloading 2 episodes
-        logger.log(u"Pausing backlog so it doesn't collide with episode search", logger.DEBUG)
-        sickbeard.backlogSearchScheduler.action.amPaused = True
-        while sickbeard.backlogSearchScheduler.action.am_running():
-            logger.log(u"Backlog isn't waiting yet, trying again in 1s", logger.DEBUG)
-            time.sleep(1)
+        backlogPaused = sickbeard.backlogSearchScheduler.action.amPaused
+        if not backlogPaused:
+            # pause the backlog to prevent race conditions downloading 2 episodes
+            logger.log(u"Pausing backlog so it doesn't collide with episode search", logger.DEBUG)
+            sickbeard.backlogSearchScheduler.action.amPaused = True
+            while sickbeard.backlogSearchScheduler.action.am_running():
+                logger.log(u"Backlog isn't waiting yet, trying again in 1s", logger.DEBUG)
+                time.sleep(1)
 
-        logger.log(u"Backlog has stopped, running search now", logger.DEBUG)
+        logger.log(u"Backlog is stopped, running search now", logger.DEBUG)
 
         self._changeMissingEpisodes()
 
@@ -71,8 +73,11 @@ class CurrentSearcher():
         sickbeard.updateAiringList()
         sickbeard.updateComingList()
 
-        logger.log(u"Search is done, resuming backlog", logger.DEBUG)
-        sickbeard.backlogSearchScheduler.action.amPaused = False
+        if not backlogPaused:
+            logger.log(u"Search is done, resuming backlog", logger.DEBUG)
+            sickbeard.backlogSearchScheduler.action.amPaused = False
+        else:
+            logger.log(u"Search is done, leaving backlog paused", logger.DEBUG)
 
         self.amActive = False
 
diff --git a/sickbeard/tv.py b/sickbeard/tv.py
index 5a922ee93eebbb23f2ab532eb5ca926ffe114b14..6558d3854d7381a1833ae4c2d37dd44de7e57894 100644
--- a/sickbeard/tv.py
+++ b/sickbeard/tv.py
@@ -812,6 +812,7 @@ class TVShow(object):
         logger.log(u"any,best = "+str(anyQualities)+" "+str(bestQualities)+" and we are "+str(quality), logger.DEBUG)
 
         if quality not in anyQualities + bestQualities:
+            logger.log(u"I know for sure I don't want this episode, saying no", logger.DEBUG)
             return False
 
         myDB = db.DBConnection()
@@ -838,6 +839,8 @@ class TVShow(object):
             elif manualSearch:
                 logger.log(u"Usually I would ignore this ep but because you forced the search I'm overriding the default and allowing the quality", logger.DEBUG)
                 return True
+            else:
+                logger.log(u"This quality looks like something we might want but I don't know for sure yet", logger.DEBUG)
 
         curStatus, curQuality = Quality.splitCompositeStatus(epStatus)
 
diff --git a/sickbeard/tvcache.py b/sickbeard/tvcache.py
index de53cd04132b15759fe4dd184c42b3233cf039e1..562baba56f93a042229fa4f63cd73105af7accfa 100644
--- a/sickbeard/tvcache.py
+++ b/sickbeard/tvcache.py
@@ -217,6 +217,7 @@ class TVCache():
                         if sceneHelpers.isGoodResult(name, curShow, False):
                             logger.log(u"Successfully matched "+name+" to "+curShow.name+" with regex", logger.DEBUG)
                             tvdb_id = curShow.tvdbid
+                            break
 
                 if tvdb_id:
 
diff --git a/sickbeard/tvrage.py b/sickbeard/tvrage.py
index ba9e7a454cc81837f071cd0a93bb2720c0d1224e..fb7e4f33399ef83dee83f596053c3cc27c307c26 100644
--- a/sickbeard/tvrage.py
+++ b/sickbeard/tvrage.py
@@ -47,16 +47,21 @@ class TVRage:
         if self.show.tvrid == 0:
 
             # if it's the right show then use the tvrage ID that the last lookup found (cached in self._trvid)
-            if self.confirmShow() or self.checkSync():
+            show_is_right = self.confirmShow() or self.checkSync()
 
-                if self._tvrid == 0 or self._tvrname == None:
-                    raise exceptions.TVRageException("We confirmed sync but got invalid data (no ID/name)")
+            if not show_is_right:
+                raise exceptions.TVRageException("Shows aren't the same, aborting")
+
+            if self._tvrid == 0 or self._tvrname == None:
+                raise exceptions.TVRageException("We confirmed sync but got invalid data (no ID/name)")
+
+            if show_is_right:
 
                 logger.log(u"Setting TVRage ID for "+show.name+" to "+str(self._tvrid))
                 self.show.tvrid = self._tvrid
                 self.show.saveToDB()
 
-        if self.show.tvrname == "" or self.show.tvrname == None:
+        if not self.show.tvrname:
 
             if self._tvrname == None:
                 self._getTVRageInfo()
diff --git a/sickbeard/ui.py b/sickbeard/ui.py
index 56768d969942c993a095dbeccbd1c7c2c7691925..2cb39305aa570520234ee43c5e7a0ca96bd4e18f 100644
--- a/sickbeard/ui.py
+++ b/sickbeard/ui.py
@@ -127,3 +127,26 @@ def addShowsFromRootDir(dir):
             del sickbeard.loadingShowList[showDir]
 
     return returnStr
+
+class Flash:
+    _messages = []
+    _errors = []
+
+    def message(self, title, detail=''):
+        Flash._messages.append((title, detail))
+
+    def error(self, title, detail=''):
+        Flash._errors.append((title, detail))
+
+    def messages(self):
+        tempMessages = Flash._messages
+        Flash._messages = []
+        return tempMessages
+
+    def errors(self):
+        tempErrors = Flash._errors
+        Flash._errors = []
+        return tempErrors
+
+flash = Flash()
+
diff --git a/sickbeard/versionChecker.py b/sickbeard/versionChecker.py
index 9bdba4938adbd84f041428cfa91b6d2a0ba3b608..f7c304b3691212ced608bf31a0235dfd6ae322ab 100644
--- a/sickbeard/versionChecker.py
+++ b/sickbeard/versionChecker.py
@@ -77,7 +77,7 @@ class CheckVersion():
 
 class UpdateManager():
     def get_update_url(self):
-        return sickbeard.WEB_ROOT+"/home/update/?pid="+str(os.getpid())
+        return sickbeard.WEB_ROOT+"/home/update/?pid="+str(sickbeard.PID)
 
 class WindowsUpdateManager(UpdateManager):
 
diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py
index 364262d772bc64e0171edbccf672d953423d0d2d..1a0ceb68383f85ed64c67b6df911bcbe0c210ad9 100644
--- a/sickbeard/webserve.py
+++ b/sickbeard/webserve.py
@@ -35,7 +35,7 @@ import metadata.helpers
 
 from sickbeard import config
 from sickbeard import history, notifiers, processTV, search, providers
-from sickbeard import tv, versionChecker
+from sickbeard import tv, versionChecker, ui
 from sickbeard import logger, helpers, exceptions, classes, db
 from sickbeard import encodingKludge as ek
 
@@ -58,28 +58,6 @@ import sickbeard
 from sickbeard import browser
 
 
-class Flash:
-    _messages = []
-    _errors = []
-
-    def message(self, title, detail=''):
-        Flash._messages.append((title, detail))
-
-    def error(self, title, detail=''):
-        Flash._errors.append((title, detail))
-
-    def messages(self):
-        tempMessages = Flash._messages
-        Flash._messages = []
-        return tempMessages
-
-    def errors(self):
-        tempErrors = Flash._errors
-        Flash._errors = []
-        return tempErrors
-
-flash = Flash()
-
 class PageTemplate (Template):
     def __init__(self, *args, **KWs):
         KWs['file'] = os.path.join(sickbeard.PROG_DIR, "data/interfaces/default/", KWs['file'])
@@ -99,7 +77,7 @@ class PageTemplate (Template):
             { 'title': 'Config',          'key': 'config'         },
             { 'title': logPageTitle,      'key': 'errorlogs'      },
         ]
-        self.flash = Flash()
+        self.flash = ui.Flash()
 
 def redirect(abspath, *args, **KWs):
     assert abspath[0] == '/'
@@ -123,7 +101,7 @@ def _munge(string):
 
 def _genericMessage(subject, message):
     t = PageTemplate(file="genericMessage.tmpl")
-    t.submenu = HomeMenu
+    t.submenu = HomeMenu()
     t.subject = subject
     t.message = message
     return _munge(t)
@@ -169,7 +147,7 @@ class ManageSearches:
         # force it to run the next time it looks
         sickbeard.backlogSearchScheduler.forceSearch()
         logger.log(u"Backlog search started in background")
-        flash.message('Backlog search started',
+        ui.flash.message('Backlog search started',
                       'The backlog search has begun and will run in the background')
         redirect("/manage/manageSearches")
 
@@ -180,7 +158,7 @@ class ManageSearches:
         result = sickbeard.currentSearchScheduler.forceRun()
         if result:
             logger.log(u"Search forced")
-            flash.message('Episode search started',
+            ui.flash.message('Episode search started',
                           'Note: RSS feeds may not be updated if they have been retrieved too recently')
 
         redirect("/manage/manageSearches")
@@ -325,7 +303,7 @@ class Manage:
                 errors.append('<b>%s:</b><br />\n<ul>' % showObj.name + '\n'.join(['<li>%s</li>' % error for error in curErrors]) + "</ul>")
 
         if len(errors) > 0:
-            flash.error('%d error%s while saving changes:' % (len(errors), "" if len(errors) == 1 else "s"),
+            ui.flash.error('%d error%s while saving changes:' % (len(errors), "" if len(errors) == 1 else "s"),
                         "<br />\n".join(errors))
 
         redirect("/manage")
@@ -388,7 +366,7 @@ class Manage:
 
 
         if len(errors) > 0:
-            flash.error("Errors encountered",
+            ui.flash.error("Errors encountered",
                         '<br >\n'.join(errors))
 
         messageDetail = ""
@@ -409,7 +387,7 @@ class Manage:
             messageDetail += "</li>\n</ul>\n<br />"
 
         if len(updates+refreshes+renames) > 0:
-            flash.message("The following actions were queued:<br /><br />",
+            ui.flash.message("The following actions were queued:<br /><br />",
                           messageDetail)
 
         redirect("/manage")
@@ -440,7 +418,7 @@ class History:
 
         myDB = db.DBConnection()
         myDB.action("DELETE FROM history WHERE 1=1")
-        flash.message('History cleared')
+        ui.flash.message('History cleared')
         redirect("/history")
 
 
@@ -449,7 +427,7 @@ class History:
 
         myDB = db.DBConnection()
         myDB.action("DELETE FROM history WHERE date < "+str((datetime.datetime.today()-datetime.timedelta(days=30)).strftime(history.dateFormat)))
-        flash.message('Removed all history entries greater than 30 days old')
+        ui.flash.message('Removed all history entries greater than 30 days old')
         redirect("/history")
 
 
@@ -472,7 +450,7 @@ class ConfigGeneral:
     @cherrypy.expose
     def saveGeneral(self, log_dir=None, web_port=None, web_log=None, web_ipv6=None,
                     launch_browser=None, web_username=None,
-                    web_password=None, season_folders_default=None,
+                    web_password=None, season_folders_format=None, season_folders_default=None,
                     version_notify=None, naming_show_name=None, naming_ep_type=None,
                     naming_multi_ep_type=None, naming_ep_name=None,
                     naming_use_periods=None, naming_sep_type=None, naming_quality=None,
@@ -586,6 +564,7 @@ class ConfigGeneral:
         sickbeard.ART_THUMBNAILS = art_thumbnails
         sickbeard.ART_SEASON_THUMBNAILS = art_season_thumbnails
 
+        sickbeard.SEASON_FOLDERS_FORMAT = season_folders_format
         sickbeard.SEASON_FOLDERS_DEFAULT = int(season_folders_default)
         sickbeard.QUALITY_DEFAULT = newQuality
 
@@ -611,10 +590,10 @@ class ConfigGeneral:
         if len(results) > 0:
             for x in results:
                 logger.log(x, logger.ERROR)
-            flash.error('Error(s) Saving Configuration',
+            ui.flash.error('Error(s) Saving Configuration',
                         '<br />\n'.join(results))
         else:
-            flash.message('Configuration Saved')
+            ui.flash.message('Configuration Saved')
 
         redirect("/config/general/")
 
@@ -793,10 +772,10 @@ class ConfigEpisodeDownloads:
         if len(results) > 0:
             for x in results:
                 logger.log(x, logger.ERROR)
-            flash.error('Error(s) Saving Configuration',
+            ui.flash.error('Error(s) Saving Configuration',
                         '<br />\n'.join(results))
         else:
-            flash.message('Configuration Saved')
+            ui.flash.message('Configuration Saved')
 
         redirect("/config/episodedownloads/")
 
@@ -966,10 +945,10 @@ class ConfigProviders:
         if len(results) > 0:
             for x in results:
                 logger.log(x, logger.ERROR)
-            flash.error('Error(s) Saving Configuration',
+            ui.flash.error('Error(s) Saving Configuration',
                         '<br />\n'.join(results))
         else:
-            flash.message('Configuration Saved')
+            ui.flash.message('Configuration Saved')
 
         redirect("/config/providers/")
 
@@ -1037,10 +1016,10 @@ class ConfigNotifications:
         if len(results) > 0:
             for x in results:
                 logger.log(x, logger.ERROR)
-            flash.error('Error(s) Saving Configuration',
+            ui.flash.error('Error(s) Saving Configuration',
                         '<br />\n'.join(results))
         else:
-            flash.message('Configuration Saved')
+            ui.flash.message('Configuration Saved')
 
         redirect("/config/notifications/")
 
@@ -1065,13 +1044,14 @@ class Config:
 def haveXBMC():
     return sickbeard.XBMC_HOST != None and len(sickbeard.XBMC_HOST) > 0
 
-HomeMenu = [
+def HomeMenu():
+    return [
     { 'title': 'Add Shows',              'path': 'home/addShows/'                           },
     { 'title': 'Manual Post-Processing', 'path': 'home/postprocess/'                        },
     { 'title': 'Update XBMC',            'path': 'home/updateXBMC/', 'requires': haveXBMC   },
-    { 'title': 'Restart',                'path': 'home/restart/?pid='+str(os.getpid())      },
+    { 'title': 'Restart',                'path': 'home/restart/?pid='+str(sickbeard.PID)    },
     { 'title': 'Shutdown',               'path': 'home/shutdown/'                           },
-]
+    ]
 
 class HomePostProcess:
 
@@ -1079,7 +1059,7 @@ class HomePostProcess:
     def index(self):
 
         t = PageTemplate(file="home_postprocess.tmpl")
-        t.submenu = HomeMenu
+        t.submenu = HomeMenu()
         return _munge(t)
 
     @cherrypy.expose
@@ -1102,7 +1082,7 @@ class NewHomeAddShows:
     def index(self):
 
         t = PageTemplate(file="home_addShows.tmpl")
-        t.submenu = HomeMenu
+        t.submenu = HomeMenu()
         return _munge(t)
 
     @cherrypy.expose
@@ -1139,7 +1119,7 @@ class NewHomeAddShows:
 
         if not os.path.isdir(dir):
             logger.log(u"The provided directory "+dir+" doesn't exist", logger.ERROR)
-            flash.error("Unable to find the directory <tt>%s</tt>" % dir)
+            ui.flash.error("Unable to find the directory <tt>%s</tt>" % dir)
             redirect("/home/addShows")
 
         showDirs = []
@@ -1152,14 +1132,14 @@ class NewHomeAddShows:
 
         if len(showDirs) == 0:
             logger.log(u"The provided directory "+dir+" has no shows in it", logger.ERROR)
-            flash.error("The provided root folder <tt>%s</tt> has no shows in it." % dir)
+            ui.flash.error("The provided root folder <tt>%s</tt> has no shows in it." % dir)
             redirect("/home/addShows")
 
         #result = ui.addShowsFromRootDir(dir)
 
         myTemplate = PageTemplate(file="home_addRootDir.tmpl")
         myTemplate.showDirs = [urllib.quote_plus(x.encode('utf-8')) for x in showDirs]
-        myTemplate.submenu = HomeMenu
+        myTemplate.submenu = HomeMenu()
         return _munge(myTemplate)
 
         url = "/home/addShows/addShow?"+"&".join(["showDir="+urllib.quote_plus(x.encode('utf-8')) for x in showDirs])
@@ -1178,7 +1158,7 @@ class NewHomeAddShows:
 
         # if we got a TVDB ID then make a show out of it
         sickbeard.showQueueScheduler.action.addShow(int(whichSeries), showToAdd)
-        flash.message('Show added', 'Adding the specified show into '+showToAdd)
+        ui.flash.message('Show added', 'Adding the specified show into '+showToAdd)
         # no need to display anything now that we added the show, so continue on to the next show
         return self.addShows(showDirs)
 
@@ -1196,7 +1176,7 @@ class NewHomeAddShows:
             redirect("/home")
 
         t = PageTemplate(file="home_addShow.tmpl")
-        t.submenu = HomeMenu
+        t.submenu = HomeMenu()
 
         # make sure everything's unescaped
         showDirs = [os.path.normpath(urllib.unquote_plus(x)) for x in showDirs]
@@ -1207,7 +1187,7 @@ class NewHomeAddShows:
 
         # if the dir we're given doesn't exist and we can't create it then skip it
         if not helpers.makeDir(showToAdd):
-            flash.error("Warning", "Unable to create dir "+showToAdd+", skipping")
+            ui.flash.error("Warning", "Unable to create dir "+showToAdd+", skipping")
             # recursively continue on our way, encoding the input as though we came from the web form
             return self.addShows([urllib.quote_plus(x.encode('utf-8')) for x in restOfShowDirs])
 
@@ -1219,18 +1199,18 @@ class NewHomeAddShows:
             except exceptions.NoNFOException, e:
                 # we couldn't get a tvdb id from the file so let them know and just print the search page
                 if ek.ek(os.path.isfile, ek.ek(os.path.join, showToAdd, "tvshow.nfo.old")):
-                    flash.error('Warning', 'Unable to retrieve TVDB ID from tvshow.nfo, renamed it to tvshow.nfo.old and ignoring it')
+                    ui.flash.error('Warning', 'Unable to retrieve TVDB ID from tvshow.nfo, renamed it to tvshow.nfo.old and ignoring it')
 
                 # no tvshow.nfo.old means we couldn't rename it and we can't continue adding this show
                 # encode the input as though we came from the web form
                 else:
-                    flash.error('Warning', 'Unable to retrieve TVDB ID from tvshow.nfo and unable to rename it - you will need to remove it manually')
+                    ui.flash.error('Warning', 'Unable to retrieve TVDB ID from tvshow.nfo and unable to rename it - you will need to remove it manually')
                     return self.addShows([urllib.quote_plus(x.encode('utf-8')) for x in restOfShowDirs])
 
             # if we got a TVDB ID then make a show out of it
             if tvdb_id:
                 sickbeard.showQueueScheduler.action.addShow(tvdb_id, showToAdd)
-                flash.message('Show added', 'Auto-added show from tvshow.nfo in '+showToAdd)
+                ui.flash.message('Show added', 'Auto-added show from tvshow.nfo in '+showToAdd)
                 # no need to display anything now that we added the show, so continue on to the next show
                 return self.addShows([urllib.quote_plus(x.encode('utf-8')) for x in restOfShowDirs])
 
@@ -1329,7 +1309,7 @@ class Home:
     def index(self):
 
         t = PageTemplate(file="home.tmpl")
-        t.submenu = HomeMenu
+        t.submenu = HomeMenu()
         return _munge(t)
 
     addShows = NewHomeAddShows()
@@ -1380,7 +1360,7 @@ class Home:
     @cherrypy.expose
     def restart(self, pid=None):
 
-        if str(pid) != str(os.getpid()):
+        if str(pid) != str(sickbeard.PID):
             redirect("/home")
 
         # do a soft restart
@@ -1394,7 +1374,7 @@ class Home:
     @cherrypy.expose
     def update(self, pid=None):
 
-        if str(pid) != str(os.getpid()):
+        if str(pid) != str(sickbeard.PID):
             redirect("/home")
 
         updated = sickbeard.versionCheckScheduler.action.update()
@@ -1434,19 +1414,19 @@ class Home:
             t.showLoc = (showObj._location, False)
 
         if sickbeard.showQueueScheduler.action.isBeingAdded(showObj):
-            flash.message('This show is in the process of being downloaded from theTVDB.com - the info below is incomplete.')
+            ui.flash.message('This show is in the process of being downloaded from theTVDB.com - the info below is incomplete.')
 
         elif sickbeard.showQueueScheduler.action.isBeingUpdated(showObj):
-            flash.message('The information below is in the process of being updated.')
+            ui.flash.message('The information below is in the process of being updated.')
 
         elif sickbeard.showQueueScheduler.action.isBeingRefreshed(showObj):
-            flash.message('The episodes below are currently being refreshed from disk')
+            ui.flash.message('The episodes below are currently being refreshed from disk')
 
         elif sickbeard.showQueueScheduler.action.isInRefreshQueue(showObj):
-            flash.message('This show is queued to be refreshed.')
+            ui.flash.message('This show is queued to be refreshed.')
 
         elif sickbeard.showQueueScheduler.action.isInUpdateQueue(showObj):
-            flash.message('This show is queued and awaiting an update.')
+            ui.flash.message('This show is queued and awaiting an update.')
 
         if not sickbeard.showQueueScheduler.action.isBeingAdded(showObj):
             if not sickbeard.showQueueScheduler.action.isBeingUpdated(showObj):
@@ -1506,7 +1486,7 @@ class Home:
         if not location and not anyQualities and not bestQualities and not seasonfolders:
 
             t = PageTemplate(file="editShow.tmpl")
-            t.submenu = HomeMenu
+            t.submenu = HomeMenu()
             with showObj.lock:
                 t.show = showObj
 
@@ -1574,7 +1554,7 @@ class Home:
             return errors
 
         if len(errors) > 0:
-            flash.error('%d error%s while saving changes:' % (len(errors), "" if len(errors) == 1 else "s"),
+            ui.flash.error('%d error%s while saving changes:' % (len(errors), "" if len(errors) == 1 else "s"),
                         '<ul>' + '\n'.join(['<li>%s</li>' % error for error in errors]) + "</ul>")
 
         redirect("/home/displayShow?show=" + show)
@@ -1596,7 +1576,7 @@ class Home:
 
         showObj.deleteShow()
 
-        flash.message('<b>%s</b> has been deleted' % showObj.name)
+        ui.flash.message('<b>%s</b> has been deleted' % showObj.name)
         redirect("/home")
 
     @cherrypy.expose
@@ -1614,7 +1594,7 @@ class Home:
         try:
             sickbeard.showQueueScheduler.action.refreshShow(showObj)
         except exceptions.CantRefreshException, e:
-            flash.error("Unable to refresh this show.",
+            ui.flash.error("Unable to refresh this show.",
                         str(e))
 
         time.sleep(3)
@@ -1636,7 +1616,7 @@ class Home:
         try:
             sickbeard.showQueueScheduler.action.updateShow(showObj, bool(force))
         except exceptions.CantUpdateException, e:
-            flash.error("Unable to update this show.",
+            ui.flash.error("Unable to update this show.",
                         str(e))
 
         # just give it some time
@@ -1650,9 +1630,9 @@ class Home:
 
         for curHost in [x.strip() for x in sickbeard.XBMC_HOST.split(",")]:
             if xbmc.updateLibrary(curHost, showName=showName):
-                flash.message("Command sent to XBMC host " + curHost + " to update library")
+                ui.flash.message("Command sent to XBMC host " + curHost + " to update library")
             else:
-                flash.error("Unable to contact XBMC host " + curHost)
+                ui.flash.error("Unable to contact XBMC host " + curHost)
         redirect('/home')
 
 
@@ -1680,7 +1660,7 @@ class Home:
         if show == None or eps == None or status == None:
             errMsg = "You must specify a show and at least one episode"
             if direct:
-                flash.error('Error', errMsg)
+                ui.flash.error('Error', errMsg)
                 return json.dumps({'result': 'error'})
             else:
                 return _genericMessage("Error", errMsg)
@@ -1688,7 +1668,7 @@ class Home:
         if not statusStrings.has_key(int(status)):
             errMsg = "Invalid status"
             if direct:
-                flash.error('Error', errMsg)
+                ui.flash.error('Error', errMsg)
                 return json.dumps({'result': 'error'})
             else:
                 return _genericMessage("Error", errMsg)
@@ -1698,7 +1678,7 @@ class Home:
         if showObj == None:
             errMsg = "Error", "Show not in show list"
             if direct:
-                flash.error('Error', errMsg)
+                ui.flash.error('Error', errMsg)
                 return json.dumps({'result': 'error'})
             else:
                 return _genericMessage("Error", errMsg)
@@ -1750,7 +1730,7 @@ class Home:
 
         if not foundEpisode:
             message = 'No downloads were found'
-            flash.error(message, "Couldn't find a download for <i>%s</i>" % epObj.prettyName(True))
+            ui.flash.error(message, "Couldn't find a download for <i>%s</i>" % epObj.prettyName(True))
             logger.log(message)
 
         else:
@@ -1760,9 +1740,9 @@ class Home:
             result = search.snatchEpisode(foundEpisode)
             providerModule = foundEpisode.provider
             if providerModule == None:
-                flash.error('Provider is configured incorrectly, unable to download')
+                ui.flash.error('Provider is configured incorrectly, unable to download')
             else:
-                flash.message('Episode <b>%s</b> snatched from <b>%s</b>' % (foundEpisode.name, providerModule.name))
+                ui.flash.message('Episode <b>%s</b> snatched from <b>%s</b>' % (foundEpisode.name, providerModule.name))
 
             #TODO: check if the download was successful
 
@@ -1792,7 +1772,7 @@ class WebInterface:
         if showObj == None:
             return "Unable to find show" #TODO: make it return a standard image
 
-        posterFilename = os.path.abspath(ek.ek(os.path.join, showObj.location, "folder.jpg"))
+        posterFilename = os.path.abspath(sickbeard.metadata_generator.get_poster_path(showObj))
         if ek.ek(os.path.isfile, posterFilename):
             try:
                 from PIL import Image