def updateShows(self): sickrage.LOGGER.debug( "SHOW_WATCHLIST::CHECK::START - Trakt Show Watchlist") if not len(self.ShowWatchlist): sickrage.LOGGER.debug( "No shows found in your watchlist, aborting watchlist update") return indexer = int(sickrage.TRAKT_DEFAULT_INDEXER) trakt_id = sickrage.INDEXER_API(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] # sickrage.LOGGER.debug(u"Checking Show: %s %s %s" % (trakt_id, indexer_id, show[b'title'])) if int(sickrage.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(sickrage.TRAKT_METHOD_ADD) == 1: newShow = findCertainShow(sickrage.showList, indexer_id) if newShow is not None: setEpisodeToWanted(newShow, 1, 1) else: self.todoWanted.append((indexer_id, 1, 1)) sickrage.LOGGER.debug( "SHOW_WATCHLIST::CHECK::FINISH - Trakt Show Watchlist")
def removeShowFromTraktLibrary(self, show_obj): if self.findShow(show_obj.indexer, show_obj.indexerid): trakt_id = sickrage.INDEXER_API( 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 sickrage.LOGGER.debug("Removing %s from tv library" % show_obj.name) try: self.trakt_api.traktRequest("sync/collection/remove", data, method='POST') except traktException as e: sickrage.LOGGER.warning( "Could not connect to Trakt service. Aborting removing show %s from Trakt library. Error: %s" % (show_obj.name, repr(e)))
def updateEpisodes(self): """ Sets episodes to wanted that are in trakt watchlist """ sickrage.LOGGER.debug( "SHOW_WATCHLIST::CHECK::START - Trakt Episode Watchlist") if not len(self.EpisodeWatchlist): sickrage.LOGGER.debug( "No episode found in your watchlist, aborting episode update") return managed_show = [] indexer = int(sickrage.TRAKT_DEFAULT_INDEXER) trakt_id = sickrage.INDEXER_API(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 = findCertainShow(sickrage.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: sickrage.LOGGER.debug( "Could not parse the output from trakt for %s " % show[b"title"]) sickrage.LOGGER.debug( "SHOW_WATCHLIST::CHECK::FINISH - Trakt Episode Watchlist")
def _xem_exceptions_fetcher(): if shouldRefresh('xem'): for indexer in sickrage.INDEXER_API().indexers: sickrage.LOGGER.info( "Checking for XEM scene exception updates for " + sickrage.INDEXER_API(indexer).name) url = "http://thexem.de/map/allNames?origin=%s&seasonNumbers=1" % sickrage.INDEXER_API( indexer).config['xem_origin'] parsedJSON = getURL(url, session=xem_session, timeout=90, json=True) if not parsedJSON: sickrage.LOGGER.debug( "Check scene exceptions update failed for " + sickrage.INDEXER_API(indexer).name + ", Unable to get URL: " + url) continue if parsedJSON[b'result'] == 'failure': continue for indexerid, names in parsedJSON[b'data'].iteritems(): try: xem_exception_dict[int(indexerid)] = names except Exception as e: sickrage.LOGGER.warning( "XEM: Rejected entry: indexerid:{0}; names:{1}".format( indexerid, names)) sickrage.LOGGER.debug( "XEM: Rejected entry error message:{0}".format(str(e))) setLastRefresh('xem') return xem_exception_dict
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 = sickrage.INDEXER_API(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 traktShowList = [] 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 }) traktShowList.append(show) post_data = {'shows': traktShowList} return post_data
def removeEpisodeFromTraktWatchList(self): if sickrage.TRAKT_SYNC_WATCHLIST and sickrage.USE_TRAKT: sickrage.LOGGER.debug( "WATCHLIST::REMOVE::START - Look for Episodes to Remove from Trakt Watchlist" ) 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 = main_db.MainDB().select(sql_selection) if episodes is not None: trakt_data = [] for cur_episode in episodes: trakt_id = sickrage.INDEXER_API( 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"]) ) and cur_episode[ b"status"] not in Quality.SNATCHED + Quality.SNATCHED_PROPER + [ UNKNOWN ] + [WANTED]: sickrage.LOGGER.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: sickrage.LOGGER.warning( "Could not connect to Trakt service. Error: %s" % e) sickrage.LOGGER.debug( "WATCHLIST::REMOVE::FINISH - Look for Episodes to Remove from Trakt Watchlist" )
def removeEpisodeFromTraktCollection(self): if sickrage.TRAKT_SYNC_REMOVE and sickrage.TRAKT_SYNC and sickrage.USE_TRAKT: sickrage.LOGGER.debug( "COLLECTION::REMOVE::START - Look for Episodes to Remove From Trakt Collection" ) 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 = main_db.MainDB().select(sql_selection) if episodes is not None: trakt_data = [] for cur_episode in episodes: trakt_id = sickrage.INDEXER_API( 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"] == '': sickrage.LOGGER.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: sickrage.LOGGER.warning( "Could not connect to Trakt service. Error: %s" % e) sickrage.LOGGER.debug( "COLLECTION::REMOVE::FINISH - Look for Episodes to Remove From Trakt Collection" )
def addEpisodeToTraktWatchList(self): if sickrage.TRAKT_SYNC_WATCHLIST and sickrage.USE_TRAKT: sickrage.LOGGER.debug( "WATCHLIST::ADD::START - Look for Episodes to Add to Trakt Watchlist" ) 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 = main_db.MainDB().select(sql_selection) if episodes is not None: trakt_data = [] for cur_episode in episodes: trakt_id = sickrage.INDEXER_API( 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"])): sickrage.LOGGER.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: sickrage.LOGGER.warning( "Could not connect to Trakt service. Error %s" % e) sickrage.LOGGER.debug( "WATCHLIST::ADD::FINISH - Look for Episodes to Add to Trakt Watchlist" )
def addShowToTraktWatchList(self): if sickrage.TRAKT_SYNC_WATCHLIST and sickrage.USE_TRAKT: sickrage.LOGGER.debug( "SHOW_WATCHLIST::ADD::START - Look for Shows to Add to Trakt Watchlist" ) if sickrage.showList is not None: trakt_data = [] for show in sickrage.showList: trakt_id = sickrage.INDEXER_API( show.indexer).config[b'trakt_id'] if not self._checkInList( trakt_id, str( show.indexerid), '0', '0', List='Show'): sickrage.LOGGER.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: sickrage.LOGGER.warning( "Could not connect to Trakt service. Error: %s" % e) sickrage.LOGGER.debug( "SHOW_WATCHLIST::ADD::FINISH - Look for Shows to Add to Trakt Watchlist" )
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 = sickrage.INDEXER_API( 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): sickrage.LOGGER.debug("Adding %s to tv library" % show_obj.name) try: self.trakt_api.traktRequest("sync/collection", data, method='POST') except traktException as e: sickrage.LOGGER.warning( "Could not connect to Trakt service. Aborting adding show %s to Trakt library. Error: %s" % (show_obj.name, repr(e))) return
def _ep_data(self, ep_obj): """ Creates an elementTree XML structure for a MediaBrowser style episode.xml and returns the resulting data object. show_obj: a TVShow instance to create the NFO for """ eps_to_write = [ep_obj] + ep_obj.relatedEps indexer_lang = ep_obj.show.lang try: # There's gotta be a better way of doing this but we don't wanna # change the language value elsewhere lINDEXER_API_PARMS = sickrage.INDEXER_API( ep_obj.show.indexer).api_params.copy() if indexer_lang and not indexer_lang == sickrage.INDEXER_DEFAULT_LANGUAGE: lINDEXER_API_PARMS[b'language'] = indexer_lang if ep_obj.show.dvdorder != 0: lINDEXER_API_PARMS[b'dvdorder'] = True t = sickrage.INDEXER_API( ep_obj.show.indexer).indexer(**lINDEXER_API_PARMS) myShow = t[ep_obj.show.indexerid] except indexer_shownotfound as e: raise ShowNotFoundException(e.message) except indexer_error as e: sickrage.LOGGER.error( "Unable to connect to TVDB while creating meta files - skipping - {}" .format(e)) return False rootNode = Element("details") movie = SubElement(rootNode, "movie") movie.attrib[b"isExtra"] = "false" movie.attrib[b"isSet"] = "false" movie.attrib[b"isTV"] = "true" # write an MediaBrowser XML containing info for all matching episodes for curEpToWrite in eps_to_write: try: myEp = myShow[curEpToWrite.season][curEpToWrite.episode] except (indexer_episodenotfound, indexer_seasonnotfound): sickrage.LOGGER.info( "Unable to find episode %dx%d on %s... has it been removed? Should I delete from db?" % (curEpToWrite.season, curEpToWrite.episode, sickrage.INDEXER_API(ep_obj.show.indexer).name)) return None if curEpToWrite == ep_obj: # root (or single) episode # default to today's date for specials if firstaired is not set if curEpToWrite.season == 0 and not getattr( myEp, 'firstaired', None): myEp[b'firstaired'] = str(datetime.date.fromordinal(1)) if not (getattr(myEp, 'episodename', None) and getattr(myEp, 'firstaired', None)): return None episode = movie if curEpToWrite.name: EpisodeName = SubElement(episode, "title") EpisodeName.text = curEpToWrite.name SeasonNumber = SubElement(episode, "season") SeasonNumber.text = str(curEpToWrite.season) EpisodeNumber = SubElement(episode, "episode") EpisodeNumber.text = str(curEpToWrite.episode) if getattr(myShow, "firstaired", None): try: year_text = str( datetime.datetime.strptime(myShow[b"firstaired"], dateFormat).year) if year_text: year = SubElement(episode, "year") year.text = year_text except: pass if getattr(myShow, "overview", None): plot = SubElement(episode, "plot") plot.text = myShow[b"overview"] if curEpToWrite.description: Overview = SubElement(episode, "episodeplot") Overview.text = curEpToWrite.description if getattr(myShow, 'contentrating', None): mpaa = SubElement(episode, "mpaa") mpaa.text = myShow[b"contentrating"] if not ep_obj.relatedEps and getattr(myEp, "rating", None): try: rating = int((float(myEp[b'rating']) * 10)) except ValueError: rating = 0 if rating: Rating = SubElement(episode, "rating") Rating.text = str(rating) if getattr(myEp, 'director', None): director = SubElement(episode, "director") director.text = myEp[b'director'] if getattr(myEp, 'writer', None): writer = SubElement(episode, "credits") writer.text = myEp[b'writer'] if getattr(myShow, '_actors', None) or getattr( myEp, 'gueststars', None): cast = SubElement(episode, "cast") if getattr(myEp, 'gueststars', None) and isinstance( myEp[b'gueststars'], basestring): for actor in (x.strip() for x in myEp[b'gueststars'].split('|') if x.strip()): cur_actor = SubElement(cast, "actor") cur_actor.text = actor if getattr(myShow, '_actors', None): for actor in myShow[b'_actors']: if 'name' in actor and actor[b'name'].strip(): cur_actor = SubElement(cast, "actor") cur_actor.text = actor[b'name'].strip() else: # append data from (if any) related episodes if curEpToWrite.name: if not EpisodeName.text: EpisodeName.text = curEpToWrite.name else: EpisodeName.text = EpisodeName.text + ", " + curEpToWrite.name if curEpToWrite.description: if not Overview.text: Overview.text = curEpToWrite.description else: Overview.text = Overview.text + "\r" + curEpToWrite.description indentXML(rootNode) data = ElementTree(rootNode) return data
def _ep_data(self, ep_obj): """ Creates an elementTree XML structure for a MediaBrowser style episode.xml and returns the resulting data object. show_obj: a TVShow instance to create the NFO for """ eps_to_write = [ep_obj] + ep_obj.relatedEps persons_dict = { 'Director': [], 'GuestStar': [], 'Writer': [] } indexer_lang = ep_obj.show.lang try: lINDEXER_API_PARMS = sickrage.INDEXER_API(ep_obj.show.indexer).api_params.copy() lINDEXER_API_PARMS[b'actors'] = True if indexer_lang and not indexer_lang == sickrage.INDEXER_DEFAULT_LANGUAGE: lINDEXER_API_PARMS[b'language'] = indexer_lang if ep_obj.show.dvdorder != 0: lINDEXER_API_PARMS[b'dvdorder'] = True t = sickrage.INDEXER_API(ep_obj.show.indexer).indexer(**lINDEXER_API_PARMS) myShow = t[ep_obj.show.indexerid] except indexer_shownotfound as e: raise ShowNotFoundException(e.message) except indexer_error as e: sickrage.LOGGER.error("Unable to connect to " + sickrage.INDEXER_API( ep_obj.show.indexer).name + " while creating meta files - skipping - {}".format(e)) return False rootNode = Element("Item") # write an MediaBrowser XML containing info for all matching episodes for curEpToWrite in eps_to_write: try: myEp = myShow[curEpToWrite.season][curEpToWrite.episode] except (indexer_episodenotfound, indexer_seasonnotfound): sickrage.LOGGER.info("Unable to find episode %dx%d on %s... has it been removed? Should I delete from db?" % (curEpToWrite.season, curEpToWrite.episode, sickrage.INDEXER_API(ep_obj.show.indexer).name)) return None if curEpToWrite == ep_obj: # root (or single) episode # default to today's date for specials if firstaired is not set if ep_obj.season == 0 and not getattr(myEp, 'firstaired', None): myEp[b'firstaired'] = str(datetime.date.fromordinal(1)) if not (getattr(myEp, 'episodename', None) and getattr(myEp, 'firstaired', None)): return None episode = rootNode if curEpToWrite.name: EpisodeName = SubElement(episode, "EpisodeName") EpisodeName.text = curEpToWrite.name EpisodeNumber = SubElement(episode, "EpisodeNumber") EpisodeNumber.text = str(ep_obj.episode) if ep_obj.relatedEps: EpisodeNumberEnd = SubElement(episode, "EpisodeNumberEnd") EpisodeNumberEnd.text = str(curEpToWrite.episode) SeasonNumber = SubElement(episode, "SeasonNumber") SeasonNumber.text = str(curEpToWrite.season) if not ep_obj.relatedEps and getattr(myEp, 'absolute_number', None): absolute_number = SubElement(episode, "absolute_number") absolute_number.text = str(myEp[b'absolute_number']) if curEpToWrite.airdate != datetime.date.fromordinal(1): FirstAired = SubElement(episode, "FirstAired") FirstAired.text = str(curEpToWrite.airdate) MetadataType = SubElement(episode, "Type") MetadataType.text = "Episode" if curEpToWrite.description: Overview = SubElement(episode, "Overview") Overview.text = curEpToWrite.description if not ep_obj.relatedEps: if getattr(myEp, 'rating', None): Rating = SubElement(episode, "Rating") Rating.text = myEp[b'rating'] if getattr(myShow, 'imdb_id', None): IMDB_ID = SubElement(episode, "IMDB_ID") IMDB_ID.text = myShow[b'imdb_id'] IMDB = SubElement(episode, "IMDB") IMDB.text = myShow[b'imdb_id'] IMDbId = SubElement(episode, "IMDbId") IMDbId.text = myShow[b'imdb_id'] indexerid = SubElement(episode, "id") indexerid.text = str(curEpToWrite.indexerid) # fill in Persons section with collected directors, guest starts and writers Persons = SubElement(episode, "Persons") for person_type, names in persons_dict.iteritems(): # remove doubles names = list(set(names)) for cur_name in names: Person = SubElement(Persons, "Person") cur_person_name = SubElement(Person, "Name") cur_person_name.text = cur_name cur_person_type = SubElement(Person, "Type") cur_person_type.text = person_type if getattr(myShow, '_actors', None): for actor in myShow[b'_actors']: if not ('name' in actor and actor[b'name'].strip()): continue cur_actor = SubElement(Persons, "Person") cur_actor_name = SubElement(cur_actor, "Name") cur_actor_name.text = actor[b'name'].strip() cur_actor_type = SubElement(cur_actor, "Type") cur_actor_type.text = "Actor" if 'role' in actor and actor[b'role'].strip(): cur_actor_role = SubElement(cur_actor, "Role") cur_actor_role.text = actor[b'role'].strip() Language = SubElement(episode, "Language") try: Language.text = myEp[b'language'] except Exception: Language.text = sickrage.INDEXER_DEFAULT_LANGUAGE # tvrage api doesn't provide language so we must assume a value here thumb = SubElement(episode, "filename") # TODO: See what this is needed for.. if its still needed # just write this to the NFO regardless of whether it actually exists or not # note: renaming files after nfo generation will break this, tough luck thumb_text = self.get_episode_thumb_path(ep_obj) if thumb_text: thumb.text = thumb_text else: # append data from (if any) related episodes if curEpToWrite.episode: if not EpisodeNumberEnd.text: EpisodeNumberEnd.text = curEpToWrite.episode else: EpisodeNumberEnd.text = EpisodeNumberEnd.text + ", " + curEpToWrite.episode if curEpToWrite.name: if not EpisodeName.text: EpisodeName.text = curEpToWrite.name else: EpisodeName.text = EpisodeName.text + ", " + curEpToWrite.name if curEpToWrite.description: if not Overview.text: Overview.text = curEpToWrite.description else: Overview.text = Overview.text + "\r" + curEpToWrite.description # collect all directors, guest stars and writers if getattr(myEp, 'director', None): persons_dict[b'Director'] += [x.strip() for x in myEp[b'director'].split('|') if x.strip()] if getattr(myEp, 'gueststars', None): persons_dict[b'GuestStar'] += [x.strip() for x in myEp[b'gueststars'].split('|') if x.strip()] if getattr(myEp, 'writer', None): persons_dict[b'Writer'] += [x.strip() for x in myEp[b'writer'].split('|') if x.strip()] indentXML(rootNode) data = ElementTree(rootNode) return data
def __str__(self): return str(self.date) + " " + self.name + " " + str( self.season) + "x" + str(self.episode) + " of " + str( self.indexerid) + " from " + str( sickrage.INDEXER_API(self.indexer).name)
def xem_refresh(indexer_id, indexer, force=False): """ Refresh data from xem for a tv show :param indexer_id: int """ if not indexer_id or indexer_id < 1: return indexer_id = int(indexer_id) indexer = int(indexer) MAX_REFRESH_AGE_SECS = 86400 # 1 day rows = main_db.MainDB().select( "SELECT last_refreshed FROM xem_refresh WHERE indexer = ? AND indexer_id = ?", [indexer, indexer_id]) if rows: lastRefresh = int(rows[0][b'last_refreshed']) refresh = int(time.mktime(datetime.datetime.today().timetuple()) ) > lastRefresh + MAX_REFRESH_AGE_SECS else: refresh = True if refresh or force: sickrage.LOGGER.debug( 'Looking up XEM scene mapping for show %s on %s' % (indexer_id, sickrage.INDEXER_API(indexer).name)) # mark refreshed main_db.MainDB().upsert( "xem_refresh", { 'indexer': indexer, 'last_refreshed': int(time.mktime(datetime.datetime.today().timetuple())) }, {'indexer_id': indexer_id}) try: from scene_exceptions import xem_session # XEM MAP URL url = "http://thexem.de/map/havemap?origin=%s" % sickrage.INDEXER_API( indexer).config[b'xem_origin'] parsedJSON = getURL(url, session=xem_session, json=True) if not parsedJSON or 'result' not in parsedJSON or 'success' not in parsedJSON[ b'result'] or 'data' not in parsedJSON or str( indexer_id) not in parsedJSON[b'data']: return # XEM API URL url = "http://thexem.de/map/all?id={}&origin={}&destination=scene".format( indexer_id, sickrage.INDEXER_API(indexer).config[b'xem_origin']) parsedJSON = getURL(url, session=xem_session, json=True) if not ((parsedJSON and 'result' in parsedJSON) and 'success' in parsedJSON[b'result']): sickrage.LOGGER.info('No XEM data for show "%s on %s"' % ( indexer_id, sickrage.INDEXER_API(indexer).name, )) return cl = [] for entry in parsedJSON[b'data']: if 'scene' in entry: cl.append([ "UPDATE tv_episodes SET scene_season = ?, scene_episode = ?, scene_absolute_number = ? WHERE showid = ? AND season = ? AND episode = ?", [ entry[b'scene'][b'season'], entry[b'scene'][b'episode'], entry[b'scene'][b'absolute'], indexer_id, entry[sickrage.INDEXER_API( indexer).config[b'xem_origin']][b'season'], entry[sickrage.INDEXER_API( indexer).config[b'xem_origin']][b'episode'] ] ]) if 'scene_2' in entry: # for doubles cl.append([ "UPDATE tv_episodes SET scene_season = ?, scene_episode = ?, scene_absolute_number = ? WHERE showid = ? AND season = ? AND episode = ?", [ entry[b'scene_2'][b'season'], entry[b'scene_2'][b'episode'], entry[b'scene_2'][b'absolute'], indexer_id, entry[sickrage.INDEXER_API( indexer).config[b'xem_origin']][b'season'], entry[sickrage.INDEXER_API( indexer).config[b'xem_origin']][b'episode'] ] ]) if len(cl) > 0: main_db.MainDB().mass_action(cl) except Exception as e: sickrage.LOGGER.warning( "Exception while refreshing XEM data for show " + str(indexer_id) + " on " + sickrage.INDEXER_API(indexer).name + ": {}".format(e)) sickrage.LOGGER.debug(traceback.format_exc())
def _parse_string(self, name): if not name: return matches = [] bestResult = None for (cur_regex_num, cur_regex_name, cur_regex) in self.compiled_regexes: match = cur_regex.match(name) if not match: continue result = ParseResult(name) result.which_regex = [cur_regex_name] result.score = 0 - cur_regex_num named_groups = match.groupdict().keys() if 'series_name' in named_groups: result.series_name = match.group('series_name') if result.series_name: result.series_name = self.clean_series_name(result.series_name) result.score += 1 if 'series_num' in named_groups and match.group('series_num'): result.score += 1 if 'season_num' in named_groups: tmp_season = int(match.group('season_num')) if cur_regex_name == 'bare' and tmp_season in (19, 20): continue result.season_number = tmp_season result.score += 1 if 'ep_num' in named_groups: ep_num = self._convert_number(match.group('ep_num')) if 'extra_ep_num' in named_groups and match.group('extra_ep_num'): result.episode_numbers = range(ep_num, self._convert_number(match.group('extra_ep_num')) + 1) result.score += 1 else: result.episode_numbers = [ep_num] result.score += 1 if 'ep_ab_num' in named_groups: ep_ab_num = self._convert_number(match.group('ep_ab_num')) if 'extra_ab_ep_num' in named_groups and match.group('extra_ab_ep_num'): result.ab_episode_numbers = range(ep_ab_num, self._convert_number(match.group('extra_ab_ep_num')) + 1) result.score += 1 else: result.ab_episode_numbers = [ep_ab_num] result.score += 1 if 'air_date' in named_groups: air_date = match.group('air_date') try: result.air_date = parser.parse(air_date, fuzzy=True).date() result.score += 1 except Exception: continue if 'extra_info' in named_groups: tmp_extra_info = match.group('extra_info') # Show.S04.Special or Show.S05.Part.2.Extras is almost certainly not every episode in the season if tmp_extra_info and cur_regex_name == 'season_only' and re.search( r'([. _-]|^)(special|extra)s?\w*([. _-]|$)', tmp_extra_info, re.I): continue result.extra_info = tmp_extra_info result.score += 1 if 'release_group' in named_groups: result.release_group = match.group('release_group') result.score += 1 if 'version' in named_groups: # assigns version to anime file if detected using anime regex. Non-anime regex receives -1 version = match.group('version') if version: result.version = version else: result.version = 1 else: result.version = -1 matches.append(result) if len(matches): # pick best match with highest score based on placement bestResult = max(sorted(matches, reverse=True, key=lambda x: x.which_regex), key=lambda x: x.score) show = None if not self.naming_pattern: # try and create a show object for this result show = self.get_show(bestResult.series_name, self.tryIndexers) # confirm passed in show object indexer id matches result show object indexer id if show: if self.showObj and show.indexerid != self.showObj.indexerid: show = None bestResult.show = show elif not show and self.showObj: bestResult.show = self.showObj # if this is a naming pattern test or result doesn't have a show object then return best result if not bestResult.show or self.naming_pattern: return bestResult # get quality bestResult.quality = Quality.nameQuality(name, bestResult.show.is_anime) new_episode_numbers = [] new_season_numbers = [] new_absolute_numbers = [] # if we have an air-by-date show then get the real season/episode numbers if bestResult.is_air_by_date: from sickrage.core.databases import main_db airdate = bestResult.air_date.toordinal() sql_result = main_db.MainDB().select( "SELECT season, episode FROM tv_episodes WHERE showid = ? AND indexer = ? AND airdate = ?", [bestResult.show.indexerid, bestResult.show.indexer, airdate]) season_number = None episode_numbers = [] if sql_result: season_number = int(sql_result[0][0]) episode_numbers = [int(sql_result[0][1])] if not season_number or not len(episode_numbers): try: lINDEXER_API_PARMS = sickrage.INDEXER_API(bestResult.show.indexer).api_params.copy() if bestResult.show.lang: lINDEXER_API_PARMS[b'language'] = bestResult.show.lang t = sickrage.INDEXER_API(bestResult.show.indexer).indexer(**lINDEXER_API_PARMS) epObj = t[bestResult.show.indexerid].airedOn(bestResult.air_date)[0] season_number = int(epObj[b"seasonnumber"]) episode_numbers = [int(epObj[b"episodenumber"])] except indexer_episodenotfound: sickrage.LOGGER.warning( "Unable to find episode with date " + bestResult.air_date + " for show " + bestResult.show.name + ", skipping") episode_numbers = [] except indexer_error as e: sickrage.LOGGER.warning( "Unable to contact " + sickrage.INDEXER_API(bestResult.show.indexer).name + ": {}".format( e)) episode_numbers = [] for epNo in episode_numbers: s = season_number e = epNo if bestResult.show.is_scene: (s, e) = get_indexer_numbering(bestResult.show.indexerid, bestResult.show.indexer, season_number, epNo) new_episode_numbers.append(e) new_season_numbers.append(s) elif bestResult.show.is_anime and len(bestResult.ab_episode_numbers): scene_season = get_scene_exception_by_name(bestResult.series_name)[1] for epAbsNo in bestResult.ab_episode_numbers: a = epAbsNo if bestResult.show.is_scene: a = get_indexer_absolute_numbering(bestResult.show.indexerid, bestResult.show.indexer, epAbsNo, True, scene_season) (s, e) = get_all_episodes_from_absolute_number(bestResult.show, [a]) new_absolute_numbers.append(a) new_episode_numbers.extend(e) new_season_numbers.append(s) elif bestResult.season_number and len(bestResult.episode_numbers): for epNo in bestResult.episode_numbers: s = bestResult.season_number e = epNo if bestResult.show.is_scene: (s, e) = get_indexer_numbering(bestResult.show.indexerid, bestResult.show.indexer, bestResult.season_number, epNo) if bestResult.show.is_anime: a = get_absolute_number_from_season_and_episode(bestResult.show, s, e) if a: new_absolute_numbers.append(a) new_episode_numbers.append(e) new_season_numbers.append(s) # need to do a quick sanity check heregex. It's possible that we now have episodes # from more than one season (by tvdb numbering), and this is just too much # for sickrage, so we'd need to flag it. new_season_numbers = list(set(new_season_numbers)) # remove duplicates if len(new_season_numbers) > 1: raise InvalidNameException("Scene numbering results episodes from " "seasons %s, (i.e. more than one) and " "sickrage does not support this. " "Sorry." % (new_season_numbers)) # I guess it's possible that we'd have duplicate episodes too, so lets # eliminate them new_episode_numbers = list(set(new_episode_numbers)) new_episode_numbers.sort() # maybe even duplicate absolute numbers so why not do them as well new_absolute_numbers = list(set(new_absolute_numbers)) new_absolute_numbers.sort() if len(new_absolute_numbers): bestResult.ab_episode_numbers = new_absolute_numbers if len(new_season_numbers) and len(new_episode_numbers): bestResult.episode_numbers = new_episode_numbers bestResult.season_number = new_season_numbers[0] if bestResult.show.is_scene: sickrage.LOGGER.debug("Converted parsed result {} into {}".format(bestResult.original_name, bestResult)) # CPU sleep gen.sleep(1) return bestResult
def _ep_data(self, ep_obj): """ Creates a key value structure for a Tivo episode metadata file and returns the resulting data object. ep_obj: a TVEpisode instance to create the metadata file for. Lookup the show in http://thetvdb.com/ using the python library: https://github.com/dbr/indexer_api/ The results are saved in the object myShow. The key values for the tivo metadata file are from: http://pytivo.sourceforge.net/wiki/index.php/Metadata """ data = "" eps_to_write = [ep_obj] + ep_obj.relatedEps indexer_lang = ep_obj.show.lang try: lINDEXER_API_PARMS = sickrage.INDEXER_API( ep_obj.show.indexer).api_params.copy() lINDEXER_API_PARMS[b'actors'] = True if indexer_lang and not indexer_lang == sickrage.INDEXER_DEFAULT_LANGUAGE: lINDEXER_API_PARMS[b'language'] = indexer_lang if ep_obj.show.dvdorder != 0: lINDEXER_API_PARMS[b'dvdorder'] = True t = sickrage.INDEXER_API( ep_obj.show.indexer).indexer(**lINDEXER_API_PARMS) myShow = t[ep_obj.show.indexerid] except indexer_shownotfound as e: raise ShowNotFoundException(str(e)) except indexer_error as e: sickrage.LOGGER.error( "Unable to connect to " + sickrage.INDEXER_API(ep_obj.show.indexer).name + " while creating meta files - skipping - " + str(e)) return False for curEpToWrite in eps_to_write: try: myEp = myShow[curEpToWrite.season][curEpToWrite.episode] except (indexer_episodenotfound, indexer_seasonnotfound): sickrage.LOGGER.info( "Unable to find episode %dx%d on %s... has it been removed? Should I delete from db?" % (curEpToWrite.season, curEpToWrite.episode, sickrage.INDEXER_API(ep_obj.show.indexer).name)) return None if ep_obj.season == 0 and not getattr(myEp, 'firstaired', None): myEp[b"firstaired"] = str(datetime.date.fromordinal(1)) if not (getattr(myEp, 'episodename', None) and getattr(myEp, 'firstaired', None)): return None if getattr(myShow, 'seriesname', None): data += ("title : " + myShow[b"seriesname"] + "\n") data += ("seriesTitle : " + myShow[b"seriesname"] + "\n") data += ("episodeTitle : " + curEpToWrite._format_pattern('%Sx%0E %EN') + "\n") # This should be entered for episodic shows and omitted for movies. The standard tivo format is to enter # the season number followed by the episode number for that season. For example, enter 201 for season 2 # episode 01. # This only shows up if you go into the Details from the Program screen. # This seems to disappear once the video is transferred to TiVo. # NOTE: May not be correct format, missing season, but based on description from wiki leaving as is. data += ("episodeNumber : " + str(curEpToWrite.episode) + "\n") # Must be entered as true or false. If true, the year from originalAirDate will be shown in parentheses # after the episode's title and before the description on the Program screen. # FIXME: Hardcode isEpisode to true for now, not sure how to handle movies data += "isEpisode : true\n" # Write the synopsis of the video here # Micrsoft Word's smartquotes can die in a fire. sanitizedDescription = curEpToWrite.description # Replace double curly quotes sanitizedDescription = sanitizedDescription.replace( "\u201c", "\"").replace("\u201d", "\"") # Replace single curly quotes sanitizedDescription = sanitizedDescription.replace( "\u2018", "'").replace("\u2019", "'").replace("\u02BC", "'") data += ("description : " + sanitizedDescription + "\n") # Usually starts with "SH" and followed by 6-8 digits. # Tivo uses zap2it for thier data, so the series id is the zap2it_id. if getattr(myShow, 'zap2it_id', None): data += ("seriesId : " + myShow[b"zap2it_id"] + "\n") # This is the call sign of the channel the episode was recorded from. if getattr(myShow, 'network', None): data += ("callsign : " + myShow[b"network"] + "\n") # This must be entered as yyyy-mm-ddThh:mm:ssZ (the t is capitalized and never changes, the Z is also # capitalized and never changes). This is the original air date of the episode. # NOTE: Hard coded the time to T00:00:00Z as we really don't know when during the day the first run happened. if curEpToWrite.airdate != datetime.date.fromordinal(1): data += ("originalAirDate : " + str(curEpToWrite.airdate) + "T00:00:00Z\n") # This shows up at the beginning of the description on the Program screen and on the Details screen. if getattr(myShow, '_actors', None): for actor in myShow[b"_actors"]: if 'name' in actor and actor[b'name'].strip(): data += ("vActor : " + actor[b'name'].strip() + "\n") # This is shown on both the Program screen and the Details screen. if getattr(myEp, 'rating', None): try: rating = float(myEp[b'rating']) except ValueError: rating = 0.0 # convert 10 to 4 star rating. 4 * rating / 10 # only whole numbers or half numbers work. multiply by 2, round, divide by 2.0 rating = round(8 * rating / 10) / 2.0 data += ("starRating : " + str(rating) + "\n") # This is shown on both the Program screen and the Details screen. # It uses the standard TV rating system of: TV-Y7, TV-Y, TV-G, TV-PG, TV-14, TV-MA and TV-NR. if getattr(myShow, 'contentrating', None): data += ("tvRating : " + str(myShow[b"contentrating"]) + "\n") # This field can be repeated as many times as necessary or omitted completely. if ep_obj.show.genre: for genre in ep_obj.show.genre.split('|'): if genre: data += ("vProgramGenre : " + str(genre) + "\n") # NOTE: The following are metadata keywords are not used # displayMajorNumber # showingBits # displayMinorNumber # colorCode # vSeriesGenre # vGuestStar, vDirector, vExecProducer, vProducer, vWriter, vHost, vChoreographer # partCount # partIndex return data
def run(self): ShowQueueItem.run(self) sickrage.LOGGER.debug("Beginning update of " + self.show.name) sickrage.LOGGER.debug("Retrieving show info from " + sickrage.INDEXER_API(self.show.indexer).name + "") try: self.show.loadFromIndexer(cache=not self.force) except indexer_error as e: sickrage.LOGGER.warning( "Unable to contact " + sickrage.INDEXER_API(self.show.indexer).name + ", aborting: {}".format(e)) return except indexer_attributenotfound as e: sickrage.LOGGER.error( "Data retrieved from " + sickrage.INDEXER_API(self.show.indexer).name + " was incomplete, aborting: {}".format(e)) return sickrage.LOGGER.debug("Retrieving show info from TMDb") try: self.show.loadTMDbInfo() except Exception as e: sickrage.LOGGER.error("Error loading TMDb info: {}".format(e)) sickrage.LOGGER.debug(traceback.format_exc()) try: sickrage.LOGGER.debug( "Attempting to retrieve show info from IMDb") self.show.loadIMDbInfo() except Exception as e: sickrage.LOGGER.error("Error loading IMDb info: {}".format(e)) sickrage.LOGGER.debug(traceback.format_exc()) # have to save show before reading episodes from db try: self.show.saveToDB() except Exception as e: sickrage.LOGGER.error( "Error saving show info to the database: {}".format(e)) sickrage.LOGGER.debug(traceback.format_exc()) # get episode list from DB sickrage.LOGGER.debug("Loading all episodes from the database") DBEpList = self.show.loadEpisodesFromDB() # get episode list from TVDB sickrage.LOGGER.debug("Loading all episodes from " + sickrage.INDEXER_API(self.show.indexer).name + "") try: IndexerEpList = self.show.loadEpisodesFromIndexer( cache=not self.force) except indexer_exception as e: sickrage.LOGGER.error( "Unable to get info from " + sickrage.INDEXER_API(self.show.indexer).name + ", the show info will not be refreshed: {}".format(e)) IndexerEpList = None if IndexerEpList is None: sickrage.LOGGER.error( "No data returned from " + sickrage.INDEXER_API(self.show.indexer).name + ", unable to update this show") else: # for each ep we found on the Indexer delete it from the DB list for curSeason in IndexerEpList: for curEpisode in IndexerEpList[curSeason]: curEp = self.show.getEpisode(curSeason, curEpisode) curEp.saveToDB() if curSeason in DBEpList and curEpisode in DBEpList[ curSeason]: del DBEpList[curSeason][curEpisode] # remaining episodes in the DB list are not on the indexer, just delete them from the DB for curSeason in DBEpList: for curEpisode in DBEpList[curSeason]: sickrage.LOGGER.info("Permanently deleting episode " + str(curSeason) + "x" + str(curEpisode) + " from the database") curEp = self.show.getEpisode(curSeason, curEpisode) try: curEp.deleteEpisode() except EpisodeDeletedException: pass # save show again, in case episodes have changed try: self.show.saveToDB() except Exception as e: sickrage.LOGGER.error( "Error saving show info to the database: {}".format(e)) sickrage.LOGGER.debug(traceback.format_exc()) sickrage.LOGGER.debug("Finished update of " + self.show.name) sickrage.SHOWQUEUE.refreshShow(self.show, self.force) self.finish()
def run(self): ShowQueueItem.run(self) sickrage.LOGGER.info("Starting to add show {}".format(self.showDir)) # make sure the Indexer IDs are valid try: lINDEXER_API_PARMS = sickrage.INDEXER_API( self.indexer).api_params.copy() if self.lang: lINDEXER_API_PARMS[b'language'] = self.lang sickrage.LOGGER.info("" + str(sickrage.INDEXER_API(self.indexer).name) + ": " + repr(lINDEXER_API_PARMS)) t = sickrage.INDEXER_API( 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: sickrage.LOGGER.error( "Show in " + self.showDir + " has no name on " + str(sickrage.INDEXER_API(self.indexer).name) + ", probably the wrong language used to search with.") notifications.error( "Unable to add show", "Show in " + self.showDir + " has no name on " + str(sickrage.INDEXER_API(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: sickrage.LOGGER.error( "Show " + str(s[b'seriesname']) + " is on " + str(sickrage.INDEXER_API(self.indexer).name) + " but contains no season/episode data.") notifications.error( "Unable to add show", "Show " + str(s[b'seriesname']) + " is on " + str(sickrage.INDEXER_API(self.indexer).name) + " but contains no season/episode data.") self._finishEarly() return except Exception as e: sickrage.LOGGER.error( "%s Error while loading information from indexer %s. Error: %r" % (self.indexer_id, sickrage.INDEXER_API(self.indexer).name, e)) 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, sickrage.INDEXER_API( self.indexer).name, self.indexer_id)) if sickrage.USE_TRAKT: trakt_id = sickrage.INDEXER_API( self.indexer).config[b'trakt_id'] trakt_api = TraktAPI(sickrage.SSL_VERIFY, sickrage.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: self.show = TVShow(self.indexer, self.indexer_id, self.lang) self.show.loadFromIndexer() # set up initial values self.show.location = self.showDir self.show.subtitles = self.subtitles if self.subtitles is not None else sickrage.SUBTITLES_DEFAULT self.show.quality = self.quality if self.quality else sickrage.QUALITY_DEFAULT self.show.flatten_folders = self.flatten_folders if self.flatten_folders is not None else sickrage.FLATTEN_FOLDERS_DEFAULT self.show.anime = self.anime if self.anime is not None else sickrage.ANIME_DEFAULT self.show.scene = self.scene if self.scene is not None else sickrage.SCENE_DEFAULT self.show.archive_firstmatch = self.archive if self.archive is not None else sickrage.ARCHIVE_DEFAULT self.show.paused = self.paused if self.paused is not None else False # set up default new/missing episode status sickrage.LOGGER.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 indexer_exception as e: sickrage.LOGGER.error("Unable to add show due to an error with " + sickrage.INDEXER_API(self.indexer).name + ": {}".format(e)) if self.show: notifications.error("Unable to add " + str(self.show.name) + " due to an error with " + sickrage.INDEXER_API(self.indexer).name + "") else: notifications.error( "Unable to add show due to an error with " + sickrage.INDEXER_API(self.indexer).name + "") self._finishEarly() return except MultipleShowObjectsException: sickrage.LOGGER.warning("The show in " + self.showDir + " is already in your show list, skipping") notifications.error( 'Show skipped', "The show in " + self.showDir + " is already in your show list") self._finishEarly() return except Exception as e: sickrage.LOGGER.error("Error trying to add show: {}".format(e)) sickrage.LOGGER.debug(traceback.format_exc()) self._finishEarly() raise sickrage.LOGGER.debug("Retrieving show info from TMDb") try: self.show.loadTMDbInfo() except Exception as e: sickrage.LOGGER.error("Error loading TMDb info: {}".format(e)) try: sickrage.LOGGER.debug( "Attempting to retrieve show info from IMDb") self.show.loadIMDbInfo() except Exception as e: sickrage.LOGGER.error("Error loading IMDb info: {}".format(e)) try: self.show.saveToDB() except Exception as e: sickrage.LOGGER.error( "Error saving the show to the database: {}".format(e)) sickrage.LOGGER.debug(traceback.format_exc()) self._finishEarly() raise # add it to the show list sickrage.showList.append(self.show) try: self.show.loadEpisodesFromIndexer() except Exception as e: sickrage.LOGGER.error( "Error with " + sickrage.INDEXER_API(self.show.indexer).name + ", not creating episode list: {}".format(e)) sickrage.LOGGER.debug(traceback.format_exc()) # update internal name cache sickrage.NAMECACHE.buildNameCache() try: self.show.loadEpisodesFromDir() except Exception as e: sickrage.LOGGER.error( "Error searching dir for episodes: {}".format(e)) sickrage.LOGGER.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: sickrage.LOGGER.info( "Launching backlog for this show since its episodes are WANTED" ) sickrage.BACKLOGSEARCHER.searchBacklog([self.show]) self.show.writeMetadata() self.show.updateMetadata() self.show.populateCache() self.show.flushEpisodes() if sickrage.USE_TRAKT: # if there are specific episodes that need to be added by trakt sickrage.TRAKTSEARCHER.manageNewShow(self.show) # add show to trakt.tv library if sickrage.TRAKT_SYNC: sickrage.TRAKTSEARCHER.addShowToTraktLibrary(self.show) if sickrage.TRAKT_SYNC_WATCHLIST: sickrage.LOGGER.info("update watchlist") sickrage.NOTIFIERS.trakt_notifier.update_watchlist( show_obj=self.show) # Load XEM data to DB for show 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 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 _season_banners_dict(show_obj, season): """ Should return a dict like: result = {<season number>: {1: '<url 1>', 2: <url 2>, ...},} """ # This holds our resulting dictionary of season art result = {} indexer_lang = show_obj.lang try: # There's gotta be a better way of doing this but we don't wanna # change the language value elsewhere lINDEXER_API_PARMS = sickrage.INDEXER_API( show_obj.indexer).api_params.copy() lINDEXER_API_PARMS[b'banners'] = True if indexer_lang and not indexer_lang == sickrage.INDEXER_DEFAULT_LANGUAGE: lINDEXER_API_PARMS[b'language'] = indexer_lang t = sickrage.INDEXER_API( show_obj.indexer).indexer(**lINDEXER_API_PARMS) indexer_show_obj = t[show_obj.indexerid] except (indexer_error, IOError) as e: sickrage.LOGGER.warning( "Unable to look up show on " + sickrage.INDEXER_API(show_obj.indexer).name + ", not downloading images: {}".format(e)) sickrage.LOGGER.debug( "Indexer " + sickrage.INDEXER_API(show_obj.indexer).name + " maybe experiencing some problems. Try again later") return result # if we have no season banners then just finish if not getattr(indexer_show_obj, '_banners', None): return result # if we have no season banners then just finish if 'season' not in indexer_show_obj[ b'_banners'] or 'seasonwide' not in indexer_show_obj[ b'_banners'][b'season']: return result # Give us just the normal season graphics seasonsArtObj = indexer_show_obj[b'_banners'][b'season'][b'seasonwide'] # Returns a nested dictionary of season art with the season # number as primary key. It's really overkill but gives the option # to present to user via ui to pick down the road. result[season] = {} # find the correct season in the TVDB object and just copy the dict into our result dict for seasonArtID in seasonsArtObj.keys(): if int(seasonsArtObj[seasonArtID] [b'season']) == season and seasonsArtObj[seasonArtID][ b'language'] == sickrage.INDEXER_DEFAULT_LANGUAGE: result[season][seasonArtID] = seasonsArtObj[seasonArtID][ b'_bannerpath'] return result
def _retrieve_show_image(self, image_type, show_obj, which=None): """ Gets an image URL from theTVDB.com and TMDB.com, downloads it and returns the data. image_type: type of image to retrieve (currently supported: fanart, poster, banner) show_obj: a TVShow object to use when searching for the image which: optional, a specific numbered poster to look for Returns: the binary image data if available, or else None """ image_url = None indexer_lang = show_obj.lang try: # There's gotta be a better way of doing this but we don't wanna # change the language value elsewhere lINDEXER_API_PARMS = sickrage.INDEXER_API( show_obj.indexer).api_params.copy() lINDEXER_API_PARMS[b'banners'] = True if indexer_lang and not indexer_lang == sickrage.INDEXER_DEFAULT_LANGUAGE: lINDEXER_API_PARMS[b'language'] = indexer_lang if show_obj.dvdorder != 0: lINDEXER_API_PARMS[b'dvdorder'] = True t = sickrage.INDEXER_API( show_obj.indexer).indexer(**lINDEXER_API_PARMS) indexer_show_obj = t[show_obj.indexerid] except (indexer_error, IOError) as e: sickrage.LOGGER.warning( "Unable to look up show on " + sickrage.INDEXER_API(show_obj.indexer).name + ", not downloading images: {}".format(e)) sickrage.LOGGER.debug( "Indexer " + sickrage.INDEXER_API(show_obj.indexer).name + " maybe experiencing some problems. Try again later") return None if image_type not in ('fanart', 'poster', 'banner', 'poster_thumb', 'banner_thumb'): sickrage.LOGGER.error("Invalid image type " + str(image_type) + ", couldn't find it in the " + sickrage.INDEXER_API(show_obj.indexer).name + " object") return None if image_type == 'poster_thumb': if getattr(indexer_show_obj, 'poster', None): image_url = re.sub('posters', '_cache/posters', indexer_show_obj[b'poster']) if not image_url: # Try and get images from Fanart.TV image_url = self._retrieve_show_images_from_fanart( show_obj, image_type) if not image_url: # Try and get images from TMDB image_url = self._retrieve_show_images_from_tmdb( show_obj, image_type) elif image_type == 'banner_thumb': if getattr(indexer_show_obj, 'banner', None): image_url = re.sub('graphical', '_cache/graphical', indexer_show_obj[b'banner']) if not image_url: # Try and get images from Fanart.TV image_url = self._retrieve_show_images_from_fanart( show_obj, image_type) else: if getattr(indexer_show_obj, image_type, None): image_url = indexer_show_obj[image_type] if not image_url: # Try and get images from Fanart.TV image_url = self._retrieve_show_images_from_fanart( show_obj, image_type) if not image_url: # Try and get images from TMDB image_url = self._retrieve_show_images_from_tmdb( show_obj, image_type) if image_url: image_data = metadata_helpers.getShowImage(image_url, which) return image_data return None
def retrieve_exceptions(): """ Looks up the exceptions on github, parses them into a dict, and inserts them into the scene_exceptions table in cache.db. Also clears the scene name cache. """ for indexer in sickrage.INDEXER_API().indexers: if shouldRefresh(sickrage.INDEXER_API(indexer).name): sickrage.LOGGER.info("Checking for scene exception updates for " + sickrage.INDEXER_API(indexer).name + "") loc = sickrage.INDEXER_API(indexer).config[b'scene_loc'] try: data = getURL(loc, session=sickrage.INDEXER_API(indexer).session) except Exception: continue if data is None: # When data is None, trouble connecting to github, or reading file failed sickrage.LOGGER.debug( "Check scene exceptions update failed. Unable to update from: " + loc) continue setLastRefresh(sickrage.INDEXER_API(indexer).name) # each exception is on one line with the format indexer_id: 'show name 1', 'show name 2', etc for cur_line in data.splitlines(): indexer_id, _, aliases = cur_line.partition( ':') # @UnusedVariable if not aliases: continue indexer_id = int(indexer_id) # regex out the list of shows, taking \' into account # alias_list = [re.sub(r'\\(.)', r'\1', x) for x in re.findall(r"'(.*?)(?<!\\)',?", aliases)] alias_list = [{ re.sub(r'\\(.)', r'\1', x): -1 } for x in re.findall(r"'(.*?)(?<!\\)',?", aliases)] exception_dict[indexer_id] = alias_list del alias_list # cleanup del data # XEM scene exceptions _xem_exceptions_fetcher() for xem_ex in xem_exception_dict: if xem_ex in exception_dict: exception_dict[ xem_ex] = exception_dict[xem_ex] + xem_exception_dict[xem_ex] else: exception_dict[xem_ex] = xem_exception_dict[xem_ex] # AniDB scene exceptions _anidb_exceptions_fetcher() for anidb_ex in anidb_exception_dict: if anidb_ex in exception_dict: exception_dict[anidb_ex] = exception_dict[ anidb_ex] + anidb_exception_dict[anidb_ex] else: exception_dict[anidb_ex] = anidb_exception_dict[anidb_ex] queries = [] for cur_indexer_id in exception_dict: sql_ex = cache_db.CacheDB().select( "SELECT * FROM scene_exceptions WHERE indexer_id = ?;", [cur_indexer_id]) existing_exceptions = [x[b"show_name"] for x in sql_ex] if not cur_indexer_id in exception_dict: continue for cur_exception_dict in exception_dict[cur_indexer_id]: for ex in cur_exception_dict.iteritems(): cur_exception, curSeason = ex if cur_exception not in existing_exceptions: queries.append([ "INSERT OR IGNORE INTO scene_exceptions (indexer_id, show_name, season) VALUES (?,?,?);", [cur_indexer_id, cur_exception, curSeason] ]) if queries: cache_db.CacheDB().mass_action(queries) sickrage.LOGGER.debug("Updated scene exceptions") else: sickrage.LOGGER.debug("No scene exceptions update needed") # cleanup exception_dict.clear() anidb_exception_dict.clear() xem_exception_dict.clear()
def _ep_data(self, ep_obj): """ Creates an elementTree XML structure for a WDTV style episode.xml and returns the resulting data object. ep_obj: a TVShow instance to create the NFO for """ eps_to_write = [ep_obj] + ep_obj.relatedEps indexer_lang = ep_obj.show.lang try: lINDEXER_API_PARMS = sickrage.INDEXER_API( ep_obj.show.indexer).api_params.copy() lINDEXER_API_PARMS[b'actors'] = True if indexer_lang and not indexer_lang == sickrage.INDEXER_DEFAULT_LANGUAGE: lINDEXER_API_PARMS[b'language'] = indexer_lang if ep_obj.show.dvdorder != 0: lINDEXER_API_PARMS[b'dvdorder'] = True t = sickrage.INDEXER_API( ep_obj.show.indexer).indexer(**lINDEXER_API_PARMS) myShow = t[ep_obj.show.indexerid] except indexer_shownotfound as e: raise ShowNotFoundException(e.message) except indexer_error as e: sickrage.LOGGER.error( "Unable to connect to " + sickrage.INDEXER_API(ep_obj.show.indexer).name + " while creating meta files - skipping - {}".format(e)) return False rootNode = Element("details") # write an WDTV XML containing info for all matching episodes for curEpToWrite in eps_to_write: try: myEp = myShow[curEpToWrite.season][curEpToWrite.episode] except (indexer_episodenotfound, indexer_seasonnotfound): sickrage.LOGGER.info( "Unable to find episode %dx%d on %s... has it been removed? Should I delete from db?" % (curEpToWrite.season, curEpToWrite.episode, sickrage.INDEXER_API(ep_obj.show.indexer).name)) return None if ep_obj.season == 0 and not getattr(myEp, 'firstaired', None): myEp[b"firstaired"] = str(datetime.date.fromordinal(1)) if not (getattr(myEp, 'episodename', None) and getattr(myEp, 'firstaired', None)): return None if len(eps_to_write) > 1: episode = SubElement(rootNode, "details") else: episode = rootNode # TODO: get right EpisodeID episodeID = SubElement(episode, "id") episodeID.text = str(curEpToWrite.indexerid) title = SubElement(episode, "title") title.text = ep_obj.prettyName() if getattr(myShow, 'seriesname', None): seriesName = SubElement(episode, "series_name") seriesName.text = myShow[b"seriesname"] if curEpToWrite.name: episodeName = SubElement(episode, "episode_name") episodeName.text = curEpToWrite.name seasonNumber = SubElement(episode, "season_number") seasonNumber.text = str(curEpToWrite.season) episodeNum = SubElement(episode, "episode_number") episodeNum.text = str(curEpToWrite.episode) firstAired = SubElement(episode, "firstaired") if curEpToWrite.airdate != datetime.date.fromordinal(1): firstAired.text = str(curEpToWrite.airdate) if getattr(myShow, 'firstaired', None): try: year_text = str( datetime.datetime.strptime(myShow[b"firstaired"], dateFormat).year) if year_text: year = SubElement(episode, "year") year.text = year_text except Exception: pass if curEpToWrite.season != 0 and getattr(myShow, 'runtime', None): runtime = SubElement(episode, "runtime") runtime.text = myShow[b"runtime"] if getattr(myShow, 'genre', None): genre = SubElement(episode, "genre") genre.text = " / ".join([ x.strip() for x in myShow[b"genre"].split('|') if x.strip() ]) if getattr(myEp, 'director', None): director = SubElement(episode, "director") director.text = myEp[b'director'] if getattr(myShow, '_actors', None): for actor in myShow[b'_actors']: if not ('name' in actor and actor[b'name'].strip()): continue cur_actor = SubElement(episode, "actor") cur_actor_name = SubElement(cur_actor, "name") cur_actor_name.text = actor[b'name'] if 'role' in actor and actor[b'role'].strip(): cur_actor_role = SubElement(cur_actor, "role") cur_actor_role.text = actor[b'role'].strip() if curEpToWrite.description: overview = SubElement(episode, "overview") overview.text = curEpToWrite.description # Make it purdy indentXML(rootNode) data = ElementTree(rootNode) return data
def fix_xem_numbering(indexer_id, indexer): """ Returns a dict of (season, episode) : (sceneSeason, sceneEpisode) mappings for an entire show. Both the keys and values of the dict are tuples. Will be empty if there are no scene numbers set in xem """ if indexer_id is None: return {} indexer_id = int(indexer_id) indexer = int(indexer) rows = main_db.MainDB().select( 'SELECT season, episode, absolute_number, scene_season, scene_episode, scene_absolute_number FROM tv_episodes WHERE indexer = ? AND showid = ?', [indexer, indexer_id]) last_absolute_number = None last_scene_season = None last_scene_episode = None last_scene_absolute_number = None update_absolute_number = False update_scene_season = False update_scene_episode = False update_scene_absolute_number = False sickrage.LOGGER.debug( 'Fixing any XEM scene mapping issues for show %s on %s' % (indexer_id, sickrage.INDEXER_API(indexer).name)) cl = [] for row in rows: season = int(row[b'season']) episode = int(row[b'episode']) if not int(row[b'scene_season']) and last_scene_season: scene_season = last_scene_season + 1 update_scene_season = True else: scene_season = int(row[b'scene_season']) if last_scene_season and scene_season < last_scene_season: scene_season = last_scene_season + 1 update_scene_season = True if not int(row[b'scene_episode']) and last_scene_episode: scene_episode = last_scene_episode + 1 update_scene_episode = True else: scene_episode = int(row[b'scene_episode']) if last_scene_episode and scene_episode < last_scene_episode: scene_episode = last_scene_episode + 1 update_scene_episode = True # check for unset values and correct them if not int(row[b'absolute_number']) and last_absolute_number: absolute_number = last_absolute_number + 1 update_absolute_number = True else: absolute_number = int(row[b'absolute_number']) if last_absolute_number and absolute_number < last_absolute_number: absolute_number = last_absolute_number + 1 update_absolute_number = True if not int( row[b'scene_absolute_number']) and last_scene_absolute_number: scene_absolute_number = last_scene_absolute_number + 1 update_scene_absolute_number = True else: scene_absolute_number = int(row[b'scene_absolute_number']) if last_scene_absolute_number and scene_absolute_number < last_scene_absolute_number: scene_absolute_number = last_scene_absolute_number + 1 update_scene_absolute_number = True # store values for lookup on next iteration last_absolute_number = absolute_number last_scene_season = scene_season last_scene_episode = scene_episode last_scene_absolute_number = scene_absolute_number if update_absolute_number: cl.append([ "UPDATE tv_episodes SET absolute_number = ? WHERE showid = ? AND season = ? AND episode = ?", [absolute_number, indexer_id, season, episode] ]) update_absolute_number = False if update_scene_season: cl.append([ "UPDATE tv_episodes SET scene_season = ? WHERE showid = ? AND season = ? AND episode = ?", [scene_season, indexer_id, season, episode] ]) update_scene_season = False if update_scene_episode: cl.append([ "UPDATE tv_episodes SET scene_episode = ? WHERE showid = ? AND season = ? AND episode = ?", [scene_episode, indexer_id, season, episode] ]) update_scene_episode = False if update_scene_absolute_number: cl.append([ "UPDATE tv_episodes SET scene_absolute_number = ? WHERE showid = ? AND season = ? AND episode = ?", [scene_absolute_number, indexer_id, season, episode] ]) update_scene_absolute_number = False if len(cl) > 0: main_db.MainDB().mass_action(cl)
def _show_data(self, show_obj): """ Creates an elementTree XML structure for a MediaBrowser-style series.xml returns the resulting data object. show_obj: a TVShow instance to create the NFO for """ indexer_lang = show_obj.lang # There's gotta be a better way of doing this but we don't wanna # change the language value elsewhere lINDEXER_API_PARMS = sickrage.INDEXER_API(show_obj.indexer).api_params.copy() lINDEXER_API_PARMS[b'actors'] = True if indexer_lang and not indexer_lang == sickrage.INDEXER_DEFAULT_LANGUAGE: lINDEXER_API_PARMS[b'language'] = indexer_lang if show_obj.dvdorder != 0: lINDEXER_API_PARMS[b'dvdorder'] = True t = sickrage.INDEXER_API(show_obj.indexer).indexer(**lINDEXER_API_PARMS) tv_node = Element("Series") try: myShow = t[int(show_obj.indexerid)] except indexer_shownotfound: sickrage.LOGGER.error("Unable to find show with id " + str(show_obj.indexerid) + " on " + sickrage.INDEXER_API( show_obj.indexer).name + ", skipping it") raise except indexer_error: sickrage.LOGGER.error( "" + sickrage.INDEXER_API(show_obj.indexer).name + " is down, can't use its data to make the NFO") raise # check for title and id if not (getattr(myShow, 'seriesname', None) and getattr(myShow, 'id', None)): sickrage.LOGGER.info("Incomplete info for show with id " + str(show_obj.indexerid) + " on " + sickrage.INDEXER_API( show_obj.indexer).name + ", skipping it") return False if getattr(myShow, 'id', None): indexerid = SubElement(tv_node, "id") indexerid.text = str(myShow[b'id']) if getattr(myShow, 'seriesname', None): SeriesName = SubElement(tv_node, "SeriesName") SeriesName.text = myShow[b'seriesname'] if getattr(myShow, 'status', None): Status = SubElement(tv_node, "Status") Status.text = myShow[b'status'] if getattr(myShow, 'network', None): Network = SubElement(tv_node, "Network") Network.text = myShow[b'network'] if getattr(myShow, 'airs_time', None): Airs_Time = SubElement(tv_node, "Airs_Time") Airs_Time.text = myShow[b'airs_time'] if getattr(myShow, 'airs_dayofweek', None): Airs_DayOfWeek = SubElement(tv_node, "Airs_DayOfWeek") Airs_DayOfWeek.text = myShow[b'airs_dayofweek'] FirstAired = SubElement(tv_node, "FirstAired") if getattr(myShow, 'firstaired', None): FirstAired.text = myShow[b'firstaired'] if getattr(myShow, 'contentrating', None): ContentRating = SubElement(tv_node, "ContentRating") ContentRating.text = myShow[b'contentrating'] MPAARating = SubElement(tv_node, "MPAARating") MPAARating.text = myShow[b'contentrating'] certification = SubElement(tv_node, "certification") certification.text = myShow[b'contentrating'] MetadataType = SubElement(tv_node, "Type") MetadataType.text = "Series" if getattr(myShow, 'overview', None): Overview = SubElement(tv_node, "Overview") Overview.text = myShow[b'overview'] if getattr(myShow, 'firstaired', None): PremiereDate = SubElement(tv_node, "PremiereDate") PremiereDate.text = myShow[b'firstaired'] if getattr(myShow, 'rating', None): Rating = SubElement(tv_node, "Rating") Rating.text = myShow[b'rating'] if getattr(myShow, 'firstaired', None): try: year_text = str(datetime.datetime.strptime(myShow[b'firstaired'], dateFormat).year) if year_text: ProductionYear = SubElement(tv_node, "ProductionYear") ProductionYear.text = year_text except Exception: pass if getattr(myShow, 'runtime', None): RunningTime = SubElement(tv_node, "RunningTime") RunningTime.text = myShow[b'runtime'] Runtime = SubElement(tv_node, "Runtime") Runtime.text = myShow[b'runtime'] if getattr(myShow, 'imdb_id', None): imdb_id = SubElement(tv_node, "IMDB_ID") imdb_id.text = myShow[b'imdb_id'] imdb_id = SubElement(tv_node, "IMDB") imdb_id.text = myShow[b'imdb_id'] imdb_id = SubElement(tv_node, "IMDbId") imdb_id.text = myShow[b'imdb_id'] if getattr(myShow, 'zap2it_id', None): Zap2ItId = SubElement(tv_node, "Zap2ItId") Zap2ItId.text = myShow[b'zap2it_id'] if getattr(myShow, 'genre', None) and isinstance(myShow[b"genre"], basestring): Genres = SubElement(tv_node, "Genres") for genre in myShow[b'genre'].split('|'): if genre.strip(): cur_genre = SubElement(Genres, "Genre") cur_genre.text = genre.strip() Genre = SubElement(tv_node, "Genre") Genre.text = "|".join([x.strip() for x in myShow[b"genre"].split('|') if x.strip()]) if getattr(myShow, 'network', None): Studios = SubElement(tv_node, "Studios") Studio = SubElement(Studios, "Studio") Studio.text = myShow[b'network'] if getattr(myShow, '_actors', None): Persons = SubElement(tv_node, "Persons") for actor in myShow[b'_actors']: if not ('name' in actor and actor[b'name'].strip()): continue cur_actor = SubElement(Persons, "Person") cur_actor_name = SubElement(cur_actor, "Name") cur_actor_name.text = actor[b'name'].strip() cur_actor_type = SubElement(cur_actor, "Type") cur_actor_type.text = "Actor" if 'role' in actor and actor[b'role'].strip(): cur_actor_role = SubElement(cur_actor, "Role") cur_actor_role.text = actor[b'role'].strip() indentXML(tv_node) data = ElementTree(tv_node) return data
def _ep_data(self, ep_obj): """ Creates an elementTree XML structure for an KODI-style episode.nfo and returns the resulting data object. show_obj: a TVEpisode instance to create the NFO for """ eps_to_write = [ep_obj] + ep_obj.relatedEps indexer_lang = ep_obj.show.lang lINDEXER_API_PARMS = sickrage.INDEXER_API( ep_obj.show.indexer).api_params.copy() lINDEXER_API_PARMS[b'actors'] = True if indexer_lang and not indexer_lang == sickrage.INDEXER_DEFAULT_LANGUAGE: lINDEXER_API_PARMS[b'language'] = indexer_lang if ep_obj.show.dvdorder != 0: lINDEXER_API_PARMS[b'dvdorder'] = True try: t = sickrage.INDEXER_API( ep_obj.show.indexer).indexer(**lINDEXER_API_PARMS) myShow = t[ep_obj.show.indexerid] except indexer_shownotfound as e: raise ShowNotFoundException(e.message) except indexer_error as e: sickrage.LOGGER.error( "Unable to connect to {} while creating meta files - skipping - {}" .format(sickrage.INDEXER_API(ep_obj.show.indexer).name, e)) return if len(eps_to_write) > 1: rootNode = Element("kodimultiepisode") else: rootNode = Element("episodedetails") # write an NFO containing info for all matching episodes for curEpToWrite in eps_to_write: try: myEp = myShow[curEpToWrite.season][curEpToWrite.episode] except (indexer_episodenotfound, indexer_seasonnotfound): sickrage.LOGGER.info( "Unable to find episode %dx%d on %s... has it been removed? Should I delete from db?" % (curEpToWrite.season, curEpToWrite.episode, sickrage.INDEXER_API(ep_obj.show.indexer).name)) return None if not getattr(myEp, 'firstaired', None): myEp[b"firstaired"] = str(datetime.date.fromordinal(1)) if not getattr(myEp, 'episodename', None): sickrage.LOGGER.debug( "Not generating nfo because the ep has no title") return None sickrage.LOGGER.debug("Creating metadata for episode " + str(ep_obj.season) + "x" + str(ep_obj.episode)) if len(eps_to_write) > 1: episode = SubElement(rootNode, "episodedetails") else: episode = rootNode if getattr(myEp, 'episodename', None): title = SubElement(episode, "title") title.text = myEp[b'episodename'] if getattr(myShow, 'seriesname', None): showtitle = SubElement(episode, "showtitle") showtitle.text = myShow[b'seriesname'] season = SubElement(episode, "season") season.text = str(curEpToWrite.season) episodenum = SubElement(episode, "episode") episodenum.text = str(curEpToWrite.episode) uniqueid = SubElement(episode, "uniqueid") uniqueid.text = str(curEpToWrite.indexerid) if curEpToWrite.airdate != datetime.date.fromordinal(1): aired = SubElement(episode, "aired") aired.text = str(curEpToWrite.airdate) if getattr(myEp, 'overview', None): plot = SubElement(episode, "plot") plot.text = myEp[b'overview'] if curEpToWrite.season and getattr(myShow, 'runtime', None): runtime = SubElement(episode, "runtime") runtime.text = myShow[b"runtime"] if getattr(myEp, 'airsbefore_season', None): displayseason = SubElement(episode, "displayseason") displayseason.text = myEp[b'airsbefore_season'] if getattr(myEp, 'airsbefore_episode', None): displayepisode = SubElement(episode, "displayepisode") displayepisode.text = myEp[b'airsbefore_episode'] if getattr(myEp, 'filename', None): thumb = SubElement(episode, "thumb") thumb.text = myEp[b'filename'].strip() # watched = SubElement(episode, "watched") # watched.text = 'false' if getattr(myEp, 'writer', None): ep_credits = SubElement(episode, "credits") ep_credits.text = myEp[b'writer'].strip() if getattr(myEp, 'director', None): director = SubElement(episode, "director") director.text = myEp[b'director'].strip() if getattr(myEp, 'rating', None): rating = SubElement(episode, "rating") rating.text = myEp[b'rating'] if getattr(myEp, 'gueststars', None) and isinstance( myEp[b'gueststars'], basestring): for actor in (x.strip() for x in myEp[b'gueststars'].split('|') if x.strip()): cur_actor = SubElement(episode, "actor") cur_actor_name = SubElement(cur_actor, "name") cur_actor_name.text = actor if getattr(myShow, '_actors', None): for actor in myShow[b'_actors']: cur_actor = SubElement(episode, "actor") if 'name' in actor and actor[b'name'].strip(): cur_actor_name = SubElement(cur_actor, "name") cur_actor_name.text = actor[b'name'].strip() else: continue if 'role' in actor and actor[b'role'].strip(): cur_actor_role = SubElement(cur_actor, "role") cur_actor_role.text = actor[b'role'].strip() if 'image' in actor and actor[b'image'].strip(): cur_actor_thumb = SubElement(cur_actor, "thumb") cur_actor_thumb.text = actor[b'image'].strip() # Make it purdy indentXML(rootNode) data = ElementTree(rootNode) return data
def _show_data(self, show_obj): """ Creates an elementTree XML structure for a MediaBrowser-style series.xml returns the resulting data object. show_obj: a TVShow instance to create the NFO for """ indexer_lang = show_obj.lang lINDEXER_API_PARMS = sickrage.INDEXER_API( show_obj.indexer).api_params.copy() lINDEXER_API_PARMS[b'actors'] = True if indexer_lang and not indexer_lang == sickrage.INDEXER_DEFAULT_LANGUAGE: lINDEXER_API_PARMS[b'language'] = indexer_lang if show_obj.dvdorder != 0: lINDEXER_API_PARMS[b'dvdorder'] = True t = sickrage.INDEXER_API( show_obj.indexer).indexer(**lINDEXER_API_PARMS) rootNode = Element("details") tv_node = SubElement(rootNode, "movie") tv_node.attrib[b"isExtra"] = "false" tv_node.attrib[b"isSet"] = "false" tv_node.attrib[b"isTV"] = "true" try: myShow = t[int(show_obj.indexerid)] except indexer_shownotfound: sickrage.LOGGER.error("Unable to find show with id " + str(show_obj.indexerid) + " on tvdb, skipping it") raise except indexer_error: sickrage.LOGGER.error( "TVDB is down, can't use its data to make the NFO") raise # check for title and id if not (getattr(myShow, 'seriesname', None) and getattr(myShow, 'id', None)): sickrage.LOGGER.info("Incomplete info for show with id " + str(show_obj.indexerid) + " on " + sickrage.INDEXER_API(show_obj.indexer).name + ", skipping it") return False SeriesName = SubElement(tv_node, "title") SeriesName.text = myShow[b'seriesname'] if getattr(myShow, "genre", None): Genres = SubElement(tv_node, "genres") for genre in myShow[b'genre'].split('|'): if genre and genre.strip(): cur_genre = SubElement(Genres, "Genre") cur_genre.text = genre.strip() if getattr(myShow, 'firstaired', None): FirstAired = SubElement(tv_node, "premiered") FirstAired.text = myShow[b'firstaired'] if getattr(myShow, "firstaired", None): try: year_text = str( datetime.datetime.strptime(myShow[b"firstaired"], dateFormat).year) if year_text: year = SubElement(tv_node, "year") year.text = year_text except Exception: pass if getattr(myShow, 'overview', None): plot = SubElement(tv_node, "plot") plot.text = myShow[b"overview"] if getattr(myShow, 'rating', None): try: rating = int(float(myShow[b'rating']) * 10) except ValueError: rating = 0 if rating: Rating = SubElement(tv_node, "rating") Rating.text = str(rating) if getattr(myShow, 'status', None): Status = SubElement(tv_node, "status") Status.text = myShow[b'status'] if getattr(myShow, "contentrating", None): mpaa = SubElement(tv_node, "mpaa") mpaa.text = myShow[b"contentrating"] if getattr(myShow, 'imdb_id', None): imdb_id = SubElement(tv_node, "id") imdb_id.attrib[b"moviedb"] = "imdb" imdb_id.text = myShow[b'imdb_id'] if getattr(myShow, 'id', None): indexerid = SubElement(tv_node, "indexerid") indexerid.text = myShow[b'id'] if getattr(myShow, 'runtime', None): Runtime = SubElement(tv_node, "runtime") Runtime.text = myShow[b'runtime'] if getattr(myShow, '_actors', None): cast = SubElement(tv_node, "cast") for actor in myShow[b'_actors']: if 'name' in actor and actor[b'name'].strip(): cur_actor = SubElement(cast, "actor") cur_actor.text = actor[b'name'].strip() indentXML(rootNode) data = ElementTree(rootNode) return data
def _show_data(self, show_obj): """ Creates an elementTree XML structure for an KODI-style tvshow.nfo and returns the resulting data object. show_obj: a TVShow instance to create the NFO for """ show_ID = show_obj.indexerid indexer_lang = show_obj.lang lINDEXER_API_PARMS = sickrage.INDEXER_API( show_obj.indexer).api_params.copy() lINDEXER_API_PARMS[b'actors'] = True if indexer_lang and not indexer_lang == sickrage.INDEXER_DEFAULT_LANGUAGE: lINDEXER_API_PARMS[b'language'] = indexer_lang if show_obj.dvdorder != 0: lINDEXER_API_PARMS[b'dvdorder'] = True t = sickrage.INDEXER_API( show_obj.indexer).indexer(**lINDEXER_API_PARMS) tv_node = Element("tvshow") try: myShow = t[int(show_ID)] except indexer_shownotfound: sickrage.LOGGER.error("Unable to find show with id " + str(show_ID) + " on " + sickrage.INDEXER_API(show_obj.indexer).name + ", skipping it") raise except indexer_error: sickrage.LOGGER.error( "" + sickrage.INDEXER_API(show_obj.indexer).name + " is down, can't use its data to add this show") raise # check for title and id if not (getattr(myShow, 'seriesname', None) and getattr(myShow, 'id', None)): sickrage.LOGGER.info("Incomplete info for show with id " + str(show_ID) + " on " + sickrage.INDEXER_API(show_obj.indexer).name + ", skipping it") return False title = SubElement(tv_node, "title") title.text = myShow[b"seriesname"] if getattr(myShow, 'rating', None): rating = SubElement(tv_node, "rating") rating.text = myShow[b"rating"] if getattr(myShow, 'firstaired', None): try: year_text = str( datetime.datetime.strptime(myShow[b"firstaired"], dateFormat).year) if year_text: year = SubElement(tv_node, "year") year.text = year_text except: pass if getattr(myShow, 'overview', None): plot = SubElement(tv_node, "plot") plot.text = myShow[b"overview"] if getattr(myShow, 'id', None): episodeguide = SubElement(tv_node, "episodeguide") episodeguideurl = SubElement(episodeguide, "url") episodeguideurl.text = sickrage.INDEXER_API( show_obj.indexer).config[b'base_url'] + str( myShow[b"id"]) + '/all/en.zip' if getattr(myShow, 'contentrating', None): mpaa = SubElement(tv_node, "mpaa") mpaa.text = myShow[b"contentrating"] if getattr(myShow, 'id', None): indexerid = SubElement(tv_node, "id") indexerid.text = str(myShow[b"id"]) if getattr(myShow, 'genre', None) and isinstance( myShow[b"genre"], basestring): genre = SubElement(tv_node, "genre") genre.text = " / ".join(x.strip() for x in myShow[b"genre"].split('|') if x.strip()) if getattr(myShow, 'firstaired', None): premiered = SubElement(tv_node, "premiered") premiered.text = myShow[b"firstaired"] if getattr(myShow, 'network', None): studio = SubElement(tv_node, "studio") studio.text = myShow[b"network"].strip() if getattr(myShow, '_actors', None): for actor in myShow[b'_actors']: cur_actor = SubElement(tv_node, "actor") if 'name' in actor and actor[b'name'].strip(): cur_actor_name = SubElement(cur_actor, "name") cur_actor_name.text = actor[b'name'].strip() else: continue if 'role' in actor and actor[b'role'].strip(): cur_actor_role = SubElement(cur_actor, "role") cur_actor_role.text = actor[b'role'].strip() if 'image' in actor and actor[b'image'].strip(): cur_actor_thumb = SubElement(cur_actor, "thumb") cur_actor_thumb.text = actor[b'image'].strip() # Make it purdy indentXML(tv_node) data = ElementTree(tv_node) return data
def loadFromIndexer(self, season=None, episode=None, cache=True, tvapi=None, cachedSeason=None): if season is None: season = self.season if episode is None: episode = self.episode sickrage.LOGGER.debug("%s: Loading episode details from %s for episode S%02dE%02d" % (self.show.indexerid, sickrage.INDEXER_API(self.show.indexer).name, season or 0, episode or 0)) indexer_lang = self.show.lang try: if cachedSeason is None: if tvapi is None: lINDEXER_API_PARMS = sickrage.INDEXER_API(self.indexer).api_params.copy() if not cache: lINDEXER_API_PARMS[b'cache'] = False if indexer_lang: lINDEXER_API_PARMS[b'language'] = indexer_lang if self.show.dvdorder != 0: lINDEXER_API_PARMS[b'dvdorder'] = True t = sickrage.INDEXER_API(self.indexer).indexer(**lINDEXER_API_PARMS) else: t = tvapi myEp = t[self.show.indexerid][season][episode] else: myEp = cachedSeason[episode] except (indexer_error, IOError) as e: sickrage.LOGGER.debug("" + sickrage.INDEXER_API(self.indexer).name + " threw up an error: {}".format(e)) # if the episode is already valid just log it, if not throw it up if self.name: sickrage.LOGGER.debug("" + sickrage.INDEXER_API( self.indexer).name + " timed out but we have enough info from other sources, allowing the error") return else: sickrage.LOGGER.error("" + sickrage.INDEXER_API(self.indexer).name + " timed out, unable to create the episode") return False except (indexer_episodenotfound, indexer_seasonnotfound): sickrage.LOGGER.debug("Unable to find the episode on " + sickrage.INDEXER_API( self.indexer).name + "... has it been removed? Should I delete from db?") # if I'm no longer on the Indexers but I once was then delete myself from the DB if self.indexerid != -1: self.deleteEpisode() return if getattr(myEp, 'episodename', None) is None: sickrage.LOGGER.info("This episode %s - S%02dE%02d has no name on %s. Setting to an empty string" % ( self.show.name, season or 0, episode or 0, sickrage.INDEXER_API(self.indexer).name)) setattr(myEp, 'episodename', '') # # if I'm incomplete on TVDB but I once was complete then just delete myself from the DB for now # if self.indexerid != -1: # self.deleteEpisode() # return False if getattr(myEp, 'absolute_number', None) is None: sickrage.LOGGER.debug("This episode %s - S%02dE%02d has no absolute number on %s" % ( self.show.name, season or 0, episode or 0, sickrage.INDEXER_API(self.indexer).name)) else: sickrage.LOGGER.debug("%s: The absolute_number for S%02dE%02d is: %s " % ( self.show.indexerid, season or 0, episode or 0, myEp[b"absolute_number"])) self.absolute_number = int(myEp[b"absolute_number"]) self.name = getattr(myEp, 'episodename', "") self.season = season self.episode = episode xem_refresh(self.show.indexerid, self.show.indexer) self.scene_absolute_number = get_scene_absolute_numbering( self.show.indexerid, self.show.indexer, self.absolute_number ) self.scene_season, self.scene_episode = get_scene_numbering( self.show.indexerid, self.show.indexer, self.season, self.episode ) self.description = getattr(myEp, 'overview', "") firstaired = getattr(myEp, 'firstaired', None) if not firstaired or firstaired == "0000-00-00": firstaired = str(datetime.date.fromordinal(1)) rawAirdate = [int(x) for x in firstaired.split("-")] try: self.airdate = datetime.date(rawAirdate[0], rawAirdate[1], rawAirdate[2]) except (ValueError, IndexError): sickrage.LOGGER.warning("Malformed air date of %s retrieved from %s for (%s - S%02dE%02d)" % ( firstaired, sickrage.INDEXER_API(self.indexer).name, self.show.name, season or 0, episode or 0)) # if I'm incomplete on the indexer but I once was complete then just delete myself from the DB for now if self.indexerid != -1: self.deleteEpisode() return False # early conversion to int so that episode doesn't get marked dirty self.indexerid = getattr(myEp, 'id', None) if self.indexerid is None: sickrage.LOGGER.error("Failed to retrieve ID from " + sickrage.INDEXER_API(self.indexer).name) if self.indexerid != -1: self.deleteEpisode() return False # don't update show status if show dir is missing, unless it's missing on purpose if not os.path.isdir( self.show._location) and not sickrage.CREATE_MISSING_SHOW_DIRS and not sickrage.ADD_SHOWS_WO_DIR: sickrage.LOGGER.info( "The show dir %s is missing, not bothering to change the episode statuses since it'd probably be invalid" % self.show._location) return if self.location: sickrage.LOGGER.debug("%s: Setting status for S%02dE%02d based on status %s and location %s" % (self.show.indexerid, season or 0, episode or 0, statusStrings[self.status], self.location)) if not os.path.isfile(self.location): if self.airdate >= datetime.date.today() or self.airdate == datetime.date.fromordinal(1): sickrage.LOGGER.debug("Episode airs in the future or has no airdate, marking it %s" % statusStrings[ UNAIRED]) self.status = UNAIRED elif self.status in [UNAIRED, UNKNOWN]: # Only do UNAIRED/UNKNOWN, it could already be snatched/ignored/skipped, or downloaded/archived to disconnected media sickrage.LOGGER.debug( "Episode has already aired, marking it %s" % statusStrings[self.show.default_ep_status]) self.status = self.show.default_ep_status if self.season > 0 else SKIPPED # auto-skip specials else: sickrage.LOGGER.debug( "Not touching status [ %s ] It could be skipped/ignored/snatched/archived" % statusStrings[ self.status]) # if we have a media file then it's downloaded elif isMediaFile(self.location): # leave propers alone, you have to either post-process them or manually change them back if self.status not in Quality.SNATCHED_PROPER + Quality.DOWNLOADED + Quality.SNATCHED + Quality.ARCHIVED: sickrage.LOGGER.debug( "5 Status changes from " + str(self.status) + " to " + str( Quality.statusFromName(self.location))) self.status = Quality.statusFromName(self.location, anime=self.show.is_anime) # shouldn't get here probably else: sickrage.LOGGER.debug("6 Status changes from " + str(self.status) + " to " + str(UNKNOWN)) self.status = UNKNOWN