diff --git a/data/images/corbeille.png b/data/images/corbeille.png new file mode 100644 index 0000000000000000000000000000000000000000..d057ce92d6a136700cbc91f37e9e76660fba1b30 Binary files /dev/null and b/data/images/corbeille.png differ diff --git a/data/interfaces/default/displayShow.tmpl b/data/interfaces/default/displayShow.tmpl index edfb26c580d4bf6a7b6d3412e3068b30e5660d66..608f63fb5c3e04d6d55f6803cb953b8baef8470d 100644 --- a/data/interfaces/default/displayShow.tmpl +++ b/data/interfaces/default/displayShow.tmpl @@ -89,6 +89,7 @@ <script type="text/javascript" src="$sbRoot/js/plotTooltip.js?$sbPID"></script> <script type="text/javascript" src="$sbRoot/js/ajaxEpSearch.js?$sbPID"></script> <script type="text/javascript" src="$sbRoot/js/ajaxEpSubtitles.js?$sbPID"></script> +<script type="text/javascript" src="$sbRoot/js/ajaxhisttrunc.js?$sbPID"></script> <div class="align-left"><b>Change Show:</b> <div class="navShow"><img id="prevShow" width="16" height="18" src="$sbRoot/images/prev.gif" alt="<<" title="Prev Show" /></div> @@ -209,13 +210,13 @@ Change Audio of selected episodes to #for $epResult in $sqlResults: #if int($epResult["season"]) != $curSeason: - <tr><td colspan="10"><a name="season-$epResult["season"]"></a></td></tr> + <tr><td colspan="11"><a name="season-$epResult["season"]"></a></td></tr> <tr class="seasonheader" id="season-$epResult["season"]"> <td colspan="9"> <h2>#if int($epResult["season"]) == 0 then "Specials" else "Season "+str($epResult["season"])#</h2> </td> </tr> - <tr id="season-$epResult["season"]-cols"><th width="1%"><input type="checkbox" class="seasonCheck" id="$epResult["season"]" /></th><th>NFO</th><th>TBN</th><th>Episode</th><th>Name</th><th class="nowrap">Airdate</th><th>Filename</th><th>Audio</th>#if $sickbeard.USE_SUBTITLES and $show.subtitles then "<th>Subs</th>" else ""#<th>Status</th><th>Search</th></tr> + <tr id="season-$epResult["season"]-cols"><th width="1%"><input type="checkbox" class="seasonCheck" id="$epResult["season"]" /></th><th>NFO</th><th>TBN</th><th>Episode</th><th>Name</th><th class="nowrap">Airdate</th><th>Filename</th><th>Audio</th>#if $sickbeard.USE_SUBTITLES and $show.subtitles then "<th>Subs</th>" else ""#<th>Status</th><th>Search</th><th>Hist</th></tr> #set $curSeason = int($epResult["season"]) #end if @@ -280,6 +281,9 @@ $epLoc <a class="epSubtitlesSearch" href="searchEpisodeSubtitles?show=$show.tvdbid&season=$epResult["season"]&episode=$epResult["episode"]"><img src="$sbRoot/images/closed_captioning.png" height="16" alt="search subtitles" title="Search Subtitles" /></a> #end if </td> + <td align="center"> + <a class="histtrunc" href="trunchistory?epid=$epResult["episode_id"]"><img src="$sbRoot/images/corbeille.png" height="16" alt="trunc" title="Trunc Downloaded links History" /></a> + </td> </tr> #end for diff --git a/data/js/ajaxHisttrunc.js b/data/js/ajaxHisttrunc.js new file mode 100644 index 0000000000000000000000000000000000000000..82db735ed8ef45568e3a25a98c1b860ee8cd00f7 --- /dev/null +++ b/data/js/ajaxHisttrunc.js @@ -0,0 +1,45 @@ +(function () { + + $.ajaxHisttrunc = { + defaults: { + size: 16, + colorRow: false, + loadingImage: 'loading16_dddddd.gif', + noImage: 'no16.png', + yesImage: 'yes16.png' + } + }; + + $.fn.ajaxHisttrunc = function (options) { + options = $.extend({}, $.ajaxHisttrunc.defaults, options); + + $('.histtrunc').click(function () { + var parent = $(this).parent(); + + // put the ajax spinner (for non white bg) placeholder while we wait + parent.empty(); + parent.append($("<img/>").attr({"src": sbRoot + "/images/" + options.loadingImage, "height": options.size, "alt": "", "title": "loading"})); + + $.getJSON($(this).attr('href'), function (data) { + // if they failed then just put the red X + if (data.result == 'failure') { + img_name = options.noImage; + img_result = 'failed'; + + // if the snatch was successful then apply the corresponding class and fill in the row appropriately + } else { + img_name = options.yesImage; + img_result = 'success'; + + } + + // put the corresponding image as the result for the the row + parent.empty(); + parent.append($("<img/>").attr({"src": sbRoot + "/images/" + img_name, "height": options.size, "alt": img_result, "title": img_result})); + }); + + // fon't follow the link + return false; + }); + }; +})(); diff --git a/data/js/displayShow.js b/data/js/displayShow.js index 7dc7a937515dd3fe6948bf3b5b73d0445e96845a..30869610e6ed6f7efa8ed502bdafecf68d3bb7d5 100644 --- a/data/js/displayShow.js +++ b/data/js/displayShow.js @@ -2,6 +2,7 @@ $(document).ready(function(){ $('#sbRoot').ajaxEpSearch({'colorRow': true}); $('#sbRoot').ajaxEpSubtitlesSearch({'colorRow': true}); + $('#sbRoot').ajaxHisttrunc({'colorRow': true}); $('#seasonJump').change(function() { var id = $(this).val(); diff --git a/sickbeard/databases/mainDB.py b/sickbeard/databases/mainDB.py index 3dc8d4b4c26f8f41971c68b514e24ce3edee1ce7..22a250bcb2d49fccd93bdc20c644c91145591cf9 100644 --- a/sickbeard/databases/mainDB.py +++ b/sickbeard/databases/mainDB.py @@ -25,7 +25,7 @@ from sickbeard.providers.generic import GenericProvider from sickbeard import encodingKludge as ek from sickbeard.name_parser.parser import NameParser, InvalidNameException -MAX_DB_VERSION = 13 +MAX_DB_VERSION = 14 class MainSanityCheck(db.DBSanityCheck): @@ -100,7 +100,8 @@ class InitialSchema (db.SchemaUpgrade): "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_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);" + "CREATE TABLE history (action NUMERIC, date NUMERIC, showid NUMERIC, season NUMERIC, episode NUMERIC, quality NUMERIC, resource TEXT, provider NUMERIC);", + "CREATE TABLE episode_links (episode_id INTEGER, link TEXT);" ] for query in queries: self.connection.action(query) @@ -697,3 +698,11 @@ class AddSubtitlesSupport(Add1080pAndRawHDQualities): self.addColumn("tv_episodes", "subtitles_searchcount") self.addColumn("tv_episodes", "subtitles_lastsearch", "TIMESTAMP", str(datetime.datetime.min)) self.incDBVersion() + +class AddSubtitlesSupport(AddSubtitlesSupport): + def test(self): + return self.checkDBVersion() >= 14 + + def execute(self): + self.connection.action("CREATE TABLE episode_links (episode_id INTEGER, link TEXT)") + self.incDBVersion() diff --git a/sickbeard/nzbSplitter.py b/sickbeard/nzbSplitter.py index 048c8a59e6c0850f1851fda38407346378c933e0..ab7eeba121340fed4900cfd7128be20ee2cc9ec4 100644 --- a/sickbeard/nzbSplitter.py +++ b/sickbeard/nzbSplitter.py @@ -32,7 +32,7 @@ def getSeasonNZBs(name, urlData, season): try: showXML = etree.ElementTree(etree.XML(urlData)) except SyntaxError: - logger.log(u"Unable to parse the XML of "+name+", not splitting it", logger.ERROR) + logger.log(u"Unable to parse the XML of "+name+", not splitting it", logger.DEBUG) return ({},'') filename = name.replace(".nzb", "") diff --git a/sickbeard/providers/binnewz/nzbindex.py b/sickbeard/providers/binnewz/nzbindex.py index b340c99a9ad84517f3c3577d44eea67350b4b263..d2a0547b102a7b671757273b62591c785ed5ff30 100644 --- a/sickbeard/providers/binnewz/nzbindex.py +++ b/sickbeard/providers/binnewz/nzbindex.py @@ -44,4 +44,4 @@ class NZBIndex(NZBDownloader): for tr in results: nzblink = tr.find("a", text="Download") - return NZBGetURLSearchResult(self, nzblink.get("href"), None, refererURL) + return NZBGetURLSearchResult(self, nzblink.get("href"), None, nzblink.get("href")) diff --git a/sickbeard/providers/cpasbien.py b/sickbeard/providers/cpasbien.py index 9bfc47c368fe9fa5c4813e96e702eaa4b58754ab..0794f087e42b4f0b1655f15459b1dc161266f446 100644 --- a/sickbeard/providers/cpasbien.py +++ b/sickbeard/providers/cpasbien.py @@ -143,7 +143,7 @@ class CpasbienSearchResult: self.title = title self.url = url self.quality = quality - self.audio_langs=[audio_langs] + self.audio_langs=audio_langs def getNZB(self): return self.opener.open( self.url , 'wb').read() diff --git a/sickbeard/providers/gks.py b/sickbeard/providers/gks.py index 32ac1a7b71deeae8169193efe0b2dd0bdbe93df1..345a161a8619f3455f710a0b3b472e113fce1736 100644 --- a/sickbeard/providers/gks.py +++ b/sickbeard/providers/gks.py @@ -134,7 +134,7 @@ class GksSearchResult: self.title = title self.url = url self.quality = quality - self.audio_langs=[audio_langs] + self.audio_langs=audio_langs def getNZB(self): return self.opener.open( self.url , 'wb').read() diff --git a/sickbeard/providers/t411.py b/sickbeard/providers/t411.py index 5113e9a31c2164e5ed176038e8959e53bb6d81fb..3faa8533c7dcf31940c2551c274e6c83e661ab00 100644 --- a/sickbeard/providers/t411.py +++ b/sickbeard/providers/t411.py @@ -139,7 +139,7 @@ class T411SearchResult: self.title = title self.url = url self.quality = quality - self.audio_langs=[audio_langs] + self.audio_langs=audio_langs def getNZB(self): return self.opener.open( self.url , 'wb').read() diff --git a/sickbeard/search.py b/sickbeard/search.py index cfc541c34fcf627ef2fa64e3e01791bfac0b48c4..f6d87388ba85595021562e76d0be382042538936 100644 --- a/sickbeard/search.py +++ b/sickbeard/search.py @@ -23,8 +23,7 @@ import traceback import sickbeard -from common import SNATCHED, Quality, SEASON_RESULT, MULTI_EP_RESULT - +from common import SNATCHED, Quality, SEASON_RESULT, MULTI_EP_RESULT,ARCHIVED, IGNORED, UNAIRED, WANTED, SKIPPED from sickbeard import logger, db, show_name_helpers, exceptions, helpers from sickbeard import sab from sickbeard import nzbget @@ -124,48 +123,51 @@ def snatchEpisode(result, endStatus=SNATCHED): """ # NZBs can be sent straight to SAB or saved to disk - if result.resultType in ("nzb", "nzbdata"): - if sickbeard.NZB_METHOD == "blackhole": - dlResult = _downloadResult(result) - elif sickbeard.NZB_METHOD == "sabnzbd": - dlResult = sab.sendNZB(result) - elif sickbeard.NZB_METHOD == "nzbget": - dlResult = nzbget.sendNZB(result) + if hasattr(result,'resultType'): + if result.resultType in ("nzb", "nzbdata"): + if sickbeard.NZB_METHOD == "blackhole": + dlResult = _downloadResult(result) + elif sickbeard.NZB_METHOD == "sabnzbd": + dlResult = sab.sendNZB(result) + elif sickbeard.NZB_METHOD == "nzbget": + dlResult = nzbget.sendNZB(result) + else: + logger.log(u"Unknown NZB action specified in config: " + sickbeard.NZB_METHOD, logger.ERROR) + dlResult = False + + # TORRENTs can be sent to clients or saved to disk + elif result.resultType in ("torrent", "torrentdata"): + # torrents are saved to disk when blackhole mode + if sickbeard.TORRENT_METHOD == "blackhole": + dlResult = _downloadResult(result) + else: + client = clients.getClientIstance(sickbeard.TORRENT_METHOD)() + if hasattr(result,'extraInfo') and result.resultType=="torrentdata": + result.content=result.extraInfo[0] + dlResult = client.sendTORRENT(result) else: - logger.log(u"Unknown NZB action specified in config: " + sickbeard.NZB_METHOD, logger.ERROR) + logger.log(u"Unknown result type, unable to download it", logger.ERROR) dlResult = False - - # TORRENTs can be sent to clients or saved to disk - elif result.resultType in ("torrent", "torrentdata"): - # torrents are saved to disk when blackhole mode - if sickbeard.TORRENT_METHOD == "blackhole": - dlResult = _downloadResult(result) - else: - client = clients.getClientIstance(sickbeard.TORRENT_METHOD)() - if hasattr(result,'extraInfo') and result.resultType=="torrentdata": - result.content=result.extraInfo[0] - dlResult = client.sendTORRENT(result) + + if dlResult == False: + return False + + history.logSnatch(result) + + # don't notify when we re-download an episode + for curEpObj in result.episodes: + with curEpObj.lock: + curEpObj.status = Quality.compositeStatus(endStatus, result.quality) + curEpObj.audio_langs = result.audio_lang + curEpObj.saveToDB() + + if curEpObj.status not in Quality.DOWNLOADED: + notifiers.notify_snatch(curEpObj.prettyName()) + + return True else: - logger.log(u"Unknown result type, unable to download it", logger.ERROR) - dlResult = False - - if dlResult == False: return False - history.logSnatch(result) - - # don't notify when we re-download an episode - for curEpObj in result.episodes: - with curEpObj.lock: - curEpObj.status = Quality.compositeStatus(endStatus, result.quality) - curEpObj.audio_langs = result.audio_lang - curEpObj.saveToDB() - - if curEpObj.status not in Quality.DOWNLOADED: - notifiers.notify_snatch(curEpObj.prettyName()) - - return True - def searchForNeededEpisodes(): logger.log(u"Searching all providers for any needed episodes") @@ -221,32 +223,68 @@ def searchForNeededEpisodes(): return foundResults.values() -def pickBestResult(results, quality_list=None): +def pickBestResult(results, quality_list=None, episode=None): logger.log(u"Picking the best result out of "+str([x.name for x in results]), logger.DEBUG) - + links=[] + myDB = db.DBConnection() + for eps in episode.values(): + if hasattr(eps,'tvdbid'): + epidr=myDB.select("SELECT episode_id from tv_episodes where tvdbid=?",[eps.tvdbid]) + listlink=myDB.select("SELECT link from episode_links where episode_id=?",[epidr[0][0]]) + for dlink in listlink: + links.append(dlink[0]) # find the best result for the current episode - bestResult = None - for cur_result in results: - logger.log("Quality of "+cur_result.name+" is "+Quality.qualityStrings[cur_result.quality]) + bestResult = None + for cur_result in results: + if hasattr(cur_result,'item'): + if hasattr(cur_result.item,'nzburl'): + eplink=cur_result.item.nzburl + else: + eplink=cur_result.item.url + else: + if hasattr(cur_result,'nzburl'): + eplink=cur_result.nzburl + else: + eplink=cur_result.url + logger.log("Quality of "+cur_result.name+" is "+Quality.qualityStrings[cur_result.quality]) - if quality_list and cur_result.quality not in quality_list: - logger.log(cur_result.name+" is a quality we know we don't want, rejecting it", logger.DEBUG) - continue + if quality_list and cur_result.quality not in quality_list: + logger.log(cur_result.name+" is a quality we know we don't want, rejecting it", logger.DEBUG) + continue - if not bestResult or bestResult.quality < cur_result.quality and cur_result.quality != Quality.UNKNOWN: - bestResult = cur_result - elif bestResult.quality == cur_result.quality: - if "proper" in cur_result.name.lower() or "repack" in cur_result.name.lower(): - bestResult = cur_result - elif "internal" in bestResult.name.lower() and "internal" not in cur_result.name.lower(): + if eplink in links: + logger.log(eplink +" was already downloaded so let's skip it assuming the download failed, you can erase the downloaded links for that episode if you want", logger.DEBUG) + continue + + if not bestResult or bestResult.quality < cur_result.quality and cur_result.quality != Quality.UNKNOWN: bestResult = cur_result - - if bestResult: - logger.log(u"Picked "+bestResult.name+" as the best", logger.DEBUG) - else: - logger.log(u"No result picked.", logger.DEBUG) - + + elif bestResult.quality == cur_result.quality: + if "proper" in cur_result.name.lower() or "repack" in cur_result.name.lower(): + bestResult = cur_result + elif "internal" in bestResult.name.lower() and "internal" not in cur_result.name.lower(): + bestResult = cur_result + + if bestResult: + logger.log(u"Picked "+bestResult.name+" as the best", logger.DEBUG) + + if hasattr(bestResult,'item'): + if hasattr(bestResult.item,'nzburl'): + eplink=bestResult.item.nzburl + else: + eplink=bestResult.item.url + else: + if hasattr(bestResult,'nzburl'): + eplink=bestResult.nzburl + else: + eplink=bestResult.url + count=myDB.select("SELECT count(*) from episode_links where episode_id=? and link=?",[epidr[0][0],eplink]) + if count[0][0]==0: + myDB.action("INSERT INTO episode_links (episode_id, link) VALUES (?,?)",[epidr[0][0],eplink]) + else: + logger.log(u"No result picked.", logger.DEBUG) + return bestResult def isFinalResult(result): @@ -331,202 +369,224 @@ def findEpisode(episode, manualSearch=False): if not didSearch: logger.log(u"No NZB/Torrent providers found or enabled in the sickbeard config. Please check your settings.", logger.ERROR) - - bestResult = pickBestResult(foundResults) - + epi={} + epi[1]=episode + bestResult = pickBestResult(foundResults,episode=epi) return bestResult def findSeason(show, season): - logger.log(u"Searching for stuff we need from "+show.name+" season "+str(season)) - - foundResults = {} - - didSearch = False - - for curProvider in providers.sortedProviderList(): - - if not curProvider.isActive(): - continue - - try: - curResults = curProvider.findSeasonResults(show, season) - - # make a list of all the results for this provider - for curEp in curResults: - - # skip non-tv crap - curResults[curEp] = filter(lambda x: show_name_helpers.filterBadReleases(x.name) and show_name_helpers.isGoodResult(x.name, show), curResults[curEp]) - - if curEp in foundResults: - foundResults[curEp] += curResults[curEp] - else: - foundResults[curEp] = curResults[curEp] - - except exceptions.AuthException, e: - logger.log(u"Authentication error: "+ex(e), logger.ERROR) - continue - except Exception, e: - logger.log(u"Error while searching "+curProvider.name+", skipping: "+ex(e), logger.ERROR) - logger.log(traceback.format_exc(), logger.DEBUG) - continue - - didSearch = True - - if not didSearch: - logger.log(u"No NZB/Torrent providers found or enabled in the sickbeard config. Please check your settings.", logger.ERROR) + myDB = db.DBConnection() + allEps = [int(x["episode"]) for x in myDB.select("SELECT episode FROM tv_episodes WHERE showid = ? AND season = ?", [show.tvdbid, season])] + logger.log(u"Episode list: "+str(allEps), logger.DEBUG) + + reallywanted=[] + notwanted=[] finalResults = [] - - anyQualities, bestQualities = Quality.splitQuality(show.quality) - - # pick the best season NZB - bestSeasonNZB = None - if SEASON_RESULT in foundResults: - bestSeasonNZB = pickBestResult(foundResults[SEASON_RESULT], anyQualities+bestQualities) - - highest_quality_overall = 0 - for cur_season in foundResults: - for cur_result in foundResults[cur_season]: - if cur_result.quality != Quality.UNKNOWN and cur_result.quality > highest_quality_overall: - highest_quality_overall = cur_result.quality - logger.log(u"The highest quality of any match is "+Quality.qualityStrings[highest_quality_overall], logger.DEBUG) - - # see if every episode is wanted - if bestSeasonNZB: - - # get the quality of the season nzb - seasonQual = Quality.nameQuality(bestSeasonNZB.name) - seasonQual = bestSeasonNZB.quality - logger.log(u"The quality of the season NZB is "+Quality.qualityStrings[seasonQual], logger.DEBUG) - - myDB = db.DBConnection() - allEps = [int(x["episode"]) for x in myDB.select("SELECT episode FROM tv_episodes WHERE showid = ? AND season = ?", [show.tvdbid, season])] - logger.log(u"Episode list: "+str(allEps), logger.DEBUG) - - allWanted = True - anyWanted = False - for curEpNum in allEps: - if not show.wantEpisode(season, curEpNum, seasonQual): - allWanted = False - else: - anyWanted = True - - # if we need every ep in the season and there's nothing better then just download this and be done with it - if allWanted and bestSeasonNZB.quality == highest_quality_overall: - logger.log(u"Every ep in this season is needed, downloading the whole NZB "+bestSeasonNZB.name) - epObjs = [] - for curEpNum in allEps: - epObjs.append(show.getEpisode(season, curEpNum)) - bestSeasonNZB.episodes = epObjs - return [bestSeasonNZB] - - elif not anyWanted: - logger.log(u"No eps from this season are wanted at this quality, ignoring the result of "+bestSeasonNZB.name, logger.DEBUG) - + for curEpNum in allEps: + sqlResults = myDB.select("SELECT status FROM tv_episodes WHERE showid = ? AND season = ? AND episode = ?", [show.tvdbid, season, curEpNum]) + epStatus = int(sqlResults[0]["status"]) + if epStatus ==3: + reallywanted.append(curEpNum) else: - - if bestSeasonNZB.provider.providerType == GenericProvider.NZB: - logger.log(u"Breaking apart the NZB and adding the individual ones to our results", logger.DEBUG) - - # if not, break it apart and add them as the lowest priority results - individualResults = nzbSplitter.splitResult(bestSeasonNZB) - - individualResults = filter(lambda x: show_name_helpers.filterBadReleases(x.name) and show_name_helpers.isGoodResult(x.name, show), individualResults) - - for curResult in individualResults: - if len(curResult.episodes) == 1: - epNum = curResult.episodes[0].episode - elif len(curResult.episodes) > 1: - epNum = MULTI_EP_RESULT - - if epNum in foundResults: - foundResults[epNum].append(curResult) + notwanted.append(curEpNum) + if notwanted != []: + for EpNum in reallywanted: + showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, show.tvdbid) + episode = showObj.getEpisode(season, EpNum) + finalResults.append(findEpisode(episode, manualSearch=True)) + return finalResults + else: + logger.log(u"Searching for stuff we need from "+show.name+" season "+str(season)) + + foundResults = {} + + didSearch = False + + for curProvider in providers.sortedProviderList(): + + if not curProvider.isActive(): + continue + + try: + curResults = curProvider.findSeasonResults(show, season) + + # make a list of all the results for this provider + for curEp in curResults: + + # skip non-tv crap + curResults[curEp] = filter(lambda x: show_name_helpers.filterBadReleases(x.name) and show_name_helpers.isGoodResult(x.name, show), curResults[curEp]) + + if curEp in foundResults: + foundResults[curEp] += curResults[curEp] else: - foundResults[epNum] = [curResult] - - # If this is a torrent all we can do is leech the entire torrent, user will have to select which eps not do download in his torrent client - else: - - # Season result from BTN must be a full-season torrent, creating multi-ep result for it. - logger.log(u"Adding multi-ep result for full-season torrent. Set the episodes you don't want to 'don't download' in your torrent client if desired!") + foundResults[curEp] = curResults[curEp] + + except exceptions.AuthException, e: + logger.log(u"Authentication error: "+ex(e), logger.ERROR) + continue + except Exception, e: + logger.log(u"Error while searching "+curProvider.name+", skipping: "+ex(e), logger.DEBUG) + logger.log(traceback.format_exc(), logger.DEBUG) + continue + + didSearch = True + + if not didSearch: + logger.log(u"No NZB/Torrent providers found or enabled in the sickbeard config. Please check your settings.", logger.ERROR) + + finalResults = [] + + anyQualities, bestQualities = Quality.splitQuality(show.quality) + + # pick the best season NZB + bestSeasonNZB = None + if SEASON_RESULT in foundResults: + bestSeasonNZB = pickBestResult(foundResults[SEASON_RESULT], anyQualities+bestQualities,episode=show.episodes[1]) + + highest_quality_overall = 0 + for cur_season in foundResults: + for cur_result in foundResults[cur_season]: + if cur_result.quality != Quality.UNKNOWN and cur_result.quality > highest_quality_overall: + highest_quality_overall = cur_result.quality + logger.log(u"The highest quality of any match is "+Quality.qualityStrings[highest_quality_overall], logger.DEBUG) + + # see if every episode is wanted + if bestSeasonNZB: + + # get the quality of the season nzb + seasonQual = Quality.nameQuality(bestSeasonNZB.name) + seasonQual = bestSeasonNZB.quality + logger.log(u"The quality of the season NZB is "+Quality.qualityStrings[seasonQual], logger.DEBUG) + + myDB = db.DBConnection() + allEps = [int(x["episode"]) for x in myDB.select("SELECT episode FROM tv_episodes WHERE showid = ? AND season = ?", [show.tvdbid, season])] + logger.log(u"Episode list: "+str(allEps), logger.DEBUG) + + allWanted = True + anyWanted = False + for curEpNum in allEps: + if not show.wantEpisode(season, curEpNum, seasonQual): + allWanted = False + else: + anyWanted = True + + # if we need every ep in the season and there's nothing better then just download this and be done with it + if allWanted and bestSeasonNZB.quality == highest_quality_overall: + logger.log(u"Every ep in this season is needed, downloading the whole NZB "+bestSeasonNZB.name) epObjs = [] for curEpNum in allEps: epObjs.append(show.getEpisode(season, curEpNum)) bestSeasonNZB.episodes = epObjs - - epNum = MULTI_EP_RESULT - if epNum in foundResults: - foundResults[epNum].append(bestSeasonNZB) - else: - foundResults[epNum] = [bestSeasonNZB] - - # go through multi-ep results and see if we really want them or not, get rid of the rest - multiResults = {} - if MULTI_EP_RESULT in foundResults: - for multiResult in foundResults[MULTI_EP_RESULT]: - - logger.log(u"Seeing if we want to bother with multi-episode result "+multiResult.name, logger.DEBUG) - - # see how many of the eps that this result covers aren't covered by single results - neededEps = [] - notNeededEps = [] - for epObj in multiResult.episodes: - epNum = epObj.episode - # if we have results for the episode - if epNum in foundResults and len(foundResults[epNum]) > 0: - # but the multi-ep is worse quality, we don't want it - # TODO: wtf is this False for - #if False and multiResult.quality <= pickBestResult(foundResults[epNum]): - # notNeededEps.append(epNum) - #else: - neededEps.append(epNum) + return [bestSeasonNZB] + + elif not anyWanted: + logger.log(u"No eps from this season are wanted at this quality, ignoring the result of "+bestSeasonNZB.name, logger.DEBUG) + + else: + + if bestSeasonNZB.provider.providerType == GenericProvider.NZB: + logger.log(u"Breaking apart the NZB and adding the individual ones to our results", logger.DEBUG) + + # if not, break it apart and add them as the lowest priority results + individualResults = nzbSplitter.splitResult(bestSeasonNZB) + + individualResults = filter(lambda x: show_name_helpers.filterBadReleases(x.name) and show_name_helpers.isGoodResult(x.name, show), individualResults) + + for curResult in individualResults: + if len(curResult.episodes) == 1: + epNum = curResult.episodes[0].episode + elif len(curResult.episodes) > 1: + epNum = MULTI_EP_RESULT + + if epNum in foundResults: + foundResults[epNum].append(curResult) + else: + foundResults[epNum] = [curResult] + + # If this is a torrent all we can do is leech the entire torrent, user will have to select which eps not do download in his torrent client else: - neededEps.append(epNum) - - logger.log(u"Single-ep check result is neededEps: "+str(neededEps)+", notNeededEps: "+str(notNeededEps), logger.DEBUG) - - if not neededEps: - logger.log(u"All of these episodes were covered by single nzbs, ignoring this multi-ep result", logger.DEBUG) + + # Season result from BTN must be a full-season torrent, creating multi-ep result for it. + logger.log(u"Adding multi-ep result for full-season torrent. Set the episodes you don't want to 'don't download' in your torrent client if desired!") + epObjs = [] + for curEpNum in allEps: + epObjs.append(show.getEpisode(season, curEpNum)) + bestSeasonNZB.episodes = epObjs + + epNum = MULTI_EP_RESULT + if epNum in foundResults: + foundResults[epNum].append(bestSeasonNZB) + else: + foundResults[epNum] = [bestSeasonNZB] + + # go through multi-ep results and see if we really want them or not, get rid of the rest + multiResults = {} + if MULTI_EP_RESULT in foundResults: + for multiResult in foundResults[MULTI_EP_RESULT]: + + logger.log(u"Seeing if we want to bother with multi-episode result "+multiResult.name, logger.DEBUG) + + # see how many of the eps that this result covers aren't covered by single results + neededEps = [] + notNeededEps = [] + for epObj in multiResult.episodes: + epNum = epObj.episode + # if we have results for the episode + if epNum in foundResults and len(foundResults[epNum]) > 0: + # but the multi-ep is worse quality, we don't want it + # TODO: wtf is this False for + #if False and multiResult.quality <= pickBestResult(foundResults[epNum]): + # notNeededEps.append(epNum) + #else: + neededEps.append(epNum) + else: + neededEps.append(epNum) + + logger.log(u"Single-ep check result is neededEps: "+str(neededEps)+", notNeededEps: "+str(notNeededEps), logger.DEBUG) + + if not neededEps: + logger.log(u"All of these episodes were covered by single nzbs, ignoring this multi-ep result", logger.DEBUG) + continue + + # check if these eps are already covered by another multi-result + multiNeededEps = [] + multiNotNeededEps = [] + for epObj in multiResult.episodes: + epNum = epObj.episode + if epNum in multiResults: + multiNotNeededEps.append(epNum) + else: + multiNeededEps.append(epNum) + + logger.log(u"Multi-ep check result is multiNeededEps: "+str(multiNeededEps)+", multiNotNeededEps: "+str(multiNotNeededEps), logger.DEBUG) + + if not multiNeededEps: + logger.log(u"All of these episodes were covered by another multi-episode nzbs, ignoring this multi-ep result", logger.DEBUG) + continue + + # if we're keeping this multi-result then remember it + for epObj in multiResult.episodes: + multiResults[epObj.episode] = multiResult + + # don't bother with the single result if we're going to get it with a multi result + for epObj in multiResult.episodes: + epNum = epObj.episode + if epNum in foundResults: + logger.log(u"A needed multi-episode result overlaps with a single-episode result for ep #"+str(epNum)+", removing the single-episode results from the list", logger.DEBUG) + del foundResults[epNum] + + finalResults += set(multiResults.values()) + + # of all the single ep results narrow it down to the best one for each episode + for curEp in foundResults: + if curEp in (MULTI_EP_RESULT, SEASON_RESULT): continue - - # check if these eps are already covered by another multi-result - multiNeededEps = [] - multiNotNeededEps = [] - for epObj in multiResult.episodes: - epNum = epObj.episode - if epNum in multiResults: - multiNotNeededEps.append(epNum) - else: - multiNeededEps.append(epNum) - - logger.log(u"Multi-ep check result is multiNeededEps: "+str(multiNeededEps)+", multiNotNeededEps: "+str(multiNotNeededEps), logger.DEBUG) - - if not multiNeededEps: - logger.log(u"All of these episodes were covered by another multi-episode nzbs, ignoring this multi-ep result", logger.DEBUG) + + if len(foundResults[curEp]) == 0: continue - - # if we're keeping this multi-result then remember it - for epObj in multiResult.episodes: - multiResults[epObj.episode] = multiResult - - # don't bother with the single result if we're going to get it with a multi result - for epObj in multiResult.episodes: - epNum = epObj.episode - if epNum in foundResults: - logger.log(u"A needed multi-episode result overlaps with a single-episode result for ep #"+str(epNum)+", removing the single-episode results from the list", logger.DEBUG) - del foundResults[epNum] - - finalResults += set(multiResults.values()) - - # of all the single ep results narrow it down to the best one for each episode - for curEp in foundResults: - if curEp in (MULTI_EP_RESULT, SEASON_RESULT): - continue - - if len(foundResults[curEp]) == 0: - continue - - finalResults.append(pickBestResult(foundResults[curEp])) - - return finalResults + print curEp + finalResults.append(pickBestResult(foundResults[curEp],None,episode=show.episodes[1])) + + return finalResults diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index deaa2cc2d9bd62a88fe5fa537b058b12f8df96e2..65a92316f0fc96d67d2aea2a864af1e48beaf5d9 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -3162,6 +3162,16 @@ class Home: redirect("/home/displayShow?show=" + show) + @cherrypy.expose + def trunchistory(self, epid): + + myDB = db.DBConnection() + nbep = myDB.select("Select count(*) from episode_links where episode_id=?",[epid]) + myDB.action("DELETE from episode_links where episode_id=?",[epid]) + messnum = str(nbep[0][0]) + ' history links deleted' + ui.notifications.message('Episode History Truncated' , messnum) + return json.dumps({'result': 'ok'}) + @cherrypy.expose def searchEpisode(self, show=None, season=None, episode=None):