diff --git a/SickBeard.py b/SickBeard.py
index a5456a42a30ec8709ab35008ff577b49cda04dbd..a3a32124034f4a00d927cec5186b43a26948bcdf 100755
--- a/SickBeard.py
+++ b/SickBeard.py
@@ -26,10 +26,14 @@ codecs.register(lambda name: codecs.lookup('utf-8') if name == 'cp65001' else No
 import time
 import signal
 import sys
-import shutil
 import subprocess
 import traceback
 
+import shutil
+import lib.shutil_custom
+
+shutil.copyfile = lib.shutil_custom.copyfile_custom
+
 if sys.version_info < (2, 6):
     print "Sorry, requires Python 2.6 or 2.7."
     sys.exit(1)
diff --git a/gui/slick/images/providers/binsearch.png b/gui/slick/images/providers/binsearch.png
new file mode 100644
index 0000000000000000000000000000000000000000..890ff858119a95e9841fc9bb8f38bd5989d850f8
Binary files /dev/null and b/gui/slick/images/providers/binsearch.png differ
diff --git a/gui/slick/interfaces/default/config_general.tmpl b/gui/slick/interfaces/default/config_general.tmpl
index a42dad039b965463c989f1eaa042c9b0efa1dd07..3a45fbe84ba014f784fc0b2dce5ce1bce3096a78 100644
--- a/gui/slick/interfaces/default/config_general.tmpl
+++ b/gui/slick/interfaces/default/config_general.tmpl
@@ -83,6 +83,16 @@
 							</label>
 						</div>
 
+						<div class="field-pair">
+							<label for="update_shows_on_snatch">
+								<span class="component-title">Update shows on snatch</span>
+								<span class="component-desc">
+									<input type="checkbox" name="update_shows_on_snatch" id="update_shows_on_snatch" #if $sickbeard.UPDATE_SHOWS_ON_SNATCH then 'checked="checked"' else ''#/>
+									<p>with information such as next air dates, show ended, etc.</p>
+								</span>
+							</label>
+						</div>
+
 						<div class="field-pair">
 							<span class="component-title">Send to trash for actions</span>
 							<span class="component-desc">
@@ -334,7 +344,7 @@
 								<span class="component-title">Browser video player</span>
 								<span class="component-desc">
 									<input type="checkbox" name="play_videos" id="play_videos" #if $sickbeard.PLAY_VIDEOS then 'checked="checked"' else ''#/>
-									<p>play video files from display show page<?p>
+									<p>play video files from display show page</p>
 								</span>
 							</label>
 						</div>
@@ -568,14 +578,29 @@
 								<span class="component-title">Branch version:</span>
 								<span class="component-desc">
 									<select id="branchVersion" class="form-control form-control-inline input-sm pull-left">
-									#if $sickbeard.versionCheckScheduler.action.list_remote_branches() 
-									#for $cur_branch in $sickbeard.versionCheckScheduler.action.list_remote_branches():
-										<option value="$cur_branch" #if $cur_branch == $sickbeard.BRANCH then 'selected="selected"' else ''#>$cur_branch</option>
-									#end for
+									#set $gh_branch = $sickbeard.versionCheckScheduler.action.list_remote_branches()
+									#if $gh_branch:
+										#for $cur_branch in $gh_branch:
+											#if $sickbeard.GIT_USERNAME and $sickbeard.GIT_PASSWORD and $sickbeard.DEVELOPER == 1
+												<option value="$cur_branch" #if $cur_branch == $sickbeard.BRANCH then 'selected="selected"' else ''#>$cur_branch</option>
+											#else if $sickbeard.GIT_USERNAME and $sickbeard.GIT_PASSWORD and $cur_branch in ['master', 'develop']
+												<option value="$cur_branch" #if $cur_branch == $sickbeard.BRANCH then 'selected="selected"' else ''#>$cur_branch</option>
+											#else if $cur_branch == 'master'
+												<option value="$cur_branch" #if $cur_branch == $sickbeard.BRANCH then 'selected="selected"' else ''#>$cur_branch</option>
+											#end if
+										#end for
 									#end if
 									</select>
-									<input class="btn btn-inline" style="margin-left: 6px;" type="button" id="branchCheckout" value="Checkout Branch">
-									<div class="clear-left"><p>select branch to use (restart required)</p></div>
+									#if not $gh_branch
+									   <input class="btn btn-inline" style="margin-left: 6px;" type="button" id="branchCheckout" value="Checkout Branch" disabled>
+									#else
+									   <input class="btn btn-inline" style="margin-left: 6px;" type="button" id="branchCheckout" value="Checkout Branch">
+									#end if
+									#if not $gh_branch
+									   <div class="clear-left" style="color:#FF0000"><p>Error: No branches found.</p></div>
+									#else
+									   <div class="clear-left"><p>select branch to use (restart required)</p></div>
+									#end if
 								</span>
 							</label>
 						</div>
@@ -630,11 +655,11 @@
 							</label>
 						</div>
 
-						<div class="field-pair">
+						<div class="field-pair" hidden>
 							<label for="git_autoissues">
 								<span class="component-title">Git auto-issues submit</span>
 								<span class="component-desc">
-									<input type="checkbox" name="git_autoissues" id="git_autoissues" #if True == $sickbeard.GIT_AUTOISSUES then 'checked="checked"' else ''#/>
+									<input type="checkbox" name="git_autoissues" id="git_autoissues" #if True == $sickbeard.GIT_AUTOISSUES then 'checked="checked"' else ''# disable/>
 									<p>automatically submit bug/issue reports to our issue tracker when errors are logged</p>
 								</span>
 							</label>
diff --git a/gui/slick/interfaces/default/config_notifications.tmpl b/gui/slick/interfaces/default/config_notifications.tmpl
index 5d367cb8adc0326f5c8a3532275a65a788d01164..2a397300f72f902e27fb3b25356f1b43e667b306 100644
--- a/gui/slick/interfaces/default/config_notifications.tmpl
+++ b/gui/slick/interfaces/default/config_notifications.tmpl
@@ -1444,15 +1444,16 @@
                                 </label>
                             </div>
                             <div class="field-pair">
-                                <label for="trakt_use_watchlist">
-                                    <span class="component-title">Use watchlist:</span>
+                                <label for="trakt_sync_watchlist">
+                                    <span class="component-title">Sync watchlist:</span>
                                     <span class="component-desc">
-                                        <input type="checkbox" class="enabler" name="trakt_use_watchlist" id="trakt_use_watchlist" #if $sickbeard.TRAKT_USE_WATCHLIST then "checked=\"checked\"" else ""# />
-                                        <p>get new shows from your trakt watchlist.</p>
+                                        <input type="checkbox" class="enabler" name="trakt_sync_watchlist" id="trakt_sync_watchlist" #if $sickbeard.TRAKT_SYNC_WATCHLIST then "checked=\"checked\"" else ""# />
+                                        <p>sync your SickRage show watchlist with your trakt show watchlist (either Show and Episode).</p>
+                                        <p>Episode will be added on watch list when wanted or snatched and will be removed when downloaded </p>
                                     </span>
                                 </label>
                             </div>
-                            <div id="content_trakt_use_watchlist">
+                            <div id="content_trakt_sync_watchlist">
                                 <div class="field-pair">
                                     <label for="trakt_method_add">
                                         <span class="component-title">Watchlist add method:</span>
diff --git a/gui/slick/interfaces/default/config_providers.tmpl b/gui/slick/interfaces/default/config_providers.tmpl
index ef5bc13c72fb452eb4c187372b50d4adf118bd38..88aa3e084d452733b35f9da3223c7cc4f23abdd5 100644
--- a/gui/slick/interfaces/default/config_providers.tmpl
+++ b/gui/slick/interfaces/default/config_providers.tmpl
@@ -37,7 +37,7 @@ var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#;
 <!--
 \$(document).ready(function(){
 #for $curTorrentRssProvider in $sickbeard.torrentRssProviderList:
-    \$(this).addTorrentRssProvider('$curTorrentRssProvider.getID()', '$curTorrentRssProvider.name', '$curTorrentRssProvider.url', '$curTorrentRssProvider.cookies');
+    \$(this).addTorrentRssProvider('$curTorrentRssProvider.getID()', '$curTorrentRssProvider.name', '$curTorrentRssProvider.url', '$curTorrentRssProvider.cookies', '$curTorrentRssProvider.titleTAG');
 #end for
 });
 //-->
@@ -688,7 +688,16 @@ var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#;
                                 <span class="component-desc">eg. uid=xx;pass=yy</span>
                             </label>
                         </div>
-                        
+                        <div class="field-pair">
+                            <label for="torrentrss_titleTAG">
+                                <span class="component-title">Search element:</span>
+                                <input type="text" id="torrentrss_titleTAG" class="form-control input-sm input200" value="title"/>
+                            </label>
+                            <label>
+                                <span class="component-title">&nbsp;</span>
+                                <span class="component-desc">eg: title</span>
+                            </label>
+                        </div>
                         <div id="torrentrss_add_div">
                             <input type="button" class="btn torrentrss_save" id="torrentrss_add" value="Add" />
                         </div>
diff --git a/gui/slick/interfaces/default/home.tmpl b/gui/slick/interfaces/default/home.tmpl
index a2d29e07c31ec6d3b0f9ae2aa84eed86a15fd4d3..5a406c25be462d8babd382a15e61d510f1faf16b 100644
--- a/gui/slick/interfaces/default/home.tmpl
+++ b/gui/slick/interfaces/default/home.tmpl
@@ -384,7 +384,13 @@ $myShowList.sort(lambda x, y: cmp(x.name, y.name))
 		<div class="show-date">
 #if $cur_airs_next
     #set $ldatetime = $sbdatetime.sbdatetime.convert_to_setting($network_timezones.parse_date_time($cur_airs_next,$curShow.airs,$curShow.network))
-			<span class="${fuzzydate}">$sbdatetime.sbdatetime.sbfdate($ldatetime)</span>
+			<span class="${fuzzydate}">
+                #try
+                    $sbdatetime.sbdatetime.sbfdate($ldatetime)
+                #except ValueError
+                    Invalid date
+                #end try
+            </span>
 #else
     #set $output_html = '?'
     #if None is not $display_status
@@ -536,7 +542,13 @@ $myShowList.sort(lambda x, y: cmp(x.name, y.name))
 	
 	#if $cur_airs_next
     #set $ldatetime = $sbdatetime.sbdatetime.convert_to_setting($network_timezones.parse_date_time($cur_airs_next,$curShow.airs,$curShow.network))
-		<td align="center" class="nowrap"><div class="${fuzzydate}">$sbdatetime.sbdatetime.sbfdate($ldatetime)</div><span class="sort_data">$calendar.timegm($ldatetime.timetuple())</span></td>
+		<td align="center" class="nowrap"><div class="${fuzzydate}">
+		#try
+		  $sbdatetime.sbdatetime.sbfdate($ldatetime)
+		#except ValueError
+		  Invalid date
+		#end try
+		</div><span class="sort_data">$calendar.timegm($ldatetime.timetuple())</span></td>
     #else:
     	<td align="center" class="nowrap"></td>
     #end if
diff --git a/gui/slick/interfaces/default/inc_top.tmpl b/gui/slick/interfaces/default/inc_top.tmpl
index e67363a9b98535416a3dac990465a42c8afc6731..07950ecedc8361cf458c944d8f56b24842035257 100644
--- a/gui/slick/interfaces/default/inc_top.tmpl
+++ b/gui/slick/interfaces/default/inc_top.tmpl
@@ -90,7 +90,7 @@
 				\$("#SubMenu a:contains('Clear History')").addClass('btn clearhistory').html('<span class="ui-icon ui-icon-trash pull-left"></span> Clear History');
 				\$("#SubMenu a:contains('Trim History')").addClass('btn trimhistory').html('<span class="ui-icon ui-icon-trash pull-left"></span> Trim History');
 				\$("#SubMenu a[href$='/errorlogs/clearerrors/']").addClass('btn').html('<span class="ui-icon ui-icon-trash pull-left"></span> Clear Errors');
-				#if sickbeard.GIT_USERNAME and sickbeard.GIT_PASSWORD and sickbeard.GIT_AUTOISSUES == 1:
+				#if sickbeard.GIT_USERNAME and sickbeard.GIT_PASSWORD:
 				\$("#SubMenu a[href$='/errorlogs/submit_errors/']").addClass('btn').html('<span class="ui-icon ui-icon-arrowreturnthick-1-n pull-left"></span> Submit Errors');
 				#end if
 				\$("#SubMenu a:contains('Re-scan')").addClass('btn').html('<span class="ui-icon ui-icon-refresh pull-left"></span> Re-scan');
@@ -253,7 +253,13 @@
 		</span>
 		</div>
 		#end if
-	  
+	  	
+	  	#if $sickbeard.BRANCH and $sickbeard.BRANCH != 'master' and not $sickbeard.DEVELOPER
+		<div class="alert alert-danger upgrade-notification" role="alert">
+			<span>You're using the $sickbeard.BRANCH branch. Please use 'master' unless specifically asked</span>
+		</div>
+		#end if
+		
 		#if $sickbeard.NEWEST_VERSION_STRING:	
 		<div class="alert alert-success upgrade-notification" role="alert">
 			<span>$sickbeard.NEWEST_VERSION_STRING</span>
diff --git a/gui/slick/interfaces/default/manage_backlogOverview.tmpl b/gui/slick/interfaces/default/manage_backlogOverview.tmpl
index 223c7199715a260c853bd218a357ca2962559cf9..e83b418017e51ef3eb5ab2ba98e715f2f780309c 100644
--- a/gui/slick/interfaces/default/manage_backlogOverview.tmpl
+++ b/gui/slick/interfaces/default/manage_backlogOverview.tmpl
@@ -46,25 +46,22 @@
 #end if
 #set $totalWanted = 0
 #set $totalQual = 0
-#set $totalSnatched = 0
 
 #for $curShow in $sickbeard.showList:
 #set $totalWanted = $totalWanted + $showCounts[$curShow.indexerid][$Overview.WANTED]
 #set $totalQual = $totalQual + $showCounts[$curShow.indexerid][$Overview.QUAL]
-#set $totalSnatched = $totalSnatched + $showCounts[$curShow.indexerid][$Overview.SNATCHED]
 #end for
 
 <div class="h2footer pull-right">
     <span class="listing-key wanted">Wanted: <b>$totalWanted</b></span>
     <span class="listing-key qual">Low Quality: <b>$totalQual</b></span>
-    <span class="listing-key snatched">Snatched: <b>$totalSnatched</b></span>
 </div><br/>
 
 <div class="float-left">
 Jump to Show
 	<select id="pickShow" class="form-control form-control-inline input-sm">
 	#for $curShow in sorted($sickbeard.showList, key = operator.attrgetter('name')):
-	#if $showCounts[$curShow.indexerid][$Overview.QUAL] + $showCounts[$curShow.indexerid][$Overview.WANTED] + $showCounts[$curShow.indexerid][$Overview.SNATCHED] != 0:
+	#if $showCounts[$curShow.indexerid][$Overview.QUAL] + $showCounts[$curShow.indexerid][$Overview.WANTED] != 0:
 	<option value="$curShow.indexerid">$curShow.name</option>
 	#end if
 	#end for
@@ -75,7 +72,7 @@ Jump to Show
 
 #for $curShow in sorted($sickbeard.showList, key = operator.attrgetter('name')):
 
-#if $showCounts[$curShow.indexerid][$Overview.QUAL] + $showCounts[$curShow.indexerid][$Overview.WANTED] + $showCounts[$curShow.indexerid][$Overview.SNATCHED] == 0:
+#if $showCounts[$curShow.indexerid][$Overview.QUAL] + $showCounts[$curShow.indexerid][$Overview.WANTED] == 0:
 #continue
 #end if
 
@@ -85,7 +82,6 @@ Jump to Show
 			<div class="pull-right">
 				<span class="listing-key wanted">Wanted: <b>$showCounts[$curShow.indexerid][$Overview.WANTED]</b></span>
 				<span class="listing-key qual">Low Quality: <b>$showCounts[$curShow.indexerid][$Overview.QUAL]</b></span>
-				<span class="listing-key snatched">Snatched: <b>$showCounts[$curShow.indexerid][$Overview.SNATCHED]</b></span>
 				<a class="btn btn-inline forceBacklog" href="$sbRoot/manage/backlogShow?indexer_id=$curShow.indexerid"><i class="icon-play-circle icon-white"></i> Force Backlog</a>
 			</div>
 		</td>
@@ -101,7 +97,7 @@ Jump to Show
         #continue
     #end try
 
-    #if $overview not in ($Overview.QUAL, $Overview.WANTED, $Overview.SNATCHED):
+    #if $overview not in ($Overview.QUAL, $Overview.WANTED):
         #continue
     #end if
 
diff --git a/gui/slick/js/configProviders.js b/gui/slick/js/configProviders.js
index da21be5bc75f5df3e1745f2ae1064ac7e057d249..aa9965afd6c57f2f4ff32583e2e1c1f46b5a8a22 100644
--- a/gui/slick/js/configProviders.js
+++ b/gui/slick/js/configProviders.js
@@ -80,9 +80,9 @@ $(document).ready(function(){
 
     }
 
-    $.fn.addTorrentRssProvider = function (id, name, url, cookies) {
+    $.fn.addTorrentRssProvider = function (id, name, url, cookies, titleTAG) {
 
-        var newData = [name, url, cookies];
+        var newData = [name, url, cookies, titleTAG];
         torrentRssProviders[id] = newData;
 
         $('#editATorrentRssProvider').addOption(id, name);
@@ -122,9 +122,10 @@ $(document).ready(function(){
 
     }
 
-    $.fn.updateTorrentRssProvider = function (id, url, cookies) {
+    $.fn.updateTorrentRssProvider = function (id, url, cookies, titleTAG) {
         torrentRssProviders[id][1] = url;
         torrentRssProviders[id][2] = cookies;
+        torrentRssProviders[id][3] = titleTAG;
         $(this).populateTorrentRssSection();
         $(this).makeTorrentRssProviderString();
     }
@@ -277,7 +278,7 @@ $(document).ready(function(){
         var selectedProvider = $('#editATorrentRssProvider :selected').val();
 
         if (selectedProvider == 'addTorrentRss') {
-            var data = ['','',''];
+            var data = ['','','','title'];
             $('#torrentrss_add_div').show();
             $('#torrentrss_update_div').hide();
         } else {
@@ -289,15 +290,18 @@ $(document).ready(function(){
         $('#torrentrss_name').val(data[0]);
         $('#torrentrss_url').val(data[1]);
         $('#torrentrss_cookies').val(data[2]);
+        $('#torrentrss_titleTAG').val(data[3]);
 
         if (selectedProvider == 'addTorrentRss') {
             $('#torrentrss_name').removeAttr("disabled");
             $('#torrentrss_url').removeAttr("disabled");
             $('#torrentrss_cookies').removeAttr("disabled");
+            $('#torrentrss_titleTAG').removeAttr("disabled");
         } else {
             $('#torrentrss_name').attr("disabled", "disabled");
             $('#torrentrss_url').removeAttr("disabled");
             $('#torrentrss_cookies').removeAttr("disabled");
+            $('#torrentrss_titleTAG').removeAttr("disabled");
             $('#torrentrss_delete').removeAttr("disabled");
         }
 
@@ -386,7 +390,7 @@ $(document).ready(function(){
 
     });
 
-    $('#torrentrss_url,#torrentrss_cookies').change(function(){
+    $('#torrentrss_url,#torrentrss_cookies,#torrentrss_titleTAG').change(function(){
 
         var selectedProvider = $('#editATorrentRssProvider :selected').val();
 
@@ -395,8 +399,9 @@ $(document).ready(function(){
 
         var url = $('#torrentrss_url').val();
         var cookies = $('#torrentrss_cookies').val();
+        var titleTAG = $('#torrentrss_titleTAG').val();
 
-        $(this).updateTorrentRssProvider(selectedProvider, url, cookies);
+        $(this).updateTorrentRssProvider(selectedProvider, url, cookies, titleTAG);
     });
 
     $('body').on('change', '#editAProvider',function(){
@@ -504,7 +509,8 @@ $(document).ready(function(){
         var name = $('#torrentrss_name').val();
         var url = $('#torrentrss_url').val();
         var cookies = $('#torrentrss_cookies').val();
-        var params = { name: name, url: url, cookies: cookies}
+        var titleTAG = $('#torrentrss_titleTAG').val();
+        var params = { name: name, url: url, cookies: cookies, titleTAG: titleTAG}
 
         // send to the form with ajax, get a return value
         $.getJSON(sbRoot + '/config/providers/canAddTorrentRssProvider', params,
@@ -514,7 +520,7 @@ $(document).ready(function(){
                     return;
                 }
 
-                $(this).addTorrentRssProvider(data.success, name, url, cookies);
+                $(this).addTorrentRssProvider(data.success, name, url, cookies, titleTAG);
                 $(this).refreshEditAProvider();
         });
     });
diff --git a/lib/shutil_custom/__init__.py b/lib/shutil_custom/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..32cdd9bf36c8b7f90c2a3d00b74d679112a6f74f
--- /dev/null
+++ b/lib/shutil_custom/__init__.py
@@ -0,0 +1,52 @@
+import os
+import platform
+import stat
+try:
+    from shutil import SpecialFileError, Error
+except:
+    from shutil import Error
+from shutil import _samefile
+
+
+def copyfile_custom(src, dst):
+    """Copy data from src to dst"""
+    if _samefile(src, dst):
+        raise Error("`%s` and `%s` are the same file" % (src, dst))
+
+    for fn in [src, dst]:
+        try:
+            st = os.stat(fn)
+        except OSError:
+            # File most likely does not exist
+            pass
+        else:
+            # XXX What about other special files? (sockets, devices...)
+            if stat.S_ISFIFO(st.st_mode):
+                try:
+                    raise SpecialFileError("`%s` is a named pipe" % fn)
+                except NameError:
+                    raise Error("`%s` is a named pipe" % fn)
+
+    try:
+        # Windows
+        O_BINARY = os.O_BINARY
+    except:
+        O_BINARY = 0
+
+    READ_FLAGS = os.O_RDONLY | O_BINARY
+    WRITE_FLAGS = os.O_WRONLY | os.O_CREAT | os.O_TRUNC | O_BINARY
+    BUFFER_SIZE = 128*1024
+
+    try:
+        fin = os.open(src, READ_FLAGS)
+        fout = os.open(dst, WRITE_FLAGS)
+        for x in iter(lambda: os.read(fin, BUFFER_SIZE), ""):
+            os.write(fout, x)
+    except Exception as e:
+        raise e
+    finally:
+        try:
+            os.close(fin)
+            os.close(fout)
+        except:
+            pass
diff --git a/setup.py b/setup.py
index a3e2eb89c6228633cbb39e9c6bb6b9d70f7a6ec1..5a694247417c3f73bdaf2953a4d9e02365a400d1 100644
--- a/setup.py
+++ b/setup.py
@@ -3,7 +3,6 @@ import urllib
 import ConfigParser
 import sys
 import os
-import shutil
 import zipfile
 import subprocess
 import fnmatch
@@ -11,6 +10,11 @@ import googlecode_upload
 
 from distutils.core import setup
 
+import shutil
+import lib.shutil_custom
+
+shutil.copyfile = lib.shutil_custom.copyfile_custom
+
 try:
     import py2exe
 except:
diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py
index 9ca4047022bd7dbaed0924d9f5fd84610ca78320..0ed591483d544d01f585f3ae9052c471c82949d6 100755
--- a/sickbeard/__init__.py
+++ b/sickbeard/__init__.py
@@ -25,6 +25,9 @@ import os
 import re
 import os.path
 import shutil
+import lib.shutil_custom
+
+shutil.copyfile = lib.shutil_custom.copyfile_custom
 
 from threading import Lock
 import sys
@@ -35,7 +38,7 @@ from sickbeard import providers, metadata, config, webserveInit
 from sickbeard.providers.generic import GenericProvider
 from providers import ezrss, btn, newznab, womble, thepiratebay, oldpiratebay, torrentleech, kat, iptorrents, \
     omgwtfnzbs, scc, hdtorrents, torrentday, hdbits, hounddawgs, nextgen, speedcd, nyaatorrents, fanzub, torrentbytes, animezb, \
-    freshontv, bitsoup, t411, tokyotoshokan, shazbat, rarbg, alpharatio, tntvillage
+    freshontv, bitsoup, t411, tokyotoshokan, shazbat, rarbg, alpharatio, tntvillage, binsearch
 from sickbeard.config import CheckSection, check_setting_int, check_setting_str, check_setting_float, ConfigMigrator, \
     naming_ep_type
 from sickbeard import searchBacklog, showUpdater, versionChecker, properFinder, autoPostProcesser, \
@@ -124,6 +127,7 @@ GIT_USERNAME = None
 GIT_PASSWORD = None
 GIT_PATH = None
 GIT_AUTOISSUES = False
+DEVELOPER = False
 
 INIT_LOCK = Lock()
 started = False
@@ -170,6 +174,7 @@ CACHE_DIR = None
 ACTUAL_CACHE_DIR = None
 ROOT_DIRS = None
 UPDATE_SHOWS_ON_START = False
+UPDATE_SHOWS_ON_SNATCH = False
 TRASH_REMOVE_SHOW = False
 TRASH_ROTATE_LOGS = False
 SORT_ARTICLE = False
@@ -261,6 +266,8 @@ NZBS_HASH = None
 
 WOMBLE = False
 
+BINSEARCH = False
+
 OMGWTFNZBS = False
 OMGWTFNZBS_USERNAME = None
 OMGWTFNZBS_APIKEY = None
@@ -405,7 +412,7 @@ TRAKT_USERNAME = None
 TRAKT_PASSWORD = None
 TRAKT_REMOVE_WATCHLIST = False
 TRAKT_REMOVE_SERIESLIST = False
-TRAKT_USE_WATCHLIST = False
+TRAKT_SYNC_WATCHLIST = False
 TRAKT_METHOD_ADD = 0
 TRAKT_START_PAUSED = False
 TRAKT_USE_RECOMMENDED = False
@@ -516,10 +523,10 @@ def initialize(consoleLogging=True):
             TORRENT_USERNAME, TORRENT_PASSWORD, TORRENT_HOST, TORRENT_PATH, TORRENT_SEED_TIME, TORRENT_PAUSED, TORRENT_HIGH_BANDWIDTH, TORRENT_LABEL, TORRENT_LABEL_ANIME, TORRENT_VERIFY_CERT, TORRENT_RPCURL, TORRENT_AUTH_TYPE, \
             USE_KODI, KODI_ALWAYS_ON, KODI_NOTIFY_ONSNATCH, KODI_NOTIFY_ONDOWNLOAD, KODI_NOTIFY_ONSUBTITLEDOWNLOAD, KODI_UPDATE_FULL, KODI_UPDATE_ONLYFIRST, \
             KODI_UPDATE_LIBRARY, KODI_HOST, KODI_USERNAME, KODI_PASSWORD, BACKLOG_FREQUENCY, \
-            USE_TRAKT, TRAKT_USERNAME, TRAKT_PASSWORD, TRAKT_REMOVE_WATCHLIST, TRAKT_USE_WATCHLIST, TRAKT_METHOD_ADD, TRAKT_START_PAUSED, traktCheckerScheduler, TRAKT_USE_RECOMMENDED, TRAKT_SYNC, TRAKT_DEFAULT_INDEXER, TRAKT_REMOVE_SERIESLIST, TRAKT_DISABLE_SSL_VERIFY, TRAKT_TIMEOUT, \
+            USE_TRAKT, TRAKT_USERNAME, TRAKT_PASSWORD, TRAKT_REMOVE_WATCHLIST, TRAKT_SYNC_WATCHLIST, TRAKT_METHOD_ADD, TRAKT_START_PAUSED, traktCheckerScheduler, TRAKT_USE_RECOMMENDED, TRAKT_SYNC, TRAKT_DEFAULT_INDEXER, TRAKT_REMOVE_SERIESLIST, TRAKT_DISABLE_SSL_VERIFY, TRAKT_TIMEOUT, \
             USE_PLEX, PLEX_NOTIFY_ONSNATCH, PLEX_NOTIFY_ONDOWNLOAD, PLEX_NOTIFY_ONSUBTITLEDOWNLOAD, PLEX_UPDATE_LIBRARY, \
             PLEX_SERVER_HOST, PLEX_SERVER_TOKEN, PLEX_HOST, PLEX_USERNAME, PLEX_PASSWORD, DEFAULT_BACKLOG_FREQUENCY, MIN_BACKLOG_FREQUENCY, BACKLOG_STARTUP, SKIP_REMOVED_FILES, \
-            showUpdateScheduler, __INITIALIZED__, LAUNCH_BROWSER, UPDATE_SHOWS_ON_START, TRASH_REMOVE_SHOW, TRASH_ROTATE_LOGS, SORT_ARTICLE, showList, loadingShowList, \
+            showUpdateScheduler, __INITIALIZED__, LAUNCH_BROWSER, UPDATE_SHOWS_ON_START, UPDATE_SHOWS_ON_SNATCH, TRASH_REMOVE_SHOW, TRASH_ROTATE_LOGS, SORT_ARTICLE, showList, loadingShowList, \
             NEWZNAB_DATA, NZBS, NZBS_UID, NZBS_HASH, INDEXER_DEFAULT, INDEXER_TIMEOUT, USENET_RETENTION, TORRENT_DIR, \
             QUALITY_DEFAULT, FLATTEN_FOLDERS_DEFAULT, SUBTITLES_DEFAULT, STATUS_DEFAULT, DAILYSEARCH_STARTUP, \
             GROWL_NOTIFY_ONSNATCH, GROWL_NOTIFY_ONDOWNLOAD, GROWL_NOTIFY_ONSUBTITLEDOWNLOAD, TWITTER_NOTIFY_ONSNATCH, TWITTER_NOTIFY_ONDOWNLOAD, TWITTER_NOTIFY_ONSUBTITLEDOWNLOAD, USE_FREEMOBILE, FREEMOBILE_ID, FREEMOBILE_APIKEY, FREEMOBILE_NOTIFY_ONSNATCH, FREEMOBILE_NOTIFY_ONDOWNLOAD, FREEMOBILE_NOTIFY_ONSUBTITLEDOWNLOAD, \
@@ -533,7 +540,7 @@ def initialize(consoleLogging=True):
             showQueueScheduler, searchQueueScheduler, ROOT_DIRS, CACHE_DIR, ACTUAL_CACHE_DIR, TIMEZONE_DISPLAY, \
             NAMING_PATTERN, NAMING_MULTI_EP, NAMING_ANIME_MULTI_EP, NAMING_FORCE_FOLDERS, NAMING_ABD_PATTERN, NAMING_CUSTOM_ABD, NAMING_SPORTS_PATTERN, NAMING_CUSTOM_SPORTS, NAMING_ANIME_PATTERN, NAMING_CUSTOM_ANIME, NAMING_STRIP_YEAR, \
             RENAME_EPISODES, AIRDATE_EPISODES, properFinderScheduler, PROVIDER_ORDER, autoPostProcesserScheduler, \
-            WOMBLE, OMGWTFNZBS, OMGWTFNZBS_USERNAME, OMGWTFNZBS_APIKEY, providerList, newznabProviderList, torrentRssProviderList, \
+            WOMBLE, BINSEARCH, OMGWTFNZBS, OMGWTFNZBS_USERNAME, OMGWTFNZBS_APIKEY, providerList, newznabProviderList, torrentRssProviderList, \
             EXTRA_SCRIPTS, USE_TWITTER, TWITTER_USERNAME, TWITTER_PASSWORD, TWITTER_PREFIX, DAILYSEARCH_FREQUENCY, \
             USE_BOXCAR, BOXCAR_USERNAME, BOXCAR_PASSWORD, BOXCAR_NOTIFY_ONDOWNLOAD, BOXCAR_NOTIFY_ONSUBTITLEDOWNLOAD, BOXCAR_NOTIFY_ONSNATCH, \
             USE_BOXCAR2, BOXCAR2_ACCESSTOKEN, BOXCAR2_NOTIFY_ONDOWNLOAD, BOXCAR2_NOTIFY_ONSUBTITLEDOWNLOAD, BOXCAR2_NOTIFY_ONSNATCH, \
@@ -551,7 +558,7 @@ def initialize(consoleLogging=True):
             AUTOPOSTPROCESSER_FREQUENCY, SHOWUPDATE_HOUR, DEFAULT_AUTOPOSTPROCESSER_FREQUENCY, MIN_AUTOPOSTPROCESSER_FREQUENCY, \
             ANIME_DEFAULT, NAMING_ANIME, ANIMESUPPORT, USE_ANIDB, ANIDB_USERNAME, ANIDB_PASSWORD, ANIDB_USE_MYLIST, \
             ANIME_SPLIT_HOME, SCENE_DEFAULT, PLAY_VIDEOS, DOWNLOAD_URL, BACKLOG_DAYS, GIT_ORG, GIT_REPO, GIT_USERNAME, GIT_PASSWORD, \
-            GIT_AUTOISSUES, gh
+            GIT_AUTOISSUES, DEVELOPER, gh
 
         if __INITIALIZED__:
             return False
@@ -583,6 +590,7 @@ def initialize(consoleLogging=True):
         # git login info
         GIT_USERNAME = check_setting_str(CFG, 'General', 'git_username', '')
         GIT_PASSWORD = check_setting_str(CFG, 'General', 'git_password', '', censor_log=True)
+        DEVELOPER = bool(check_setting_int(CFG, 'General', 'developer', 0))
 
         # debugging
         DEBUG = bool(check_setting_int(CFG, 'General', 'debug', 0))
@@ -718,6 +726,7 @@ def initialize(consoleLogging=True):
             ANON_REDIRECT = ''
 
         UPDATE_SHOWS_ON_START = bool(check_setting_int(CFG, 'General', 'update_shows_on_start', 0))
+        UPDATE_SHOWS_ON_SNATCH = bool(check_setting_int(CFG, 'General', 'update_shows_on_snatch', 0))
         TRASH_REMOVE_SHOW = bool(check_setting_int(CFG, 'General', 'trash_remove_show', 0))
         TRASH_ROTATE_LOGS = bool(check_setting_int(CFG, 'General', 'trash_rotate_logs', 0))
 
@@ -972,7 +981,7 @@ def initialize(consoleLogging=True):
         TRAKT_PASSWORD = check_setting_str(CFG, 'Trakt', 'trakt_password', '', censor_log=True)
         TRAKT_REMOVE_WATCHLIST = bool(check_setting_int(CFG, 'Trakt', 'trakt_remove_watchlist', 0))
         TRAKT_REMOVE_SERIESLIST = bool(check_setting_int(CFG, 'Trakt', 'trakt_remove_serieslist', 0))
-        TRAKT_USE_WATCHLIST = bool(check_setting_int(CFG, 'Trakt', 'trakt_use_watchlist', 0))
+        TRAKT_SYNC_WATCHLIST = bool(check_setting_int(CFG, 'Trakt', 'trakt_sync_watchlist', 0))
         TRAKT_METHOD_ADD = check_setting_int(CFG, 'Trakt', 'trakt_method_add', 0)
         TRAKT_START_PAUSED = bool(check_setting_int(CFG, 'Trakt', 'trakt_start_paused', 0))
         TRAKT_USE_RECOMMENDED = bool(check_setting_int(CFG, 'Trakt', 'trakt_use_recommended', 0))
@@ -1502,7 +1511,7 @@ def save_config():
     new_config['General'] = {}
     new_config['General']['git_autoissues'] = int(GIT_AUTOISSUES)
     new_config['General']['git_username'] = GIT_USERNAME
-    new_config['General']['git_password'] = GIT_PASSWORD
+    new_config['General']['git_password'] = helpers.encrypt(GIT_PASSWORD, ENCRYPTION_VERSION)
     new_config['General']['git_reset'] = int(GIT_RESET)
     new_config['General']['branch'] = BRANCH
     new_config['General']['git_remote'] = GIT_REMOTE
@@ -1575,6 +1584,7 @@ def save_config():
     new_config['General']['naming_anime'] = int(NAMING_ANIME)
     new_config['General']['launch_browser'] = int(LAUNCH_BROWSER)
     new_config['General']['update_shows_on_start'] = int(UPDATE_SHOWS_ON_START)
+    new_config['General']['update_shows_on_snatch'] = int(UPDATE_SHOWS_ON_SNATCH)
     new_config['General']['trash_remove_show'] = int(TRASH_REMOVE_SHOW)
     new_config['General']['trash_rotate_logs'] = int(TRASH_ROTATE_LOGS)
     new_config['General']['sort_article'] = int(SORT_ARTICLE)
@@ -1614,6 +1624,7 @@ def save_config():
     new_config['General']['ignore_words'] = IGNORE_WORDS
     new_config['General']['require_words'] = REQUIRE_WORDS
     new_config['General']['calendar_unprotected'] = int(CALENDAR_UNPROTECTED)
+    new_config['General']['developer'] = int(DEVELOPER)
 
     new_config['Blackhole'] = {}
     new_config['Blackhole']['nzb_dir'] = NZB_DIR
@@ -1865,7 +1876,7 @@ def save_config():
     new_config['Trakt']['trakt_password'] = helpers.encrypt(TRAKT_PASSWORD, ENCRYPTION_VERSION)
     new_config['Trakt']['trakt_remove_watchlist'] = int(TRAKT_REMOVE_WATCHLIST)
     new_config['Trakt']['trakt_remove_serieslist'] = int(TRAKT_REMOVE_SERIESLIST)
-    new_config['Trakt']['trakt_use_watchlist'] = int(TRAKT_USE_WATCHLIST)
+    new_config['Trakt']['trakt_sync_watchlist'] = int(TRAKT_SYNC_WATCHLIST)
     new_config['Trakt']['trakt_method_add'] = int(TRAKT_METHOD_ADD)
     new_config['Trakt']['trakt_start_paused'] = int(TRAKT_START_PAUSED)
     new_config['Trakt']['trakt_use_recommended'] = int(TRAKT_USE_RECOMMENDED)
diff --git a/sickbeard/helpers.py b/sickbeard/helpers.py
index 7d0503e75420750d6b9aabd5b21fbe7502d8e74f..29895e95606f32695110c1b78c0a7dd4594f1d22 100644
--- a/sickbeard/helpers.py
+++ b/sickbeard/helpers.py
@@ -22,7 +22,6 @@ import os
 import ctypes
 import random
 import re
-import shutil
 import socket
 import stat
 import tempfile
@@ -58,6 +57,11 @@ from sickbeard import clients
 from cachecontrol import CacheControl, caches
 from itertools import izip, cycle
 
+import shutil
+import lib.shutil_custom
+
+shutil.copyfile = lib.shutil_custom.copyfile_custom
+
 urllib._urlopener = classes.SickBeardURLopener()
 
 
@@ -349,12 +353,8 @@ def listMediaFiles(path):
     return files
 
 
-def copyFile(srcFile, destFile):
-    if isPosix():
-        subprocess.call(['cp', srcFile, destFile])
-    else:
-        ek.ek(shutil.copyfile, srcFile, destFile)
-        
+def copyFile(srcFile, destFile):
+    ek.ek(shutil.copyfile, srcFile, destFile)
     try:
         ek.ek(shutil.copymode, srcFile, destFile)
     except OSError:
@@ -378,12 +378,6 @@ def link(src, dst):
     else:
         os.link(src, dst)
 
-def isPosix(): 
-    if os.name.startswith('posix'):
-        return True
-    else:
-        return False
-
 
 def hardlinkFile(srcFile, destFile):
     try:
@@ -923,7 +917,7 @@ def _check_against_names(nameInQuestion, show, season=-1):
     return False
 
 
-def get_show(name, tryIndexers=False):
+def get_show(name, tryIndexers=False, trySceneExceptions=False):
     if not sickbeard.showList:
         return
 
@@ -936,11 +930,18 @@ def get_show(name, tryIndexers=False):
         if cache:
             fromCache = True
             showObj = findCertainShow(sickbeard.showList, int(cache))
-
+        
+        #try indexers    
         if not showObj and tryIndexers:
             showObj = findCertainShow(sickbeard.showList,
                                       searchIndexerForShowID(full_sanitizeSceneName(name), ui=classes.ShowListUI)[2])
-
+        
+        #try scene exceptions
+        if not showObj and trySceneExceptions:
+            ShowID = sickbeard.scene_exceptions.get_scene_exception_by_name(name)[0]
+            if ShowID:
+                showObj = findCertainShow(sickbeard.showList, int(ShowID))
+                
         # add show to cache
         if showObj and not fromCache:
             sickbeard.name_cache.addNameToCache(name, showObj.indexerid)
diff --git a/sickbeard/metadata/mede8er.py b/sickbeard/metadata/mede8er.py
index 64db23f7c98aad2410fb806d1d117468290272f7..f4bd9d618d2fec1613f01e58b0e528d9b0fef573 100644
--- a/sickbeard/metadata/mede8er.py
+++ b/sickbeard/metadata/mede8er.py
@@ -131,7 +131,7 @@ class Mede8erMetadata(mediabrowser.MediaBrowserMetadata):
 
         # check for title and id
         try:
-            if myShow['seriesname'] == None or myShow['seriesname'] == "" or myShow['id'] == None or myShow['id'] == "":
+            if getattr(myShow, 'seriesname', None) == None or getattr(myShow, 'seriesname', "") == "" or getattr(myShow, 'id', None) == None or getattr(myShow, 'id', "") == "":
                 logger.log(u"Incomplete info for show with id " + str(show_obj.indexerid) + " on tvdb, skipping it", logger.ERROR)
                 return False
         except sickbeard.indexer_attributenotfound:
@@ -139,24 +139,21 @@ class Mede8erMetadata(mediabrowser.MediaBrowserMetadata):
             return False
 
         SeriesName = etree.SubElement(tv_node, "title")
-        if myShow['seriesname'] != None:
-            SeriesName.text = myShow['seriesname']
-        else:
-            SeriesName.text = ""
-
+        SeriesName.text = myShow['seriesname']
+        
         Genres = etree.SubElement(tv_node, "genres")
-        if myShow["genre"] != None:
+        if getattr(myShow, "genre", None) != None:
             for genre in myShow['genre'].split('|'):
                 if genre and genre.strip():
                     cur_genre = etree.SubElement(Genres, "Genre")
                     cur_genre.text = genre.strip()
 
         FirstAired = etree.SubElement(tv_node, "premiered")
-        if myShow['firstaired'] != None:
+        if getattr(myShow, 'firstaired', None) != None:
             FirstAired.text = myShow['firstaired']
 
         year = etree.SubElement(tv_node, "year")
-        if myShow["firstaired"] != None:
+        if getattr(myShow, "firstaired", None) != None:
             try:
                 year_text = str(datetime.datetime.strptime(myShow["firstaired"], '%Y-%m-%d').year)
                 if year_text:
@@ -167,7 +164,7 @@ class Mede8erMetadata(mediabrowser.MediaBrowserMetadata):
         if getattr(myShow, 'overview', None) is not None:
             plot.text = myShow["overview"]
 
-        if myShow['rating'] != None:
+        if getattr(myShow, 'rating', None) != None:
             try:
                 rating = int((float(myShow['rating']) * 10))
             except ValueError:
@@ -178,24 +175,24 @@ class Mede8erMetadata(mediabrowser.MediaBrowserMetadata):
                 Rating.text = rating_text
 
         Status = etree.SubElement(tv_node, "status")
-        if myShow['status'] != None:
+        if getattr(myShow, 'status', None) != None:
             Status.text = myShow['status']
 
         mpaa = etree.SubElement(tv_node, "mpaa")
-        if myShow["contentrating"] != None:
+        if getattr(myShow, "contentrating", None) != None:
             mpaa.text = myShow["contentrating"]
 
         IMDB_ID = etree.SubElement(tv_node, "id")
-        if myShow['imdb_id'] != None:
+        if getattr(myShow, 'imdb_id', None) != None:
             IMDB_ID.attrib["moviedb"] = "imdb"
             IMDB_ID.text = myShow['imdb_id']
 
         indexerid = etree.SubElement(tv_node, "indexerid")
-        if myShow['id'] != None:
+        if getattr(myShow, 'id', None) != None:
             indexerid.text = myShow['id']
 
         Runtime = etree.SubElement(tv_node, "runtime")
-        if myShow['runtime'] != None:
+        if getattr(myShow, 'runtime', None) != None:
             Runtime.text = myShow['runtime']
 
         cast = etree.SubElement(tv_node, "cast")
@@ -264,10 +261,10 @@ class Mede8erMetadata(mediabrowser.MediaBrowserMetadata):
                 # root (or single) episode
 
                 # default to today's date for specials if firstaired is not set
-                if myEp['firstaired'] == None and ep_obj.season == 0:
+                if getattr(myEp, 'firstaired', None) == None and ep_obj.season == 0:
                     myEp['firstaired'] = str(datetime.date.fromordinal(1))
 
-                if myEp['episodename'] == None or myEp['firstaired'] == None:
+                if getattr(myEp, 'episodename', None) == None or getattr(myEp, 'firstaired', None) == None:
                     return None
 
                 episode = movie
@@ -285,7 +282,7 @@ class Mede8erMetadata(mediabrowser.MediaBrowserMetadata):
                 EpisodeNumber.text = str(ep_obj.episode)
 
                 year = etree.SubElement(episode, "year")
-                if myShow["firstaired"] != None:
+                if getattr(myShow, "firstaired", None) != None:
                     try:
                         year_text = str(datetime.datetime.strptime(myShow["firstaired"], '%Y-%m-%d').year)
                         if year_text:
@@ -294,7 +291,7 @@ class Mede8erMetadata(mediabrowser.MediaBrowserMetadata):
                         pass
 
                 plot = etree.SubElement(episode, "plot")
-                if myShow["overview"] != None:
+                if getattr(myShow, "overview", None) != None:
                     plot.text = myShow["overview"]
 
                 Overview = etree.SubElement(episode, "episodeplot")
@@ -319,12 +316,12 @@ class Mede8erMetadata(mediabrowser.MediaBrowserMetadata):
                             Rating.text = rating_text
 
                 director = etree.SubElement(episode, "director")
-                director_text = myEp['director']
+                director_text = getattr(myEp, 'director', None)
                 if director_text != None:
                     director.text = director_text
 
                 credits = etree.SubElement(episode, "credits")
-                credits_text = myEp['writer']
+                credits_text = getattr(myEp, 'writer', None)
                 if credits_text != None:
                     credits.text = credits_text
 
diff --git a/sickbeard/name_parser/parser.py b/sickbeard/name_parser/parser.py
index e981269af8233548495d6b35c93e380c461ac99c..b68756c55d4b8860fdeeb04bad6e0f9af6c2028b 100644
--- a/sickbeard/name_parser/parser.py
+++ b/sickbeard/name_parser/parser.py
@@ -35,12 +35,13 @@ class NameParser(object):
     NORMAL_REGEX = 1
     ANIME_REGEX = 2
 
-    def __init__(self, file_name=True, showObj=None, tryIndexers=False, convert=False,
+    def __init__(self, file_name=True, showObj=None, tryIndexers=False, trySceneExceptions=False, convert=False,
                  naming_pattern=False):
 
         self.file_name = file_name
         self.showObj = showObj
         self.tryIndexers = tryIndexers
+        self.trySceneExceptions = trySceneExceptions
         self.convert = convert
         self.naming_pattern = naming_pattern
 
@@ -191,7 +192,7 @@ class NameParser(object):
             show = None
             if not self.naming_pattern:
                 # try and create a show object for this result
-                show = helpers.get_show(bestResult.series_name, self.tryIndexers)
+                show = helpers.get_show(bestResult.series_name, self.tryIndexers, self.trySceneExceptions)
 
             # confirm passed in show object indexer id matches result show object indexer id
             if show:
diff --git a/sickbeard/notifiers/pushbullet.py b/sickbeard/notifiers/pushbullet.py
index 64c24fedf62c84c43b8943007d624a031d18de01..bb3a1f4e06edc3aecd255fd8b1ef030b67ba186b 100644
--- a/sickbeard/notifiers/pushbullet.py
+++ b/sickbeard/notifiers/pushbullet.py
@@ -53,7 +53,7 @@ class PushbulletNotifier:
         if sickbeard.USE_PUSHBULLET:
             update_text=common.notifyStrings[common.NOTIFY_GIT_UPDATE_TEXT]
             title=common.notifyStrings[common.NOTIFY_GIT_UPDATE]
-            self._sendPushbullet(pushbullet_api=None, event=title, message=update_text + new_version, method="POST")
+            self._sendPushbullet(pushbullet_api=None, event=title, message=update_text + new_version, notificationType="note", method="POST")
 
     def _sendPushbullet(self, pushbullet_api=None, pushbullet_device=None, event=None, message=None,
                         notificationType=None, method=None, force=False):
diff --git a/sickbeard/notifiers/trakt.py b/sickbeard/notifiers/trakt.py
index 2796f1f81122fecab5985367f1d1a0b99a902b0c..abc845daf4f0d285c26554a88d2eced2d151ce86 100644
--- a/sickbeard/notifiers/trakt.py
+++ b/sickbeard/notifiers/trakt.py
@@ -80,10 +80,6 @@ class TraktNotifier:
                 # update library
                 trakt_api.traktRequest("sync/collection", data, method='POST')
 
-                # remove from watchlist
-                if sickbeard.TRAKT_REMOVE_WATCHLIST:
-                    trakt_api.traktRequest("sync/watchlist/remove", data, method='POST')
-
                 if sickbeard.TRAKT_REMOVE_SERIESLIST:
                     data = {
                         'shows': [
@@ -105,6 +101,124 @@ class TraktNotifier:
             except (traktException, traktAuthException, traktServerBusy) as e:
                 logger.log(u"Could not connect to Trakt service: %s" % ex(e), logger.WARNING)
 
+    def update_watchlist (self, show_obj = None, s = None, e = None, data_show = None, data_episode = None, update = "add"):
+
+        """
+        Sends a request to trakt indicating that the given episode is part of our library.
+
+        show_obj: The TVShow object to add to trakt
+        s: season number
+        e: episode number
+        data_show: structured object of shows traktv type
+        data_episode: structured object of episodes traktv type
+        update: type o action add or remove
+        """
+
+        trakt_api = TraktAPI(sickbeard.TRAKT_API_KEY, sickbeard.TRAKT_USERNAME, sickbeard.TRAKT_PASSWORD)
+
+        if sickbeard.USE_TRAKT:
+
+            data = {}
+            try:
+                # URL parameters
+                if show_obj is not None:
+                    trakt_id = sickbeard.indexerApi(show_obj.indexer).config['trakt_id']
+                    data = {
+                        'shows': [
+                            {
+                                'title': show_obj.name,
+                                'year': show_obj.startyear,
+                                'ids': {},
+                            }
+                        ]
+                     }
+
+                    if trakt_id == 'tvdb_id':
+                        data['shows'][0]['ids']['tvdb'] = show_obj.indexerid
+                    else:
+                        data['shows'][0]['ids']['tvrage'] = show_obj.indexerid
+                elif data_show is not None:
+                    data.update(data_show)
+                else:
+                    logger.log(u"there's a coding problem contact developer. It's needed to be provided at lest one of the two: data_show or show_obj", logger.WARNING)
+                    return False
+
+                if data_episode is not None:
+                    data['shows'][0].update(data_episode)
+
+                elif s is not None:
+                    # traktv URL parameters
+                    season = {
+                        'season': [
+                            {
+                                'number': s,
+                            }
+                        ]
+                     }
+
+                    if e is not None:
+                        # traktv URL parameters
+                        episode = {
+                            'episodes': [
+                                {
+                                    'number': e
+                                }
+                            ]
+                         }
+
+                        season['season'][0].update(episode)
+                    
+                    data['shows'][0].update(season)
+
+                trakt_url = "sync/watchlist"
+                if update=="remove":
+                    trakt_url += "/remove"
+
+                trakt_api.traktRequest(trakt_url, data, method='POST')
+
+            except (traktException, traktAuthException, traktServerBusy) as e:
+                logger.log(u"Could not connect to Trakt service: %s" % ex(e), logger.WARNING)
+                return False
+
+        return True
+
+    def trakt_show_data_generate(self, data):
+
+        showList = []
+        for indexer, indexerid, title, year in data:
+            trakt_id = sickbeard.indexerApi(indexer).config['trakt_id']
+            show = {'title': title, 'year': year, 'ids': {}}
+            if trakt_id == 'tvdb_id':
+                show['ids']['tvdb'] = indexerid
+            else:
+                show['ids']['tvrage'] = indexerid
+            showList.append(show)
+
+        post_data = {'shows': showList}
+
+        return post_data
+
+    def trakt_episode_data_generate(self, data):
+
+        # Find how many unique season we have
+        uniqueSeasons = []
+        for season, episode in data:
+            if season not in uniqueSeasons:
+                uniqueSeasons.append(season)
+
+        #build the query
+        seasonsList = []
+        for searchedSeason in uniqueSeasons:
+            episodesList = []
+            for season, episode in data:
+                if season == searchedSeason:
+                    episodesList.append({'number': episode})
+            seasonsList.append({'number': searchedSeason, 'episodes': episodesList})
+
+        post_data = {'seasons': seasonsList}
+
+        return post_data
+
     def test_notify(self, username, password, disable_ssl):
         """
         Sends a test notification to trakt with the given authentication info and returns a boolean
diff --git a/sickbeard/postProcessor.py b/sickbeard/postProcessor.py
index f19f55043634ca9b089fad5a0ddd3eae54ccbe7c..056dc4c27c7194ce9469c1a767fb3304a770b38b 100644
--- a/sickbeard/postProcessor.py
+++ b/sickbeard/postProcessor.py
@@ -167,7 +167,10 @@ class PostProcessor(object):
 
         file_path_list = []
 
-        base_name = ek.ek(os.path.basename, file_path).rpartition('.')[0]
+        if subfolders:
+            base_name = ek.ek(os.path.basename, file_path).rpartition('.')[0]
+        else:
+            base_name = file_path.rpartition('.')[0]
 
         if not base_name_only:
             base_name = base_name + '.'
@@ -214,7 +217,7 @@ class PostProcessor(object):
         # figure out which files we want to delete
         file_list = [file_path]
         if associated_files:
-            file_list = file_list + self.list_associated_files(file_path)
+            file_list = file_list + self.list_associated_files(file_path, base_name_only=True, subfolders=True)
 
         if not file_list:
             self._log(u"There were no files associated with " + file_path + ", not deleting anything", logger.DEBUG)
@@ -489,7 +492,7 @@ class PostProcessor(object):
         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, tryIndexers=True, convert=True)
+        np = NameParser(file, tryIndexers=True, trySceneExceptions=True, convert=True)
         parse_result = np.parse(name)
 
         # show object
@@ -897,6 +900,7 @@ class PostProcessor(object):
 
         # update the ep info before we rename so the quality & release name go into the name properly
         sql_l = []
+        trakt_data = [] 
         for cur_ep in [ep_obj] + ep_obj.relatedEps:
             with cur_ep.lock:
 
@@ -928,6 +932,15 @@ class PostProcessor(object):
 
                 sql_l.append(cur_ep.get_sql())
 
+                trakt_data.append((cur_ep.season, cur_ep.episode))
+
+        data = notifiers.trakt_notifier.trakt_episode_data_generate(trakt_data)
+
+        if sickbeard.USE_TRAKT and sickbeard.TRAKT_SYNC_WATCHLIST and sickbeard.TRAKT_REMOVE_WATCHLIST:
+            logger.log(u"Remove episodes, showid: indexerid " + str(show.indexerid) + ", Title " + str(show.name) + " to Traktv Watchlist", logger.DEBUG)
+            if data:
+                notifiers.trakt_notifier.update_watchlist(show, data_episode=data, update="remove")
+
         if len(sql_l) > 0:
             myDB = db.DBConnection()
             myDB.mass_action(sql_l)
diff --git a/sickbeard/processTV.py b/sickbeard/processTV.py
index 7da8dc368475f0b550baaddfdfd00941de6a9390..4243b5cb8821dc17b66b18f9310605ac1523435c 100644
--- a/sickbeard/processTV.py
+++ b/sickbeard/processTV.py
@@ -19,7 +19,6 @@
 from __future__ import with_statement
 
 import os
-import shutil
 import stat
 
 import sickbeard
@@ -36,6 +35,12 @@ from sickbeard import failedProcessor
 from lib.unrar2 import RarFile, RarInfo
 from lib.unrar2.rar_exceptions import *
 
+import shutil
+import lib.shutil_custom
+
+shutil.copyfile = lib.shutil_custom.copyfile_custom
+
+
 class ProcessResult:
     def __init__(self):
        self.result = True
@@ -174,18 +179,18 @@ def processDir(dirName, nzbName=None, process_method=None, force=False, is_prior
 
     #Don't Link media when the media is extracted from a rar in the same path
     if process_method in ('hardlink', 'symlink') and videoInRar:
-        result.result = process_media(path, videoInRar, nzbName, 'move', force, is_priority, result)
+        process_media(path, videoInRar, nzbName, 'move', force, is_priority, result)
         delete_files(path, rarContent, result)
         for video in set(videoFiles) - set(videoInRar):
-            result.result = process_media(path, [video], nzbName, process_method, force, is_priority, result)
+            process_media(path, [video], nzbName, process_method, force, is_priority, result)
     elif sickbeard.DELRARCONTENTS and videoInRar:
-        result.result = process_media(path, videoInRar, nzbName, process_method, force, is_priority, result)
+        process_media(path, videoInRar, nzbName, process_method, force, is_priority, result)
         delete_files(path, rarContent, result, True)
         for video in set(videoFiles) - set(videoInRar):
-            result.result = process_media(path, [video], nzbName, process_method, force, is_priority, result)
+            process_media(path, [video], nzbName, process_method, force, is_priority, result)
     else:
         for video in videoFiles:
-            result.result = process_media(path, [video], nzbName, process_method, force, is_priority, result)
+            process_media(path, [video], nzbName, process_method, force, is_priority, result)
 
     #Process Video File in all TV Subdir
     for dir in [x for x in dirs if validateDir(path, x, nzbNameOriginal, failed, result)]:
@@ -367,7 +372,7 @@ def unRAR(path, rarFiles, force, result):
                 result.result = False
                 continue
             except NoFileToExtract:
-                result.output += logHelper(u"Failed Unrar archive (0): Unrar: No file to extract, file already exist?".format(archive), logger.ERROR)
+                result.output += logHelper(u"Failed Unrar archive {0}: Unrar: No file to extract, file already exist?".format(archive), logger.ERROR)
                 result.result = False
                 continue
             except GenericRARError:
@@ -414,7 +419,7 @@ def already_postprocessed(dirName, videofile, force, result):
         
         #Needed if we have downloaded the same episode @ different quality
         #But we need to make sure we check the history of the episode we're going to PP, and not others
-        np = NameParser(dirName, tryIndexers=True, convert=True)
+        np = NameParser(dirName, tryIndexers=True, trySceneExceptions=True, convert=True)
         try: #if it fails to find any info (because we're doing an unparsable folder (like the TV root dir) it will throw an exception, which we want to ignore
             parse_result = np.parse(dirName)
         except: #ignore the exception, because we kind of expected it, but create parse_result anyway so we can perform a check on it.
diff --git a/sickbeard/providers/__init__.py b/sickbeard/providers/__init__.py
index d8af81879bdbc31a3594973c07e0367773d34abe..17b8cd2e0c06140e1997d1ad9aa73c80d07de56a 100755
--- a/sickbeard/providers/__init__.py
+++ b/sickbeard/providers/__init__.py
@@ -44,6 +44,7 @@ __all__ = ['ezrss',
            'shazbat',
            'rarbg',
            'tntvillage',
+           'binsearch',
 ]
 
 import sickbeard
@@ -166,6 +167,7 @@ def makeTorrentRssProvider(configString):
         return None
 
     cookies = None
+    titleTAG = 'title'
     search_mode = 'eponly'
     search_fallback = 0
     enable_daily = 0
@@ -173,12 +175,14 @@ def makeTorrentRssProvider(configString):
 
     try:
         values = configString.split('|')
-        if len(values) == 8:
+        if len(values) == 9:
+            name, url, cookies, titleTAG, enabled, search_mode, search_fallback, enable_daily, enable_backlog = values
+        elif len(values) == 8:
             name, url, cookies, enabled, search_mode, search_fallback, enable_daily, enable_backlog = values
         else:
             name = values[0]
             url = values[1]
-            enabled = values[3]
+            enabled = values[4]
     except ValueError:
         logger.log(u"Skipping RSS Torrent provider string: '" + configString + "', incorrect format",
                    logger.ERROR)
@@ -189,7 +193,7 @@ def makeTorrentRssProvider(configString):
     except:
         return
 
-    newProvider = torrentRss.TorrentRssProvider(name, url, cookies, search_mode, search_fallback, enable_daily,
+    newProvider = torrentRss.TorrentRssProvider(name, url, cookies, titleTAG, search_mode, search_fallback, enable_daily,
                                                 enable_backlog)
     newProvider.enabled = enabled == '1'
 
diff --git a/sickbeard/providers/alpharatio.py b/sickbeard/providers/alpharatio.py
index be7fd45799df4fc73b6fe61145080a8cb24d793a..9493ef9c58e0c0292e05b95a6b8b8ee4489a599a 100755
--- a/sickbeard/providers/alpharatio.py
+++ b/sickbeard/providers/alpharatio.py
@@ -189,9 +189,9 @@ class AlphaRatioProvider(generic.TorrentProvider):
                             try:
                                 title = link.contents[0]
                                 download_url = self.urls['download'] % (url['href'])
-                                id = link['href'].replace('torrents.php?id=', '').split('&')[0]
-                                seeders = cells[len(cells)-2].string
-                                leechers = cells[len(cells)-1].string
+                                id = link['href'][-6:]
+                                seeders = cells[len(cells)-2].contents[0]
+                                leechers = cells[len(cells)-1].contents[0]
                             except (AttributeError, TypeError):
                                 continue
 
diff --git a/sickbeard/providers/binsearch.py b/sickbeard/providers/binsearch.py
new file mode 100644
index 0000000000000000000000000000000000000000..a0ae8995fca115469a4fa624b8168d553ca4bf2b
--- /dev/null
+++ b/sickbeard/providers/binsearch.py
@@ -0,0 +1,119 @@
+# Author: moparisthebest <admin@moparisthebest.com>
+#
+# 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 urllib
+import re
+import time
+
+import sickbeard
+
+import generic
+
+from sickbeard import logger
+from sickbeard import tvcache
+from sickbeard.exceptions import AuthException
+
+class BinSearchProvider(generic.NZBProvider):
+    def __init__(self):
+        generic.NZBProvider.__init__(self, "BinSearch")
+        self.enabled = False
+        self.cache = BinSearchCache(self)
+        self.urls = {'base_url': 'https://www.binsearch.info/'}
+        self.url = self.urls['base_url']
+
+    def isEnabled(self):
+        return self.enabled
+
+class BinSearchCache(tvcache.TVCache):
+    def __init__(self, provider):
+        tvcache.TVCache.__init__(self, provider)
+        # only poll Binsearch every 30 minutes max
+        self.minTime = 30
+
+        # compile and save our regular expressions
+
+        # this pulls the title from the URL in the description
+        self.descTitleStart = re.compile('^.*https?://www\.binsearch\.info/.b=')
+        self.descTitleEnd = re.compile('&amp;.*$')
+
+        # these clean up the horrible mess of a title if the above fail
+        self.titleCleaners = [
+            re.compile('.?yEnc.?\(\d+/\d+\)$'),
+            re.compile(' \[\d+/\d+\] '),
+        ]
+
+    def _get_title_and_url(self, item):
+        """
+        Retrieves the title and URL data from the item XML node
+
+        item: An elementtree.ElementTree element representing the <item> tag of the RSS feed
+
+        Returns: A tuple containing two strings representing title and URL respectively
+        """
+
+        title = item.get('description')
+        if title:
+            title = u'' + title
+            if self.descTitleStart.match(title):
+                title = self.descTitleStart.sub('', title)
+                title = self.descTitleEnd.sub('', title)
+                title = title.replace('+', '.')
+            else:
+                # just use the entire title, looks hard/impossible to parse
+                title = item.get('title')
+                if title:
+                    for titleCleaner in self.titleCleaners:
+                        title = titleCleaner.sub('', title)
+
+        url = item.get('link')
+        if url:
+            url = url.replace('&amp;', '&')
+
+        return (title, url)
+
+    def updateCache(self):
+        # check if we should update
+        if not self.shouldUpdate():
+            return
+
+        # clear cache
+        self._clearCache()
+
+        # set updated
+        self.setLastUpdate()
+
+        cl = []
+        for group in ['alt.binaries.boneless','alt.binaries.misc','alt.binaries.hdtv','alt.binaries.hdtv.x264','alt.binaries.tv','alt.binaries.tvseries']:
+            url = self.provider.url + 'rss.php?'
+            urlArgs = {'max': 1000,'g': group}
+
+            url += urllib.urlencode(urlArgs)
+
+            logger.log(u"BinSearch cache update URL: " + url, logger.DEBUG)
+
+            for item in self.getRSSFeed(url)['entries'] or []:
+                ci = self._parseItem(item)
+                if ci is not None:
+                    cl.append(ci)
+
+        if len(cl) > 0:
+            myDB = self._getDB()
+            myDB.mass_action(cl)
+
+    def _checkAuth(self, data):
+        return data if data['feed'] and data['feed']['title'] != 'Invalid Link' else None
+
+provider = BinSearchProvider()
diff --git a/sickbeard/providers/rsstorrent.py b/sickbeard/providers/rsstorrent.py
index d6b4b15a18ad0b4780acc127860954e94227ae0e..6df14be4f308c24f143cdc8168859da08ee8531b 100644
--- a/sickbeard/providers/rsstorrent.py
+++ b/sickbeard/providers/rsstorrent.py
@@ -33,7 +33,7 @@ from lib.bencode import bdecode
 
 
 class TorrentRssProvider(generic.TorrentProvider):
-    def __init__(self, name, url, cookies='', search_mode='eponly', search_fallback=False, enable_daily=False,
+    def __init__(self, name, url, cookies='', titleTAG='title', search_mode='eponly', search_fallback=False, enable_daily=False,
                  enable_backlog=False):
         generic.TorrentProvider.__init__(self, name)
         self.cache = TorrentRssCache(self)
@@ -51,11 +51,13 @@ class TorrentRssProvider(generic.TorrentProvider):
         self.enable_daily = enable_daily
         self.enable_backlog = enable_backlog
         self.cookies = cookies
+        self.titleTAG = titleTAG
 
     def configStr(self):
-        return "%s|%s|%s|%d|%s|%d|%d|%d" % (self.name or '',
+        return "%s|%s|%s|%s|%d|%s|%d|%d|%d" % (self.name or '',
                                             self.url or '',
                                             self.cookies or '',
+                                            self.titleTAG or '',
                                             self.enabled,
                                             self.search_mode or '',
                                             self.search_fallback,
@@ -74,7 +76,7 @@ class TorrentRssProvider(generic.TorrentProvider):
 
     def _get_title_and_url(self, item):
 
-        title = item.get('title')
+        title = item.get(self.titleTAG)
         if title:
             title = u'' + title
             title = title.replace(' ', '.')
@@ -165,4 +167,4 @@ class TorrentRssCache(tvcache.TVCache):
         if self.provider.cookies:
             self.provider.headers.update({'Cookie': self.provider.cookies})
 
-        return self.getRSSFeed(self.provider.url)
\ No newline at end of file
+        return self.getRSSFeed(self.provider.url)
diff --git a/sickbeard/search.py b/sickbeard/search.py
index c649826290af4f2c9d511b55c2be2e428ce13efe..aa6995b0673d0fa9e8e1f9d1c1cdc19e9b782d7d 100644
--- a/sickbeard/search.py
+++ b/sickbeard/search.py
@@ -149,6 +149,7 @@ def snatchEpisode(result, endStatus=SNATCHED):
 
     # don't notify when we re-download an episode
     sql_l = []
+    trakt_data = []
     for curEpObj in result.episodes:
         with curEpObj.lock:
             if isFirstBestMatch(result):
@@ -161,10 +162,22 @@ def snatchEpisode(result, endStatus=SNATCHED):
         if curEpObj.status not in Quality.DOWNLOADED:
             notifiers.notify_snatch(curEpObj._format_pattern('%SN - %Sx%0E - %EN - %QN') + " from " + result.provider.name)
 
+            trakt_data.append((curEpObj.season, curEpObj.episode))
+
+    data = notifiers.trakt_notifier.trakt_episode_data_generate(trakt_data)
+
+    if sickbeard.USE_TRAKT and sickbeard.TRAKT_SYNC_WATCHLIST:
+        logger.log(u"Add episodes, showid: indexerid " + str(result.show.indexerid) + ", Title " + str(result.show.name) + " to Traktv Watchlist", logger.DEBUG)
+        if data:
+            notifiers.trakt_notifier.update_watchlist(result.show, data_episode=data, update="add")
+
     if len(sql_l) > 0:
         myDB = db.DBConnection()
         myDB.mass_action(sql_l)
 
+    if sickbeard.UPDATE_SHOWS_ON_SNATCH and not sickbeard.showQueueScheduler.action.isBeingUpdated(result.show) and result.show.status == "Continuing":
+        sickbeard.showQueueScheduler.action.updateShow(result.show, True)
+
     return True
 
 
diff --git a/sickbeard/show_queue.py b/sickbeard/show_queue.py
index 3b36c9ae7b76d80e6123313b22589ddf94dd23af..8a26aee4a371408dc0b694693490824194c69e69 100644
--- a/sickbeard/show_queue.py
+++ b/sickbeard/show_queue.py
@@ -25,7 +25,7 @@ import sickbeard
 from lib.imdb import _exceptions as imdb_exceptions
 from sickbeard.common import SKIPPED, WANTED
 from sickbeard.tv import TVShow
-from sickbeard import exceptions, logger, ui, db
+from sickbeard import exceptions, logger, ui, db, notifiers
 from sickbeard import generic_queue
 from sickbeard import name_cache
 from sickbeard.exceptions import ex
@@ -386,6 +386,10 @@ class QueueItemAdd(ShowQueueItem):
             if sickbeard.TRAKT_SYNC:
                 sickbeard.traktCheckerScheduler.action.addShowToTraktLibrary(self.show)
 
+            if sickbeard.TRAKT_SYNC_WATCHLIST:
+                logger.log(u"update watchlist")
+                notifiers.trakt_notifier.update_watchlist(self.show)
+
         # Load XEM data to DB for show
         sickbeard.scene_numbering.xem_refresh(self.show.indexerid, self.show.indexer, force=True)
 
diff --git a/sickbeard/traktChecker.py b/sickbeard/traktChecker.py
index dfe5b5ac590d2abe2d361968a5f28b9820a45c88..59761e5449df2b1e127f41e5b6b13992112e61b0 100644
--- a/sickbeard/traktChecker.py
+++ b/sickbeard/traktChecker.py
@@ -27,34 +27,45 @@ from sickbeard.exceptions import ex
 from sickbeard import logger
 from sickbeard import helpers
 from sickbeard import search_queue
-from sickbeard.common import SKIPPED, WANTED
-
+from sickbeard import db
+from sickbeard import notifiers
+from sickbeard.common import SNATCHED, SNATCHED_PROPER, DOWNLOADED, SKIPPED, UNAIRED, IGNORED, ARCHIVED, WANTED, UNKNOWN, FAILED
+from common import Quality, qualityPresetStrings, statusStrings
 from lib.trakt import *
 from trakt.exceptions import traktException, traktAuthException, traktServerBusy
 
-
 class TraktChecker():
 
     def __init__(self):
         self.todoWanted = []
         self.trakt_api = TraktAPI(sickbeard.TRAKT_API_KEY, sickbeard.TRAKT_USERNAME, sickbeard.TRAKT_PASSWORD, sickbeard.TRAKT_DISABLE_SSL_VERIFY, sickbeard.TRAKT_TIMEOUT)
+        self.todoBacklog = []
+        self.ShowWatchlist = []
+        self.EpisodeWatchlist = []
 
     def run(self, force=False):
-        try:
-            # add shows from trakt.tv watchlist
-            if sickbeard.TRAKT_USE_WATCHLIST:
-                self.todoWanted = []  # its about to all get re-added
-                if len(sickbeard.ROOT_DIRS.split('|')) < 2:
-                    logger.log(u"No default root directory", logger.ERROR)
-                    return
-                self.updateShows()
-                self.updateEpisodes()
+        if not sickbeard.USE_TRAKT:
+            logger.log(u"Trakt integrazione disabled, quit", logger.DEBUG)
+            return
+
+        # add shows from trakt.tv watchlist
+        if sickbeard.TRAKT_SYNC_WATCHLIST:
+            self.todoWanted = []  # its about to all get re-added
+            if len(sickbeard.ROOT_DIRS.split('|')) < 2:
+                logger.log(u"No default root directory", logger.ERROR)
+                return
+
+            try:
+                self.syncWatchlist()
+            except Exception:
+                logger.log(traceback.format_exc(), logger.DEBUG)
 
-            # sync trakt.tv library with sickrage library
-            if sickbeard.TRAKT_SYNC:
-                self.syncLibrary()
-        except Exception as e:
-            logger.log('Trakt: Error Syncing library. Reason: {0}'.format(str(e)), logger.DEBUG)
+            try:
+                # sync trakt.tv library with sickrage library
+                if sickbeard.TRAKT_SYNC:
+                    self.syncLibrary()
+            except Exception:
+                logger.log(traceback.format_exc(), logger.DEBUG) 
 
     def findShow(self, indexer, indexerid):
         traktShow = None
@@ -143,20 +154,104 @@ class TraktChecker():
                 logger.log(u"Could not connect to Trakt service: %s" % ex(e), logger.WARNING)
                 return
 
+    def syncWatchlist(self):
+
+        logger.log(u"Syncing Trakt.tv show watchlist", logger.DEBUG)
+
+        logger.log(u"Getting ShowWatchlist", logger.DEBUG)
+        if self._getShowWatchlist():
+            self.addShowToTraktWatchList()
+            self.updateShows()
+
+        logger.log(u"Getting EpisodeWatchlist", logger.DEBUG)
+        if self._getEpisodeWatchlist():
+            self.removeEpisodeFromTraktWatchList()
+            self.addEpisodeToTraktWatchList()
+            self.updateEpisodes()
+
+    def removeEpisodeFromTraktWatchList(self):
+
+        logger.log(u"Start looking if some episode has to be removed from watchlist", logger.DEBUG)
+
+        if not len(self.EpisodeWatchlist):
+            logger.log(u"No episode found in your watchlist, aborting watchlist update", logger.DEBUG)
+            return True
+
+        trakt_data = []
+        for episode in self.EpisodeWatchlist:
+            tvdb_id = int(episode['show']['ids']['tvdb'])
+            tvrage_id = int(episode['show']['ids']['tvrage'] or 0)
+            newShow = helpers.findCertainShow(sickbeard.showList, [tvdb_id, tvrage_id])
+            if newShow is not None:
+                ep_obj = newShow.getEpisode(int(episode['episode']['season']), int(episode['episode']['number']))
+                if ep_obj is not None:
+                    if ep_obj.status != WANTED and ep_obj.status != UNKNOWN and ep_obj.status not in Quality.SNATCHED and ep_obj.status not in Quality.SNATCHED_PROPER:
+                        logger.log(u"Removing episode: Indexer " + str(newShow.indexer) + ", indexer_id " + str(newShow.indexerid) + ", Title " + str(newShow.name) + ", Season " + str(episode['episode']['season']) + ", Episode " + str(episode['episode']['number']) + ", Status " + str(ep_obj.status) + " from Watchlist", logger.DEBUG)
+                        trakt_data.append((ep_obj.season, ep_obj.episode))
+                else:
+                    logger.log(u"Episode: Indexer " + str(newShow.indexer) + ", indexer_id " + str(newShow.indexerid) + ", Title " + str(newShow.name) + ", Season " + str(episode['episode']["season"]) + ", Episode" + str(episode['episode']["number"]) + " not in Sickberad ShowList", logger.DEBUG)
+                    continue
+            else:
+                logger.log(u"Show: tvdb_id " + str(episode['show']['ids']['tvdb']) + ", Title " + str(episode['show']['title']) + " not in Sickberad ShowList", logger.DEBUG)
+                continue
+
+        if len(trakt_data):
+            data = notifiers.trakt_notifier.trakt_episode_data_generate(trakt_data)
+            notifiers.trakt_notifier.update_watchlist(newShow, data_episode=data, update="remove")
+            self._getEpisodeWatchlist()
+
+        logger.log(u"Stop looking if some episode has to be removed from watchlist", logger.DEBUG)
+
+    def addEpisodeToTraktWatchList(self):
+
+        if sickbeard.TRAKT_SYNC_WATCHLIST and sickbeard.USE_TRAKT:
+            logger.log(u"Start looking if some WANTED episode need to be added to watchlist", logger.DEBUG)
+
+            myDB = db.DBConnection()
+            sql_selection='select tv_shows.indexer, showid, show_name, season, episode from tv_episodes,tv_shows where tv_shows.indexer_id = tv_episodes.showid and tv_episodes.status in ('+','.join([str(x) for x in Quality.SNATCHED + Quality.SNATCHED_PROPER + [WANTED]])+')'
+            episodes = myDB.select(sql_selection)
+            if episodes is not None:
+                trakt_data = []
+                for cur_episode in episodes:
+                    newShow = helpers.findCertainShow(sickbeard.showList, int(cur_episode["showid"])) 
+                    if not self.check_watchlist(newShow, cur_episode["season"], cur_episode["episode"]):
+                        logger.log(u"Episode: Indexer " + str(cur_episode["indexer"]) + ", indexer_id " + str(cur_episode["showid"])+ ", Title " +  str(cur_episode["show_name"]) + " " + str(cur_episode["season"]) + "x" + str(cur_episode["episode"]) + " should be added to watchlist", logger.DEBUG)
+                        trakt_data.append((cur_episode["season"], cur_episode["episode"]))
+
+                if len(trakt_data):
+                    data = notifiers.trakt_notifier.trakt_episode_data_generate(trakt_data)
+                    notifiers.trakt_notifier.update_watchlist(newShow, data_episode=data)
+                    self._getEpisodeWatchlist()
+
+            logger.log(u"Stop looking if some WANTED episode need to be added to watchlist", logger.DEBUG)
+
+    def addShowToTraktWatchList(self):
+
+        if sickbeard.TRAKT_SYNC_WATCHLIST and sickbeard.USE_TRAKT:
+            logger.log(u"Start looking if some show need to be added to watchlist", logger.DEBUG)
+
+            if sickbeard.showList is not None:
+                trakt_data = []
+                for show in sickbeard.showList:
+                    if not self.check_watchlist(show):
+                        logger.log(u"Show: Indexer " + str(show.indexer) + ", indexer_id " + str(show.indexerid) + ", Title " +  str(show.name) + " should be added to watchlist", logger.DEBUG)
+                        trakt_data.append((show.indexer, show.indexerid, show.name, show.startyear))
+
+                if len(trakt_data):
+                    data = notifiers.trakt_notifier.trakt_show_data_generate(trakt_data)
+                    notifiers.trakt_notifier.update_watchlist(data_show=data)
+                    self._getShowWatchlist()
+
+            logger.log(u"Stop looking if some show need to be added to watchlist", logger.DEBUG)
+
     def updateShows(self):
         logger.log(u"Starting trakt show watchlist check", logger.DEBUG)
 
-        try:
-            watchlist = self.trakt_api.traktRequest("sync/watchlist/shows")
-        except (traktException, traktAuthException, traktServerBusy) as e:
-            logger.log(u"Could not connect to Trakt service: %s" % ex(e), logger.WARNING)
-            return
-
-        if not len(watchlist):
+        if not len(self.ShowWatchlist):
             logger.log(u"No shows found in your watchlist, aborting watchlist update", logger.DEBUG)
             return
 
-        for show in watchlist:
+        for show in self.ShowWatchlist:
             indexer = int(sickbeard.TRAKT_DEFAULT_INDEXER)
             if indexer == 2:
                 indexer_id = int(show["show"]["ids"]["tvrage"])
@@ -181,33 +276,28 @@ class TraktChecker():
         """
         logger.log(u"Starting trakt episode watchlist check", logger.DEBUG)
 
-        try:
-            watchlist = self.trakt_api.traktRequest("sync/watchlist/episodes")
-        except (traktException, traktAuthException, traktServerBusy) as e:
-            logger.log(u"Could not connect to Trakt service: %s" % ex(e), logger.WARNING)
-            return
-
-        if not len(watchlist):
-            logger.log(u"No shows found in your watchlist, aborting watchlist update", logger.DEBUG)
+        if not len(self.EpisodeWatchlist):
+            logger.log(u"No episode found in your watchlist, aborting episode update", logger.DEBUG)
             return
 
-        for show in watchlist:
+        managed_show = []
+        for show in self.EpisodeWatchlist:
             indexer = int(sickbeard.TRAKT_DEFAULT_INDEXER)
             if indexer == 2:
                 indexer_id = int(show["show"]["ids"]["tvrage"])
             else:
                 indexer_id = int(show["show"]["ids"]["tvdb"])
 
-            self.addDefaultShow(indexer, indexer_id, show["show"]["title"], SKIPPED)
             newShow = helpers.findCertainShow(sickbeard.showList, indexer_id)
-
             try:
-                if newShow and newShow.indexer == indexer:
-                    for episode in show["episode"]:
-                        if newShow is not None:
-                            self.setEpisodeToWanted(newShow, episode["season"], episode["number"])
-                        else:
-                            self.todoWanted.append((indexer_id, episode["season"], episode["number"]))
+                if newShow is None:
+                    if indexer_id not in managed_show:
+                        self.addDefaultShow(indexer, indexer_id, show["show"]["title"], SKIPPED)
+                        managed_show.append(indexer_id)
+                    self.todoWanted.append((indexer_id, show['episode']['season'], show['episode']['number']))
+                else:
+                    if newShow.indexer == indexer:
+                        self.setEpisodeToWanted(newShow, show['episode']['season'], show['episode']['number'])
             except TypeError:
                 logger.log(u"Could not parse the output from trakt for " + show["show"]["title"], logger.DEBUG)
 
@@ -270,3 +360,48 @@ class TraktChecker():
         for episode in episodes:
             self.todoWanted.remove(episode)
             self.setEpisodeToWanted(show, episode[1], episode[2])
+
+    def check_watchlist (self, show_obj, season=None, episode=None):
+
+        found = False
+        if episode is not None:
+            watchlist = self.EpisodeWatchlist
+        else:
+            watchlist = self.ShowWatchlist
+
+        for watchlist_el in watchlist:
+
+            trakt_id = sickbeard.indexerApi(show_obj.indexer).config['trakt_id']
+            if trakt_id == 'tvdb_id':
+                indexer_id = int(watchlist_el['show']['ids']["tvdb"])
+            else:
+                indexer_id = int(watchlist_el['show']['ids']["tvrage"])
+
+            if indexer_id == show_obj.indexerid and season is None and episode is None:
+                found=True
+                break
+            elif indexer_id == show_obj.indexerid and season == watchlist_el['episode']["season"] and episode == watchlist_el['episode']["number"]:
+                found=True
+                break
+
+        return found
+
+    def _getShowWatchlist(self):
+
+        try:
+            self.ShowWatchlist = self.trakt_api.traktRequest("sync/watchlist/shows")
+        except (traktException, traktAuthException, traktServerBusy) as e:
+            logger.log(u"Could not connect to trakt service, cannot download Show Watchlist: %s" % ex(e), logger.ERROR)
+            return False
+
+        return True
+
+    def _getEpisodeWatchlist(self):
+
+        try:
+            self.EpisodeWatchlist = self.trakt_api.traktRequest("sync/watchlist/episodes")
+        except (traktException, traktAuthException, traktServerBusy) as e:
+            logger.log(u"Could not connect to trakt service, cannot download Episode Watchlist: %s" % ex(e), logger.WARNING)
+            return False
+
+        return True
diff --git a/sickbeard/tv.py b/sickbeard/tv.py
index 9ba4cc00f0eb6fcddb9b217b076fd00497e2cd90..a7fef0de60bbb6afda2823720eeae2c1cc25697d 100644
--- a/sickbeard/tv.py
+++ b/sickbeard/tv.py
@@ -25,7 +25,6 @@ import re
 import glob
 import stat
 import traceback
-import shutil
 
 import sickbeard
 
@@ -62,6 +61,11 @@ from common import DOWNLOADED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, ARCHIVE
 from common import NAMING_DUPLICATE, NAMING_EXTEND, NAMING_LIMITED_EXTEND, NAMING_SEPARATED_REPEAT, \
     NAMING_LIMITED_EXTEND_E_PREFIXED
 
+import shutil
+import lib.shutil_custom
+
+shutil.copyfile = lib.shutil_custom.copyfile_custom
+
 
 def dirty_setter(attr_name):
     def wrapper(self, val):
@@ -1031,6 +1035,10 @@ class TVShow(object):
             except OSError, e:
                 logger.log(u'Unable to %s %s: %s / %s' % (action, self._location, repr(e), str(e)), logger.WARNING)
 
+        if sickbeard.USE_TRAKT and sickbeard.TRAKT_SYNC_WATCHLIST:
+            logger.log(u"Removing show: indexerid " + str(self.indexerid) + ", Title " + str(self.name) + " from Watchlist", logger.DEBUG)
+            notifiers.trakt_notifier.update_watchlist(self, update="remove")
+
     def populateCache(self):
         cache_inst = image_cache.ImageCache()
 
@@ -1269,8 +1277,10 @@ class TVShow(object):
             anyQualities, bestQualities = Quality.splitQuality(self.quality)  # @UnusedVariable
             if bestQualities:
                 maxBestQuality = max(bestQualities)
+                minBestQuality = min(bestQualities)
             else:
                 maxBestQuality = None
+                minBestQuality = None
 
             epStatus, curQuality = Quality.splitCompositeStatus(epStatus)
 
@@ -1284,6 +1294,12 @@ class TVShow(object):
                 return Overview.SNATCHED
             elif maxBestQuality == None:
                 return Overview.GOOD
+            # if the want only first match and already have one call it good
+            elif self.archive_firstmatch and curQuality in bestQualities:
+                return Overview.GOOD
+            # if they want only first match and current quality is higher than minimal best quality call it good
+            elif self.archive_firstmatch and minBestQuality != None and curQuality > minBestQuality:
+                return Overview.GOOD
             # if they have one but it's not the best they want then mark it as qual
             elif curQuality < maxBestQuality:
                 return Overview.QUAL
diff --git a/sickbeard/versionChecker.py b/sickbeard/versionChecker.py
index a1061049199f2cc755107e46b85aeb2bfb7aabdd..3631d310be9f471e0bea5fa97856c696268803d6 100644
--- a/sickbeard/versionChecker.py
+++ b/sickbeard/versionChecker.py
@@ -18,7 +18,6 @@
 
 import os
 import platform
-import shutil
 import subprocess
 import re
 import urllib
@@ -33,6 +32,12 @@ from sickbeard import logger
 from sickbeard.exceptions import ex
 from sickbeard import encodingKludge as ek
 
+import shutil
+import lib.shutil_custom
+
+shutil.copyfile = lib.shutil_custom.copyfile_custom
+
+
 class CheckVersion():
     """
     Version check class meant to run as a thread object with the sr scheduler.
diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py
index 88b2c9e62c8ab03c095db8e9ffc85943030dd13b..7faf558189a37bb0bb56cec9a5eb94da87a97768 100644
--- a/sickbeard/webserve.py
+++ b/sickbeard/webserve.py
@@ -42,7 +42,7 @@ from sickbeard import network_timezones
 from sickbeard import sbdatetime
 from sickbeard.providers import newznab, rsstorrent
 from sickbeard.common import Quality, Overview, statusStrings, qualityPresetStrings, cpu_presets
-from sickbeard.common import SNATCHED, UNAIRED, IGNORED, ARCHIVED, WANTED, FAILED
+from sickbeard.common import SNATCHED, UNAIRED, IGNORED, ARCHIVED, WANTED, FAILED, SKIPPED
 from sickbeard.common import SD, HD720p, HD1080p
 from sickbeard.exceptions import ex
 from sickbeard.blackandwhitelist import BlackAndWhiteList
@@ -1159,7 +1159,7 @@ class Home(WebRoot):
         if not branchDest:
             return json.dumps({ "status": "error", 'message': 'branchDest empty' })
         try:
-            response = requests.get("https://raw.githubusercontent.com/SICKRAGETV/SickRage/" + str(branchDest) +"/sickbeard/databases/mainDB.py")
+            response = requests.get("https://raw.githubusercontent.com/SICKRAGETV/SickRage/" + str(branchDest) +"/sickbeard/databases/mainDB.py", verify=False)
             response.raise_for_status()
             match = re.search(r"MAX_DB_VERSION\s=\s(?P<version>\d{2,3})",response.text)
             branchDestDBversion = int(match.group('version'))
@@ -1724,6 +1724,7 @@ class Home(WebRoot):
                 return self._genericMessage("Error", errMsg)
 
         segments = {}
+        trakt_data = []
         if eps is not None:
 
             sql_l = []
@@ -1771,6 +1772,21 @@ class Home(WebRoot):
                     # mass add to database
                     sql_l.append(epObj.get_sql())
 
+                    trakt_data.append((epObj.season, epObj.episode))
+
+            data = notifiers.trakt_notifier.trakt_episode_data_generate(trakt_data)
+
+            if sickbeard.USE_TRAKT and sickbeard.TRAKT_SYNC_WATCHLIST:
+                if int(status) in [WANTED, FAILED]:
+                    logger.log(u"Add episodes, showid: indexerid " + str(showObj.indexerid) + ", Title " + str(showObj.name) + " to Watchlist", logger.DEBUG)
+                    upd = "add"
+                elif int(status) in [ARCHIVED, IGNORED, SKIPPED ] + Quality.DOWNLOADED:
+                    logger.log(u"Remove episodes, showid: indexerid " + str(showObj.indexerid) + ", Title " + str(showObj.name) + " from Watchlist", logger.DEBUG)
+                    upd = "remove"
+
+                if data:
+                    notifiers.trakt_notifier.update_watchlist(showObj, data_episode=data, update=upd)
+
             if len(sql_l) > 0:
                 myDB = db.DBConnection()
                 myDB.mass_action(sql_l)
@@ -3610,7 +3626,7 @@ class ConfigGeneral(Config):
         sickbeard.save_config()
 
     def saveGeneral(self, log_dir=None, log_nr = 5, log_size = 1048576, web_port=None, web_log=None, encryption_version=None, web_ipv6=None,
-                    update_shows_on_start=None, trash_remove_show=None, trash_rotate_logs=None, update_frequency=None,
+                    update_shows_on_start=None, update_shows_on_snatch=None, trash_remove_show=None, trash_rotate_logs=None, update_frequency=None,
                     launch_browser=None, showupdate_hour=3, web_username=None,
                     api_key=None, indexer_default=None, timezone_display=None, cpu_preset=None,
                     web_password=None, version_notify=None, enable_https=None, https_cert=None, https_key=None,
@@ -3635,6 +3651,7 @@ class ConfigGeneral(Config):
         sickbeard.LOG_NR = log_nr
         sickbeard.LOG_SIZE = log_size
         sickbeard.UPDATE_SHOWS_ON_START = config.checkbox_to_value(update_shows_on_start)
+        sickbeard.UPDATE_SHOWS_ON_SNATCH = config.checkbox_to_value(update_shows_on_snatch)
         sickbeard.TRASH_REMOVE_SHOW = config.checkbox_to_value(trash_remove_show)
         sickbeard.TRASH_ROTATE_LOGS = config.checkbox_to_value(trash_rotate_logs)
         config.change_UPDATE_FREQUENCY(update_frequency)
@@ -4172,7 +4189,7 @@ class ConfigProviders(Config):
         return '1'
 
 
-    def canAddTorrentRssProvider(self, name, url, cookies):
+    def canAddTorrentRssProvider(self, name, url, cookies, titleTAG):
 
         if not name:
             return json.dumps({'error': 'Invalid name specified'})
@@ -4180,7 +4197,7 @@ class ConfigProviders(Config):
         providerDict = dict(
             zip([x.getID() for x in sickbeard.torrentRssProviderList], sickbeard.torrentRssProviderList))
 
-        tempProvider = rsstorrent.TorrentRssProvider(name, url, cookies)
+        tempProvider = rsstorrent.TorrentRssProvider(name, url, cookies, titleTAG)
 
         if tempProvider.getID() in providerDict:
             return json.dumps({'error': 'Exists as ' + providerDict[tempProvider.getID()].name})
@@ -4192,7 +4209,7 @@ class ConfigProviders(Config):
                 return json.dumps({'error': errMsg})
 
 
-    def saveTorrentRssProvider(self, name, url, cookies):
+    def saveTorrentRssProvider(self, name, url, cookies, titleTAG):
 
         if not name or not url:
             return '0'
@@ -4203,11 +4220,12 @@ class ConfigProviders(Config):
             providerDict[name].name = name
             providerDict[name].url = config.clean_url(url)
             providerDict[name].cookies = cookies
+            providerDict[name].titleTAG = titleTAG
 
             return providerDict[name].getID() + '|' + providerDict[name].configStr()
 
         else:
-            newProvider = rsstorrent.TorrentRssProvider(name, url, cookies)
+            newProvider = rsstorrent.TorrentRssProvider(name, url, cookies, titleTAG)
             sickbeard.torrentRssProviderList.append(newProvider)
             return newProvider.getID() + '|' + newProvider.configStr()
 
@@ -4309,10 +4327,10 @@ class ConfigProviders(Config):
                 if not curTorrentRssProviderStr:
                     continue
 
-                curName, curURL, curCookies = curTorrentRssProviderStr.split('|')
+                curName, curURL, curCookies, curTitleTAG = curTorrentRssProviderStr.split('|')
                 curURL = config.clean_url(curURL)
 
-                newProvider = rsstorrent.TorrentRssProvider(curName, curURL, curCookies)
+                newProvider = rsstorrent.TorrentRssProvider(curName, curURL, curCookies, curTitleTAG)
 
                 curID = newProvider.getID()
 
@@ -4321,6 +4339,7 @@ class ConfigProviders(Config):
                     torrentRssProviderDict[curID].name = curName
                     torrentRssProviderDict[curID].url = curURL
                     torrentRssProviderDict[curID].cookies = curCookies
+                    torrentRssProviderDict[curID].curTitleTAG = curTitleTAG
                 else:
                     sickbeard.torrentRssProviderList.append(newProvider)
 
@@ -4568,7 +4587,7 @@ class ConfigNotifications(Config):
                           use_nmj=None, nmj_host=None, nmj_database=None, nmj_mount=None, use_synoindex=None,
                           use_nmjv2=None, nmjv2_host=None, nmjv2_dbloc=None, nmjv2_database=None,
                           use_trakt=None, trakt_username=None, trakt_password=None,
-                          trakt_remove_watchlist=None, trakt_use_watchlist=None, trakt_method_add=None,
+                          trakt_remove_watchlist=None, trakt_sync_watchlist=None, trakt_method_add=None,
                           trakt_start_paused=None, trakt_use_recommended=None, trakt_sync=None,
                           trakt_default_indexer=None, trakt_remove_serieslist=None, trakt_disable_ssl_verify=None, trakt_timeout=None,
                           use_synologynotifier=None, synologynotifier_notify_onsnatch=None,
@@ -4686,7 +4705,7 @@ class ConfigNotifications(Config):
         sickbeard.TRAKT_PASSWORD = trakt_password
         sickbeard.TRAKT_REMOVE_WATCHLIST = config.checkbox_to_value(trakt_remove_watchlist)
         sickbeard.TRAKT_REMOVE_SERIESLIST = config.checkbox_to_value(trakt_remove_serieslist)
-        sickbeard.TRAKT_USE_WATCHLIST = config.checkbox_to_value(trakt_use_watchlist)
+        sickbeard.TRAKT_SYNC_WATCHLIST = config.checkbox_to_value(trakt_sync_watchlist)
         sickbeard.TRAKT_METHOD_ADD = int(trakt_method_add)
         sickbeard.TRAKT_START_PAUSED = config.checkbox_to_value(trakt_start_paused)
         sickbeard.TRAKT_USE_RECOMMENDED = config.checkbox_to_value(trakt_use_recommended)
@@ -4878,7 +4897,7 @@ class ErrorLogs(WebRoot):
         return t.respond()
 
     def haveErrors(self):
-        if len(classes.ErrorViewer.errors) > 0 and sickbeard.GIT_USERNAME and sickbeard.GIT_PASSWORD and sickbeard.GIT_AUTOISSUES == 1:
+        if len(classes.ErrorViewer.errors) > 0:
             return True
 
     def clearerrors(self):
@@ -4886,7 +4905,50 @@ class ErrorLogs(WebRoot):
         return self.redirect("/errorlogs/")
 
     def viewlog(self, minLevel=logger.INFO, logFilter="<NONE>",logSearch=None, maxLines=500):
+        
+        def Get_Data(Levelmin, data_in, lines_in, regex, Filter, Search, mlines):
+            
+            lastLine = False
+            numLines = lines_in
+            numToShow = min(maxLines, numLines + len(data_in))
+            
+            finalData = [] 
+            
+            for x in reversed(data_in):
+
+                x = ek.ss(x)
+                match = re.match(regex, x)
+
+                if match:
+                    level = match.group(7)
+                    logName = match.group(8)
+                    if level not in logger.reverseNames:
+                        lastLine = False
+                        continue
 
+                    if logSearch and logSearch.lower() in x.lower():
+                        lastLine = True
+                        finalData.append(x)
+                        numLines += 1
+                    elif not logSearch and logger.reverseNames[level] >= minLevel and (logFilter == '<NONE>' or logName.startswith(logFilter)):
+                        lastLine = True
+                        finalData.append(x)
+                        numLines += 1
+                    else:
+                        lastLine = False
+                        continue
+
+                elif lastLine:
+                    finalData.append("AA" + x)
+                    numLines += 1
+
+                
+
+                if numLines >= numToShow:
+                    return finalData
+                
+            return finalData
+            
         t = PageTemplate(rh=self, file="viewlogs.tmpl")
         t.submenu = self.ErrorLogsMenu()
 
@@ -4913,51 +4975,24 @@ class ErrorLogs(WebRoot):
         if logFilter not in logNameFilters:
             logFilter = '<NONE>'
 
-
+        regex = "^(\d\d\d\d)\-(\d\d)\-(\d\d)\s*(\d\d)\:(\d\d):(\d\d)\s*([A-Z]+)\s*(.+?)\s*\:\:\s*(.*)$"
+        
         data = []
+        
         if os.path.isfile(logger.logFile):
             with ek.ek(codecs.open, *[logger.logFile, 'r', 'utf-8']) as f:
-                data = f.readlines()
-
-        regex = "^(\d\d\d\d)\-(\d\d)\-(\d\d)\s*(\d\d)\:(\d\d):(\d\d)\s*([A-Z]+)\s*(.+?)\s*\:\:\s*(.*)$"
-
-        finalData = []
+                data = Get_Data(minLevel, f.readlines(), 0, regex, logFilter, logSearch, maxLines)
+                
+        for i in range (1 , int(sickbeard.LOG_NR)):
+            if os.path.isfile(logger.logFile + "." + str(i)) and (len(data) <= maxLines):
+                with ek.ek(codecs.open, *[logger.logFile + "." + str(i), 'r', 'utf-8']) as f:
+                        data += Get_Data(minLevel, f.readlines(), len(data), regex, logFilter, logSearch, maxLines)
 
-        numLines = 0
-        lastLine = False
-        numToShow = min(maxLines, len(data))
-
-        for x in reversed(data):
-
-            x = ek.ss(x)
-            match = re.match(regex, x)
-
-            if match:
-                level = match.group(7)
-                logName = match.group(8)
-                if level not in logger.reverseNames:
-                    lastLine = False
-                    continue
-
-                if logSearch and logSearch.lower() in x.lower():
-                    lastLine = True
-                    finalData.append(x)
-                elif not logSearch and logger.reverseNames[level] >= minLevel and (logFilter == '<NONE>' or logName.startswith(logFilter)):
-                    lastLine = True
-                    finalData.append(x)
-                else:
-                    lastLine = False
-                    continue
-
-            elif lastLine:
-                finalData.append("AA" + x)
-
-            numLines += 1
+        
 
-            if numLines >= numToShow:
-                break
+               
 
-        result = "".join(finalData)
+        result = "".join(data)
 
         t.logLines = result
         t.minLevel = minLevel
@@ -4969,6 +5004,7 @@ class ErrorLogs(WebRoot):
 
     def submit_errors(self):
         if not (sickbeard.GIT_USERNAME and sickbeard.GIT_PASSWORD):
+            ui.notifications.error("Missing information", "Please set your GitHub username and password in the config.")
             logger.log(u'Please set your GitHub username and password in the config, unable to submit issue ticket to GitHub!')
         else:
             issue = logger.submit_errors()
diff --git a/tests/test_lib.py b/tests/test_lib.py
index 7f152882d1ab7f4c655124a9764ea849ae43f1c2..65c6e406a692099a2057c7aa935debbf1925a135 100644
--- a/tests/test_lib.py
+++ b/tests/test_lib.py
@@ -31,7 +31,6 @@ sys.path.append(os.path.abspath('..'))
 sys.path.append(os.path.abspath('../lib'))
 
 import sickbeard
-import shutil
 
 from sickbeard import providers, tvcache
 from sickbeard import db
@@ -39,6 +38,11 @@ from sickbeard.databases import mainDB
 from sickbeard.databases import cache_db, failed_db
 from sickbeard.tv import TVEpisode
 
+import shutil
+import lib.shutil_custom
+
+shutil.copyfile = lib.shutil_custom.copyfile_custom
+
 #=================
 # test globals
 #=================
diff --git a/updater.py b/updater.py
index 3ce3d6247aa3bf8564799ad431ea0b611affc8e0..7d3ae8b89dce3599b1b27f6cd007677dac1334f9 100644
--- a/updater.py
+++ b/updater.py
@@ -1,4 +1,9 @@
-import subprocess, os, time, sys, os.path, shutil, re
+import subprocess, os, time, sys, os.path, re
+
+import shutil
+import lib.shutil_custom
+
+shutil.copyfile = lib.shutil_custom.copyfile_custom
 
 try:
     log_file = open('sb-update.log', 'w')