From 27acad72880dec1fe69aaa1c23c56a8fd9921747 Mon Sep 17 00:00:00 2001
From: sarak_000 <sarak_000@HTPC>
Date: Wed, 7 Aug 2013 22:19:16 +0200
Subject: [PATCH] implementation of french scheduler for already existing
 english eps

---
 data/interfaces/default/config_search.tmpl | 12 +++-
 data/interfaces/default/displayShow.tmpl   |  8 ++-
 data/interfaces/default/editShow.tmpl      |  1 +
 data/interfaces/default/history.tmpl       |  4 ++
 data/interfaces/default/home.tmpl          | 16 +++--
 data/interfaces/default/inc_top.tmpl       |  1 +
 sickbeard/__init__.py                      | 34 ++++++++--
 sickbeard/common.py                        | 10 ++-
 sickbeard/databases/mainDB.py              | 11 ++-
 sickbeard/frenchFinder.py                  | 79 ++++++++++++++++++++++
 sickbeard/postProcessor.py                 |  4 +-
 sickbeard/providers/binnewz/__init__.py    | 11 +--
 sickbeard/providers/cpasbien.py            | 10 +--
 sickbeard/providers/generic.py             | 66 +++++++++++++++++-
 sickbeard/providers/gks.py                 | 14 ++--
 sickbeard/providers/kat.py                 | 13 ++--
 sickbeard/providers/newznab.py             | 12 ++--
 sickbeard/providers/piratebay/__init__.py  |  4 +-
 sickbeard/providers/t411.py                | 30 ++++----
 sickbeard/scheduler.py                     |  5 +-
 sickbeard/search_queue.py                  |  2 +-
 sickbeard/show_queue.py                    | 24 +++++++
 sickbeard/subtitles.py                     |  2 +-
 sickbeard/traktWatchListChecker.py         |  2 +-
 sickbeard/tv.py                            | 27 +++++---
 sickbeard/webapi.py                        | 10 +--
 sickbeard/webserve.py                      | 45 ++++++++++--
 27 files changed, 369 insertions(+), 88 deletions(-)
 create mode 100644 sickbeard/frenchFinder.py

diff --git a/data/interfaces/default/config_search.tmpl b/data/interfaces/default/config_search.tmpl
index b4c1d816a..1b63b585d 100644
--- a/data/interfaces/default/config_search.tmpl
+++ b/data/interfaces/default/config_search.tmpl
@@ -42,10 +42,18 @@
                             <input type="checkbox" name="download_propers" id="download_propers" #if $sickbeard.DOWNLOAD_PROPERS == True then "checked=\"checked\"" else ""# />
                             <label class="clearfix" for="download_propers">
                                 <span class="component-title">Download Propers</span>
-                                <span class="component-desc">Replace original download with "Proper/Repack" if nuked?</span>
+                                <span class="component-desc">Replace original download with "Proper/Repack" if nuked? Needs restart</span>
                             </label>
                         </div>
-
+						
+						<div class="field-pair">
+                            <input type="checkbox" name="download_french" id="download_french" #if $sickbeard.DOWNLOAD_FRENCH == True then "checked=\"checked\"" else ""# />
+                            <label class="clearfix" for="download_french">
+                                <span class="component-title">Auto download French</span>
+                                <span class="component-desc">Replace non french downloads with French ones if they exist even if audio is English (for the show where option is activated, will check every weeks). Needs restart</span>
+                            </label>
+                        </div>
+						
                         <div class="field-pair">
                             <label class="nocheck clearfix">
                                 <span class="component-title">Search Frequency</span>
diff --git a/data/interfaces/default/displayShow.tmpl b/data/interfaces/default/displayShow.tmpl
index c139eaee4..5a83780ba 100644
--- a/data/interfaces/default/displayShow.tmpl
+++ b/data/interfaces/default/displayShow.tmpl
@@ -195,8 +195,12 @@
 #end if
 
 	<tr><td class="showLegend">Info Language:</td><td><img src="$sbRoot/images/flags/${show.lang}.png" width="16" height="11" alt="$show.lang" title="$show.lang" /></td></tr>
+    #if int($show.frenchsearch) == 1
+    <tr><td class="showLegend">Audio Language:</td><td><img src="$sbRoot/images/flags/${show.audio_lang}.png" width="16" height="11" alt="$show.audio_lang" title="$show.lang" />  <img src="$sbRoot/images/flags/fr.png" width="16" height="11" alt="fr" title="fr" /></td></tr>
+    #else
     <tr><td class="showLegend">Audio Language:</td><td><img src="$sbRoot/images/flags/${show.audio_lang}.png" width="16" height="11" alt="$show.audio_lang" title="$show.lang" /></td></tr>
-	<tr><td class="showLegend">Custom Names :</td><td>#if $show.exceptions then $exceptions_string else $show.name#</td></tr>
+    #end if
+    <tr><td class="showLegend">Custom Names :</td><td>#if $show.exceptions then $exceptions_string else $show.name#</td></tr>
 	#if $show.imdbid:
 	#if $show.imdb_info['rating'] !='':
 	<tr><td class="showLegend">Rating :</td><td>$show.imdb_info['rating']</td></tr>
@@ -206,7 +210,7 @@
 	#else:
 	<tr><td class="showLegend">No Rating</td><td></td></tr>
 	#end if
-    </td></tr>
+	</td></tr>
     </table>
     <td width="25%">
     <table>
diff --git a/data/interfaces/default/editShow.tmpl b/data/interfaces/default/editShow.tmpl
index bf721d5ab..9990ccfe0 100644
--- a/data/interfaces/default/editShow.tmpl
+++ b/data/interfaces/default/editShow.tmpl
@@ -125,6 +125,7 @@ selected
 <table>
 <b>Flatten files (no folders):</b> <input type="checkbox" name="flatten_folders" #if $show.flatten_folders == 1 and not $sickbeard.NAMING_FORCE_FOLDERS then "checked=\"checked\"" else ""# #if $sickbeard.NAMING_FORCE_FOLDERS then "disabled=\"disabled\"" else ""#/><br /><br />
 <b>Paused:</b> <input type="checkbox" name="paused" #if $show.paused == 1 then "checked=\"checked\"" else ""# /><br /><br />
+<b>French Search (auto) :</b> <input type="checkbox" name="frenchsearch" #if $show.frenchsearch == 1 and $sickbeard.DOWNLOAD_FRENCH then "checked=\"checked\"" else ""##if not $sickbeard.DOWNLOAD_FRENCH then " disabled=\"disabled\"" else ""# /><br /><br />
 <b>Subtitles:</b> <input type="checkbox" name="subtitles"#if $show.subtitles == 1 and $sickbeard.USE_SUBTITLES then " checked=\"checked\"" else ""##if not $sickbeard.USE_SUBTITLES then " disabled=\"disabled\"" else ""#/><br /><br />
 
 <b>Air by date: </b> 
diff --git a/data/interfaces/default/history.tmpl b/data/interfaces/default/history.tmpl
index 759101f2c..aa7ca1cb1 100644
--- a/data/interfaces/default/history.tmpl
+++ b/data/interfaces/default/history.tmpl
@@ -53,7 +53,11 @@
 #set $curStatus, $curQuality = $Quality.splitCompositeStatus(int($hItem["action"]))
   <tr>
     <td class="nowrap">$datetime.datetime.strptime(str($hItem["date"]), $history.dateFormat)</td>
+    #if "snatchedfr" in $hItem["resource"].lower
+    <td width="35%"><a style="color: #000000; text-align: center;" href="$sbRoot/home/displayShow?show=$hItem["showid"]#season-$hItem["season"]">$hItem["show_name"] - <%=str(hItem["season"]) +"x"+ "%02i" % int(hItem["episode"]) %> <span class="quality Proper">French</span></a></td>
+    #else
     <td width="35%"><a style="color: #000000; text-align: center;" href="$sbRoot/home/displayShow?show=$hItem["showid"]#season-$hItem["season"]">$hItem["show_name"] - <%=str(hItem["season"]) +"x"+ "%02i" % int(hItem["episode"]) %>#if "proper" in $hItem["resource"].lower or "repack" in $hItem["resource"].lower then ' <span class="quality Proper">Proper</span>' else ""#</a></td>
+    #end if
     <td align="center" #if $curStatus == SUBTITLED then 'class="subtitles_column"' else ''#><span style="cursor: help;" title="$os.path.basename($hItem["resource"])">$statusStrings[$curStatus]</span>
     	#if $curStatus == SUBTITLED:
     		<img width="16" height="11" src="/images/flags/<%= hItem["resource"][len(hItem["resource"])-6:len(hItem["resource"])-4]+'.png'%>">
diff --git a/data/interfaces/default/home.tmpl b/data/interfaces/default/home.tmpl
index 97e911181..89acd8941 100644
--- a/data/interfaces/default/home.tmpl
+++ b/data/interfaces/default/home.tmpl
@@ -14,10 +14,10 @@
 
 #set $myDB = $db.DBConnection()
 #set $today = str($datetime.date.today().toordinal())
