diff --git a/data/interfaces/default/displayShow.tmpl b/data/interfaces/default/displayShow.tmpl index 72425b00cb6c39017bed8100273d933f5d991e8a..abf55244f17e30517e5db40f0d2ce685289a6148 100644 --- a/data/interfaces/default/displayShow.tmpl +++ b/data/interfaces/default/displayShow.tmpl @@ -85,6 +85,12 @@ All other statuses will automatically update to the appropriate "Downloaded ..." <br /> </div> +Jump To: +#for $seasonNum in $seasonResults: + <a href="#season-$seasonNum["season"]"> + #if int($seasonNum["season"]) == 0 then "Specials</a>" else "Season "+str($seasonNum["season"])+"</a> |" +#end for + <table class="sickbeardTable"> #for $epResult in $sqlResults: @@ -94,8 +100,7 @@ All other statuses will automatically update to the appropriate "Downloaded ..." <td colspan=8> <br /> <h2>#if int($epResult["season"]) == 0 then "Specials" else "Season "+str($epResult["season"])# - <br /> - </h2></td> + </h2><font size="-3"><a href="#top">[top]</a></font></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>Airdate</th><th>Filename</th><th>Status</th><th>Actions</th></tr> #set $curSeason = int($epResult["season"]) @@ -133,7 +138,7 @@ $epLoc <a href="searchEpisode?show=$show.tvdbid&season=$epResult["season"]&episode=$epResult["episode"]">search</a> #end if </td> - <tr> + </tr> #end for </table><br /><br /> diff --git a/data/interfaces/default/home_addShows.tmpl b/data/interfaces/default/home_addShows.tmpl index 15ead13711ac1b09b416225c987b9dcaeb657db7..6e375f1d9d4f07b09b348b8df863ae9002e7c9b6 100644 --- a/data/interfaces/default/home_addShows.tmpl +++ b/data/interfaces/default/home_addShows.tmpl @@ -12,6 +12,7 @@ <form method="POST" action="addShows" accept-charset="utf-8"> Single show folder: <input type="text" name="showDirs" id="showDir" size="50"> <input type="submit" value="Add Show"> </form> +<p><font size="-2"><b>Hint:</b> To start tracking a show you don't have yet, enter the path where you would like it stored, i.e. /path/to/New Show</font> <br /> <br /> <h2>Add multiple shows from a root directory</h2> @@ -24,4 +25,4 @@ Root show folder: <input type="text" name="dir" id="showsDir" size="50"> <input jQuery('#showsDir').fileBrowser({ title: 'Select Root Show Folder', key: 'showsPath' }); </script> -#include $os.path.join($sickbeard.PROG_DIR, "data/interfaces/default/inc_bottom.tmpl") \ No newline at end of file +#include $os.path.join($sickbeard.PROG_DIR, "data/interfaces/default/inc_bottom.tmpl") diff --git a/data/interfaces/default/inc_top.tmpl b/data/interfaces/default/inc_top.tmpl index 488146735b8ad726b562ececa671073b990bcde3..ea29c2b4e22472e11ced77d66eac94fe446322a8 100644 --- a/data/interfaces/default/inc_top.tmpl +++ b/data/interfaces/default/inc_top.tmpl @@ -31,6 +31,7 @@ </script> </head> <body> +<a name="top"> <div id="header"> <span id="logo"><a href="$sbRoot/home/" title="Sick Beard homepage"><img alt="Sick Beard" src="$sbRoot/images/sickbeard_small.png" width="150"></a></span> <span id="versiontext">alpha $sickbeard.version.SICKBEARD_VERSION</span> @@ -57,27 +58,29 @@ </span> #end if -#if $sickbeard.VERSION_NOTIFY and $sickbeard.NEWEST_VERSION_STRING: +#if $sickbeard.NEWEST_VERSION_STRING: +<br /> <div class="message ui-state-highlight ui-corner-all"> <p><span class="ui-icon ui-icon-alert"></span>$sickbeard.NEWEST_VERSION_STRING</a></p> </div> -<br /> #end if <h1>#if $varExists('header') then $header else $title#</h1> #for $curMessage in $flash.messages(): +<br /> <div class="message ui-state-highlight ui-corner-all"> <p><span class="ui-icon ui-icon-info"></span>$curMessage[0]</p> $curMessage[1] </div> #end for -<br /> + #for $curError in $flash.errors(): +<br /> <div class="message ui-state-error ui-corner-all"> <p><span class="ui-icon ui-icon-alert"></span>$curError[0]</p> $curError[1] </div> #end for -<br /> \ No newline at end of file + diff --git a/data/interfaces/default/manage_manageSearches.tmpl b/data/interfaces/default/manage_manageSearches.tmpl index 96576c1aeae1416ab5f47c1993d78dd653c441f9..4679cf1abee93a6b1c922afe1503f18ae30fbd45 100644 --- a/data/interfaces/default/manage_manageSearches.tmpl +++ b/data/interfaces/default/manage_manageSearches.tmpl @@ -27,9 +27,12 @@ $backlogPI.currentStatus['title']<br /> #if not $searchStatus: Not in progress (<a href="$sbRoot/manage/manageSearches/forceSearch">force</a>)<br /> #else: -In Progress +In Progress<br /> #end if <br /> +<b>Version Check:</b><br /> +<a href="$sbRoot/manage/manageSearches/forceVersionCheck">Force version check</a><br /> +<br /> <br /> -#include $os.path.join($sickbeard.PROG_DIR, "data/interfaces/default/inc_bottom.tmpl") \ No newline at end of file +#include $os.path.join($sickbeard.PROG_DIR, "data/interfaces/default/inc_bottom.tmpl") diff --git a/sickbeard/common.py b/sickbeard/common.py index bd5f2e934f4d37b57501b69618f185165bebd9bc..fcff81604905c1cc487feb53757b3a81896336f9 100644 --- a/sickbeard/common.py +++ b/sickbeard/common.py @@ -275,7 +275,7 @@ sceneExceptions = {72546: ['CSI'], 194751: ['Conan', 'Conan (2010)'], 164451: ['Carlos (2010)'], 70726: ['Babylon 5', 'Babylon5'], - 83714: ['Genius', 'Genius With Dave Gormand'], + 83714: ['Genius', 'Genius With Dave Gormand'], } countryList = {'Australia': 'AU', diff --git a/sickbeard/providers/generic.py b/sickbeard/providers/generic.py index 43316771f7f0b7dfebdcde04f3da002151d153f4..d818ff326e232f7dfa3db243359bf71fc9c4e129 100644 --- a/sickbeard/providers/generic.py +++ b/sickbeard/providers/generic.py @@ -116,7 +116,7 @@ class GenericProvider: return result - def downloadResult (self, result): + def downloadResult(self, result): logger.log(u"Downloading a result from " + self.name+" at " + result.url) @@ -138,9 +138,13 @@ class GenericProvider: logger.log(u"Saving to " + fileName, logger.DEBUG) - fileOut = open(fileName, writeMode) - fileOut.write(data) - fileOut.close() + try: + fileOut = open(fileName, writeMode) + fileOut.write(data) + fileOut.close() + except IOError, e: + logger.log("Unable to save the NZB: "+str(e).decode('utf-8'), logger.ERROR) + return False return True @@ -196,13 +200,17 @@ class GenericProvider: logger.log(u"Unable to parse the filename "+title+" into a valid episode", logger.WARNING) continue - if epInfo.seasonnumber != episode.season or episode.episode not in epInfo.episodenumbers: + if episode.show.is_air_by_date: + if epInfo.episodenumbers[0] != episode.airdate: + logger.log("Episode "+title+" didn't air on "+str(episode.airdate)+", skipping it", logger.DEBUG) + continue + elif epInfo.seasonnumber != episode.season or episode.episode not in epInfo.episodenumbers: 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(epInfo.seasonnumber, epInfo.episodenumbers[0], quality, manualSearch): + 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 diff --git a/sickbeard/providers/newznab.py b/sickbeard/providers/newznab.py index c381514e33121f0a8a9bb476eb9d35934df76e36..4b830a22802f68d724d78bff3846d9f5dda76979 100644 --- a/sickbeard/providers/newznab.py +++ b/sickbeard/providers/newznab.py @@ -70,13 +70,20 @@ class NewznabProvider(generic.NZBProvider): return params # search directly by tvrage id - params['rid'] = show.tvrid + if show.tvrid: + params['rid'] = show.tvrid + # if we can't then fall back on a very basic name search + else: + params['q'] = sceneHelpers.sanitizeSceneName(show.name) if season != None: # air-by-date means &season=2010&q=2010.03, no other way to do it atm if show.is_air_by_date: params['season'] = season.split('-')[0] - params['q'] = season.replace('-', '.') + if 'q' in params: + params['q'] += '.' + season.replace('-', '.') + else: + params['q'] = season.replace('-', '.') else: params['season'] = season @@ -90,7 +97,11 @@ class NewznabProvider(generic.NZBProvider): return params # search directly by tvrage id - params['rid'] = ep_obj.show.tvrid + if ep_obj.show.tvrid: + params['rid'] = ep_obj.show.tvrid + # if we can't then fall back on a very basic name search + else: + params['q'] = sceneHelpers.sanitizeSceneName(ep_obj.show.name) if ep_obj.show.is_air_by_date: date_str = str(ep_obj.airdate) diff --git a/sickbeard/sceneHelpers.py b/sickbeard/sceneHelpers.py index 386b1c0747255bb4c521ce9b96e426666410e598..f72d0f8f47d8185d693edaaf6dc67d27b3b69208 100644 --- a/sickbeard/sceneHelpers.py +++ b/sickbeard/sceneHelpers.py @@ -176,13 +176,13 @@ def allPossibleShowNames(show): # if we have "Show Name Australia" or "Show Name (Australia)" this will add "Show Name (AU)" for # any countries defined in common.countryList # (and vice versa) - for curName in showNames: + for curName in set(showNames): for curCountry in country_list: if curName.endswith(' '+curCountry): - logger.log(u"Show names ends with "+curCountry+", so trying to add ("+country_list[curCountry]+") to it as well", logger.DEBUG) + logger.log(u"Show name "+str(curName)+" ends with "+curCountry+", so trying to add ("+country_list[curCountry]+") to it as well", logger.DEBUG) newShowNames.append(curName.replace(' '+curCountry, ' ('+country_list[curCountry]+')')) elif curName.endswith(' ('+curCountry+')'): - logger.log(u"Show names ends with "+curCountry+", so trying to add ("+country_list[curCountry]+") to it as well", logger.DEBUG) + logger.log(u"Show name "+str(curName)+" ends with "+curCountry+", so trying to add ("+country_list[curCountry]+") to it as well", logger.DEBUG) newShowNames.append(curName.replace(' ('+curCountry+')', ' ('+country_list[curCountry]+')')) showNames += newShowNames diff --git a/sickbeard/search.py b/sickbeard/search.py index 56cad5ef0a34b3b753c92c47ea692ac372aa790b..0c16e428b22e6a44558d757ad223fd9032e5243a 100644 --- a/sickbeard/search.py +++ b/sickbeard/search.py @@ -88,7 +88,7 @@ def snatchEpisode(result, endStatus=SNATCHED): dlResult = False if dlResult == False: - return + return False history.logSnatch(result) @@ -104,6 +104,8 @@ def snatchEpisode(result, endStatus=SNATCHED): sickbeard.updateAiringList() sickbeard.updateComingList() + + return True def searchForNeededEpisodes(): @@ -329,6 +331,7 @@ def findSeason(show, season): # 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]: @@ -342,27 +345,48 @@ def findSeason(show, season): # 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 - if False and multiResult.quality <= pickBestResult(foundResults[epNum]): - notNeededEps.append(epNum) - else: - neededEps.append(epNum) + # 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"Result is neededEps: "+str(neededEps)+", notNeededEps: "+str(notNeededEps), logger.DEBUG) + 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 neededEps: + 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 episode "+str(epNum)+", removing its results from the list", logger.DEBUG) + 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.append(multiResult) + finalResults += set(multiResults.values()) # of all the single ep results narrow it down to the best one for each episode for curEp in foundResults: diff --git a/sickbeard/searchCurrent.py b/sickbeard/searchCurrent.py index c78902b62f82d5777eaf67e4fffa535bc6e1fd0b..0e51693fbb9e1220403e3774fe0d987fdc6fae3b 100644 --- a/sickbeard/searchCurrent.py +++ b/sickbeard/searchCurrent.py @@ -68,7 +68,6 @@ class CurrentSearcher(): search.snatchEpisode(curResult) time.sleep(2) - # update our lists to reflect any changes we just made sickbeard.updateAiringList() sickbeard.updateComingList() diff --git a/sickbeard/tv.py b/sickbeard/tv.py index 6558d3854d7381a1833ae4c2d37dd44de7e57894..518f3dff6797b1dc3f8f9d6e79a91e991b42cf64 100644 --- a/sickbeard/tv.py +++ b/sickbeard/tv.py @@ -143,6 +143,8 @@ class TVShow(object): def writeShowNFO(self): + result = False + if not ek.ek(os.path.isdir, self._location): logger.log(str(self.tvdbid) + u": Show dir doesn't exist, skipping NFO generation") return False @@ -1379,7 +1381,7 @@ class TVEpisode: naming_quality = sickbeard.NAMING_QUALITY if ((self.show.genre and "Talk Show" in self.show.genre) or self.show.air_by_date) and sickbeard.NAMING_DATES: - goodEpString = str(self.airdate) + goodEpString = self.airdate.strftime("%Y.%m.%d") else: goodEpString = config.naming_ep_type[naming_ep_type] % {'seasonnumber': self.season, 'episodenumber': self.episode} diff --git a/sickbeard/versionChecker.py b/sickbeard/versionChecker.py index f7c304b3691212ced608bf31a0235dfd6ae322ab..a57af18982645a81a00cac1ae553774aafe3c58e 100644 --- a/sickbeard/versionChecker.py +++ b/sickbeard/versionChecker.py @@ -57,18 +57,19 @@ class CheckVersion(): return install_type - def check_for_new_version(self): + def check_for_new_version(self, force=False): - if not sickbeard.VERSION_NOTIFY: + if not sickbeard.VERSION_NOTIFY and not force: logger.log(u"Version checking is disabled, not checking for the newest version") - return + return False logger.log(u"Checking if "+self.install_type+" needs an update") if not self.updater.need_update(): logger.log(u"No update needed") - return + return False self.updater.set_newest_text() + return True def update(self): diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index 1a0ceb68383f85ed64c67b6df911bcbe0c210ad9..b2f29e8295e8e7cae2d7f1e87904a8b2217d2b5e 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -174,6 +174,15 @@ class ManageSearches: redirect("/manage/manageSearches") + @cherrypy.expose + def forceVersionCheck(self): + + # force a check to see if there is a new version + result = sickbeard.versionCheckScheduler.action.check_for_new_version(force=True) + if result: + logger.log(u"Forcing version check") + + redirect("/manage/manageSearches") class Manage: @@ -1400,6 +1409,11 @@ class Home: myDB = db.DBConnection() + seasonResults = myDB.select( + "SELECT DISTINCT season FROM tv_episodes WHERE showid = ? ORDER BY season desc", + [showObj.tvdbid] + ) + sqlResults = myDB.select( "SELECT * FROM tv_episodes WHERE showid = ? ORDER BY season*1000+episode DESC", [showObj.tvdbid] @@ -1438,6 +1452,7 @@ class Home: t.show = showObj t.sqlResults = sqlResults + t.seasonResults = seasonResults epCounts = {} epCats = {} @@ -1739,7 +1754,9 @@ class Home: logger.log(u"Downloading episode from " + foundEpisode.url) result = search.snatchEpisode(foundEpisode) providerModule = foundEpisode.provider - if providerModule == None: + if not result: + ui.flash.error('Error while attempting to snatch '+foundEpisode.name+', check your logs') + elif providerModule == None: ui.flash.error('Provider is configured incorrectly, unable to download') else: ui.flash.message('Episode <b>%s</b> snatched from <b>%s</b>' % (foundEpisode.name, providerModule.name))