def test_notify(self, username, blacklist_name=None): """ Sends a test notification to trakt with the given authentication info and returns a boolean representing success. api: The api string to use username: The username to use blacklist_name: slug of trakt list used to hide not interested show Returns: True if the request succeeded, False otherwise """ try: trakt_api = TraktAPI(sickbeard.SSL_VERIFY, sickbeard.TRAKT_TIMEOUT) trakt_api.validateAccount() if blacklist_name and blacklist_name is not None: trakt_lists = trakt_api.traktRequest("users/" + username + "/lists") found = False for trakt_list in trakt_lists: if (trakt_list[b'ids'][b'slug'] == blacklist_name): return "Test notice sent successfully to Trakt" if not found: return "Trakt blacklist doesn't exists" else: return "Test notice sent successfully to Trakt" except (traktException, traktAuthException, traktServerBusy) as e: logging.warning("Could not connect to Trakt service: %s" % ex(e)) return "Test notice failed to Trakt: %s" % ex(e)
def __init__(self): self.trakt_api = TraktAPI(sickbeard.SSL_VERIFY, sickbeard.TRAKT_TIMEOUT) self.todoBacklog = [] self.todoWanted = [] self.ShowWatchlist = {} self.EpisodeWatchlist = {} self.Collectionlist = {} self.amActive = False
def update_library(self, ep_obj): """ Sends a request to trakt indicating that the given episode is part of our library. ep_obj: The TVEpisode object to add to trakt """ trakt_id = sickbeard.indexerApi(ep_obj.show.indexer).config[b'trakt_id'] trakt_api = TraktAPI(sickbeard.SSL_VERIFY, sickbeard.TRAKT_TIMEOUT) if sickbeard.USE_TRAKT: try: # URL parameters data = { 'shows': [ { 'title': ep_obj.show.name, 'year': ep_obj.show.startyear, 'ids': {}, } ] } if trakt_id == 'tvdb_id': data[b'shows'][0][b'ids'][b'tvdb'] = ep_obj.show.indexerid else: data[b'shows'][0][b'ids'][b'tvrage'] = ep_obj.show.indexerid if sickbeard.TRAKT_SYNC_WATCHLIST: if sickbeard.TRAKT_REMOVE_SERIESLIST: trakt_api.traktRequest("sync/watchlist/remove", data, method='POST') # Add Season and Episode + Related Episodes data[b'shows'][0][b'seasons'] = [{'number': ep_obj.season, 'episodes': []}] for relEp_Obj in [ep_obj] + ep_obj.relatedEps: data[b'shows'][0][b'seasons'][0][b'episodes'].append({'number': relEp_Obj.episode}) if sickbeard.TRAKT_SYNC_WATCHLIST: if sickbeard.TRAKT_REMOVE_WATCHLIST: trakt_api.traktRequest("sync/watchlist/remove", data, method='POST') # update library trakt_api.traktRequest("sync/collection", data, method='POST') except (traktException, traktAuthException, traktServerBusy) as e: logging.warning("Could not connect to Trakt service: %s" % ex(e))
def run(self): ShowQueueItem.run(self) logging.info("Starting to add show {}".format(self.showDir)) # make sure the Indexer IDs are valid try: lINDEXER_API_PARMS = sickbeard.indexerApi(self.indexer).api_params.copy() if self.lang: lINDEXER_API_PARMS[b"language"] = self.lang logging.info("" + str(sickbeard.indexerApi(self.indexer).name) + ": " + repr(lINDEXER_API_PARMS)) t = sickbeard.indexerApi(self.indexer).indexer(**lINDEXER_API_PARMS) s = t[self.indexer_id] # 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: logging.error( "Show in " + self.showDir + " has no name on " + str(sickbeard.indexerApi(self.indexer).name) + ", probably the wrong language used to search with." ) 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: logging.error( "Show " + str(s[b"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[b"seriesname"]) + " is on " + str(sickbeard.indexerApi(self.indexer).name) + " but contains no season/episode data.", ) self._finishEarly() return except Exception as e: logging.error( "%s Error while loading information from indexer %s. Error: %r" % (self.indexer_id, sickbeard.indexerApi(self.indexer).name, ex(e)) ) ui.notifications.error( "Unable to add show", "Unable to look up the show in %s on %s using ID %s, not using the NFO. Delete .nfo and try adding manually again." % (self.showDir, sickbeard.indexerApi(self.indexer).name, self.indexer_id), ) 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._finishEarly() return try: newShow = TVShow(self.indexer, self.indexer_id, self.lang) newShow.loadFromIndexer() self.show = newShow # set up initial values self.show.location = self.showDir self.show.subtitles = self.subtitles if self.subtitles != 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 != None else sickbeard.FLATTEN_FOLDERS_DEFAULT ) self.show.anime = self.anime if self.anime != None else sickbeard.ANIME_DEFAULT self.show.scene = self.scene if self.scene != None else sickbeard.SCENE_DEFAULT self.show.archive_firstmatch = self.archive if self.archive != None else sickbeard.ARCHIVE_DEFAULT self.show.paused = self.paused if self.paused != None else False # set up default new/missing episode status logging.info("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: logging.error( "Unable to add show due to an error with " + sickbeard.indexerApi(self.indexer).name + ": {}".format(ex(e)) ) 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: logging.warning("The show in " + self.showDir + " is already in your show list, skipping") ui.notifications.error("Show skipped", "The show in " + self.showDir + " is already in your show list") self._finishEarly() return except Exception as e: logging.error("Error trying to add show: {}".format(ex(e))) logging.debug(traceback.format_exc()) self._finishEarly() raise logging.debug("Retrieving show info from IMDb") try: self.show.loadIMDbInfo() except imdb_exceptions.IMDbError as e: logging.warning(" Something wrong on IMDb api: {}".format(ex(e))) except Exception as e: logging.error("Error loading IMDb info: {}".format(ex(e))) try: self.show.saveToDB() except Exception as e: logging.error("Error saving the show to the database: {}".format(ex(e))) logging.debug(traceback.format_exc()) self._finishEarly() raise # add it to the show list sickbeard.showList.append(self.show) try: self.show.loadEpisodesFromIndexer() except Exception as e: logging.error( "Error with " + sickbeard.indexerApi(self.show.indexer).name + ", not creating episode list: {}".format(ex(e)) ) logging.debug(traceback.format_exc()) # update internal name cache name_cache.buildNameCache() try: self.show.loadEpisodesFromDir() except Exception as e: logging.error("Error searching dir for episodes: {}".format(ex(e))) logging.debug(traceback.format_exc()) # 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: logging.info("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: logging.info("update watchlist") notifiers.trakt_notifier.update_watchlist(show_obj=self.show) # Load XEM data to DB for show scene_numbering.xem_refresh(self.show.indexerid, self.show.indexer, force=True) # check if show has XEM mapping so we can determin if searches should go by scene numbering or indexer numbering. if not self.scene and scene_numbering.get_xem_numbering_for_show(self.show.indexerid, self.show.indexer): self.show.scene = 1 # After initial add, set to default_status_after. self.show.default_ep_status = self.default_status_after self.finish()
class TraktChecker(object): def __init__(self): self.trakt_api = TraktAPI(sickbeard.SSL_VERIFY, sickbeard.TRAKT_TIMEOUT) self.todoBacklog = [] self.todoWanted = [] self.ShowWatchlist = {} self.EpisodeWatchlist = {} self.Collectionlist = {} self.amActive = False def run(self, force=False): self.amActive = True # add shows from trakt.tv watchlist if sickbeard.TRAKT_SYNC_WATCHLIST: self.todoWanted = [] # its about to all get re-added if len(sickbeard.ROOT_DIRS.split("|")) < 2: logging.warning("No default root directory") return try: self.syncWatchlist() except Exception: logging.debug(traceback.format_exc()) try: # sync trakt.tv library with sickrage library self.syncLibrary() except Exception: logging.debug(traceback.format_exc()) self.amActive = False def findShow(self, indexer, indexerid): traktShow = None try: library = self.trakt_api.traktRequest("sync/collection/shows") or [] if not library: logging.warning("Could not connect to trakt service, aborting library check") return if not len(library): logging.debug("No shows found in your library, aborting library update") return traktShow = [ x for x in library if int(indexerid) in [int(x[b"show"][b"ids"][b"tvdb"] or 0), int(x[b"show"][b"ids"][b"tvrage"] or 0)] ] except traktException as e: logging.warning("Could not connect to Trakt service. Aborting library check. Error: %s" % repr(e)) return traktShow def removeShowFromTraktLibrary(self, show_obj): if self.findShow(show_obj.indexer, show_obj.indexerid): trakt_id = sickbeard.indexerApi(show_obj.indexer).config[b"trakt_id"] # URL parameters data = {"shows": [{"title": show_obj.name, "year": show_obj.startyear, "ids": {}}]} if trakt_id == "tvdb_id": data[b"shows"][0][b"ids"][b"tvdb"] = show_obj.indexerid else: data[b"shows"][0][b"ids"][b"tvrage"] = show_obj.indexerid logging.debug("Removing %s from trakt.tv library" % show_obj.name) try: self.trakt_api.traktRequest("sync/collection/remove", data, method="POST") except traktException as e: logging.warning( "Could not connect to Trakt service. Aborting removing show %s from Trakt library. Error: %s" % (show_obj.name, repr(e)) ) def addShowToTraktLibrary(self, show_obj): """ Sends a request to trakt indicating that the given show and all its episodes is part of our library. show_obj: The TVShow object to add to trakt """ data = {} if not self.findShow(show_obj.indexer, show_obj.indexerid): trakt_id = sickbeard.indexerApi(show_obj.indexer).config[b"trakt_id"] # URL parameters data = {"shows": [{"title": show_obj.name, "year": show_obj.startyear, "ids": {}}]} if trakt_id == "tvdb_id": data[b"shows"][0][b"ids"][b"tvdb"] = show_obj.indexerid else: data[b"shows"][0][b"ids"][b"tvrage"] = show_obj.indexerid if len(data): logging.debug("Adding %s to trakt.tv library" % show_obj.name) try: self.trakt_api.traktRequest("sync/collection", data, method="POST") except traktException as e: logging.warning( "Could not connect to Trakt service. Aborting adding show %s to Trakt library. Error: %s" % (show_obj.name, repr(e)) ) return def syncLibrary(self): if sickbeard.TRAKT_SYNC and sickbeard.USE_TRAKT: logging.debug("Sync SiCKRAGE with Trakt Collection") if self._getShowCollection(): self.addEpisodeToTraktCollection() if sickbeard.TRAKT_SYNC_REMOVE: self.removeEpisodeFromTraktCollection() def removeEpisodeFromTraktCollection(self): if sickbeard.TRAKT_SYNC_REMOVE and sickbeard.TRAKT_SYNC and sickbeard.USE_TRAKT: logging.debug("COLLECTION::REMOVE::START - Look for Episodes to Remove From Trakt Collection") myDB = db.DBConnection() sql_selection = "SELECT tv_shows.indexer, tv_shows.startyear, showid, show_name, season, episode, tv_episodes.status, tv_episodes.location FROM tv_episodes,tv_shows WHERE tv_shows.indexer_id = tv_episodes.showid" episodes = myDB.select(sql_selection) if episodes is not None: trakt_data = [] for cur_episode in episodes: trakt_id = sickbeard.indexerApi(cur_episode[b"indexer"]).config[b"trakt_id"] if self._checkInList( trakt_id, str(cur_episode[b"showid"]), str(cur_episode[b"season"]), str(cur_episode[b"episode"]), List="Collection", ): if cur_episode[b"location"] == "": logging.debug( "Removing Episode %s S%02dE%02d from collection" % (cur_episode[b"show_name"], cur_episode[b"season"], cur_episode[b"episode"]) ) trakt_data.append( ( cur_episode[b"showid"], cur_episode[b"indexer"], cur_episode[b"show_name"], cur_episode[b"startyear"], cur_episode[b"season"], cur_episode[b"episode"], ) ) if len(trakt_data): try: data = self.trakt_bulk_data_generate(trakt_data) self.trakt_api.traktRequest("sync/collection/remove", data, method="POST") self._getShowCollection() except traktException as e: logging.warning("Could not connect to Trakt service. Error: %s" % ex(e)) logging.debug("COLLECTION::REMOVE::FINISH - Look for Episodes to Remove From Trakt Collection") def addEpisodeToTraktCollection(self): if sickbeard.TRAKT_SYNC and sickbeard.USE_TRAKT: logging.debug("COLLECTION::ADD::START - Look for Episodes to Add to Trakt Collection") myDB = db.DBConnection() sql_selection = ( "SELECT tv_shows.indexer, tv_shows.startyear, showid, show_name, season, episode FROM tv_episodes,tv_shows WHERE tv_shows.indexer_id = tv_episodes.showid AND tv_episodes.status IN (" + ",".join([str(x) for x in Quality.DOWNLOADED + [ARCHIVED]]) + ")" ) episodes = myDB.select(sql_selection) if episodes is not None: trakt_data = [] for cur_episode in episodes: trakt_id = sickbeard.indexerApi(cur_episode[b"indexer"]).config[b"trakt_id"] if not self._checkInList( trakt_id, str(cur_episode[b"showid"]), str(cur_episode[b"season"]), str(cur_episode[b"episode"]), List="Collection", ): logging.debug( "Adding Episode %s S%02dE%02d to collection" % (cur_episode[b"show_name"], cur_episode[b"season"], cur_episode[b"episode"]) ) trakt_data.append( ( cur_episode[b"showid"], cur_episode[b"indexer"], cur_episode[b"show_name"], cur_episode[b"startyear"], cur_episode[b"season"], cur_episode[b"episode"], ) ) if len(trakt_data): try: data = self.trakt_bulk_data_generate(trakt_data) self.trakt_api.traktRequest("sync/collection", data, method="POST") self._getShowCollection() except traktException as e: logging.warning("Could not connect to Trakt service. Error: %s" % ex(e)) logging.debug("COLLECTION::ADD::FINISH - Look for Episodes to Add to Trakt Collection") def syncWatchlist(self): if sickbeard.TRAKT_SYNC_WATCHLIST and sickbeard.USE_TRAKT: logging.debug("Sync SiCKRAGE with Trakt Watchlist") self.removeShowFromSickRage() if self._getShowWatchlist(): self.addShowToTraktWatchList() self.updateShows() if self._getEpisodeWatchlist(): self.removeEpisodeFromTraktWatchList() self.addEpisodeToTraktWatchList() self.updateEpisodes() def removeEpisodeFromTraktWatchList(self): if sickbeard.TRAKT_SYNC_WATCHLIST and sickbeard.USE_TRAKT: logging.debug("WATCHLIST::REMOVE::START - Look for Episodes to Remove from Trakt Watchlist") myDB = db.DBConnection() sql_selection = "SELECT tv_shows.indexer, tv_shows.startyear, showid, show_name, season, episode, tv_episodes.status FROM tv_episodes,tv_shows WHERE tv_shows.indexer_id = tv_episodes.showid" episodes = myDB.select(sql_selection) if episodes is not None: trakt_data = [] for cur_episode in episodes: trakt_id = sickbeard.indexerApi(cur_episode[b"indexer"]).config[b"trakt_id"] if self._checkInList( trakt_id, str(cur_episode[b"showid"]), str(cur_episode[b"season"]), str(cur_episode[b"episode"]) ): if cur_episode[b"status"] not in Quality.SNATCHED + Quality.SNATCHED_PROPER + [UNKNOWN] + [ WANTED ]: logging.debug( "Removing Episode %s S%02dE%02d from watchlist" % (cur_episode[b"show_name"], cur_episode[b"season"], cur_episode[b"episode"]) ) trakt_data.append( ( cur_episode[b"showid"], cur_episode[b"indexer"], cur_episode[b"show_name"], cur_episode[b"startyear"], cur_episode[b"season"], cur_episode[b"episode"], ) ) if len(trakt_data): try: data = self.trakt_bulk_data_generate(trakt_data) self.trakt_api.traktRequest("sync/watchlist/remove", data, method="POST") self._getEpisodeWatchlist() except traktException as e: logging.warning("Could not connect to Trakt service. Error: %s" % ex(e)) logging.debug("WATCHLIST::REMOVE::FINISH - Look for Episodes to Remove from Trakt Watchlist") def addEpisodeToTraktWatchList(self): if sickbeard.TRAKT_SYNC_WATCHLIST and sickbeard.USE_TRAKT: logging.debug("WATCHLIST::ADD::START - Look for Episodes to Add to Trakt Watchlist") myDB = db.DBConnection() sql_selection = ( "SELECT tv_shows.indexer, tv_shows.startyear, showid, show_name, season, episode FROM tv_episodes,tv_shows WHERE tv_shows.indexer_id = tv_episodes.showid AND tv_episodes.status IN (" + ",".join([str(x) for x in Quality.SNATCHED + Quality.SNATCHED_PROPER + [WANTED]]) + ")" ) episodes = myDB.select(sql_selection) if episodes is not None: trakt_data = [] for cur_episode in episodes: trakt_id = sickbeard.indexerApi(cur_episode[b"indexer"]).config[b"trakt_id"] if not self._checkInList( trakt_id, str(cur_episode[b"showid"]), str(cur_episode[b"season"]), str(cur_episode[b"episode"]) ): logging.debug( "Adding Episode %s S%02dE%02d to watchlist" % (cur_episode[b"show_name"], cur_episode[b"season"], cur_episode[b"episode"]) ) trakt_data.append( ( cur_episode[b"showid"], cur_episode[b"indexer"], cur_episode[b"show_name"], cur_episode[b"startyear"], cur_episode[b"season"], cur_episode[b"episode"], ) ) if len(trakt_data): try: data = self.trakt_bulk_data_generate(trakt_data) self.trakt_api.traktRequest("sync/watchlist", data, method="POST") self._getEpisodeWatchlist() except traktException as e: logging.warning("Could not connect to Trakt service. Error %s" % ex(e)) logging.debug("WATCHLIST::ADD::FINISH - Look for Episodes to Add to Trakt Watchlist") def addShowToTraktWatchList(self): if sickbeard.TRAKT_SYNC_WATCHLIST and sickbeard.USE_TRAKT: logging.debug("SHOW_WATCHLIST::ADD::START - Look for Shows to Add to Trakt Watchlist") if sickbeard.showList is not None: trakt_data = [] for show in sickbeard.showList: trakt_id = sickbeard.indexerApi(show.indexer).config[b"trakt_id"] if not self._checkInList(trakt_id, str(show.indexerid), "0", "0", List="Show"): logging.debug( "Adding Show: Indexer %s %s - %s to Watchlist" % (trakt_id, str(show.indexerid), show.name) ) show_el = {"title": show.name, "year": show.startyear, "ids": {}} if trakt_id == "tvdb_id": show_el[b"ids"][b"tvdb"] = show.indexerid else: show_el[b"ids"][b"tvrage"] = show.indexerid trakt_data.append(show_el) if len(trakt_data): try: data = {"shows": trakt_data} self.trakt_api.traktRequest("sync/watchlist", data, method="POST") self._getShowWatchlist() except traktException as e: logging.warning("Could not connect to Trakt service. Error: %s" % ex(e)) logging.debug("SHOW_WATCHLIST::ADD::FINISH - Look for Shows to Add to Trakt Watchlist") def removeShowFromSickRage(self): if sickbeard.TRAKT_SYNC_WATCHLIST and sickbeard.USE_TRAKT and sickbeard.TRAKT_REMOVE_SHOW_FROM_SICKRAGE: logging.debug("SHOW_SICKRAGE::REMOVE::START - Look for Shows to remove from SiCKRAGE") if sickbeard.showList: for show in sickbeard.showList: if show.status == "Ended": try: progress = self.trakt_api.traktRequest("shows/" + show.imdbid + "/progress/watched") or [] except traktException as e: logging.warning( "Could not connect to Trakt service. Aborting removing show %s from SiCKRAGE. Error: %s" % (show.name, repr(e)) ) return if ( "aired" in progress and "completed" in progress and progress[b"aired"] == progress["completed"] ): sickbeard.showQueueScheduler.action.removeShow(show, full=True) logging.debug("Show: %s has been removed from SiCKRAGE" % show.name) logging.debug("SHOW_SICKRAGE::REMOVE::FINISH - Trakt Show Watchlist") def updateShows(self): logging.debug("SHOW_WATCHLIST::CHECK::START - Trakt Show Watchlist") if not len(self.ShowWatchlist): logging.debug("No shows found in your watchlist, aborting watchlist update") return indexer = int(sickbeard.TRAKT_DEFAULT_INDEXER) trakt_id = sickbeard.indexerApi(indexer).config[b"trakt_id"] for show_el in self.ShowWatchlist[trakt_id]: indexer_id = int(str(show_el)) show = self.ShowWatchlist[trakt_id][show_el] # logging.debug(u"Checking Show: %s %s %s" % (trakt_id, indexer_id, show[b'title'])) if int(sickbeard.TRAKT_METHOD_ADD) != 2: self.addDefaultShow(indexer, indexer_id, show[b"title"], SKIPPED) else: self.addDefaultShow(indexer, indexer_id, show[b"title"], WANTED) if int(sickbeard.TRAKT_METHOD_ADD) == 1: newShow = helpers.findCertainShow(sickbeard.showList, indexer_id) if newShow is not None: setEpisodeToWanted(newShow, 1, 1) else: self.todoWanted.append((indexer_id, 1, 1)) logging.debug("SHOW_WATCHLIST::CHECK::FINISH - Trakt Show Watchlist") def updateEpisodes(self): """ Sets episodes to wanted that are in trakt watchlist """ logging.debug("SHOW_WATCHLIST::CHECK::START - Trakt Episode Watchlist") if not len(self.EpisodeWatchlist): logging.debug("No episode found in your watchlist, aborting episode update") return managed_show = [] indexer = int(sickbeard.TRAKT_DEFAULT_INDEXER) trakt_id = sickbeard.indexerApi(indexer).config[b"trakt_id"] for show_el in self.EpisodeWatchlist[trakt_id]: indexer_id = int(show_el) show = self.EpisodeWatchlist[trakt_id][show_el] newShow = helpers.findCertainShow(sickbeard.showList, indexer_id) try: if newShow is None: if indexer_id not in managed_show: self.addDefaultShow(indexer, indexer_id, show[b"title"], SKIPPED) managed_show.append(indexer_id) for season_el in show[b"seasons"]: season = int(season_el) for episode_el in show[b"seasons"][season_el][b"episodes"]: self.todoWanted.append((indexer_id, season, int(episode_el))) else: if newShow.indexer == indexer: for season_el in show[b"seasons"]: season = int(season_el) for episode_el in show[b"seasons"][season_el][b"episodes"]: setEpisodeToWanted(newShow, season, int(episode_el)) except TypeError: logging.debug("Could not parse the output from trakt for %s " % show[b"title"]) logging.debug("SHOW_WATCHLIST::CHECK::FINISH - Trakt Episode Watchlist") @staticmethod def addDefaultShow(indexer, indexer_id, name, status): """ Adds a new show with the default settings """ if not helpers.findCertainShow(sickbeard.showList, int(indexer_id)): logging.info("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, helpers.sanitizeFileName(name)) dir_exists = helpers.makeDir(showPath) if not dir_exists: logging.warning("Unable to create the folder %s , can't add the show" % showPath) 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, archive=sickbeard.ARCHIVE_DEFAULT, ) else: logging.warning("There was an error creating the show, no root directory setting found") return def manageNewShow(self, show): logging.debug("Checking if trakt watch list wants to search for episodes from new show " + show.name) episodes = [i for i in self.todoWanted if i[0] == show.indexerid] for episode in episodes: self.todoWanted.remove(episode) setEpisodeToWanted(show, episode[1], episode[2]) def _checkInList(self, trakt_id, showid, season, episode, List=None): """ Check in the Watchlist or CollectionList for Show Is the Show, Season and Episode in the trakt_id list (tvdb / tvrage) """ # logging.debug(u"Checking Show: %s %s %s " % (trakt_id, showid, List)) if "Collection" == List: try: if self.Collectionlist[trakt_id][showid][b"seasons"][season][b"episodes"][episode] == episode: return True except Exception: return False elif "Show" == List: try: if self.ShowWatchlist[trakt_id][showid][b"id"] == showid: return True except Exception: return False else: try: if self.EpisodeWatchlist[trakt_id][showid][b"seasons"][season][b"episodes"][episode] == episode: return True except Exception: return False def _getShowWatchlist(self): """ Get Watchlist and parse once into addressable structure """ try: self.ShowWatchlist = {"tvdb_id": {}, "tvrage_id": {}} TraktShowWatchlist = self.trakt_api.traktRequest("sync/watchlist/shows") tvdb_id = "tvdb" tvrage_id = "tvrage" for watchlist_el in TraktShowWatchlist: tvdb = False tvrage = False if not watchlist_el[b"show"][b"ids"][b"tvdb"] is None: tvdb = True if not watchlist_el[b"show"][b"ids"][b"tvrage"] is None: tvrage = True title = watchlist_el[b"show"][b"title"] year = str(watchlist_el[b"show"][b"year"]) if tvdb: showid = str(watchlist_el[b"show"][b"ids"][tvdb_id]) self.ShowWatchlist[tvdb_id + "_id"][showid] = {"id": showid, "title": title, "year": year} if tvrage: showid = str(watchlist_el[b"show"][b"ids"][tvrage_id]) self.ShowWatchlist[tvrage_id + "_id"][showid] = {"id": showid, "title": title, "year": year} except traktException as e: logging.warning("Could not connect to trakt service, cannot download Show Watchlist: %s" % repr(e)) return False return True def _getEpisodeWatchlist(self): """ Get Watchlist and parse once into addressable structure """ try: self.EpisodeWatchlist = {"tvdb_id": {}, "tvrage_id": {}} TraktEpisodeWatchlist = self.trakt_api.traktRequest("sync/watchlist/episodes") tvdb_id = "tvdb" tvrage_id = "tvrage" for watchlist_el in TraktEpisodeWatchlist: tvdb = False tvrage = False if not watchlist_el[b"show"][b"ids"][b"tvdb"] is None: tvdb = True if not watchlist_el[b"show"][b"ids"][b"tvrage"] is None: tvrage = True title = watchlist_el[b"show"][b"title"] year = str(watchlist_el[b"show"][b"year"]) season = str(watchlist_el[b"episode"][b"season"]) episode = str(watchlist_el[b"episode"][b"number"]) if tvdb: showid = str(watchlist_el[b"show"][b"ids"][tvdb_id]) if showid not in self.EpisodeWatchlist[tvdb_id + "_id"].keys(): self.EpisodeWatchlist[tvdb_id + "_id"][showid] = { "id": showid, "title": title, "year": year, "seasons": {}, } if season not in self.EpisodeWatchlist[tvdb_id + "_id"][showid][b"seasons"].keys(): self.EpisodeWatchlist[tvdb_id + "_id"][showid][b"seasons"][season] = { "s": season, "episodes": {}, } if ( episode not in self.EpisodeWatchlist[tvdb_id + "_id"][showid][b"seasons"][season]["episodes"].keys() ): self.EpisodeWatchlist[tvdb_id + "_id"][showid][b"seasons"][season][b"episodes"][ episode ] = episode if tvrage: showid = str(watchlist_el[b"show"][b"ids"][tvrage_id]) if showid not in self.EpisodeWatchlist[tvrage_id + "_id"].keys(): self.EpisodeWatchlist[tvrage_id + "_id"][showid] = { "id": showid, "title": title, "year": year, "seasons": {}, } if season not in self.EpisodeWatchlist[tvrage_id + "_id"][showid][b"seasons"].keys(): self.EpisodeWatchlist[tvrage_id + "_id"][showid][b"seasons"][season] = { "s": season, "episodes": {}, } if ( episode not in self.EpisodeWatchlist[tvrage_id + "_id"][showid][b"seasons"][season]["episodes"].keys() ): self.EpisodeWatchlist[tvrage_id + "_id"][showid][b"seasons"][season][b"episodes"][ episode ] = episode except traktException as e: logging.warning("Could not connect to trakt service, cannot download Episode Watchlist: %s" % repr(e)) return False return True def _getShowCollection(self): """ Get Collection and parse once into addressable structure """ try: self.Collectionlist = {"tvdb_id": {}, "tvrage_id": {}} logging.debug("Getting Show Collection") TraktCollectionList = self.trakt_api.traktRequest("sync/collection/shows") tvdb_id = "tvdb" tvrage_id = "tvrage" for watchlist_el in TraktCollectionList: tvdb = False tvrage = False if not watchlist_el[b"show"][b"ids"][b"tvdb"] is None: tvdb = True if not watchlist_el[b"show"][b"ids"][b"tvrage"] is None: tvrage = True title = watchlist_el[b"show"][b"title"] year = str(watchlist_el[b"show"][b"year"]) if "seasons" in watchlist_el: for season_el in watchlist_el[b"seasons"]: for episode_el in season_el[b"episodes"]: season = str(season_el[b"number"]) episode = str(episode_el[b"number"]) if tvdb: showid = str(watchlist_el[b"show"][b"ids"][tvdb_id]) if showid not in self.Collectionlist[tvdb_id + "_id"].keys(): self.Collectionlist[tvdb_id + "_id"][showid] = { "id": showid, "title": title, "year": year, "seasons": {}, } if season not in self.Collectionlist[tvdb_id + "_id"][showid][b"seasons"].keys(): self.Collectionlist[tvdb_id + "_id"][showid][b"seasons"][season] = { "s": season, "episodes": {}, } if ( episode not in self.Collectionlist[tvdb_id + "_id"][showid][b"seasons"][season][ "episodes" ].keys() ): self.Collectionlist[tvdb_id + "_id"][showid][b"seasons"][season][b"episodes"][ episode ] = episode if tvrage: showid = str(watchlist_el[b"show"][b"ids"][tvrage_id]) if showid not in self.Collectionlist[tvrage_id + "_id"].keys(): self.Collectionlist[tvrage_id + "_id"][showid] = { "id": showid, "title": title, "year": year, "seasons": {}, } if season not in self.Collectionlist[tvrage_id + "_id"][showid][b"seasons"].keys(): self.Collectionlist[tvrage_id + "_id"][showid][b"seasons"][season] = { "s": season, "episodes": {}, } if ( episode not in self.Collectionlist[tvrage_id + "_id"][showid][b"seasons"][season][ "episodes" ].keys() ): self.Collectionlist[tvrage_id + "_id"][showid][b"seasons"][season][b"episodes"][ episode ] = episode except traktException as e: logging.warning("Could not connect to trakt service, cannot download Show Collection: %s" % repr(e)) return False return True @staticmethod def trakt_bulk_data_generate(data): """ Build the JSON structure to send back to Trakt """ uniqueShows = {} uniqueSeasons = {} for showid, indexerid, show_name, startyear, season, episode in data: if showid not in uniqueShows: uniqueShows[showid] = {"title": show_name, "year": startyear, "ids": {}, "seasons": []} trakt_id = sickbeard.indexerApi(indexerid).config[b"trakt_id"] if trakt_id == "tvdb_id": uniqueShows[showid][b"ids"][b"tvdb"] = showid else: uniqueShows[showid][b"ids"][b"tvrage"] = showid uniqueSeasons[showid] = [] # Get the unique seasons per Show for showid, indexerid, show_name, startyear, season, episode in data: if season not in uniqueSeasons[showid]: uniqueSeasons[showid].append(season) # build the query showList = [] seasonsList = {} for searchedShow in uniqueShows: seasonsList[searchedShow] = [] for searchedSeason in uniqueSeasons[searchedShow]: episodesList = [] for showid, indexerid, show_name, startyear, season, episode in data: if season == searchedSeason and showid == searchedShow: episodesList.append({"number": episode}) show = uniqueShows[searchedShow] show[b"seasons"].append({"number": searchedSeason, "episodes": episodesList}) showList.append(show) post_data = {"shows": showList} return post_data
class TraktChecker(object): def __init__(self): self.trakt_api = TraktAPI(sickbeard.SSL_VERIFY, sickbeard.TRAKT_TIMEOUT) self.todoBacklog = [] self.todoWanted = [] self.ShowWatchlist = {} self.EpisodeWatchlist = {} self.Collectionlist = {} self.amActive = False def run(self, force=False): self.amActive = True # add shows from trakt.tv watchlist if sickbeard.TRAKT_SYNC_WATCHLIST: self.todoWanted = [] # its about to all get re-added if len(sickbeard.ROOT_DIRS.split('|')) < 2: logging.warning("No default root directory") return try: self.syncWatchlist() except Exception: logging.debug(traceback.format_exc()) try: # sync trakt.tv library with sickrage library self.syncLibrary() except Exception: logging.debug(traceback.format_exc()) self.amActive = False def findShow(self, indexer, indexerid): traktShow = None try: library = self.trakt_api.traktRequest( "sync/collection/shows") or [] if not library: logging.warning( "Could not connect to trakt service, aborting library check" ) return if not len(library): logging.debug( "No shows found in your library, aborting library update") return traktShow = [ x for x in library if int(indexerid) in [ int(x[b'show'][b'ids'][b'tvdb'] or 0), int(x[b'show'][b'ids'][b'tvrage'] or 0) ] ] except traktException as e: logging.warning( "Could not connect to Trakt service. Aborting library check. Error: %s" % repr(e)) return traktShow def removeShowFromTraktLibrary(self, show_obj): if self.findShow(show_obj.indexer, show_obj.indexerid): trakt_id = sickbeard.indexerApi( show_obj.indexer).config[b'trakt_id'] # URL parameters data = { 'shows': [{ 'title': show_obj.name, 'year': show_obj.startyear, 'ids': {} }] } if trakt_id == 'tvdb_id': data[b'shows'][0][b'ids'][b'tvdb'] = show_obj.indexerid else: data[b'shows'][0][b'ids'][b'tvrage'] = show_obj.indexerid logging.debug("Removing %s from trakt.tv library" % show_obj.name) try: self.trakt_api.traktRequest("sync/collection/remove", data, method='POST') except traktException as e: logging.warning( "Could not connect to Trakt service. Aborting removing show %s from Trakt library. Error: %s" % (show_obj.name, repr(e))) def addShowToTraktLibrary(self, show_obj): """ Sends a request to trakt indicating that the given show and all its episodes is part of our library. show_obj: The TVShow object to add to trakt """ data = {} if not self.findShow(show_obj.indexer, show_obj.indexerid): trakt_id = sickbeard.indexerApi( show_obj.indexer).config[b'trakt_id'] # URL parameters data = { 'shows': [{ 'title': show_obj.name, 'year': show_obj.startyear, 'ids': {} }] } if trakt_id == 'tvdb_id': data[b'shows'][0][b'ids'][b'tvdb'] = show_obj.indexerid else: data[b'shows'][0][b'ids'][b'tvrage'] = show_obj.indexerid if len(data): logging.debug("Adding %s to trakt.tv library" % show_obj.name) try: self.trakt_api.traktRequest("sync/collection", data, method='POST') except traktException as e: logging.warning( "Could not connect to Trakt service. Aborting adding show %s to Trakt library. Error: %s" % (show_obj.name, repr(e))) return def syncLibrary(self): if sickbeard.TRAKT_SYNC and sickbeard.USE_TRAKT: logging.debug("Sync SiCKRAGE with Trakt Collection") if self._getShowCollection(): self.addEpisodeToTraktCollection() if sickbeard.TRAKT_SYNC_REMOVE: self.removeEpisodeFromTraktCollection() def removeEpisodeFromTraktCollection(self): if sickbeard.TRAKT_SYNC_REMOVE and sickbeard.TRAKT_SYNC and sickbeard.USE_TRAKT: logging.debug( "COLLECTION::REMOVE::START - Look for Episodes to Remove From Trakt Collection" ) myDB = db.DBConnection() sql_selection = 'SELECT tv_shows.indexer, tv_shows.startyear, showid, show_name, season, episode, tv_episodes.status, tv_episodes.location FROM tv_episodes,tv_shows WHERE tv_shows.indexer_id = tv_episodes.showid' episodes = myDB.select(sql_selection) if episodes is not None: trakt_data = [] for cur_episode in episodes: trakt_id = sickbeard.indexerApi( cur_episode[b"indexer"]).config[b'trakt_id'] if self._checkInList(trakt_id, str(cur_episode[b"showid"]), str(cur_episode[b"season"]), str(cur_episode[b"episode"]), List='Collection'): if cur_episode[b"location"] == '': logging.debug( "Removing Episode %s S%02dE%02d from collection" % (cur_episode[b"show_name"], cur_episode[b"season"], cur_episode[b"episode"])) trakt_data.append((cur_episode[b"showid"], cur_episode[b"indexer"], cur_episode[b"show_name"], cur_episode[b"startyear"], cur_episode[b"season"], cur_episode[b"episode"])) if len(trakt_data): try: data = self.trakt_bulk_data_generate(trakt_data) self.trakt_api.traktRequest("sync/collection/remove", data, method='POST') self._getShowCollection() except traktException as e: logging.warning( "Could not connect to Trakt service. Error: %s" % ex(e)) logging.debug( "COLLECTION::REMOVE::FINISH - Look for Episodes to Remove From Trakt Collection" ) def addEpisodeToTraktCollection(self): if sickbeard.TRAKT_SYNC and sickbeard.USE_TRAKT: logging.debug( "COLLECTION::ADD::START - Look for Episodes to Add to Trakt Collection" ) myDB = db.DBConnection() sql_selection = 'SELECT tv_shows.indexer, tv_shows.startyear, showid, show_name, season, episode FROM tv_episodes,tv_shows WHERE tv_shows.indexer_id = tv_episodes.showid AND tv_episodes.status IN (' + ','.join( [str(x) for x in Quality.DOWNLOADED + [ARCHIVED]]) + ')' episodes = myDB.select(sql_selection) if episodes is not None: trakt_data = [] for cur_episode in episodes: trakt_id = sickbeard.indexerApi( cur_episode[b"indexer"]).config[b'trakt_id'] if not self._checkInList(trakt_id, str(cur_episode[b"showid"]), str(cur_episode[b"season"]), str(cur_episode[b"episode"]), List='Collection'): logging.debug( "Adding Episode %s S%02dE%02d to collection" % (cur_episode[b"show_name"], cur_episode[b"season"], cur_episode[b"episode"])) trakt_data.append( (cur_episode[b"showid"], cur_episode[b"indexer"], cur_episode[b"show_name"], cur_episode[b"startyear"], cur_episode[b"season"], cur_episode[b"episode"])) if len(trakt_data): try: data = self.trakt_bulk_data_generate(trakt_data) self.trakt_api.traktRequest("sync/collection", data, method='POST') self._getShowCollection() except traktException as e: logging.warning( "Could not connect to Trakt service. Error: %s" % ex(e)) logging.debug( "COLLECTION::ADD::FINISH - Look for Episodes to Add to Trakt Collection" ) def syncWatchlist(self): if sickbeard.TRAKT_SYNC_WATCHLIST and sickbeard.USE_TRAKT: logging.debug("Sync SiCKRAGE with Trakt Watchlist") self.removeShowFromSickRage() if self._getShowWatchlist(): self.addShowToTraktWatchList() self.updateShows() if self._getEpisodeWatchlist(): self.removeEpisodeFromTraktWatchList() self.addEpisodeToTraktWatchList() self.updateEpisodes() def removeEpisodeFromTraktWatchList(self): if sickbeard.TRAKT_SYNC_WATCHLIST and sickbeard.USE_TRAKT: logging.debug( "WATCHLIST::REMOVE::START - Look for Episodes to Remove from Trakt Watchlist" ) myDB = db.DBConnection() sql_selection = 'SELECT tv_shows.indexer, tv_shows.startyear, showid, show_name, season, episode, tv_episodes.status FROM tv_episodes,tv_shows WHERE tv_shows.indexer_id = tv_episodes.showid' episodes = myDB.select(sql_selection) if episodes is not None: trakt_data = [] for cur_episode in episodes: trakt_id = sickbeard.indexerApi( cur_episode[b"indexer"]).config[b'trakt_id'] if self._checkInList(trakt_id, str(cur_episode[b"showid"]), str(cur_episode[b"season"]), str(cur_episode[b"episode"])): if cur_episode[ b"status"] not in Quality.SNATCHED + Quality.SNATCHED_PROPER + [ UNKNOWN ] + [WANTED]: logging.debug( "Removing Episode %s S%02dE%02d from watchlist" % (cur_episode[b"show_name"], cur_episode[b"season"], cur_episode[b"episode"])) trakt_data.append((cur_episode[b"showid"], cur_episode[b"indexer"], cur_episode[b"show_name"], cur_episode[b"startyear"], cur_episode[b"season"], cur_episode[b"episode"])) if len(trakt_data): try: data = self.trakt_bulk_data_generate(trakt_data) self.trakt_api.traktRequest("sync/watchlist/remove", data, method='POST') self._getEpisodeWatchlist() except traktException as e: logging.warning( "Could not connect to Trakt service. Error: %s" % ex(e)) logging.debug( "WATCHLIST::REMOVE::FINISH - Look for Episodes to Remove from Trakt Watchlist" ) def addEpisodeToTraktWatchList(self): if sickbeard.TRAKT_SYNC_WATCHLIST and sickbeard.USE_TRAKT: logging.debug( "WATCHLIST::ADD::START - Look for Episodes to Add to Trakt Watchlist" ) myDB = db.DBConnection() sql_selection = 'SELECT tv_shows.indexer, tv_shows.startyear, showid, show_name, season, episode FROM tv_episodes,tv_shows WHERE tv_shows.indexer_id = tv_episodes.showid AND tv_episodes.status IN (' + ','.join( [ str(x) for x in Quality.SNATCHED + Quality.SNATCHED_PROPER + [WANTED] ]) + ')' episodes = myDB.select(sql_selection) if episodes is not None: trakt_data = [] for cur_episode in episodes: trakt_id = sickbeard.indexerApi( cur_episode[b"indexer"]).config[b'trakt_id'] if not self._checkInList(trakt_id, str(cur_episode[b"showid"]), str(cur_episode[b"season"]), str(cur_episode[b"episode"])): logging.debug( "Adding Episode %s S%02dE%02d to watchlist" % (cur_episode[b"show_name"], cur_episode[b"season"], cur_episode[b"episode"])) trakt_data.append( (cur_episode[b"showid"], cur_episode[b"indexer"], cur_episode[b"show_name"], cur_episode[b"startyear"], cur_episode[b"season"], cur_episode[b"episode"])) if len(trakt_data): try: data = self.trakt_bulk_data_generate(trakt_data) self.trakt_api.traktRequest("sync/watchlist", data, method='POST') self._getEpisodeWatchlist() except traktException as e: logging.warning( "Could not connect to Trakt service. Error %s" % ex(e)) logging.debug( "WATCHLIST::ADD::FINISH - Look for Episodes to Add to Trakt Watchlist" ) def addShowToTraktWatchList(self): if sickbeard.TRAKT_SYNC_WATCHLIST and sickbeard.USE_TRAKT: logging.debug( "SHOW_WATCHLIST::ADD::START - Look for Shows to Add to Trakt Watchlist" ) if sickbeard.showList is not None: trakt_data = [] for show in sickbeard.showList: trakt_id = sickbeard.indexerApi( show.indexer).config[b'trakt_id'] if not self._checkInList( trakt_id, str( show.indexerid), '0', '0', List='Show'): logging.debug( "Adding Show: Indexer %s %s - %s to Watchlist" % (trakt_id, str(show.indexerid), show.name)) show_el = { 'title': show.name, 'year': show.startyear, 'ids': {} } if trakt_id == 'tvdb_id': show_el[b'ids'][b'tvdb'] = show.indexerid else: show_el[b'ids'][b'tvrage'] = show.indexerid trakt_data.append(show_el) if len(trakt_data): try: data = {'shows': trakt_data} self.trakt_api.traktRequest("sync/watchlist", data, method='POST') self._getShowWatchlist() except traktException as e: logging.warning( "Could not connect to Trakt service. Error: %s" % ex(e)) logging.debug( "SHOW_WATCHLIST::ADD::FINISH - Look for Shows to Add to Trakt Watchlist" ) def removeShowFromSickRage(self): if sickbeard.TRAKT_SYNC_WATCHLIST and sickbeard.USE_TRAKT and sickbeard.TRAKT_REMOVE_SHOW_FROM_SICKRAGE: logging.debug( "SHOW_SICKRAGE::REMOVE::START - Look for Shows to remove from SiCKRAGE" ) if sickbeard.showList: for show in sickbeard.showList: if show.status == "Ended": try: progress = self.trakt_api.traktRequest( "shows/" + show.imdbid + "/progress/watched") or [] except traktException as e: logging.warning( "Could not connect to Trakt service. Aborting removing show %s from SiCKRAGE. Error: %s" % (show.name, repr(e))) return if 'aired' in progress and 'completed' in progress and progress[ b'aired'] == progress['completed']: sickbeard.showQueueScheduler.action.removeShow( show, full=True) logging.debug( "Show: %s has been removed from SiCKRAGE" % show.name) logging.debug( "SHOW_SICKRAGE::REMOVE::FINISH - Trakt Show Watchlist") def updateShows(self): logging.debug("SHOW_WATCHLIST::CHECK::START - Trakt Show Watchlist") if not len(self.ShowWatchlist): logging.debug( "No shows found in your watchlist, aborting watchlist update") return indexer = int(sickbeard.TRAKT_DEFAULT_INDEXER) trakt_id = sickbeard.indexerApi(indexer).config[b'trakt_id'] for show_el in self.ShowWatchlist[trakt_id]: indexer_id = int(str(show_el)) show = self.ShowWatchlist[trakt_id][show_el] # logging.debug(u"Checking Show: %s %s %s" % (trakt_id, indexer_id, show[b'title'])) if int(sickbeard.TRAKT_METHOD_ADD) != 2: self.addDefaultShow(indexer, indexer_id, show[b'title'], SKIPPED) else: self.addDefaultShow(indexer, indexer_id, show[b'title'], WANTED) if int(sickbeard.TRAKT_METHOD_ADD) == 1: newShow = helpers.findCertainShow(sickbeard.showList, indexer_id) if newShow is not None: setEpisodeToWanted(newShow, 1, 1) else: self.todoWanted.append((indexer_id, 1, 1)) logging.debug("SHOW_WATCHLIST::CHECK::FINISH - Trakt Show Watchlist") def updateEpisodes(self): """ Sets episodes to wanted that are in trakt watchlist """ logging.debug("SHOW_WATCHLIST::CHECK::START - Trakt Episode Watchlist") if not len(self.EpisodeWatchlist): logging.debug( "No episode found in your watchlist, aborting episode update") return managed_show = [] indexer = int(sickbeard.TRAKT_DEFAULT_INDEXER) trakt_id = sickbeard.indexerApi(indexer).config[b'trakt_id'] for show_el in self.EpisodeWatchlist[trakt_id]: indexer_id = int(show_el) show = self.EpisodeWatchlist[trakt_id][show_el] newShow = helpers.findCertainShow(sickbeard.showList, indexer_id) try: if newShow is None: if indexer_id not in managed_show: self.addDefaultShow(indexer, indexer_id, show[b'title'], SKIPPED) managed_show.append(indexer_id) for season_el in show[b'seasons']: season = int(season_el) for episode_el in show[b'seasons'][season_el][ b'episodes']: self.todoWanted.append( (indexer_id, season, int(episode_el))) else: if newShow.indexer == indexer: for season_el in show[b'seasons']: season = int(season_el) for episode_el in show[b'seasons'][season_el][ b'episodes']: setEpisodeToWanted(newShow, season, int(episode_el)) except TypeError: logging.debug("Could not parse the output from trakt for %s " % show[b"title"]) logging.debug( "SHOW_WATCHLIST::CHECK::FINISH - Trakt Episode Watchlist") @staticmethod def addDefaultShow(indexer, indexer_id, name, status): """ Adds a new show with the default settings """ if not helpers.findCertainShow(sickbeard.showList, int(indexer_id)): logging.info("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, helpers.sanitizeFileName(name)) dir_exists = helpers.makeDir(showPath) if not dir_exists: logging.warning( "Unable to create the folder %s , can't add the show" % showPath) 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, archive=sickbeard.ARCHIVE_DEFAULT) else: logging.warning( "There was an error creating the show, no root directory setting found" ) return def manageNewShow(self, show): logging.debug( "Checking if trakt watch list wants to search for episodes from new show " + show.name) episodes = [i for i in self.todoWanted if i[0] == show.indexerid] for episode in episodes: self.todoWanted.remove(episode) setEpisodeToWanted(show, episode[1], episode[2]) def _checkInList(self, trakt_id, showid, season, episode, List=None): """ Check in the Watchlist or CollectionList for Show Is the Show, Season and Episode in the trakt_id list (tvdb / tvrage) """ # logging.debug(u"Checking Show: %s %s %s " % (trakt_id, showid, List)) if "Collection" == List: try: if self.Collectionlist[trakt_id][showid][b'seasons'][season][ b'episodes'][episode] == episode: return True except Exception: return False elif "Show" == List: try: if self.ShowWatchlist[trakt_id][showid][b'id'] == showid: return True except Exception: return False else: try: if self.EpisodeWatchlist[trakt_id][showid][b'seasons'][season][ b'episodes'][episode] == episode: return True except Exception: return False def _getShowWatchlist(self): """ Get Watchlist and parse once into addressable structure """ try: self.ShowWatchlist = {'tvdb_id': {}, 'tvrage_id': {}} TraktShowWatchlist = self.trakt_api.traktRequest( "sync/watchlist/shows") tvdb_id = 'tvdb' tvrage_id = 'tvrage' for watchlist_el in TraktShowWatchlist: tvdb = False tvrage = False if not watchlist_el[b'show'][b'ids'][b"tvdb"] is None: tvdb = True if not watchlist_el[b'show'][b'ids'][b"tvrage"] is None: tvrage = True title = watchlist_el[b'show'][b'title'] year = str(watchlist_el[b'show'][b'year']) if tvdb: showid = str(watchlist_el[b'show'][b'ids'][tvdb_id]) self.ShowWatchlist[tvdb_id + '_id'][showid] = { 'id': showid, 'title': title, 'year': year } if tvrage: showid = str(watchlist_el[b'show'][b'ids'][tvrage_id]) self.ShowWatchlist[tvrage_id + '_id'][showid] = { 'id': showid, 'title': title, 'year': year } except traktException as e: logging.warning( "Could not connect to trakt service, cannot download Show Watchlist: %s" % repr(e)) return False return True def _getEpisodeWatchlist(self): """ Get Watchlist and parse once into addressable structure """ try: self.EpisodeWatchlist = {'tvdb_id': {}, 'tvrage_id': {}} TraktEpisodeWatchlist = self.trakt_api.traktRequest( "sync/watchlist/episodes") tvdb_id = 'tvdb' tvrage_id = 'tvrage' for watchlist_el in TraktEpisodeWatchlist: tvdb = False tvrage = False if not watchlist_el[b'show'][b'ids'][b"tvdb"] is None: tvdb = True if not watchlist_el[b'show'][b'ids'][b"tvrage"] is None: tvrage = True title = watchlist_el[b'show'][b'title'] year = str(watchlist_el[b'show'][b'year']) season = str(watchlist_el[b'episode'][b'season']) episode = str(watchlist_el[b'episode'][b'number']) if tvdb: showid = str(watchlist_el[b'show'][b'ids'][tvdb_id]) if showid not in self.EpisodeWatchlist[tvdb_id + '_id'].keys(): self.EpisodeWatchlist[tvdb_id + '_id'][showid] = { 'id': showid, 'title': title, 'year': year, 'seasons': {} } if season not in self.EpisodeWatchlist[ tvdb_id + '_id'][showid][b'seasons'].keys(): self.EpisodeWatchlist[ tvdb_id + '_id'][showid][b'seasons'][season] = { 's': season, 'episodes': {} } if episode not in self.EpisodeWatchlist[tvdb_id + '_id'][ showid][b'seasons'][season]['episodes'].keys(): self.EpisodeWatchlist[tvdb_id + '_id'][showid][ b'seasons'][season][b'episodes'][episode] = episode if tvrage: showid = str(watchlist_el[b'show'][b'ids'][tvrage_id]) if showid not in self.EpisodeWatchlist[tvrage_id + '_id'].keys(): self.EpisodeWatchlist[tvrage_id + '_id'][showid] = { 'id': showid, 'title': title, 'year': year, 'seasons': {} } if season not in self.EpisodeWatchlist[ tvrage_id + '_id'][showid][b'seasons'].keys(): self.EpisodeWatchlist[ tvrage_id + '_id'][showid][b'seasons'][season] = { 's': season, 'episodes': {} } if episode not in self.EpisodeWatchlist[tvrage_id + '_id'][ showid][b'seasons'][season]['episodes'].keys(): self.EpisodeWatchlist[tvrage_id + '_id'][showid][ b'seasons'][season][b'episodes'][episode] = episode except traktException as e: logging.warning( "Could not connect to trakt service, cannot download Episode Watchlist: %s" % repr(e)) return False return True def _getShowCollection(self): """ Get Collection and parse once into addressable structure """ try: self.Collectionlist = {'tvdb_id': {}, 'tvrage_id': {}} logging.debug("Getting Show Collection") TraktCollectionList = self.trakt_api.traktRequest( "sync/collection/shows") tvdb_id = 'tvdb' tvrage_id = 'tvrage' for watchlist_el in TraktCollectionList: tvdb = False tvrage = False if not watchlist_el[b'show'][b'ids'][b"tvdb"] is None: tvdb = True if not watchlist_el[b'show'][b'ids'][b"tvrage"] is None: tvrage = True title = watchlist_el[b'show'][b'title'] year = str(watchlist_el[b'show'][b'year']) if 'seasons' in watchlist_el: for season_el in watchlist_el[b'seasons']: for episode_el in season_el[b'episodes']: season = str(season_el[b'number']) episode = str(episode_el[b'number']) if tvdb: showid = str( watchlist_el[b'show'][b'ids'][tvdb_id]) if showid not in self.Collectionlist[ tvdb_id + '_id'].keys(): self.Collectionlist[tvdb_id + '_id'][showid] = { 'id': showid, 'title': title, 'year': year, 'seasons': {} } if season not in self.Collectionlist[ tvdb_id + '_id'][showid][b'seasons'].keys(): self.Collectionlist[ tvdb_id + '_id'][showid][b'seasons'][season] = { 's': season, 'episodes': {} } if episode not in self.Collectionlist[ tvdb_id + '_id'][showid][b'seasons'][ season]['episodes'].keys(): self.Collectionlist[ tvdb_id + '_id'][showid][b'seasons'][season][ b'episodes'][episode] = episode if tvrage: showid = str( watchlist_el[b'show'][b'ids'][tvrage_id]) if showid not in self.Collectionlist[ tvrage_id + '_id'].keys(): self.Collectionlist[tvrage_id + '_id'][showid] = { 'id': showid, 'title': title, 'year': year, 'seasons': {} } if season not in self.Collectionlist[ tvrage_id + '_id'][showid][b'seasons'].keys(): self.Collectionlist[ tvrage_id + '_id'][showid][b'seasons'][season] = { 's': season, 'episodes': {} } if episode not in self.Collectionlist[ tvrage_id + '_id'][showid][b'seasons'][ season]['episodes'].keys(): self.Collectionlist[ tvrage_id + '_id'][showid][b'seasons'][season][ b'episodes'][episode] = episode except traktException as e: logging.warning( "Could not connect to trakt service, cannot download Show Collection: %s" % repr(e)) return False return True @staticmethod def trakt_bulk_data_generate(data): """ Build the JSON structure to send back to Trakt """ uniqueShows = {} uniqueSeasons = {} for showid, indexerid, show_name, startyear, season, episode in data: if showid not in uniqueShows: uniqueShows[showid] = { 'title': show_name, 'year': startyear, 'ids': {}, 'seasons': [] } trakt_id = sickbeard.indexerApi(indexerid).config[b'trakt_id'] if trakt_id == 'tvdb_id': uniqueShows[showid][b'ids'][b"tvdb"] = showid else: uniqueShows[showid][b'ids'][b"tvrage"] = showid uniqueSeasons[showid] = [] # Get the unique seasons per Show for showid, indexerid, show_name, startyear, season, episode in data: if season not in uniqueSeasons[showid]: uniqueSeasons[showid].append(season) # build the query showList = [] seasonsList = {} for searchedShow in uniqueShows: seasonsList[searchedShow] = [] for searchedSeason in uniqueSeasons[searchedShow]: episodesList = [] for showid, indexerid, show_name, startyear, season, episode in data: if season == searchedSeason and showid == searchedShow: episodesList.append({'number': episode}) show = uniqueShows[searchedShow] show[b'seasons'].append({ 'number': searchedSeason, 'episodes': episodesList }) showList.append(show) post_data = {'shows': showList} return post_data
def run(self): ShowQueueItem.run(self) logging.info("Starting to add show {}".format(self.showDir)) # make sure the Indexer IDs are valid try: lINDEXER_API_PARMS = sickbeard.indexerApi( self.indexer).api_params.copy() if self.lang: lINDEXER_API_PARMS[b'language'] = self.lang logging.info("" + str(sickbeard.indexerApi(self.indexer).name) + ": " + repr(lINDEXER_API_PARMS)) t = sickbeard.indexerApi( self.indexer).indexer(**lINDEXER_API_PARMS) s = t[self.indexer_id] # 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: logging.error( "Show in " + self.showDir + " has no name on " + str(sickbeard.indexerApi(self.indexer).name) + ", probably the wrong language used to search with.") 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: logging.error("Show " + str(s[b'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[b'seriesname']) + " is on " + str(sickbeard.indexerApi(self.indexer).name) + " but contains no season/episode data.") self._finishEarly() return except Exception as e: logging.error( "%s Error while loading information from indexer %s. Error: %r" % (self.indexer_id, sickbeard.indexerApi( self.indexer).name, ex(e))) ui.notifications.error( "Unable to add show", "Unable to look up the show in %s on %s using ID %s, not using the NFO. Delete .nfo and try adding manually again." % (self.showDir, sickbeard.indexerApi( self.indexer).name, self.indexer_id)) 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._finishEarly() return try: newShow = TVShow(self.indexer, self.indexer_id, self.lang) newShow.loadFromIndexer() self.show = newShow # set up initial values self.show.location = self.showDir self.show.subtitles = self.subtitles if self.subtitles != 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 != None else sickbeard.FLATTEN_FOLDERS_DEFAULT self.show.anime = self.anime if self.anime != None else sickbeard.ANIME_DEFAULT self.show.scene = self.scene if self.scene != None else sickbeard.SCENE_DEFAULT self.show.archive_firstmatch = self.archive if self.archive != None else sickbeard.ARCHIVE_DEFAULT self.show.paused = self.paused if self.paused != None else False # set up default new/missing episode status logging.info( "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: logging.error("Unable to add show due to an error with " + sickbeard.indexerApi(self.indexer).name + ": {}".format(ex(e))) 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: logging.warning("The show in " + self.showDir + " is already in your show list, skipping") ui.notifications.error( 'Show skipped', "The show in " + self.showDir + " is already in your show list") self._finishEarly() return except Exception as e: logging.error("Error trying to add show: {}".format(ex(e))) logging.debug(traceback.format_exc()) self._finishEarly() raise logging.debug("Retrieving show info from IMDb") try: self.show.loadIMDbInfo() except imdb_exceptions.IMDbError as e: logging.warning(" Something wrong on IMDb api: {}".format(ex(e))) except Exception as e: logging.error("Error loading IMDb info: {}".format(ex(e))) try: self.show.saveToDB() except Exception as e: logging.error("Error saving the show to the database: {}".format( ex(e))) logging.debug(traceback.format_exc()) self._finishEarly() raise # add it to the show list sickbeard.showList.append(self.show) try: self.show.loadEpisodesFromIndexer() except Exception as e: logging.error("Error with " + sickbeard.indexerApi(self.show.indexer).name + ", not creating episode list: {}".format(ex(e))) logging.debug(traceback.format_exc()) # update internal name cache name_cache.buildNameCache() try: self.show.loadEpisodesFromDir() except Exception as e: logging.error("Error searching dir for episodes: {}".format(ex(e))) logging.debug(traceback.format_exc()) # 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: logging.info( "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: logging.info("update watchlist") notifiers.trakt_notifier.update_watchlist(show_obj=self.show) # Load XEM data to DB for show scene_numbering.xem_refresh(self.show.indexerid, self.show.indexer, force=True) # check if show has XEM mapping so we can determin if searches should go by scene numbering or indexer numbering. if not self.scene and scene_numbering.get_xem_numbering_for_show( self.show.indexerid, self.show.indexer): self.show.scene = 1 # After initial add, set to default_status_after. self.show.default_ep_status = self.default_status_after self.finish()
def update_watchlist(self, show_obj=None, s=None, e=None, data_show=None, data_episode=None, update="add"): """ Sends a request to trakt indicating that the given episode is part of our library. show_obj: The TVShow object to add to trakt s: season number e: episode number data_show: structured object of shows traktv type data_episode: structured object of episodes traktv type update: type o action add or remove """ trakt_api = TraktAPI(sickbeard.SSL_VERIFY, sickbeard.TRAKT_TIMEOUT) if sickbeard.USE_TRAKT: data = {} try: # URL parameters if show_obj is not None: trakt_id = sickbeard.indexerApi(show_obj.indexer).config[b'trakt_id'] data = { 'shows': [ { 'title': show_obj.name, 'year': show_obj.startyear, 'ids': {}, } ] } if trakt_id == 'tvdb_id': data[b'shows'][0][b'ids'][b'tvdb'] = show_obj.indexerid else: data[b'shows'][0][b'ids'][b'tvrage'] = show_obj.indexerid elif data_show is not None: data.update(data_show) else: logging.warning( "there's a coding problem contact developer. It's needed to be provided at lest one of the two: data_show or show_obj") return False if data_episode is not None: data[b'shows'][0].update(data_episode) elif s is not None: # traktv URL parameters season = { 'season': [ { 'number': s, } ] } if e is not None: # traktv URL parameters episode = { 'episodes': [ { 'number': e } ] } season[b'season'][0].update(episode) data[b'shows'][0].update(season) trakt_url = "sync/watchlist" if update == "remove": trakt_url += "/remove" trakt_api.traktRequest(trakt_url, data, method='POST') except (traktException, traktAuthException, traktServerBusy) as e: logging.warning("Could not connect to Trakt service: %s" % ex(e)) return False return True