-#set $downloadedEps = $myDB.select("SELECT showid, COUNT(*) FROM tv_episodes WHERE (status IN ("+",".join([str(x) for x in $Quality.DOWNLOADED + [$ARCHIVED]])+") OR (status IN ("+",".join([str(x) for x in $Quality.SNATCHED + $Quality.SNATCHED_PROPER])+") AND location != '')) AND season != 0 and episode != 0 AND airdate <= "+$today+" GROUP BY showid")
-#set $snatchedEps = $myDB.select("SELECT showid, COUNT(*) FROM tv_episodes WHERE (status IN ("+",".join([str(x) for x in $Quality.SNATCHED + [$ARCHIVED]])+") OR (status IN ("+",".join([str(x) for x in $Quality.SNATCHED + $Quality.SNATCHED_PROPER])+") AND location != '')) AND season != 0 and episode != 0 AND airdate <= "+$today+" GROUP BY showid")
-#set $wantedEps = $myDB.select("SELECT showid, COUNT(*) FROM tv_episodes WHERE (status IN ("+",".join([str(x) for x in $Quality.WANTED + [$ARCHIVED]])+") OR (status IN ("+",".join([str(x) for x in $Quality.SNATCHED + $Quality.SNATCHED_PROPER])+") AND location != '')) AND season != 0 and episode != 0 AND airdate <= "+$today+" GROUP BY showid")
-#set $allEps = $myDB.select("SELECT showid, COUNT(*) FROM tv_episodes WHERE season != 0 and episode != 0 AND (airdate != 1 OR status IN ("+",".join([str(x) for x in ($Quality.DOWNLOADED + $Quality.SNATCHED + $Quality.SNATCHED_PROPER) + [$ARCHIVED]])+")) AND airdate <= "+$today+" AND status != "+str($IGNORED)+" GROUP BY showid")
+#set $downloadedEps = $myDB.select("SELECT showid, COUNT(*) FROM tv_episodes WHERE (status IN ("+",".join([str(x) for x in $Quality.DOWNLOADED + [$ARCHIVED]])+") OR (status IN ("+",".join([str(x) for x in $Quality.SNATCHED + $Quality.SNATCHED_PROPER + $Quality.SNATCHED_FRENCH])+") AND location != '')) AND season != 0 and episode != 0 AND airdate <= "+$today+" GROUP BY showid")
+#set $snatchedEps = $myDB.select("SELECT showid, COUNT(*) FROM tv_episodes WHERE (status IN ("+",".join([str(x) for x in $Quality.SNATCHED + [$ARCHIVED]])+") OR (status IN ("+",".join([str(x) for x in $Quality.SNATCHED + $Quality.SNATCHED_PROPER + $Quality.SNATCHED_FRENCH])+") AND location != '')) AND season != 0 and episode != 0 AND airdate <= "+$today+" GROUP BY showid")
+#set $wantedEps = $myDB.select("SELECT showid, COUNT(*) FROM tv_episodes WHERE (status IN ("+",".join([str(x) for x in $Quality.WANTED + [$ARCHIVED]])+") OR (status IN ("+",".join([str(x) for x in $Quality.SNATCHED + $Quality.SNATCHED_PROPER + $Quality.SNATCHED_FRENCH])+") AND location != '')) AND season != 0 and episode != 0 AND airdate <= "+$today+" GROUP BY showid")
+#set $allEps = $myDB.select("SELECT showid, COUNT(*) FROM tv_episodes WHERE season != 0 and episode != 0 AND (airdate != 1 OR status IN ("+",".join([str(x) for x in ($Quality.DOWNLOADED + $Quality.SNATCHED + $Quality.SNATCHED_PROPER + $Quality.SNATCHED_FRENCH) + [$ARCHIVED]])+")) AND airdate <= "+$today+" AND status != "+str($IGNORED)+" GROUP BY showid")
 #set $fr = $myDB.select("SELECT showid, COUNT(*) FROM tv_episodes WHERE audio_langs = 'fr' AND location != '' AND season != 0 and episode != 0 AND airdate <= "+$today+" GROUP BY showid")
 #set $subfr = $myDB.select("SELECT showid, COUNT(*) FROM tv_episodes WHERE audio_langs <> 'fr' AND subtitles like '%fr%' AND location != '' AND season != 0 and episode != 0 AND airdate <= "+$today+" GROUP BY showid")
 #set $layout = $sickbeard.HOME_LAYOUT
@@ -345,8 +345,12 @@ function setLayout(imdb,name){
 		subfr : "$nomSub",
 		downloads : setPbDl($nom,$nomSna,$den), 
 		frenchep : setPbFr($nomfr,$nomSub,$denfr), 
-		active : "$active", 
-		audio : "<img src=\"$sbRoot/images/flags/${curShow.audio_lang}.png\" alt=\"$curShow.audio_lang\" width=\"16\" />", 
+		active : "$active",
+		#if int($curShow.frenchsearch) == 1
+		audio : "<img src=\"$sbRoot/images/flags/${curShow.audio_lang}.png\" alt=\"$curShow.audio_lang\" width=\"16\" />  <img src=\"$sbRoot/images/flags/fr.png\" alt=\"fr\" width=\"16\" />", 
+		#else
+		audio : "<img src=\"$sbRoot/images/flags/${curShow.audio_lang}.png\" alt=\"$curShow.audio_lang\" width=\"16\" />",
+		#end if
 		status : "$curShow.status"  		
 	},
 #end for
diff --git a/data/interfaces/default/inc_top.tmpl b/data/interfaces/default/inc_top.tmpl
index cdbdeb6af..0b9b1cf47 100644
--- a/data/interfaces/default/inc_top.tmpl
+++ b/data/interfaces/default/inc_top.tmpl
@@ -102,6 +102,7 @@
         \$("#SubMenu a[href='/home/updatePLEX/']").addClass('btn').html('<span class="ui-icon ui-icon-refresh pull-left"></span> Update PLEX </a>');
 		\$("#SubMenu a:contains('Force')").addClass('btn').html('<span class="ui-icon ui-icon-transfer-e-w pull-left"></span> Force Full Update </a>');
 		\$("#SubMenu a:contains('Rename')").addClass('btn').html('<span class="ui-icon ui-icon-tag pull-left"></span> Preview Rename </a>');
+		\$("#SubMenu a:contains('French')").addClass('btn').html('<span class="ui-icon ui-icon-refresh pull-left"></span> French Search </a>');
 		\$("#SubMenu a[href='/config/subtitles/']").addClass('btn').html('<span class="ui-icon ui-icon-comment pull-left"></span> Search Subtitles </a>');
 		\$("#SubMenu a[href^='/home/subtitleShow']").addClass('btn').html('<span class="ui-icon ui-icon-comment pull-left"></span> Download Subtitles </a>');
 		\$("#SubMenu a[href^='/home/subtitleShowClean']").addClass('btn').html('<span class="ui-icon ui-icon-comment pull-left"></span> Clean Subtitles </a>');
diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py
index 26991d04d..b35026db2 100755
--- a/sickbeard/__init__.py
+++ b/sickbeard/__init__.py
@@ -33,7 +33,7 @@ from sickbeard import providers, metadata
 from providers import ezrss, tvtorrents, torrentleech, btn, nzbsrus, newznab, womble, nzbx, omgwtfnzbs, binnewz, t411, cpasbien, piratebay, gks, kat
 from sickbeard.config import CheckSection, check_setting_int, check_setting_str, ConfigMigrator
 
-from sickbeard import searchCurrent, searchBacklog, showUpdater, versionChecker, properFinder, autoPostProcesser, subtitles, traktWatchListChecker, SentFTPChecker
+from sickbeard import searchCurrent, searchBacklog, showUpdater, versionChecker, properFinder, frenchFinder, autoPostProcesser, subtitles, traktWatchListChecker, SentFTPChecker
 from sickbeard import helpers, db, exceptions, show_queue, search_queue, scheduler
 from sickbeard import logger
 from sickbeard import naming
@@ -74,6 +74,7 @@ showUpdateScheduler = None
 versionCheckScheduler = None
 showQueueScheduler = None
 searchQueueScheduler = None
+frenchFinderScheduler = None
 properFinderScheduler = None
 autoPostProcesserScheduler = None
 autoTorrentPostProcesserScheduler = None
@@ -156,6 +157,7 @@ USENET_RETENTION = None
 TORRENT_METHOD = None
 TORRENT_DIR = None
 DOWNLOAD_PROPERS = None
+DOWNLOAD_FRENCH = None
 PREFERED_METHOD = None
 SEARCH_FREQUENCY = None
 BACKLOG_SEARCH_FREQUENCY = 1
@@ -427,7 +429,7 @@ def initialize(consoleLogging=True):
     with INIT_LOCK:
 
         global LOG_DIR, WEB_PORT, WEB_LOG, WEB_ROOT, WEB_USERNAME, WEB_PASSWORD, WEB_HOST, WEB_IPV6, USE_API, API_KEY, ENABLE_HTTPS, HTTPS_CERT, HTTPS_KEY, \
-                USE_NZBS, USE_TORRENTS, NZB_METHOD, NZB_DIR, DOWNLOAD_PROPERS, TORRENT_METHOD, PREFERED_METHOD, \
+                USE_NZBS, USE_TORRENTS, NZB_METHOD, NZB_DIR, DOWNLOAD_PROPERS, DOWNLOAD_FRENCH, TORRENT_METHOD, PREFERED_METHOD, \
                 SAB_USERNAME, SAB_PASSWORD, SAB_APIKEY, SAB_CATEGORY, SAB_HOST, \
                 NZBGET_PASSWORD, NZBGET_CATEGORY, NZBGET_HOST, currentSearchScheduler, backlogSearchScheduler, \
                 TORRENT_USERNAME, TORRENT_PASSWORD, TORRENT_HOST, TORRENT_PATH, TORRENT_RATIO, TORRENT_PAUSED, TORRENT_LABEL, \
@@ -456,7 +458,7 @@ def initialize(consoleLogging=True):
                 KEEP_PROCESSED_DIR, PROCESS_METHOD, TV_DOWNLOAD_DIR, TORRENT_DOWNLOAD_DIR, TVDB_BASE_URL, MIN_SEARCH_FREQUENCY, \
                 showQueueScheduler, searchQueueScheduler, ROOT_DIRS, CACHE_DIR, ACTUAL_CACHE_DIR, TVDB_API_PARMS, \
                 NAMING_PATTERN, NAMING_MULTI_EP, NAMING_FORCE_FOLDERS, NAMING_ABD_PATTERN, NAMING_CUSTOM_ABD, \
-                RENAME_EPISODES, properFinderScheduler, PROVIDER_ORDER, autoPostProcesserScheduler, autoTorrentPostProcesserScheduler, \
+                RENAME_EPISODES, frenchFinderScheduler, properFinderScheduler, PROVIDER_ORDER, autoPostProcesserScheduler, autoTorrentPostProcesserScheduler, \
                 NZBSRUS, NZBSRUS_UID, NZBSRUS_HASH, WOMBLE, NZBX, NZBX_COMPLETION, OMGWTFNZBS, OMGWTFNZBS_UID, OMGWTFNZBS_KEY, providerList, newznabProviderList, \
                 EXTRA_SCRIPTS, USE_TWITTER, TWITTER_USERNAME, TWITTER_PASSWORD, TWITTER_PREFIX, \
                 USE_NOTIFO, NOTIFO_USERNAME, NOTIFO_APISECRET, NOTIFO_NOTIFY_ONDOWNLOAD, NOTIFO_NOTIFY_ONSUBTITLEDOWNLOAD, NOTIFO_NOTIFY_ONSNATCH, \
