def backlogShow(self, indexer_id): show_obj = Show.find(sickbeard.showList, int(indexer_id)) if show_obj: sickbeard.backlogSearchScheduler.action.searchBacklog([show_obj]) return self.redirect("/manage/backlogOverview/")
def find_propers(self, search_date=None): results = [] db = DBConnection() placeholder = ','.join([ str(x) for x in Quality.DOWNLOADED + Quality.SNATCHED + Quality.SNATCHED_BEST ]) sql_results = db.select( 'SELECT s.show_name, e.showid, e.season, e.episode, e.status, e.airdate' ' FROM tv_episodes AS e' ' INNER JOIN tv_shows AS s ON (e.showid = s.indexer_id)' ' WHERE e.airdate >= ' + str(search_date.toordinal()) + ' AND e.status IN (' + placeholder + ') and e.is_proper = 0') for result in sql_results or []: show = Show.find(sickbeard.showList, int(result[b'showid'])) if show: episode = show.getEpisode(result[b'season'], result[b'episode']) for term in self.proper_strings: search_strings = self.get_episode_search_strings( episode, add_string=term) for search_string in search_strings: for item in self.search(search_string): title, url = self._get_title_and_url(item) results.append( Proper(title, url, datetime.today(), show)) return results
def addDefaultShow(indexer, indexer_id, name, status): """ Adds a new show with the default settings """ if not Show.find(sickbeard.showList, int(indexer_id)): logger.log("Adding show " + str(indexer_id)) root_dirs = sickbeard.ROOT_DIRS.split('|') try: location = root_dirs[int(root_dirs[0]) + 1] except Exception: location = None if location: showPath = ek(os.path.join, location, sanitize_filename(name)) dir_exists = helpers.makeDir(showPath) if not dir_exists: logger.log("Unable to create the folder {0} , can't add the show".format(showPath), logger.WARNING) return else: helpers.chmodAsParent(showPath) sickbeard.showQueueScheduler.action.add_show(int(indexer), int(indexer_id), showPath, default_status=status, quality=int(sickbeard.QUALITY_DEFAULT), season_folders=int(sickbeard.SEASON_FOLDERS_DEFAULT), paused=sickbeard.TRAKT_START_PAUSED, default_status_after=status) else: logger.log("There was an error creating the show, no root directory setting found", logger.WARNING) return
def updateShows(self): logger.debug("SHOW_WATCHLIST::CHECK::START - Trakt Show Watchlist") self._getShowWatchlist() if not self.ShowWatchlist: logger.debug( "No shows found in your watchlist, aborting watchlist update") return for index, indexer in sickchill.indexer: if indexer.slug in self.ShowWatchlist: for show_el in self.ShowWatchlist[indexer.slug]: indexer_id = int(str(show_el)) show = self.ShowWatchlist[indexer.slug][show_el] # logger.debug("Checking Show: %s %s %s" % (slug, indexer_id, show['title'])) if int(settings.TRAKT_METHOD_ADD) != 2: self.addDefaultShow(index, indexer_id, show["title"], SKIPPED) else: self.addDefaultShow(index, indexer_id, show["title"], WANTED) if int(settings.TRAKT_METHOD_ADD) == 1: newShow = Show.find(settings.showList, indexer_id) if newShow is not None: setEpisodeToWanted(newShow, 1, 1) else: self.todoWanted.append((indexer_id, 1, 1)) logger.debug("SHOW_WATCHLIST::CHECK::FINISH - Trakt Show Watchlist")
def get_scene_exceptions(indexer_id, season=-1): """ Given a indexer_id, return a list of all the scene exceptions. """ if indexer_id not in exceptions_cache or season not in exceptions_cache[indexer_id]: cache_db_con = db.DBConnection("cache.db") exceptions = cache_db_con.select("SELECT show_name FROM scene_exceptions WHERE indexer_id = ? and season = ?", [indexer_id, season]) if exceptions: exeptions_list = list({cur_exception["show_name"] for cur_exception in exceptions}) if indexer_id not in exceptions_cache: exceptions_cache[indexer_id] = {} exceptions_cache[indexer_id][season] = exeptions_list results = [] if indexer_id in exceptions_cache and season in exceptions_cache[indexer_id]: results += exceptions_cache[indexer_id][season] # Add generic exceptions regardless of the season if there is no exception for season if season != -1: get_scene_exceptions(indexer_id) if indexer_id in exceptions_cache and -1 in exceptions_cache[indexer_id]: results += exceptions_cache[indexer_id][-1] else: show = Show.find(settings.showList, indexer_id) if show: if show.show_name: results.append(helpers.full_sanitizeSceneName(show.show_name)) if show.custom_name: results.append(helpers.full_sanitizeSceneName(show.custom_name)) response = list({result for result in results}) logger.debug(f"get_scene_exceptions: {response}") logger.debug(f"exceptions_cache: {exceptions_cache.get(indexer_id)}") return response
def updateShows(self): logger.log("SHOW_WATCHLIST::CHECK::START - Trakt Show Watchlist", logger.DEBUG) self._getShowWatchlist() if not self.ShowWatchlist: logger.log("No shows found in your watchlist, aborting watchlist update", logger.DEBUG) return indexer = int(sickbeard.TRAKT_DEFAULT_INDEXER) trakt_id = sickbeard.indexerApi(indexer).config['trakt_id'] for show_el in self.ShowWatchlist[trakt_id]: indexer_id = int(str(show_el)) show = self.ShowWatchlist[trakt_id][show_el] # logger.log(u"Checking Show: %s %s %s" % (trakt_id, indexer_id, show['title']),logger.DEBUG) if int(sickbeard.TRAKT_METHOD_ADD) != 2: self.addDefaultShow(indexer, indexer_id, show['title'], SKIPPED) else: self.addDefaultShow(indexer, indexer_id, show['title'], WANTED) if int(sickbeard.TRAKT_METHOD_ADD) == 1: newShow = Show.find(sickbeard.showList, indexer_id) if newShow is not None: setEpisodeToWanted(newShow, 1, 1) else: self.todoWanted.append((indexer_id, 1, 1)) logger.log("SHOW_WATCHLIST::CHECK::FINISH - Trakt Show Watchlist", logger.DEBUG)
def get_scene_absolute_numbering(indexer_id, indexer, absolute_number, fallback_to_xem=True): """ Returns a tuple, (season, episode), with the scene numbering (if there is one), otherwise returns the xem numbering (if fallback_to_xem is set), otherwise returns the TVDB numbering. (so the return values will always be set) :param indexer_id: int ;param absolute_number: int :param fallback_to_xem: bool If set (the default), check xem for matches if there is no local scene numbering :return: (int, int) a tuple with (season, episode) """ if indexer_id is None or absolute_number is None: return absolute_number indexer_id = int(indexer_id) indexer = int(indexer) showObj = Show.find(sickbeard.showList, indexer_id) if showObj and not showObj.is_scene: return absolute_number result = find_scene_absolute_numbering(indexer_id, indexer, absolute_number) if result: return result else: if fallback_to_xem: xem_result = find_xem_absolute_numbering(indexer_id, indexer, absolute_number) if xem_result: return xem_result return absolute_number
def downloadSubtitleMissed(self, *args, **kwargs): to_download = {} # make a list of all shows and their associated args for arg in kwargs: indexer_id, what = arg.split("-") # we don't care about unchecked checkboxes if kwargs[arg] != "on": continue if indexer_id not in to_download: to_download[indexer_id] = [] to_download[indexer_id].append(what) for cur_indexer_id in to_download: # get a list of all the eps we want to download subtitles if they just said "all" if "all" in to_download[cur_indexer_id]: main_db_con = db.DBConnection() all_eps_results = main_db_con.select( "SELECT season, episode FROM tv_episodes WHERE (status LIKE '%4' OR status LIKE '%6') {0} AND showid = ? AND location != ''".format( ("AND season != 0 ", "")[settings.SUBTITLES_INCLUDE_SPECIALS] ), [cur_indexer_id], ) to_download[cur_indexer_id] = [str(x["season"]) + "x" + str(x["episode"]) for x in all_eps_results] for epResult in to_download[cur_indexer_id]: season, episode = epResult.split("x") show = Show.find(settings.showList, int(cur_indexer_id)) show.getEpisode(season, episode).download_subtitles() return self.redirect("/manage/subtitleMissed/")
def get_scene_numbering(indexer_id, indexer, season, episode, fallback_to_xem=True): """ Returns a tuple, (season, episode), with the scene numbering (if there is one), otherwise returns the xem numbering (if fallback_to_xem is set), otherwise returns the indexer numbering. (so the return values will always be set) :param indexer_id: int :param season: int :param episode: int :param fallback_to_xem: bool If set (the default), check xem for matches if there is no local scene numbering :return: (int, int) a tuple with (season, episode) """ if indexer_id is None or season is None or episode is None: return season, episode showObj = Show.find(settings.showList, int(indexer_id)) if showObj and not showObj.is_scene: return season, episode result = find_scene_numbering(int(indexer_id), int(indexer), season, episode) if result: return result else: if fallback_to_xem: xem_result = find_xem_numbering(int(indexer_id), int(indexer), season, episode) if xem_result: return xem_result return season, episode
def set_scene_numbering(indexer_id, indexer, season=None, episode=None, # pylint:disable=too-many-arguments absolute_number=None, sceneSeason=None, sceneEpisode=None, sceneAbsolute=None): """ Set scene numbering for a season/episode. To clear the scene numbering, leave both sceneSeason and sceneEpisode as None. """ if indexer_id is None: return indexer_id = int(indexer_id) indexer = int(indexer) main_db_con = db.DBConnection() if season and episode: main_db_con.action( "INSERT OR IGNORE INTO scene_numbering (indexer, indexer_id, season, episode) VALUES (?,?,?,?)", [indexer, indexer_id, season, episode]) main_db_con.action( "UPDATE scene_numbering SET scene_season = ?, scene_episode = ? WHERE indexer = ? and indexer_id = ? and season = ? and episode = ?", [sceneSeason, sceneEpisode, indexer, indexer_id, season, episode]) elif absolute_number: main_db_con.action( "INSERT OR IGNORE INTO scene_numbering (indexer, indexer_id, absolute_number) VALUES (?,?,?)", [indexer, indexer_id, absolute_number]) main_db_con.action( "UPDATE scene_numbering SET scene_absolute_number = ? WHERE indexer = ? and indexer_id = ? and absolute_number = ?", [sceneAbsolute, indexer, indexer_id, absolute_number]) # Reload data from DB so that cache and db are in sync show = Show.find(sickbeard.showList, indexer_id) show.flushEpisodes()
def test_find(self): """ Test find tv shows by indexer_id """ sickbeard.QUALITY_DEFAULT = Quality.FULLHDTV sickbeard.showList = [] show123 = TestTVShow(0, 123) show456 = TestTVShow(0, 456) show789 = TestTVShow(0, 789) shows = [show123, show456, show789] shows_duplicate = shows + shows test_cases = { (False, None): None, (False, ''): None, (False, '123'): None, (False, 123): None, (False, 12.3): None, (True, None): None, (True, ''): None, (True, '123'): None, (True, 123): show123, (True, 12.3): None, (True, 456): show456, (True, 789): show789, } unicode_test_cases = { (False, ''): None, (False, '123'): None, (True, ''): None, (True, '123'): None, } for tests in test_cases, unicode_test_cases: for ((use_shows, indexer_id), result) in six.iteritems(tests): if use_shows: self.assertEqual(Show.find(shows, indexer_id), result) else: self.assertEqual(Show.find(None, indexer_id), result) with self.assertRaises(MultipleShowObjectsException): Show.find(shows_duplicate, 456)
def test_find(self): """ Test find tv shows by indexer_id """ settings.QUALITY_DEFAULT = Quality.FULLHDTV settings.showList = [] show123 = TestTVShow(0, 123) show456 = TestTVShow(0, 456) show789 = TestTVShow(0, 789) shows = [show123, show456, show789] shows_duplicate = shows + shows test_cases = { (False, None): None, (False, ""): None, (False, "123"): None, (False, 123): None, (False, 12.3): None, (True, None): None, (True, ""): None, (True, "123"): show123, (True, 123): show123, (True, 12.3): None, (True, 456): show456, (True, 789): show789, } unicode_test_cases = { (False, ""): None, (False, "123"): None, (True, ""): None, (True, "123"): show123, } for tests in test_cases, unicode_test_cases: for ((use_shows, indexer_id), result) in tests.items(): if use_shows: assert Show.find(shows, indexer_id) == result else: assert Show.find(None, indexer_id) == result with self.assertRaises(MultipleShowObjectsException): Show.find(shows_duplicate, 456)
def get_show(self): """ :return: The show object associated with ``self.indexer_id`` or ``None`` """ try: return Show.find(sickbeard.showList, self.indexer_id) except MultipleShowObjectsException: return None
def get_all_scene_exceptions(indexer_id): """ Get all scene exceptions for a show ID :param indexer_id: ID to check :return: dict of exceptions """ all_exceptions_dict = {} cache_db_con = db.DBConnection("cache.db") exceptions = cache_db_con.select("SELECT show_name, season, custom FROM scene_exceptions WHERE indexer_id = ?", [indexer_id]) if indexer_id in exceptions_cache: del exceptions_cache[indexer_id] for cur_exception in exceptions: if cur_exception["season"] not in all_exceptions_dict: all_exceptions_dict[cur_exception["season"]] = [] all_exceptions_dict[cur_exception["season"]].append({"show_name": cur_exception["show_name"], "custom": bool(cur_exception["custom"])}) if indexer_id not in exceptions_cache: exceptions_cache[indexer_id] = {} if cur_exception["season"] not in exceptions_cache[indexer_id]: exceptions_cache[indexer_id][cur_exception["season"]] = [] exceptions_cache[indexer_id][cur_exception["season"]].append(cur_exception["show_name"]) show = Show.find(settings.showList, indexer_id) if show: sanitized_name = helpers.full_sanitizeSceneName(show.show_name) sanitized_custom_name = helpers.full_sanitizeSceneName(show.custom_name) if sanitized_name or sanitized_custom_name: if -1 not in all_exceptions_dict: all_exceptions_dict[-1] = [] if indexer_id not in exceptions_cache: exceptions_cache[indexer_id] = {} if -1 not in exceptions_cache[indexer_id]: exceptions_cache[indexer_id][-1] = [] if sanitized_name: all_exceptions_dict[-1].append({"show_name": sanitized_name, "custom": False}) if sanitized_name not in exceptions_cache[indexer_id][-1]: exceptions_cache[indexer_id][-1].append(sanitized_name) if sanitized_custom_name: all_exceptions_dict[-1].append({"show_name": sanitized_custom_name, "custom": False}) if sanitized_custom_name not in exceptions_cache[indexer_id][-1]: exceptions_cache[indexer_id][-1].append(sanitized_custom_name) logger.debug(f"get_all_scene_exceptions: {all_exceptions_dict}") logger.debug(f"exceptions_cache for {indexer_id}: {exceptions_cache.get(indexer_id)}") return all_exceptions_dict
def _add_cache_entry(self, name, url, parse_result=None, indexer_id=0): # check if we passed in a parsed result or should we try and create one if not parse_result: # create show_obj from indexer_id if available show_obj = None if indexer_id: show_obj = Show.find(sickbeard.showList, indexer_id) try: parse_result = NameParser(showObj=show_obj).parse(name) except (InvalidNameException, InvalidShowException) as error: logger.log("{0}".format(error), logger.DEBUG) return None if not parse_result or not parse_result.series_name: return None # if we made it this far then lets add the parsed result to cache for usager later on season = parse_result.season_number if parse_result.season_number else 1 episodes = parse_result.episode_numbers if season and episodes: # store episodes as a seperated string episode_text = "|" + "|".join( {str(episode) for episode in episodes if episode}) + "|" # get the current timestamp cur_timestamp = int( time.mktime(datetime.datetime.today().timetuple())) # get quality of release quality = parse_result.quality assert isinstance(name, six.text_type) # get release group release_group = parse_result.release_group # get version version = parse_result.version logger.log( "Added RSS item: [" + name + "] to cache: [" + self.provider_id + "]", logger.DEBUG) return [ "INSERT OR IGNORE INTO [" + self.provider_id + "] (name, season, episodes, indexerid, url, time, quality, release_group, version) VALUES (?,?,?,?,?,?,?,?,?)", [ name, season, episode_text, parse_result.show.indexerid, url, cur_timestamp, quality, release_group, version ] ]
def updateEpisodes(self): """ Sets episodes to wanted that are in trakt watchlist """ logger.log("SHOW_WATCHLIST::CHECK::START - Trakt Episode Watchlist", logger.DEBUG) self._getEpisodeWatchlist() if not self.EpisodeWatchlist: logger.log( "No episode found in your watchlist, aborting episode update", logger.DEBUG) return managed_show = [] for index, indexer in sickchill.indexer: if indexer.slug in self.EpisodeWatchlist: for show_el in self.EpisodeWatchlist[indexer.slug]: indexer_id = int(show_el) show = self.EpisodeWatchlist[indexer.slug][show_el] newShow = Show.find(sickbeard.showList, indexer_id) try: if newShow is None: if indexer_id not in managed_show: self.addDefaultShow(index, indexer_id, show['title'], SKIPPED) managed_show.append(indexer_id) for season_el in show['seasons']: season = int(season_el) for episode_el in show['seasons'][ season_el]['episodes']: self.todoWanted.append( (indexer_id, season, int(episode_el))) else: if newShow.indexer == indexer: for season_el in show['seasons']: season = int(season_el) for episode_el in show['seasons'][ season_el]['episodes']: setEpisodeToWanted( newShow, season, int(episode_el)) except TypeError: logger.log( "Could not parse the output from trakt for {0} ". format(show["title"]), logger.DEBUG) logger.log("SHOW_WATCHLIST::CHECK::FINISH - Trakt Episode Watchlist", logger.DEBUG)
def fetch_indexer_favorites(self): """Get favorited show information from Indexers""" indexer_favorites = sickchill.indexer.get_indexer_favorites() results = [] for series in indexer_favorites: if Show().find(settings.showList, series.id): continue results.append(series) self.cache_image(series.id) return results
def test_validate_indexer_id(self): """ Tests if the indexer_id is valid and if so if it returns the right show """ settings.QUALITY_DEFAULT = Quality.FULLHDTV settings.showList = [] show123 = TestTVShow(0, 123) show456 = TestTVShow(0, 456) show789 = TestTVShow(0, 789) settings.showList = [ show123, show456, show789, ] invalid_show_id = ("Invalid show ID", None) indexer_id_list = [ None, "", "", "123", "123", "456", "456", "789", "789", 123, 456, 789, ["123", "456"], ["123", "456"], [123, 456] ] results_list = [ invalid_show_id, invalid_show_id, invalid_show_id, (None, show123), (None, show123), (None, show456), (None, show456), (None, show789), (None, show789), (None, show123), (None, show456), (None, show789), invalid_show_id, invalid_show_id, invalid_show_id, ] assert len(indexer_id_list) == len( results_list ), "Number of parameters ({0:d}) and results ({1:d}) does not match".format( len(indexer_id_list), len(results_list)) for (index, indexer_id) in enumerate(indexer_id_list): assert Show._validate_indexer_id(indexer_id) == results_list[index]
def get_id_from_name(name): """ Looks up the given name in the scene_names table in cache.db. :param name: The show name to look up. :return: the TVDB id that resulted from the cache lookup or None if the show wasn't found in the cache """ name = helpers.full_sanitizeSceneName(name) if name not in name_cache: cache_db_con = db.DBConnection("cache.db") results = cache_db_con.select_one("SELECT indexer_id FROM scene_names WHERE name = ?", [name]) if results: indexer_id = results["indexer_id"] show = Show.find(settings.showList, indexer_id) if show: build_name_cache(show) else: return int(name_cache[name])
def updateEpisodes(self): """ Sets episodes to wanted that are in trakt watchlist """ logger.log("SHOW_WATCHLIST::CHECK::START - Trakt Episode Watchlist", logger.DEBUG) self._getEpisodeWatchlist() if not self.EpisodeWatchlist: logger.log("No episode found in your watchlist, aborting episode update", logger.DEBUG) return managed_show = [] indexer = int(sickbeard.TRAKT_DEFAULT_INDEXER) trakt_id = sickbeard.indexerApi(indexer).config['trakt_id'] for show_el in self.EpisodeWatchlist[trakt_id]: indexer_id = int(show_el) show = self.EpisodeWatchlist[trakt_id][show_el] newShow = Show.find(sickbeard.showList, indexer_id) try: if newShow is None: if indexer_id not in managed_show: self.addDefaultShow(indexer, indexer_id, show['title'], SKIPPED) managed_show.append(indexer_id) for season_el in show['seasons']: season = int(season_el) for episode_el in show['seasons'][season_el]['episodes']: self.todoWanted.append((indexer_id, season, int(episode_el))) else: if newShow.indexer == indexer: for season_el in show['seasons']: season = int(season_el) for episode_el in show['seasons'][season_el]['episodes']: setEpisodeToWanted(newShow, season, int(episode_el)) except TypeError: logger.log("Could not parse the output from trakt for {0} ".format(show["title"]), logger.DEBUG) logger.log("SHOW_WATCHLIST::CHECK::FINISH - Trakt Episode Watchlist", logger.DEBUG)
def index(self, show=None, imageType="", provider: int = None): if not show: return self._genericMessage(_("Error"), _("You must specify a show")) show_obj = Show.find(sickchill.settings.showList, int(show)) if not show_obj: return self._genericMessage(_("Error"), _("Show not in show list")) self.set_header("Cache-Control", "max-age=0,no-cache,no-store") self.set_header("Content-Type", "application/json") provider = int(provider) if provider == ShowIndexer.FANART: metadata_generator = GenericMetadata() images = metadata_generator._retrieve_show_image_urls_from_fanart( show_obj, imageType, multiple=True) images = list({ "image": image, "thumb": re.sub("/fanart/", "/preview/", image) } for image in images) elif provider == ShowIndexer.TMDB: metadata_generator = GenericMetadata() images = metadata_generator._retrieve_show_image_urls_from_tmdb( show_obj, imageType, multiple=True) images = list({"image": image, "thumb": image} for image in images) else: if "poster" == imageType: images = sickchill.indexer[provider].series_poster_url( show_obj, multiple=True) elif "banner" == imageType: images = sickchill.indexer[provider].series_banner_url( show_obj, multiple=True) elif "fanart" == imageType: images = sickchill.indexer[provider].series_fanart_url( show_obj, multiple=True) else: return self._genericMessage(_("Error"), _("Invalid image provider")) images = list({"image": image, "thumb": image} for image in images) return json.dumps(images)
def set_scene_numbering(indexer_id, indexer, season=None, episode=None, absolute_number=None, sceneSeason=None, sceneEpisode=None, sceneAbsolute=None): """ Set scene numbering for a season/episode. To clear the scene numbering, leave both sceneSeason and sceneEpisode as None. """ if indexer_id is None: return indexer_id = int(indexer_id) indexer = int(indexer) main_db_con = db.DBConnection() if season and episode: main_db_con.action( "INSERT OR IGNORE INTO scene_numbering (indexer, indexer_id, season, episode) VALUES (?,?,?,?)", [indexer, indexer_id, season, episode]) main_db_con.action( "UPDATE scene_numbering SET scene_season = ?, scene_episode = ? WHERE indexer = ? and indexer_id = ? and season = ? and episode = ?", [sceneSeason, sceneEpisode, indexer, indexer_id, season, episode], ) elif absolute_number: main_db_con.action( "INSERT OR IGNORE INTO scene_numbering (indexer, indexer_id, absolute_number) VALUES (?,?,?)", [indexer, indexer_id, absolute_number]) main_db_con.action( "UPDATE scene_numbering SET scene_absolute_number = ? WHERE indexer = ? and indexer_id = ? and absolute_number = ?", [sceneAbsolute, indexer, indexer_id, absolute_number], ) # Reload data from DB so that cache and db are in sync show = Show.find(settings.showList, indexer_id) show.flushEpisodes()
def test_validate_indexer_id(self): """ Tests if the indexer_id is valid and if so if it returns the right show """ sickbeard.QUALITY_DEFAULT = Quality.FULLHDTV sickbeard.showList = [] show123 = TestTVShow(0, 123) show456 = TestTVShow(0, 456) show789 = TestTVShow(0, 789) sickbeard.showList = [ show123, show456, show789, ] invalid_show_id = ('Invalid show ID', None) indexer_id_list = [ None, '', '', '123', '123', '456', '456', '789', '789', 123, 456, 789, ['123', '456'], ['123', '456'], [123, 456] ] results_list = [ invalid_show_id, invalid_show_id, invalid_show_id, (None, show123), (None, show123), (None, show456), (None, show456), (None, show789), (None, show789), (None, show123), (None, show456), (None, show789), invalid_show_id, invalid_show_id, invalid_show_id ] self.assertEqual( len(indexer_id_list), len(results_list), 'Number of parameters ({0:d}) and results ({1:d}) does not match'. format(len(indexer_id_list), len(results_list))) for (index, indexer_id) in enumerate(indexer_id_list): self.assertEqual(Show._validate_indexer_id(indexer_id), results_list[index]) # pylint: disable=protected-access
def test_validate_indexer_id(self): """ Tests if the indexer_id is valid and if so if it returns the right show """ sickbeard.QUALITY_DEFAULT = Quality.FULLHDTV sickbeard.showList = [] show123 = TestTVShow(0, 123) show456 = TestTVShow(0, 456) show789 = TestTVShow(0, 789) sickbeard.showList = [ show123, show456, show789, ] invalid_show_id = ('Invalid show ID', None) indexer_id_list = [ None, '', '', '123', '123', '456', '456', '789', '789', 123, 456, 789, ['123', '456'], ['123', '456'], [123, 456] ] results_list = [ invalid_show_id, invalid_show_id, invalid_show_id, (None, show123), (None, show123), (None, show456), (None, show456), (None, show789), (None, show789), (None, show123), (None, show456), (None, show789), invalid_show_id, invalid_show_id, invalid_show_id ] self.assertEqual( len(indexer_id_list), len(results_list), 'Number of parameters ({0:d}) and results ({1:d}) does not match'.format(len(indexer_id_list), len(results_list)) ) for (index, indexer_id) in enumerate(indexer_id_list): self.assertEqual(Show._validate_indexer_id(indexer_id), results_list[index]) # pylint: disable=protected-access
def run(self): super(QueueItemAdd, self).run() if self.showDir: try: assert isinstance(self.showDir, six.text_type) except AssertionError: logger.log(traceback.format_exc(), logger.WARNING) self._finish_early() return logger.log('Starting to add show {0}'.format( 'by ShowDir: {0}'.format(self.showDir) if self. showDir else 'by Indexer Id: {0}'.format(self.indexer_id))) # make sure the Indexer IDs are valid try: s = sickchill.indexer.series_by_id(indexerid=self.indexer_id, indexer=self.indexer, language=self.lang) if not s: error_string = 'Could not find show with id:{0} on {1}, skipping'.format( self.indexer_id, sickchill.indexer.name(self.indexer)) logger.log(error_string) ui.notifications.error('Unable to add show', error_string) self._finish_early() return # Let's try to create the show Dir if it's not provided. This way we force the show dir to build build using the # Indexers provided series name if self.root_dir and not self.showDir: if not s.seriesName: logger.log( 'Unable to get a show {0}, can\'t add the show'.format( self.showDir)) self._finish_early() return self.showDir = ek(os.path.join, self.root_dir, sanitize_filename(s.seriesName)) dir_exists = makeDir(self.showDir) if not dir_exists: logger.log( 'Unable to create the folder {0}, can\'t add the show'. format(self.showDir)) self._finish_early() return chmodAsParent(self.showDir) # this usually only happens if they have an NFO in their show dir which gave us a Indexer ID that has no proper english version of the show if getattr(s, 'seriesName', None) is None: # noinspection PyPep8 error_string = 'Show in {0} has no name on {1}, probably searched with the wrong language. Delete .nfo and add manually in the correct language.'.format( self.showDir, sickchill.indexer.name(self.indexer)) logger.log(error_string, logger.WARNING) ui.notifications.error('Unable to add show', error_string) self._finish_early() return except Exception as error: error_string = 'Unable to look up the show in {0} on {1} using ID {2}, not using the NFO. Delete .nfo and try adding manually again.'.format( self.showDir, sickchill.indexer.name(self.indexer), self.indexer_id) logger.log('{0}: {1}'.format(error_string, error), logger.ERROR) ui.notifications.error('Unable to add show', error_string) if sickbeard.USE_TRAKT: trakt_api = TraktAPI(sickbeard.SSL_VERIFY, sickbeard.TRAKT_TIMEOUT) title = self.showDir.split('/')[-1] data = { 'shows': [{ 'title': title, 'ids': { sickchill.indexer.slug(self.indexer): self.indexer_id } }] } trakt_api.traktRequest('sync/watchlist/remove', data, method='POST') self._finish_early() return try: try: newShow = TVShow(self.indexer, self.indexer_id, self.lang) except MultipleShowObjectsException as error: # If we have the show in our list, but the location is wrong, lets fix it and refresh! existing_show = Show.find(sickbeard.showList, self.indexer_id) # noinspection PyProtectedMember if existing_show and not ek(os.path.isdir, existing_show._location): newShow = existing_show else: raise error newShow.loadFromIndexer() self.show = newShow # set up initial values self.show.location = self.showDir self.show.subtitles = self.subtitles if self.subtitles is not None else sickbeard.SUBTITLES_DEFAULT self.show.subtitles_sr_metadata = self.subtitles_sr_metadata self.show.quality = self.quality if self.quality else sickbeard.QUALITY_DEFAULT self.show.season_folders = self.season_folders if self.season_folders is not None else sickbeard.SEASON_FOLDERS_DEFAULT self.show.anime = self.anime if self.anime is not None else sickbeard.ANIME_DEFAULT self.show.scene = self.scene if self.scene is not None else sickbeard.SCENE_DEFAULT self.show.paused = self.paused if self.paused is not None else False # set up default new/missing episode status logger.log( 'Setting all episodes to the specified default status: {0}'. format(self.show.default_ep_status)) self.show.default_ep_status = self.default_status if self.show.anime: self.show.release_groups = BlackAndWhiteList( self.show.indexerid) if self.blacklist: self.show.release_groups.set_black_keywords(self.blacklist) if self.whitelist: self.show.release_groups.set_white_keywords(self.whitelist) # # be smart-ish about this # if self.show.genre and 'talk show' in self.show.genre.lower(): # self.show.air_by_date = 1 # if self.show.genre and 'documentary' in self.show.genre.lower(): # self.show.air_by_date = 0 # if self.show.classification and 'sports' in self.show.classification.lower(): # self.show.sports = 1 except Exception as error: error_string = 'Unable to add {0} due to an error with {1}'.format( self.show.name if self.show else 'show', sickchill.indexer.name(self.indexer)) logger.log('{0}: {1}'.format(error_string, error), logger.ERROR) logger.log('Error trying to add show: {0}'.format(error), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) ui.notifications.error('Unable to add show', error_string) self._finish_early() return except MultipleShowObjectsException: error_string = 'The show in {0} is already in your show list, skipping'.format( self.showDir) logger.log(error_string, logger.WARNING) ui.notifications.error('Show skipped', error_string) self._finish_early() return logger.log('Retrieving show info from IMDb', logger.DEBUG) try: self.show.loadIMDbInfo() except imdb_exceptions.IMDbError as error: logger.log(' Something wrong on IMDb api: {0}'.format(error), logger.WARNING) except Exception as error: logger.log('Error loading IMDb info: {0}'.format(error), logger.ERROR) try: self.show.saveToDB() except Exception as error: logger.log( 'Error saving the show to the database: {0}'.format(error), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) self._finish_early() raise # add it to the show list if not Show.find(sickbeard.showList, self.indexer_id): sickbeard.showList.append(self.show) try: self.show.loadEpisodesFromIndexer() except Exception as error: logger.log( 'Error with {0}, not creating episode list: {1}'.format( self.show.idxr.name, error), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) # update internal name cache name_cache.buildNameCache(self.show) try: self.show.loadEpisodesFromDir() except Exception as error: logger.log('Error searching dir for episodes: {0}'.format(error), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) # if they set default ep status to WANTED then run the backlog to search for episodes # FIXME: This needs to be a backlog queue item!!! if self.show.default_ep_status == WANTED: logger.log( 'Launching backlog for this show since its episodes are WANTED' ) sickbeard.backlogSearchScheduler.action.searchBacklog([self.show]) self.show.writeMetadata() self.show.updateMetadata() self.show.populateCache() self.show.flushEpisodes() if sickbeard.USE_TRAKT: # if there are specific episodes that need to be added by trakt sickbeard.traktCheckerScheduler.action.manageNewShow(self.show) # add show to trakt.tv library if sickbeard.TRAKT_SYNC: sickbeard.traktCheckerScheduler.action.addShowToTraktLibrary( self.show) if sickbeard.TRAKT_SYNC_WATCHLIST: logger.log('update watchlist') notifiers.trakt_notifier.update_watchlist(show_obj=self.show) # Load XEM data to DB for show scene_numbering.xem_refresh(self.show.indexerid, self.show.indexer, force=True) # check if show has XEM mapping so we can determine if searches should go by scene numbering or indexer numbering. if not self.scene and scene_numbering.get_xem_numbering_for_show( self.show.indexerid, self.show.indexer): self.show.scene = 1 # After initial add, set to default_status_after. self.show.default_ep_status = self.default_status_after super(QueueItemAdd, self).finish() self.finish()
def run(self): # pylint: disable=too-many-branches, too-many-statements, too-many-return-statements super(QueueItemAdd, self).run() if self.showDir: try: assert isinstance(self.showDir, six.text_type) except AssertionError: logger.log(traceback.format_exc(), logger.WARNING) self._finish_early() return logger.log('Starting to add show {0}'.format('by ShowDir: {0}'.format(self.showDir) if self.showDir else 'by Indexer Id: {0}'.format(self.indexer_id))) # make sure the Indexer IDs are valid try: lINDEXER_API_PARMS = sickbeard.indexerApi(self.indexer).api_params.copy() lINDEXER_API_PARMS['language'] = self.lang or sickbeard.INDEXER_DEFAULT_LANGUAGE logger.log('{0}: {1!r}'.format(sickbeard.indexerApi(self.indexer).name, lINDEXER_API_PARMS)) t = sickbeard.indexerApi(self.indexer).indexer(**lINDEXER_API_PARMS) s = t[self.indexer_id] # Let's try to create the show Dir if it's not provided. This way we force the show dir to build build using the # Indexers provided series name if self.root_dir and not self.showDir: show_name = get_showname_from_indexer(self.indexer, self.indexer_id, self.lang) if not show_name: logger.log('Unable to get a show {0}, can\'t add the show'.format(self.showDir)) self._finish_early() return self.showDir = ek(os.path.join, self.root_dir, sanitize_filename(show_name)) dir_exists = makeDir(self.showDir) if not dir_exists: logger.log('Unable to create the folder {0}, can\'t add the show'.format(self.showDir)) self._finish_early() return chmodAsParent(self.showDir) # this usually only happens if they have an NFO in their show dir which gave us a Indexer ID that has no proper english version of the show if getattr(s, 'seriesname', None) is None: # noinspection PyPep8 error_string = 'Show in {0} has no name on {1}, probably searched with the wrong language. Delete .nfo and add manually in the correct language.'.format( self.showDir, sickbeard.indexerApi(self.indexer).name) logger.log(error_string, logger.WARNING) ui.notifications.error('Unable to add show', error_string) self._finish_early() return # if the show has no episodes/seasons if not s: error_string = 'Show {0} is on {1} but contains no season/episode data.'.format( s[b'seriesname'], sickbeard.indexerApi(self.indexer).name) logger.log(error_string) ui.notifications.error('Unable to add show', error_string) self._finish_early() return except Exception as error: error_string = 'Unable to look up the show in {0} on {1} using ID {2}, not using the NFO. Delete .nfo and try adding manually again.'.format( self.showDir, sickbeard.indexerApi(self.indexer).name, self.indexer_id) logger.log('{0}: {1}'.format(error_string, error), logger.ERROR) ui.notifications.error( 'Unable to add show', error_string) if sickbeard.USE_TRAKT: trakt_id = sickbeard.indexerApi(self.indexer).config[b'trakt_id'] trakt_api = TraktAPI(sickbeard.SSL_VERIFY, sickbeard.TRAKT_TIMEOUT) title = self.showDir.split('/')[-1] data = { 'shows': [ { 'title': title, 'ids': {} } ] } if trakt_id == 'tvdb_id': data['shows'][0]['ids']['tvdb'] = self.indexer_id else: data['shows'][0]['ids']['tvrage'] = self.indexer_id trakt_api.traktRequest('sync/watchlist/remove', data, method='POST') self._finish_early() return try: try: newShow = TVShow(self.indexer, self.indexer_id, self.lang) except MultipleShowObjectsException as error: # If we have the show in our list, but the location is wrong, lets fix it and refresh! existing_show = Show.find(sickbeard.showList, self.indexer_id) # noinspection PyProtectedMember if existing_show and not ek(os.path.isdir, existing_show._location): # pylint: disable=protected-access newShow = existing_show else: raise error newShow.loadFromIndexer() self.show = newShow # set up initial values self.show.location = self.showDir self.show.subtitles = self.subtitles if self.subtitles is not None else sickbeard.SUBTITLES_DEFAULT self.show.subtitles_sr_metadata = self.subtitles_sr_metadata self.show.quality = self.quality if self.quality else sickbeard.QUALITY_DEFAULT self.show.season_folders = self.season_folders if self.season_folders is not None else sickbeard.SEASON_FOLDERS_DEFAULT self.show.anime = self.anime if self.anime is not None else sickbeard.ANIME_DEFAULT self.show.scene = self.scene if self.scene is not None else sickbeard.SCENE_DEFAULT self.show.paused = self.paused if self.paused is not None else False # set up default new/missing episode status logger.log('Setting all episodes to the specified default status: {0}' .format(self.show.default_ep_status)) self.show.default_ep_status = self.default_status if self.show.anime: self.show.release_groups = BlackAndWhiteList(self.show.indexerid) if self.blacklist: self.show.release_groups.set_black_keywords(self.blacklist) if self.whitelist: self.show.release_groups.set_white_keywords(self.whitelist) # # be smart-ish about this # if self.show.genre and 'talk show' in self.show.genre.lower(): # self.show.air_by_date = 1 # if self.show.genre and 'documentary' in self.show.genre.lower(): # self.show.air_by_date = 0 # if self.show.classification and 'sports' in self.show.classification.lower(): # self.show.sports = 1 except sickbeard.indexer_exception as error: error_string = 'Unable to add {0} due to an error with {1}'.format( self.show.name if self.show else 'show', sickbeard.indexerApi(self.indexer).name) logger.log('{0}: {1}'.format(error_string, error), logger.ERROR) ui.notifications.error('Unable to add show', error_string) self._finish_early() return except MultipleShowObjectsException: error_string = 'The show in {0} is already in your show list, skipping'.format(self.showDir) logger.log(error_string, logger.WARNING) ui.notifications.error('Show skipped', error_string) self._finish_early() return except Exception as error: logger.log('Error trying to add show: {0}'.format(error), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) self._finish_early() raise logger.log('Retrieving show info from IMDb', logger.DEBUG) try: self.show.loadIMDbInfo() except imdb_exceptions.IMDbError as error: logger.log(' Something wrong on IMDb api: {0}'.format(error), logger.WARNING) except Exception as error: logger.log('Error loading IMDb info: {0}'.format(error), logger.ERROR) try: self.show.saveToDB() except Exception as error: logger.log('Error saving the show to the database: {0}'.format(error), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) self._finish_early() raise # add it to the show list if not Show.find(sickbeard.showList, self.indexer_id): sickbeard.showList.append(self.show) try: self.show.loadEpisodesFromIndexer() except Exception as error: logger.log( 'Error with {0}, not creating episode list: {1}'.format (sickbeard.indexerApi(self.show.indexer).name, error), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) # update internal name cache name_cache.buildNameCache(self.show) try: self.show.loadEpisodesFromDir() except Exception as error: logger.log('Error searching dir for episodes: {0}'.format(error), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) # if they set default ep status to WANTED then run the backlog to search for episodes # FIXME: This needs to be a backlog queue item!!! if self.show.default_ep_status == WANTED: logger.log('Launching backlog for this show since its episodes are WANTED') sickbeard.backlogSearchScheduler.action.searchBacklog([self.show]) self.show.writeMetadata() self.show.updateMetadata() self.show.populateCache() self.show.flushEpisodes() if sickbeard.USE_TRAKT: # if there are specific episodes that need to be added by trakt sickbeard.traktCheckerScheduler.action.manageNewShow(self.show) # add show to trakt.tv library if sickbeard.TRAKT_SYNC: sickbeard.traktCheckerScheduler.action.addShowToTraktLibrary(self.show) if sickbeard.TRAKT_SYNC_WATCHLIST: logger.log('update watchlist') notifiers.trakt_notifier.update_watchlist(show_obj=self.show) # Load XEM data to DB for show scene_numbering.xem_refresh(self.show.indexerid, self.show.indexer, force=True) # check if show has XEM mapping so we can determine if searches should go by scene numbering or indexer numbering. if not self.scene and scene_numbering.get_xem_numbering_for_show(self.show.indexerid, self.show.indexer): self.show.scene = 1 # After initial add, set to default_status_after. self.show.default_ep_status = self.default_status_after super(QueueItemAdd, self).finish() self.finish()
def find_needed_episodes(self, episode, manualSearch=False, downCurQuality=False): needed_eps = {} cl = [] cache_db_con = self._get_db() if not episode: sql_results = cache_db_con.select( "SELECT * FROM results WHERE provider = ?", [self.provider_id]) elif not isinstance(episode, list): sql_results = cache_db_con.select( "SELECT * FROM results WHERE provider = ? AND indexerid = ? AND season = ? AND episodes LIKE ?", [ self.provider_id, episode.show.indexerid, episode.season, "%|" + str(episode.episode) + "|%" ]) else: for ep_obj in episode: cl.append([ "SELECT * FROM results WHERE provider = ? AND indexerid = ? AND season = ? AND episodes LIKE ? AND quality IN (" + ",".join([str(x) for x in ep_obj.wantedQuality]) + ")", [ self.provider_id, ep_obj.show.indexerid, ep_obj.season, "%|" + str(ep_obj.episode) + "|%" ] ]) sql_results = cache_db_con.mass_action(cl, fetchall=True) sql_results = list(itertools.chain(*sql_results)) # for each cache entry for cur_result in sql_results: # get the show object, or if it's not one of our shows then ignore it show_obj = Show.find(settings.showList, int(cur_result["indexerid"])) if not show_obj: continue # ignored/required words, and non-tv junk if not show_name_helpers.filter_bad_releases(cur_result["name"], show=show_obj): continue # skip if provider is anime only and show is not anime if self.provider.anime_only and not show_obj.is_anime: logger.debug("" + str(show_obj.name) + " is not an anime, skiping") continue # get season and ep data (ignoring multi-eps for now) cur_season = int(cur_result["season"]) if cur_season == -1: continue cur_ep = cur_result["episodes"].split("|")[1] if not cur_ep: continue cur_ep = int(cur_ep) cur_quality = int(cur_result["quality"]) cur_release_group = cur_result["release_group"] cur_version = cur_result["version"] # if the show says we want that episode then add it to the list if not show_obj.wantEpisode(cur_season, cur_ep, cur_quality, manualSearch, downCurQuality): logger.debug("Ignoring " + cur_result["name"]) continue ep_obj = show_obj.getEpisode(cur_season, cur_ep) # build a result object title = cur_result["name"] url = cur_result["url"] logger.info("Found result " + title + " at " + url) result = self.provider.get_result([ep_obj]) result.show = show_obj result.url = url result.name = title result.quality = cur_quality result.release_group = cur_release_group result.version = cur_version result.content = None # add it to the list if ep_obj not in needed_eps: needed_eps[ep_obj] = [result] else: needed_eps[ep_obj].append(result) # datetime stamp this search so cache gets cleared self.set_last_search() return needed_eps
def run(self, force=False): # pylint:disable=too-many-branches """ Runs the daily searcher, queuing selected episodes for search :param force: Force search """ if self.amActive: return self.amActive = True _ = force logger.log("Searching for new released episodes ...") if not network_timezones.network_dict: network_timezones.update_network_dict() if network_timezones.network_dict: curDate = (datetime.date.today() + datetime.timedelta(days=1)).toordinal() else: curDate = (datetime.date.today() + datetime.timedelta(days=2)).toordinal() curTime = datetime.datetime.now(network_timezones.sb_timezone) main_db_con = db.DBConnection() sql_results = main_db_con.select("SELECT showid, airdate, season, episode FROM tv_episodes WHERE status = ? AND (airdate <= ? and airdate > 1)", [common.UNAIRED, curDate]) sql_l = [] show = None for sqlEp in sql_results: try: if not show or int(sqlEp[b"showid"]) != show.indexerid: show = Show.find(sickbeard.showList, int(sqlEp[b"showid"])) # for when there is orphaned series in the database but not loaded into our showlist if not show or show.paused: continue except MultipleShowObjectsException: logger.log("ERROR: expected to find a single show matching " + str(sqlEp[b'showid'])) continue if show.airs and show.network: # This is how you assure it is always converted to local time air_time = network_timezones.parse_date_time(sqlEp[b'airdate'], show.airs, show.network).astimezone(network_timezones.sb_timezone) # filter out any episodes that haven't started airing yet, # but set them to the default status while they are airing # so they are snatched faster if air_time > curTime: continue ep = show.getEpisode(sqlEp[b"season"], sqlEp[b"episode"]) with ep.lock: if ep.season == 0: logger.log("New episode " + ep.pretty_name() + " airs today, setting status to SKIPPED because is a special season") ep.status = common.SKIPPED else: logger.log("New episode {0} airs today, setting to default episode status for this show: {1}".format(ep.pretty_name(), common.statusStrings[ep.show.default_ep_status])) ep.status = ep.show.default_ep_status sql_l.append(ep.get_sql()) if sql_l: main_db_con = db.DBConnection() main_db_con.mass_action(sql_l) else: logger.log("No new released episodes found ...") # queue episode for daily search dailysearch_queue_item = sickbeard.search_queue.DailySearchQueueItem() sickbeard.searchQueueScheduler.action.add_item(dailysearch_queue_item) self.amActive = False
def run(self, force=False): # pylint:disable=too-many-branches """ Runs the daily searcher, queuing selected episodes for search :param force: Force search """ if self.amActive: return self.amActive = True logger.info(_("Searching for new released episodes ...")) if not network_timezones.network_dict: network_timezones.update_network_dict() if network_timezones.network_dict: curDate = (datetime.date.today() + datetime.timedelta(days=1)).toordinal() else: curDate = (datetime.date.today() + datetime.timedelta(days=2)).toordinal() curTime = datetime.datetime.now(network_timezones.sb_timezone) main_db_con = db.DBConnection() sql_results = main_db_con.select( "SELECT showid, airdate, season, episode FROM tv_episodes WHERE status = ? AND (airdate <= ? and airdate > 1)", [common.UNAIRED, curDate]) sql_l = [] show = None for sqlEp in sql_results: try: if not show or int(sqlEp["showid"]) != show.indexerid: show = Show.find(settings.showList, int(sqlEp["showid"])) # for when there is orphaned series in the database but not loaded into our showlist if not show or show.paused: continue except MultipleShowObjectsException: logger.info("ERROR: expected to find a single show matching " + str(sqlEp["showid"])) continue if show.airs and show.network: # This is how you assure it is always converted to local time air_time = network_timezones.parse_date_time( sqlEp["airdate"], show.airs, show.network).astimezone(network_timezones.sb_timezone) # filter out any episodes that haven't started airing yet, # but set them to the default status while they are airing # so they are snatched faster if air_time > curTime: continue ep = show.getEpisode(sqlEp["season"], sqlEp["episode"]) with ep.lock: if ep.season == 0: logger.info( "New episode " + ep.pretty_name + " airs today, setting status to SKIPPED because is a special season" ) ep.status = common.SKIPPED else: logger.info( "New episode {0} airs today, setting to default episode status for this show: {1}" .format( ep.pretty_name, common.statusStrings[ep.show.default_ep_status])) ep.status = ep.show.default_ep_status sql_l.append(ep.get_sql()) if sql_l: main_db_con = db.DBConnection() main_db_con.mass_action(sql_l) else: logger.info("No new released episodes found ...") # queue episode for daily search dailysearch_queue_item = sickchill.oldbeard.search_queue.DailySearchQueueItem( ) settings.searchQueueScheduler.action.add_item(dailysearch_queue_item) self.amActive = False
def _add_cache_entry(self, name, url, size, seeders, leechers, parse_result=None, indexer_id=0): # check if we passed in a parsed result or should we try and create one if not parse_result: # create show_obj from indexer_id if available show_obj = None if indexer_id: show_obj = Show.find(settings.showList, indexer_id) try: parse_result = NameParser(showObj=show_obj).parse(name) except (InvalidNameException, InvalidShowException) as error: logger.debug("{0}".format(error)) return None if not parse_result or not parse_result.series_name: return None # if we made it this far then lets add the parsed result to cache for usage later on season = parse_result.season_number if parse_result.season_number else 1 episodes = parse_result.episode_numbers if season and episodes: # store episodes as a separated string episode_text = "|" + "|".join( {str(episode) for episode in sorted(episodes) if episode}) + "|" # get the current timestamp cur_timestamp = int( time.mktime(datetime.datetime.today().timetuple())) # get quality of release quality = parse_result.quality # get release group release_group = parse_result.release_group # get version version = parse_result.version logger.debug( _("Added RSS item: [{}] to cache: {}").format( name, self.provider_id)) return ({ 'provider': self.provider_id, 'name': name, 'season': season, 'episodes': episode_text, 'indexerid': parse_result.show.indexerid, 'url': url, 'time': cur_timestamp, 'quality': quality, 'release_group': release_group, 'version': version, 'seeders': seeders, 'leechers': leechers, 'size': size }, { 'url': url })
def massEdit(self, toEdit=None): t = PageTemplate(rh=self, filename="manage_massEdit.mako") if not toEdit: return self.redirect("/manage/") showIDs = toEdit.split("|") showList = [] showNames = [] for curID in showIDs: curID = int(curID) show_obj = Show.find(sickbeard.showList, curID) if show_obj: showList.append(show_obj) showNames.append(show_obj.name) season_folders_all_same = True last_season_folders = None paused_all_same = True last_paused = None default_ep_status_all_same = True last_default_ep_status = None anime_all_same = True last_anime = None sports_all_same = True last_sports = None quality_all_same = True last_quality = None subtitles_all_same = True last_subtitles = None scene_all_same = True last_scene = None air_by_date_all_same = True last_air_by_date = None root_dir_list = [] for curShow in showList: cur_root_dir = self.__gooey_path(curShow._location, 'dirname') if cur_root_dir and cur_root_dir != curShow._location and cur_root_dir not in root_dir_list: root_dir_list.append(cur_root_dir) # if we know they're not all the same then no point even bothering if paused_all_same: # if we had a value already and this value is different then they're not all the same if last_paused not in (None, curShow.paused): paused_all_same = False else: last_paused = curShow.paused if default_ep_status_all_same: if last_default_ep_status not in (None, curShow.default_ep_status): default_ep_status_all_same = False else: last_default_ep_status = curShow.default_ep_status if anime_all_same: # if we had a value already and this value is different then they're not all the same if last_anime not in (None, curShow.is_anime): anime_all_same = False else: last_anime = curShow.anime if season_folders_all_same: if last_season_folders not in (None, curShow.season_folders): season_folders_all_same = False else: last_season_folders = curShow.season_folders if quality_all_same: if last_quality not in (None, curShow.quality): quality_all_same = False else: last_quality = curShow.quality if subtitles_all_same: if last_subtitles not in (None, curShow.subtitles): subtitles_all_same = False else: last_subtitles = curShow.subtitles if scene_all_same: if last_scene not in (None, curShow.scene): scene_all_same = False else: last_scene = curShow.scene if sports_all_same: if last_sports not in (None, curShow.sports): sports_all_same = False else: last_sports = curShow.sports if air_by_date_all_same: if last_air_by_date not in (None, curShow.air_by_date): air_by_date_all_same = False else: last_air_by_date = curShow.air_by_date default_ep_status_value = last_default_ep_status if default_ep_status_all_same else None paused_value = last_paused if paused_all_same else None anime_value = last_anime if anime_all_same else None season_folders_value = last_season_folders if season_folders_all_same else None quality_value = last_quality if quality_all_same else None subtitles_value = last_subtitles if subtitles_all_same else None scene_value = last_scene if scene_all_same else None sports_value = last_sports if sports_all_same else None air_by_date_value = last_air_by_date if air_by_date_all_same else None return t.render(showList=toEdit, showNames=showNames, default_ep_status_value=default_ep_status_value, paused_value=paused_value, anime_value=anime_value, season_folders_value=season_folders_value, quality_value=quality_value, subtitles_value=subtitles_value, scene_value=scene_value, sports_value=sports_value, air_by_date_value=air_by_date_value, root_dir_list=root_dir_list, title=_('Mass Edit'), header=_('Mass Edit'), controller='manage', action='massEdit', topmenu='manage')
def run(self, force=False): # pylint: disable=too-many-branches, too-many-statements, too-many-locals if not sickbeard.USE_SUBTITLES: return if not sickbeard.subtitles.enabled_service_list(): logger.log('Not enough services selected. At least 1 service is required to ' 'search subtitles in the background', logger.WARNING) return self.amActive = True def dhm(td): days = td.days hours = td.seconds // 60 ** 2 minutes = (td.seconds // 60) % 60 ret = ('', '{0} days, '.format(days))[days > 0] + \ ('', '{0} hours, '.format(hours))[hours > 0] + \ ('', '{0} minutes'.format(minutes))[minutes > 0] if days == 1: ret = ret.replace('days', 'day') if hours == 1: ret = ret.replace('hours', 'hour') if minutes == 1: ret = ret.replace('minutes', 'minute') return ret.rstrip(', ') logger.log('Checking for missed subtitles', logger.INFO) database = db.DBConnection() sql_results = database.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 age " "FROM tv_episodes AS e INNER JOIN tv_shows AS s " "ON (e.showid = s.indexer_id) " "WHERE s.subtitles = 1 AND e.subtitles NOT LIKE ? " + ("AND e.season != 0 ", "")[sickbeard.SUBTITLES_INCLUDE_SPECIALS] + "AND e.location != '' AND e.status IN ({}) ORDER BY age ASC".format(','.join(['?'] * len(Quality.DOWNLOADED))), [datetime.datetime.now().toordinal(), wanted_languages(True)] + Quality.DOWNLOADED ) if not sql_results: logger.log('No subtitles to download', logger.INFO) self.amActive = False return for ep_to_sub in sql_results: try: # Encode path to system encoding. subtitle_path = ep_to_sub[b'location'].encode(sickbeard.SYS_ENCODING) except UnicodeEncodeError: # Fallback to UTF-8. subtitle_path = ep_to_sub[b'location'].encode('utf-8') if not os.path.isfile(subtitle_path): logger.log('Episode file does not exist, cannot download subtitles for {0} {1}'.format (ep_to_sub[b'show_name'], episode_num(ep_to_sub[b'season'], ep_to_sub[b'episode']) or episode_num(ep_to_sub[b'season'], ep_to_sub[b'episode'], numbering='absolute')), logger.DEBUG) continue if not needs_subtitles(ep_to_sub[b'subtitles']): logger.log('Episode already has all needed subtitles, skipping {0} {1}'.format (ep_to_sub[b'show_name'], episode_num(ep_to_sub[b'season'], ep_to_sub[b'episode']) or episode_num(ep_to_sub[b'season'], ep_to_sub[b'episode'], numbering='absolute')), logger.DEBUG) continue try: lastsearched = datetime.datetime.strptime(ep_to_sub[b'lastsearch'], dateTimeFormat) except ValueError: lastsearched = datetime.datetime.min try: if not force: now = datetime.datetime.now() days = int(ep_to_sub[b'age']) delay_time = datetime.timedelta(hours=8 if days < 10 else 7 * 24 if days < 30 else 30 * 24) # Search every hour for the first 24 hours since aired, then every 8 hours until 10 days passes # After 10 days, search every 7 days, after 30 days search once a month # Will always try an episode regardless of age at least 2 times if lastsearched + delay_time > now and int(ep_to_sub[b'searchcount']) > 2 and days: logger.log('Subtitle search for {0} {1} delayed for {2}'.format (ep_to_sub[b'show_name'], episode_num(ep_to_sub[b'season'], ep_to_sub[b'episode']) or episode_num(ep_to_sub[b'season'], ep_to_sub[b'episode'], numbering='absolute'), dhm(lastsearched + delay_time - now)), logger.DEBUG) continue logger.log('Searching for missing subtitles of {0} {1}'.format (ep_to_sub[b'show_name'], episode_num(ep_to_sub[b'season'], ep_to_sub[b'episode']) or episode_num(ep_to_sub[b'season'], ep_to_sub[b'episode'], numbering='absolute')), logger.INFO) show_object = Show.find(sickbeard.showList, int(ep_to_sub[b'showid'])) if not show_object: logger.log('Show with ID {0} not found in the database'.format(ep_to_sub[b'showid']), logger.DEBUG) continue episode_object = show_object.getEpisode(ep_to_sub[b'season'], ep_to_sub[b'episode']) if isinstance(episode_object, str): logger.log('{0} {1} not found in the database'.format (ep_to_sub[b'show_name'], episode_num(ep_to_sub[b'season'], ep_to_sub[b'episode']) or episode_num(ep_to_sub[b'season'], ep_to_sub[b'episode'], numbering='absolute')), logger.DEBUG) continue try: new_subtitles = episode_object.download_subtitles() except Exception as error: logger.log('Unable to find subtitles for {0} {1}. Error: {2}'.format (ep_to_sub[b'show_name'], episode_num(ep_to_sub[b'season'], ep_to_sub[b'episode']) or episode_num(ep_to_sub[b'season'], ep_to_sub[b'episode'], numbering='absolute'), ex(error)), logger.ERROR) continue if new_subtitles: logger.log('Downloaded {0} subtitles for {1} {2}'.format (', '.join(new_subtitles), ep_to_sub[b'show_name'], episode_num(ep_to_sub[b'season'], ep_to_sub[b'episode']) or episode_num(ep_to_sub[b'season'], ep_to_sub[b'episode'], numbering='absolute'))) except Exception as error: logger.log('Error while searching subtitles for {0} {1}. Error: {2}'.format (ep_to_sub[b'show_name'], episode_num(ep_to_sub[b'season'], ep_to_sub[b'episode']) or episode_num(ep_to_sub[b'season'], ep_to_sub[b'episode'], numbering='absolute'), ex(error)), logger.ERROR) continue logger.log('Finished checking for missed subtitles', logger.INFO) self.amActive = False
def addShowByID( self, indexer_id, show_name, indexer="TVDB", which_series=None, indexer_lang=None, root_dir=None, default_status=None, quality_preset=None, any_qualities=None, best_qualities=None, season_folders=None, subtitles=None, full_show_path=None, other_shows=None, skip_show=None, provided_indexer=None, anime=None, scene=None, blacklist=None, whitelist=None, default_status_after=None, default_season_folders=None, configure_show_options=None, ): if indexer != "TVDB": indexer_id = helpers.tvdbid_from_remote_id(indexer_id, indexer.upper()) if not indexer_id: logger.info( "Unable to to find tvdb ID to add {0}".format(show_name)) ui.notifications.error( "Unable to add {0}".format(show_name), "Could not add {0}. We were unable to locate the tvdb id at this time." .format(show_name)) return indexer_id = try_int(indexer_id) if indexer_id <= 0 or Show.find(settings.showList, indexer_id): return # Sanitize the parameter anyQualities and bestQualities. As these would normally be passed as lists any_qualities = any_qualities.split(",") if any_qualities else [] best_qualities = best_qualities.split(",") if best_qualities else [] # If configure_show_options is enabled let's use the provided settings if config.checkbox_to_value(configure_show_options): # prepare the inputs for passing along scene = config.checkbox_to_value(scene) anime = config.checkbox_to_value(anime) season_folders = config.checkbox_to_value(season_folders) subtitles = config.checkbox_to_value(subtitles) if whitelist: whitelist = short_group_names(whitelist) if blacklist: blacklist = short_group_names(blacklist) if not any_qualities: any_qualities = [] if not best_qualities or try_int(quality_preset, None): best_qualities = [] if not isinstance(any_qualities, list): any_qualities = [any_qualities] if not isinstance(best_qualities, list): best_qualities = [best_qualities] quality = Quality.combineQualities( [int(q) for q in any_qualities], [int(q) for q in best_qualities]) location = root_dir else: default_status = settings.STATUS_DEFAULT quality = settings.QUALITY_DEFAULT season_folders = settings.SEASON_FOLDERS_DEFAULT subtitles = settings.SUBTITLES_DEFAULT anime = settings.ANIME_DEFAULT scene = settings.SCENE_DEFAULT default_status_after = settings.STATUS_DEFAULT_AFTER if settings.ROOT_DIRS: root_dirs = settings.ROOT_DIRS.split("|") location = root_dirs[int(root_dirs[0]) + 1] else: location = None if not location: logger.info( "There was an error creating the show, no root directory setting found" ) return _("No root directories setup, please go back and add one.") show_name = sickchill.indexer[1].get_series_by_id( indexer_id, indexer_lang).seriesName show_dir = None if not show_name: ui.notifications.error(_("Unable to add show")) return self.redirect("/home/") # add the show settings.showQueueScheduler.action.add_show( indexer=1, indexer_id=indexer_id, showDir=show_dir, default_status=default_status, quality=quality, season_folders=season_folders, lang=indexer_lang, subtitles=subtitles, subtitles_sr_metadata=None, anime=anime, scene=scene, paused=None, blacklist=blacklist, whitelist=whitelist, default_status_after=default_status_after, root_dir=location, ) ui.notifications.message( _("Show added"), _("Adding the specified show {show_name}").format( show_name=show_name)) # done adding show return self.redirect("/home/")
def massAddTable(self, rootDir=None): t = PageTemplate(rh=self, filename="home_massAddTable.mako") if not rootDir: return _("No folders selected.") elif not isinstance(rootDir, list): root_dirs = [rootDir] else: root_dirs = rootDir root_dirs = [unquote_plus(xhtml_unescape(x)) for x in root_dirs] if settings.ROOT_DIRS: default_index = int(settings.ROOT_DIRS.split("|")[0]) else: default_index = 0 if len(root_dirs) > default_index: tmp = root_dirs[default_index] if tmp in root_dirs: root_dirs.remove(tmp) root_dirs.insert(0, tmp) dir_list = [] main_db_con = db.DBConnection() for root_dir in root_dirs: # noinspection PyBroadException try: file_list = os.listdir(root_dir) except Exception: continue for cur_file in file_list: # noinspection PyBroadException try: cur_path = os.path.normpath( os.path.join(root_dir, cur_file)) if not os.path.isdir(cur_path): continue # ignore Synology folders if cur_file.lower() in ["#recycle", "@eadir"]: continue except Exception: continue cur_dir = { "dir": cur_path, "existing_info": (None, None, None), "display_dir": "<b>" + os.path.dirname(cur_path) + os.sep + "</b>" + os.path.basename(cur_path), } # see if the folder is in KODI already dirResults = main_db_con.select( "SELECT indexer_id FROM tv_shows WHERE location = ? LIMIT 1", [cur_path]) if dirResults: cur_dir["added_already"] = True else: cur_dir["added_already"] = False dir_list.append(cur_dir) indexer_id = show_name = indexer = None for cur_provider in settings.metadata_provider_dict.values(): if not (indexer_id and show_name): (indexer_id, show_name, indexer) = cur_provider.retrieveShowMetadata(cur_path) if all((indexer_id, show_name, indexer)): break if all((indexer_id, show_name, indexer)): cur_dir["existing_info"] = (indexer_id, show_name, indexer) if indexer_id and Show.find(settings.showList, indexer_id): cur_dir["added_already"] = True return t.render(dirList=dir_list)
def massEditSubmit(self, paused=None, default_ep_status=None, anime=None, sports=None, scene=None, season_folders=None, quality_preset=None, subtitles=None, air_by_date=None, anyQualities=None, bestQualities=None, toEdit=None, *args, **kwargs): dir_map = {} for cur_arg in filter(lambda x: x.startswith('orig_root_dir_'), kwargs): dir_map[kwargs[cur_arg]] = ek( six.text_type, kwargs[cur_arg.replace('orig_root_dir_', 'new_root_dir_')], 'utf-8') showIDs = toEdit.split("|") errors = [] for curShow in showIDs: curErrors = [] show_obj = Show.find(sickbeard.showList, int(curShow or 0)) if not show_obj: continue cur_root_dir = self.__gooey_path(show_obj._location, 'dirname') cur_show_dir = self.__gooey_path(show_obj._location, 'basename') if cur_root_dir and dir_map.get( cur_root_dir ) and cur_root_dir != dir_map.get(cur_root_dir): new_show_dir = ek(os.path.join, dir_map[cur_root_dir], cur_show_dir) logger.log("For show " + show_obj.name + " changing dir from " + show_obj._location + " to " + new_show_dir) else: new_show_dir = show_obj._location new_paused = ('off', 'on')[(paused == 'enable', show_obj.paused)[paused == 'keep']] new_default_ep_status = ( default_ep_status, show_obj.default_ep_status)[default_ep_status == 'keep'] new_anime = ('off', 'on')[(anime == 'enable', show_obj.anime)[anime == 'keep']] new_sports = ('off', 'on')[(sports == 'enable', show_obj.sports)[sports == 'keep']] new_scene = ('off', 'on')[(scene == 'enable', show_obj.scene)[scene == 'keep']] new_air_by_date = ( 'off', 'on')[(air_by_date == 'enable', show_obj.air_by_date)[air_by_date == 'keep']] new_season_folders = ('off', 'on')[( season_folders == 'enable', show_obj.season_folders)[season_folders == 'keep']] new_subtitles = ('off', 'on')[(subtitles == 'enable', show_obj.subtitles)[subtitles == 'keep']] if quality_preset == 'keep': anyQualities, bestQualities = Quality.splitQuality( show_obj.quality) elif try_int(quality_preset, None): bestQualities = [] exceptions_list = [] curErrors += self.editShow(curShow, new_show_dir, anyQualities, bestQualities, exceptions_list, defaultEpStatus=new_default_ep_status, season_folders=new_season_folders, paused=new_paused, sports=new_sports, subtitles=new_subtitles, anime=new_anime, scene=new_scene, air_by_date=new_air_by_date, directCall=True) if curErrors: logger.log("Errors: " + str(curErrors), logger.ERROR) errors.append( '<b>{0}:</b>\n<ul>'.format(show_obj.name) + ' '.join( ['<li>{0}</li>'.format(error) for error in curErrors]) + "</ul>") if len(errors) > 0: ui.notifications.error( _('{num_errors:d} error{plural} while saving changes:').format( num_errors=len(errors), plural="" if len(errors) == 1 else "s"), " ".join(errors)) return self.redirect("/manage/")
def run(self, force=False): if not settings.USE_SUBTITLES: return if not enabled_service_list(): logger.warning( "Not enough services selected. At least 1 service is required to " "search subtitles in the background") return self.amActive = True def dhm(td): days = td.days hours = td.seconds // 60**2 minutes = (td.seconds // 60) % 60 ret = ("", "{0} days, ".format(days))[days > 0] + ( "", "{0} hours, ".format(hours))[hours > 0] + ( "", "{0} minutes".format(minutes))[minutes > 0] if days == 1: ret = ret.replace("days", "day") if hours == 1: ret = ret.replace("hours", "hour") if minutes == 1: ret = ret.replace("minutes", "minute") return ret.rstrip(", ") logger.info("Checking for missed subtitles") database = db.DBConnection() sql_results = database.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 age FROM tv_episodes AS e INNER JOIN tv_shows AS s ON (e.showid = s.indexer_id) " "WHERE s.subtitles = 1 AND e.subtitles NOT LIKE ? " + ("AND e.season != 0 ", "")[settings.SUBTITLES_INCLUDE_SPECIALS] + "AND e.location != '' AND e.status IN ({}) ORDER BY age ASC". format(",".join(["?"] * len(Quality.DOWNLOADED))), [datetime.datetime.now().toordinal(), wanted_languages(True)] + Quality.DOWNLOADED, ) if not sql_results: logger.info("No subtitles to download") self.amActive = False return for ep_to_sub in sql_results: if not os.path.isfile(ep_to_sub["location"]): logger.debug( "Episode file does not exist, cannot download subtitles for {0} {1}" .format( ep_to_sub["show_name"], episode_num(ep_to_sub["season"], ep_to_sub["episode"]) or episode_num(ep_to_sub["season"], ep_to_sub["episode"], numbering="absolute"), )) continue if not needs_subtitles(ep_to_sub["subtitles"]): logger.debug( "Episode already has all needed subtitles, skipping {0} {1}" .format( ep_to_sub["show_name"], episode_num(ep_to_sub["season"], ep_to_sub["episode"]) or episode_num(ep_to_sub["season"], ep_to_sub["episode"], numbering="absolute"), )) continue try: lastsearched = datetime.datetime.strptime( ep_to_sub["lastsearch"], dateTimeFormat) except ValueError: lastsearched = datetime.datetime.min try: if not force: now = datetime.datetime.now() days = int(ep_to_sub["age"]) delay_time = datetime.timedelta( hours=8 if days < 10 else 7 * 24 if days < 30 else 30 * 24) # Search every hour for the first 24 hours since aired, then every 8 hours until 10 days passes # After 10 days, search every 7 days, after 30 days search once a month # Will always try an episode regardless of age at least 2 times if lastsearched + delay_time > now and int( ep_to_sub["searchcount"]) > 2 and days: logger.debug( "Subtitle search for {0} {1} delayed for {2}". format( ep_to_sub["show_name"], episode_num(ep_to_sub["season"], ep_to_sub["episode"]) or episode_num(ep_to_sub["season"], ep_to_sub["episode"], numbering="absolute"), dhm(lastsearched + delay_time - now), )) continue logger.info( "Searching for missing subtitles of {0} {1}".format( ep_to_sub["show_name"], episode_num(ep_to_sub["season"], ep_to_sub["episode"]) or episode_num(ep_to_sub["season"], ep_to_sub["episode"], numbering="absolute"), )) show_object = Show.find(settings.showList, int(ep_to_sub["showid"])) if not show_object: logger.debug( "Show with ID {0} not found in the database".format( ep_to_sub["showid"])) continue episode_object = show_object.getEpisode( ep_to_sub["season"], ep_to_sub["episode"]) if isinstance(episode_object, str): logger.debug("{0} {1} not found in the database".format( ep_to_sub["show_name"], episode_num(ep_to_sub["season"], ep_to_sub["episode"]) or episode_num(ep_to_sub["season"], ep_to_sub["episode"], numbering="absolute"), )) continue try: new_subtitles = episode_object.download_subtitles() except Exception as error: logger.error( "Unable to find subtitles for {0} {1}. Error: {2}". format( ep_to_sub["show_name"], episode_num(ep_to_sub["season"], ep_to_sub["episode"]) or episode_num(ep_to_sub["season"], ep_to_sub["episode"], numbering="absolute"), str(error), )) continue if new_subtitles: logger.info("Downloaded {0} subtitles for {1} {2}".format( ", ".join(new_subtitles), ep_to_sub["show_name"], episode_num(ep_to_sub["season"], ep_to_sub["episode"]) or episode_num(ep_to_sub["season"], ep_to_sub["episode"], numbering="absolute"), )) except Exception as error: logger.error( "Error while searching subtitles for {0} {1}. Error: {2}". format( ep_to_sub["show_name"], episode_num(ep_to_sub["season"], ep_to_sub["episode"]) or episode_num(ep_to_sub["season"], ep_to_sub["episode"], numbering="absolute"), str(error), )) continue logger.info("Finished checking for missed subtitles") self.amActive = False
def massUpdate(self, toUpdate=None, toRefresh=None, toRename=None, toDelete=None, toRemove=None, toMetadata=None, toSubtitle=None): toUpdate = toUpdate.split('|') if toUpdate else [] toRefresh = toRefresh.split('|') if toRefresh else [] toRename = toRename.split('|') if toRename else [] toSubtitle = toSubtitle.split('|') if toSubtitle else [] toDelete = toDelete.split('|') if toDelete else [] toRemove = toRemove.split('|') if toRemove else [] toMetadata = toMetadata.split('|') if toMetadata else [] errors = [] refreshes = [] updates = [] renames = [] subtitles = [] for curShowID in set(toUpdate + toRefresh + toRename + toSubtitle + toDelete + toRemove + toMetadata): if curShowID == '': continue show_obj = Show.find(sickbeard.showList, int(curShowID)) if not show_obj: continue if curShowID in toDelete: sickbeard.showQueueScheduler.action.remove_show(show_obj, True) # don't do anything else if it's being deleted continue if curShowID in toRemove: sickbeard.showQueueScheduler.action.remove_show(show_obj) # don't do anything else if it's being remove continue if curShowID in toUpdate: try: sickbeard.showQueueScheduler.action.update_show( show_obj, True) updates.append(show_obj.name) except CantUpdateShowException as e: errors.append( _("Unable to update show: {exception_format}").format( exception_format=e)) # don't bother refreshing shows that were updated anyway if curShowID in toRefresh and curShowID not in toUpdate: try: sickbeard.showQueueScheduler.action.refresh_show(show_obj) refreshes.append(show_obj.name) except CantRefreshShowException as e: errors.append( _("Unable to refresh show {show_name}: {exception_format}" ).format(show_name=show_obj.name, exception_format=e)) if curShowID in toRename: sickbeard.showQueueScheduler.action.rename_show_episodes( show_obj) renames.append(show_obj.name) if curShowID in toSubtitle: sickbeard.showQueueScheduler.action.download_subtitles( show_obj) subtitles.append(show_obj.name) if errors: ui.notifications.error(_("Errors encountered"), '<br >\n'.join(errors)) messageDetail = "" if updates: messageDetail += "<br><b>" + _("Updates") + "</b><br><ul><li>" messageDetail += "</li><li>".join(updates) messageDetail += "</li></ul>" if refreshes: messageDetail += "<br><b>" + _("Refreshes") + "</b><br><ul><li>" messageDetail += "</li><li>".join(refreshes) messageDetail += "</li></ul>" if renames: messageDetail += "<br><b>" + _("Renames") + "</b><br><ul><li>" messageDetail += "</li><li>".join(renames) messageDetail += "</li></ul>" if subtitles: messageDetail += "<br><b>" + _("Subtitles") + "</b><br><ul><li>" messageDetail += "</li><li>".join(subtitles) messageDetail += "</li></ul>" if updates + refreshes + renames + subtitles: ui.notifications.message( _("The following actions were queued") + ":", messageDetail) return self.redirect("/manage/")