diff --git a/.gitignore b/.gitignore
index f081a23927ace05633476a242501b46c4c126988..2698b0051deb51173074222a3a739dec1de4e6de 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,7 +2,7 @@
 ######################
 cache/*
 cache.db*
-config.ini
+config.ini*
 Logs/*
 sickbeard.db*
 autoProcessTV/autoProcessTV.cfg
diff --git a/data/interfaces/default/apiBuilder.tmpl b/data/interfaces/default/apiBuilder.tmpl
index f6a1dd5570d9bc10f8ad170648497af7e0fe9fb2..09d66dc9d7c5e323b64946c636210478599f2cec 100644
--- a/data/interfaces/default/apiBuilder.tmpl
+++ b/data/interfaces/default/apiBuilder.tmpl
@@ -90,8 +90,8 @@ addList("sb.setdefaults-status", "Archived", "&status=archived", "sb.setdefaults
 addList("sb.setdefaults-status", "Ignored", "&status=ignored", "sb.setdefaults-opt");
 
 addOption("sb.setdefaults-opt", "Optional Param", "", 1);
-addList("sb.setdefaults-opt", "No Season Folder", "&season_folder=0", "quality");
-addList("sb.setdefaults-opt", "Use Season Folder", "&season_folder=1", "quality");
+addList("sb.setdefaults-opt", "Flatten (No Season Folder)", "&flatten_folders=1", "quality");
+addList("sb.setdefaults-opt", "Use Season Folder", "&flatten_folders=0", "quality");
 
 addOption("shows", "Optional Param", "", 1);
 addOption("shows", "Show Only Paused", "&paused=1");
@@ -108,8 +108,8 @@ addList("show.addexisting-tvdbid", "101501 (Ancient Aliens)", "&tvdbid=101501",
 addList("show.addexisting-tvdbid", "80348 (Chuck)", "&tvdbid=80348", "show.addexisting-opt");
 
 addOption("show.addexisting-opt", "Optional Param", "", 1);
-addList("show.addexisting-opt", "No Season Folder", "&season_folder=0", "quality");
-addList("show.addexisting-opt", "Use Season Folder", "&season_folder=1", "quality");
+addList("show.addexisting-opt", "Flatten (No Season Folder)", "&flatten_folders=1", "quality");
+addList("show.addexisting-opt", "Use Season Folder", "&flatten_folders=0", "quality");
 
 addList("show.addnew", "101501 (Ancient Aliens)", "&tvdbid=101501", "show.addnew-loc");
 addList("show.addnew", "80348 (Chuck)", "&tvdbid=80348", "show.addnew-loc");
@@ -127,8 +127,8 @@ addList("show.addnew-status", "Archived", "&status=archived", "show.addnew-opt")
 addList("show.addnew-status", "Ignored", "&status=ignored", "show.addnew-opt");
 
 addOption("show.addnew-opt", "Optional Param", "", 1);
-addList("show.addnew-opt", "No Season Folder", "&season_folder=0", "quality");
-addList("show.addnew-opt", "Use Season Folder", "&season_folder=1", "quality");
+addList("show.addnew-opt", "Flatten (No Season Folder)", "&flatten_folders=1", "quality");
+addList("show.addnew-opt", "Use Season Folder", "&flatten_folders=0", "quality");
 
 addOptGroup("sb.searchtvdb", "Search by Name");
 addList("sb.searchtvdb", "Lost", "&name=Lost", "sb.searchtvdb-lang");
@@ -258,15 +258,25 @@ addList("episode.setstatus", "$curShow.name", "&tvdbid=$curShow.tvdbid", "episod
 
 // build out each show's season+episode list for episode.setstatus cmd
 #for $curShow in $episodeSQLResults:
+    #set $curSeason = -1
     #for $curShowSeason in $episodeSQLResults[$curShow]:
+        #if $curShowSeason.season != $curSeason and $curShowSeason.season != 0:
+            // insert just the season as the ep number is now optional
+            addList("episode.setstatus-$curShow", "Season $curShowSeason.season", "&season=$curShowSeason.season", "episode-status-$curShow");
+        #end if
+        #set $curSeason = int($curShowSeason.season)
 addList("episode.setstatus-$curShow", "$curShowSeason.season x $curShowSeason.episode", "&season=$curShowSeason.season&episode=$curShowSeason.episode", "episode-status-$curShow");
     #end for
-addOption("episode-status-$curShow", "Wanted", "&status=wanted");
-addOption("episode-status-$curShow", "Skipped", "&status=skipped");
-addOption("episode-status-$curShow", "Archived", "&status=archived");
-addOption("episode-status-$curShow", "Ignored", "&status=ignored");
+addList("episode-status-$curShow", "Wanted", "&status=wanted", "force");
+addList("episode-status-$curShow", "Skipped", "&status=skipped", "force");
+addList("episode-status-$curShow", "Archived", "&status=archived", "force");
+addList("episode-status-$curShow", "Ignored", "&status=ignored", "force");
 #end for
 
+addOption("force", "Optional Param", "", 1);
+addOption("force", "Replace Downloaded EP", "&force=1");
+addOption("force", "Skip Downloaded EP", "&force=0");
+
 addOption("future", "Optional Param", "", 1);
 addList("future", "Sort by Date", "&sort=date", "future-type");
 addList("future", "Sort by Network", "&sort=network", "future-type");
diff --git a/sickbeard/tv.py b/sickbeard/tv.py
index 06e636531aba8507e64bac58caaa2c0ad1acd8c0..b9b543e4d69d6f5ae7af444559658f35213609fa 100644
--- a/sickbeard/tv.py
+++ b/sickbeard/tv.py
@@ -116,19 +116,20 @@ class TVShow(object):
                 self.episodes[curSeason][curEp] = None
                 del myEp
 
+    def getAllEpisodes(self, season=None):
 
-    def getAllEpisodes(self):
-        
         myDB = db.DBConnection()
-        results = myDB.select("SELECT season, episode FROM tv_episodes WHERE showid = ?", [self.tvdbid])
+        if season == None:
+            results = myDB.select("SELECT season, episode FROM tv_episodes WHERE showid = ?", [self.tvdbid])
+        else:
+            results = myDB.select("SELECT season, episode FROM tv_episodes WHERE showid = ? AND season = ?", [self.tvdbid, season])
 
         ep_list = []
-
         for cur_result in results:
             cur_ep = self.getEpisode(int(cur_result["season"]), int(cur_result["episode"]))
             if cur_ep:
                 ep_list.append(cur_ep)
-        
+
         return ep_list
 
 
diff --git a/sickbeard/webapi.py b/sickbeard/webapi.py
index a7e005e06c03bfbe09073adcf73e21a32f0dcbef..f66f641b87ae8027ba60ec7d210131b48bc5d9ea 100644
--- a/sickbeard/webapi.py
+++ b/sickbeard/webapi.py
@@ -67,7 +67,7 @@ result_type_map = {RESULT_SUCCESS: "success",
 
 class Api:
     """ api class that returns json results """
-    version = 0.2
+    version = 0.3
     intent = 4
 
     @cherrypy.expose
@@ -167,7 +167,7 @@ class Api:
             callback = request.params.get('callback') or request.params.get('jsonp')
             if callback != None:
                 out = callback + '(' + out + ');' # wrap with JSONP call if requested
-        except Exception, e: # if we fail to generate the output fake a error
+        except Exception, e: # if we fail to generate the output fake an error
             logger.log(u"API :: " + traceback.format_exc(), logger.DEBUG)
             out = '{"result":"' + result_type_map[RESULT_ERROR] + '", "message": "error while composing output: "' + ex(e) + '"}'
         return out
@@ -493,6 +493,13 @@ class TVDBShorthandWrapper(ApiCall):
 #     helper functions         #
 ################################
 
+def _sizeof_fmt(num):
+    for x in ['bytes', 'KB', 'MB', 'GB', 'TB']:
+        if num < 1024.00:
+            return "%3.2f %s" % (num, x)
+        num /= 1024.00
+
+
 def _is_int(data):
     try:
         int(data)
@@ -799,7 +806,7 @@ class CMD_Episode(ApiCall):
             return _responds(RESULT_FAILURE, msg="Show not found")
 
         myDB = db.DBConnection(row_type="dict")
-        sqlResults = myDB.select("SELECT name, description, airdate, status, location FROM tv_episodes WHERE showid = ? AND episode = ? AND season = ?", [self.tvdbid, self.e, self.s])
+        sqlResults = myDB.select("SELECT name, description, airdate, status, location, file_size, release_name FROM tv_episodes WHERE showid = ? AND episode = ? AND season = ?", [self.tvdbid, self.e, self.s])
         if not len(sqlResults) == 1:
             raise ApiError("Episode not found")
         episode = sqlResults[0]
@@ -814,7 +821,7 @@ class CMD_Episode(ApiCall):
         if bool(self.fullPath) == True and showPath:
             pass
         elif bool(self.fullPath) == False and showPath:
-            #i am using the length because lstrip removes to much
+            # using the length because lstrip removes to much
             showPathLength = len(showPath) + 1 # the / or \ yeah not that nice i know
             episode["location"] = episode["location"][showPathLength:]
         elif not showPath: # show dir is broken ... episode path will be empty
@@ -824,6 +831,7 @@ class CMD_Episode(ApiCall):
         status, quality = Quality.splitCompositeStatus(int(episode["status"]))
         episode["status"] = _get_status_Strings(status)
         episode["quality"] = _get_quality_string(quality)
+        episode["file_size_human"] = _sizeof_fmt(episode["file_size"])
 
         myDB.connection.close()
         return _responds(RESULT_SUCCESS, episode)
@@ -875,26 +883,29 @@ class CMD_EpisodeSearch(ApiCall):
 
 
 class CMD_EpisodeSetStatus(ApiCall):
-    _help = {"desc": "set status of an episode",
+    _help = {"desc": "set status of an episode or season (when no ep is provided)",
              "requiredParameters": {"tvdbid": {"desc": "thetvdb.com unique id of a show"},
                                    "season": {"desc": "the season number"},
-                                   "episode": {"desc": "the episode number"},
                                    "status": {"desc": "the status values: wanted, skipped, archived, ignored"}
-                                  }
+                                  },
+             "optionalParameters": {"episode": {"desc": "the episode number"},
+                                    "force": {"desc": "should we replace existing (downloaded) episodes or not"}
+                                     }
              }
 
     def __init__(self, args, kwargs):
         # required
         self.tvdbid, args = self.check_params(args, kwargs, "tvdbid", None, True, "int", [])
         self.s, args = self.check_params(args, kwargs, "season", None, True, "int", [])
-        self.e, args = self.check_params(args, kwargs, "episode", None, True, "int", [])
         self.status, args = self.check_params(args, kwargs, "status", None, True, "string", ["wanted", "skipped", "archived", "ignored"])
         # optional
+        self.e, args = self.check_params(args, kwargs, "episode", None, False, "int", [])
+        self.force, args = self.check_params(args, kwargs, "force", 0, False, "bool", [])
         # super, missing, help
         ApiCall.__init__(self, args, kwargs)
 
     def run(self):
-        """ set status of an episode """
+        """ set status of an episode or a season (when no ep is provided) """
         showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(self.tvdbid))
         if not showObj:
             return _responds(RESULT_FAILURE, msg="Show not found")
@@ -904,47 +915,67 @@ class CMD_EpisodeSetStatus(ApiCall):
             if str(statusStrings[status]).lower() == str(self.status).lower():
                 self.status = status
                 break
-        # this should be obsolete bcause of the above
-        if not self.status in statusStrings.statusStrings:
-            return _responds(RESULT_FAILURE, msg="Invalid Status")
+        else: # if we dont break out of the for loop we got here.
+            # the allowed values has at least one item that could not be matched against the internal status strings
+            raise ApiError("The status string could not be matched to a status. Report to Devs!")
 
-        epObj = showObj.getEpisode(int(self.s), int(self.e))
-        if epObj == None:
-            return _responds(RESULT_FAILURE, msg="Episode not found")
-
-        #only allow the status options we want
-        if int(self.status) not in (3, 5, 6, 7):
-            return _responds(RESULT_FAILURE, msg="Show not found")
-
-        segment_list = []
-        if int(self.status) == WANTED:
-            # figure out what segment the episode is in and remember it so we can backlog it
-            if epObj.show.air_by_date:
-                ep_segment = str(epObj.airdate)[:7]
-            else:
-                ep_segment = epObj.season
-
-            if ep_segment not in segment_list:
-                segment_list.append(ep_segment)
+        ep_list = []
+        if self.e:
+            epObj = showObj.getEpisode(self.s, self.e)
+            if epObj == None:
+                return _responds(RESULT_FAILURE, msg="Episode not found")
+            ep_list = [epObj]
+        else:
+            # get all episode numbers frome self,season
+            ep_list = showObj.getAllEpisodes(season=self.s)
+
+        def _epResult(result_code, ep, msg=""):
+            return {'season': ep.season, 'episode': ep.episode, 'status': _get_status_Strings(ep.status), 'result': result_type_map[result_code], 'message': msg}
+
+        ep_results = []
+        failure = False
+        start_backlog = False
+        ep_segment = None
+        for epObj in ep_list:
+            if ep_segment == None and self.status == WANTED:
+                # figure out what segment the episode is in and remember it so we can backlog it
+                if showObj.air_by_date:
+                    ep_segment = str(epObj.airdate)[:7]
+                else:
+                    ep_segment = epObj.season
+
+            with epObj.lock:
+                # don't let them mess up UNAIRED episodes
+                if epObj.status == UNAIRED:
+                    if self.e != None: # setting the status of a unaired is only considert a failure if we directly wanted this episode, but is ignored on a season request
+                        ep_results.append(_epResult(RESULT_FAILURE, epObj, "Refusing to change status because it is UNAIRED"))
+                        failure = True
+                    continue
 
-        with epObj.lock:
-            # don't let them mess up UNAIRED episodes
-            if epObj.status == UNAIRED:
-                return _responds(RESULT_FAILURE, msg="Refusing to change status because it is UNAIRED")
+                # allow the user to force setting the status for an already downloaded episode
+                if epObj.status in Quality.DOWNLOADED and not self.force:
+                    ep_results.append(_epResult(RESULT_FAILURE, epObj, "Refusing to change status because it is already marked as DOWNLOADED"))
+                    failure = True
+                    continue
 
-            if int(self.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):
-                return _responds(RESULT_FAILURE, msg="Refusing to change status to DOWNLOADED because it's not SNATCHED/DOWNLOADED")
+                epObj.status = self.status
+                epObj.saveToDB()
 
-            epObj.status = int(self.status)
-            epObj.saveToDB()
+                if self.status == WANTED:
+                    start_backlog = True
+                ep_results.append(_epResult(RESULT_SUCCESS, epObj))
 
-            for cur_segment in segment_list:
-                cur_backlog_queue_item = search_queue.BacklogQueueItem(showObj, cur_segment)
-                sickbeard.searchQueueScheduler.action.add_item(cur_backlog_queue_item) #@UndefinedVariable
-                logger.log(u"API :: Starting backlog for " + showObj.name + " season " + str(cur_segment) + " because some eps were set to wanted")
-                return _responds(RESULT_SUCCESS, msg="Episode status changed to Wanted, and backlog started")
+        extra_msg = ""
+        if start_backlog:
+            cur_backlog_queue_item = search_queue.BacklogQueueItem(showObj, ep_segment)
+            sickbeard.searchQueueScheduler.action.add_item(cur_backlog_queue_item) #@UndefinedVariable
+            logger.log(u"API :: Starting backlog for " + showObj.name + " season " + str(ep_segment) + " because some episodes were set to WANTED")
+            extra_msg = " Backlog started"
 
-        return _responds(RESULT_SUCCESS, msg="Episode status successfully changed to " + statusStrings[epObj.status])
+        if failure:
+            return _responds(RESULT_FAILURE, ep_results, 'Failed to set all or some status. Check data.' + extra_msg)
+        else:
+            return _responds(RESULT_SUCCESS, msg='All status set successfully.' + extra_msg)
 
 
 class CMD_Exceptions(ApiCall):