@@ -575,6 +577,7 @@ def initialize(consoleLogging=True):
             PREFERED_METHOD = 'nzb'
 
         DOWNLOAD_PROPERS = bool(check_setting_int(CFG, 'General', 'download_propers', 1))
+        DOWNLOAD_FRENCH = bool(check_setting_int(CFG, 'General', 'download_french', 0))
         USENET_RETENTION = check_setting_int(CFG, 'General', 'usenet_retention', 500)
         SEARCH_FREQUENCY = check_setting_int(CFG, 'General', 'search_frequency', DEFAULT_SEARCH_FREQUENCY)
         if SEARCH_FREQUENCY < MIN_SEARCH_FREQUENCY:
@@ -999,6 +1002,11 @@ def initialize(consoleLogging=True):
                                                      cycleTime=properFinderInstance.updateInterval,
                                                      threadName="FINDPROPERS",
                                                      runImmediately=False)
+        
+        frenchFinderScheduler = scheduler.Scheduler(frenchFinder.FrenchFinder(),
+                                                     cycleTime=datetime.timedelta(minutes=10080),
+                                                     threadName="FINDFRENCH",
+                                                     runImmediately=True)
 
         if PROCESS_AUTOMATICALLY:
             autoPostProcesserScheduler = scheduler.Scheduler(autoPostProcesser.PostProcesser( TV_DOWNLOAD_DIR ),
@@ -1046,7 +1054,7 @@ def start():
 
     global __INITIALIZED__, currentSearchScheduler, backlogSearchScheduler, \
             showUpdateScheduler, versionCheckScheduler, showQueueScheduler, \
-            properFinderScheduler, autoPostProcesserScheduler, autoTorrentPostProcesserScheduler, searchQueueScheduler, \
+            frenchFinderScheduler, properFinderScheduler, autoPostProcesserScheduler, autoTorrentPostProcesserScheduler, searchQueueScheduler, \
             subtitlesFinderScheduler, started, USE_SUBTITLES, \
             traktWatchListCheckerSchedular, started, \
             sentFTPSchedular, started
@@ -1072,7 +1080,9 @@ def start():
 
             # start the search queue checker
             searchQueueScheduler.thread.start()
-
+            
+            if DOWNLOAD_FRENCH:
+                frenchFinderScheduler.thread.start()
             # start the queue checker
             if DOWNLOAD_PROPERS:
                 properFinderScheduler.thread.start()
@@ -1101,7 +1111,7 @@ def start():
 def halt():
 
     global __INITIALIZED__, currentSearchScheduler, backlogSearchScheduler, showUpdateScheduler, \
-            showQueueScheduler, properFinderScheduler, autoPostProcesserScheduler, autoTorrentPostProcesserScheduler, searchQueueScheduler, \
+            showQueueScheduler, frenchFinderScheduler, properFinderScheduler, autoPostProcesserScheduler, autoTorrentPostProcesserScheduler, searchQueueScheduler, \
             subtitlesFinderScheduler, started, \
             traktWatchListCheckerSchedular
 
@@ -1193,7 +1203,16 @@ def halt():
                     properFinderScheduler.thread.join(10)
                 except:
                     pass
-    
+                
+            if frenchFinderScheduler:
+                frenchFinderScheduler.abort = True
+                logger.log(u"Waiting for the FRENCHFINDER thread to exit")
+                try:
+                    frenchFinderScheduler.thread.join(10)
+                except:
+                    pass
+                            
+            if subtitlesFinderScheduler:
                 subtitlesFinderScheduler.abort = True
                 logger.log(u"Waiting for the SUBTITLESFINDER thread to exit")
                 try:
@@ -1321,6 +1340,7 @@ def save_config():
     new_config['General']['usenet_retention'] = int(USENET_RETENTION)
     new_config['General']['search_frequency'] = int(SEARCH_FREQUENCY)
     new_config['General']['download_propers'] = int(DOWNLOAD_PROPERS)
+    new_config['General']['download_french'] = int(DOWNLOAD_FRENCH)
     new_config['General']['quality_default'] = int(QUALITY_DEFAULT)
     new_config['General']['status_default'] = int(STATUS_DEFAULT)
     new_config['General']['audio_show_default'] = AUDIO_SHOW_DEFAULT
diff --git a/sickbeard/common.py b/sickbeard/common.py
index f2f8d840b..83facaddd 100644
--- a/sickbeard/common.py
+++ b/sickbeard/common.py
@@ -58,6 +58,7 @@ ARCHIVED = 6 # episodes that you don't have locally (counts toward download comp
 IGNORED = 7 # episodes that you don't want included in your download stats
 SNATCHED_PROPER = 9 # qualified with quality
 SUBTITLED = 10 # qualified with quality
+SNATCHED_FRENCH = 11 # episodes downloaded in english then autodownloaded in french
 
 NAMING_REPEAT = 1
 NAMING_EXTEND = 2
@@ -220,11 +221,13 @@ class Quality:
     DOWNLOADED = None
     SNATCHED = None
     SNATCHED_PROPER = None
+    SNATCHED_FRENCH = None
     WANTED = None
 
 Quality.DOWNLOADED = [Quality.compositeStatus(DOWNLOADED, x) for x in Quality.qualityStrings.keys()]
 Quality.SNATCHED = [Quality.compositeStatus(SNATCHED, x) for x in Quality.qualityStrings.keys()]
 Quality.SNATCHED_PROPER = [Quality.compositeStatus(SNATCHED_PROPER, x) for x in Quality.qualityStrings.keys()]
+Quality.SNATCHED_FRENCH = [Quality.compositeStatus(SNATCHED_FRENCH, x) for x in Quality.qualityStrings.keys()]
 Quality.WANTED = [Quality.compositeStatus(WANTED, x) for x in Quality.qualityStrings.keys()]
 
 SD = Quality.combineQualities([Quality.SDTV, Quality.SDDVD], [])
@@ -252,13 +255,14 @@ class StatusStrings:
                               DOWNLOADED: "Downloaded",
                               SKIPPED: "Skipped",
                               SNATCHED_PROPER: "Snatched (Proper)",
+                              SNATCHED_FRENCH: "Snatched (French)",
                               WANTED: "Wanted",
                               ARCHIVED: "Archived",
                               IGNORED: "Ignored",
                               SUBTITLED: "Subtitled"}
 
     def __getitem__(self, name):
-        if name in Quality.DOWNLOADED + Quality.SNATCHED + Quality.SNATCHED_PROPER:
+        if name in Quality.DOWNLOADED + Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.SNATCHED_FRENCH:
             status, quality = Quality.splitCompositeStatus(name)
             if quality == Quality.NONE:
                 return self.statusStrings[status]
@@ -268,7 +272,7 @@ class StatusStrings:
             return self.statusStrings[name]
 
     def has_key(self, name):
-        return name in self.statusStrings or name in Quality.DOWNLOADED or name in Quality.SNATCHED or name in Quality.SNATCHED_PROPER
+        return name in self.statusStrings or name in Quality.DOWNLOADED or name in Quality.SNATCHED or name in Quality.SNATCHED_PROPER or name in Quality.SNATCHED_FRENCH
 
 statusStrings = StatusStrings()
 
@@ -280,7 +284,7 @@ class Overview:
     SKIPPED = SKIPPED # 5
 
     # For both snatched statuses. Note: SNATCHED/QUAL have same value and break dict.
