diff --git a/data/interfaces/default/config_notifications.tmpl b/data/interfaces/default/config_notifications.tmpl
index 24222ad182..b8c472058b 100644
--- a/data/interfaces/default/config_notifications.tmpl
+++ b/data/interfaces/default/config_notifications.tmpl
@@ -17,7 +17,7 @@
 
 
             <br />
-            <h2>Home Theater</h2>
+            <h2>Home Theater / NAS</h2>
             <br />
 
 
@@ -366,17 +366,18 @@
 
                 <div class="component-group clearfix">
                     <div class="component-group-desc">
-                        <img class="notifier-icon" src="$sbRoot/images/notifiers/synoindex.png" alt="" title="Synology Indexer" />
-                        <h3><a href="http://synology.com/" rel="noreferrer" onclick="window.open('${sickbeard.ANON_REDIRECT}' + this.href, '_blank'); return false;">Synology Indexer</a></h3>
+                        <img class="notifier-icon" src="$sbRoot/images/notifiers/synoindex.png" alt="" title="Synology" />
+                        <h3><a href="http://synology.com/" rel="noreferrer" onclick="window.open('${sickbeard.ANON_REDIRECT}' + this.href, '_blank'); return false;">Synology</a></h3>
+                        <p>The Synology DiskStation NAS.</p>
                         <p>Synology Indexer is the daemon running on the Synology NAS to build its media database.</p>
+                        <p>Synology Notifier is the notification system of Synology DSM.</p>
                     </div>
-
                     <fieldset class="component-group-list">
                         <div class="field-pair">
                             <input type="checkbox" class="enabler" name="use_synoindex" id="use_synoindex" #if $sickbeard.USE_SYNOINDEX then "checked=\"checked\"" else ""# />
                             <label class="clearfix" for="use_synoindex">
                                 <span class="component-title">Enable</span>
-                                <span class="component-desc">Should Sick Beard send notifications to the synoindex daemon?<br /><br />
+                                <span class="component-desc">Should Sick Beard send Synology commands?<br /><br />
                                 </span>
                             </label>
                             <label class="nocheck clearfix" for="use_synoindex">
@@ -386,8 +387,31 @@
                         </div>
 
                         <div id="content_use_synoindex">
+                            <div class="field-pair">
+                                <input type="checkbox" name="synoindex_notify_onsnatch" id="synoindex_notify_onsnatch" #if $sickbeard.SYNOINDEX_NOTIFY_ONSNATCH then "checked=\"checked\"" else ""# />
+                                <label class="clearfix" for="synoindex_notify_onsnatch">
+                                    <span class="component-title">Notify on Snatch</span>
+                                    <span class="component-desc">Send notification when we start a download?</span>
+                                </label>
+                            </div>
+                            <div class="field-pair">
+                                <input type="checkbox" name="synoindex_notify_ondownload" id="synoindex_notify_ondownload" #if $sickbeard.SYNOINDEX_NOTIFY_ONDOWNLOAD then "checked=\"checked\"" else ""# />
+                                <label class="clearfix" for="synoindex_notify_ondownload">
+                                    <span class="component-title">Notify on Download</span>
+                                    <span class="component-desc">Send notification when we finish a download?</span>
+                                </label>
+                            </div>
+                            <div class="field-pair">
+                                <input type="checkbox" name="synoindex_update_library" id="synoindex_update_library" #if $sickbeard.SYNOINDEX_UPDATE_LIBRARY then "checked=\"checked\"" else ""# />
+                                <label class="clearfix" for="synoindex_update_library">
+                                    <span class="component-title">Update Library</span>
+                                    <span class="component-desc">Update Synology library when we finish a download?</span>
+                                </label>
+                            </div>
+                            <div class="testNotification" id="testSynoNotify-result">Click below to test.</div>
+                            <input type="button" class="btn" value="Test Notice (SynoDSMnotify)" id="testSynoNotify" />
                             <input type="submit" class="btn config_submitter" value="Save Changes" />
-                        </div><!-- /content_use_pytivo //-->
+                        </div><!-- /content_use_synoindex //-->
 
                     </fieldset>
                 </div><!-- /synoindex component-group //-->
@@ -397,14 +421,14 @@
                     <div class="component-group-desc">
                         <img class="notifier-icon" src="$sbRoot/images/notifiers/pytivo.png" alt="" title="pyTivo" />
                         <h3><a href="http://pytivo.sourceforge.net/wiki/index.php/PyTivo" rel="noreferrer" onclick="window.open('${sickbeard.ANON_REDIRECT}' + this.href, '_blank'); return false;">pyTivo</a></h3>
-                        <p>pyTivo is both an HMO and GoBack server.  This notifier will load the completed downloads to your Tivo.</p>
+                        <p>pyTivo is both an HMO and GoBack server. This notifier will load the completed downloads to your Tivo.</p>
                     </div>
                     <fieldset class="component-group-list">
                         <div class="field-pair">
                             <input type="checkbox" class="enabler" name="use_pytivo" id="use_pytivo" #if $sickbeard.USE_PYTIVO then "checked=\"checked\"" else ""# />
                             <label class="clearfix" for="use_pytivo">
                                 <span class="component-title">Enable</span>
-                                <span class="component-desc">Should Sick Beard send notifications to pyTivo?<br /><br /></span>
+                                <span class="component-desc">Should Sick Beard update commands to pyTivo?<br /><br /></span>
                             </label>
                             <label class="nocheck clearfix" for="use_pytivo">
                                 <span class="component-title">&nbsp;</span>
@@ -637,7 +661,7 @@
                             <input type="checkbox" class="enabler" name="use_pushover" id="use_pushover" #if $sickbeard.USE_PUSHOVER then "checked=\"checked\"" else ""# />
                             <label class="clearfix" for="use_pushover">
                                 <span class="component-title">Enable</span>
-                                <span class="component-desc">Should Sick Beard send notifications through Pushover?</span>
+                                <span class="component-desc">Should Sick Beard send Pushover notifications?</span>
                             </label>
                         </div>
 
@@ -678,14 +702,14 @@
                     <div class="component-group-desc">
                         <img class="notifier-icon" src="$sbRoot/images/notifiers/boxcar.png" alt="" title="Boxcar" />
                         <h3><a href="http://boxcar.io/" rel="noreferrer" onclick="window.open('${sickbeard.ANON_REDIRECT}' + this.href, '_blank'); return false;">Boxcar</a></h3>
-                        <p>Read your messages where and when you want them! A subscription will be sent if needed.</p>
+                        <p>Universal push notification for iOS. Read your messages where and when you want them! A subscription will be sent if needed.</p>
                     </div>
                     <fieldset class="component-group-list">
                         <div class="field-pair">
                             <input type="checkbox" class="enabler" name="use_boxcar" id="use_boxcar" #if $sickbeard.USE_BOXCAR then "checked=\"checked\"" else ""# />
                             <label class="clearfix" for="use_boxcar">
                                 <span class="component-title">Enable</span>
-                                <span class="component-desc">Should Sick Beard send notifications through Boxcar?</span>
+                                <span class="component-desc">Should Sick Beard send Boxcar notifications?</span>
                             </label>
                         </div>
 
@@ -792,7 +816,7 @@
                 </div><br />
 
                 <br />
-                <h2>Online</h2>
+                <h2>Social</h2>
                 <br />
 
 
diff --git a/data/js/configNotifications.js b/data/js/configNotifications.js
index a97b339cb1..c23d689c3f 100644
--- a/data/js/configNotifications.js
+++ b/data/js/configNotifications.js
@@ -257,4 +257,15 @@ $(document).ready(function () {
                 $("#testNMA").attr("disabled", false);
             });
     });
+
+    $("#testSynoNotify").click(function () {
+        $(this).attr("disabled", true);
+        $("#testSynoNotify-result").html(loading);
+        $.get(sbRoot + "/home/testSynoNotify")
+            .done(function (data) {
+                $("#testSynoNotify-result").html(data);
+                $("#testSynoNotify").attr("disabled", false);
+            });
+    });
+
 });
diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py
index 0b78865377..0f9a634cae 100644
--- a/sickbeard/__init__.py
+++ b/sickbeard/__init__.py
@@ -58,7 +58,7 @@
 CONFIG_FILE = None
 
 # this is the version of the config we EXPECT to find
-CONFIG_VERSION = 5
+CONFIG_VERSION = 6
 
 PROG_DIR = '.'
 MY_FULLNAME = None
@@ -267,6 +267,9 @@
 NMJ_MOUNT = None
 
 USE_SYNOINDEX = False
+SYNOINDEX_NOTIFY_ONSNATCH = False
+SYNOINDEX_NOTIFY_ONDOWNLOAD = False
+SYNOINDEX_UPDATE_LIBRARY = False
 
 USE_NMJv2 = False
 NMJv2_HOST = None
@@ -341,7 +344,8 @@ def initialize(consoleLogging=True):
                 EXTRA_SCRIPTS, USE_TWITTER, TWITTER_USERNAME, TWITTER_PASSWORD, TWITTER_PREFIX, \
                 USE_BOXCAR, BOXCAR_USERNAME, BOXCAR_PASSWORD, BOXCAR_NOTIFY_ONDOWNLOAD, BOXCAR_NOTIFY_ONSNATCH, \
                 USE_PUSHOVER, PUSHOVER_USERKEY, PUSHOVER_NOTIFY_ONDOWNLOAD, PUSHOVER_NOTIFY_ONSNATCH, \
-                USE_LIBNOTIFY, LIBNOTIFY_NOTIFY_ONSNATCH, LIBNOTIFY_NOTIFY_ONDOWNLOAD, USE_NMJ, NMJ_HOST, NMJ_DATABASE, NMJ_MOUNT, USE_NMJv2, NMJv2_HOST, NMJv2_DATABASE, NMJv2_DBLOC, USE_SYNOINDEX, \
+                USE_LIBNOTIFY, LIBNOTIFY_NOTIFY_ONSNATCH, LIBNOTIFY_NOTIFY_ONDOWNLOAD, USE_NMJ, NMJ_HOST, NMJ_DATABASE, NMJ_MOUNT, USE_NMJv2, NMJv2_HOST, NMJv2_DATABASE, NMJv2_DBLOC, \
+                USE_SYNOINDEX, SYNOINDEX_NOTIFY_ONSNATCH, SYNOINDEX_NOTIFY_ONDOWNLOAD, SYNOINDEX_UPDATE_LIBRARY, \
                 USE_LISTVIEW, METADATA_XBMC, METADATA_XBMC_12PLUS, METADATA_MEDIABROWSER, METADATA_MEDE8ER, METADATA_PS3, metadata_provider_dict, \
                 GIT_PATH, MOVE_ASSOCIATED_FILES, \
                 COMING_EPS_LAYOUT, COMING_EPS_SORT, COMING_EPS_DISPLAY_PAUSED, METADATA_WDTV, METADATA_TIVO, IGNORE_WORDS, CREATE_MISSING_SHOW_DIRS, \
@@ -602,6 +606,9 @@ def initialize(consoleLogging=True):
 
         CheckSection(CFG, 'Synology')
         USE_SYNOINDEX = bool(check_setting_int(CFG, 'Synology', 'use_synoindex', 0))
+        SYNOINDEX_NOTIFY_ONSNATCH = bool(check_setting_int(CFG, 'Synology', 'synoindex_notify_onsnatch', 0))
+        SYNOINDEX_NOTIFY_ONDOWNLOAD = bool(check_setting_int(CFG, 'Synology', 'synoindex_notify_ondownload', 0))
+        SYNOINDEX_UPDATE_LIBRARY = bool(check_setting_int(CFG, 'Synology', 'synoindex_update_library', 0))
 
         CheckSection(CFG, 'Trakt')
         USE_TRAKT = bool(check_setting_int(CFG, 'Trakt', 'use_trakt', 0))
@@ -665,55 +672,66 @@ def initialize(consoleLogging=True):
         newznabProviderList = providers.getNewznabProviderList(NEWZNAB_DATA)
         providerList = providers.makeProviderList()
 
-        # initialize schedulars
-        currentSearchScheduler = scheduler.Scheduler(searchCurrent.CurrentSearcher(),
-                                                     cycleTime=datetime.timedelta(minutes=SEARCH_FREQUENCY),
-                                                     threadName="SEARCH",
-                                                     runImmediately=True)
-
-        # the interval for this is stored inside the ShowUpdater class
-        showUpdaterInstance = showUpdater.ShowUpdater()
-        showUpdateScheduler = scheduler.Scheduler(showUpdaterInstance,
-                                               cycleTime=showUpdaterInstance.updateInterval,
-                                               threadName="SHOWUPDATER",
-                                               runImmediately=False)
+        # initialize schedulers
 
+        # updaters
         versionCheckScheduler = scheduler.Scheduler(versionChecker.CheckVersion(),
-                                                     cycleTime=datetime.timedelta(hours=12),
-                                                     threadName="CHECKVERSION",
-                                                     runImmediately=True)
+                                                    cycleTime=datetime.timedelta(hours=12),
+                                                    threadName="CHECKVERSION"
+                                                    )
 
         showQueueScheduler = scheduler.Scheduler(show_queue.ShowQueue(),
-                                               cycleTime=datetime.timedelta(seconds=3),
-                                               threadName="SHOWQUEUE",
-                                               silent=True)
+                                                 cycleTime=datetime.timedelta(seconds=3),
+                                                 threadName="SHOWQUEUE",
+                                                 silent=True)
 
