def get_xem_ids(): global xem_ids_list for tvid, name in iteritems(sickbeard.TVInfoAPI().xem_supported_sources): xem_ids = _xem_get_ids(name, sickbeard.TVInfoAPI(tvid).config['xem_origin']) if len(xem_ids): xem_ids_list[tvid] = xem_ids
def get_url(self, key): if TVINFO_TVMAZE == key: return '%sshows/%s' % (sickbeard.TVInfoAPI( TVINFO_TVMAZE).config['base_url'], self.tvmaze_ids[key]) return '%slookup/shows?%s=%s%s' % ( sickbeard.TVInfoAPI(TVINFO_TVMAZE).config['base_url'], self.tvmaze_ids[key], ('', 'tt')[key == TVINFO_IMDB], (self[key], '%07d' % self[key])[key == TVINFO_IMDB])
def _get_episode_thumb_url(ep_obj): # type: (sickbeard.tv.TVEpisode) -> Optional[AnyStr] """ Returns the URL to use for downloading an episode's thumbnail. Uses theTVDB.com and TVRage.com data. :param ep_obj: a TVEpisode object for which to grab the thumb URL :return: URL to thumb """ ep_obj_list = [ep_obj] + ep_obj.related_ep_obj # validate show from .. import helpers if not helpers.validate_show(ep_obj.show_obj): return None # try all included episodes in case some have thumbs and others don't for cur_ep_obj in ep_obj_list: if TVINFO_TVDB == cur_ep_obj.show_obj.tvid: show_lang = cur_ep_obj.show_obj.lang try: tvinfo_config = sickbeard.TVInfoAPI(TVINFO_TVDB).api_params.copy() tvinfo_config['dvdorder'] = 0 != cur_ep_obj.show_obj.dvdorder tvinfo_config['no_dummy'] = True if show_lang and not 'en' == show_lang: tvinfo_config['language'] = show_lang t = sickbeard.TVInfoAPI(TVINFO_TVDB).setup(**tvinfo_config) ep_info = t[cur_ep_obj.show_obj.prodid][cur_ep_obj.season][cur_ep_obj.episode] except (BaseTVinfoEpisodenotfound, BaseTVinfoSeasonnotfound, TypeError): ep_info = None else: ep_info = helpers.validate_show(cur_ep_obj.show_obj, cur_ep_obj.season, cur_ep_obj.episode) if not ep_info: continue thumb_url = getattr(ep_info, 'filename', None) \ or (isinstance(ep_info, dict) and ep_info.get('filename', None)) if thumb_url not in (None, False, ''): return thumb_url return None
def _episode_strings(self, ep_obj # type: TVEpisode ): # type: (...) -> List[Dict[AnyStr, List[AnyStr]]] """ :param ep_obj: episode object :return: """ search_params = [] base_params = {} if not ep_obj: return [base_params] ep_detail = None if ep_obj.show_obj.air_by_date or ep_obj.show_obj.is_sports: airdate = str(ep_obj.airdate).split('-') base_params['season'] = airdate[0] if ep_obj.show_obj.air_by_date: base_params['ep'] = '/'.join(airdate[1:]) ep_detail = '+"%s.%s"' % (base_params['season'], '.'.join(airdate[1:])) elif ep_obj.show_obj.is_anime: base_params['ep'] = '%i' % (helpers.try_int(ep_obj.scene_absolute_number) or helpers.try_int(ep_obj.scene_episode)) ep_detail = '%02d' % helpers.try_int(base_params['ep']) else: base_params['season'], base_params['ep'] = ( (ep_obj.season, ep_obj.episode), (ep_obj.scene_season, ep_obj.scene_episode))[ep_obj.show_obj.is_scene] ep_detail = sickbeard.config.naming_ep_type[2] % { 'seasonnumber': helpers.try_int(base_params['season'], 1), 'episodenumber': helpers.try_int(base_params['ep'], 1)} # id search params = base_params.copy() use_id = False for i in sickbeard.TVInfoAPI().all_sources: if i in ep_obj.show_obj.ids and 0 < ep_obj.show_obj.ids[i]['id'] and i in self.caps: params[self.caps[i]] = ep_obj.show_obj.ids[i]['id'] use_id = True use_id and search_params.append(params) spacer = 'nzbgeek.info' in self.url.lower() and ' ' or '.' # query search and exceptions name_exceptions = get_show_names(ep_obj, spacer) if sickbeard.scene_exceptions.has_abs_episodes(ep_obj): search_params.append({'q': '%s%s%s' % (ep_obj.show_obj.name, spacer, base_params['ep'])}) for cur_exception in name_exceptions: params = base_params.copy() params['q'] = cur_exception search_params.append(params) if ep_detail: params = base_params.copy() params['q'] = '%s%s%s' % (cur_exception, spacer, ep_detail) 'season' in params and params.pop('season') 'ep' in params and params.pop('ep') search_params.append(params) return [{'Episode': search_params}]
def _season_image_dict(self, show_obj, season, image_type): # type: (sickbeard.tv.TVShow, int, AnyStr) -> Dict[int, Dict[int, AnyStr]] """ image_type : Type of image to fetch, 'seasons' or 'seasonwides' image_type type : String Should return a dict like: result = {<season number>: {1: '<url 1>', 2: <url 2>, ...},} """ result = {} try: # There's gotta be a better way of doing this but we don't wanna # change the language value elsewhere tvinfo_config = sickbeard.TVInfoAPI(show_obj.tvid).api_params.copy() tvinfo_config[image_type] = True tvinfo_config['dvdorder'] = 0 != show_obj.dvdorder if 'en' != getattr(show_obj, 'lang', None): tvinfo_config['language'] = show_obj.lang t = sickbeard.TVInfoAPI(show_obj.tvid).setup(**tvinfo_config) tvinfo_obj_show = t[show_obj.prodid] except (BaseTVinfoError, IOError) as e: logger.log(u'Unable to look up show on ' + sickbeard.TVInfoAPI( show_obj.tvid).name + ', not downloading images: ' + ex(e), logger.WARNING) return result if not self._valid_show(tvinfo_obj_show, show_obj): return result season_images = getattr(tvinfo_obj_show, 'banners', {}).get( ('season', 'seasonwide')['seasonwides' == image_type], {}).get(season, {}) for image_id in season_images: if season not in result: result[season] = {} result[season][image_id] = season_images[image_id]['bannerpath'] return result
def create_show_metadata(self, show_obj, force=False): # type: (sickbeard.tv.TVShow, bool) -> bool result = False if self.show_metadata and show_obj and (not self._has_show_metadata(show_obj) or force): logger.log('Metadata provider %s creating show metadata for %s' % (self.name, show_obj.name), logger.DEBUG) try: result = self.write_show_file(show_obj) except BaseTVinfoError as e: logger.log('Unable to find useful show metadata for %s on %s: %s' % ( self.name, sickbeard.TVInfoAPI(show_obj.tvid).name, ex(e)), logger.WARNING) return result
def create_episode_metadata(self, ep_obj, force=False): # type: (sickbeard.tv.TVEpisode, bool) -> bool result = False if self.episode_metadata and ep_obj and (not self.has_episode_metadata(ep_obj) or force): logger.log('Metadata provider %s creating episode metadata for %s' % (self.name, ep_obj.pretty_name()), logger.DEBUG) try: result = self.write_ep_file(ep_obj) except BaseTVinfoError as e: logger.log('Unable to find useful episode metadata for %s on %s: %s' % ( self.name, sickbeard.TVInfoAPI(ep_obj.show_obj.tvid).name, ex(e)), logger.WARNING) return result
def __str__(self): if self.show_obj: prodid = self.show_obj.prodid tvid = self.show_obj.tvid elif self.parsed_show_obj: prodid = self.parsed_show_obj.prodid tvid = self.parsed_show_obj.tvid else: prodid = self.prodid tvid = self.tvid return '%s %s %sx%s of %s from %s' % ( self.date, self.name, self.season, self.episode, prodid, sickbeard.TVInfoAPI(tvid).name)
def _xem_exceptions_fetcher(): global xem_exception_dict xem_list = 'xem_us' for cur_show_obj in sickbeard.showList: if cur_show_obj.is_anime and not cur_show_obj.paused: xem_list = 'xem' break if should_refresh(xem_list): for tvid in [ i for i in sickbeard.TVInfoAPI().sources if 'xem_origin' in sickbeard.TVInfoAPI(i).config ]: logger.log(u'Checking for XEM scene exception updates for %s' % sickbeard.TVInfoAPI(tvid).name) url = 'http://thexem.de/map/allNames?origin=%s%s&seasonNumbers=1'\ % (sickbeard.TVInfoAPI(tvid).config['xem_origin'], ('&language=us', '')['xem' == xem_list]) parsed_json = helpers.get_url(url, parse_json=True, timeout=90) if not parsed_json: logger.log( u'Check scene exceptions update failed for %s, Unable to get URL: %s' % (sickbeard.TVInfoAPI(tvid).name, url), logger.ERROR) continue if 'failure' == parsed_json['result']: continue for prodid, names in iteritems(parsed_json['data']): try: xem_exception_dict[(tvid, int(prodid))] = names except (BaseException, Exception): continue set_last_refresh(xem_list) return xem_exception_dict
def _season_strings(self, ep_obj # type: TVEpisode ): # type: (...) -> List[Dict[AnyStr, List[AnyStr]]] """ :param ep_obj: episode object :return: """ search_params = [] base_params = {} # season ep_detail = None if ep_obj.show_obj.air_by_date or ep_obj.show_obj.is_sports: airdate = str(ep_obj.airdate).split('-')[0] base_params['season'] = airdate base_params['q'] = airdate if ep_obj.show_obj.air_by_date: ep_detail = '+"%s"' % airdate elif ep_obj.show_obj.is_anime: base_params['season'] = '%d' % ep_obj.scene_absolute_number else: base_params['season'] = str((ep_obj.season, ep_obj.scene_season)[bool(ep_obj.show_obj.is_scene)]) ep_detail = 'S%02d' % helpers.try_int(base_params['season'], 1) # id search params = base_params.copy() use_id = False for i in sickbeard.TVInfoAPI().all_sources: if i in ep_obj.show_obj.ids and 0 < ep_obj.show_obj.ids[i]['id'] and i in self.caps: params[self.caps[i]] = ep_obj.show_obj.ids[i]['id'] use_id = True use_id and search_params.append(params) spacer = 'nzbgeek.info' in self.url.lower() and ' ' or '.' # query search and exceptions name_exceptions = get_show_names(ep_obj, spacer) for cur_exception in name_exceptions: params = base_params.copy() if 'q' in params: params['q'] = '%s%s%s' % (cur_exception, spacer, params['q']) search_params.append(params) if ep_detail: params = base_params.copy() params['q'] = '%s%s%s' % (cur_exception, spacer, ep_detail) 'season' in params and params.pop('season') 'ep' in params and params.pop('ep') search_params.append(params) return [{'Season': search_params}]
def _valid_show(fetched_show_info, show_obj): # type: (Dict, sickbeard.tv.TVShow) -> bool """ Test the integrity of fetched show data :param fetched_show_info: the object returned from the tvinfo source :param show_obj: Show that the fetched data relates to :return: True if fetched_show_obj is valid data otherwise False """ if not (isinstance(fetched_show_info, dict) and isinstance(getattr(fetched_show_info, 'data', None), (list, dict)) and 'seriesname' in getattr(fetched_show_info, 'data', [])): logger.log(u'Show %s not found on %s ' % (show_obj.name, sickbeard.TVInfoAPI(show_obj.tvid).name), logger.WARNING) return False return True
def get_tvmaze_by_name(showname, premiere_date): """ :param showname: show name :type showname: AnyStr :param premiere_date: premiere date :type premiere_date: datetime.date :return: :rtype: Dict """ ids = {} try: url = '%ssearch/shows?%s' % ( sickbeard.TVInfoAPI(TVINFO_TVMAZE).config['base_url'], urlencode({'q': clean_show_name(showname)})) res = get_tvmaze_data(url=url, parse_json=True, raise_status_code=True, timeout=120) if res: for r in res: if 'show' in r and 'premiered' in r[ 'show'] and 'externals' in r['show']: premiered = parse(r['show']['premiered'], fuzzy=True) if abs(premiere_date - premiered.date()) < datetime.timedelta(days=2): ids[TVINFO_TVRAGE] = r['show']['externals'].get( 'tvrage', 0) ids[TVINFO_TVDB] = r['show']['externals'].get( 'thetvdb', 0) ids[TVINFO_IMDB] = try_int( str(r['show']['externals'].get('imdb')).replace( 'tt', '')) ids[TVINFO_TVMAZE] = r['show'].get('id', 0) break except (BaseException, Exception): pass return {k: v for k, v in iteritems(ids) if v not in (None, '', 0)}
def fix_xem_numbering(tvid, prodid): """ :param tvid: tvid :type tvid: int :param prodid: prodid :type prodid: int or long """ if None is prodid: return {} tvid, prodid = int(tvid), int(prodid) my_db = db.DBConnection() rows = my_db.select( 'SELECT season, episode, absolute_number, scene_season, scene_episode, scene_absolute_number' ' FROM tv_episodes' ' WHERE indexer = ? AND showid = ?', [tvid, prodid]) 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 logger.log( u'Fixing any XEM scene mapping issues for show %s on %s' % (prodid, sickbeard.TVInfoAPI(tvid).name), logger.DEBUG) cl = [] for row in rows: season = int(row['season']) episode = int(row['episode']) if not int(row['scene_season']) and last_scene_season: scene_season = last_scene_season + 1 update_scene_season = True else: scene_season = int(row['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['scene_episode']) and last_scene_episode: scene_episode = last_scene_episode + 1 update_scene_episode = True else: scene_episode = int(row['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['absolute_number']) and last_absolute_number: absolute_number = last_absolute_number + 1 update_absolute_number = True else: absolute_number = int(row['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['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['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 indexer = ? AND showid = ?' ' AND season = ? AND episode = ?', [absolute_number, tvid, prodid, season, episode] ]) update_absolute_number = False if update_scene_season: cl.append([ 'UPDATE tv_episodes' ' SET scene_season = ?' ' WHERE indexer = ? AND showid = ?' ' AND season = ? AND episode = ?', [scene_season, tvid, prodid, season, episode] ]) update_scene_season = False if update_scene_episode: cl.append([ 'UPDATE tv_episodes' ' SET scene_episode = ?' ' WHERE indexer = ? AND showid = ?' ' AND season = ? AND episode = ?', [scene_episode, tvid, prodid, season, episode] ]) update_scene_episode = False if update_scene_absolute_number: cl.append([ 'UPDATE tv_episodes' ' SET scene_absolute_number = ?' ' WHERE indexer = ? AND showid = ?' ' AND season = ? AND episode = ?', [scene_absolute_number, tvid, prodid, season, episode] ]) update_scene_absolute_number = False if 0 < len(cl): my_db = db.DBConnection() my_db.mass_action(cl)
def run(self): ShowQueueItem.run(self) logger.log('Starting to add show %s' % self.showDir) # make sure the TV info source IDs are valid try: tvinfo_config = sickbeard.TVInfoAPI(self.tvid).api_params.copy() if self.lang: tvinfo_config['language'] = self.lang logger.log(u'' + str(sickbeard.TVInfoAPI(self.tvid).name) + ': ' + repr(tvinfo_config)) t = sickbeard.TVInfoAPI(self.tvid).setup(**tvinfo_config) s = t[self.prodid, False] if getattr(t, 'show_not_found', False): logger.log( 'Show %s was not found on %s, maybe show was deleted' % (self.show_name, sickbeard.TVInfoAPI(self.tvid).name), logger.ERROR) self._finishEarly() return # this usually only happens if they have an NFO in their show dir # which gave us a TV info source ID that has no proper english version of the show if None is getattr(s, 'seriesname', None): logger.log( 'Show in %s has no name on %s, probably the wrong language used to search with.' % (self.showDir, sickbeard.TVInfoAPI(self.tvid).name), logger.ERROR) ui.notifications.error( 'Unable to add show', 'Show in %s has no name on %s, probably the wrong language.' ' Delete .nfo and add manually in the correct language.' % (self.showDir, sickbeard.TVInfoAPI(self.tvid).name)) self._finishEarly() return except (BaseException, Exception): logger.log( 'Unable to find show ID:%s on TV info: %s' % (self.prodid, sickbeard.TVInfoAPI(self.tvid).name), logger.ERROR) 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.TVInfoAPI( self.tvid).name, self.prodid)) self._finishEarly() return try: new_show_obj = TVShow(self.tvid, self.prodid, self.lang) new_show_obj.load_from_tvinfo() self.show_obj = new_show_obj # set up initial values self.show_obj.location = self.showDir self.show_obj.subtitles = self.subtitles if None is not self.subtitles else sickbeard.SUBTITLES_DEFAULT self.show_obj.quality = self.quality if self.quality else sickbeard.QUALITY_DEFAULT self.show_obj.upgrade_once = self.upgrade_once self.show_obj.flatten_folders = self.flatten_folders if None is not self.flatten_folders \ else sickbeard.FLATTEN_FOLDERS_DEFAULT self.show_obj.anime = self.anime if None is not self.anime else sickbeard.ANIME_DEFAULT self.show_obj.scene = self.scene if None is not self.scene else sickbeard.SCENE_DEFAULT self.show_obj.paused = self.paused if None is not self.paused else False self.show_obj.prune = self.prune if None is not self.prune else 0 self.show_obj.tag = self.tag if None is not self.tag else 'Show List' if self.show_obj.anime: self.show_obj.release_groups = BlackAndWhiteList( self.show_obj.tvid, self.show_obj.prodid, self.show_obj.tvid_prodid) if self.blacklist: self.show_obj.release_groups.set_black_keywords( self.blacklist) if self.whitelist: self.show_obj.release_groups.set_white_keywords( self.whitelist) # be smartish about this if self.show_obj.genre and 'talk show' in self.show_obj.genre.lower( ): self.show_obj.air_by_date = 1 if self.show_obj.genre and 'documentary' in self.show_obj.genre.lower( ): self.show_obj.air_by_date = 0 if self.show_obj.classification and 'sports' in self.show_obj.classification.lower( ): self.show_obj.sports = 1 except Exception as e: if check_exception_type(e, ExceptionTuples.tvinfo_exception): logger.log( 'Unable to add show due to an error with %s: %s' % (sickbeard.TVInfoAPI(self.tvid).name, ex(e)), logger.ERROR) if self.show_obj: ui.notifications.error( 'Unable to add %s due to an error with %s' % (self.show_obj.name, sickbeard.TVInfoAPI( self.tvid).name)) else: ui.notifications.error( 'Unable to add show due to an error with %s' % sickbeard.TVInfoAPI(self.tvid).name) self._finishEarly() return elif check_exception_type( e, exceptions_helper.MultipleShowObjectsException): logger.log( 'The show in %s is already in your show list, skipping' % self.showDir, logger.ERROR) ui.notifications.error( 'Show skipped', 'The show in %s is already in your show list' % self.showDir) self._finishEarly() return else: logger.log('Error trying to add show: %s' % ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.ERROR) self._finishEarly() raise self.show_obj.load_imdb_info() try: self.show_obj.save_to_db() except (BaseException, Exception) as e: logger.log('Error saving the show to the database: %s' % ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.ERROR) self._finishEarly() raise # add it to the show list sickbeard.showList.append(self.show_obj) try: self.show_obj.load_episodes_from_tvinfo() except (BaseException, Exception) as e: logger.log( 'Error with %s, not creating episode list: %s' % (sickbeard.TVInfoAPI(self.show_obj.tvid).name, ex(e)), logger.ERROR) logger.log(traceback.format_exc(), logger.ERROR) try: self.show_obj.load_episodes_from_dir() except (BaseException, Exception) as e: logger.log('Error searching directory for episodes: %s' % ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.ERROR) # if they gave a custom status then change all the eps to it my_db = db.DBConnection() if self.default_status != SKIPPED: logger.log( 'Setting all episodes to the specified default status: %s' % sickbeard.common.statusStrings[self.default_status]) my_db.action( 'UPDATE tv_episodes' ' SET status = ?' ' WHERE status = ?' ' AND indexer = ? AND showid = ?' ' AND season != 0', [ self.default_status, SKIPPED, self.show_obj.tvid, self.show_obj.prodid ]) items_wanted = self._get_wanted(my_db, self.default_wanted_begin, latest=False) items_wanted += self._get_wanted(my_db, self.default_wanted_latest, latest=True) self.show_obj.write_metadata() self.show_obj.update_metadata() self.show_obj.populate_cache() self.show_obj.flush_episodes() # load ids _ = self.show_obj.ids # if sickbeard.USE_TRAKT: # # if there are specific episodes that need to be added by trakt # sickbeard.traktCheckerScheduler.action.manageNewShow(self.show_obj) # # # add show to trakt.tv library # if sickbeard.TRAKT_SYNC: # sickbeard.traktCheckerScheduler.action.addShowToTraktLibrary(self.show_obj) # Load XEM data to DB for show sickbeard.scene_numbering.xem_refresh(self.show_obj.tvid, self.show_obj.prodid, force=True) if self.show_obj.scene: # enable/disable scene flag based on if show has an explicit _scene_ mapping at XEM self.show_obj.scene = sickbeard.scene_numbering.has_xem_scene_mapping( self.show_obj.tvid, self.show_obj.prodid) # if "scene" numbering is disabled during add show, output availability to log if None is not self.scene and not self.show_obj.scene and \ self.show_obj.prodid in sickbeard.scene_exceptions.xem_ids_list[self.show_obj.tvid]: logger.log( 'No scene number mappings found at TheXEM. Therefore, episode scene numbering disabled, ' 'edit show and enable it to manually add custom numbers for search and media processing.' ) try: self.show_obj.save_to_db() except (BaseException, Exception) as e: logger.log('Error saving the show to the database: %s' % ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.ERROR) self._finishEarly() raise # update internal name cache name_cache.buildNameCache(self.show_obj) self.show_obj.load_episodes_from_db() if self.show_obj.tvid in (TVINFO_TVRAGE, TVINFO_TVDB): # noinspection SqlResolve oh = my_db.select( 'SELECT resource FROM history WHERE indexer = 0 AND showid = ?', [self.show_obj.prodid]) if oh: found = False for o in oh: np = NameParser(file_name=True, indexer_lookup=False, try_scene_exceptions=True) try: pr = np.parse(o['resource']) except (BaseException, Exception): continue if pr.show_obj.tvid == self.show_obj.tvid and pr.show_obj.prodid == self.show_obj.prodid: found = True break if found: my_db.action( 'UPDATE history SET indexer = ? WHERE indexer = 0 AND showid = ?', [self.show_obj.tvid, self.show_obj.prodid]) msg = ' the specified show into ' + self.showDir # if started with WANTED eps then run the backlog if WANTED == self.default_status or items_wanted: logger.log( 'Launching backlog for this show since episodes are WANTED') sickbeard.backlogSearchScheduler.action.search_backlog( [self.show_obj]) ui.notifications.message( 'Show added/search', 'Adding and searching for episodes of' + msg) else: ui.notifications.message('Show added', 'Adding' + msg) self.finish()
def xem_refresh(tvid, prodid, force=False): """ Refresh data from xem for a tv show :param tvid: :type tvid: int :param prodid: :type prodid: int :param force: :type force: bool """ if None is prodid: return tvid, prodid = int(tvid), int(prodid) tvinfo = sickbeard.TVInfoAPI(tvid) if 'xem_origin' not in tvinfo.config or prodid not in xem_ids_list.get( tvid, []): return xem_origin = tvinfo.config['xem_origin'] # XEM API URL url = 'http://thexem.de/map/all?id=%s&origin=%s&destination=scene' % ( prodid, xem_origin) max_refresh_age_secs = 86400 # 1 day my_db = db.DBConnection() rows = my_db.select( 'SELECT last_refreshed' ' FROM xem_refresh' ' WHERE indexer = ? AND indexer_id = ?', [tvid, prodid]) if rows: last_refresh = int(rows[0]['last_refreshed']) refresh = int(time.mktime(datetime.datetime.today().timetuple()) ) > last_refresh + max_refresh_age_secs else: refresh = True if refresh or force: logger.log( u'Looking up XEM scene mapping for show %s on %s' % (prodid, tvinfo.name), logger.DEBUG) # mark refreshed my_db.upsert( 'xem_refresh', dict(last_refreshed=int( time.mktime(datetime.datetime.today().timetuple()))), dict(indexer=tvid, indexer_id=prodid)) try: parsed_json = sickbeard.helpers.get_url(url, parse_json=True, timeout=90) if not parsed_json or '' == parsed_json: logger.log( u'No XEM data for show %s on %s' % (prodid, tvinfo.name), logger.MESSAGE) return if 'success' in parsed_json['result']: cl = map_list( lambda entry: [ 'UPDATE tv_episodes' ' SET scene_season = ?, scene_episode = ?, scene_absolute_number = ?' ' WHERE indexer = ? AND showid = ?' ' AND season = ? AND episode = ?', [ entry.get('scene%s' % ('', '_2')['scene_2' in entry]).get(v) for v in ('season', 'episode', 'absolute') ] + [tvid, prodid] + [ entry.get(xem_origin).get(v) for v in ('season', 'episode') ] ], filter_iter(lambda x: 'scene' in x, parsed_json['data'])) if 0 < len(cl): my_db = db.DBConnection() my_db.mass_action(cl) else: logger.log( u'Empty lookup result - no XEM data for show %s on %s' % (prodid, tvinfo.name), logger.DEBUG) except (BaseException, Exception) as e: logger.log( u'Exception refreshing XEM data for show ' + str(prodid) + ' on ' + tvinfo.name + ': ' + ex(e), logger.WARNING) logger.log(traceback.format_exc(), logger.ERROR)
def _show_data(self, show_obj): # type: (sickbeard.tv.TVShow) -> Optional[Union[bool, etree.Element]] """ Creates an elementTree XML structure for a Kodi-style tvshow.nfo and returns the resulting data object. show_obj: a TVShow instance to create the NFO for """ show_ID = show_obj.prodid show_lang = show_obj.lang tvinfo_config = sickbeard.TVInfoAPI(show_obj.tvid).api_params.copy() tvinfo_config['actors'] = True if show_lang and not 'en' == show_lang: tvinfo_config['language'] = show_lang if 0 != show_obj.dvdorder: tvinfo_config['dvdorder'] = True t = sickbeard.TVInfoAPI(show_obj.tvid).setup(**tvinfo_config) tv_node = etree.Element('tvshow') try: show_info = t[int(show_ID)] except BaseTVinfoShownotfound as e: logger.log( 'Unable to find show with id %s on %s, skipping it' % (show_ID, sickbeard.TVInfoAPI(show_obj.tvid).name), logger.ERROR) raise e except BaseTVinfoError as e: logger.log( '%s is down, can\'t use its data to add this show' % sickbeard.TVInfoAPI(show_obj.tvid).name, logger.ERROR) raise e if not self._valid_show(show_info, show_obj): return # check for title and id if None is getattr(show_info, 'seriesname', None) or None is getattr( show_info, 'id', None): logger.log( 'Incomplete info for show with id %s on %s, skipping it' % (show_ID, sickbeard.TVInfoAPI(show_obj.tvid).name), logger.ERROR) return False title = etree.SubElement(tv_node, 'title') if None is not getattr(show_info, 'seriesname', None): title.text = '%s' % show_info['seriesname'] # year = etree.SubElement(tv_node, 'year') premiered = etree.SubElement(tv_node, 'premiered') premiered_text = self.get_show_year(show_obj, show_info, year_only=False) if premiered_text: premiered.text = '%s' % premiered_text has_id = False tvdb_id = None for tvid, slug in map_iter( lambda _tvid: (_tvid, sickbeard.TVInfoAPI(_tvid).config.get('kodi_slug')), list(sickbeard.TVInfoAPI().all_sources)): mid = slug and show_obj.ids[tvid].get('id') if mid: has_id = True kwargs = dict(type=slug) if TVINFO_TVDB == tvid: kwargs.update(dict(default='true')) tvdb_id = str(mid) uniqueid = etree.SubElement(tv_node, 'uniqueid', **kwargs) uniqueid.text = '%s%s' % (('', 'tt')[TVINFO_IMDB == tvid], mid) if not has_id: logger.log( 'Incomplete info for show with id %s on %s, skipping it' % (show_ID, sickbeard.TVInfoAPI(show_obj.tvid).name), logger.ERROR) return False ratings = etree.SubElement(tv_node, 'ratings') if None is not getattr(show_info, 'rating', None): # todo: name dynamic depending on source rating = etree.SubElement(ratings, 'rating', name='thetvdb', max='10') rating_value = etree.SubElement(rating, 'value') rating_value.text = '%s' % show_info['rating'] if None is not getattr(show_info, 'siteratingcount', None): ratings_votes = etree.SubElement(rating, 'votes') ratings_votes.text = '%s' % show_info['siteratingcount'] plot = etree.SubElement(tv_node, 'plot') if None is not getattr(show_info, 'overview', None): plot.text = '%s' % show_info['overview'] episodeguide = etree.SubElement(tv_node, 'episodeguide') episodeguideurl = etree.SubElement(episodeguide, 'url', post='yes', cache='auth.json') if tvdb_id: episodeguideurl.text = sickbeard.TVInfoAPI( TVINFO_TVDB).config['epg_url'].replace('{MID}', tvdb_id) mpaa = etree.SubElement(tv_node, 'mpaa') if None is not getattr(show_info, 'contentrating', None): mpaa.text = '%s' % show_info['contentrating'] genre = etree.SubElement(tv_node, 'genre') if None is not getattr(show_info, 'genre', None): if isinstance(show_info['genre'], string_types): genre.text = ' / '.join([ x.strip() for x in show_info['genre'].split('|') if x.strip() ]) studio = etree.SubElement(tv_node, 'studio') if None is not getattr(show_info, 'network', None): studio.text = '%s' % show_info['network'] self.add_actor_element(show_info, etree, tv_node) # Make it purdy sg_helpers.indent_xml(tv_node) # output valid xml # data = etree.ElementTree(tv_node) # output non valid xml that Kodi accepts data = decode_str(etree.tostring(tv_node)) parts = data.split('episodeguide') if 3 == len(parts): data = 'episodeguide'.join( [parts[0], parts[1].replace('&quot;', '"'), parts[2]]) return data
def run(self): ShowQueueItem.run(self) last_update = datetime.date.fromordinal( self.show_obj.last_update_indexer) if not sickbeard.TVInfoAPI(self.show_obj.tvid).config['active']: logger.log( 'TV info source %s is marked inactive, aborting update for show %s and continue with refresh.' % (sickbeard.TVInfoAPI( self.show_obj.tvid).config['name'], self.show_obj.name)) sickbeard.showQueueScheduler.action.refreshShow( self.show_obj, self.force, self.scheduled_update, after_update=True) return logger.log('Beginning update of %s' % self.show_obj.name) logger.log( 'Retrieving show info from %s' % sickbeard.TVInfoAPI(self.show_obj.tvid).name, logger.DEBUG) try: result = self.show_obj.load_from_tvinfo(cache=not self.force) if None is not result: return except Exception as e: if check_exception_type(e, ExceptionTuples.tvinfo_error): logger.log( 'Unable to contact %s, aborting: %s' % (sickbeard.TVInfoAPI(self.show_obj.tvid).name, ex(e)), logger.WARNING) return elif check_exception_type( e, ExceptionTuples.tvinfo_attributenotfound): logger.log( 'Data retrieved from %s was incomplete, aborting: %s' % (sickbeard.TVInfoAPI(self.show_obj.tvid).name, ex(e)), logger.ERROR) return else: raise e if self.force_web: self.show_obj.load_imdb_info() try: self.show_obj.save_to_db() except (BaseException, Exception) as e: logger.log('Error saving the show to the database: %s' % ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.ERROR) # get episode list from DB logger.log('Loading all episodes from the database', logger.DEBUG) db_ep_obj_list = self.show_obj.load_episodes_from_db(update=True) # get episode list from TVDB logger.log( 'Loading all episodes from %s' % sickbeard.TVInfoAPI(self.show_obj.tvid).name, logger.DEBUG) try: tvinfo_ep_list = self.show_obj.load_episodes_from_tvinfo( cache=not self.force, update=True) except Exception as e: if check_exception_type(e, ExceptionTuples.tvinfo_exception): logger.log( 'Unable to get info from %s, the show info will not be refreshed: %s' % (sickbeard.TVInfoAPI(self.show_obj.tvid).name, ex(e)), logger.ERROR) tvinfo_ep_list = None else: raise e if None is tvinfo_ep_list: logger.log( 'No data returned from %s, unable to update episodes for show: %s' % (sickbeard.TVInfoAPI( self.show_obj.tvid).name, self.show_obj.name), logger.ERROR) elif not tvinfo_ep_list or 0 == len(tvinfo_ep_list): logger.log( 'No episodes returned from %s for show: %s' % (sickbeard.TVInfoAPI( self.show_obj.tvid).name, self.show_obj.name), logger.WARNING) else: # for each ep we found on TVDB delete it from the DB list for cur_season in tvinfo_ep_list: for cur_episode in tvinfo_ep_list[cur_season]: logger.log( 'Removing %sx%s from the DB list' % (cur_season, cur_episode), logger.DEBUG) if cur_season in db_ep_obj_list and cur_episode in db_ep_obj_list[ cur_season]: del db_ep_obj_list[cur_season][cur_episode] # for the remaining episodes in the DB list just delete them from the DB for cur_season in db_ep_obj_list: for cur_episode in db_ep_obj_list[cur_season]: ep_obj = self.show_obj.get_episode(cur_season, cur_episode) status = sickbeard.common.Quality.splitCompositeStatus( ep_obj.status)[0] if should_delete_episode(status): logger.log( 'Permanently deleting episode %sx%s from the database' % (cur_season, cur_episode), logger.MESSAGE) try: ep_obj.delete_episode() except exceptions_helper.EpisodeDeletedException: pass else: logger.log( 'Not deleting episode %sx%s from the database because status is: %s' % (cur_season, cur_episode, statusStrings[status]), logger.MESSAGE) # update indexer mapper once a month (using the day of the first ep as random date) update_over_month = (datetime.date.today() - last_update).days > 31 if (self.scheduled_update or update_over_month) and tvinfo_ep_list.get(1, {}).get( 1, False): first_ep_airdate = self.show_obj.get_episode( 1, 1, no_create=True).airdate day = (first_ep_airdate.day, 28)[28 < first_ep_airdate.day] if datetime.date.today().day == day or update_over_month or \ -8 < (datetime.date.today() - first_ep_airdate).days < 31: map_indexers_to_show(self.show_obj, force=True) if self.priority != generic_queue.QueuePriorities.NORMAL: self.kwargs['priority'] = self.priority sickbeard.showQueueScheduler.action.refreshShow( self.show_obj, self.force, self.scheduled_update, after_update=True, force_image_cache=self.force_web, **self.kwargs)
def _parse_string(self, name): # type: (AnyStr) -> Optional[ParseResult] """ :param name: name to parse :type name: AnyStr :return: :rtype: ParseResult or None """ if not name: return matches = [] initial_best_result = None for reg_ex in self.compiled_regexes: for (cur_regex_num, cur_regex_name, cur_regex) in self.compiled_regexes[reg_ex]: new_name = helpers.remove_non_release_groups(name, 'anime' in cur_regex_name) match = cur_regex.match(new_name) if not match: continue if 'garbage_name' == cur_regex_name: return result = ParseResult(new_name) result.which_regex = [cur_regex_name] result.score = 0 - cur_regex_num named_groups = list_keys(match.groupdict()) 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) name_parts = re.match(r'(?i)(.*)[ -]((?:part|pt)[ -]?\w+)$', result.series_name) try: result.series_name = name_parts.group(1) result.extra_info = name_parts.group(2) except (AttributeError, IndexError): pass result.score += 1 if 'anime' in cur_regex_name and not (self.show_obj and self.show_obj.is_anime): p_show_obj = helpers.get_show(result.series_name, True) if p_show_obj and self.show_obj and not (p_show_obj.tvid == self.show_obj.tvid and p_show_obj.prodid == self.show_obj.prodid): p_show_obj = None if not p_show_obj and self.show_obj: p_show_obj = self.show_obj if p_show_obj and not p_show_obj.is_anime: continue 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 'bare' == cur_regex_name and tmp_season in (19, 20): continue result.season_number = tmp_season result.score += 1 def _process_epnum(captures, capture_names, grp_name, extra_grp_name, ep_numbers, parse_result): ep_num = self._convert_number(captures.group(grp_name)) extra_grp_name = 'extra_%s' % extra_grp_name ep_numbers = '%sepisode_numbers' % ep_numbers if extra_grp_name in capture_names and captures.group(extra_grp_name): try: if hasattr(self.show_obj, 'get_episode'): _ep_obj = self.show_obj.get_episode(parse_result.season_number, ep_num) else: tmp_show_obj = helpers.get_show(parse_result.series_name, True) if tmp_show_obj and hasattr(tmp_show_obj, 'get_episode'): _ep_obj = tmp_show_obj.get_episode(parse_result.season_number, ep_num) else: _ep_obj = None except (BaseException, Exception): _ep_obj = None en = _ep_obj and _ep_obj.name and re.match(r'^\W*(\d+)', _ep_obj.name) or None es = en and en.group(1) or None extra_ep_num = self._convert_number(captures.group(extra_grp_name)) parse_result.__dict__[ep_numbers] = list_range(ep_num, extra_ep_num + 1) if not ( _ep_obj and es and es != captures.group(extra_grp_name)) and ( 0 < extra_ep_num - ep_num < 10) else [ep_num] parse_result.score += 1 else: parse_result.__dict__[ep_numbers] = [ep_num] parse_result.score += 1 return parse_result if 'ep_num' in named_groups: result = _process_epnum(match, named_groups, 'ep_num', 'ep_num', '', result) if 'ep_ab_num' in named_groups: result = _process_epnum(match, named_groups, 'ep_ab_num', 'ab_ep_num', 'ab_', result) if 'air_year' in named_groups and 'air_month' in named_groups and 'air_day' in named_groups: year = int(match.group('air_year')) try: month = int(match.group('air_month')) except ValueError: try: month = time.strptime(match.group('air_month')[0:3], '%b').tm_mon except ValueError as e: raise InvalidNameException(ex(e)) day = int(match.group('air_day')) # make an attempt to detect YYYY-DD-MM formats if 12 < month: tmp_month = month month = day day = tmp_month try: result.air_date = datetime.date( year + ((1900, 2000)[0 < year < 28], 0)[1900 < year], month, day) except ValueError as e: raise InvalidNameException(ex(e)) 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 'season_only' == cur_regex_name and re.search( r'([. _-]|^)(special|extra)s?\w*([. _-]|$)', tmp_extra_info, re.I): continue if tmp_extra_info: if result.extra_info: tmp_extra_info = '%s %s' % (result.extra_info, tmp_extra_info) 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 = helpers.try_int(version) else: result.version = 1 else: result.version = -1 if None is result.season_number and result.episode_numbers and not result.air_date and \ cur_regex_name in ['no_season', 'no_season_general', 'no_season_multi_ep'] and \ re.search(r'(?i)\bpart.?\d{1,2}\b', result.original_name): result.season_number = 1 matches.append(result) if len(matches): # pick best match with highest score based on placement best_result = max(sorted(matches, reverse=True, key=lambda x: x.which_regex), key=lambda x: x.score) show_obj = None if not self.naming_pattern: # try and create a show object for this result show_obj = helpers.get_show(best_result.series_name, self.try_scene_exceptions) # confirm passed in show object tvid_prodid matches result show object tvid_prodid if show_obj and not self.testing: if self.show_obj and show_obj.tvid_prodid != self.show_obj.tvid_prodid: show_obj = None elif not show_obj and self.show_obj: show_obj = self.show_obj best_result.show_obj = show_obj if not best_result.series_name and getattr(show_obj, 'name', None): best_result.series_name = show_obj.name if show_obj and show_obj.is_anime and 1 < len(self.compiled_regexes[1]) and 1 != reg_ex: continue # if this is a naming pattern test then return best result if not show_obj or self.naming_pattern: if not show_obj and not self.naming_pattern and not self.testing: # ensure anime regex test but use initial best if show still not found if 0 == reg_ex: initial_best_result = best_result matches = [] # clear non-anime match scores continue return initial_best_result return best_result # get quality new_name = helpers.remove_non_release_groups(name, show_obj.is_anime) best_result.quality = common.Quality.nameQuality(new_name, show_obj.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 best_result.is_air_by_date: season_number, episode_numbers = None, [] airdate = best_result.air_date.toordinal() my_db = db.DBConnection() sql_result = my_db.select( 'SELECT season, episode, name' ' FROM tv_episodes' ' WHERE indexer = ? AND showid = ?' ' AND airdate = ?', [show_obj.tvid, show_obj.prodid, airdate]) if sql_result: season_number = int(sql_result[0]['season']) episode_numbers = [int(sql_result[0]['episode'])] if 1 < len(sql_result): # multi-eps broadcast on this day nums = {'1': 'one', '2': 'two', '3': 'three', '4': 'four', '5': 'five', '6': 'six', '7': 'seven', '8': 'eight', '9': 'nine', '10': 'ten'} patt = '(?i)(?:e(?:p(?:isode)?)?|part|pt)[. _-]?(%s)' try: src_num = str(re.findall(patt % r'\w+', best_result.extra_info)[0]) alt_num = nums.get(src_num) or list(iterkeys(nums))[ list(itervalues(nums)).index(src_num)] re_partnum = re.compile(patt % ('%s|%s' % (src_num, alt_num))) for ep_details in sql_result: if re_partnum.search(ep_details['name']): season_number = int(ep_details['season']) episode_numbers = [int(ep_details['episode'])] break except (BaseException, Exception): pass if self.indexer_lookup and not season_number or not len(episode_numbers): try: tvinfo_config = sickbeard.TVInfoAPI(show_obj.tvid).api_params.copy() if show_obj.lang: tvinfo_config['language'] = show_obj.lang t = sickbeard.TVInfoAPI(show_obj.tvid).setup(**tvinfo_config) ep_obj = t[show_obj.prodid].aired_on(best_result.air_date)[0] season_number = int(ep_obj['seasonnumber']) episode_numbers = [int(ep_obj['episodenumber'])] except BaseTVinfoEpisodenotfound as e: logger.log(u'Unable to find episode with date ' + str(best_result.air_date) + ' for show ' + show_obj.name + ', skipping', logger.WARNING) episode_numbers = [] except BaseTVinfoError as e: logger.log(u'Unable to contact ' + sickbeard.TVInfoAPI(show_obj.tvid).name + ': ' + ex(e), logger.WARNING) episode_numbers = [] for epNo in episode_numbers: s = season_number e = epNo if self.convert and show_obj.is_scene: (s, e) = scene_numbering.get_indexer_numbering( show_obj.tvid, show_obj.prodid, season_number, epNo) new_episode_numbers.append(e) new_season_numbers.append(s) elif show_obj.is_anime and len(best_result.ab_episode_numbers) and not self.testing: scene_season = scene_exceptions.get_scene_exception_by_name(best_result.series_name)[2] for epAbsNo in best_result.ab_episode_numbers: a = epAbsNo if self.convert and show_obj.is_scene: a = scene_numbering.get_indexer_absolute_numbering( show_obj.tvid, show_obj.prodid, epAbsNo, True, scene_season) (s, e) = helpers.get_all_episodes_from_absolute_number(show_obj, [a]) new_absolute_numbers.append(a) new_episode_numbers.extend(e) new_season_numbers.append(s) elif best_result.season_number and len(best_result.episode_numbers) and not self.testing: for epNo in best_result.episode_numbers: s = best_result.season_number e = epNo if self.convert and show_obj.is_scene: (s, e) = scene_numbering.get_indexer_numbering( show_obj.tvid, show_obj.prodid, best_result.season_number, epNo) if show_obj.is_anime: a = helpers.get_absolute_number_from_season_and_episode(show_obj, 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 here. It's possible that we now have episodes # from more than one season (by tvdb numbering), and this is just too much, so flag it. new_season_numbers = list(set(new_season_numbers)) # remove duplicates if 1 < len(new_season_numbers): raise InvalidNameException('Scene numbering results episodes from ' 'seasons %s, (i.e. more than one) and ' 'SickGear does not support this. ' 'Sorry.' % (str(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): best_result.ab_episode_numbers = new_absolute_numbers if len(new_season_numbers) and len(new_episode_numbers): best_result.episode_numbers = new_episode_numbers best_result.season_number = new_season_numbers[0] if self.convert and show_obj.is_scene: logger.log(u'Converted parsed result %s into %s' % (best_result.original_name, decode_str(str(best_result), errors='xmlcharrefreplace')), logger.DEBUG) helpers.cpu_sleep() return best_result
def _ep_data(self, ep_obj): # type: (sickbeard.tv.TVEpisode) -> Optional[Union[bool, AnyStr]] """ 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 show_info. The key values for the tivo metadata file are from: http://pytivo.sourceforge.net/wiki/index.php/Metadata """ data = '' ep_obj_list_to_write = [ep_obj] + ep_obj.related_ep_obj show_lang = ep_obj.show_obj.lang try: tvinfo_config = sickbeard.TVInfoAPI( ep_obj.show_obj.tvid).api_params.copy() tvinfo_config['actors'] = True if show_lang and not 'en' == show_lang: tvinfo_config['language'] = show_lang if 0 != ep_obj.show_obj.dvdorder: tvinfo_config['dvdorder'] = True t = sickbeard.TVInfoAPI( ep_obj.show_obj.tvid).setup(**tvinfo_config) show_info = t[ep_obj.show_obj.prodid] except BaseTVinfoShownotfound as e: raise exceptions_helper.ShowNotFoundException(ex(e)) except BaseTVinfoError as e: logger.log( "Unable to connect to %s while creating meta files - skipping - %s" % (sickbeard.TVInfoAPI(ep_obj.show_obj.tvid).name, ex(e)), logger.ERROR) return False if not self._valid_show(show_info, ep_obj.show_obj): return for cur_ep_obj in ep_obj_list_to_write: try: ep_info = show_info[cur_ep_obj.season][cur_ep_obj.episode] except (BaseException, Exception): logger.log( "Unable to find episode %sx%s on %s... has it been removed? Should I delete from db?" % (cur_ep_obj.season, cur_ep_obj.episode, sickbeard.TVInfoAPI(ep_obj.show_obj.tvid).name)) return None if None is getattr(ep_info, 'firstaired', None) and 0 == ep_obj.season: ep_info["firstaired"] = str(datetime.date.fromordinal(1)) if None is getattr(ep_info, 'episodename', None) or None is getattr( ep_info, 'firstaired', None): return None if None is not getattr(show_info, 'seriesname', None): data += ("title : " + show_info["seriesname"] + "\n") data += ("seriesTitle : " + show_info["seriesname"] + "\n") data += ("episodeTitle : " + cur_ep_obj._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(cur_ep_obj.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 = cur_ep_obj.description # Replace double curly quotes sanitizedDescription = sanitizedDescription.replace( u"\u201c", "\"").replace(u"\u201d", "\"") # Replace single curly quotes sanitizedDescription = sanitizedDescription.replace( u"\u2018", "'").replace(u"\u2019", "'").replace(u"\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 None is not getattr(show_info, 'zap2it_id', None): data += ("seriesId : " + show_info["zap2it_id"] + "\n") # This is the call sign of the channel the episode was recorded from. if None is not getattr(show_info, 'network', None): data += ("callsign : " + show_info["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 cur_ep_obj.airdate != datetime.date.fromordinal(1): data += ("originalAirDate : " + str(cur_ep_obj.airdate) + "T00:00:00Z\n") # This shows up at the beginning of the description on the Program screen and on the Details screen. for actor in getattr(show_info, 'actors', []): data += ('vActor : %s\n' % actor['character']['name'] and actor['person']['name'] and actor['character']['name'] != actor['person']['name'] and '%s (%s)' % (actor['character']['name'], actor['person']['name']) or actor['person']['name'] or actor['character']['name']) # This is shown on both the Program screen and the Details screen. if None is not getattr(ep_info, 'rating', None): try: rating = float(ep_info['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 None is not getattr(show_info, 'contentrating', None): data += ("tvRating : " + str(show_info["contentrating"]) + "\n") # This field can be repeated as many times as necessary or omitted completely. if ep_obj.show_obj.genre: for genre in ep_obj.show_obj.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 _ep_data(self, ep_obj): # type: (sickbeard.tv.TVEpisode) -> Optional[Union[bool, etree.Element]] """ 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 """ ep_obj_list_to_write = [ep_obj] + ep_obj.related_ep_obj show_lang = ep_obj.show_obj.lang try: tvinfo_config = sickbeard.TVInfoAPI( ep_obj.show_obj.tvid).api_params.copy() tvinfo_config['actors'] = True if show_lang and not 'en' == show_lang: tvinfo_config['language'] = show_lang if 0 != ep_obj.show_obj.dvdorder: tvinfo_config['dvdorder'] = True t = sickbeard.TVInfoAPI( ep_obj.show_obj.tvid).setup(**tvinfo_config) show_info = t[ep_obj.show_obj.prodid] except BaseTVinfoShownotfound as e: raise exceptions_helper.ShowNotFoundException(ex(e)) except BaseTVinfoError as e: logger.log( "Unable to connect to %s while creating meta files - skipping - %s" % (sickbeard.TVInfoAPI(ep_obj.show_obj.tvid).name, ex(e)), logger.ERROR) return False if not self._valid_show(show_info, ep_obj.show_obj): return rootNode = etree.Element("details") data = None # write an WDTV XML containing info for all matching episodes for cur_ep_obj in ep_obj_list_to_write: try: ep_info = show_info[cur_ep_obj.season][cur_ep_obj.episode] except (BaseException, Exception): logger.log( "Unable to find episode %sx%s on %s... has it been removed? Should I delete from db?" % (cur_ep_obj.season, cur_ep_obj.episode, sickbeard.TVInfoAPI(ep_obj.show_obj.tvid).name)) return None if None is getattr(ep_info, 'firstaired', None) and 0 == ep_obj.season: ep_info["firstaired"] = str(datetime.date.fromordinal(1)) if None is getattr(ep_info, 'episodename', None) or None is getattr( ep_info, 'firstaired', None): return None if 1 < len(ep_obj_list_to_write): episode = etree.SubElement(rootNode, "details") else: episode = rootNode episodeID = etree.SubElement(episode, "id") episodeID.text = str(cur_ep_obj.epid) title = etree.SubElement(episode, "title") title.text = '%s' % ep_obj.pretty_name() seriesName = etree.SubElement(episode, "series_name") if None is not getattr(show_info, 'seriesname', None): seriesName.text = '%s' % show_info["seriesname"] episodeName = etree.SubElement(episode, "episode_name") if None is not cur_ep_obj.name: episodeName.text = '%s' % cur_ep_obj.name seasonNumber = etree.SubElement(episode, "season_number") seasonNumber.text = str(cur_ep_obj.season) episodeNum = etree.SubElement(episode, "episode_number") episodeNum.text = str(cur_ep_obj.episode) firstAired = etree.SubElement(episode, "firstaired") if cur_ep_obj.airdate != datetime.date.fromordinal(1): firstAired.text = str(cur_ep_obj.airdate) year = etree.SubElement(episode, "year") year_text = self.get_show_year(ep_obj.show_obj, show_info) if year_text: year.text = '%s' % year_text runtime = etree.SubElement(episode, "runtime") if 0 != cur_ep_obj.season: if None is not getattr(show_info, 'runtime', None): runtime.text = '%s' % show_info["runtime"] genre = etree.SubElement(episode, "genre") if None is not getattr(show_info, 'genre', None): genre.text = " / ".join( [x for x in show_info["genre"].split('|') if x]) director = etree.SubElement(episode, "director") director_text = getattr(ep_info, 'director', None) if None is not director_text: director.text = '%s' % director_text for actor in getattr(show_info, 'actors', []): cur_actor = etree.SubElement(episode, 'actor') cur_actor_name = etree.SubElement(cur_actor, 'name') cur_actor_name.text = '%s' % actor['person']['name'] cur_actor_role = etree.SubElement(cur_actor, 'role') cur_actor_role_text = '%s' % actor['character']['name'] if cur_actor_role_text: cur_actor_role.text = '%s' % cur_actor_role_text overview = etree.SubElement(episode, "overview") if None is not cur_ep_obj.description: overview.text = '%s' % cur_ep_obj.description # Make it purdy sg_helpers.indent_xml(rootNode) data = etree.ElementTree(rootNode) return data
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. """ global exception_dict, anidb_exception_dict, xem_exception_dict # exceptions are stored on github pages for tvid in sickbeard.TVInfoAPI().sources: if should_refresh(sickbeard.TVInfoAPI(tvid).name): logger.log(u'Checking for scene exception updates for %s' % sickbeard.TVInfoAPI(tvid).name) url = sickbeard.TVInfoAPI(tvid).config['scene_url'] url_data = helpers.get_url(url) if None is url_data: # When None is urlData, trouble connecting to github logger.log( u'Check scene exceptions update failed. Unable to get URL: %s' % url, logger.ERROR) continue else: set_last_refresh(sickbeard.TVInfoAPI(tvid).name) # each exception is on one line with the format indexer_id: 'show name 1', 'show name 2', etc for cur_line in url_data.splitlines(): cur_line = cur_line prodid, sep, aliases = cur_line.partition(':') if not aliases: continue prodid = int(prodid) # 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[(tvid, prodid)] = alias_list del alias_list del url_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] changed_exceptions = False # write all the exceptions we got off the net into the database my_db = db.DBConnection() cl = [] for cur_tvid_prodid in exception_dict: # get a list of the existing exceptions for this ID existing_exceptions = [ x['show_name'] for x in my_db.select( 'SELECT show_name' ' FROM scene_exceptions' ' WHERE indexer = ? AND indexer_id = ?', list(cur_tvid_prodid)) ] if cur_tvid_prodid not in exception_dict: continue for cur_exception_dict in exception_dict[cur_tvid_prodid]: try: cur_exception, cur_season = next(iteritems(cur_exception_dict)) except (BaseException, Exception): logger.log('scene exception error', logger.ERROR) logger.log(traceback.format_exc(), logger.ERROR) continue # if this exception isn't already in the DB then add it if cur_exception not in existing_exceptions: if PY2 and not isinstance(cur_exception, text_type): cur_exception = text_type(cur_exception, 'utf-8', 'replace') cl.append([ 'INSERT INTO scene_exceptions' ' (indexer, indexer_id, show_name, season) VALUES (?,?,?,?)', list(cur_tvid_prodid) + [cur_exception, cur_season] ]) changed_exceptions = True if cl: my_db.mass_action(cl) # since this could invalidate the results of the cache we clear it out after updating if changed_exceptions: logger.log(u'Updated scene exceptions') else: logger.log(u'No scene exceptions update needed') # cleanup exception_dict.clear() anidb_exception_dict.clear() xem_exception_dict.clear()
def _show_data(self, show_obj): # type: (sickbeard.tv.TVShow) -> Optional[Union[bool, etree.Element]] """ 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 """ show_lang = show_obj.lang tvinfo_config = sickbeard.TVInfoAPI(show_obj.tvid).api_params.copy() tvinfo_config['actors'] = True if show_lang and not 'en' == show_lang: tvinfo_config['language'] = show_lang if 0 != show_obj.dvdorder: tvinfo_config['dvdorder'] = True t = sickbeard.TVInfoAPI(show_obj.tvid).setup(**tvinfo_config) rootNode = etree.Element('details') tv_node = etree.SubElement(rootNode, 'movie') tv_node.attrib['isExtra'] = 'false' tv_node.attrib['isSet'] = 'false' tv_node.attrib['isTV'] = 'true' try: show_info = t[int(show_obj.prodid)] except BaseTVinfoShownotfound as e: logger.log( u'Unable to find show with id ' + str(show_obj.prodid) + ' on tvdb, skipping it', logger.ERROR) raise e except BaseTVinfoError as e: logger.log(u'TVDB is down, can\'t use its data to make the NFO', logger.ERROR) raise e if not self._valid_show(show_info, show_obj): return # check for title and id try: if None is show_info['seriesname'] \ or '' == show_info['seriesname'] \ or None is show_info['id'] \ or '' == show_info['id']: logger.log( 'Incomplete info for show with id %s on %s, skipping it' % (show_obj.prodid, sickbeard.TVInfoAPI(show_obj.tvid).name), logger.ERROR) return False except BaseTVinfoAttributenotfound: logger.log( 'Incomplete info for show with id %s on %s, skipping it' % (show_obj.prodid, sickbeard.TVInfoAPI(show_obj.tvid).name), logger.ERROR) return False SeriesName = etree.SubElement(tv_node, 'title') if None is not show_info['seriesname']: SeriesName.text = '%s' % show_info['seriesname'] else: SeriesName.text = '' Genres = etree.SubElement(tv_node, 'genres') if None is not show_info['genre']: for genre in show_info['genre'].split('|'): if genre and genre.strip(): cur_genre = etree.SubElement(Genres, 'Genre') cur_genre.text = '%s' % genre.strip() FirstAired = etree.SubElement(tv_node, 'premiered') if None is not show_info['firstaired']: FirstAired.text = '%s' % show_info['firstaired'] year = etree.SubElement(tv_node, 'year') year_text = self.get_show_year(show_obj, show_info) if year_text: year.text = '%s' % year_text if None is not show_info['rating']: try: rating = int((float(show_info['rating']) * 10)) except ValueError: rating = 0 Rating = etree.SubElement(tv_node, 'rating') rating_text = str(rating) if None is not rating_text: Rating.text = '%s' % rating_text Status = etree.SubElement(tv_node, 'status') if None is not show_info['status']: Status.text = '%s' % show_info['status'] mpaa = etree.SubElement(tv_node, 'mpaa') if None is not show_info['contentrating']: mpaa.text = '%s' % show_info['contentrating'] IMDB_ID = etree.SubElement(tv_node, 'id') if None is not show_info['imdb_id']: IMDB_ID.attrib['moviedb'] = 'imdb' IMDB_ID.text = '%s' % show_info['imdb_id'] prodid = etree.SubElement(tv_node, 'indexerid') if None is not show_info['id']: prodid.text = '%s' % show_info['id'] Runtime = etree.SubElement(tv_node, 'runtime') if None is not show_info['runtime']: Runtime.text = '%s' % show_info['runtime'] cast = etree.SubElement(tv_node, 'cast') self.add_actor_element(show_info, etree, cast) sg_helpers.indent_xml(rootNode) data = etree.ElementTree(rootNode) return data
def _get_proper_list(aired_since_shows, # type: datetime.datetime recent_shows, # type: List[Tuple[int, int]] recent_anime, # type: List[Tuple[int, int]] proper_dict=None # type: Dict[AnyStr, List[Proper]] ): # type: (...) -> List[Proper] """ :param aired_since_shows: date since aired :param recent_shows: list of recent shows :param recent_anime: list of recent anime shows :param proper_dict: dict with provider keys containing Proper objects :return: list of propers """ propers = {} # make sure the episode has been downloaded before history_limit = datetime.datetime.now() - datetime.timedelta(days=30) my_db = db.DBConnection() # for each provider get a list of arbitrary Propers orig_thread_name = threading.currentThread().name # filter provider list for: # 1. from recent search: recent search enabled providers # 2. native proper search: active search enabled providers provider_list = filter_list( lambda p: p.is_active() and (p.enable_recentsearch, p.enable_backlog)[None is proper_dict], sickbeard.providers.sortedProviderList()) search_threads = [] if None is proper_dict: # if not a recent proper search create a thread per provider to search for Propers proper_dict = {} for cur_provider in provider_list: if not recent_anime and cur_provider.anime_only: continue provider_id = cur_provider.get_id() logger.log('Searching for new Proper releases at [%s]' % cur_provider.name) proper_dict[provider_id] = [] search_threads.append(threading.Thread(target=_search_provider, kwargs={'cur_provider': cur_provider, 'provider_propers': proper_dict[provider_id], 'aired_since_shows': aired_since_shows, 'recent_shows': recent_shows, 'recent_anime': recent_anime}, name='%s :: [%s]' % (orig_thread_name, cur_provider.name))) search_threads[-1].start() # wait for all searches to finish for cur_thread in search_threads: cur_thread.join() for cur_provider in provider_list: if not recent_anime and cur_provider.anime_only: continue found_propers = proper_dict.get(cur_provider.get_id(), []) if not found_propers: continue # if they haven't been added by a different provider than add the Proper to the list for cur_proper in found_propers: name = _generic_name(cur_proper.name) if name in propers: continue try: np = NameParser(False, show_obj=cur_proper.parsed_show_obj, indexer_lookup=False) parse_result = np.parse(cur_proper.name) except (InvalidNameException, InvalidShowException, Exception): continue # get the show object cur_proper.parsed_show_obj = (cur_proper.parsed_show_obj or helpers.find_show_by_id(parse_result.show_obj.tvid_prodid)) if None is cur_proper.parsed_show_obj: logger.log('Skip download; cannot find show with ID [%s] at %s' % (cur_proper.prodid, sickbeard.TVInfoAPI(cur_proper.tvid).name), logger.ERROR) continue cur_proper.tvid = cur_proper.parsed_show_obj.tvid cur_proper.prodid = cur_proper.parsed_show_obj.prodid if not (-1 != cur_proper.prodid and parse_result.series_name and parse_result.episode_numbers and (cur_proper.tvid, cur_proper.prodid) in recent_shows + recent_anime): continue # only get anime Proper if it has release group and version if parse_result.is_anime and not parse_result.release_group and -1 == parse_result.version: logger.log('Ignored Proper with no release group and version in name [%s]' % cur_proper.name, logger.DEBUG) continue if not show_name_helpers.pass_wordlist_checks(cur_proper.name, parse=False, indexer_lookup=False, show_obj=cur_proper.parsed_show_obj): logger.log('Ignored unwanted Proper [%s]' % cur_proper.name, logger.DEBUG) continue re_x = dict(re_prefix='.*', re_suffix='.*') result = show_name_helpers.contains_any(cur_proper.name, cur_proper.parsed_show_obj.rls_ignore_words, rx=cur_proper.parsed_show_obj.rls_ignore_words_regex, **re_x) if None is not result and result: logger.log('Ignored Proper containing ignore word [%s]' % cur_proper.name, logger.DEBUG) continue result = show_name_helpers.contains_any(cur_proper.name, cur_proper.parsed_show_obj.rls_require_words, rx=cur_proper.parsed_show_obj.rls_require_words_regex, **re_x) if None is not result and not result: logger.log('Ignored Proper for not containing any required word [%s]' % cur_proper.name, logger.DEBUG) continue cur_size = getattr(cur_proper, 'size', None) if failed_history.has_failed(cur_proper.name, cur_size, cur_provider.name): continue cur_proper.season = parse_result.season_number if None is not parse_result.season_number else 1 cur_proper.episode = parse_result.episode_numbers[0] # check if we actually want this Proper (if it's the right quality) sql_result = my_db.select( 'SELECT release_group, status, version, release_name' ' FROM tv_episodes' ' WHERE indexer = ? AND showid = ?' ' AND season = ? AND episode = ?' ' LIMIT 1', [cur_proper.tvid, cur_proper.prodid, cur_proper.season, cur_proper.episode]) if not sql_result: continue # only keep the Proper if we already retrieved the same quality ep (don't get better/worse ones) # check if we want this release: same quality as current, current has correct status # restrict other release group releases to Proper's old_status, old_quality = Quality.splitCompositeStatus(int(sql_result[0]['status'])) cur_proper.quality = Quality.nameQuality(cur_proper.name, parse_result.is_anime) cur_proper.is_repack, cur_proper.properlevel = Quality.get_proper_level( parse_result.extra_info_no_name(), parse_result.version, parse_result.is_anime, check_is_repack=True) cur_proper.proper_level = cur_proper.properlevel # local non global value if old_status in SNATCHED_ANY: old_release_group = '' # noinspection SqlResolve history_results = my_db.select( 'SELECT resource FROM history' ' WHERE indexer = ? AND showid = ?' ' AND season = ? AND episode = ? AND quality = ? AND date >= ?' ' AND (%s) ORDER BY date DESC LIMIT 1' % ' OR '.join( ['action = "%d%02d"' % (old_quality, x) for x in SNATCHED_ANY]), [cur_proper.tvid, cur_proper.prodid, cur_proper.season, cur_proper.episode, cur_proper.quality, history_limit.strftime(history.dateFormat)]) if len(history_results): try: old_release_group = np.parse(history_results[0]['resource']).release_group except (BaseException, Exception): pass else: old_release_group = sql_result[0]['release_group'] try: same_release_group = parse_result.release_group.lower() == old_release_group.lower() except (BaseException, Exception): same_release_group = parse_result.release_group == old_release_group if old_status not in SNATCHED_ANY + [DOWNLOADED, ARCHIVED] \ or cur_proper.quality != old_quality \ or (cur_proper.is_repack and not same_release_group): continue np = NameParser(False, show_obj=cur_proper.parsed_show_obj, indexer_lookup=False) try: extra_info = np.parse(sql_result[0]['release_name']).extra_info_no_name() except (BaseException, Exception): extra_info = None # don't take Proper of the same level we already downloaded old_proper_level, old_extra_no_name, old_name = \ get_old_proper_level(cur_proper.parsed_show_obj, cur_proper.tvid, cur_proper.prodid, cur_proper.season, parse_result.episode_numbers, old_status, cur_proper.quality, extra_info, parse_result.version, parse_result.is_anime) if cur_proper.proper_level <= old_proper_level: continue is_web = (old_quality in (Quality.HDWEBDL, Quality.FULLHDWEBDL, Quality.UHD4KWEB) or (old_quality == Quality.SDTV and isinstance(sql_result[0]['release_name'], string_types) and re.search(r'\Wweb.?(dl|rip|.([hx]\W?26[45]|hevc))\W', sql_result[0]['release_name'], re.I))) if is_web: old_name = (old_name, sql_result[0]['release_name'])[old_name in ('', None)] old_webdl_type = get_webdl_type(old_extra_no_name, old_name) new_webdl_type = get_webdl_type(parse_result.extra_info_no_name(), cur_proper.name) if old_webdl_type != new_webdl_type: logger.log('Ignored Proper webdl source [%s], does not match existing webdl source [%s] for [%s]' % (old_webdl_type, new_webdl_type, cur_proper.name), logger.DEBUG) continue # for webdls, prevent Propers from different groups log_same_grp = 'Ignored Proper from release group [%s] does not match existing group [%s] for [%s]' \ % (parse_result.release_group, old_release_group, cur_proper.name) if sickbeard.PROPERS_WEBDL_ONEGRP and is_web and not same_release_group: logger.log(log_same_grp, logger.DEBUG) continue # check if we actually want this Proper (if it's the right release group and a higher version) if parse_result.is_anime: old_version = int(sql_result[0]['version']) if not (-1 < old_version < parse_result.version): continue if not same_release_group: logger.log(log_same_grp, logger.DEBUG) continue found_msg = 'Found anime Proper v%s to replace v%s' % (parse_result.version, old_version) else: found_msg = 'Found Proper [%s]' % cur_proper.name # noinspection SqlResolve history_results = my_db.select( 'SELECT resource FROM history' ' WHERE indexer = ? AND showid = ?' ' AND season = ? AND episode = ? AND quality = ? AND date >= ?' ' AND (%s)' % ' OR '.join(['action LIKE "%%%02d"' % x for x in SNATCHED_ANY + [DOWNLOADED, ARCHIVED]]), [cur_proper.tvid, cur_proper.prodid, cur_proper.season, cur_proper.episode, cur_proper.quality, history_limit.strftime(history.dateFormat)]) # skip if the episode has never downloaded, because a previous quality is required to match the Proper if not len(history_results): logger.log('Ignored Proper cannot find a recent history item for [%s]' % cur_proper.name, logger.DEBUG) continue # make sure that none of the existing history downloads are the same Proper as the download candidate clean_proper_name = _generic_name(helpers.remove_non_release_groups( cur_proper.name, cur_proper.parsed_show_obj.is_anime)) is_same = False for hitem in history_results: # if the result exists in history already we need to skip it if clean_proper_name == _generic_name(helpers.remove_non_release_groups( ek.ek(os.path.basename, hitem['resource']))): is_same = True break if is_same: logger.log('Ignored Proper already in history [%s]' % cur_proper.name) continue logger.log(found_msg, logger.DEBUG) # finish populating the Proper instance # cur_proper.show_obj = cur_proper.parsed_show_obj.prodid cur_proper.provider = cur_provider cur_proper.extra_info = parse_result.extra_info cur_proper.extra_info_no_name = parse_result.extra_info_no_name cur_proper.release_group = parse_result.release_group cur_proper.is_anime = parse_result.is_anime cur_proper.version = parse_result.version propers[name] = cur_proper cur_provider.log_result('Propers', len(propers), '%s' % cur_provider.name) return list_values(propers)
def send_nzb(search_result): """ :param search_result: search result :type search_result: NZBSearchResult or NZBDataSearchResult :return: """ result = False add_to_top = False nzbget_prio = 0 authed, auth_msg, rpc_client = test_nzbget(sickbeard.NZBGET_HOST, sickbeard.NZBGET_USE_HTTPS, sickbeard.NZBGET_USERNAME, sickbeard.NZBGET_PASSWORD) if not authed: return authed dupekey = '' dupescore = 0 # if it aired recently make it high priority and generate DupeKey/Score for cur_ep_obj in search_result.ep_obj_list: if '' == dupekey: dupekey = 'SickGear-%s%s' % (sickbeard.TVInfoAPI( cur_ep_obj.show_obj.tvid).config.get( 'dupekey', ''), cur_ep_obj.show_obj.prodid) dupekey += '-%s.%s' % (cur_ep_obj.season, cur_ep_obj.episode) if 1 == search_result.priority: add_to_top = True nzbget_prio = sickbeard.NZBGET_PRIORITY if Quality.UNKNOWN != search_result.quality: dupescore = search_result.quality * 100 dupescore += (0, 9 + search_result.properlevel)[0 < search_result.properlevel] nzbcontent64 = None if 'nzbdata' == search_result.resultType: data = search_result.get_data() if not data: return False nzbcontent64 = b64encodestring(data, keep_eol=True) elif 'Anizb' == search_result.provider.name and 'nzb' == search_result.resultType: gen_provider = GenericProvider('') data = gen_provider.get_url(search_result.url) if None is data: return result nzbcontent64 = b64encodestring(data, keep_eol=True) logger.log(u'Sending NZB to NZBGet: %s' % search_result.name) try: # Find out if nzbget supports priority (Version 9.0+), old versions beginning with a 0.x will use the old cmd nzbget_version_str = rpc_client.version() nzbget_version = try_int( nzbget_version_str[:nzbget_version_str.find('.')]) # v13+ has a combined append method that accepts both (url and content) # also the return value has changed from boolean to integer # (Positive number representing NZBID of the queue item. 0 and negative numbers represent error codes.) if 13 <= nzbget_version: nzbget_result = 0 < rpc_client.append( '%s.nzb' % search_result.name, (nzbcontent64, search_result.url)[None is nzbcontent64], sickbeard.NZBGET_CATEGORY, nzbget_prio, False, False, dupekey, dupescore, 'score') elif 12 == nzbget_version: if None is not nzbcontent64: nzbget_result = rpc_client.append( '%s.nzb' % search_result.name, sickbeard.NZBGET_CATEGORY, nzbget_prio, False, nzbcontent64, False, dupekey, dupescore, 'score') else: nzbget_result = rpc_client.appendurl( '%s.nzb' % search_result.name, sickbeard.NZBGET_CATEGORY, nzbget_prio, False, search_result.url, False, dupekey, dupescore, 'score') elif 0 == nzbget_version: if None is not nzbcontent64: nzbget_result = rpc_client.append( '%s.nzb' % search_result.name, sickbeard.NZBGET_CATEGORY, add_to_top, nzbcontent64) else: if 'nzb' == search_result.resultType: gen_provider = GenericProvider('') data = gen_provider.get_url(search_result.url) if None is data: return result nzbcontent64 = b64encodestring(data, keep_eol=True) nzbget_result = rpc_client.append( '%s.nzb' % search_result.name, sickbeard.NZBGET_CATEGORY, add_to_top, nzbcontent64) else: if None is not nzbcontent64: nzbget_result = rpc_client.append( '%s.nzb' % search_result.name, sickbeard.NZBGET_CATEGORY, nzbget_prio, False, nzbcontent64) else: nzbget_result = rpc_client.appendurl( '%s.nzb' % search_result.name, sickbeard.NZBGET_CATEGORY, nzbget_prio, False, search_result.url) if nzbget_result: logger.log(u'NZB sent to NZBGet successfully', logger.DEBUG) result = True else: logger.log( u'NZBGet could not add %s.nzb to the queue' % search_result.name, logger.ERROR) except (BaseException, Exception): logger.log( u'Connect Error to NZBGet: could not add %s.nzb to the queue' % search_result.name, logger.ERROR) return result
def _show_data(self, show_obj): # type: (sickbeard.tv.TVShow) -> Optional[Union[bool, etree.Element]] """ 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 """ show_lang = show_obj.lang # There's gotta be a better way of doing this but we don't wanna # change the language value elsewhere tvinfo_config = sickbeard.TVInfoAPI(show_obj.tvid).api_params.copy() tvinfo_config['actors'] = True if show_lang and not 'en' == show_lang: tvinfo_config['language'] = show_lang if 0 != show_obj.dvdorder: tvinfo_config['dvdorder'] = True t = sickbeard.TVInfoAPI(show_obj.tvid).setup(**tvinfo_config) tv_node = etree.Element("Series") try: show_info = t[int(show_obj.prodid)] except Exception as e: if check_exception_type(e, ExceptionTuples.tvinfo_shownotfound): logger.log( "Unable to find show with id %s on %s, skipping it" % (show_obj.prodid, sickbeard.TVInfoAPI(show_obj.tvid).name), logger.ERROR) raise elif check_exception_type(e, ExceptionTuples.tvinfo_error): logger.log( "%s is down, can't use its data to make the NFO" % sickbeard.TVInfoAPI(show_obj.tvid).name, logger.ERROR) raise else: raise e if not self._valid_show(show_info, show_obj): return # check for title and id if None is getattr(show_info, 'seriesname', None) or None is getattr( show_info, 'id', None): logger.log( "Incomplete info for show with id %s on %s, skipping it" % (show_obj.prodid, sickbeard.TVInfoAPI(show_obj.tvid).name), logger.ERROR) return False prodid = etree.SubElement(tv_node, "id") if None is not getattr(show_info, 'id', None): prodid.text = str(show_info['id']) tvid = etree.SubElement(tv_node, "indexer") if None is not show_obj.tvid: tvid.text = str(show_obj.tvid) SeriesName = etree.SubElement(tv_node, "SeriesName") if None is not getattr(show_info, 'seriesname', None): SeriesName.text = '%s' % show_info['seriesname'] Status = etree.SubElement(tv_node, "Status") if None is not getattr(show_info, 'status', None): Status.text = '%s' % show_info['status'] Network = etree.SubElement(tv_node, "Network") if None is not getattr(show_info, 'network', None): Network.text = '%s' % show_info['network'] Airs_Time = etree.SubElement(tv_node, "Airs_Time") if None is not getattr(show_info, 'airs_time', None): Airs_Time.text = '%s' % show_info['airs_time'] Airs_DayOfWeek = etree.SubElement(tv_node, "Airs_DayOfWeek") if None is not getattr(show_info, 'airs_dayofweek', None): Airs_DayOfWeek.text = '%s' % show_info['airs_dayofweek'] FirstAired = etree.SubElement(tv_node, "FirstAired") if None is not getattr(show_info, 'firstaired', None): FirstAired.text = '%s' % show_info['firstaired'] ContentRating = etree.SubElement(tv_node, "ContentRating") MPAARating = etree.SubElement(tv_node, "MPAARating") certification = etree.SubElement(tv_node, "certification") if None is not getattr(show_info, 'contentrating', None): ContentRating.text = '%s' % show_info['contentrating'] MPAARating.text = '%s' % show_info['contentrating'] certification.text = '%s' % show_info['contentrating'] MetadataType = etree.SubElement(tv_node, "Type") MetadataType.text = "Series" Overview = etree.SubElement(tv_node, "Overview") if None is not getattr(show_info, 'overview', None): Overview.text = '%s' % show_info['overview'] PremiereDate = etree.SubElement(tv_node, "PremiereDate") if None is not getattr(show_info, 'firstaired', None): PremiereDate.text = '%s' % show_info['firstaired'] Rating = etree.SubElement(tv_node, "Rating") if None is not getattr(show_info, 'rating', None): Rating.text = '%s' % show_info['rating'] ProductionYear = etree.SubElement(tv_node, "ProductionYear") year_text = self.get_show_year(show_obj, show_info) if year_text: ProductionYear.text = '%s' % year_text RunningTime = etree.SubElement(tv_node, "RunningTime") Runtime = etree.SubElement(tv_node, "Runtime") if None is not getattr(show_info, 'runtime', None): RunningTime.text = '%s' % show_info['runtime'] Runtime.text = '%s' % show_info['runtime'] IMDB_ID = etree.SubElement(tv_node, "IMDB_ID") IMDB = etree.SubElement(tv_node, "IMDB") IMDbId = etree.SubElement(tv_node, "IMDbId") if None is not getattr(show_info, 'imdb_id', None): IMDB_ID.text = '%s' % show_info['imdb_id'] IMDB.text = '%s' % show_info['imdb_id'] IMDbId.text = '%s' % show_info['imdb_id'] Zap2ItId = etree.SubElement(tv_node, "Zap2ItId") if None is not getattr(show_info, 'zap2it_id', None): Zap2ItId.text = '%s' % show_info['zap2it_id'] Genres = etree.SubElement(tv_node, "Genres") for genre in show_info['genre'].split('|'): if genre: cur_genre = etree.SubElement(Genres, "Genre") cur_genre.text = '%s' % genre Genre = etree.SubElement(tv_node, "Genre") if None is not getattr(show_info, 'genre', None): Genre.text = "|".join( [x for x in show_info["genre"].split('|') if x]) Studios = etree.SubElement(tv_node, "Studios") Studio = etree.SubElement(Studios, "Studio") if None is not getattr(show_info, 'network', None): Studio.text = '%s' % show_info['network'] Persons = etree.SubElement(tv_node, 'Persons') for actor in getattr(show_info, 'actors', []): cur_actor = etree.SubElement(Persons, 'Person') cur_actor_name = etree.SubElement(cur_actor, 'Name') cur_actor_name.text = '%s' % actor['person']['name'] cur_actor_type = etree.SubElement(cur_actor, 'Type') cur_actor_type.text = 'Actor' cur_actor_role = etree.SubElement(cur_actor, 'Role') cur_actor_role_text = '%s' % actor['character']['name'] if cur_actor_role_text: cur_actor_role.text = '%s' % cur_actor_role_text helpers.indent_xml(tv_node) data = etree.ElementTree(tv_node) return data
def _ep_data(self, ep_obj): # type: (sickbeard.tv.TVEpisode) -> Optional[Union[bool, etree.Element]] """ 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 """ ep_obj_list_to_write = [ep_obj] + ep_obj.related_ep_obj persons_dict = {'Director': [], 'GuestStar': [], 'Writer': []} show_lang = ep_obj.show_obj.lang try: tvinfo_config = sickbeard.TVInfoAPI( ep_obj.show_obj.tvid).api_params.copy() tvinfo_config['actors'] = True if show_lang and not 'en' == show_lang: tvinfo_config['language'] = show_lang if 0 != ep_obj.show_obj.dvdorder: tvinfo_config['dvdorder'] = True t = sickbeard.TVInfoAPI( ep_obj.show_obj.tvid).setup(**tvinfo_config) show_info = t[ep_obj.show_obj.prodid] except Exception as e: if check_exception_type(e, ExceptionTuples.tvinfo_shownotfound): raise exceptions_helper.ShowNotFoundException(ex(e)) elif check_exception_type(e, ExceptionTuples.tvinfo_error): logger.log( "Unable to connect to %s while creating meta files - skipping - %s" % (sickbeard.TVInfoAPI(ep_obj.show_obj.tvid).name, ex(e)), logger.ERROR) return False else: raise e if not self._valid_show(show_info, ep_obj.show_obj): return rootNode = etree.Element("Item") # write an MediaBrowser XML containing info for all matching episodes for cur_ep_obj in ep_obj_list_to_write: try: ep_info = show_info[cur_ep_obj.season][cur_ep_obj.episode] except (BaseException, Exception): logger.log( "Unable to find episode %sx%s on %s.. has it been removed? Should I delete from db?" % (cur_ep_obj.season, cur_ep_obj.episode, sickbeard.TVInfoAPI(ep_obj.show_obj.tvid).name)) return None if cur_ep_obj == ep_obj: # root (or single) episode # default to today's date for specials if firstaired is not set if None is getattr(ep_info, 'firstaired', None) and 0 == ep_obj.season: ep_info['firstaired'] = str(datetime.date.fromordinal(1)) if None is getattr(ep_info, 'episodename', None) or None is getattr( ep_info, 'firstaired', None): return None episode = rootNode EpisodeName = etree.SubElement(episode, "EpisodeName") if None is not cur_ep_obj.name: EpisodeName.text = '%s' % cur_ep_obj.name else: EpisodeName.text = "" EpisodeNumber = etree.SubElement(episode, "EpisodeNumber") EpisodeNumber.text = str(ep_obj.episode) if ep_obj.related_ep_obj: EpisodeNumberEnd = etree.SubElement( episode, "EpisodeNumberEnd") EpisodeNumberEnd.text = str(cur_ep_obj.episode) SeasonNumber = etree.SubElement(episode, "SeasonNumber") SeasonNumber.text = str(cur_ep_obj.season) if not ep_obj.related_ep_obj: absolute_number = etree.SubElement(episode, "absolute_number") if None is not getattr(ep_info, 'absolute_number', None): absolute_number.text = '%s' % ep_info['absolute_number'] FirstAired = etree.SubElement(episode, "FirstAired") if cur_ep_obj.airdate != datetime.date.fromordinal(1): FirstAired.text = str(cur_ep_obj.airdate) else: FirstAired.text = "" MetadataType = etree.SubElement(episode, "Type") MetadataType.text = "Episode" Overview = etree.SubElement(episode, "Overview") if None is not cur_ep_obj.description: Overview.text = '%s' % cur_ep_obj.description else: Overview.text = "" if not ep_obj.related_ep_obj: Rating = etree.SubElement(episode, "Rating") if None is not getattr(ep_info, 'rating', None): Rating.text = '%s' % ep_info['rating'] IMDB_ID = etree.SubElement(episode, "IMDB_ID") IMDB = etree.SubElement(episode, "IMDB") IMDbId = etree.SubElement(episode, "IMDbId") if None is not getattr(show_info, 'imdb_id', None): IMDB_ID.text = '%s' % show_info['imdb_id'] IMDB.text = '%s' % show_info['imdb_id'] IMDbId.text = '%s' % show_info['imdb_id'] prodid = etree.SubElement(episode, "id") prodid.text = str(cur_ep_obj.show_obj.prodid) tvid = etree.SubElement(episode, "indexer") tvid.text = str(cur_ep_obj.show_obj.tvid) Persons = etree.SubElement(episode, "Persons") Language = etree.SubElement(episode, "Language") try: Language.text = '%s' % cur_ep_obj.show_obj.lang except (BaseException, Exception): Language.text = 'en' # tvrage api doesn't provide language so we must assume a value here thumb = etree.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 = '%s' % thumb_text else: # append data from (if any) related episodes EpisodeNumberEnd.text = str(cur_ep_obj.episode) if cur_ep_obj.name: if not EpisodeName.text: EpisodeName.text = '%s' % cur_ep_obj.name else: EpisodeName.text = '%s, %s' % (EpisodeName.text, cur_ep_obj.name) if cur_ep_obj.description: if not Overview.text: Overview.text = '%s' % cur_ep_obj.description else: Overview.text = '%s\r%s' % (Overview.text, cur_ep_obj.description) # collect all directors, guest stars and writers if None is not getattr(ep_info, 'director', None): persons_dict['Director'] += [ x.strip() for x in ep_info['director'].split('|') if x ] if None is not getattr(ep_info, 'gueststars', None): persons_dict['GuestStar'] += [ x.strip() for x in ep_info['gueststars'].split('|') if x ] if None is not getattr(ep_info, 'writer', None): persons_dict['Writer'] += [ x.strip() for x in ep_info['writer'].split('|') if x ] # fill in Persons section with collected directors, guest starts and writers for person_type, names in iteritems(persons_dict): # remove doubles names = list(set(names)) for cur_name in names: Person = etree.SubElement(Persons, "Person") cur_person_name = etree.SubElement(Person, "Name") cur_person_name.text = '%s' % cur_name cur_person_type = etree.SubElement(Person, "Type") cur_person_type.text = '%s' % person_type helpers.indent_xml(rootNode) data = etree.ElementTree(rootNode) return data
def _ep_data(self, ep_obj): # type: (sickbeard.tv.TVEpisode) -> Optional[Union[bool, etree.Element]] """ Creates an elementTree XML structure for an XBMC-style episode.nfo and returns the resulting data object. show_obj: a TVEpisode instance to create the NFO for """ ep_obj_list_to_write = [ep_obj] + ep_obj.related_ep_obj show_lang = ep_obj.show_obj.lang tvinfo_config = sickbeard.TVInfoAPI( ep_obj.show_obj.tvid).api_params.copy() tvinfo_config['actors'] = True if show_lang and not 'en' == show_lang: tvinfo_config['language'] = show_lang if 0 != ep_obj.show_obj.dvdorder: tvinfo_config['dvdorder'] = True try: t = sickbeard.TVInfoAPI( ep_obj.show_obj.tvid).setup(**tvinfo_config) show_info = t[ep_obj.show_obj.prodid] except BaseTVinfoShownotfound as e: raise exceptions_helper.ShowNotFoundException(ex(e)) except BaseTVinfoError as e: logger.log( 'Unable to connect to %s while creating meta files - skipping - %s' % (sickbeard.TVInfoAPI(ep_obj.show_obj.tvid).name, ex(e)), logger.ERROR) return if not self._valid_show(show_info, ep_obj.show_obj): return if 1 < len(ep_obj_list_to_write): rootNode = etree.Element('xbmcmultiepisode') else: rootNode = etree.Element('episodedetails') # write an NFO containing info for all matching episodes for cur_ep_obj in ep_obj_list_to_write: try: ep_info = show_info[cur_ep_obj.season][cur_ep_obj.episode] except (BaseTVinfoEpisodenotfound, BaseTVinfoSeasonnotfound) as e: logger.log( 'Unable to find episode %sx%s on %s.. has it been removed? Should I delete from db?' % (cur_ep_obj.season, cur_ep_obj.episode, sickbeard.TVInfoAPI(ep_obj.show_obj.tvid).name)) return None except (BaseException, Exception): logger.log( u'Not generating nfo because failed to fetched tv info data at this time', logger.DEBUG) return None if None is getattr(ep_info, 'firstaired', None): ep_info['firstaired'] = str(datetime.date.fromordinal(1)) if None is getattr(ep_info, 'episodename', None): logger.log(u'Not generating nfo because the ep has no title', logger.DEBUG) return None logger.log( u'Creating metadata for episode ' + str(ep_obj.season) + 'x' + str(ep_obj.episode), logger.DEBUG) if 1 < len(ep_obj_list_to_write): episode = etree.SubElement(rootNode, 'episodedetails') else: episode = rootNode title = etree.SubElement(episode, 'title') if None is not cur_ep_obj.name: title.text = '%s' % cur_ep_obj.name showtitle = etree.SubElement(episode, 'showtitle') if None is not cur_ep_obj.show_obj.name: showtitle.text = '%s' % cur_ep_obj.show_obj.name season = etree.SubElement(episode, 'season') season.text = str(cur_ep_obj.season) episodenum = etree.SubElement(episode, 'episode') episodenum.text = str(cur_ep_obj.episode) uniqueid = etree.SubElement(episode, 'uniqueid') uniqueid.text = str(cur_ep_obj.epid) aired = etree.SubElement(episode, 'aired') if cur_ep_obj.airdate != datetime.date.fromordinal(1): aired.text = str(cur_ep_obj.airdate) else: aired.text = '' plot = etree.SubElement(episode, 'plot') if None is not cur_ep_obj.description: plot.text = '%s' % cur_ep_obj.description runtime = etree.SubElement(episode, 'runtime') if 0 != cur_ep_obj.season: if None is not getattr(show_info, 'runtime', None): runtime.text = '%s' % show_info['runtime'] displayseason = etree.SubElement(episode, 'displayseason') if None is not getattr(ep_info, 'airsbefore_season', None): displayseason_text = ep_info['airsbefore_season'] if None is not displayseason_text: displayseason.text = '%s' % displayseason_text displayepisode = etree.SubElement(episode, 'displayepisode') if None is not getattr(ep_info, 'airsbefore_episode', None): displayepisode_text = ep_info['airsbefore_episode'] if None is not displayepisode_text: displayepisode.text = '%s' % displayepisode_text thumb = etree.SubElement(episode, 'thumb') thumb_text = getattr(ep_info, 'filename', None) if None is not thumb_text: thumb.text = '%s' % thumb_text watched = etree.SubElement(episode, 'watched') watched.text = 'false' credits = etree.SubElement(episode, 'credits') credits_text = getattr(ep_info, 'writer', None) if None is not credits_text: credits.text = '%s' % credits_text director = etree.SubElement(episode, 'director') director_text = getattr(ep_info, 'director', None) if None is not director_text: director.text = '%s' % director_text rating = etree.SubElement(episode, 'rating') rating_text = getattr(ep_info, 'rating', None) if None is not rating_text: rating.text = '%s' % rating_text gueststar_text = getattr(ep_info, 'gueststars', None) if isinstance(gueststar_text, string_types): for actor in (x.strip() for x in gueststar_text.split('|') if x.strip()): cur_actor = etree.SubElement(episode, 'actor') cur_actor_name = etree.SubElement(cur_actor, 'name') cur_actor_name.text = '%s' % actor self.add_actor_element(show_info, etree, episode) # Make it purdy sg_helpers.indent_xml(rootNode) data = etree.ElementTree(rootNode) return data
def _ep_data(self, ep_obj): # type: (sickbeard.tv.TVEpisode) -> Optional[Union[bool, etree.Element]] """ 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 """ ep_obj_list_to_write = [ep_obj] + ep_obj.related_ep_obj show_lang = ep_obj.show_obj.lang try: # There's gotta be a better way of doing this but we don't wanna # change the language value elsewhere tvinfo_config = sickbeard.TVInfoAPI( ep_obj.show_obj.tvid).api_params.copy() if show_lang and not 'en' == show_lang: tvinfo_config['language'] = show_lang if 0 != ep_obj.show_obj.dvdorder: tvinfo_config['dvdorder'] = True t = sickbeard.TVInfoAPI( ep_obj.show_obj.tvid).setup(**tvinfo_config) show_info = t[ep_obj.show_obj.prodid] except BaseTVinfoShownotfound as e: raise exceptions_helper.ShowNotFoundException(ex(e)) except BaseTVinfoError as e: logger.log( 'Unable to connect to %s while creating meta files - skipping - %s' % (sickbeard.TVInfoAPI(ep_obj.show_obj.tvid).name, ex(e)), logger.ERROR) return False if not self._valid_show(show_info, ep_obj.show_obj): return rootNode = etree.Element('details') movie = etree.SubElement(rootNode, 'movie') movie.attrib['isExtra'] = 'false' movie.attrib['isSet'] = 'false' movie.attrib['isTV'] = 'true' # write an MediaBrowser XML containing info for all matching episodes for cur_ep_obj in ep_obj_list_to_write: try: ep_info = show_info[cur_ep_obj.season][cur_ep_obj.episode] except (BaseException, Exception): logger.log( u'Unable to find episode %sx%s on tvdb... has it been removed? Should I delete from db?' % (cur_ep_obj.season, cur_ep_obj.episode)) return None if cur_ep_obj == ep_obj: # root (or single) episode # default to today's date for specials if firstaired is not set if None is ep_info['firstaired'] and 0 == ep_obj.season: ep_info['firstaired'] = str(datetime.date.fromordinal(1)) if None is ep_info['episodename'] or None is ep_info[ 'firstaired']: return None episode = movie EpisodeName = etree.SubElement(episode, 'title') if None is not cur_ep_obj.name: EpisodeName.text = '%s' % cur_ep_obj.name else: EpisodeName.text = '' SeasonNumber = etree.SubElement(episode, 'season') SeasonNumber.text = str(cur_ep_obj.season) EpisodeNumber = etree.SubElement(episode, 'episode') EpisodeNumber.text = str(ep_obj.episode) year = etree.SubElement(episode, 'year') year_text = self.get_show_year(ep_obj.show_obj, show_info) if year_text: year.text = '%s' % year_text plot = etree.SubElement(episode, 'plot') if None is not show_info['overview']: plot.text = '%s' % show_info['overview'] Overview = etree.SubElement(episode, 'episodeplot') if None is not cur_ep_obj.description: Overview.text = '%s' % cur_ep_obj.description else: Overview.text = '' mpaa = etree.SubElement(episode, 'mpaa') if None is not show_info['contentrating']: mpaa.text = '%s' % show_info['contentrating'] if not ep_obj.related_ep_obj: if None is not ep_info['rating']: try: rating = int((float(ep_info['rating']) * 10)) except ValueError: rating = 0 Rating = etree.SubElement(episode, 'rating') rating_text = str(rating) if None is not rating_text: Rating.text = '%s' % rating_text director = etree.SubElement(episode, 'director') director_text = ep_info['director'] if None is not director_text: director.text = '%s' % director_text credits = etree.SubElement(episode, 'credits') credits_text = ep_info['writer'] if None is not credits_text: credits.text = '%s' % credits_text cast = etree.SubElement(episode, 'cast') self.add_actor_element(show_info, etree, cast) else: # append data from (if any) related episodes if cur_ep_obj.name: if not EpisodeName.text: EpisodeName.text = '%s' % cur_ep_obj.name else: EpisodeName.text = '%s, %s' % (EpisodeName.text, cur_ep_obj.name) if cur_ep_obj.description: if not Overview.text: Overview.text = '%s' % cur_ep_obj.description else: Overview.text = '%s\r%s' % (Overview.text, cur_ep_obj.description) sg_helpers.indent_xml(rootNode) data = etree.ElementTree(rootNode) return data
def _show_data(self, show_obj): # type: (sickbeard.tv.TVShow) -> Optional[Union[bool, etree.Element]] """ Creates an elementTree XML structure for an XBMC-style tvshow.nfo and returns the resulting data object. show_obj: a TVShow instance to create the NFO for """ show_id = show_obj.prodid show_lang = show_obj.lang tvinfo_config = sickbeard.TVInfoAPI(show_obj.tvid).api_params.copy() tvinfo_config['actors'] = True if show_lang and not 'en' == show_lang: tvinfo_config['language'] = show_lang if 0 != show_obj.dvdorder: tvinfo_config['dvdorder'] = True t = sickbeard.TVInfoAPI(show_obj.tvid).setup(**tvinfo_config) tv_node = etree.Element('tvshow') try: show_info = t[int(show_id)] except BaseTVinfoShownotfound as e: logger.log( 'Unable to find show with id %s on %s, skipping it' % (show_id, sickbeard.TVInfoAPI(show_obj.tvid).name), logger.ERROR) raise e except BaseTVinfoError as e: logger.log( '%s is down, can\'t use its data to add this show' % sickbeard.TVInfoAPI(show_obj.tvid).name, logger.ERROR) raise e if not self._valid_show(show_info, show_obj): return # check for title and id if None is getattr(show_info, 'seriesname', None) or None is getattr( show_info, 'id', None): logger.log( 'Incomplete info for show with id %s on %s, skipping it' % (show_id, sickbeard.TVInfoAPI(show_obj.tvid).name), logger.ERROR) return False title = etree.SubElement(tv_node, 'title') if None is not getattr(show_info, 'seriesname', None): title.text = '%s' % show_info['seriesname'] rating = etree.SubElement(tv_node, 'rating') if None is not getattr(show_info, 'rating', None): rating.text = '%s' % show_info['rating'] year = etree.SubElement(tv_node, 'year') year_text = self.get_show_year(show_obj, show_info) if year_text: year.text = '%s' % year_text plot = etree.SubElement(tv_node, 'plot') if None is not getattr(show_info, 'overview', None): plot.text = '%s' % show_info['overview'] episodeguide = etree.SubElement(tv_node, 'episodeguide') episodeguideurl = etree.SubElement(episodeguide, 'url') episodeguideurl2 = etree.SubElement(tv_node, 'episodeguideurl') if None is not getattr(show_info, 'id', None): showurl = sickbeard.TVInfoAPI( show_obj.tvid).config['base_url'] + str( show_info['id']) + '/all/en.zip' episodeguideurl.text = '%s' % showurl episodeguideurl2.text = '%s' % showurl mpaa = etree.SubElement(tv_node, 'mpaa') if None is not getattr(show_info, 'contentrating', None): mpaa.text = '%s' % show_info['contentrating'] prodid = etree.SubElement(tv_node, 'id') if None is not getattr(show_info, 'id', None): prodid.text = str(show_info['id']) tvid = etree.SubElement(tv_node, 'indexer') if None is not show_obj.tvid: tvid.text = str(show_obj.tvid) genre = etree.SubElement(tv_node, 'genre') if None is not getattr(show_info, 'genre', None): if isinstance(show_info['genre'], string_types): genre.text = ' / '.join(x.strip() for x in show_info['genre'].split('|') if x.strip()) premiered = etree.SubElement(tv_node, 'premiered') if None is not getattr(show_info, 'firstaired', None): premiered.text = '%s' % show_info['firstaired'] studio = etree.SubElement(tv_node, 'studio') if None is not getattr(show_info, 'network', None): studio.text = '%s' % show_info['network'] self.add_actor_element(show_info, etree, tv_node) # Make it purdy sg_helpers.indent_xml(tv_node) data = etree.ElementTree(tv_node) return data
def _retrieve_show_image(self, image_type, show_obj, which=None): # type: (AnyStr, sickbeard.tv.TVShow, bool) -> Optional[bytes, List[AnyStr]] """ Gets an image URL from theTVDB.com, fanart.tv and TMDB.com, downloads it and returns the data. If type is fanart, multiple image src urls are returned instead of a single data image. image_type: type of image to retrieve (currently supported: fanart, poster, banner, poster_thumb, banner_thumb) 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 """ show_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 tvinfo_config = sickbeard.TVInfoAPI(show_obj.tvid).api_params.copy() if image_type.startswith('fanart'): tvinfo_config['fanart'] = True elif image_type.startswith('poster'): tvinfo_config['posters'] = True else: tvinfo_config['banners'] = True tvinfo_config['dvdorder'] = 0 != show_obj.dvdorder if show_lang and not 'en' == show_lang: tvinfo_config['language'] = show_lang t = sickbeard.TVInfoAPI(show_obj.tvid).setup(**tvinfo_config) show_info = t[show_obj.prodid, False] except (BaseTVinfoError, IOError) as e: logger.log(u"Unable to look up show on " + sickbeard.TVInfoAPI( show_obj.tvid).name + ", not downloading images: " + ex(e), logger.WARNING) return None if not self._valid_show(show_info, show_obj): return None return_links = False if 'fanart_all' == image_type: return_links = True image_type = 'fanart' if image_type not in ('poster', 'banner', 'fanart', 'poster_thumb', 'banner_thumb'): logger.log(u"Invalid image type " + str(image_type) + ", couldn't find it in the " + sickbeard.TVInfoAPI( show_obj.tvid).name + " object", logger.ERROR) return None image_urls = [] init_url = None alt_tvdb_urls = [] def build_url(s_o, image_mode): _urls = [[], []] _url = s_o[image_mode] if _url and _url.startswith('http'): if 'poster' == image_mode: _url = re.sub('posters', '_cache/posters', _url) elif 'banner' == image_mode: _url = re.sub('graphical', '_cache/graphical', _url) _urls[0].append(_url) try: alt_url = '%swww.%s%s' % re.findall( r'(https?://)(?:artworks\.)?(thetvdb\.[^/]+/banners/[^\d]+[^.]+)(?:_t)(.*)', _url)[0][0:3] if alt_url not in _urls[0]: _urls[1].append(alt_url) except (IndexError, Exception): try: alt_url = '%sartworks.%s_t%s' % re.findall( r'(https?://)(?:www\.)?(thetvdb\.[^/]+/banners/[^\d]+[^.]+)(.*)', _url)[0][0:3] if alt_url not in _urls[0]: _urls[1].append(alt_url) except (IndexError, Exception): pass return _urls if 'poster_thumb' == image_type: if None is not getattr(show_info, image_type, None): image_urls, alt_tvdb_urls = build_url(show_info, image_type) elif None is not getattr(show_info, 'poster', None): image_urls, alt_tvdb_urls = build_url(show_info, 'poster') for item in self._fanart_urls_from_show(show_obj, image_type, show_lang, True) or []: image_urls.append(item[2]) if 0 == len(image_urls): for item in self._tmdb_image_url(show_obj, image_type) or []: image_urls.append(item[2]) elif 'banner_thumb' == image_type: if None is not getattr(show_info, image_type, None): image_urls, alt_tvdb_urls = build_url(show_info, image_type) elif None is not getattr(show_info, 'banner', None): image_urls, alt_tvdb_urls = build_url(show_info, 'banner') for item in self._fanart_urls_from_show(show_obj, image_type, show_lang, True) or []: image_urls.append(item[2]) else: for item in self._fanart_urls_from_show(show_obj, image_type, show_lang) or []: image_urls.append(item[2]) if None is not getattr(show_info, image_type, None): image_url = show_info[image_type] if image_url: image_urls.append(image_url) if 'poster' == image_type: init_url = image_url # check extra provided images in '_banners' key if None is not getattr(show_info, '_banners', None) and \ isinstance(show_info['_banners'].get(image_type, None), (list, dict)): for res, value in iteritems(show_info['_banners'][image_type]): for item in itervalues(value): image_urls.append(item['bannerpath']) if 0 == len(image_urls) or 'fanart' == image_type: for item in self._tmdb_image_url(show_obj, image_type) or []: image_urls.append('%s?%s' % (item[2], item[0])) img_data = len(image_urls) or None if img_data: # remove duplicates, keeping order image_urls = list(OrderedDict.fromkeys(image_urls)) if return_links: return image_urls else: img_data = metadata_helpers.getShowImage( (init_url, image_urls[0])[None is init_url], which, show_obj.name, bool(len(alt_tvdb_urls))) if None is img_data and len(alt_tvdb_urls): for url in alt_tvdb_urls: image_data = metadata_helpers.getShowImage( (init_url, url)[None is init_url], which, show_obj.name) if None is not image_data: break if None is not img_data: return img_data if None is not img_data: return img_data