-    SNATCHED = SNATCHED_PROPER # 9
+    SNATCHED = SNATCHED_PROPER = SNATCHED_FRENCH # 9
 
     overviewStrings = {SKIPPED: "skipped",
                        WANTED: "wanted",
diff --git a/sickbeard/databases/mainDB.py b/sickbeard/databases/mainDB.py
index 6295a8050..7f4c747de 100644
--- a/sickbeard/databases/mainDB.py
+++ b/sickbeard/databases/mainDB.py
@@ -97,7 +97,7 @@ class InitialSchema (db.SchemaUpgrade):
 
     def execute(self):
         queries = [
-            "CREATE TABLE tv_shows (show_id INTEGER PRIMARY KEY, location TEXT, show_name TEXT, tvdb_id NUMERIC, network TEXT, genre TEXT, runtime NUMERIC, quality NUMERIC, airs TEXT, status TEXT, seasonfolders NUMERIC, paused NUMERIC, startyear NUMERIC);",
+            "CREATE TABLE tv_shows (show_id INTEGER PRIMARY KEY, location TEXT, show_name TEXT, tvdb_id NUMERIC, network TEXT, genre TEXT, runtime NUMERIC, quality NUMERIC, airs TEXT, status TEXT, seasonfolders NUMERIC, paused NUMERIC, startyear NUMERIC, frenchsearch NUMERIC);",
             "CREATE TABLE tv_episodes (episode_id INTEGER PRIMARY KEY, showid NUMERIC, tvdbid NUMERIC, name TEXT, season NUMERIC, episode NUMERIC, description TEXT, airdate NUMERIC, hasnfo NUMERIC, hastbn NUMERIC, status NUMERIC, location TEXT);",
             "CREATE TABLE info (last_backlog NUMERIC, last_tvdb NUMERIC);",
             "CREATE TABLE history (action NUMERIC, date NUMERIC, showid NUMERIC, season NUMERIC, episode NUMERIC, quality NUMERIC, resource TEXT, provider NUMERIC);",
@@ -357,8 +357,15 @@ class AddLang (FixSabHostURL):
 
     def execute(self):
         self.addColumn("tv_shows", "lang", "TEXT", "fr")
+        
+class AddFrenchSearch (AddLang):
+    def test(self):
+        return self.hasColumn("tv_shows", "frenchsearch")
+
+    def execute(self):
+        self.addColumn("tv_shows", "frenchsearch", "NUMERIC", 0)
 
-class AddCustomSearchNames (AddLang):
+class AddCustomSearchNames (AddFrenchSearch):
     def test(self):
         return self.hasColumn("tv_shows", "custom_search_names")
 
diff --git a/sickbeard/frenchFinder.py b/sickbeard/frenchFinder.py
new file mode 100644
index 000000000..3b1ba86c1
--- /dev/null
+++ b/sickbeard/frenchFinder.py
@@ -0,0 +1,79 @@
+# Author: Nic Wolfe <nic@wolfeden.ca>
+# URL: http://code.google.com/p/sickbeard/
+#
+# This file is part of Sick Beard.
+#
+# Sick Beard is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Sick Beard is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Sick Beard.  If not, see <http://www.gnu.org/licenses/>.
+
+import datetime
+import operator
+
+import sickbeard
+
+from sickbeard import db
+from sickbeard import helpers, logger, show_name_helpers
+from sickbeard import providers
+from sickbeard import search
+from sickbeard import history
+
+from sickbeard.common import DOWNLOADED, SNATCHED, SNATCHED_FRENCH, Quality
+
+from lib.tvdb_api import tvdb_api, tvdb_exceptions
+
+from name_parser.parser import NameParser, InvalidNameException
+
+
+class FrenchFinder():
+
+    def __init__(self, force=None, show=None):
+
+        #TODOif not sickbeard.DOWNLOAD_FRENCH:
+        #    return
+        if sickbeard.showList==None:
+            return
+        logger.log(u"Beginning the search for french episodes")
+       
+        frenchlist=[]
+        #get list of english episodes that we want to search in french
+        myDB = db.DBConnection()
+        if show:
+            frenchsql=myDB.select("SELECT showid, season, episode from tv_episodes where audio_langs='en' and tv_episodes.showid =?",[show]) 
+        else:
+            frenchsql=myDB.select("SELECT showid, season, episode from tv_episodes, tv_shows where audio_langs='en' and tv_episodes.showid = tv_shows.tvdb_id and tv_shows.frenchsearch = 1")
+        #make the episodes objects
+        for episode in frenchsql:
+            showObj = helpers.findCertainShow(sickbeard.showList, episode[0])
+            epObj = showObj.getEpisode(episode[1], episode[2])
+            frenchlist.append(epObj)
+        
+        #for each episode in frenchlist fire a search in french
+        for frepisode in frenchlist:
+            result=[]
+            for curProvider in providers.sortedProviderList():
+
+                if not curProvider.isActive():
+                    continue
+
+                logger.log(u"Searching for french episodes on "+curProvider.name +" for " +frepisode.show.name +" season "+str(frepisode.season)+" episode "+str(frepisode.episode))
+                curfrench = curProvider.findFrench(frepisode, manualSearch=True)
+                for x in curfrench:
+                    result.append(x)
+            best = search.pickBestResult(result, episode = epObj.episode, season = epObj.season)
+            if best:
+                best.name=best.name + ' snatchedfr'
+                logger.log(u"Found french episode for " +frepisode.show.name +" season "+str(frepisode.season)+" episode "+str(frepisode.episode))
+                search.snatchEpisode(best, SNATCHED_FRENCH)
+            else:
+                logger.log(u"No french episodes found for " +frepisode.show.name +" season "+str(frepisode.season)+" episode "+str(frepisode.episode))
+        
\ No newline at end of file
diff --git a/sickbeard/postProcessor.py b/sickbeard/postProcessor.py
index 544070e8f..74921457b 100755
--- a/sickbeard/postProcessor.py
+++ b/sickbeard/postProcessor.py
@@ -723,7 +723,7 @@ class PostProcessor(object):
         ep_quality = common.Quality.UNKNOWN
 
         # if there is a quality available in the status then we don't need to bother guessing from the filename
-        if ep_obj.status in common.Quality.SNATCHED + common.Quality.SNATCHED_PROPER:
+        if ep_obj.status in common.Quality.SNATCHED + common.Quality.SNATCHED_PROPER + common.Quality.SNATCHED_FRENCH:
             oldStatus, ep_quality = common.Quality.splitCompositeStatus(ep_obj.status) #@UnusedVariable
             if ep_quality != common.Quality.UNKNOWN:
                 self._log(u"The old status had a quality in it, using that: "+common.Quality.qualityStrings[ep_quality], logger.DEBUG)
@@ -789,7 +789,7 @@ class PostProcessor(object):
         """
         
         # if SB downloaded this on purpose then this is a priority download
-        if self.in_history or ep_obj.status in common.Quality.SNATCHED + common.Quality.SNATCHED_PROPER:
+        if self.in_history or ep_obj.status in common.Quality.SNATCHED + common.Quality.SNATCHED_PROPER + common.Quality.SNATCHED_FRENCH:
             self._log(u"SB snatched this episode so I'm marking it as priority", logger.DEBUG)
             return True
         
diff --git a/sickbeard/providers/binnewz/__init__.py b/sickbeard/providers/binnewz/__init__.py
index 2e9f22988..86ee5d0af 100644
--- a/sickbeard/providers/binnewz/__init__.py
+++ b/sickbeard/providers/binnewz/__init__.py
@@ -92,7 +92,7 @@ class BinNewzProvider(generic.NZBProvider):
             result.append(showName + ".saison %2d" % season)
         return result
 
-    def _get_episode_search_strings(self, ep_obj):
+    def _get_episode_search_strings(self, ep_obj, french=None):
         strings = []
         showNames = show_name_helpers.allPossibleShowNames(ep_obj.show)
         global globepid
@@ -134,7 +134,7 @@ class BinNewzProvider(generic.NZBProvider):
         return data
 
     #wtf with the signature change...
-    def _doSearch(self, searchString=None, show=None, season=None):
+    def _doSearch(self, searchString=None, show=None, season=None, french=None):
         if searchString is None:
             return []
         logger.log("BinNewz : Searching for " + searchString)
@@ -161,7 +161,7 @@ class BinNewzProvider(generic.NZBProvider):
                 language = cells[3].find("img").get("src")
 
                 if show:
-                    if show.audio_lang == "fr":
+                    if show.audio_lang == "fr" or french:
                         if not "_fr" in language:
                             continue
                     elif show.audio_lang == "en":
@@ -220,7 +220,10 @@ class BinNewzProvider(generic.NZBProvider):
                             binsearch_result = downloader.search(searchItem, minSize, newsgroup)
                             if binsearch_result:
                                 links = []
-                                binsearch_result.audio_langs = show.audio_lang
+                                if french:
+                                    binsearch_result.audio_langs = 'fr'
+                                else:
+                                    binsearch_result.audio_langs = show.audio_lang
                                 binsearch_result.title = name
                                 binsearch_result.quality = quality
                                 myDB = db.DBConnection()
diff --git a/sickbeard/providers/cpasbien.py b/sickbeard/providers/cpasbien.py
index bbb3b228c..5952a9e09 100644
--- a/sickbeard/providers/cpasbien.py
+++ b/sickbeard/providers/cpasbien.py
@@ -53,7 +53,7 @@ class CpasbienProvider(generic.TorrentProvider):
             result.append( showName + " S%02d" % season )
         return result
 
-    def _get_episode_search_strings(self, ep_obj):
+    def _get_episode_search_strings(self, ep_obj, french=None):
 
         strings = []
 
@@ -70,7 +70,7 @@ class CpasbienProvider(generic.TorrentProvider):
     def getQuality(self, item):
         return item.getQuality()
         
-    def _doSearch(self, searchString, show=None, season=None):
+    def _doSearch(self, searchString, show=None, season=None, french=None):
 
         results = []
         searchUrl = self.url + '/recherche/'
@@ -91,7 +91,7 @@ class CpasbienProvider(generic.TorrentProvider):
             title = str(link.text).lower().strip()  
             pageURL = link['href']
 
-            if "vostfr" in title and ((not show.subtitles) or show.audio_lang == "fr"):
+            if "vostfr" in title and ((not show.subtitles) or show.audio_lang == "fr" or french):
                 continue
 
             torrentPage = self.opener.open( pageURL )
@@ -123,8 +123,10 @@ class CpasbienProvider(generic.TorrentProvider):
                 else:
                     quality = Quality.SDTV
 
-                if show:
+                if show and french==None:
                     results.append( CpasbienSearchResult( self.opener, title, downloadURL, quality, str(show.audio_lang) ) )
+                elif show and french:
+                    results.append( CpasbienSearchResult( self.opener, title, downloadURL, quality, 'fr' ) )
                 else:
                     results.append( CpasbienSearchResult( self.opener, title, downloadURL, quality ) )
 
diff --git a/sickbeard/providers/generic.py b/sickbeard/providers/generic.py
index 4848c976a..84d7dd19b 100644
--- a/sickbeard/providers/generic.py
+++ b/sickbeard/providers/generic.py
@@ -190,13 +190,13 @@ class GenericProvider:
         quality = Quality.nameQuality(title)
         return quality
 
-    def _doSearch(self, show=None, season=None):
+    def _doSearch(self, show=None, season=None, french=None):
         return []
 
     def _get_season_search_strings(self, show, season, episode=None):
         return []
 
-    def _get_episode_search_strings(self, ep_obj):
+    def _get_episode_search_strings(self, ep_obj, french=None):
         return []
     
     def _get_title_and_url(self, item):
@@ -411,7 +411,69 @@ class GenericProvider:
 
         return [classes.Proper(x['name'], x['url'], datetime.datetime.fromtimestamp(x['time'])) for x in results]
 
+    def findFrench(self, episode=None, manualSearch=False):
+        results = []
+        self._checkAuth()
+
+        logger.log(u"Searching "+self.name+" for " + episode.prettyName())
+       
+        itemList = []
+
+        for cur_search_string in self._get_episode_search_strings(episode,'french'):
+            itemList += self._doSearch(cur_search_string, show=episode.show, french='french')
+
+        for item in itemList:
+
+            (title, url) = self._get_title_and_url(item)
+
+            # parse the file name
+            try:
+                myParser = NameParser()
+                parse_result = myParser.parse(title)
+            except InvalidNameException:
+                logger.log(u"Unable to parse the filename "+title+" into a valid episode", logger.WARNING)
+                continue
+            
+            language = self._get_language(title,item)
+
+            if episode.show.air_by_date:
+                if parse_result.air_date != episode.airdate:
+                    logger.log("Episode "+title+" didn't air on "+str(episode.airdate)+", skipping it", logger.DEBUG)
+                    continue
+            elif parse_result.season_number != episode.season or episode.episode not in parse_result.episode_numbers:
+                logger.log("Episode "+title+" isn't "+str(episode.season)+"x"+str(episode.episode)+", skipping it", logger.DEBUG)
+                continue
 
+            quality = self.getQuality(item)
+
+            if not episode.show.wantEpisode(episode.season, episode.episode, quality, manualSearch):
+                logger.log(u"Ignoring result "+title+" because we don't want an episode that is "+Quality.qualityStrings[quality], logger.DEBUG)
+                continue
+            
+            if not language == 'fr':
+                logger.log(u"Ignoring result "+title+" because the language: " + showLanguages[language] + " does not match the desired language: French")
+                continue
+
+            logger.log(u"Found result " + title + " at " + url, logger.DEBUG)
+
+            result = self.getResult([episode])
+            result.item = item
+            if hasattr(item , 'getNZB'):
+                result.extraInfo = [item.getNZB() ]
+            elif hasattr(item , 'extraInfo'):
+                result.extraInfo = item.extraInfo
+            result.url = url
+            result.name = title
+            result.quality = quality
+            if hasattr(item , 'audio_langs'):
+                result.audio_lang=''.join(item.audio_langs)
+
+            else:
+                result.audio_lang=language
+            results.append(result)
+
+        return results
+    
 class NZBProvider(GenericProvider):
 
     def __init__(self, name):
diff --git a/sickbeard/providers/gks.py b/sickbeard/providers/gks.py
index 5578c4229..c6dff7f4c 100644
--- a/sickbeard/providers/gks.py
+++ b/sickbeard/providers/gks.py
@@ -73,16 +73,20 @@ class GksProvider(generic.TorrentProvider):
                 results.append(result)
         return results
 
-    def _get_episode_search_strings(self, ep_obj):
+    def _get_episode_search_strings(self, ep_obj, french=None):
 
         showNames = show_name_helpers.allPossibleShowNames(ep_obj.show)
         results = []
         for showName in showNames:
-            for result in self.getSearchParams( "%s S%02dE%02d" % ( showName, ep_obj.season, ep_obj.episode), ep_obj.show.audio_lang) :
+            if french:
+                lang='fr'
+            else:
+                lang=ep_obj.show.audio_lang
+            for result in self.getSearchParams( "%s S%02dE%02d" % ( showName, ep_obj.season, ep_obj.episode), lang) :
                 results.append(result)
         return results
         
-    def _doSearch(self, searchString, show=None, season=None):
+    def _doSearch(self, searchString, show=None, season=None, french=None):
         results = []
         searchUrl = self.url+'rdirect.php?type=search&'+searchString
         logger.log(u"Search URL: " + searchUrl, logger.DEBUG)
@@ -130,8 +134,10 @@ class GksProvider(generic.TorrentProvider):
                     if quality==Quality.UNKNOWN and title:
                         if '720p' not in title.lower() and '1080p' not in title.lower():
                             quality=Quality.SDTV
-                    if show:
+                    if show and french==None:
                         results.append( GksSearchResult( self.opener, title, downloadURL, quality, str(show.audio_lang) ) )
+                    elif show and french:
+                        results.append( GksSearchResult( self.opener, title, downloadURL, quality, 'fr' ) )
                     else:
                         results.append( GksSearchResult( self.opener, title, downloadURL, quality ) )
         return results
diff --git a/sickbeard/providers/kat.py b/sickbeard/providers/kat.py
index 84d8d60f7..f57d6f538 100644
--- a/sickbeard/providers/kat.py
+++ b/sickbeard/providers/kat.py
@@ -91,7 +91,7 @@ class KATProvider(generic.TorrentProvider):
     
         return [params]
 
-    def _get_episode_search_strings(self, ep_obj):
+    def _get_episode_search_strings(self, ep_obj,french=None):
     
         params = {}
         
@@ -121,7 +121,10 @@ class KATProvider(generic.TorrentProvider):
                 to_return.append(cur_return)
 
         logger.log(u"KAT _get_episode_search_strings for %s is returning %s" % (repr(ep_obj), repr(params)), logger.DEBUG)
-        lang = ep_obj.show.audio_lang
+        if french:
+            lang='fr'
+        else:
+            lang = ep_obj.show.audio_lang
         return to_return
 
     def getURL(self, url, headers=None):
@@ -180,7 +183,7 @@ class KATProvider(generic.TorrentProvider):
             logger.log(u"Unknown exception while loading URL " + url + ": " + traceback.format_exc(), logger.ERROR)
             return None
 
-    def _doSearch(self, search_params, show=None, season=None):
+    def _doSearch(self, search_params, show=None, season=None, french=None):
         # First run a search using the advanced format -- results are probably more reliable, but often not available for several weeks
         # http://kat.ph/usearch/%22james%20may%22%20season:1%20episode:1%20verified:1/?rss=1
         def advancedEpisodeParamBuilder(params):
@@ -191,7 +194,7 @@ class KATProvider(generic.TorrentProvider):
                 episodeParam = episodeParam + 'season:' + str(params.pop('season')) +"%20"
             if 'episode' in params:
                 episodeParam = episodeParam + 'episode:' + str(params.pop('episode')) +"%20"
-            if str(lang)=="fr":
+            if str(lang)=="fr" or french:
                 episodeParam = episodeParam + ' french'
             return episodeParam
         searchURL = self._buildSearchURL(advancedEpisodeParamBuilder, search_params);
@@ -209,7 +212,7 @@ class KATProvider(generic.TorrentProvider):
                 episodeParam = episodeParam + 'S' + str(params.pop('season')).zfill(2)
                 if 'episode' in params:
                     episodeParam += 'E' + str(params.pop('episode')).zfill(2)
-                if str(lang)=="fr":
+                if str(lang)=="fr" or french:
                     episodeParam = episodeParam + ' french'
                 return episodeParam
             searchURL = self._buildSearchURL(fuzzyEpisodeParamBuilder, search_params);
diff --git a/sickbeard/providers/newznab.py b/sickbeard/providers/newznab.py
index e085c774c..378266a1f 100644
--- a/sickbeard/providers/newznab.py
+++ b/sickbeard/providers/newznab.py
@@ -110,7 +110,7 @@ class NewznabProvider(generic.NZBProvider):
 
         return to_return
 
-    def _get_episode_search_strings(self, ep_obj):
+    def _get_episode_search_strings(self, ep_obj, french=None):
         showNames = show_name_helpers.allPossibleShowNames(ep_obj.show)
         for show_name in showNames:
             ep_obj.show.sname=show_name
@@ -122,9 +122,13 @@ class NewznabProvider(generic.NZBProvider):
         # search directly by tvrage id
             if ep_obj.show.tvrid:
                 params['rid'] = ep_obj.show.tvrid
+                if ep_obj.show.audio_lang=="fr" or french:
+                    params['q'] = "french"
+                else:
+                    params['q'] = helpers.sanitizeSceneName(ep_obj.show.sname)
         # if we can't then fall back on a very basic name search
             else:
-                if ep_obj.show.audio_lang=="fr":
+                if ep_obj.show.audio_lang=="fr" or french:
                     params['q'] = helpers.sanitizeSceneName(ep_obj.show.sname) + " french"
                 else:
                     params['q'] = helpers.sanitizeSceneName(ep_obj.show.sname)
@@ -195,10 +199,10 @@ class NewznabProvider(generic.NZBProvider):
 
         return True
 
-    def _doSearch(self, search_params, show=None, max_age=0, season=None):
+    def _doSearch(self, search_params, show=None, max_age=0, season=None, french=None):
         
         cat = '5030,5040'
-        if show and show.audio_lang != u"en":
+        if (show and show.audio_lang != u"en") or french:
             cat = '5020'
 
         params = {"t": "tvsearch",
diff --git a/sickbeard/providers/piratebay/__init__.py b/sickbeard/providers/piratebay/__init__.py
index f1fd78e6e..35ffe0d9d 100644
--- a/sickbeard/providers/piratebay/__init__.py
+++ b/sickbeard/providers/piratebay/__init__.py
@@ -176,7 +176,7 @@ class ThePirateBayProvider(generic.TorrentProvider):
         
         return [search_string]
 
-    def _get_episode_search_strings(self, ep_obj):
+    def _get_episode_search_strings(self, ep_obj,french=None):
        
         search_string = {'Episode': []}
        
@@ -198,7 +198,7 @@ class ThePirateBayProvider(generic.TorrentProvider):
     
         return [search_string]
 
-    def _doSearch(self, search_params, show=None, season=None):
+    def _doSearch(self, search_params, show=None, season=None, french=None):
     
         results = []
         items = {'Season': [], 'Episode': []}
diff --git a/sickbeard/providers/t411.py b/sickbeard/providers/t411.py
index dc332ab98..0074b2b76 100644
--- a/sickbeard/providers/t411.py
+++ b/sickbeard/providers/t411.py
@@ -45,10 +45,10 @@ class T411Provider(generic.TorrentProvider):
     def isEnabled(self):
         return sickbeard.T411
     
-    def getSearchParams(self, searchString, audio_lang, subcat):
-        if audio_lang == "en":
+    def getSearchParams(self, searchString, audio_lang, subcat, french):
+        if audio_lang == "en" and french==None:
             return urllib.urlencode( {'search': searchString, 'cat' : 210, 'submit' : 'Recherche', 'subcat': subcat } ) + "&term%5B17%5D%5B%5D=540&term%5B17%5D%5B%5D=721"
-        elif audio_lang == "fr":
+        elif audio_lang == "fr" or french:
             return urllib.urlencode( {'search': searchString, 'cat' : 210, 'submit' : 'Recherche', 'subcat': subcat } ) + "&term%5B17%5D%5B%5D=541&term%5B17%5D%5B%5D=542"
         else:
             return urllib.urlencode( {'search': searchString, 'cat' : 210, 'submit' : 'Recherche', 'subcat': subcat } )
@@ -77,24 +77,24 @@ class T411Provider(generic.TorrentProvider):
             results.append( self.getSearchParams(showName + " saison %02d" % season, show.audio_lang, 634 ))
         return results
 
-    def _get_episode_search_strings(self, ep_obj):
+    def _get_episode_search_strings(self, ep_obj, french=None):
 
         showNames = show_name_helpers.allPossibleShowNames(ep_obj.show)
         results = []
         for showName in showNames:
-            results.append( self.getSearchParams( "%s S%02dE%02d" % ( showName, ep_obj.season, ep_obj.episode), ep_obj.show.audio_lang, 433 ))
+            results.append( self.getSearchParams( "%s S%02dE%02d" % ( showName, ep_obj.season, ep_obj.episode), ep_obj.show.audio_lang, 433, french ))
             if (int(ep_obj.season) < 31 and int(ep_obj.episode) < 61):
-                results.append( self.getSearchParams( showName, ep_obj.show.audio_lang, 433)+ "&" + urllib.urlencode({'term[46][]': self.episodeValue(ep_obj.episode), 'term[45][]': self.seasonValue(ep_obj.season)}))
+                results.append( self.getSearchParams( showName, ep_obj.show.audio_lang, 433, french)+ "&" + urllib.urlencode({'term[46][]': self.episodeValue(ep_obj.episode), 'term[45][]': self.seasonValue(ep_obj.season)}))
             #results.append( self.getSearchParams( "%s %dx%d" % ( showName, ep_obj.season, ep_obj.episode ), ep_obj.show.audio_lang , 433 )) MAY RETURN 1x12 WHEN SEARCHING 1x1
-            results.append( self.getSearchParams( "%s %dx%02d" % ( showName, ep_obj.season, ep_obj.episode ), ep_obj.show.audio_lang, 433 ))
-            results.append( self.getSearchParams( "%s S%02dE%02d" % ( showName, ep_obj.season, ep_obj.episode), ep_obj.show.audio_lang, 637 ))
+            results.append( self.getSearchParams( "%s %dx%02d" % ( showName, ep_obj.season, ep_obj.episode ), ep_obj.show.audio_lang, 433, french ))
+            results.append( self.getSearchParams( "%s S%02dE%02d" % ( showName, ep_obj.season, ep_obj.episode), ep_obj.show.audio_lang, 637, french ))
             if (int(ep_obj.season) < 31 and int(ep_obj.episode) < 61):
-                results.append( self.getSearchParams( showName, ep_obj.show.audio_lang, 637)+ "&" + urllib.urlencode({'term[46][]': self.episodeValue(ep_obj.episode), 'term[45][]': self.seasonValue(ep_obj.season)}))
+                results.append( self.getSearchParams( showName, ep_obj.show.audio_lang, 637, french)+ "&" + urllib.urlencode({'term[46][]': self.episodeValue(ep_obj.episode), 'term[45][]': self.seasonValue(ep_obj.season)}))
             #results.append( self.getSearchParams( "%s %dx%d" % ( showName, ep_obj.season, ep_obj.episode ), ep_obj.show.audio_lang, 637 ))
-            results.append( self.getSearchParams( "%s %dx%02d" % ( showName, ep_obj.season, ep_obj.episode ), ep_obj.show.audio_lang, 637 ))
-            results.append( self.getSearchParams( "%s S%02dE%02d" % ( showName, ep_obj.season, ep_obj.episode), ep_obj.show.audio_lang, 634))
+            results.append( self.getSearchParams( "%s %dx%02d" % ( showName, ep_obj.season, ep_obj.episode ), ep_obj.show.audio_lang, 637, french ))
+            results.append( self.getSearchParams( "%s S%02dE%02d" % ( showName, ep_obj.season, ep_obj.episode), ep_obj.show.audio_lang, 634, french))
             #results.append( self.getSearchParams( "%s %dx%d" % ( showName, ep_obj.season, ep_obj.episode ), ep_obj.show.audio_lang, 634 ))
-            results.append( self.getSearchParams( "%s %dx%02d" % ( showName, ep_obj.season, ep_obj.episode ), ep_obj.show.audio_lang, 634 ))
+            results.append( self.getSearchParams( "%s %dx%02d" % ( showName, ep_obj.season, ep_obj.episode ), ep_obj.show.audio_lang, 634, french ))
         return results
     
     def _get_title_and_url(self, item):
@@ -108,7 +108,7 @@ class T411Provider(generic.TorrentProvider):
         data = urllib.urlencode({'login': login, 'password' : password, 'submit' : 'Connexion', 'remember': 1, 'url' : '/'})
         self.opener.open(self.url + '/users/login', data)
     
-    def _doSearch(self, searchString, show=None, season=None):
+    def _doSearch(self, searchString, show=None, season=None, french=None):
         
         if not self.login_done:
             self._doLogin( sickbeard.T411_USERNAME, sickbeard.T411_PASSWORD )
@@ -133,8 +133,10 @@ class T411Provider(generic.TorrentProvider):
                 if quality==Quality.UNKNOWN and title:
                     if '720p' not in title.lower() and '1080p' not in title.lower():
                         quality=Quality.SDTV
-                if show:
+                if show and french==None:
                     results.append( T411SearchResult( self.opener, link['title'], downloadURL, quality, str(show.audio_lang) ) )
+                elif show and french:
+                    results.append( T411SearchResult( self.opener, link['title'], downloadURL, quality, 'fr' ) )
                 else:
                     results.append( T411SearchResult( self.opener, link['title'], downloadURL, quality ) )
                 
diff --git a/sickbeard/scheduler.py b/sickbeard/scheduler.py
index 92a59c85d..d175faced 100644
--- a/sickbeard/scheduler.py
+++ b/sickbeard/scheduler.py
@@ -70,7 +70,10 @@ class Scheduler:
                 try:
                     if not self.silent:
                         logger.log(u"Starting new thread: "+self.threadName, logger.DEBUG)
-                    self.action.run()
+                    if self.threadName == 'FINDFRENCH':
+                        self.action.__init__()
+                    else:
+                        self.action.run()
                 except Exception, e:
                     logger.log(u"Exception generated in thread "+self.threadName+": " + ex(e), logger.ERROR)
                     logger.log(repr(traceback.format_exc()), logger.DEBUG)
diff --git a/sickbeard/search_queue.py b/sickbeard/search_queue.py
index 9549d7b01..41654d6a2 100644
--- a/sickbeard/search_queue.py
+++ b/sickbeard/search_queue.py
@@ -227,7 +227,7 @@ class BacklogQueueItem(generic_queue.QueueItem):
                 highestBestQuality = 0
 
             # if we need a better one then say yes
-            if (curStatus in (common.DOWNLOADED, common.SNATCHED, common.SNATCHED_PROPER) and curQuality < highestBestQuality) or curStatus == common.WANTED:
+            if (curStatus in (common.DOWNLOADED, common.SNATCHED, common.SNATCHED_PROPER, common.SNATCHED_FRENCH) and curQuality < highestBestQuality) or curStatus == common.WANTED:
                 wantSeason = True
                 break
 
diff --git a/sickbeard/show_queue.py b/sickbeard/show_queue.py
index 56c290622..eb2056d1c 100644
--- a/sickbeard/show_queue.py
+++ b/sickbeard/show_queue.py
@@ -140,6 +140,14 @@ class ShowQueue(generic_queue.GenericQueue):
         self.add_item(queueItemObj)
 
         return queueItemObj
+    
+    def searchFrench(self, show, force=False):
+
+        queueItemObj = QueueItemsearchFrench(show)
+
+        self.add_item(queueItemObj)
+
+        return queueItemObj
 
     def addShow(self, tvdb_id, showDir, default_status=None, quality=None, flatten_folders=None, lang="fr", subtitles=None, audio_lang=None):
         queueItemObj = QueueItemAdd(tvdb_id, showDir, default_status, quality, flatten_folders, lang, subtitles, audio_lang)
@@ -157,6 +165,7 @@ class ShowQueueActions:
     RENAME = 5
     SUBTITLE=6
     SUBTITLE_CLEAN=7
+    FRENCH_SEARCH=8
     
     names = {REFRESH: 'Refresh',
                     ADD: 'Add',
@@ -165,6 +174,7 @@ class ShowQueueActions:
                     RENAME: 'Rename',
                     SUBTITLE: 'Subtitle',
                     SUBTITLE_CLEAN: 'Subtitle Cleaning',
+                    FRENCH_SEARCH: 'French Search',
                     }
 
 class ShowQueueItem(generic_queue.QueueItem):
@@ -285,6 +295,7 @@ class QueueItemAdd(ShowQueueItem):
             self.show.quality = self.quality if self.quality else sickbeard.QUALITY_DEFAULT
             self.show.flatten_folders = self.flatten_folders if self.flatten_folders != None else sickbeard.FLATTEN_FOLDERS_DEFAULT
             self.show.paused = 0
+            self.show.frenchsearch = 0
 
             # be smartish about this
             if self.show.genre and "talk show" in self.show.genre.lower():
@@ -465,6 +476,19 @@ class QueueItemCleanSubtitle(ShowQueueItem):
 
         self.inProgress = False
 
+class QueueItemsearchFrench(ShowQueueItem):
+    def __init__(self, show=None):
+        ShowQueueItem.__init__(self, ShowQueueActions.FRENCH_SEARCH, show)
+
+    def execute(self):
+
+        ShowQueueItem.execute(self)
+
+        logger.log(u"Searching french episodes for "+self.show.name)
+
+        self.show.searchFrench(self.show.tvdbid)
+
+        self.inProgress = False
 class QueueItemUpdate(ShowQueueItem):
     def __init__(self, show=None):
         ShowQueueItem.__init__(self, ShowQueueActions.UPDATE, show)
diff --git a/sickbeard/subtitles.py b/sickbeard/subtitles.py
index a2957162a..0ec658c9c 100644
--- a/sickbeard/subtitles.py
+++ b/sickbeard/subtitles.py
@@ -106,7 +106,7 @@ class SubtitlesFinder():
         myDB = db.DBConnection()
         today = datetime.date.today().toordinal()
         # you have 5 minutes to understand that one. Good luck
-        sqlResults = myDB.select('SELECT s.show_name, e.showid, e.season, e.episode, e.status, e.subtitles, e.subtitles_searchcount AS searchcount, e.subtitles_lastsearch AS lastsearch, e.location, (? - e.airdate) AS airdate_daydiff FROM tv_episodes AS e INNER JOIN tv_shows AS s ON (e.showid = s.tvdb_id) WHERE s.subtitles = 1 AND e.subtitles NOT LIKE (?) AND ((e.subtitles_searchcount <= 2 AND (? - e.airdate) > 7) OR (e.subtitles_searchcount <= 9 AND (? - e.airdate) <= 7)) AND (e.status IN ('+','.join([str(x) for x in Quality.DOWNLOADED])+') OR (e.status IN ('+','.join([str(x) for x in Quality.SNATCHED + Quality.SNATCHED_PROPER])+') AND e.location != ""))', [today, wantedLanguages(True), today, today])
+        sqlResults = myDB.select('SELECT s.show_name, e.showid, e.season, e.episode, e.status, e.subtitles, e.subtitles_searchcount AS searchcount, e.subtitles_lastsearch AS lastsearch, e.location, (? - e.airdate) AS airdate_daydiff FROM tv_episodes AS e INNER JOIN tv_shows AS s ON (e.showid = s.tvdb_id) WHERE s.subtitles = 1 AND e.subtitles NOT LIKE (?) AND ((e.subtitles_searchcount <= 2 AND (? - e.airdate) > 7) OR (e.subtitles_searchcount <= 9 AND (? - e.airdate) <= 7)) AND (e.status IN ('+','.join([str(x) for x in Quality.DOWNLOADED])+') OR (e.status IN ('+','.join([str(x) for x in Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.SNATCHED_FRENCH ])+') AND e.location != ""))', [today, wantedLanguages(True), today, today])
         if len(sqlResults) == 0:
             logger.log('No subtitles to download', logger.MESSAGE)
             return
diff --git a/sickbeard/traktWatchListChecker.py b/sickbeard/traktWatchListChecker.py
index ead7fefba..fb884499a 100644
--- a/sickbeard/traktWatchListChecker.py
+++ b/sickbeard/traktWatchListChecker.py
@@ -23,7 +23,7 @@ from sickbeard import encodingKludge as ek
 from sickbeard import logger,db
 from sickbeard import helpers
 from sickbeard import search_queue
-from sickbeard.common import SNATCHED, SNATCHED_PROPER, DOWNLOADED, SKIPPED, UNAIRED, IGNORED, ARCHIVED, WANTED, UNKNOWN
+from sickbeard.common import SNATCHED, SNATCHED_PROPER, SNATCHED_FRENCH, DOWNLOADED, SKIPPED, UNAIRED, IGNORED, ARCHIVED, WANTED, UNKNOWN
 from lib.trakt import *
 
 class TraktChecker():
diff --git a/sickbeard/tv.py b/sickbeard/tv.py
index e2dad314f..bc605c48c 100644
--- a/sickbeard/tv.py
+++ b/sickbeard/tv.py
@@ -33,6 +33,8 @@ import xml.etree.cElementTree as etree
 
 from name_parser.parser import NameParser, InvalidNameException
 
+from frenchFinder import FrenchFinder
+
 from lib import subliminal
 
 from lib.tidysub import cleaner
@@ -54,7 +56,7 @@ from sickbeard import history
 from sickbeard import encodingKludge as ek
 
 from common import Quality, Overview
-from common import DOWNLOADED, SNATCHED, SNATCHED_PROPER, ARCHIVED, IGNORED, UNAIRED, WANTED, SKIPPED, UNKNOWN
+from common import DOWNLOADED, SNATCHED, SNATCHED_PROPER, SNATCHED_FRENCH, ARCHIVED, IGNORED, UNAIRED, WANTED, SKIPPED, UNKNOWN
 from common import NAMING_DUPLICATE, NAMING_EXTEND, NAMING_LIMITED_EXTEND, NAMING_SEPARATED_REPEAT, NAMING_LIMITED_EXTEND_E_PREFIXED
 
 
@@ -80,6 +82,7 @@ class TVShow(object):
         self.airs = ""
         self.startyear = 0
         self.paused = 0
+        self.frenchsearch = 0
         self.air_by_date = 0
         self.subtitles = int(sickbeard.SUBTITLES_DEFAULT if sickbeard.SUBTITLES_DEFAULT else 0)
         self.lang = lang
@@ -564,11 +567,11 @@ class TVShow(object):
                     newStatus = DOWNLOADED
 
                 # if it was snatched proper and we found a higher quality one then allow the status change
-                elif oldStatus == SNATCHED_PROPER and oldQuality < newQuality:
+                elif (oldStatus == SNATCHED_PROPER or oldStatus == SNATCHED_FRENCH) and oldQuality < newQuality:
                     logger.log(u"STATUS: this ep used to be snatched proper with quality "+Quality.qualityStrings[oldQuality]+" but a file exists with quality "+Quality.qualityStrings[newQuality]+" so I'm setting the status to DOWNLOADED", logger.DEBUG)
                     newStatus = DOWNLOADED
 
-                elif oldStatus not in (SNATCHED, SNATCHED_PROPER):
+                elif oldStatus not in (SNATCHED, SNATCHED_PROPER, SNATCHED_FRENCH):
                     newStatus = DOWNLOADED
 
                 if newStatus != None:
@@ -634,7 +637,7 @@ class TVShow(object):
             self.quality = int(sqlResults[0]["quality"])
             self.flatten_folders = int(sqlResults[0]["flatten_folders"])
             self.paused = int(sqlResults[0]["paused"])
-
+            self.frenchsearch = int(sqlResults[0]["frenchsearch"])
             self._location = sqlResults[0]["location"]
 
             if self.tvrid == 0:
@@ -952,6 +955,11 @@ class TVShow(object):
         except Exception as e:
             logger.log("Error occurred when cleaning subtitles: " + str(e), logger.DEBUG)
             return
+    
+    def searchFrench(self, show):
+        logger.log("Sending french episodes search")
+        FrenchFinder('force',show)
+        return
 
     def saveToDB(self):
         logger.log(str(self.tvdbid) + ": Saving show info to database", logger.DEBUG)
@@ -969,6 +977,7 @@ class TVShow(object):
                         "airs": self.airs,
                         "status": self.status,
                         "flatten_folders": self.flatten_folders,
+                        "frenchsearch":self.frenchsearch,
                         "paused": self.paused,
                         "air_by_date": self.air_by_date,
                         "subtitles": self.subtitles,
@@ -1048,7 +1057,7 @@ class TVShow(object):
         curStatus, curQuality = Quality.splitCompositeStatus(epStatus)
 
         # if we are re-downloading then we only want it if it's in our bestQualities list and better than what we have
-        if curStatus in Quality.DOWNLOADED + Quality.SNATCHED + Quality.SNATCHED_PROPER and quality in bestQualities and quality > curQuality:
+        if curStatus in Quality.DOWNLOADED + Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.SNATCHED_FRENCH and quality in bestQualities and quality > curQuality:
             logger.log(u"We already have this ep but the new one is better quality, saying yes", logger.DEBUG)
             return True
 
@@ -1066,7 +1075,7 @@ class TVShow(object):
             return Overview.SKIPPED
         elif epStatus == ARCHIVED:
             return Overview.GOOD
-        elif epStatus in Quality.DOWNLOADED + Quality.SNATCHED + Quality.SNATCHED_PROPER:
+        elif epStatus in Quality.DOWNLOADED + Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.SNATCHED_FRENCH:
 
             anyQualities, bestQualities = Quality.splitQuality(self.quality) #@UnusedVariable
             if bestQualities:
@@ -1076,7 +1085,7 @@ class TVShow(object):
 
             epStatus, curQuality = Quality.splitCompositeStatus(epStatus)
     
-            if epStatus in (SNATCHED, SNATCHED_PROPER):
+            if epStatus in (SNATCHED, SNATCHED_PROPER, SNATCHED_FRENCH):
                 return Overview.SNATCHED
             # if they don't want re-downloads then we call it good if they have anything
             elif maxBestQuality == None:
@@ -1488,7 +1497,7 @@ class TVEpisode(object):
         if not ek.ek(os.path.isfile, self.location):
 
             # if we don't have the file
-            if self.airdate >= datetime.date.today() and self.status not in Quality.SNATCHED + Quality.SNATCHED_PROPER:
+            if self.airdate >= datetime.date.today() and self.status not in Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.SNATCHED_FRENCH:
                 # and it hasn't aired yet set the status to UNAIRED
                 logger.log(u"Episode airs in the future, changing status from " + str(self.status) + " to " + str(UNAIRED), logger.DEBUG)
                 self.status = UNAIRED
@@ -1514,7 +1523,7 @@ class TVEpisode(object):
         # if we have a media file then it's downloaded
         elif sickbeard.helpers.isMediaFile(self.location):
             # leave propers alone, you have to either post-process them or manually change them back
-            if self.status not in Quality.SNATCHED_PROPER + Quality.DOWNLOADED + Quality.SNATCHED + [ARCHIVED]:
+            if self.status not in Quality.SNATCHED_FRENCH + Quality.SNATCHED_PROPER + Quality.DOWNLOADED + Quality.SNATCHED + [ARCHIVED]:
                 logger.log(u"5 Status changes from " + str(self.status) + " to " + str(Quality.statusFromName(self.location)), logger.DEBUG)
                 self.status = Quality.statusFromName(self.location)
 
diff --git a/sickbeard/webapi.py b/sickbeard/webapi.py
index 5ff4e2d1e..08be5696c 100644
--- a/sickbeard/webapi.py
+++ b/sickbeard/webapi.py
@@ -34,7 +34,7 @@ from sickbeard import db, logger, exceptions, history, ui, helpers
 from sickbeard.exceptions import ex
 from sickbeard import encodingKludge as ek
 from sickbeard import search_queue
-from sickbeard.common import SNATCHED, SNATCHED_PROPER, DOWNLOADED, SKIPPED, UNAIRED, IGNORED, ARCHIVED, WANTED, UNKNOWN
+from sickbeard.common import SNATCHED, SNATCHED_PROPER, SNATCHED_FRENCH, DOWNLOADED, SKIPPED, UNAIRED, IGNORED, ARCHIVED, WANTED, UNKNOWN
 from common import Quality, qualityPresetStrings, statusStrings
 from sickbeard import image_cache
 from lib.tvdb_api import tvdb_api, tvdb_exceptions
@@ -2222,7 +2222,7 @@ class CMD_ShowStats(ApiCall):
         episode_status_counts_total = {}
         episode_status_counts_total["total"] = 0
         for status in statusStrings.statusStrings.keys():
-            if status in [UNKNOWN, DOWNLOADED, SNATCHED, SNATCHED_PROPER]:
+            if status in [UNKNOWN, DOWNLOADED, SNATCHED, SNATCHED_PROPER, SNATCHED_FRENCH]:
                 continue
             episode_status_counts_total[status] = 0
 
@@ -2238,7 +2238,7 @@ class CMD_ShowStats(ApiCall):
         # add all snatched qualities
         episode_qualities_counts_snatch = {}
         episode_qualities_counts_snatch["total"] = 0
-        for statusCode in Quality.SNATCHED + Quality.SNATCHED_PROPER:
+        for statusCode in Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.SNATCHED_FRENCH:
             status, quality = Quality.splitCompositeStatus(statusCode)
             if quality in [Quality.NONE]:
                 continue
@@ -2255,7 +2255,7 @@ class CMD_ShowStats(ApiCall):
             if status in Quality.DOWNLOADED:
                 episode_qualities_counts_download["total"] += 1
                 episode_qualities_counts_download[int(row["status"])] += 1
-            elif status in Quality.SNATCHED + Quality.SNATCHED_PROPER:
+            elif status in Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.SNATCHED_FRENCH:
                 episode_qualities_counts_snatch["total"] += 1
                 episode_qualities_counts_snatch[int(row["status"])] += 1
             elif status == 0: # we dont count NONE = 0 = N/A
@@ -2396,7 +2396,7 @@ class CMD_ShowsStats(ApiCall):
         stats["shows_total"] = len(sickbeard.showList)
         stats["shows_active"] = len([show for show in sickbeard.showList if show.paused == 0 and show.status != "Ended"])
         stats["ep_downloaded"] = myDB.select("SELECT COUNT(*) FROM tv_episodes WHERE status IN (" + ",".join([str(show) for show in Quality.DOWNLOADED + [ARCHIVED]]) + ") AND season != 0 and episode != 0 AND airdate <= " + today + "")[0][0]
-        stats["ep_total"] = myDB.select("SELECT COUNT(*) FROM tv_episodes WHERE season != 0 and episode != 0 AND (airdate != 1 OR status IN (" + ",".join([str(show) for show in (Quality.DOWNLOADED + Quality.SNATCHED + Quality.SNATCHED_PROPER) + [ARCHIVED]]) + ")) AND airdate <= " + today + " AND status != " + str(IGNORED) + "")[0][0]
+        stats["ep_total"] = myDB.select("SELECT COUNT(*) FROM tv_episodes WHERE season != 0 and episode != 0 AND (airdate != 1 OR status IN (" + ",".join([str(show) for show in (Quality.DOWNLOADED + Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.SNATCHED_FRENCH) + [ARCHIVED]]) + ")) AND airdate <= " + today + " AND status != " + str(IGNORED) + "")[0][0]
 
         myDB.connection.close()
         return _responds(RESULT_SUCCESS, stats)
diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py
index d941c248d..536adf6ef 100644
--- a/sickbeard/webserve.py
+++ b/sickbeard/webserve.py
@@ -226,7 +226,7 @@ class Manage:
 
         status_list = [int(whichStatus)]
         if status_list[0] == SNATCHED:
-            status_list = Quality.SNATCHED + Quality.SNATCHED_PROPER
+            status_list = Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.SNATCHED_FRENCH
 
         cur_show_results = myDB.select("SELECT season, episode, name FROM tv_episodes WHERE showid = ? AND season != 0 AND status IN ("+','.join(['?']*len(status_list))+")", [int(tvdb_id)] + status_list)
 
@@ -249,7 +249,7 @@ class Manage:
             whichStatus = int(whichStatus)
             status_list = [whichStatus]
             if status_list[0] == SNATCHED:
-                status_list = Quality.SNATCHED + Quality.SNATCHED_PROPER
+                status_list = Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.SNATCHED_FRENCH
         else:
             status_list = []
 
@@ -288,7 +288,7 @@ class Manage:
 
         status_list = [int(oldStatus)]
         if status_list[0] == SNATCHED:
-            status_list = Quality.SNATCHED + Quality.SNATCHED_PROPER
+            status_list = Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.SNATCHED_FRENCH
 
         to_change = {}
 
@@ -996,7 +996,7 @@ class ConfigSearch:
     @cherrypy.expose
     def saveSearch(self, use_nzbs=None, use_torrents=None, nzb_dir=None, sab_username=None, sab_password=None,
                        sab_apikey=None, sab_category=None, sab_host=None, nzbget_password=None, nzbget_category=None, nzbget_host=None,
-                       torrent_dir=None,torrent_method=None, nzb_method=None, usenet_retention=None, search_frequency=None, download_propers=None, torrent_username=None, torrent_password=None, torrent_host=None, torrent_label=None, torrent_path=None, 
+                       torrent_dir=None,torrent_method=None, nzb_method=None, usenet_retention=None, search_frequency=None, download_propers=None, download_french=None, torrent_username=None, torrent_password=None, torrent_host=None, torrent_label=None, torrent_path=None, 
                        torrent_ratio=None, torrent_paused=None, ignore_words=None, prefered_method=None, torrent_use_ftp = None, ftp_host=None, ftp_port=None, ftp_timeout=None, ftp_passive = None, ftp_login=None, ftp_password=None, ftp_remotedir=None):
 
         results = []
@@ -1013,7 +1013,11 @@ class ConfigSearch:
             download_propers = 1
         else:
             download_propers = 0
-
+        
+        if download_french == "on":
+            download_french = 1
+        else:
+            download_french = 0
         if use_nzbs == "on":
             use_nzbs = 1
         else:
@@ -1047,6 +1051,7 @@ class ConfigSearch:
         sickbeard.IGNORE_WORDS = ignore_words
         
         sickbeard.DOWNLOAD_PROPERS = download_propers
+        sickbeard.DOWNLOAD_FRENCH = download_french
 
         sickbeard.SAB_USERNAME = sab_username
         sickbeard.SAB_PASSWORD = sab_password
@@ -2905,6 +2910,7 @@ class Home:
                 t.submenu.append({ 'title': 'Force Full Update',    'path': 'home/updateShow?show=%d&amp;force=1'%showObj.tvdbid })
                 t.submenu.append({ 'title': 'Update show in XBMC',  'path': 'home/updateXBMC?showName=%s'%urllib.quote_plus(showObj.name.encode('utf-8')), 'requires': haveXBMC })
                 t.submenu.append({ 'title': 'Preview Rename',       'path': 'home/testRename?show=%d'%showObj.tvdbid })
+                t.submenu.append({ 'title': 'French Search',       'path': 'home/frenchSearch?show=%d'%showObj.tvdbid })
                 if sickbeard.USE_SUBTITLES and not sickbeard.showQueueScheduler.action.isBeingSubtitled(showObj) and not sickbeard.showQueueScheduler.action.isBeingCleanedSubtitle(showObj) and showObj.subtitles:
                     t.submenu.append({ 'title': 'Download Subtitles', 'path': 'home/subtitleShow?show=%d'%showObj.tvdbid })
                     t.submenu.append({ 'title': 'Clean Subtitles', 'path': 'home/subtitleShowClean?show=%d'%showObj.tvdbid })
@@ -2950,7 +2956,7 @@ class Home:
         return result['description'] if result else 'Episode not found.'
 
     @cherrypy.expose
-    def editShow(self, show=None, location=None, anyQualities=[], bestQualities=[], exceptions_list=[], flatten_folders=None, paused=None, directCall=False, air_by_date=None, tvdbLang=None, audio_lang=None, subtitles=None):
+    def editShow(self, show=None, location=None, anyQualities=[], bestQualities=[], exceptions_list=[], flatten_folders=None, paused=None, frenchsearch=None, directCall=False, air_by_date=None, tvdbLang=None, audio_lang=None, subtitles=None):
 
         if show == None:
             errString = "Invalid show ID: "+str(show)
@@ -2990,6 +2996,11 @@ class Home:
             paused = 1
         else:
             paused = 0
+            
+        if frenchsearch == "on":
+            frenchsearch = 1
+        else:
+            frenchsearch = 0
 
         if air_by_date == "on":
             air_by_date = 1
@@ -3047,6 +3058,7 @@ class Home:
             showObj.paused = paused
             showObj.air_by_date = air_by_date
             showObj.subtitles = subtitles
+            showObj.frenchsearch = frenchsearch
             showObj.lang = tvdb_lang
             showObj.audio_lang = audio_lang
 
@@ -3198,6 +3210,25 @@ class Home:
 
         redirect("/home/displayShow?show="+str(showObj.tvdbid))
     
+    @cherrypy.expose
+    
+    def frenchSearch(self, show=None, force=0):
+
+        if show == None:
+            return _genericMessage("Error", "Invalid show ID")
+
+        showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(show))
+
+        if showObj == None:
+            return _genericMessage("Error", "Unable to find the specified show")
+
+        # search and download subtitles
+        sickbeard.showQueueScheduler.action.searchFrench(showObj, bool(force)) #@UndefinedVariable
+
+        time.sleep(3)
+
+        redirect("/home/displayShow?show="+str(showObj.tvdbid))
+    
     @cherrypy.expose
     def updateXBMC(self, showName=None):
         if sickbeard.XBMC_UPDATE_ONLYFIRST:
@@ -3280,7 +3311,7 @@ class Home:
                         logger.log(u"Refusing to change status of "+curEp+" because it is UNAIRED", logger.ERROR)
                         continue
 
-                    if int(status) in Quality.DOWNLOADED and epObj.status not in Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.DOWNLOADED + [IGNORED] and not ek.ek(os.path.isfile, epObj.location):
+                    if int(status) in Quality.DOWNLOADED and epObj.status not in Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.SNATCHED_FRENCH + Quality.DOWNLOADED + [IGNORED] and not ek.ek(os.path.isfile, epObj.location):
                         logger.log(u"Refusing to change status of "+curEp+" to DOWNLOADED because it's not SNATCHED/DOWNLOADED", logger.ERROR)
                         continue
 
-- 
GitLab