-        searchQueueScheduler = scheduler.Scheduler(search_queue.SearchQueue(),
-                                               cycleTime=datetime.timedelta(seconds=3),
-                                               threadName="SEARCHQUEUE",
-                                               silent=True)
+        showUpdaterInstance = showUpdater.ShowUpdater()  # the interval for this is stored inside the class
+        showUpdateScheduler = scheduler.Scheduler(showUpdaterInstance,
+                                                  cycleTime=showUpdaterInstance.updateInterval,
+                                                  threadName="SHOWUPDATER",
+                                                  run_delay=showUpdaterInstance.updateInterval,
+                                                  silent=True
+                                                  )
 
-        properFinderInstance = properFinder.ProperFinder()
-        properFinderScheduler = scheduler.Scheduler(properFinderInstance,
-                                                     cycleTime=properFinderInstance.updateInterval,
-                                                     threadName="FINDPROPERS",
-                                                     runImmediately=False)
-        if not DOWNLOAD_PROPERS:
-            properFinderScheduler.silent = True
+        # searchers
+        searchQueueScheduler = scheduler.Scheduler(search_queue.SearchQueue(),
+                                                   cycleTime=datetime.timedelta(seconds=3),
+                                                   threadName="SEARCHQUEUE",
+                                                   silent=True
+                                                   )
 
-        autoPostProcesserScheduler = scheduler.Scheduler(autoPostProcesser.PostProcesser(),
-                                                     cycleTime=datetime.timedelta(minutes=10),
-                                                     threadName="POSTPROCESSER",
-                                                     runImmediately=True)
-        if not PROCESS_AUTOMATICALLY:
-            autoPostProcesserScheduler.silent = True
+        currentSearchScheduler = scheduler.Scheduler(searchCurrent.CurrentSearcher(),
+                                                     cycleTime=datetime.timedelta(minutes=SEARCH_FREQUENCY),
+                                                     threadName="SEARCH",
+                                                     run_delay=datetime.timedelta(minutes=5)
+                                                     )
 
         backlogSearchScheduler = searchBacklog.BacklogSearchScheduler(searchBacklog.BacklogSearcher(),
                                                                       cycleTime=datetime.timedelta(minutes=get_backlog_cycle_time()),
                                                                       threadName="BACKLOG",
-                                                                      runImmediately=True)
+                                                                      run_delay=datetime.timedelta(minutes=17)
+                                                                      )
+
         backlogSearchScheduler.action.cycleTime = BACKLOG_SEARCH_FREQUENCY
 
+        properFinderInstance = properFinder.ProperFinder()  # the interval for this is stored inside the class
+        properFinderScheduler = scheduler.Scheduler(properFinderInstance,
+                                                    cycleTime=properFinderInstance.updateInterval,
+                                                    threadName="FINDPROPERS",
+                                                    run_delay=properFinderInstance.updateInterval,
+                                                    silent=True
+                                                    )
+
+        # processors
+        autoPostProcesserScheduler = scheduler.Scheduler(autoPostProcesser.PostProcesser(),
+                                                         cycleTime=datetime.timedelta(minutes=10),
+                                                         threadName="POSTPROCESSER",
+                                                         run_delay=datetime.timedelta(minutes=5)
+                                                         )
+
+        if not PROCESS_AUTOMATICALLY:
+            autoPostProcesserScheduler.silent = True
+
         showList = []
         loadingShowList = {}
 
@@ -1114,6 +1132,9 @@ def save_config():
 
     new_config['Synology'] = {}
     new_config['Synology']['use_synoindex'] = int(USE_SYNOINDEX)
+    new_config['Synology']['synoindex_notify_onsnatch'] = int(SYNOINDEX_NOTIFY_ONSNATCH)
+    new_config['Synology']['synoindex_notify_ondownload'] = int(SYNOINDEX_NOTIFY_ONDOWNLOAD)
+    new_config['Synology']['synoindex_update_library'] = int(SYNOINDEX_UPDATE_LIBRARY)
 
     new_config['NMJv2'] = {}
     new_config['NMJv2']['use_nmjv2'] = int(USE_NMJv2)
diff --git a/sickbeard/config.py b/sickbeard/config.py
index 89acbac676..ad0035747a 100644
--- a/sickbeard/config.py
+++ b/sickbeard/config.py
@@ -645,3 +645,10 @@ def _migrate_metadata(metadata, metadata_name, use_banner):
         sickbeard.METADATA_WDTV = _migrate_metadata(metadata_wdtv, 'WDTV', use_banner)
         sickbeard.METADATA_TIVO = _migrate_metadata(metadata_tivo, 'TIVO', use_banner)
         sickbeard.METADATA_MEDE8ER = _migrate_metadata(metadata_mede8er, 'Mede8er', use_banner)
+
+    # Migration v6: Synology notifier update
+    def _migrate_v6(self):
+        """ Updates Synology notifier to reflect that their now is an update library option instead misusing the enable option """
+
+        # clone use_synoindex to update_library since this now has notification options
+        sickbeard.SYNOINDEX_UPDATE_LIBRARY = bool(check_setting_int(self.config_obj, 'Synology', 'use_synoindex', 0))
diff --git a/sickbeard/helpers.py b/sickbeard/helpers.py
index d63f150e74..db9cb7348d 100644
--- a/sickbeard/helpers.py
+++ b/sickbeard/helpers.py
@@ -50,6 +50,7 @@
 from sickbeard.exceptions import MultipleShowObjectsException, ex
 from sickbeard import logger, classes
 from sickbeard.common import USER_AGENT, mediaExtensions, XML_NSMAP
+from sickbeard.common import mediaExtensions
 
 from sickbeard import db
 from sickbeard import encodingKludge as ek
@@ -82,6 +83,32 @@ def indentXML(elem, level=0):
             elem.tail = i
 
 
