def getEpisodes(search_thread, searchstatus): """ Get all episodes located in a search thread with a specific status """ results = [] # NOTE!: Show.find called with just indexerid! show_obj = Show.find(sickbeard.showList, int(search_thread.show.indexerid)) if not show_obj: if not search_thread.show.is_recently_deleted: logger.log(u'No Show Object found for show with indexerID: {0}'. format(search_thread.show.indexerid), logger.ERROR) return results if not isinstance(search_thread.segment, list): search_thread.segment = [search_thread.segment] for ep_obj in search_thread.segment: ep = show_obj.get_episode(ep_obj.season, ep_obj.episode) results.append({ 'show': show_obj.indexerid, 'episode': ep.episode, 'episodeindexid': ep.indexerid, 'season': ep.season, 'searchstatus': searchstatus, 'status': statusStrings[ep.status], 'quality': getQualityClass(ep), 'overview': Overview.overviewStrings[show_obj.get_overview(ep.status)], }) return results
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 test_validate_indexer_id(self): sickbeard.QUALITY_DEFAULT = Quality.FULLHDTV 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, '', u'', '123', u'123', '456', u'456', '789', u'789', 123, 456, 789, ['123', '456'], [u'123', u'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 (%d) and results (%d) does not match' % (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])
def find_propers(self, search_date=datetime.datetime.today()): """ Searches providers for PROPER or REPACK releases Returns a list of objects of type classes.Proper """ results = [] myDB = db.DBConnection() sqlResults = myDB.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 (' + ','.join([str(x) for x in Quality.DOWNLOADED]) + ')' + ' OR (e.status IN (' + ','.join([str(x) for x in Quality.SNATCHED]) + ')))' ) if not sqlResults: return results for sqlshow in sqlResults: self.show = Show.find(sickbeard.showList, int(sqlshow["showid"])) if self.show: curEp = self.show.getEpisode(sqlshow["season"], sqlshow["episode"]) searchStrings = self._get_episode_search_strings(curEp, add_string='PROPER|REPACK') for searchString in searchStrings: for item in self.search(searchString): title, url = self._get_title_and_url(item) if re.match(r'.*(REPACK|PROPER).*', title, re.I): results.append(classes.Proper(title, url, datetime.datetime.today(), self.show)) return results
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 add_show(indexer, indexer_id, show_name, status): """ Adds a new show with the default settings """ if not Show.find(sickbeard.showList, int(indexer_id)): root_dirs = sickbeard.ROOT_DIRS.split('|') location = root_dirs[int(root_dirs[0]) + 1] if root_dirs else None if location: show_path = ek(os.path.join, location, show_name) logger.log(u"Adding show '{}' with ID: '{}' in location: '{}'".format(show_name, indexer_id, show_path)) dir_exists = helpers.makeDir(show_path) if not dir_exists: logger.log(u"Unable to create the folder {}. Unable to add the show {}".format(show_path, show_name), logger.WARNING) return else: logger.log(u"Creating the folder '{}'".format(show_path), logger.DEBUG) helpers.chmodAsParent(show_path) sickbeard.showQueueScheduler.action.addShow(indexer, indexer_id, show_path, default_status=status, quality=int(sickbeard.QUALITY_DEFAULT), flatten_folders=int(sickbeard.FLATTEN_FOLDERS_DEFAULT), paused=sickbeard.TRAKT_START_PAUSED, default_status_after=status) else: logger.log(u"There was an error creating the show, no root directory setting found", logger.WARNING) return
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 + ')' ) for result in sql_results or []: show = Show.find(sickbeard.showList, int(result['showid'])) if show: episode = show.getEpisode(result['season'], result['episode']) for term in self.proper_strings: search_strings = self._get_episode_search_strings(episode, add_string=term) for item in self.search(search_strings[0]): title, url = self._get_title_and_url(item) results.append(Proper(title, url, datetime.today(), show)) return results
def fetch_trakt_shows(self): if not self.show_watchlist: logger.log(u"No shows found in your watchlist, aborting watchlist update", logger.DEBUG) else: indexer = int(sickbeard.TRAKT_DEFAULT_INDEXER) trakt_id = sickbeard.indexerApi(indexer).config['trakt_id'] for watchlisted_show in self.show_watchlist[trakt_id]: indexer_id = int(watchlisted_show) show_obj = self.show_watchlist[trakt_id][watchlisted_show] if show_obj['year'] and show_obj['slug'].endswith(str(show_obj['year'])): show_name = '{} ({})'.format(show_obj['title'], show_obj['year']) else: show_name = show_obj['title'] if int(sickbeard.TRAKT_METHOD_ADD) != 2: self.add_show(indexer, indexer_id, show_name, SKIPPED) else: self.add_show(indexer, indexer_id, show_name, WANTED) if int(sickbeard.TRAKT_METHOD_ADD) == 1: new_show = Show.find(sickbeard.showList, indexer_id) if new_show: setEpisodeToWanted(new_show, 1, 1) else: self.todoWanted.append(indexer_id, 1, 1)
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 find_propers(self, proper_candidates): results = [] for proper_candidate in proper_candidates: show_obj = Show.find(sickbeard.showList, int(proper_candidate[b'showid'])) if proper_candidate[b'showid'] else None if show_obj: episode_obj = show_obj.get_episode(proper_candidate[b'season'], proper_candidate[b'episode']) for term in self.proper_strings: search_strings = self._get_episode_search_strings(episode_obj, add_string=term) for item in self.search(search_strings[0], ep_obj=episode_obj): title, url = self._get_title_and_url(item) seeders, leechers = self._get_result_info(item) size = self._get_size(item) pubdate = self._get_pubdate(item) torrent_hash = self._get_hash(item) # This will be retrived from parser proper_tags = None results.append(Proper(title, url, datetime.today(), show_obj, seeders, leechers, size, pubdate, torrent_hash, proper_tags)) return results
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, s.lang, 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 + ')') for result in sql_results or []: show = Show.find(sickbeard.showList, int(result['showid'])) if show: episode = show.getEpisode(result['season'], result['episode']) for term in self.proper_strings: search_strings = self._get_episode_search_strings( episode, add_string=term) for item in self.search(search_strings[0]): 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(u"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(u"Unable to create the folder {0!s} , can't add the show".format(showPath), logger.WARNING) return else: helpers.chmodAsParent(showPath) sickbeard.showQueueScheduler.action.addShow(int(indexer), int(indexer_id), showPath, default_status=status, quality=int(sickbeard.QUALITY_DEFAULT), flatten_folders=int(sickbeard.FLATTEN_FOLDERS_DEFAULT), paused=sickbeard.TRAKT_START_PAUSED, default_status_after=status) else: logger.log(u"There was an error creating the show, no root directory setting found", logger.WARNING) return
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 updateShows(self): logger.log(u"SHOW_WATCHLIST::CHECK::START - Trakt Show Watchlist", logger.DEBUG) if not len(self.ShowWatchlist): logger.log(u"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(u"SHOW_WATCHLIST::CHECK::FINISH - Trakt Show Watchlist", logger.DEBUG)
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(u"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(u"Unable to create the folder %s , can't add the show" % showPath, logger.WARNING) return else: helpers.chmodAsParent(showPath) sickbeard.showQueueScheduler.action.addShow(int(indexer), int(indexer_id), showPath, default_status=status, quality=int(sickbeard.QUALITY_DEFAULT), flatten_folders=int(sickbeard.FLATTEN_FOLDERS_DEFAULT), paused=sickbeard.TRAKT_START_PAUSED, default_status_after=status) else: logger.log(u"There was an error creating the show, no root directory setting found", logger.WARNING) return
def test_validate_indexer_id(self): """ Test validate indexer id """ sickbeard.QUALITY_DEFAULT = Quality.FULLHDTV 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, '', u'', '123', u'123', '456', u'456', '789', u'789', 123, 456, 789, ['123', '456'], [u'123', u'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 (%d) and results (%d) does not match' % (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 getEpisode(show, season=None, episode=None, absolute=None): """ Get a specific episode object based on show, season and episode number :param show: Season number :param season: Season number :param season: Season number :param absolute: Optional if the episode number is a scene absolute number :return: episode object """ if show is None: return "Invalid show parameters" show_obj = Show.find(sickbeard.showList, int(show)) if show_obj is None: return "Invalid show paramaters" if absolute: ep_obj = show_obj.get_episode(absolute_number=absolute) elif season and episode: ep_obj = show_obj.get_episode(season, episode) else: return "Invalid paramaters" if ep_obj is None: return "Episode couldn't be retrieved" return ep_obj
def test_find(self): """ Test find """ 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 """ 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 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 _addCacheEntry(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 showObj from indexer_id if available showObj = None if indexer_id: showObj = Show.find(sickbeard.showList, indexer_id) try: parse_result = NameParser(showObj=showObj).parse(name) except (InvalidNameException, InvalidShowException) as error: logger.log(u"{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 episodeText = "|" + "|".join( {str(episode) for episode in episodes if episode}) + "|" # get the current timestamp curTimestamp = int( time.mktime(datetime.datetime.today().timetuple())) # get quality of release quality = parse_result.quality assert isinstance(name, unicode) # get release group release_group = parse_result.release_group # get version version = parse_result.version logger.log( u"Added RSS item: [" + name + "] to cache: [" + self.providerID + "]", logger.DEBUG) return [ "INSERT OR IGNORE INTO [" + self.providerID + "] (name, season, episodes, indexerid, url, time, quality, release_group, version) VALUES (?,?,?,?,?,?,?,?,?)", [ name, season, episodeText, parse_result.show.indexerid, url, curTimestamp, 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 = [] 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 updateWatchedData(self): try: response = self.trakt_api.traktRequest("users/me/history/episodes") changes = dict() myDB = db.DBConnection() for data in response: show_id = None if not data['show']['ids']["tvdb"] is None: show_id = data['show']['ids']["tvdb"] elif not data['show']['ids']["tvrage"] is None: show_id = data['show']['ids']["tvrage"] else: logger.log(u"Could not retrieve show_id from trakt history", logger.WARNING) continue show_name = data["show"]["title"] season = data["episode"]["season"] episode = data["episode"]["number"] watched = time.mktime(parser.parse(data["watched_at"]).timetuple()) cursor = myDB.action("UPDATE tv_episodes SET last_watched=? WHERE showid=? AND season=? AND episode=? AND (last_watched IS NULL OR last_watched < ?)", [watched, show_id, season, episode, watched]) if cursor.rowcount > 0: changes[show_name] = changes.get(show_name, 0) + 1 logger.log("Updated " + show_name + ", episode " + str(season) + "x" + str(episode) + ": Episode was watched at " + str(watched)) show = Show.find(sickbeard.showList, int(show_id)) show.last_seen = max(show.last_seen, watched) message = "Watched episodes synchronization complete: "; if (len(changes) == 0): message += "No changes detected." else: message += "Marked as watched " first = True; for show_name in changes: if (not first): message += ", " message += str(changes[show_name]) + " episodes of " + show_name first = False; logger.log(message) self._updateAllShowsNextEpisodeData() except traktException as e: logger.log(u"Could not connect to trakt service, cannot synch Watched Data: %s" % ex(e), logger.ERROR)
def _addCacheEntry(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 showObj from indexer_id if available showObj = None if indexer_id: showObj = Show.find(sickbeard.showList, indexer_id) try: myParser = NameParser(showObj=showObj) parse_result = myParser.parse(name) except InvalidNameException: logger.log(u"Unable to parse the filename " + name + " into a valid episode", logger.DEBUG) return None except InvalidShowException: logger.log(u"Unable to parse the filename " + name + " into a valid show", 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 episodeText = "|" + "|".join(map(str, episodes)) + "|" # get the current timestamp curTimestamp = int(time.mktime(datetime.datetime.today().timetuple())) # get quality of release quality = parse_result.quality name = ss(name) # get release group release_group = parse_result.release_group # get version version = parse_result.version logger.log(u"Added RSS item: [" + name + "] to cache: [" + self.providerID + "]", logger.DEBUG) return [ "INSERT OR IGNORE INTO [" + self.providerID + "] (name, season, episodes, indexerid, url, time, quality, release_group, version) VALUES (?,?,?,?,?,?,?,?,?)", [name, season, episodeText, parse_result.show.indexerid, url, curTimestamp, quality, release_group, version]]
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 = [] 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 fetch_trakt_episodes(self): """ Sets episodes to wanted that are in trakt watchlist """ logger.log(u"Retrieving episodes to sync with Trakt episode's watchlist", logger.DEBUG) if not self.episode_watchlist: logger.log(u"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 watchlist_item in self.episode_watchlist[trakt_id]: indexer_id = int(watchlist_item) show = self.episode_watchlist[trakt_id][watchlist_item] new_show = Show.find(sickbeard.showList, indexer_id) try: if not new_show: if indexer_id not in managed_show: self.add_show(indexer, indexer_id, show['title'], SKIPPED) managed_show.append(indexer_id) for season_item in show['seasons']: season = int(season_item) for episode_item in show['seasons'][season_item]['episodes']: self.todoWanted.append((indexer_id, season, int(episode_item))) else: if new_show.indexer == indexer: for season_item in show['seasons']: season = int(season_item) for episode_item in show['seasons'][season_item]['episodes']: setEpisodeToWanted(new_show, season, int(episode_item)) except TypeError: logger.log(u"Could not parse the output from trakt for '{}' ".format(show["title"]), logger.DEBUG)
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 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( b'SELECT season, episode ' b'FROM tv_episodes ' b'WHERE (status LIKE \'%4\' OR status LIKE \'%6\') ' b'AND season != 0 ' b'AND showid = ? ' b'AND location != \'\'', [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(sickbeard.showList, int(cur_indexer_id)) show.getEpisode(season, episode).download_subtitles() return self.redirect('/manage/subtitleMissed/')
def findPropers(self, search_date=datetime.datetime.today()): results = [] myDB = db.DBConnection() sqlResults = myDB.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 (' + ','.join([str(x) for x in Quality.DOWNLOADED + Quality.SNATCHED + Quality.SNATCHED_BEST]) + ')' ) for sqlshow in sqlResults or []: show = Show.find(sickbeard.showList, int(sqlshow["showid"])) if show: curEp = show.getEpisode(int(sqlshow["season"]), int(sqlshow["episode"])) for term in self.proper_strings: searchString = self._get_episode_search_strings(curEp, add_string=term) for item in self._doSearch(searchString[0]): title, url = self._get_title_and_url(item) results.append(classes.Proper(title, url, datetime.datetime.today(), show)) return results
def run(self, force=False): # pylint: disable=too-many-branches, too-many-statements, too-many-locals """Check for needed subtitles for users' shows. :param force: True if a force search needs to be executed :type force: bool """ if self.amActive: logger.log(u"Subtitle finder is still running, not starting it again", logger.DEBUG) return if not sickbeard.USE_SUBTITLES: logger.log(u"Subtitle search is disabled. Please enabled it", logger.WARNING) return if not sickbeard.subtitles.enabled_service_list(): logger.warning(u'Not enough services selected. At least 1 service is required to search subtitles in the ' u'background') return self.amActive = True def dhm(td): days = td.days hours = td.seconds // 60 ** 2 minutes = (td.seconds // 60) % 60 ret = (u'', '{} days, '.format(days))[days > 0] + \ (u'', '{} hours, '.format(hours))[hours > 0] + \ (u'', '{} 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(', ') if sickbeard.SUBTITLES_DOWNLOAD_IN_PP: self.subtitles_download_in_pp() logger.info(u'Checking for missed subtitles') database = db.DBConnection() # Shows with air date <= 30 days, have a limit of 100 results # Shows with air date > 30 days, have a limit of 200 results sql_args = [{'age_comparison': '<=', 'limit': 100}, {'age_comparison': '>', 'limit': 200}] sql_like_languages = '%' + ','.join(sorted(wanted_languages())) + '%' if sickbeard.SUBTITLES_MULTI else '%und%' sql_results = [] for args in sql_args: sql_results += database.select( "SELECT " " s.show_name, " " e.showid, " " e.season, " " e.episode," " e.release_name, " " 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.status LIKE '%4' OR e.status LIKE '%6') " " AND e.season > 0 " " AND e.location != '' " " AND age {} 30 " " AND e.subtitles NOT LIKE ? " "ORDER BY " " lastsearch ASC " "LIMIT {}".format (args['age_comparison'], args['limit']), [datetime.datetime.now().toordinal(), sql_like_languages] ) if not sql_results: logger.info(u'No subtitles to download') self.amActive = False return for ep_to_sub in sql_results: # give the CPU a break time.sleep(cpu_presets[sickbeard.CPU_PRESET]) ep_num = episode_num(ep_to_sub['season'], ep_to_sub['episode']) or \ episode_num(ep_to_sub['season'], ep_to_sub['episode'], numbering='absolute') subtitle_path = _encode(ep_to_sub['location'], encoding=sickbeard.SYS_ENCODING, fallback='utf-8') if not os.path.isfile(subtitle_path): logger.debug(u'Episode file does not exist, cannot download subtitles for %s %s', ep_to_sub['show_name'], ep_num) continue if not needs_subtitles(ep_to_sub['subtitles']): logger.debug(u'Episode already has all needed subtitles, skipping %s %s', ep_to_sub['show_name'], ep_num) 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=1 if days <= 10 else 8 if days <= 30 else 30 * 24) delay = lastsearched + delay_time - now # Search every hour until 10 days pass # After 10 days, search every 8 hours, after 30 days search once a month # Will always try an episode regardless of age for 3 times # The time resolution is minute # Only delay is the it's bigger than one minute and avoid wrongly skipping the search slot. if delay.total_seconds() > 60 and int(ep_to_sub['searchcount']) > 2: logger.debug(u'Subtitle search for %s %s delayed for %s', ep_to_sub['show_name'], ep_num, dhm(delay)) continue show_object = Show.find(sickbeard.showList, int(ep_to_sub['showid'])) if not show_object: logger.debug(u'Show with ID %s not found in the database', 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(u'%s %s not found in the database', ep_to_sub['show_name'], ep_num) continue try: episode_object.download_subtitles() except Exception as error: logger.error(u'Unable to find subtitles for %s %s. Error: %s', ep_to_sub['show_name'], ep_num, ex(error)) continue except Exception as error: logger.warning(u'Error while searching subtitles for %s %s. Error: %s', ep_to_sub['show_name'], ep_num, ex(error)) continue logger.info(u'Finished checking for missed subtitles') self.amActive = False
def findNeededEpisodes(self, episode, manualSearch=False, downCurQuality=False): neededEps = {} cl = [] myDB = self._getDB() if not episode: sqlResults = myDB.select("SELECT * FROM [" + self.providerID + "]") elif type(episode) != list: sqlResults = myDB.select( "SELECT * FROM [" + self.providerID + "] WHERE indexerid = ? AND season = ? AND episodes LIKE ?", [episode.show.indexerid, episode.season, "%|" + str(episode.episode) + "|%"]) else: for epObj in episode: cl.append([ "SELECT * FROM [" + self.providerID + "] WHERE indexerid = ? AND season = ? AND episodes LIKE ? AND quality IN (" + ",".join( [str(x) for x in epObj.wantedQuality]) + ")", [epObj.show.indexerid, epObj.season, "%|" + str(epObj.episode) + "|%"]]) sqlResults = myDB.mass_action(cl, fetchall=True) sqlResults = list(itertools.chain(*sqlResults)) # for each cache entry for curResult in sqlResults: # ignored/required words, and non-tv junk if not show_name_helpers.filterBadReleases(curResult["name"]): continue # get the show object, or if it's not one of our shows then ignore it showObj = Show.find(sickbeard.showList, int(curResult["indexerid"])) if not showObj: continue # skip if provider is anime only and show is not anime if self.provider.anime_only and not showObj.is_anime: logger.log(u"" + str(showObj.name) + " is not an anime, skiping", logger.DEBUG) continue # get season and ep data (ignoring multi-eps for now) curSeason = int(curResult["season"]) if curSeason == -1: continue curEp = curResult["episodes"].split("|")[1] if not curEp: continue curEp = int(curEp) curQuality = int(curResult["quality"]) curReleaseGroup = curResult["release_group"] curVersion = curResult["version"] # if the show says we want that episode then add it to the list if not showObj.wantEpisode(curSeason, curEp, curQuality, manualSearch, downCurQuality): logger.log(u"Skipping " + curResult["name"], logger.DEBUG) continue epObj = showObj.getEpisode(curSeason, curEp) # build a result object title = curResult["name"] url = curResult["url"] logger.log(u"Found result " + title + " at " + url) result = self.provider.get_result([epObj]) result.show = showObj result.url = url result.name = title result.quality = curQuality result.release_group = curReleaseGroup result.version = curVersion result.content = None # add it to the list if epObj not in neededEps: neededEps[epObj] = [result] else: neededEps[epObj].append(result) # datetime stamp this search so cache gets cleared self.setLastSearch() return neededEps
def run(self): ShowQueueItem.run(self) logger.log(u"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() if self.lang: lINDEXER_API_PARMS['language'] = self.lang logger.log(u"" + str(sickbeard.indexerApi(self.indexer).name) + ": " + repr(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 not self.showDir and self.root_dir: show_name = get_showname_from_indexer(self.indexer, self.indexer_id, self.lang) if show_name: self.showDir = ek(os.path.join, self.root_dir, sanitize_filename(show_name)) dir_exists = makeDir(self.showDir) if not dir_exists: logger.log( u"Unable to create the folder {0}, can't add the show" .format(self.showDir)) return chmodAsParent(self.showDir) else: logger.log( u"Unable to get a show {0}, can't add the show".format( self.showDir)) return # 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: logger.log( u"Show in {0} has no name on {1}, probably searched with the wrong language." .format(self.showDir, sickbeard.indexerApi(self.indexer).name), logger.ERROR) ui.notifications.error( "Unable to add show", "Show in " + self.showDir + " has no name on " + str(sickbeard.indexerApi(self.indexer).name) + ", probably the wrong language. Delete .nfo and add manually in the correct language." ) self._finishEarly() return # if the show has no episodes/seasons if not s: logger.log(u"Show " + str(s['seriesname']) + " is on " + str(sickbeard.indexerApi(self.indexer).name) + " but contains no season/episode data.") ui.notifications.error( "Unable to add show", "Show " + str(s['seriesname']) + " is on " + str(sickbeard.indexerApi(self.indexer).name) + " but contains no season/episode data.") self._finishEarly() return except Exception as e: logger.log( u"{0!s} Error while loading information from indexer {1!s}. Error: {2!r}" .format(self.indexer_id, sickbeard.indexerApi(self.indexer).name, ex(e)), logger.ERROR) # logger.log(u"Show name with ID %s doesn't exist on %s anymore. If you are using trakt, it will be removed from your TRAKT watchlist. If you are adding manually, try removing the nfo and adding again" % # (self.indexer_id, sickbeard.indexerApi(self.indexer).name), logger.WARNING) ui.notifications.error( "Unable to add show", "Unable to look up the show in {0!s} on {1!s} using ID {2!s}, not using the NFO. Delete .nfo and try adding manually again." .format(self.showDir, sickbeard.indexerApi(self.indexer).name, self.indexer_id)) if sickbeard.USE_TRAKT: trakt_id = sickbeard.indexerApi( self.indexer).config['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._finishEarly() 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) 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.quality = self.quality if self.quality else sickbeard.QUALITY_DEFAULT self.show.flatten_folders = self.flatten_folders if self.flatten_folders is not None else sickbeard.FLATTEN_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( u"Setting all episodes to the specified default status: " + str(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 smartish 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 e: logger.log( u"Unable to add show due to an error with " + sickbeard.indexerApi(self.indexer).name + ": " + ex(e), logger.ERROR) if self.show: ui.notifications.error( "Unable to add " + str(self.show.name) + " due to an error with " + sickbeard.indexerApi(self.indexer).name + "") else: ui.notifications.error( "Unable to add show due to an error with " + sickbeard.indexerApi(self.indexer).name + "") self._finishEarly() return except MultipleShowObjectsException: logger.log( u"The show in " + self.showDir + " is already in your show list, skipping", logger.WARNING) ui.notifications.error( 'Show skipped', "The show in " + self.showDir + " is already in your show list") self._finishEarly() return except Exception as e: logger.log(u"Error trying to add show: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) self._finishEarly() raise logger.log(u"Retrieving show info from IMDb", logger.DEBUG) try: self.show.loadIMDbInfo() except imdb_exceptions.IMDbError as e: logger.log(u" Something wrong on IMDb api: " + ex(e), logger.WARNING) except Exception as e: logger.log(u"Error loading IMDb info: " + ex(e), logger.ERROR) try: self.show.saveToDB() except Exception as e: logger.log(u"Error saving the show to the database: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) self._finishEarly() 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 e: logger.log( u"Error with " + sickbeard.indexerApi(self.show.indexer).name + ", not creating episode list: " + ex(e), 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 e: logger.log(u"Error searching dir for episodes: " + ex(e), 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( u"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(u"update watchlist") notifiers.trakt_notifier.update_watchlist(show_obj=self.show) # Load XEM data to DB for show sickbeard.scene_numbering.xem_refresh(self.show.indexerid, self.show.indexer, force=True) # check if show has XEM mapping so we can determin if searches should go by scene numbering or indexer numbering. if not self.scene and sickbeard.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 self.finish()
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( u'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 = (u'', '{0} days, '.format(days))[days > 0] + \ (u'', '{0} hours, '.format(hours))[hours > 0] + \ (u'', '{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(', ') if sickbeard.SUBTITLES_DOWNLOAD_IN_PP: self.subtitles_download_in_pp() logger.log(u'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.location != '' AND e.status IN (%s) ORDER BY age ASC" % ','.join(['?'] * len(Quality.DOWNLOADED)), [datetime.datetime.now().toordinal(), wanted_languages(True)] + Quality.DOWNLOADED) if not sql_results: logger.log(u'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['location'].encode( sickbeard.SYS_ENCODING) except UnicodeEncodeError: # Fallback to UTF-8. subtitle_path = ep_to_sub['location'].encode('utf-8') if not os.path.isfile(subtitle_path): logger.log( u'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')), logger.DEBUG) continue if not needs_subtitles(ep_to_sub['subtitles']): logger.log( u'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')), logger.DEBUG) 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.log( u'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)), logger.DEBUG) continue logger.log( u'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')), logger.INFO) show_object = Show.find(sickbeard.showList, int(ep_to_sub['showid'])) if not show_object: logger.log( u'Show with ID {0} not found in the database'.format( ep_to_sub['showid']), logger.DEBUG) continue episode_object = show_object.getEpisode( ep_to_sub['season'], ep_to_sub['episode']) if isinstance(episode_object, str): logger.log( u'{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')), logger.DEBUG) continue try: new_subtitles = episode_object.download_subtitles() except Exception as error: logger.log( u'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'), ex(error)), logger.ERROR) continue if new_subtitles: logger.log(u'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.log( u'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'), ex(error)), logger.ERROR) continue logger.log(u'Finished checking for missed subtitles', logger.INFO) self.amActive = False
def run(self, force=False): # pylint: disable=too-many-branches, too-many-statements if not sickbeard.USE_SUBTITLES: return if len(sickbeard.subtitles.enabled_service_list()) < 1: logger.log(u'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 = (u'', '%s days, ' % days)[days > 0] + \ (u'', '%s hours, ' % hours)[hours > 0] + \ (u'', '%s minutes' % 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(', ') if sickbeard.SUBTITLES_DOWNLOAD_IN_PP: self.subtitles_download_in_pp() logger.log(u'Checking for missed subtitles', logger.INFO) statuses = list({status for status in Quality.DOWNLOADED + Quality.ARCHIVED}) 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.location != '' AND e.status IN (%s) ORDER BY age ASC" % ','.join(['?'] * len(statuses)), [datetime.datetime.now().toordinal(), wanted_languages(True)] + statuses ) if not sql_results: logger.log(u'No subtitles to download', logger.INFO) self.amActive = False return for ep_to_sub in sql_results: if not ek(os.path.isfile, ep_to_sub['location']): logger.log(u'Episode file does not exist, cannot download subtitles for %s S%02dE%02d' % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode']), logger.DEBUG) continue if not needs_subtitles(ep_to_sub['subtitles']): logger.log(u'Episode already has all needed subtitles, skipping %s S%02dE%02d' % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode']), logger.DEBUG) 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.log(u"Subtitle search for %s S%02dE%02d delayed for %s" % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode'], dhm(lastsearched + delay_time - now)), logger.DEBUG) continue logger.log(u'Searching for missing subtitles of %s S%02dE%02d' % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode']), logger.INFO) show_object = Show.find(sickbeard.showList, int(ep_to_sub['showid'])) if not show_object: logger.log(u'Show with ID %s not found in the database' % ep_to_sub['showid'], logger.DEBUG) continue episode_object = show_object.getEpisode(ep_to_sub["season"], ep_to_sub["episode"]) if isinstance(episode_object, str): logger.log(u'%s S%02dE%02d not found in the database' % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode']), logger.DEBUG) continue try: new_subtitles = episode_object.download_subtitles() except Exception as error: logger.log(u'Unable to find subtitles for %s S%02dE%02d. Error: %r' % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode'], ex(error)), logger.ERROR) continue if new_subtitles: logger.log(u'Downloaded %s subtitles for %s S%02dE%02d' % (', '.join(new_subtitles), ep_to_sub['show_name'], ep_to_sub["season"], ep_to_sub["episode"])) except Exception as error: logger.log(u'Error while searching subtitles for %s S%02dE%02d. Error: %r' % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode'], ex(error)), logger.ERROR) continue logger.log(u'Finished checking for missed subtitles', logger.INFO) self.amActive = False
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: 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[b'shows'][0][b'ids'][b'tvdb'] = self.indexer_id else: data[b'shows'][0][b'ids'][b'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) 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 smartish 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 sickbeard.scene_numbering.xem_refresh(self.show.indexerid, self.show.indexer, force=True) # check if show has XEM mapping so we can determin if searches should go by scene numbering or indexer numbering. if not self.scene and sickbeard.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): # pylint:disable=too-many-locals, too-many-branches needed_eps = {} cl = [] cache_db_con = self._get_db() if not episode: sql_results = cache_db_con.select("SELECT * FROM [" + self.provider_id + "]") elif not isinstance(episode, list): sql_results = cache_db_con.select( "SELECT * FROM [" + self.provider_id + "] WHERE indexerid = ? AND season = ? AND episodes LIKE ?", [episode.show.indexerid, episode.season, "%|" + str(episode.episode) + "|%"]) else: for ep_obj in episode: cl.append([ "SELECT * FROM [" + self.provider_id + "] WHERE indexerid = ? AND season = ? AND episodes LIKE ? AND quality IN (" + ",".join( [str(x) for x in ep_obj.wantedQuality]) + ")", [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(sickbeard.showList, int(cur_result[b"indexerid"])) if not show_obj: continue # ignored/required words, and non-tv junk if not show_name_helpers.filter_bad_releases(cur_result[b"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.log("" + str(show_obj.name) + " is not an anime, skiping", logger.DEBUG) continue # get season and ep data (ignoring multi-eps for now) cur_season = int(cur_result[b"season"]) if cur_season == -1: continue cur_ep = cur_result[b"episodes"].split("|")[1] if not cur_ep: continue cur_ep = int(cur_ep) cur_quality = int(cur_result[b"quality"]) cur_release_group = cur_result[b"release_group"] cur_version = cur_result[b"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.log("Ignoring " + cur_result[b"name"], logger.DEBUG) continue ep_obj = show_obj.getEpisode(cur_season, cur_ep) # build a result object title = cur_result[b"name"] url = cur_result[b"url"] logger.log("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=unused-argument if not sickbeard.USE_SUBTITLES: return if len(sickbeard.subtitles.enabled_service_list()) < 1: logger.log(u'Not enough services selected. At least 1 service is required to ' 'search subtitles in the background', logger.WARNING) return self.amActive = True if sickbeard.SUBTITLES_DOWNLOAD_IN_PP: self.subtitles_download_in_pp() logger.log(u'Checking for subtitles', logger.INFO) # get episodes on which we want subtitles # criteria is: # - show subtitles = 1 # - episode subtitles != config wanted languages or 'und' (depends on config multi) # - search count < 2 and diff(airdate, now) > 1 week : now -> 1d # - search count < 7 and diff(airdate, now) <= 1 week : now -> 4h -> 8h -> 16h -> 1d -> 1d -> 1d today = datetime.date.today().toordinal() 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 airdate_daydiff ' + '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.subtitles_searchcount <= 2 OR (e.subtitles_searchcount <= 7 AND airdate_daydiff <= 7)) ' + 'AND e.location != ""', [today, wanted_languages(True)]) if len(sql_results) == 0: logger.log(u'No subtitles to download', logger.INFO) self.amActive = False return rules = self._get_rules() now = datetime.datetime.now() for ep_to_sub in sql_results: if not ek(os.path.isfile, ep_to_sub['location']): logger.log(u'Episode file does not exist, cannot download subtitles for episode %dx%d of show %s' % (ep_to_sub['season'], ep_to_sub['episode'], ep_to_sub['show_name']), logger.DEBUG) continue if not needs_subtitles(ep_to_sub['subtitles']): logger.log(u'Episode already has all needed subtitles, skipping episode %dx%d of show %s' % (ep_to_sub['season'], ep_to_sub['episode'], ep_to_sub['show_name']), logger.DEBUG) continue # http://bugs.python.org/issue7980#msg221094 # I dont think this needs done here, but keeping to be safe (Recent shows rule) datetime.datetime.strptime('20110101', '%Y%m%d') if ((ep_to_sub['airdate_daydiff'] > 7 and ep_to_sub['searchcount'] < 2 and now - datetime.datetime.strptime(ep_to_sub['lastsearch'], dateTimeFormat) > datetime.timedelta(hours=rules['old'][ep_to_sub['searchcount']])) or (ep_to_sub['airdate_daydiff'] <= 7 and ep_to_sub['searchcount'] < 7 and now - datetime.datetime.strptime(ep_to_sub['lastsearch'], dateTimeFormat) > datetime.timedelta(hours=rules['new'][ep_to_sub['searchcount']]))): logger.log(u'Downloading subtitles for episode %dx%d of show %s' % (ep_to_sub['season'], ep_to_sub['episode'], ep_to_sub['show_name']), logger.DEBUG) show_object = Show.find(sickbeard.showList, int(ep_to_sub['showid'])) if not show_object: logger.log(u'Show not found', logger.DEBUG) self.amActive = False return episode_object = show_object.getEpisode(int(ep_to_sub["season"]), int(ep_to_sub["episode"])) if isinstance(episode_object, str): logger.log(u'Episode not found', logger.DEBUG) self.amActive = False return existing_subtitles = episode_object.subtitles try: episode_object.download_subtitles() except Exception as error: logger.log(u'Unable to find subtitles', logger.DEBUG) logger.log(str(error), logger.DEBUG) self.amActive = False return new_subtitles = frozenset(episode_object.subtitles).difference(existing_subtitles) if new_subtitles: logger.log(u'Downloaded subtitles for S%02dE%02d in %s' % (ep_to_sub["season"], ep_to_sub["episode"], ', '.join(new_subtitles))) self.amActive = False
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: 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[b'shows'][0][b'ids'][b'tvdb'] = self.indexer_id else: data[b'shows'][0][b'ids'][b'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) 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 smartish 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 sickbeard.scene_numbering.xem_refresh(self.show.indexerid, self.show.indexer, force=True) # check if show has XEM mapping so we can determin if searches should go by scene numbering or indexer numbering. if not self.scene and sickbeard.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, force=False): # pylint: disable=unused-argument,too-many-statements,too-many-branches if not sickbeard.USE_SUBTITLES: return if len(sickbeard.subtitles.enabled_service_list()) < 1: logger.log( u'Not enough services selected. At least 1 service is required to ' 'search subtitles in the background', logger.WARNING) return self.amActive = True if sickbeard.SUBTITLES_DOWNLOAD_IN_PP: self.subtitles_download_in_pp() logger.log(u'Checking for subtitles', logger.INFO) # get episodes on which we want subtitles # criteria is: # - show subtitles = 1 # - episode subtitles != config wanted languages or 'und' (depends on config multi) # - search count < 2 and diff(airdate, now) > 1 week : now -> 1d # - search count < 7 and diff(airdate, now) <= 1 week : now -> 4h -> 8h -> 16h -> 1d -> 1d -> 1d """ Defines the hours to wait between 2 subtitles search depending on: - the episode: new or old - the number of searches done so far (searchcount), represented by the index of the list """ rules = {'old': [0, 24], 'new': [0, 4, 8, 4, 16, 24, 24]} if sickbeard.SUBTITLES_MULTI: query_languages = wanted_languages(True) else: query_languages = '%und%' today = datetime.date.today().toordinal() 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 airdate_daydiff ' '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.subtitles_searchcount <= 2 OR (e.subtitles_searchcount <= 7 AND airdate_daydiff <= 7)) ' 'AND e.location != ""', [today, query_languages]) if len(sql_results) == 0: logger.log(u'No subtitles to download', logger.INFO) self.amActive = False return now = datetime.datetime.now() for ep_to_sub in sql_results: if not ek(os.path.isfile, ep_to_sub['location']): logger.log( u'Episode file does not exist, cannot download subtitles for %s S%02dE%02d' % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode']), logger.DEBUG) continue if not needs_subtitles(ep_to_sub['subtitles']): logger.log( u'Episode already has all needed subtitles, skipping %s S%02dE%02d' % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode']), logger.DEBUG) continue logger.log( u"%s S%02dE%02d doesn't have all needed subtitles" % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode']), logger.DEBUG) try: try: lastsearched = datetime.datetime.strptime( ep_to_sub['lastsearch'], dateTimeFormat) except ValueError: lastsearched = datetime.datetime.min if ((ep_to_sub['airdate_daydiff'] > 7 and ep_to_sub['searchcount'] < 2 and now - lastsearched > datetime.timedelta( hours=rules['old'][ep_to_sub['searchcount']])) or (ep_to_sub['airdate_daydiff'] <= 7 and ep_to_sub['searchcount'] < 7 and now - lastsearched > datetime.timedelta( hours=rules['new'][ep_to_sub['searchcount']]))): logger.log( u'Started subtitles search for %s S%02dE%02d' % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode']), logger.INFO) show_object = Show.find(sickbeard.showList, int(ep_to_sub['showid'])) if not show_object: logger.log( u'Show with ID %s not found in the database' % ep_to_sub['showid'], logger.DEBUG) continue episode_object = show_object.getEpisode( int(ep_to_sub["season"]), int(ep_to_sub["episode"])) if isinstance(episode_object, str): logger.log( u'%s S%02dE%02d not found in the database' % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode']), logger.DEBUG) continue existing_subtitles = episode_object.subtitles try: episode_object.download_subtitles() except Exception as error: logger.log( u'Unable to find subtitles for %s S%02dE%02d. Error: %r' % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode'], ex(error)), logger.ERROR) continue new_subtitles = frozenset( episode_object.subtitles).difference( existing_subtitles) if new_subtitles: logger.log( u'Downloaded %s subtitles for %s S%02dE%02d' % (', '.join(new_subtitles), ep_to_sub['show_name'], ep_to_sub["season"], ep_to_sub["episode"])) except Exception as error: logger.log( u'Error while searching subtitles for %s S%02dE%02d. Error: %r' % (ep_to_sub['show_name'], ep_to_sub['season'], ep_to_sub['episode'], ex(error)), logger.ERROR) continue 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: logger.log('Daily search is still running, not starting it again', logger.DEBUG) return elif sickbeard.forcedSearchQueueScheduler.action.is_forced_search_in_progress() and not force: logger.log('Manual search is running. Can\'t start Daily search', logger.WARNING) return self.amActive = True logger.log('Searching for newly released episodes ...') if not network_dict: update_network_dict() cur_time = datetime.now(sb_timezone) cur_date = ( date.today() + timedelta(days=1 if network_dict else 2) ).toordinal() main_db_con = DBConnection() episodes_from_db = main_db_con.select( b'SELECT showid, airdate, season, episode ' b'FROM tv_episodes ' b'WHERE status = ? AND (airdate <= ? and airdate > 1)', [common.UNAIRED, cur_date] ) new_releases = [] show = None for db_episode in episodes_from_db: try: show_id = int(db_episode[b'showid']) if not show or show_id != show.indexerid: show = Show.find(sickbeard.showList, show_id) # for when there is orphaned series in the database but not loaded into our show list if not show or show.paused: continue except MultipleShowObjectsException: logger.log('ERROR: expected to find a single show matching {id}'.format(id=show_id)) continue if show.airs and show.network: # This is how you assure it is always converted to local time show_air_time = parse_date_time(db_episode[b'airdate'], show.airs, show.network) end_time = show_air_time.astimezone(sb_timezone) + timedelta(minutes=try_int(show.runtime, 60)) # filter out any episodes that haven't finished airing yet, if end_time > cur_time: continue cur_ep = show.get_episode(db_episode[b'season'], db_episode[b'episode']) with cur_ep.lock: cur_ep.status = show.default_ep_status if cur_ep.season else common.SKIPPED logger.log('Setting status ({status}) for show airing today: {name} {special}'.format( name=cur_ep.pretty_name(), status=common.statusStrings[cur_ep.status], special='(specials are not supported)' if not cur_ep.season else '' )) new_releases.append(cur_ep.get_sql()) if new_releases: main_db_con = DBConnection() main_db_con.mass_action(new_releases) else: logger.log('No newly released episodes found ...') # queue episode for daily search sickbeard.searchQueueScheduler.action.add_item( DailySearchQueueItem() ) self.amActive = False
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 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(u"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(sickbeard.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.log(u"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.log(u"New episode " + ep.prettyName() + " airs today, setting status to SKIPPED because is a special season") ep.status = common.SKIPPED else: logger.log(u"New episode %s airs today, setting to default episode status for this show: %s" % (ep.prettyName(), common.statusStrings[ep.show.default_ep_status])) ep.status = ep.show.default_ep_status sql_l.append(ep.get_sql()) if len(sql_l) > 0: main_db_con = db.DBConnection() main_db_con.mass_action(sql_l) else: logger.log(u"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=None, show=None): if sickbeard.showList == None: return logger.log(u"Beginning the search for french episodes older than " + str(sickbeard.FRENCH_DELAY) + " days") foundResults = {} finalResults = [] #show = self frenchlist = [] #get list of english episodes that we want to search in french myDB = db.DBConnection() today = datetime.date.today().toordinal() if show: frenchsql = myDB.select( "SELECT showid, season, episode from tv_episodes where audio_langs='eng' and tv_episodes.showid =? and (? - tv_episodes.airdate) > ? order by showid, airdate asc", [show, today, sickbeard.FRENCH_DELAY]) logger.log( "SELECT showid, season, episode from tv_episodes where audio_langs='eng' and tv_episodes.showid =" + str(show) + "and (" + str(today) + " - tv_episodes.airdate) > " + str(sickbeard.FRENCH_DELAY) + "order by showid, airdate asc") count = myDB.select( "SELECT count(*) from tv_episodes where audio_langs='eng' and tv_episodes.showid =? and (? - tv_episodes.airdate) > ?", [show, today, sickbeard.FRENCH_DELAY]) else: frenchsql = myDB.select( "SELECT showid, season, episode from tv_episodes, tv_shows where audio_langs='eng' and tv_episodes.showid = tv_shows.indexer_id and tv_shows.frenchsearch = 1 and (? - tv_episodes.airdate) > ? order by showid, airdate asc", [today, sickbeard.FRENCH_DELAY]) count = myDB.select( "SELECT count(*) from tv_episodes, tv_shows where audio_langs='eng' and tv_episodes.showid = tv_shows.indexer_id and tv_shows.frenchsearch = 1 and (? - tv_episodes.airdate) > ?", [today, sickbeard.FRENCH_DELAY]) #make the episodes objects #logger.log("SELECT showid, season, episode from tv_episodes where audio_langs='eng' and tv_episodes.showid =" + str(show) +"and (" +str(today)+" - tv_episodes.airdate) > "+ str(sickbeard.FRENCH_DELAY) +"order by showid, airdate asc") logger.log( "SELECT showid, season, episode from tv_episodes, tv_shows where audio_langs='eng' and tv_episodes.showid = tv_shows.indexer_id and tv_shows.frenchsearch = 1 and (" + str(today) + " - tv_episodes.airdate) > " + str(sickbeard.FRENCH_DELAY) + " order by showid, airdate asc") logger.log(u"Searching for " + str(count[0][0]) + " episodes in french") #logger.log(frenchsql) #logger.log(sickbeard.showList.) for episode in frenchsql: showObj = Show.find(sickbeard.showList, int(episode[0])) if showObj == None: logger.log("Show not in show list") #showObj = helpers.findCertainShow(sickbeard.showList, episode[0]) epObj = showObj.getEpisode(episode[1], episode[2]) #epObj = showObj.getEpisode(int(epInfo[0]), int(epInfo[1])) frenchlist.append(epObj) #for each episode in frenchlist fire a search in french delay = [] temp = None rest = count[0][0] for frepisode in frenchlist: rest = rest - 1 if frepisode.show.indexerid in delay: logger.log( u"Previous episode for show " + str(frepisode.show.name) + " not found in french so skipping this search", logger.DEBUG) continue result = [] for curProvider in providers.sortedProviderList(): foundResults[curProvider.name] = {} if not curProvider.is_active(): continue logger.log(u"Searching for french episode on " + curProvider.name + " for " + frepisode.show.name + " season " + str(frepisode.season) + " episode " + str(frepisode.episode)) #try: # logger.log(frepisode) # temp = GenericProvider() # curfrench = temp.findFrench(self, episode=frepisode, manualSearch=True) #curfrench = GenericProvider.findFrench(episode=frepi sode,manualSearch=True) #curProvider.findFrench(frepisode, manualSearch=True) #except: # logger.log(u"Exception", logger.DEBUG) # pass #for curProvider in providers: # if curProvider.anime_only and not show.is_anime: # logger.log(u"" + str(show.name) + " is not an anime, skipping", logger.DEBUG) # continue curfrench = curProvider.find_search_results( frepisode.show, frenchlist, 'sponly', True, True, 'french') #curfrench = curProvider.findFrench(frepisode, True) #temp = GenericProvider('temp') #curfrench = temp.findFrench( episode=frepisode, manualSearch=True) if len(curfrench): #make a list of all the results for this provider for curEp in curfrench: if curEp in foundResults: foundResults[ curProvider.name][curEp] += curfrench[curEp] else: foundResults[ curProvider.name][curEp] = curfrench[curEp] if not foundResults[curProvider.name]: continue bestSeasonResult = None #if SEASON_RESULT in foundResults[curProvider.name]: # bestSeasonResult = search.pickBestResult(foundResults[curProvider.name][SEASON_RESULT], show) #_______________________________________________________ test = 0 if foundResults[curProvider.name]: for cur_episode in foundResults[curProvider.name]: for x in foundResults[curProvider.name][cur_episode]: tmp = x if not show_name_helpers.filterBadReleases( x.name): #x.name): logger.log( u"French " + x.name + " isn't a valid scene release that we want, ignoring it", logger.DEBUG) test += 1 continue if sickbeard.IGNORE_WORDS == "": ignore_words = "ztreyfgut" else: ignore_words = str(sickbeard.IGNORE_WORDS) for fil in resultFilters + ignore_words.split(','): if fil == showLanguages.get(u"fre"): continue if re.search('(^|[\W_])' + fil + '($|[\W_])', x.url, re.I) or re.search( '(^|[\W_])' + fil + '($|[\W_])', x.name, re.I): logger.log( u"Invalid scene release: " + x.url + " contains " + fil + ", ignoring it", logger.DEBUG) test += 1 if test == 0: result.append(x) best = None try: epi = {} epi[1] = frepisode best = search.pickBestResult(result, showObj) except: pass if best: best.name = best.name + ' snatchedfr' logger.log(u"Found french episode for " + frepisode.show.name + " season " + str(frepisode.season) + " episode " + str(frepisode.episode)) try: search.snatchEpisode(best, SNATCHED_FRENCH) except: logger.log(u"Exception", logger.DEBUG) pass else: delay.append(frepisode.show.indexerid) logger.log(u"No french episode found for " + frepisode.show.name + " season " + str(frepisode.season) + " episode " + str(frepisode.episode)) logger.log(str(rest) + u" episodes left")
def run(self, force=False): """ Runs the daily searcher, queuing selected episodes for search :param force: Force search """ if self.amActive: return self.amActive = True logger.log(u"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) myDB = db.DBConnection() sqlResults = myDB.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 sqlResults: try: if not show or int(sqlEp["showid"]) != show.indexerid: show = Show.find(sickbeard.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.log(u"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.log( u"New episode " + ep.prettyName() + " airs today, setting status to SKIPPED because is a special season" ) ep.status = common.SKIPPED else: logger.log( u"New episode %s airs today, setting to default episode status for this show: %s" % (ep.prettyName(), common.statusStrings[ep.show.default_ep_status])) ep.status = ep.show.default_ep_status sql_l.append(ep.get_sql()) if len(sql_l) > 0: myDB = db.DBConnection() myDB.mass_action(sql_l) else: logger.log(u"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 findNeededEpisodes(self, episode, manualSearch=False, downCurQuality=False): neededEps = {} cl = [] myDB = self._getDB() if not episode: sqlResults = myDB.select("SELECT * FROM [" + self.providerID + "]") elif type(episode) != list: sqlResults = myDB.select( "SELECT * FROM [" + self.providerID + "] WHERE indexerid = ? AND season = ? AND episodes LIKE ?", [episode.show.indexerid, episode.season, "%|" + str(episode.episode) + "|%"]) else: for epObj in episode: cl.append([ "SELECT * FROM [" + self.providerID + "] WHERE indexerid = ? AND season = ? AND episodes LIKE ? AND quality IN (" + ",".join( [str(x) for x in epObj.wantedQuality]) + ")", [epObj.show.indexerid, epObj.season, "%|" + str(epObj.episode) + "|%"]]) sqlResults = myDB.mass_action(cl, fetchall=True) sqlResults = list(itertools.chain(*sqlResults)) # for each cache entry for curResult in sqlResults: # ignored/required words, and non-tv junk if not show_name_helpers.filterBadReleases(curResult["name"]): continue # get the show object, or if it's not one of our shows then ignore it showObj = Show.find(sickbeard.showList, int(curResult["indexerid"])) if not showObj: continue # skip if provider is anime only and show is not anime if self.provider.anime_only and not showObj.is_anime: logger.log(u"" + str(showObj.name) + " is not an anime, skiping", logger.DEBUG) continue # get season and ep data (ignoring multi-eps for now) curSeason = int(curResult["season"]) if curSeason == -1: continue curEp = curResult["episodes"].split("|")[1] if not curEp: continue curEp = int(curEp) curQuality = int(curResult["quality"]) curReleaseGroup = curResult["release_group"] curVersion = curResult["version"] # if the show says we want that episode then add it to the list if not showObj.wantEpisode(curSeason, curEp, curQuality, manualSearch, downCurQuality): logger.log(u"Skipping " + curResult["name"] + " because we don't want an episode that's " + Quality.qualityStrings[curQuality], logger.INFO) continue epObj = showObj.getEpisode(curSeason, curEp) # build a result object title = curResult["name"] url = curResult["url"] logger.log(u"Found result " + title + " at " + url) result = self.provider.get_result([epObj]) result.show = showObj result.url = url result.name = title result.quality = curQuality result.release_group = curReleaseGroup result.version = curVersion result.content = None # add it to the list if epObj not in neededEps: neededEps[epObj] = [result] else: neededEps[epObj].append(result) # datetime stamp this search so cache gets cleared self.setLastSearch() return neededEps