+def remove_extension(name):
+    """
+    Remove download or media extension from name (if any)
+    """
+
+    if name and "." in name:
+        base_name, sep, extension = name.rpartition('.')  # @UnusedVariable
+        if base_name and extension.lower() in ['nzb', 'torrent'] + mediaExtensions:
+            name = base_name
+
+    return name
+
+
+def remove_non_release_groups(name):
+    """
+    Remove non release groups from name
+    """
+
+    if name and "-" in name:
+        name_group = name.rsplit('-', 1)
+        if name_group[-1].upper() in ["RP", "NZBGEEK"]:
+            name = name_group[0]
+
+    return name
+
+
 def replaceExtension(filename, newExt):
     '''
     >>> replaceExtension('foo.avi', 'mkv')
diff --git a/sickbeard/name_parser/parser.py b/sickbeard/name_parser/parser.py
index 20ffdb7cb8..3bfac589fd 100644
--- a/sickbeard/name_parser/parser.py
+++ b/sickbeard/name_parser/parser.py
@@ -25,31 +25,35 @@
 import sickbeard
 
 from sickbeard import logger
+from sickbeard import encodingKludge as ek
+from sickbeard import helpers
+
+
 
 class NameParser(object):
-    def __init__(self, file_name=True):
+    def __init__(self, is_file_name=True):
 
-        self.file_name = file_name
+        self.is_file_name = is_file_name
         self.compiled_regexes = []
         self._compile_regexes()
 
     def clean_series_name(self, series_name):
         """Cleans up series name by removing any . and _
         characters, along with any trailing hyphens.
-    
+
         Is basically equivalent to replacing all _ and . with a
         space, but handles decimal numbers in string, for example:
-    
+
         >>> cleanRegexedSeriesName("an.example.1.0.test")
         'an example 1.0 test'
         >>> cleanRegexedSeriesName("an_example_1.0_test")
         'an example 1.0 test'
-        
+
         Stolen from dbr's tvnamer
         """
-        
+
         series_name = re.sub("(\D)\.(?!\s)(\D)", "\\1 \\2", series_name)
-        series_name = re.sub("(\d)\.(\d{4})", "\\1 \\2", series_name) # if it ends in a year then don't keep the dot
+        series_name = re.sub("(\d)\.(\d{4})", "\\1 \\2", series_name)  # if it ends in a year then don't keep the dot
         series_name = re.sub("(\D)\.(?!\s)", "\\1 ", series_name)
         series_name = re.sub("\.(?!\s)(\D)", " \\1", series_name)
         series_name = series_name.replace("_", " ")
@@ -66,36 +70,36 @@ def _compile_regexes(self):
                 self.compiled_regexes.append((cur_pattern_name, cur_regex))
 
     def _parse_string(self, name):
-        
+
         if not name:
             return None
-        
+
         for (cur_regex_name, cur_regex) in self.compiled_regexes:
             match = cur_regex.match(name)
 
             if not match:
                 continue
-            
+
             result = ParseResult(name)
             result.which_regex = [cur_regex_name]
-            
+
             named_groups = match.groupdict().keys()
 
             if 'series_name' in named_groups:
                 result.series_name = match.group('series_name')
                 if result.series_name:
                     result.series_name = self.clean_series_name(result.series_name)
-            
+
             if 'season_num' in named_groups:
                 tmp_season = int(match.group('season_num'))
-                if cur_regex_name == 'bare' and tmp_season in (19,20):
+                if cur_regex_name == 'bare' and tmp_season in (19, 20):
                     continue
                 result.season_number = tmp_season
-            
+
             if 'ep_num' in named_groups:
                 ep_num = self._convert_number(match.group('ep_num'))
                 if 'extra_ep_num' in named_groups and match.group('extra_ep_num'):
-                    result.episode_numbers = range(ep_num, self._convert_number(match.group('extra_ep_num'))+1)
+                    result.episode_numbers = range(ep_num, self._convert_number(match.group('extra_ep_num')) + 1)
                 else:
                     result.episode_numbers = [ep_num]
 
@@ -103,7 +107,7 @@ def _parse_string(self, name):
                 year = int(match.group('air_year'))
                 month = int(match.group('air_month'))
                 day = int(match.group('air_day'))
-                
+
                 # make an attempt to detect YYYY-DD-MM formats
                 if month > 12:
                     tmp_month = month
@@ -125,8 +129,8 @@ def _parse_string(self, name):
                 if tmp_extra_info:
                     result.is_proper = re.search('(^|[\. _-])(proper|repack)([\. _-]|$)', tmp_extra_info, re.I) is not None
 
-                # Show.S04.Special is almost certainly not every episode in the season
-                if tmp_extra_info and cur_regex_name == 'season_only' and re.match(r'([. _-]|^)(special|extra)\w*([. _-]|$)', tmp_extra_info, re.I):
+                # Show.S04.Special or Show.S05.Part.2.Extras is almost certainly not every episode in the season
+                if tmp_extra_info and cur_regex_name == 'season_only' and re.search(r'([. _-]|^)(special|extra)s?\w*([. _-]|$)', tmp_extra_info, re.I):
                     continue
                 result.extra_info = tmp_extra_info
 
@@ -148,10 +152,10 @@ def _combine_results(self, first, second, attr):
         # if the second doesn't exist then return the first
         if not second:
             return getattr(first, attr)
-        
+
         a = getattr(first, attr)
         b = getattr(second, attr)
-        
+
         # if a is good use it
         if a != None or (type(a) == list and len(a)):
             return a
@@ -159,74 +163,70 @@ def _combine_results(self, first, second, attr):
         else:
             return b
 
-    def _unicodify(self, obj, encoding = "utf-8"):
+    def _unicodify(self, obj, encoding="utf-8"):
         if isinstance(obj, basestring):
             if not isinstance(obj, unicode):
                 obj = unicode(obj, encoding)
         return obj
 
-    def _convert_number(self, number):
-        if type(number) == int:
-            return number
-
-        # good lord I'm lazy
-        if number.lower() == 'i': return 1
-        if number.lower() == 'ii': return 2
-        if number.lower() == 'iii': return 3
-        if number.lower() == 'iv': return 4
-        if number.lower() == 'v': return 5
-        if number.lower() == 'vi': return 6
-        if number.lower() == 'vii': return 7
-        if number.lower() == 'viii': return 8
-        if number.lower() == 'ix': return 9
-        if number.lower() == 'x': return 10
-        if number.lower() == 'xi': return 11
-        if number.lower() == 'xii': return 12
-        if number.lower() == 'xiii': return 13
-        if number.lower() == 'xiv': return 14
-        if number.lower() == 'xv': return 15
-        if number.lower() == 'xvi': return 16
-        if number.lower() == 'xvii': return 17
-        if number.lower() == 'xviii': return 18
-        if number.lower() == 'xix': return 19
-        if number.lower() == 'xx': return 20
-        if number.lower() == 'xxi': return 21
-        if number.lower() == 'xxii': return 22
-        if number.lower() == 'xxiii': return 23
-        if number.lower() == 'xxiv': return 24
-        if number.lower() == 'xxv': return 25
-        if number.lower() == 'xxvi': return 26
-        if number.lower() == 'xxvii': return 27
-        if number.lower() == 'xxviii': return 28
-        if number.lower() == 'xxix': return 29
-
-        return int(number)
+    def _convert_number(self, org_number):
+        """
+        Convert org_number into an integer
+        org_number: integer or representation of a number: string or unicode
+        Try force converting to int first, on error try converting from Roman numerals
+        returns integer or 0
+        """
+
+        try:
+            # try forcing to int
+            if org_number:
+                number = int(org_number)
+            else:
+                number = 0
+
+        except:
+            # on error try converting from Roman numerals
+            roman_to_int_map = (('M', 1000), ('CM', 900), ('D', 500), ('CD', 400), ('C', 100),
+                                ('XC', 90), ('L', 50), ('XL', 40), ('X', 10),
+                                ('IX', 9), ('V', 5), ('IV', 4), ('I', 1)
+                               )
+
+            roman_numeral = str(org_number).upper()
+            number = 0
+            index = 0
+
+            for numeral, integer in roman_to_int_map:
+                while roman_numeral[index:index + len(numeral)] == numeral:
+                    number += integer
+                    index += len(numeral)
+
+        return number
 
     def parse(self, name):
-        
+
         name = self._unicodify(name)
-        
+
         cached = name_parser_cache.get(name)
         if cached:
             return cached
 
         # break it into parts if there are any (dirname, file name, extension)
-        dir_name, file_name = os.path.split(name)
-        ext_match = re.match('(.*)\.\w{3,4}$', file_name)
-        if ext_match and self.file_name:
-            base_file_name = ext_match.group(1)
+        dir_name, file_name = ek.ek(os.path.split, name)
+
+        if self.is_file_name:
+            base_file_name = helpers.remove_extension(file_name)
         else:
             base_file_name = file_name
-        
+
         # use only the direct parent dir
-        dir_name = os.path.basename(dir_name)
-        
+        dir_name = ek.ek(os.path.basename, dir_name)
+
         # set up a result to use
         final_result = ParseResult(name)
-        
+
         # try parsing the file name
         file_name_result = self._parse_string(base_file_name)
-        
+
         # parse the dirname for extra info if needed
         dir_name_result = self._parse_string(dir_name)
 
@@ -263,6 +263,7 @@ def parse(self, name):
         # return it
         return final_result
 
+
 class ParseResult(object):
     def __init__(self,
                  original_name,
@@ -275,7 +276,7 @@ def __init__(self,
                  ):
 
         self.original_name = original_name
-        
+
         self.series_name = series_name
         self.season_number = season_number
         if not episode_numbers:
@@ -285,15 +286,15 @@ def __init__(self,
 
         self.extra_info = extra_info
         self.release_group = release_group
-        
+
         self.air_date = air_date
-        
+
         self.which_regex = None
-        
+
     def __eq__(self, other):
         if not other:
             return False
-        
+
         if self.series_name != other.series_name:
             return False
         if self.season_number != other.season_number:
@@ -306,7 +307,7 @@ def __eq__(self, other):
             return False
         if self.air_date != other.air_date:
             return False
-        
+
         return True
 
     def __str__(self):
@@ -315,10 +316,10 @@ def __str__(self):
         else:
             to_return = u''
         if self.season_number != None:
-            to_return += 'S'+str(self.season_number)
+            to_return += 'S' + str(self.season_number)
         if self.episode_numbers and len(self.episode_numbers):
             for e in self.episode_numbers:
-                to_return += 'E'+str(e)
+                to_return += 'E' + str(e)
 
         if self.air_by_date:
             to_return += str(self.air_date)
@@ -328,7 +329,7 @@ def __str__(self):
         if self.release_group:
             to_return += ' (' + self.release_group + ')'
 
-        to_return += ' [ABD: '+str(self.air_by_date)+']'
+        to_return += ' [ABD: ' + str(self.air_by_date) + ']'
 
         return to_return.encode('utf-8')
 
@@ -338,19 +339,20 @@ def _is_air_by_date(self):
         return False
     air_by_date = property(_is_air_by_date)
 
+
 class NameParserCache(object):
     #TODO: check if the fifo list can beskiped and only use one dict
-    _previous_parsed_list = [] # keep a fifo list of the cached items
+    _previous_parsed_list = []  # keep a fifo list of the cached items
     _previous_parsed = {}
     _cache_size = 100
-    
+
     def add(self, name, parse_result):
         self._previous_parsed[name] = parse_result
         self._previous_parsed_list.append(name)
         while len(self._previous_parsed_list) > self._cache_size:
             del_me = self._previous_parsed_list.pop(0)
             self._previous_parsed.pop(del_me)
-    
+
     def get(self, name):
         if name in self._previous_parsed:
             logger.log("Using cached parse result for: " + name, logger.DEBUG)
@@ -360,5 +362,6 @@ def get(self, name):
 
 name_parser_cache = NameParserCache()
 
+
 class InvalidNameException(Exception):
     "The given name is not valid"
diff --git a/sickbeard/notifiers/__init__.py b/sickbeard/notifiers/__init__.py
index cc4e769c21..c65d7c4293 100755
--- a/sickbeard/notifiers/__init__.py
+++ b/sickbeard/notifiers/__init__.py
@@ -36,13 +36,15 @@
 import trakt
 
 from sickbeard.common import *
+from sickbeard import logger
+from sickbeard.exceptions import ex
 
-# home theater
+# home theater/nas
 xbmc_notifier = xbmc.XBMCNotifier()
 plex_notifier = plex.PLEXNotifier()
 nmj_notifier = nmj.NMJNotifier()
-synoindex_notifier = synoindex.synoIndexNotifier()
 nmjv2_notifier = nmjv2.NMJv2Notifier()
+synoindex_notifier = synoindex.synoIndexNotifier()
 pytivo_notifier = pytivo.pyTivoNotifier()
 # devices
 growl_notifier = growl.GrowlNotifier()
@@ -51,12 +53,12 @@
 pushover_notifier = pushover.PushoverNotifier()
 boxcar_notifier = boxcar.BoxcarNotifier()
 nma_notifier = nma.NMA_Notifier()
-# online
+# social
 twitter_notifier = tweet.TwitterNotifier()
 trakt_notifier = trakt.TraktNotifier()
 
 notifiers = [
-    libnotify_notifier, # Libnotify notifier goes first because it doesn't involve blocking on network activity.
+    libnotify_notifier,  # Libnotify notifier goes first because it doesn't involve blocking on network activity.
     xbmc_notifier,
     plex_notifier,
     nmj_notifier,
@@ -75,14 +77,23 @@
 
 def notify_download(ep_name):
     for n in notifiers:
-        n.notify_download(ep_name)
+        try:
+            n.notify_download(ep_name)
+        except Exception, e:
+            logger.log(n.__class__.__name__ + ": " + ex(e), logger.ERROR)
 
 
 def notify_snatch(ep_name):
     for n in notifiers:
-        n.notify_snatch(ep_name)
+        try:
+            n.notify_snatch(ep_name)
+        except Exception, e:
+            logger.log(n.__class__.__name__ + ": " + ex(e), logger.ERROR)
 
 
 def update_library(ep_obj):
     for n in notifiers:
-        n.update_library(ep_obj)
+        try:
+            n.update_library(ep_obj=ep_obj)
+        except Exception, e:
+            logger.log(n.__class__.__name__ + ": " + ex(e), logger.ERROR)
diff --git a/sickbeard/notifiers/boxcar.py b/sickbeard/notifiers/boxcar.py
index 474bac4d8f..315a4f5916 100644
--- a/sickbeard/notifiers/boxcar.py
+++ b/sickbeard/notifiers/boxcar.py
@@ -64,7 +64,6 @@ def _sendBoxcar(self, msg, title, email, subscribe=False):
 
         # send the request to boxcar
         try:
-            # TODO: Use our getURL from helper?
             req = urllib2.Request(curUrl)
             handle = urllib2.urlopen(req, data)
             handle.close()
@@ -72,10 +71,10 @@ def _sendBoxcar(self, msg, title, email, subscribe=False):
         except urllib2.URLError, e:
             # if we get an error back that doesn't have an error code then who knows what's really happening
             if not hasattr(e, 'code'):
-                logger.log(u"BOXCAR: Boxcar notification failed." + ex(e), logger.ERROR)
+                logger.log(u"BOXCAR: Notification failed." + ex(e), logger.ERROR)
                 return False
             else:
-                logger.log(u"BOXCAR: Boxcar notification failed. Error code: " + str(e.code), logger.WARNING)
+                logger.log(u"BOXCAR: Notification failed. Error code: " + str(e.code), logger.ERROR)
 
             # HTTP status 404 if the provided email address isn't a Boxcar user.
             if e.code == 404:
@@ -103,10 +102,10 @@ def _sendBoxcar(self, msg, title, email, subscribe=False):
 
             # If you receive an HTTP status code of 400, it is because you failed to send the proper parameters
             elif e.code == 400:
-                logger.log(u"BOXCAR: Wrong data send to boxcar.", logger.ERROR)
+                logger.log(u"BOXCAR: Wrong data sent to boxcar.", logger.ERROR)
                 return False
 
-        logger.log(u"BOXCAR: Boxcar notification successful.", logger.DEBUG)
+        logger.log(u"BOXCAR: Notification successful.", logger.MESSAGE)
         return True
 
     def _notify(self, title, message, username=None, force=False):
@@ -129,8 +128,7 @@ def _notify(self, title, message, username=None, force=False):
 
         logger.log(u"BOXCAR: Sending notification for " + message, logger.DEBUG)
 
-        self._sendBoxcar(message, title, username)
-        return True
+        return self._sendBoxcar(message, title, username)
 
 ##############################################################################
 # Public functions
@@ -144,10 +142,10 @@ def notify_download(self, ep_name):
         if sickbeard.BOXCAR_NOTIFY_ONDOWNLOAD:
             self._notify(notifyStrings[NOTIFY_DOWNLOAD], ep_name)
 
-    def test_notify(self, email, title="Test"):
-        return self._sendBoxcar("This is a test notification from SickBeard", title, email)
+    def test_notify(self, boxcar_username):
+        return self._notify("This is a test notification from Sick Beard", "Test", boxcar_username, force=True)
 
-    def update_library(self, showName=None):
+    def update_library(self, ep_obj=None):
         pass
 
 notifier = BoxcarNotifier
diff --git a/sickbeard/notifiers/growl.py b/sickbeard/notifiers/growl.py
index 9d93bba727..e2b09b73ba 100644
--- a/sickbeard/notifiers/growl.py
+++ b/sickbeard/notifiers/growl.py
@@ -115,11 +115,11 @@ def _notify(self, title="Sick Beard Notification", message=None, name=None, host
             print pc
             opts['host'] = pc[0]
             opts['port'] = pc[1]
-            logger.log(u"GROWL: Sending message '" + message + "' to " + opts['host'] + ":" + str(opts['port']))
+            logger.log(u"GROWL: Sending message '" + message + "' to " + opts['host'] + ":" + str(opts['port']), logger.DEBUG)
             try:
                 return self._send_growl(opts, message)
             except Exception, e:
-                logger.log(u"GROWL: Unable to send growl to " + opts['host'] + ":" + str(opts['port']) + " - " + ex(e))
+                logger.log(u"GROWL: Unable to send growl to " + opts['host'] + ":" + str(opts['port']) + " - " + ex(e), logger.WARNING)
                 return False
 
     def _sendRegistration(self, host=None, password=None, name="Sick Beard Notification"):
@@ -161,7 +161,7 @@ def _sendRegistration(self, host=None, password=None, name="Sick Beard Notificat
         try:
             return self._send(opts['host'], opts['port'], register.encode(), opts['debug'])
         except Exception, e:
-            logger.log(u"GROWL: Unable to send growl to " + opts['host'] + ":" + str(opts['port']) + " - " + ex(e))
+            logger.log(u"GROWL: Unable to send growl to " + opts['host'] + ":" + str(opts['port']) + " - " + ex(e), logger.WARNING)
             return False
 
 ##############################################################################
@@ -183,7 +183,7 @@ def test_notify(self, host, password):
         else:
             return result
 
-    def update_library(self, showName=None):
+    def update_library(self, ep_obj=None):
         pass
 
 notifier = GrowlNotifier
diff --git a/sickbeard/notifiers/libnotify.py b/sickbeard/notifiers/libnotify.py
index d36c8fae0a..2071c07472 100644
--- a/sickbeard/notifiers/libnotify.py
+++ b/sickbeard/notifiers/libnotify.py
@@ -67,15 +67,15 @@ def init_pynotify(self):
         try:
             import pynotify
         except ImportError:
-            logger.log(u"LIBNOTIFY: Unable to import pynotify. libnotify notifications won't work.")
+            logger.log(u"LIBNOTIFY: Unable to import pynotify. libnotify notifications won't work.", logger.ERROR)
             return False
         try:
             import gobject
         except ImportError:
-            logger.log(u"LIBNOTIFY: Unable to import gobject. We can't catch a GError in display.")
+            logger.log(u"LIBNOTIFY: Unable to import gobject. We can't catch a GError in display.", logger.ERROR)
             return False
         if not pynotify.init('Sick Beard'):
-            logger.log(u"LIBNOTIFY: Initialization of pynotify failed. libnotify notifications won't work.")
+            logger.log(u"LIBNOTIFY: Initialization of pynotify failed. libnotify notifications won't work.", logger.ERROR)
             return False
         self.pynotify = pynotify
         self.gobject = gobject
@@ -119,7 +119,7 @@ def notify_download(self, ep_name):
     def test_notify(self):
         return self._notify("Test notification", "This is a test notification from Sick Beard", force=True)
 
-    def update_library(self, showName=None):
+    def update_library(self, ep_obj=None):
         pass
 
 notifier = LibnotifyNotifier
diff --git a/sickbeard/notifiers/nma.py b/sickbeard/notifiers/nma.py
index ab9e4c2d5c..84c6f73875 100644
--- a/sickbeard/notifiers/nma.py
+++ b/sickbeard/notifiers/nma.py
@@ -1,3 +1,21 @@
+# Author: Adam Landry
+# 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 sickbeard
 
 from sickbeard import logger, common
@@ -8,7 +26,7 @@ class NMA_Notifier:
 
     def _sendNMA(self, nma_api=None, nma_priority=None, event=None, message=None, force=False):
 
-        title = "Sick-Beard"
+        title = "Sick Beard"
 
         # suppress notifications if the notifier is disabled but the notify options are checked
         if not sickbeard.USE_NMA and not force:
@@ -20,10 +38,6 @@ def _sendNMA(self, nma_api=None, nma_priority=None, event=None, message=None, fo
         if nma_priority == None:
             nma_priority = sickbeard.NMA_PRIORITY
 
-        logger.log(u"NMA: title: " + title, logger.DEBUG)
-        logger.log(u"NMA: event: " + event, logger.DEBUG)
-        logger.log(u"NMA: message: " + message, logger.DEBUG)
-
         batch = False
 
         p = pynma.PyNMA()
@@ -33,12 +47,14 @@ def _sendNMA(self, nma_api=None, nma_priority=None, event=None, message=None, fo
         if len(keys) > 1:
             batch = True
 
+        logger.log("NMA: Sending notice with details: event=\"%s\", message=\"%s\", priority=%s, batch=%s" % (event, message, nma_priority, batch), logger.DEBUG)
         response = p.push(title, event, message, priority=nma_priority, batch_mode=batch)
 
         if not response[nma_api][u'code'] == u'200':
             logger.log(u"NMA: Could not send notification to NotifyMyAndroid", logger.ERROR)
             return False
         else:
+            logger.log(u"NMA: Notification sent to NotifyMyAndroid", logger.MESSAGE)
             return True
 
 ##############################################################################
@@ -56,7 +72,7 @@ def notify_download(self, ep_name):
     def test_notify(self, nma_api, nma_priority):
         return self._sendNMA(nma_api, nma_priority, event="Test", message="Testing NMA settings from Sick Beard", force=True)
 
-    def update_library(self, showName=None):
+    def update_library(self, ep_obj=None):
         pass
 
 notifier = NMA_Notifier
diff --git a/sickbeard/notifiers/nmj.py b/sickbeard/notifiers/nmj.py
index 2171103310..8c142ada81 100644
--- a/sickbeard/notifiers/nmj.py
+++ b/sickbeard/notifiers/nmj.py
@@ -23,6 +23,7 @@
 import re
 
 from sickbeard import logger
+from sickbeard.exceptions import ex
 
 try:
     import xml.etree.cElementTree as etree
@@ -45,7 +46,7 @@ def notify_settings(self, host):
         try:
             terminal = telnetlib.Telnet(host)
         except Exception:
-            logger.log(u"NMJ: Warning: unable to get a telnet session to %s" % (host), logger.ERROR)
+            logger.log(u"NMJ: Unable to get a telnet session to %s" % (host), logger.WARNING)
             return False
 
         # tell the terminal to output the necessary info to the screen so we can search it later
@@ -67,7 +68,7 @@ def notify_settings(self, host):
             logger.log(u"NMJ: Found NMJ database %s on device %s" % (database, device), logger.DEBUG)
             sickbeard.NMJ_DATABASE = database
         else:
-            logger.log(u"NMJ: Could not get current NMJ database on %s, NMJ is probably not running!" % (host), logger.ERROR)
+            logger.log(u"NMJ: Could not get current NMJ database on %s, NMJ is probably not running!" % (host), logger.WARNING)
             return False
 
         # if the device is a remote host then try to parse the mounting URL and save it to the config
@@ -79,7 +80,7 @@ def notify_settings(self, host):
                 logger.log(u"NMJ: Found mounting url on the Popcorn Hour in configuration: %s" % (mount), logger.DEBUG)
                 sickbeard.NMJ_MOUNT = mount
             else:
-                logger.log(u"NMJ: Detected a network share on the Popcorn Hour, but could not get the mounting url", logger.DEBUG)
+                logger.log(u"NMJ: Detected a network share on the Popcorn Hour, but could not get the mounting url", logger.WARNING)
                 return False
 
         return True
@@ -98,12 +99,17 @@ def _sendNMJ(self, host, database, mount=None):
         # if a mount URL is provided then attempt to open a handle to that URL
         if mount:
             try:
-                # TODO: Use our getURL from helper?
                 req = urllib2.Request(mount)
                 logger.log(u"NMJ: Try to mount network drive via url: %s" % (mount), logger.DEBUG)
                 handle = urllib2.urlopen(req)
             except IOError, e:
-                logger.log(u"NMJ: Warning: Couldn't contact Popcorn Hour on host %s: %s" % (host, e))
+                if hasattr(e, 'reason'):
+                    logger.log(u"NMJ: Could not contact Popcorn Hour on host %s: %s" % (host, e.reason), logger.WARNING)
+                elif hasattr(e, 'code'):
+                    logger.log(u"NMJ: Problem with Popcorn Hour on host %s: %s" % (host, e.code), logger.WARNING)
+                return False
+            except Exception, e:
+                logger.log(u"NMJ: Unknown exception: " + ex(e), logger.ERROR)
                 return False
 
         # build up the request URL and parameters
@@ -112,19 +118,25 @@ def _sendNMJ(self, host, database, mount=None):
             "arg0": "scanner_start",
             "arg1": database,
             "arg2": "background",
-            "arg3": ""}
+            "arg3": ""
+        }
         params = urllib.urlencode(params)
         updateUrl = UPDATE_URL % {"host": host, "params": params}
 
         # send the request to the server
         try:
-            # TODO: Use our getURL from helper?
             req = urllib2.Request(updateUrl)
             logger.log(u"NMJ: Sending NMJ scan update command via url: %s" % (updateUrl), logger.DEBUG)
             handle = urllib2.urlopen(req)
             response = handle.read()
         except IOError, e:
-            logger.log(u"NMJ: Warning: Couldn't contact Popcorn Hour on host %s: %s" % (host, e))
+            if hasattr(e, 'reason'):
+                logger.log(u"NMJ: Could not contact Popcorn Hour on host %s: %s" % (host, e.reason), logger.WARNING)
+            elif hasattr(e, 'code'):
+                logger.log(u"NMJ: Problem with Popcorn Hour on host %s: %s" % (host, e.code), logger.WARNING)
+            return False
+        except Exception, e:
+            logger.log(u"NMJ: Unknown exception: " + ex(e), logger.ERROR)
             return False
 
         # try to parse the resulting XML
@@ -137,10 +149,10 @@ def _sendNMJ(self, host, database, mount=None):
 
         # if the result was a number then consider that an error
         if int(result) > 0:
-            logger.log(u"NMJ: Popcorn Hour returned an errorcode: %s" % (result))
+            logger.log(u"NMJ: Popcorn Hour returned an errorcode: %s" % (result), logger.ERROR)
             return False
         else:
-            logger.log(u"NMJ: Started background scan.")
+            logger.log(u"NMJ: Started background scan.", logger.MESSAGE)
             return True
 
     def _notifyNMJ(self, host=None, database=None, mount=None, force=False):
@@ -176,14 +188,12 @@ def notify_snatch(self, ep_name):
         pass
 
     def notify_download(self, ep_name):
-        # TODO: Drop this and use update_library
-        if sickbeard.USE_NMJ:
-            self._notifyNMJ()
+        pass
 
     def test_notify(self, host, database, mount):
-        return self._sendNMJ(host, database, mount)
+        return self._notifyNMJ(host, database, mount, force=True)
 
-    def update_library(self, showName=None):
+    def update_library(self, ep_obj=None):
         if sickbeard.USE_NMJ:
             self._notifyNMJ()
 
diff --git a/sickbeard/notifiers/nmjv2.py b/sickbeard/notifiers/nmjv2.py
index 679381ae39..1128946837 100644
--- a/sickbeard/notifiers/nmjv2.py
+++ b/sickbeard/notifiers/nmjv2.py
@@ -44,7 +44,6 @@ def notify_settings(self, host, dbloc, instance):
         """
         try:
             url_loc = "http://" + host + ":8008/file_operation?arg0=list_user_storage_file&arg1=&arg2=" + instance + "&arg3=20&arg4=true&arg5=true&arg6=true&arg7=all&arg8=name_asc&arg9=false&arg10=false"
-            # TODO: Use our getURL from helper?
             req = urllib2.Request(url_loc)
             handle1 = urllib2.urlopen(req)
             response1 = handle1.read()
@@ -71,7 +70,7 @@ def notify_settings(self, host, dbloc, instance):
                         sickbeard.NMJv2_DATABASE = DB_path
                         return True
         except IOError, e:
-            logger.log(u"NMJv2: Warning: Couldn't contact Popcorn Hour on host %s: %s" % (host, e))
+            logger.log(u"NMJv2: Could not contact Popcorn Hour on host %s: %s" % (host, e), logger.WARNING)
             return False
 
         return False
@@ -90,9 +89,9 @@ def _sendNMJ(self, host):
         #if a host is provided then attempt to open a handle to that URL
         try:
             url_scandir = "http://" + host + ":8008/metadata_database?arg0=update_scandir&arg1=" + sickbeard.NMJv2_DATABASE + "&arg2=&arg3=update_all"
-            logger.log(u"NMJv2 scan update command send to host: %s" % (host))
+            logger.log(u"NMJv2: Scan update command send to host: %s" % (host), logger.DEBUG)
             url_updatedb = "http://" + host + ":8008/metadata_database?arg0=scanner_start&arg1=" + sickbeard.NMJv2_DATABASE + "&arg2=background&arg3="
-            logger.log(u"Try to mount network drive via url: %s" % (host), logger.DEBUG)
+            logger.log(u"NMJv2: Try to mount network drive via url: %s" % (host), logger.DEBUG)
             prereq = urllib2.Request(url_scandir)
             req = urllib2.Request(url_updatedb)
             handle1 = urllib2.urlopen(prereq)
@@ -101,7 +100,7 @@ def _sendNMJ(self, host):
             handle2 = urllib2.urlopen(req)
             response2 = handle2.read()
         except IOError, e:
-            logger.log(u"NMJv2: Warning: Couldn't contact Popcorn Hour on host %s: %s" % (host, e))
+            logger.log(u"NMJv2: Could not contact Popcorn Hour on host %s: %s" % (host, e), logger.WARNING)
             return False
 
         try:
@@ -130,15 +129,15 @@ def _sendNMJ(self, host):
 
         if int(result1) > 0:
             index = error_codes.index(result1)
-            logger.log(u"NMJv2: Popcorn Hour returned an error: %s" % (error_messages[index]))
+            logger.log(u"NMJv2: Popcorn Hour returned an error: %s" % (error_messages[index]), logger.ERROR)
             return False
         else:
             if int(result2) > 0:
                 index = error_codes.index(result2)
-                logger.log(u"NMJv2: Popcorn Hour returned an error: %s" % (error_messages[index]))
+                logger.log(u"NMJv2: Popcorn Hour returned an error: %s" % (error_messages[index]), logger.ERROR)
                 return False
             else:
-                logger.log(u"NMJv2: Started background scan.")
+                logger.log(u"NMJv2: Started background scan.", logger.MESSAGE)
                 return True
 
     def _notifyNMJ(self, host=None, force=False):
@@ -170,14 +169,12 @@ def notify_snatch(self, ep_name):
         pass
 
     def notify_download(self, ep_name):
-        # TODO: Drop this and use update_library
-        if sickbeard.USE_NMJv2:
-            self._notifyNMJ()
+        pass
 
     def test_notify(self, host):
-        return self._sendNMJ(host)
+        return self._notifyNMJ(host, force=True)
 
-    def update_library(self, showName=None):
+    def update_library(self, ep_obj=None):
         if sickbeard.USE_NMJv2:
             self._notifyNMJ()
 
diff --git a/sickbeard/notifiers/plex.py b/sickbeard/notifiers/plex.py
index e870a86102..18d87aa835 100644
--- a/sickbeard/notifiers/plex.py
+++ b/sickbeard/notifiers/plex.py
@@ -56,7 +56,7 @@ def _send_to_plex(self, command, host, username=None, password=None):
             password = sickbeard.PLEX_PASSWORD
 
         if not host:
-            logger.log(u"PLEX: No Plex host specified, check your settings", logger.DEBUG)
+            logger.log(u"PLEX: No host specified, check your settings", logger.ERROR)
             return False
 
         for key in command:
@@ -64,7 +64,7 @@ def _send_to_plex(self, command, host, username=None, password=None):
                 command[key] = command[key].encode('utf-8')
 
         enc_command = urllib.urlencode(command)
-        logger.log(u"PLEX: Plex encoded API command: " + enc_command, logger.DEBUG)
+        logger.log(u"PLEX: Encoded API command: " + enc_command, logger.DEBUG)
 
         url = 'http://%s/xbmcCmds/xbmcHttp/?%s' % (host, enc_command)
         try:
@@ -74,17 +74,16 @@ def _send_to_plex(self, command, host, username=None, password=None):
                 base64string = base64.encodestring('%s:%s' % (username, password))[:-1]
                 authheader = "Basic %s" % base64string
                 req.add_header("Authorization", authheader)
-                logger.log(u"PLEX: Contacting Plex (with auth header) via url: " + url, logger.DEBUG)
+                logger.log(u"PLEX: Contacting (with auth header) via url: " + url, logger.DEBUG)
             else:
-                logger.log(u"PLEX: Contacting Plex via url: " + url, logger.DEBUG)
+                logger.log(u"PLEX: Contacting via url: " + url, logger.DEBUG)
 
-            # TODO: Use our getURL from helper?
             response = urllib2.urlopen(req)
 
             result = response.read().decode(sickbeard.SYS_ENCODING)
             response.close()
 
-            logger.log(u"PLEX: Plex HTTP response: " + result.replace('\n', ''), logger.DEBUG)
+            logger.log(u"PLEX: HTTP response: " + result.replace('\n', ''), logger.DEBUG)
             # could return result response = re.compile('<html><li>(.+\w)</html>').findall(result)
             return 'OK'
 
@@ -101,7 +100,7 @@ def _notify(self, message, title="Sick Beard", host=None, username=None, passwor
             host: Plex Media Client(s) host:port
             username: Plex username
             password: Plex password
-            force: Used for the Test method to override config saftey checks
+            force: Used for the Test method to override config safety checks
 
         Returns:
             Returns a list results in the format of host:ip:result
@@ -123,7 +122,7 @@ def _notify(self, message, title="Sick Beard", host=None, username=None, passwor
 
         result = ''
         for curHost in [x.strip() for x in host.split(",")]:
-            logger.log(u"PLEX: Sending Plex notification to '" + curHost + "' - " + message, logger.MESSAGE)
+            logger.log(u"PLEX: Sending notification to '" + curHost + "' - " + message, logger.MESSAGE)
 
             command = {'command': 'ExecBuiltIn', 'parameter': 'Notification(' + title.encode("utf-8") + ',' + message.encode("utf-8") + ')'}
             notifyResult = self._send_to_plex(command, curHost, username, password)
@@ -147,7 +146,7 @@ def notify_download(self, ep_name):
     def test_notify(self, host, username, password):
         return self._notify("Testing Plex notifications from Sick Beard", "Test Notification", host, username, password, force=True)
 
-    def update_library(self):
+    def update_library(self, ep_obj=None):
         """Handles updating the Plex Media Server host via HTTP API
 
         Plex Media Server currently only supports updating the whole video library and not a specific path.
@@ -159,14 +158,13 @@ def update_library(self):
 
         if sickbeard.USE_PLEX and sickbeard.PLEX_UPDATE_LIBRARY:
             if not sickbeard.PLEX_SERVER_HOST:
-                logger.log(u"PLEX: No Plex Server host specified, check your settings", logger.DEBUG)
+                logger.log(u"PLEX: No Plex Media Server host specified, check your settings", logger.DEBUG)
                 return False
 
             logger.log(u"PLEX: Updating library for the Plex Media Server host: " + sickbeard.PLEX_SERVER_HOST, logger.MESSAGE)
 
             url = "http://%s/library/sections" % sickbeard.PLEX_SERVER_HOST
             try:
-                # TODO: Use our getURL from helper?
                 xml_tree = etree.parse(urllib.urlopen(url))
                 media_container = xml_tree.getroot()
             except IOError, e:
diff --git a/sickbeard/notifiers/prowl.py b/sickbeard/notifiers/prowl.py
index 74cf9657ae..30d78ade5a 100644
--- a/sickbeard/notifiers/prowl.py
+++ b/sickbeard/notifiers/prowl.py
@@ -42,12 +42,7 @@ def _notify(self, prowl_api=None, prowl_priority=None, event=None, message=None,
 
         title = "Sick Beard"
 
-        # TODO: Consolidate this to one logging dict?
-        logger.log(u"PROWL: title: " + title, logger.DEBUG)
-        logger.log(u"PROWL: event: " + event, logger.DEBUG)
-        logger.log(u"PROWL: message: " + message, logger.DEBUG)
-        logger.log(u"PROWL: api: " + prowl_api, logger.DEBUG)
-        logger.log(u"PROWL: priority: " + prowl_priority, logger.DEBUG)
+        logger.log("PROWL: Sending notice with details: event=\"%s\", message=\"%s\", priority=%s, api=%s" % (event, message, prowl_priority, prowl_api), logger.DEBUG)
 
         try:
 
@@ -74,7 +69,7 @@ def _notify(self, prowl_api=None, prowl_priority=None, event=None, message=None,
             return False
 
         if request_status == 200:
-            logger.log(u"PROWL: Notifications sent.", logger.DEBUG)
+            logger.log(u"PROWL: Notifications sent.", logger.MESSAGE)
             return True
         elif request_status == 401:
             logger.log(u"PROWL: Auth failed: %s" % response.reason, logger.ERROR)
@@ -98,7 +93,7 @@ def notify_download(self, ep_name):
     def test_notify(self, prowl_api, prowl_priority):
         return self._notify(prowl_api, prowl_priority, event="Test", message="Testing Prowl settings from Sick Beard", force=True)
 
-    def update_library(self, showName=None):
+    def update_library(self, ep_obj=None):
         pass
 
 notifier = ProwlNotifier
diff --git a/sickbeard/notifiers/pushover.py b/sickbeard/notifiers/pushover.py
index 386a194d7f..7ff796f18b 100644
--- a/sickbeard/notifiers/pushover.py
+++ b/sickbeard/notifiers/pushover.py
@@ -62,7 +62,6 @@ def _sendPushover(self, msg, title, userKey=None):
 
         # send the request to pushover
         try:
-            # TODO: Use our getURL from helper?
             req = urllib2.Request(curUrl)
             handle = urllib2.urlopen(req, data)
             handle.close()
@@ -73,7 +72,7 @@ def _sendPushover(self, msg, title, userKey=None):
                 logger.log(u"PUSHOVER: Notification failed." + ex(e), logger.ERROR)
                 return False
             else:
-                logger.log(u"PUSHOVER: Notification failed. Error code: " + str(e.code), logger.WARNING)
+                logger.log(u"PUSHOVER: Notification failed. Error code: " + str(e.code), logger.ERROR)
 
             # HTTP status 404 if the provided email address isn't a Pushover user.
             if e.code == 404:
@@ -97,10 +96,10 @@ def _sendPushover(self, msg, title, userKey=None):
                 logger.log(u"PUSHOVER: Wrong data sent to Pushover", logger.ERROR)
                 return False
 
-        logger.log(u"PUSHOVER: Notification successful.", logger.DEBUG)
+        logger.log(u"PUSHOVER: Notification successful.", logger.MESSAGE)
         return True
 
-    def _notify(self, title, message, userKey=None ):
+    def _notify(self, title, message, userKey=None, force=False):
         """
         Sends a pushover notification based on the provided info or SB config
 
@@ -110,7 +109,7 @@ def _notify(self, title, message, userKey=None ):
         """
 
         # suppress notifications if the notifier is disabled but the notify options are checked
-        if not sickbeard.USE_PUSHOVER:
+        if not sickbeard.USE_PUSHOVER and not force:
             return False
 
         # fill in omitted parameters
@@ -119,8 +118,7 @@ def _notify(self, title, message, userKey=None ):
 
         logger.log(u"PUSHOVER: Sending notification for " + message, logger.DEBUG)
 
-        self._sendPushover(message, title)
-        return True
+        return self._sendPushover(message, title)
 
 ##############################################################################
 # Public functions
@@ -135,9 +133,9 @@ def notify_download(self, ep_name):
             self._notify(notifyStrings[NOTIFY_DOWNLOAD], ep_name)
 
     def test_notify(self, userKey=None):
-        return self._sendPushover("This is a test notification from SickBeard", 'Test', userKey)
+        return self._notify("This is a test notification from Sick Beard", "Test", userKey, force=True)
 
-    def update_library(self, showName=None):
+    def update_library(self, ep_obj=None):
         pass
 
 notifier = PushoverNotifier
diff --git a/sickbeard/notifiers/pytivo.py b/sickbeard/notifiers/pytivo.py
index 6172fa2f3b..5c3446dd4d 100644
--- a/sickbeard/notifiers/pytivo.py
+++ b/sickbeard/notifiers/pytivo.py
@@ -20,9 +20,10 @@
 import sickbeard
 
 from urllib import urlencode
-from urllib2 import Request, urlopen, URLError
+from urllib2 import Request, urlopen
 
 from sickbeard import logger
+from sickbeard.exceptions import ex
 from sickbeard import encodingKludge as ek
 
 
@@ -38,7 +39,7 @@ def notify_snatch(self, ep_name):
     def notify_download(self, ep_name):
         pass
 
-    def update_library(self, ep_obj):
+    def update_library(self, ep_obj=None):
 
         if not sickbeard.USE_PYTIVO:
             return False
@@ -74,22 +75,23 @@ def update_library(self, ep_obj):
         # Finally create the url and make request
         requestUrl = "http://" + host + "/TiVoConnect?" + urlencode( {'Command': 'Push', 'Container': container, 'File': mediaFile, 'tsn': tsn})
 
-        logger.log(u"PYTIVO: Requesting " + requestUrl)
+        logger.log(u"PYTIVO: Requesting " + requestUrl, logger.DEBUG)
 
-        # TODO: Use our getURL from helper?
-        request = Request( requestUrl )
+        request = Request(requestUrl)
 
         try:
             response = urlopen(request)  # @UnusedVariable
-        except URLError, e:
+        except IOError, e:
             if hasattr(e, 'reason'):
-                logger.log(u"PYTIVO: Error, failed to reach a server - " + str(e.reason))
-                return False
+                logger.log(u"PYTIVO: Failed to reach server '%s' - %s" % (host, e.reason), logger.WARNING)
             elif hasattr(e, 'code'):
-                logger.log(u"PYTIVO: Error, the server couldn't fulfill the request - " + str(e.code))
-                return False
+                logger.log(u"PYTIVO: The server could not fulfill the request '%s' - %s" % (host, e.code), logger.WARNING)
+            return False
+        except Exception, e:
+            logger.log(u"PYTIVO: Unknown exception: " + ex(e), logger.ERROR)
+            return False
         else:
-            logger.log(u"PYTIVO: Successfully requested transfer of file")
+            logger.log(u"PYTIVO: Successfully requested transfer of file", logger.MESSAGE)
             return True
 
 notifier = pyTivoNotifier
diff --git a/sickbeard/notifiers/synoindex.py b/sickbeard/notifiers/synoindex.py
index 3edcefd2dd..7daaea6769 100755
--- a/sickbeard/notifiers/synoindex.py
+++ b/sickbeard/notifiers/synoindex.py
@@ -22,6 +22,7 @@
 import sickbeard
 
 from sickbeard import logger
+from sickbeard.common import notifyStrings, NOTIFY_SNATCH, NOTIFY_DOWNLOAD
 from sickbeard import encodingKludge as ek
 from sickbeard.exceptions import ex
 
@@ -37,14 +38,14 @@ def moveFile(self, old_file, new_file):
     def moveObject(self, old_path, new_path):
         if sickbeard.USE_SYNOINDEX:
             synoindex_cmd = ['/usr/syno/bin/synoindex', '-N', ek.ek(os.path.abspath, new_path), ek.ek(os.path.abspath, old_path)]
-            logger.log(u"SYNOINDEX: Executing command " + str(synoindex_cmd))
+            logger.log(u"SYNOINDEX: Executing command " + str(synoindex_cmd), logger.DEBUG)
             logger.log(u"SYNOINDEX: Absolute path to command: " + ek.ek(os.path.abspath, synoindex_cmd[0]), logger.DEBUG)
             try:
                 p = subprocess.Popen(synoindex_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=sickbeard.PROG_DIR)
                 out, err = p.communicate()  # @UnusedVariable
                 logger.log(u"SYNOINDEX: Script result: " + str(out), logger.DEBUG)
             except OSError, e:
-                logger.log(u"SYNOINDEX: Unable to run synoindex: " + ex(e))
+                logger.log(u"SYNOINDEX: Unable to run synoindex: " + ex(e), logger.WARNING)
 
     def deleteFolder(self, cur_path):
         self.makeObject('-D', cur_path)
@@ -61,23 +62,58 @@ def addFile(self, cur_file):
     def makeObject(self, cmd_arg, cur_path):
         if sickbeard.USE_SYNOINDEX:
             synoindex_cmd = ['/usr/syno/bin/synoindex', cmd_arg, ek.ek(os.path.abspath, cur_path)]
-            logger.log(u"SYNOINDEX: Executing command " + str(synoindex_cmd))
+            logger.log(u"SYNOINDEX: Executing command " + str(synoindex_cmd), logger.DEBUG)
             logger.log(u"SYNOINDEX: Absolute path to command: " + ek.ek(os.path.abspath, synoindex_cmd[0]), logger.DEBUG)
             try:
                 p = subprocess.Popen(synoindex_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=sickbeard.PROG_DIR)
                 out, err = p.communicate()  # @UnusedVariable
                 logger.log(u"SYNOINDEX: Script result: " + str(out), logger.DEBUG)
             except OSError, e:
-                logger.log(u"SYNOINDEX: Unable to run synoindex: " + ex(e))
+                logger.log(u"SYNOINDEX: Unable to run synoindex: " + ex(e), logger.WARNING)
+
+    def _notify(self, message, title, force=False):
+        # suppress notifications if the notifier is disabled but the notify options are checked
+        if not sickbeard.USE_SYNOINDEX and not force:
+            return False
+
+        synodsmnotify_cmd = ['/usr/syno/bin/synodsmnotify', '@administrators', title, message]
+        logger.log(u"SYNOINDEX: Executing command " + str(synodsmnotify_cmd), logger.DEBUG)
+
+        try:
+            p = subprocess.Popen(synodsmnotify_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
+                                 cwd=sickbeard.PROG_DIR)
+
+            output, err = p.communicate()  # @UnusedVariable
+            exit_status = p.returncode
+
+            logger.log(u"SYNOINDEX: Script result: " + str(output), logger.DEBUG)
+
+            if exit_status == 0:
+                return True
+            else:
+                return False
+
+        except OSError, e:
+            logger.log(u"SYNOINDEX: Unable to run synodsmnotify: " + ex(e), logger.WARNING)
+            return False
 
 ##############################################################################
 # Public functions
 ##############################################################################
 
     def notify_snatch(self, ep_name):
-        pass
+        if sickbeard.SYNOINDEX_NOTIFY_ONSNATCH:
+            self._notify(notifyStrings[NOTIFY_SNATCH], ep_name)
 
     def notify_download(self, ep_name):
-        pass
+        if sickbeard.SYNOINDEX_NOTIFY_ONDOWNLOAD:
+            self._notify(notifyStrings[NOTIFY_DOWNLOAD], ep_name)
+
+    def test_notify(self):
+        return self._notify("This is a test notification from Sick Beard", "Test", force=True)
+
+    def update_library(self, ep_obj=None):
+        if sickbeard.USE_SYNOINDEX:
+            self.addFile(ep_obj.location)
 
 notifier = synoIndexNotifier
diff --git a/sickbeard/notifiers/trakt.py b/sickbeard/notifiers/trakt.py
index 45cbed60f7..1d51366d17 100644
--- a/sickbeard/notifiers/trakt.py
+++ b/sickbeard/notifiers/trakt.py
@@ -32,7 +32,7 @@
 
 class TraktNotifier:
 
-    def _notifyTrakt(self, method, api, username, password, data={}):
+    def _notifyTrakt(self, method, api, username, password, data={}, force=False):
         """
         A generic method for communicating with trakt. Uses the method and data provided along
         with the auth info to send the command.
@@ -44,6 +44,10 @@ def _notifyTrakt(self, method, api, username, password, data={}):
 
         Returns: A boolean representing success
         """
+        # suppress notifications if the notifier is disabled but the notify options are checked
+        if not sickbeard.USE_TRAKT and not force:
+            return False
+
         logger.log(u"TRAKT: Calling method " + method, logger.DEBUG)
 
         # if the API isn't given then use the config API
@@ -71,7 +75,6 @@ def _notifyTrakt(self, method, api, username, password, data={}):
         # request the URL from trakt and parse the result as json
         try:
             logger.log(u"TRAKT: Calling method http://api.trakt.tv/" + method + ", with data" + encoded_data, logger.DEBUG)
-            # TODO: Use our getURL from helper?
             stream = urllib2.urlopen("http://api.trakt.tv/" + method, encoded_data)
             resp = stream.read()
 
@@ -85,7 +88,7 @@ def _notifyTrakt(self, method, api, username, password, data={}):
             return False
 
         if (resp["status"] == "success"):
-            logger.log(u"TRAKT: Succeeded calling method. Result: " + resp["message"], logger.DEBUG)
+            logger.log(u"TRAKT: Succeeded calling method. Result: " + resp["message"], logger.MESSAGE)
             return True
 
         logger.log(u"TRAKT: Failed calling method", logger.ERROR)
@@ -114,9 +117,9 @@ def test_notify(self, api, username, password):
         """
 
         method = "account/test/"
-        return self._notifyTrakt(method, api, username, password, {})
+        return self._notifyTrakt(method, api, username, password, {}, force=True)
 
-    def update_library(self, ep_obj):
+    def update_library(self, ep_obj=None):
         """
         Sends a request to trakt indicating that the given episode is part of our library.
 
diff --git a/sickbeard/notifiers/tweet.py b/sickbeard/notifiers/tweet.py
index 68f2ea2d4d..b243c09a62 100644
--- a/sickbeard/notifiers/tweet.py
+++ b/sickbeard/notifiers/tweet.py
@@ -47,12 +47,12 @@ def _get_authorization(self):
         oauth_consumer = oauth.Consumer(key=self.consumer_key, secret=self.consumer_secret)
         oauth_client = oauth.Client(oauth_consumer)
 
-        logger.log(u'TWITTER: Requesting temp token from Twitter')
+        logger.log(u'TWITTER: Requesting temp token from Twitter', logger.DEBUG)
 
         resp, content = oauth_client.request(self.REQUEST_TOKEN_URL, 'GET')
 
         if resp['status'] != '200':
-            logger.log(u"TWITTER: Invalid respond from Twitter requesting temp token: %s" % resp['status'])
+            logger.log(u"TWITTER: Invalid respond from Twitter requesting temp token: %s" % resp['status'], logger.ERROR)
         else:
             request_token = dict(parse_qsl(content))
 
@@ -71,26 +71,26 @@ def _get_credentials(self, key):
         token = oauth.Token(request_token['oauth_token'], request_token['oauth_token_secret'])
         token.set_verifier(key)
 
-        logger.log(u"TWITTER: Generating and signing request for an access token using key " + key)
+        logger.log(u"TWITTER: Generating and signing request for an access token using key " + key, logger.DEBUG)
 
         signature_method_hmac_sha1 = oauth.SignatureMethod_HMAC_SHA1()  # @UnusedVariable
         oauth_consumer = oauth.Consumer(key=self.consumer_key, secret=self.consumer_secret)
-        logger.log(u"TWITTER: oauth_consumer: " + str(oauth_consumer))
+        logger.log(u"TWITTER: oauth_consumer: " + str(oauth_consumer), logger.DEBUG)
         oauth_client = oauth.Client(oauth_consumer, token)
-        logger.log(u"TWITTER: oauth_client: " + str(oauth_client))
+        logger.log(u"TWITTER: oauth_client: " + str(oauth_client), logger.DEBUG)
         resp, content = oauth_client.request(self.ACCESS_TOKEN_URL, method='POST', body='oauth_verifier=%s' % key)
-        logger.log(u"TWITTER: resp, content: " + str(resp) + "," + str(content))
+        logger.log(u"TWITTER: resp, content: " + str(resp) + "," + str(content), logger.DEBUG)
 
         access_token = dict(parse_qsl(content))
-        logger.log(u"TWITTER: access_token: " + str(access_token))
+        logger.log(u"TWITTER: access_token: " + str(access_token), logger.DEBUG)
 
-        logger.log(u"TWITTER: resp[status] = " + str(resp['status']))
+        logger.log(u"TWITTER: resp[status] = " + str(resp['status']), logger.DEBUG)
         if resp['status'] != '200':
             logger.log(u"TWITTER: The request for a token with did not succeed: " + str(resp['status']), logger.ERROR)
             return False
         else:
-            logger.log(u"TWITTER: Your Twitter Access Token key: %s" % access_token['oauth_token'])
-            logger.log(u"TWITTER: Access Token secret: %s" % access_token['oauth_token_secret'])
+            logger.log(u"TWITTER: Your Twitter Access Token key: %s" % access_token['oauth_token'], logger.DEBUG)
+            logger.log(u"TWITTER: Access Token secret: %s" % access_token['oauth_token_secret'], logger.DEBUG)
             sickbeard.TWITTER_USERNAME = access_token['oauth_token']
             sickbeard.TWITTER_PASSWORD = access_token['oauth_token_secret']
             return True
@@ -102,7 +102,7 @@ def _send_tweet(self, message=None):
         access_token_key = sickbeard.TWITTER_USERNAME
         access_token_secret = sickbeard.TWITTER_PASSWORD
 
-        logger.log(u"TWITTER: Sending tweet: " + message)
+        logger.log(u"TWITTER: Sending tweet: " + message, logger.DEBUG)
 
         api = twitter.Api(username, password, access_token_key, access_token_secret)
 
@@ -136,7 +136,7 @@ def notify_download(self, ep_name):
     def test_notify(self):
         return self._notify("This is a test notification from Sick Beard", force=True)
 
-    def update_library(self, showName=None):
+    def update_library(self, ep_obj):
         pass
 
 notifier = TwitterNotifier
diff --git a/sickbeard/notifiers/xbmc.py b/sickbeard/notifiers/xbmc.py
index 3488a35c67..21c713803a 100644
--- a/sickbeard/notifiers/xbmc.py
+++ b/sickbeard/notifiers/xbmc.py
@@ -67,7 +67,7 @@ def _get_xbmc_version(self, host, username, password):
                  3  | (pre Eden)
                  4  | v11 (Eden)
                  5  | (pre Frodo)
-                 6  | v12 (Frodo)
+                 6  | v12 (Frodo) / v13 (Gotham)
 
         """
 
@@ -174,7 +174,7 @@ def _send_to_xbmc(self, command, host=None, username=None, password=None):
             password = sickbeard.XBMC_PASSWORD
 
         if not host:
-            logger.log(u"XBMC: No XBMC host passed, aborting update", logger.DEBUG)
+            logger.log(u"XBMC: No host specified, check your settings", logger.DEBUG)
             return False
 
         for key in command:
@@ -182,7 +182,7 @@ def _send_to_xbmc(self, command, host=None, username=None, password=None):
                 command[key] = command[key].encode('utf-8')
 
         enc_command = urllib.urlencode(command)
-        logger.log(u"XBMC: XBMC encoded API command: " + enc_command, logger.DEBUG)
+        logger.log(u"XBMC: Encoded API command: " + enc_command, logger.DEBUG)
 
         url = 'http://%s/xbmcCmds/xbmcHttp/?%s' % (host, enc_command)
         try:
@@ -197,11 +197,11 @@ def _send_to_xbmc(self, command, host=None, username=None, password=None):
             result = response.read().decode(sickbeard.SYS_ENCODING)
             response.close()
 
-            logger.log(u"XBMC: XBMC HTTP response: " + result.replace('\n', ''), logger.DEBUG)
+            logger.log(u"XBMC: HTTP response: " + result.replace('\n', ''), logger.DEBUG)
             return result
 
         except (urllib2.URLError, IOError), e:
-            logger.log(u"XBMC: Warning: Couldn't contact XBMC HTTP at " + fixStupidEncodings(url) + " " + ex(e), logger.WARNING)
+            logger.log(u"XBMC: Could not contact XBMC HTTP at " + fixStupidEncodings(url) + " " + ex(e), logger.WARNING)
             return False
 
     def _update_library(self, host=None, showName=None):
@@ -220,14 +220,12 @@ def _update_library(self, host=None, showName=None):
         """
 
         if not host:
-            logger.log(u"XBMC: No XBMC host passed, aborting update", logger.DEBUG)
+            logger.log(u"XBMC: No host specified, check your settings", logger.DEBUG)
             return False
 
-        logger.log(u"XBMC: Updating XBMC library via HTTP method for host: " + host, logger.DEBUG)
-
         # if we're doing per-show
         if showName:
-            logger.log(u"XBMC: Updating library in XBMC via HTTP method for show " + showName, logger.DEBUG)
+            logger.log(u"XBMC: Updating library via HTTP method for show " + showName, logger.MESSAGE)
 
             pathSql = 'select path.strPath from path, tvshow, tvshowlinkpath where ' \
                 'tvshow.c00 = "%s" and tvshowlinkpath.idShow = tvshow.idShow ' \
@@ -268,23 +266,23 @@ def _update_library(self, host=None, showName=None):
             for path in paths:
                 # we do not need it double-encoded, gawd this is dumb
                 unEncPath = urllib.unquote(path.text).decode(sickbeard.SYS_ENCODING)
-                logger.log(u"XBMC: XBMC Updating " + showName + " on " + host + " at " + unEncPath, logger.MESSAGE)
+                logger.log(u"XBMC: Updating " + showName + " on " + host + " at " + unEncPath, logger.MESSAGE)
                 updateCommand = {'command': 'ExecBuiltIn', 'parameter': 'XBMC.updatelibrary(video, %s)' % (unEncPath)}
                 request = self._send_to_xbmc(updateCommand, host)
                 if not request:
-                    logger.log(u"XBMC: Update of show directory failed on " + showName + " on " + host + " at " + unEncPath, logger.ERROR)
+                    logger.log(u"XBMC: Update of show directory failed on " + showName + " on " + host + " at " + unEncPath, logger.WARNING)
                     return False
                 # sleep for a few seconds just to be sure xbmc has a chance to finish each directory
                 if len(paths) > 1:
                     time.sleep(5)
         # do a full update if requested
         else:
-            logger.log(u"XBMC: Doing Full Library XBMC update on host: " + host, logger.DEBUG)
+            logger.log(u"XBMC: Doing Full Library update via HTTP method for host: " + host, logger.MESSAGE)
             updateCommand = {'command': 'ExecBuiltIn', 'parameter': 'XBMC.updatelibrary(video)'}
             request = self._send_to_xbmc(updateCommand, host)
 
             if not request:
-                logger.log(u"XBMC: XBMC Full Library update failed on: " + host, logger.ERROR)
+                logger.log(u"XBMC: Full Library update failed on: " + host, logger.ERROR)
                 return False
 
         return True
@@ -314,11 +312,11 @@ def _send_to_xbmc_json(self, command, host=None, username=None, password=None):
             password = sickbeard.XBMC_PASSWORD
 
         if not host:
-            logger.log(u"XBMC: No XBMC host passed, aborting update", logger.DEBUG)
+            logger.log(u"XBMC: No host specified, check your settings", logger.DEBUG)
             return False
 
         command = command.encode('utf-8')
-        logger.log(u"XBMC: XBMC JSON command: " + command, logger.DEBUG)
+        logger.log(u"XBMC: JSON command: " + command, logger.DEBUG)
 
         url = 'http://%s/jsonrpc' % (host)
         try:
@@ -340,14 +338,14 @@ def _send_to_xbmc_json(self, command, host=None, username=None, password=None):
             try:
                 result = json.load(response)
                 response.close()
-                logger.log(u"XBMC: XBMC JSON response: " + str(result), logger.DEBUG)
+                logger.log(u"XBMC: JSON response: " + str(result), logger.DEBUG)
                 return result  # need to return response for parsing
             except ValueError, e:
                 logger.log(u"XBMC: Unable to decode JSON: " + response, logger.WARNING)
                 return False
 
         except IOError, e:
-            logger.log(u"XBMC: Warning: Couldn't contact XBMC JSON API at " + fixStupidEncodings(url) + " " + ex(e), logger.WARNING)
+            logger.log(u"XBMC: Could not contact XBMC JSON API at " + fixStupidEncodings(url) + " " + ex(e), logger.WARNING)
             return False
 
     def _update_library_json(self, host=None, showName=None):
@@ -366,15 +364,13 @@ def _update_library_json(self, host=None, showName=None):
         """
 
         if not host:
-            logger.log(u"XBMC: No XBMC host passed, aborting update", logger.DEBUG)
+            logger.log(u"XBMC: No host specified, check your settings", logger.DEBUG)
             return False
 
-        logger.log(u"XBMC: Updating XBMC library via JSON method for host: " + host, logger.MESSAGE)
-
         # if we're doing per-show
         if showName:
             tvshowid = -1
-            logger.log(u"XBMC: Updating library in XBMC via JSON method for show " + showName, logger.DEBUG)
+            logger.log(u"XBMC: Updating library via JSON method for show " + showName, logger.MESSAGE)
 
             # get tvshowid by showName
             showsCommand = '{"jsonrpc":"2.0","method":"VideoLibrary.GetTVShows","id":1}'
@@ -410,11 +406,11 @@ def _update_library_json(self, host=None, showName=None):
                 logger.log(u"XBMC: No valid path found for " + showName + " with ID: " + str(tvshowid) + " on " + host, logger.WARNING)
                 return False
 
-            logger.log(u"XBMC: XBMC Updating " + showName + " on " + host + " at " + path, logger.MESSAGE)
+            logger.log(u"XBMC: Updating " + showName + " on " + host + " at " + path, logger.MESSAGE)
             updateCommand = '{"jsonrpc":"2.0","method":"VideoLibrary.Scan","params":{"directory":%s},"id":1}' % (json.dumps(path))
             request = self._send_to_xbmc_json(updateCommand, host)
             if not request:
-                logger.log(u"XBMC: Update of show directory failed on " + showName + " on " + host + " at " + path, logger.ERROR)
+                logger.log(u"XBMC: Update of show directory failed on " + showName + " on " + host + " at " + path, logger.WARNING)
                 return False
 
             # catch if there was an error in the returned request
@@ -425,12 +421,12 @@ def _update_library_json(self, host=None, showName=None):
 
         # do a full update if requested
         else:
-            logger.log(u"XBMC: Doing Full Library XBMC update on host: " + host, logger.MESSAGE)
+            logger.log(u"XBMC: Doing Full Library update via JSON method for host: " + host, logger.MESSAGE)
             updateCommand = '{"jsonrpc":"2.0","method":"VideoLibrary.Scan","id":1}'
             request = self._send_to_xbmc_json(updateCommand, host, sickbeard.XBMC_USERNAME, sickbeard.XBMC_PASSWORD)
 
             if not request:
-                logger.log(u"XBMC: XBMC Full Library update failed on: " + host, logger.ERROR)
+                logger.log(u"XBMC: Full Library update failed on: " + host, logger.ERROR)
                 return False
 
         return True
@@ -450,7 +446,7 @@ def notify_download(self, ep_name):
     def test_notify(self, host, username, password):
         return self._notify("Testing XBMC notifications from Sick Beard", "Test Notification", host, username, password, force=True)
 
-    def update_library(self, showName=None):
+    def update_library(self, ep_obj=None, show_obj=None):
         """Public wrapper for the update library functions to branch the logic for JSON-RPC or legacy HTTP API
 
         Checks the XBMC API version to branch the logic to call either the legacy HTTP API or the newer JSON-RPC over HTTP methods.
@@ -466,9 +462,16 @@ def update_library(self, showName=None):
 
         """
 
+        if ep_obj:
+            showName = ep_obj.show.name
+        elif show_obj:
+            showName = show_obj.name
+        else:
+            showName = None
+
         if sickbeard.USE_XBMC and sickbeard.XBMC_UPDATE_LIBRARY:
             if not sickbeard.XBMC_HOST:
-                logger.log(u"XBMC: No XBMC hosts specified, check your settings", logger.DEBUG)
+                logger.log(u"XBMC: No host specified, check your settings", logger.DEBUG)
                 return False
 
             if sickbeard.XBMC_UPDATE_ONLYFIRST:
@@ -479,26 +482,20 @@ def update_library(self, showName=None):
 
             result = 0
             for curHost in [x.strip() for x in host.split(",")]:
-                logger.log(u"XBMC: Sending request to update library for XBMC host: '" + curHost + "'", logger.MESSAGE)
+                logger.log(u"XBMC: Sending request to update library for host: '" + curHost + "'", logger.MESSAGE)
 
                 xbmcapi = self._get_xbmc_version(curHost, sickbeard.XBMC_USERNAME, sickbeard.XBMC_PASSWORD)
                 if xbmcapi:
                     if (xbmcapi <= 4):
                         # try to update for just the show, if it fails, do full update if enabled
                         if not self._update_library(curHost, showName):
-                            if showName:
-                                logger.log(u"XBMC: XBMC single show update failed", logger.WARNING)
-                                if  sickbeard.XBMC_UPDATE_FULL:
-                                    logger.log(u"XBMC: Falling back to XBMC full update")
-                                    self._update_library(curHost)
+                            if showName and sickbeard.XBMC_UPDATE_FULL:
+                                self._update_library(curHost)
                     else:
                         # try to update for just the show, if it fails, do full update if enabled
                         if not self._update_library_json(curHost, showName):
-                            if showName:
-                                logger.log(u"XBMC: XBMC single show update failed", logger.WARNING)
-                                if  sickbeard.XBMC_UPDATE_FULL:
-                                    logger.log(u"XBMC: Falling back to XBMC full update")
-                                    self._update_library_json(curHost)
+                            if showName and sickbeard.XBMC_UPDATE_FULL:
+                                self._update_library_json(curHost)
                 else:
                     if sickbeard.XBMC_ALWAYS_ON:
                         logger.log(u"XBMC: Failed to detect XBMC version for '" + curHost + "', check configuration and try again.", logger.ERROR)
diff --git a/sickbeard/postProcessor.py b/sickbeard/postProcessor.py
index 1ce5885c80..8ec0368b7a 100644
--- a/sickbeard/postProcessor.py
+++ b/sickbeard/postProcessor.py
@@ -83,12 +83,12 @@ def __init__(self, file_path, nzb_name=None, pp_options={}):
         self.force_replace = pp_options.get('force_replace', False)
 
         self.in_history = False
+
         self.release_group = None
-        self.is_proper = False
 
-        self.good_results = {self.NZB_NAME: False,
-                             self.FOLDER_NAME: False,
-                             self.FILE_NAME: False}
+        self.release_name = None
+
+        self.is_proper = False
 
         self.log = ''
 
@@ -359,8 +359,10 @@ def _analyze_name(self, name, file_name=True):
         if not name:
             return to_return
 
+        name = helpers.remove_non_release_groups(helpers.remove_extension(name))
+
         # parse the name to break it into show name, season, and episode
-        np = NameParser(file_name)
+        np = NameParser(False)
         parse_result = np.parse(name)
         self._log(u"Parsed " + name + " into " + str(parse_result).decode('utf-8', 'xmlcharrefreplace'), logger.DEBUG)
 
@@ -437,16 +439,10 @@ def _finalize(self, parse_result):
         self.is_proper = parse_result.is_proper
 
         # if the result is complete then remember that for later
-        if parse_result.series_name and parse_result.season_number != None and parse_result.episode_numbers and parse_result.release_group:
-            test_name = ek.ek(os.path.basename, parse_result.original_name)
-            if test_name == self.nzb_name:
-                self.good_results[self.NZB_NAME] = True
-            elif test_name == self.folder_name:
-                self.good_results[self.FOLDER_NAME] = True
-            elif test_name == self.file_name:
-                self.good_results[self.FILE_NAME] = True
-            else:
-                logger.log(u"Nothing was good, found " + repr(test_name) + " and wanted either " + repr(self.nzb_name) + ", " + repr(self.folder_name) + ", or " + repr(self.file_name))
+        if parse_result.series_name and parse_result.season_number is not None and parse_result.episode_numbers and parse_result.release_group:
+            if not self.release_name:
+                self.release_name = helpers.remove_extension(ek.ek(os.path.basename, parse_result.original_name))
+
         else:
             logger.log(u"Parse result not sufficient (all following have to be set). will not save release name", logger.DEBUG)
             logger.log(u"Parse result(series_name): " + str(parse_result.series_name), logger.DEBUG)
@@ -813,28 +809,11 @@ def process(self):
 
         # update the ep info before we rename so the quality & release name go into the name properly
         for cur_ep in [ep_obj] + ep_obj.relatedEps:
-            cur_release_name = None
-
-            # use the best possible representation of the release name
-            if self.good_results[self.NZB_NAME]:
-                cur_release_name = self.nzb_name
-                if cur_release_name.lower().endswith('.nzb'):
-                    cur_release_name = cur_release_name.rpartition('.')[0]
-
-            elif self.good_results[self.FILE_NAME]:
-                cur_release_name = self.file_name
-                # take the extension off the filename, it's not needed
-                if '.' in self.file_name:
-                    cur_release_name = self.file_name.rpartition('.')[0]
-
-            elif self.good_results[self.FOLDER_NAME]:
-                cur_release_name = self.folder_name
-
-            if cur_release_name:
-                self._log("Found release name " + cur_release_name, logger.DEBUG)
-                cur_ep.release_name = cur_release_name
+
+            if self.release_name:
+                self._log("Found release name " + self.release_name, logger.DEBUG)
+                cur_ep.release_name = self.release_name
             else:
-                logger.log(u"good results: " + repr(self.good_results), logger.DEBUG)
                 cur_ep.release_name = ""
 
             cur_ep.status = common.Quality.compositeStatus(common.DOWNLOADED, new_ep_quality)
@@ -883,30 +862,15 @@ def process(self):
         # log it to history
         history.logDownload(ep_obj, self.file_path, new_ep_quality, self.release_group)
 
-        # send notifications
+        # send notifiers download notification
         notifiers.notify_download(ep_obj.prettyName())
 
         # generate nfo/tbn
         ep_obj.createMetaFiles()
         ep_obj.saveToDB()
 
-        # do the library update for XBMC
-        notifiers.xbmc_notifier.update_library(ep_obj.show.name)
-
-        # do the library update for Plex
-        notifiers.plex_notifier.update_library()
-
-        # do the library update for NMJ
-        # nmj_notifier kicks off its library update when the notify_download is issued (inside notifiers)
-
-        # do the library update for Synology Indexer
-        notifiers.synoindex_notifier.addFile(ep_obj.location)
-
-        # do the library update for pyTivo
-        notifiers.pytivo_notifier.update_library(ep_obj)
-
-        # do the library update for Trakt
-        notifiers.trakt_notifier.update_library(ep_obj)
+        # send notifiers library update
+        notifiers.update_library(ep_obj)
 
         self._run_extra_scripts(ep_obj)
 
diff --git a/sickbeard/properFinder.py b/sickbeard/properFinder.py
index 37785358a3..1a8d041908 100644
--- a/sickbeard/properFinder.py
+++ b/sickbeard/properFinder.py
@@ -48,9 +48,6 @@ def run(self):
 
         # look for propers every night at 1 AM
         updateTime = datetime.time(hour=1)
-
-        logger.log(u"Checking proper time", logger.DEBUG)
-
         hourDiff = datetime.datetime.today().time().hour - updateTime.hour
 
         # if it's less than an interval after the update time then do an update
diff --git a/sickbeard/scheduler.py b/sickbeard/scheduler.py
index a0973d5209..e7816f9859 100644
--- a/sickbeard/scheduler.py
+++ b/sickbeard/scheduler.py
@@ -24,14 +24,12 @@
 from sickbeard import logger
 from sickbeard.exceptions import ex
 
+
 class Scheduler:
 
-    def __init__(self, action, cycleTime=datetime.timedelta(minutes=10), runImmediately=True, threadName="ScheduledThread", silent=False):
+    def __init__(self, action, cycleTime=datetime.timedelta(minutes=10), run_delay=datetime.timedelta(minutes=0), threadName="ScheduledThread", silent=False):
 
-        if runImmediately:
-            self.lastRun = datetime.datetime.fromordinal(1)
-        else:
-            self.lastRun = datetime.datetime.now()
+        self.lastRun = datetime.datetime.now() + run_delay - cycleTime
 
         self.action = action
         self.cycleTime = cycleTime
@@ -63,14 +61,14 @@ def runAction(self):
 
             currentTime = datetime.datetime.now()
 
-            if currentTime - self.lastRun > self.cycleTime:
+            if currentTime - self.lastRun >= self.cycleTime:
                 self.lastRun = currentTime
                 try:
                     if not self.silent:
-                        logger.log(u"Starting new thread: "+self.threadName, logger.DEBUG)
+                        logger.log(u"Starting new thread: " + self.threadName, logger.DEBUG)
                     self.action.run()
                 except Exception, e:
-                    logger.log(u"Exception generated in thread "+self.threadName+": " + ex(e), logger.ERROR)
+                    logger.log(u"Exception generated in thread " + self.threadName + ": " + ex(e), logger.ERROR)
                     logger.log(repr(traceback.format_exc()), logger.DEBUG)
 
             if self.abort:
diff --git a/sickbeard/showUpdater.py b/sickbeard/showUpdater.py
index 8e0324b519..0e884dd339 100644
--- a/sickbeard/showUpdater.py
+++ b/sickbeard/showUpdater.py
@@ -41,9 +41,6 @@ def run(self, force=False):
 
         update_datetime = datetime.datetime.today()
         update_date = update_datetime.date()
-
-        logger.log(u"Checking update interval", logger.DEBUG)
-
         hour_diff = update_datetime.time().hour - run_updater_time.hour
 
         # if it's less than an interval after the update time then do an update (or if we're forcing it)
diff --git a/sickbeard/tv.py b/sickbeard/tv.py
index 7ca977f201..ac785f7a02 100644
--- a/sickbeard/tv.py
+++ b/sickbeard/tv.py
@@ -292,20 +292,20 @@ def loadEpisodesFromDir(self):
             if curEpisode is None:
                 continue
 
-            # see if we should save the release name in the db
-            ep_file_name = ek.ek(os.path.basename, curEpisode.location)
-            ep_file_name = ek.ek(os.path.splitext, ep_file_name)[0]
+            if not curEpisode.release_name:
+                ep_file_name = ek.ek(os.path.basename, curEpisode.location)
+                ep_base_name = helpers.remove_non_release_groups(helpers.remove_extension(ep_file_name))
 
-            parse_result = None
-            try:
-                np = NameParser(False)
-                parse_result = np.parse(ep_file_name)
-            except InvalidNameException:
-                pass
+                parse_result = None
+                try:
+                    np = NameParser(False)
+                    parse_result = np.parse(ep_base_name)
+                except InvalidNameException:
+                    pass
 
-            if not ' ' in ep_file_name and parse_result and parse_result.release_group:
-                logger.log(u"Name " + ep_file_name + u" gave release group of " + parse_result.release_group + ", seems valid", logger.DEBUG)
-                curEpisode.release_name = ep_file_name
+                if not ' ' in ep_base_name and parse_result and parse_result.release_group:
+                    logger.log(u"Name " + ep_base_name + u" gave release group of " + parse_result.release_group + ", seems valid", logger.DEBUG)
+                    curEpisode.release_name = ep_base_name
 
             # store the reference in the show
             if curEpisode != None:
@@ -1446,24 +1446,28 @@ def us(name):
             return re.sub('[ -]', '_', name)
 
         def release_name(name):
-            if name and name.lower().endswith('.nzb'):
-                name = name.rpartition('.')[0]
+            if name:
+                name = helpers.remove_non_release_groups(helpers.remove_extension(name))
             return name
 
         def release_group(name):
-            if not name:
-                return ''
 
-            np = NameParser(name)
+            if name:
+                name = helpers.remove_non_release_groups(helpers.remove_extension(name))
+            else:
+                return ""
+
+            np = NameParser(False)
 
             try:
                 parse_result = np.parse(name)
             except InvalidNameException, e:
                 logger.log(u"Unable to get parse release_group: " + ex(e), logger.DEBUG)
-                return ''
+                return ""
 
             if not parse_result.release_group:
-                return ''
+                return ""
+
             return parse_result.release_group
 
         epStatus, epQual = Quality.splitCompositeStatus(self.status)  # @UnusedVariable
diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py
index 3f293107bc..ef640944da 100644
--- a/sickbeard/webserve.py
+++ b/sickbeard/webserve.py
@@ -744,11 +744,6 @@ def saveSearch(self, use_nzbs=None, use_torrents=None, nzb_dir=None, sab_usernam
         # Episode Search
         sickbeard.DOWNLOAD_PROPERS = config.checkbox_to_value(download_propers)
 
-        if sickbeard.DOWNLOAD_PROPERS:
-            sickbeard.properFinderScheduler.silent = False
-        else:
-            sickbeard.properFinderScheduler.silent = True
-
         config.change_SEARCH_FREQUENCY(search_frequency)
         sickbeard.USENET_RETENTION = config.to_int(usenet_retention, default=500)
 
@@ -1089,26 +1084,28 @@ def index(self):
         return _munge(t)
 
     @cherrypy.expose
-    def saveNotifications(self, use_xbmc=None, xbmc_always_on=None, xbmc_notify_onsnatch=None, xbmc_notify_ondownload=None, xbmc_update_onlyfirst=None,
-                          xbmc_update_library=None, xbmc_update_full=None, xbmc_host=None, xbmc_username=None, xbmc_password=None,
+    def saveNotifications(self,
+                          use_xbmc=None, xbmc_always_on=None, xbmc_notify_onsnatch=None, xbmc_notify_ondownload=None, xbmc_update_onlyfirst=None,
+                              xbmc_update_library=None, xbmc_update_full=None, xbmc_host=None, xbmc_username=None, xbmc_password=None,
                           use_plex=None, plex_notify_onsnatch=None, plex_notify_ondownload=None, plex_update_library=None,
-                          plex_server_host=None, plex_host=None, plex_username=None, plex_password=None,
+                              plex_server_host=None, plex_host=None, plex_username=None, plex_password=None,
                           use_growl=None, growl_notify_onsnatch=None, growl_notify_ondownload=None, growl_host=None, growl_password=None,
                           use_prowl=None, prowl_notify_onsnatch=None, prowl_notify_ondownload=None, prowl_api=None, prowl_priority=0,
                           use_twitter=None, twitter_notify_onsnatch=None, twitter_notify_ondownload=None,
                           use_boxcar=None, boxcar_notify_onsnatch=None, boxcar_notify_ondownload=None, boxcar_username=None,
                           use_pushover=None, pushover_notify_onsnatch=None, pushover_notify_ondownload=None, pushover_userkey=None,
                           use_libnotify=None, libnotify_notify_onsnatch=None, libnotify_notify_ondownload=None,
-                          use_nmj=None, nmj_host=None, nmj_database=None, nmj_mount=None, use_synoindex=None,
+                          use_nmj=None, nmj_host=None, nmj_database=None, nmj_mount=None,
+                          use_synoindex=None, synoindex_notify_onsnatch=None, synoindex_notify_ondownload=None, synoindex_update_library=None,
                           use_nmjv2=None, nmjv2_host=None, nmjv2_dbloc=None, nmjv2_database=None,
                           use_trakt=None, trakt_username=None, trakt_password=None, trakt_api=None,
                           use_pytivo=None, pytivo_notify_onsnatch=None, pytivo_notify_ondownload=None, pytivo_update_library=None,
-                          pytivo_host=None, pytivo_share_name=None, pytivo_tivo_name=None,
+                              pytivo_host=None, pytivo_share_name=None, pytivo_tivo_name=None,
                           use_nma=None, nma_notify_onsnatch=None, nma_notify_ondownload=None, nma_api=None, nma_priority=0):
 
         results = []
 
-        # Home Theater
+        # Home Theater / NAS
         sickbeard.USE_XBMC = config.checkbox_to_value(use_xbmc)
         sickbeard.XBMC_ALWAYS_ON = config.checkbox_to_value(xbmc_always_on)
         sickbeard.XBMC_NOTIFY_ONSNATCH = config.checkbox_to_value(xbmc_notify_onsnatch)
@@ -1140,6 +1137,9 @@ def saveNotifications(self, use_xbmc=None, xbmc_always_on=None, xbmc_notify_onsn
         sickbeard.NMJv2_DBLOC = nmjv2_dbloc
 
         sickbeard.USE_SYNOINDEX = config.checkbox_to_value(use_synoindex)
+        sickbeard.SYNOINDEX_NOTIFY_ONSNATCH = config.checkbox_to_value(synoindex_notify_onsnatch)
+        sickbeard.SYNOINDEX_NOTIFY_ONDOWNLOAD = config.checkbox_to_value(synoindex_notify_ondownload)
+        sickbeard.SYNOINDEX_UPDATE_LIBRARY = config.checkbox_to_value(synoindex_update_library)
 
         sickbeard.USE_PYTIVO = config.checkbox_to_value(use_pytivo)
         # sickbeard.PYTIVO_NOTIFY_ONSNATCH = config.checkbox_to_value(pytivo_notify_onsnatch)
@@ -2029,6 +2029,16 @@ def testNMA(self, nma_api=None, nma_priority=0):
         else:
             return "Test NMA notice failed"
 
+    @cherrypy.expose
+    def testSynoNotify(self):
+        cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store"
+
+        result = notifiers.synoindex_notifier.test_notify()
+        if result:
+            return "Test Synology notice sent successfully"
+        else:
+            return "Test Synology notice failed"
+
     @cherrypy.expose
     def shutdown(self, pid=None):
 
@@ -2125,7 +2135,7 @@ def displayShow(self, show=None):
                 t.submenu.append({ 'title': 'Delete',               'path': 'home/deleteShow?show=%d' % showObj.tvdbid, 'confirm': True })
                 t.submenu.append({ 'title': 'Re-scan files',        'path': 'home/refreshShow?show=%d' % showObj.tvdbid })
                 t.submenu.append({ 'title': 'Force Full Update',    'path': 'home/updateShow?show=%d&amp;force=1' % showObj.tvdbid })
-                t.submenu.append({ 'title': 'Update show in XBMC',  'path': 'home/updateXBMC?showName=%s' % urllib.quote_plus(showObj.name.encode('utf-8')), 'requires': haveXBMC })
+                t.submenu.append({ 'title': 'Update show in XBMC',  'path': 'home/updateXBMC?show=%d' % showObj.tvdbid, 'requires': haveXBMC })
                 t.submenu.append({ 'title': 'Preview Rename',       'path': 'home/testRename?show=%d' % showObj.tvdbid })
 
         t.show = showObj
@@ -2343,14 +2353,19 @@ def updateShow(self, show=None, force=0):
         redirect("/home/displayShow?show=" + str(showObj.tvdbid))
 
     @cherrypy.expose
-    def updateXBMC(self, showName=None):
+    def updateXBMC(self, show=None):
         if sickbeard.XBMC_UPDATE_ONLYFIRST:
             # only send update to first host in the list -- workaround for xbmc sql backend users
             host = sickbeard.XBMC_HOST.split(",")[0].strip()
         else:
             host = sickbeard.XBMC_HOST
 
-        if notifiers.xbmc_notifier.update_library(showName=showName):
+        if show:
+            show_obj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(show))
+        else:
+            show_obj = None
+
+        if notifiers.xbmc_notifier.update_library(show_obj=show_obj):
             ui.notifications.message("Library update command sent to XBMC host(s): " + host)
         else:
             ui.notifications.error("Unable to contact one or more XBMC host(s): " + host)