def _ep_data(self, ep_obj): """ Creates an elementTree XML structure for a MediaBrowser style episode.xml and returns the resulting data object. show_obj: a TVShow instance to create the NFO for """ eps_to_write = [ep_obj] + ep_obj.relatedEps persons_dict = {} persons_dict['Director'] = [] persons_dict['GuestStar'] = [] persons_dict['Writer'] = [] indexer_lang = ep_obj.show.lang try: lINDEXER_API_PARMS = sickbeard.indexerApi(ep_obj.show.indexer).api_params.copy() lINDEXER_API_PARMS['actors'] = True if indexer_lang and not indexer_lang == 'en': lINDEXER_API_PARMS['language'] = indexer_lang if ep_obj.show.dvdorder != 0: lINDEXER_API_PARMS['dvdorder'] = True t = sickbeard.indexerApi(ep_obj.show.indexer).indexer(**lINDEXER_API_PARMS) myShow = t[ep_obj.show.indexerid] except sickbeard.indexer_shownotfound, e: raise exceptions.ShowNotFoundException(e.message)
def _get_episode_thumb_url(self, ep_obj): """ Returns the URL to use for downloading an episode's thumbnail. Uses theTVDB.com and TVRage.com data. ep_obj: a TVEpisode object for which to grab the thumb URL """ all_eps = [ep_obj] + ep_obj.relatedEps indexer_lang = ep_obj.show.lang # get a TVDB object try: # There's gotta be a better way of doing this but we don't wanna # change the language value elsewhere lINDEXER_API_PARMS = sickbeard.indexerApi(ep_obj.show.indexer).api_params.copy() lINDEXER_API_PARMS['actors'] = True if indexer_lang and not indexer_lang == 'en': lINDEXER_API_PARMS['language'] = indexer_lang if ep_obj.show.dvdorder != 0: lINDEXER_API_PARMS['dvdorder'] = True t = sickbeard.indexerApi(ep_obj.show.indexer).indexer(**lINDEXER_API_PARMS) indexer_show_obj = t[ep_obj.show.indexerid] except sickbeard.indexer_shownotfound, e: raise exceptions.ShowNotFoundException(e.message)
def _retrieve_show_image(self, image_type, show_obj, which=None): """ Gets an image URL from theTVDB.com and TMDB.com, downloads it and returns the data. image_type: type of image to retrieve (currently supported: fanart, poster, banner) show_obj: a TVShow object to use when searching for the image which: optional, a specific numbered poster to look for Returns: the binary image data if available, or else None """ image_url = None indexer_lang = show_obj.lang try: # There's gotta be a better way of doing this but we don't wanna # change the language value elsewhere lINDEXER_API_PARMS = sickbeard.indexerApi(show_obj.indexer).api_params.copy() lINDEXER_API_PARMS['banners'] = True if indexer_lang and not indexer_lang == sickbeard.INDEXER_DEFAULT_LANGUAGE: lINDEXER_API_PARMS['language'] = indexer_lang if show_obj.dvdorder != 0: lINDEXER_API_PARMS['dvdorder'] = True t = sickbeard.indexerApi(show_obj.indexer).indexer(**lINDEXER_API_PARMS) indexer_show_obj = t[show_obj.indexerid] except (sickbeard.indexer_error, IOError), e: logger.log(u"Unable to look up show on " + sickbeard.indexerApi( show_obj.indexer).name + ", not downloading images: " + ex(e), logger.WARNING) logger.log(u"Indexer " + sickbeard.indexerApi(show_obj.indexer).name + "maybe experiencing some problems. Try again later", logger.DEBUG) return None
def _xem_refresh(indexer_id, indexer): """ Refresh data from xem for a tv show @param indexer_id: int """ if indexer_id is None: return indexer_id = int(indexer_id) indexer = int(indexer) try: logger.log( u'Looking up XEM scene mapping for show %s on %s' % (indexer_id, sickbeard.indexerApi(indexer).name,), logger.DEBUG) data = requests.get("http://thexem.de/map/all?id=%s&origin=%s&destination=scene" % ( indexer_id, sickbeard.indexerApi(indexer).config['xem_origin'],), verify=False).json() if data is None or data == '': logger.log(u'No XEN data for show "%s on %s", trying TVTumbler' % ( indexer_id, sickbeard.indexerApi(indexer).name,), logger.MESSAGE) data = requests.get("http://show-api.tvtumbler.com/api/thexem/all?id=%s&origin=%s&destination=scene" % ( indexer_id, sickbeard.indexerApi(indexer).config['xem_origin'],), verify=False).json() if data is None or data == '': logger.log(u'TVTumbler also failed for show "%s on %s". giving up.' % (indexer_id, indexer,), logger.MESSAGE) return None result = data if result: cacheDB = db.DBConnection('cache.db') cacheDB.action("INSERT OR REPLACE INTO xem_refresh (indexer, indexer_id, last_refreshed) VALUES (?,?,?)", [indexer, indexer_id, time.time()]) if 'success' in result['result']: cacheDB.action("DELETE FROM xem_numbering where indexer = ? and indexer_id = ?", [indexer, indexer_id]) for entry in result['data']: if 'scene' in entry: cacheDB.action( "INSERT INTO xem_numbering (indexer, indexer_id, season, episode, scene_season, scene_episode) VALUES (?,?,?,?,?,?)", [indexer, indexer_id, entry[sickbeard.indexerApi(indexer).config['xem_origin']]['season'], entry[sickbeard.indexerApi(indexer).config['xem_origin']]['episode'], entry['scene']['season'], entry['scene']['episode']]) if 'scene_2' in entry: # for doubles cacheDB.action( "INSERT INTO xem_numbering (indexer, indexer_id, season, episode, scene_season, scene_episode) VALUES (?,?,?,?,?,?)", [indexer, indexer_id, entry[sickbeard.indexerApi(indexer).config['xem_origin']]['season'], entry[sickbeard.indexerApi(indexer).config['xem_origin']]['episode'], entry['scene_2']['season'], entry['scene_2']['episode']]) else: logger.log(u'Failed to get XEM scene data for show %s from %s because "%s"' % ( indexer_id, sickbeard.indexerApi(indexer).name, result['message']), logger.DEBUG) else: logger.log(u"Empty lookup result - no XEM data for show %s on %s" % ( indexer_id, sickbeard.indexerApi(indexer).name,), logger.DEBUG) except Exception, e: logger.log(u"Exception while refreshing XEM data for show " + str(indexer_id) + " on " + sickbeard.indexerApi( indexer).name + ": " + ex(e), logger.WARNING) logger.log(traceback.format_exc(), logger.DEBUG) return None
def _retrieve_show_image(self, image_type, show_obj, which=None): """ Gets an image URL from theTVDB.com and TMDB.com, downloads it and returns the data. image_type: type of image to retrieve (currently supported: fanart, poster, banner) show_obj: a TVShow object to use when searching for the image which: optional, a specific numbered poster to look for Returns: the binary image data if available, or else None """ image_url = None indexer_lang = show_obj.lang try: # There's gotta be a better way of doing this but we don't wanna # change the language value elsewhere lINDEXER_API_PARMS = sickbeard.indexerApi(show_obj.indexer).api_params.copy() lINDEXER_API_PARMS['banners'] = True if indexer_lang and not indexer_lang == sickbeard.INDEXER_DEFAULT_LANGUAGE: lINDEXER_API_PARMS['language'] = indexer_lang if show_obj.dvdorder != 0: lINDEXER_API_PARMS['dvdorder'] = True t = sickbeard.indexerApi(show_obj.indexer).indexer(**lINDEXER_API_PARMS) indexer_show_obj = t[show_obj.indexerid] except (sickbeard.indexer_error, IOError) as e: logger.log(u"Unable to look up show on " + sickbeard.indexerApi( show_obj.indexer).name + ", not downloading images: " + ex(e), logger.WARNING) logger.log(u"%s may be experiencing some problems. Try again later." % sickbeard.indexerApi(show_obj.indexer).name, logger.DEBUG) return None if image_type not in ('fanart', 'poster', 'banner', 'poster_thumb', 'banner_thumb'): logger.log(u"Invalid image type " + str(image_type) + ", couldn't find it in the " + sickbeard.indexerApi( show_obj.indexer).name + " object", logger.ERROR) return None if image_type == 'poster_thumb': if getattr(indexer_show_obj, 'poster', None): image_url = re.sub('posters', '_cache/posters', indexer_show_obj['poster']) if not image_url: # Try and get images from TMDB image_url = self._retrieve_show_images_from_tmdb(show_obj, image_type) elif image_type == 'banner_thumb': if getattr(indexer_show_obj, 'banner', None): image_url = re.sub('graphical', '_cache/graphical', indexer_show_obj['banner']) else: if getattr(indexer_show_obj, image_type, None): image_url = indexer_show_obj[image_type] if not image_url: # Try and get images from TMDB image_url = self._retrieve_show_images_from_tmdb(show_obj, image_type) if image_url: image_data = metadata_helpers.getShowImage(image_url, which) return image_data return None
def get_xem_ids(): global xem_ids_list for iid, name in sickbeard.indexerApi().xem_supported_indexers.iteritems(): xem_ids = _xem_get_ids(name, sickbeard.indexerApi(iid).config['xem_origin']) if len(xem_ids): xem_ids_list[iid] = xem_ids
def _xem_exceptions_fetcher(): global xem_exception_dict xem_list = 'xem_us' for show in sickbeard.showList: if show.is_anime and not show.paused: xem_list = 'xem' break if shouldRefresh(xem_list): for indexer in [i for i in sickbeard.indexerApi().indexers if 'xem_origin' in sickbeard.indexerApi(i).config]: logger.log(u'Checking for XEM scene exception updates for %s' % sickbeard.indexerApi(indexer).name) url = 'http://thexem.de/map/allNames?origin=%s%s&seasonNumbers=1'\ % (sickbeard.indexerApi(indexer).config['xem_origin'], ('&language=us', '')['xem' == xem_list]) parsed_json = helpers.getURL(url, json=True, timeout=90) if not parsed_json: logger.log(u'Check scene exceptions update failed for %s, Unable to get URL: %s' % (sickbeard.indexerApi(indexer).name, url), logger.ERROR) continue if 'failure' == parsed_json['result']: continue for indexerid, names in parsed_json['data'].items(): try: xem_exception_dict[int(indexerid)] = names except: continue setLastRefresh(xem_list) return xem_exception_dict
def _season_banners_dict(self, show_obj, season): """ Should return a dict like: result = {<season number>: {1: '<url 1>', 2: <url 2>, ...},} """ # This holds our resulting dictionary of season art result = {} indexer_lang = show_obj.lang try: # There's gotta be a better way of doing this but we don't wanna # change the language value elsewhere lINDEXER_API_PARMS = sickbeard.indexerApi(show_obj.indexer).api_params.copy() lINDEXER_API_PARMS['banners'] = True if indexer_lang and not indexer_lang == sickbeard.INDEXER_DEFAULT_LANGUAGE: lINDEXER_API_PARMS['language'] = indexer_lang t = sickbeard.indexerApi(show_obj.indexer).indexer(**lINDEXER_API_PARMS) indexer_show_obj = t[show_obj.indexerid] except (sickbeard.indexer_error, IOError), e: logger.log(u"Unable to look up show on " + sickbeard.indexerApi( show_obj.indexer).name + ", not downloading images: " + ex(e), logger.WARNING) logger.log(u"Indexer " + sickbeard.indexerApi(show_obj.indexer).name + "maybe experiencing some problems. Try again later", logger.DEBUG) return result
def _ep_data(self, ep_obj): """ Creates an elementTree XML structure for a MediaBrowser style episode.xml and returns the resulting data object. show_obj: a TVShow instance to create the NFO for """ eps_to_write = [ep_obj] + ep_obj.relatedEps indexer_lang = ep_obj.show.lang try: # There's gotta be a better way of doing this but we don't wanna # change the language value elsewhere lINDEXER_API_PARMS = sickbeard.indexerApi(ep_obj.show.indexer).api_params.copy() if indexer_lang and not indexer_lang == sickbeard.INDEXER_DEFAULT_LANGUAGE: lINDEXER_API_PARMS['language'] = indexer_lang if ep_obj.show.dvdorder != 0: lINDEXER_API_PARMS['dvdorder'] = True t = sickbeard.indexerApi(ep_obj.show.indexer).indexer(**lINDEXER_API_PARMS) myShow = t[ep_obj.show.indexerid] except sickbeard.indexer_shownotfound, e: raise exceptions.ShowNotFoundException(e.message)
def _ep_data(self, ep_obj): """ Creates an elementTree XML structure for a WDTV style episode.xml and returns the resulting data object. ep_obj: a TVShow instance to create the NFO for """ eps_to_write = [ep_obj] + ep_obj.relatedEps indexer_lang = ep_obj.show.lang try: lINDEXER_API_PARMS = sickbeard.indexerApi(ep_obj.show.indexer).api_params.copy() lINDEXER_API_PARMS['actors'] = True if indexer_lang and not indexer_lang == sickbeard.INDEXER_DEFAULT_LANGUAGE: lINDEXER_API_PARMS['language'] = indexer_lang if ep_obj.show.dvdorder != 0: lINDEXER_API_PARMS['dvdorder'] = True t = sickbeard.indexerApi(ep_obj.show.indexer).indexer(**lINDEXER_API_PARMS) myShow = t[ep_obj.show.indexerid] except sickbeard.indexer_shownotfound, e: raise ShowNotFoundException(e.message)
def run(self, force=False): update_datetime = datetime.datetime.now() update_date = update_datetime.date() logger.log(u"Doing full update on all shows") # clean out cache directory, remove everything > 12 hours old if sickbeard.CACHE_DIR: for indexer in sickbeard.indexerApi().indexers: cache_dir = sickbeard.indexerApi(indexer).cache logger.log(u"Trying to clean cache folder " + cache_dir) # Does our cache_dir exists if not ek.ek(os.path.isdir, cache_dir): logger.log(u"Can't clean " + cache_dir + " if it doesn't exist", logger.WARNING) else: max_age = datetime.timedelta(hours=12) # Get all our cache files cache_files = ek.ek(os.listdir, cache_dir) for cache_file in cache_files: cache_file_path = ek.ek(os.path.join, cache_dir, cache_file) if ek.ek(os.path.isfile, cache_file_path): cache_file_modified = datetime.datetime.fromtimestamp( ek.ek(os.path.getmtime, cache_file_path)) if update_datetime - cache_file_modified > max_age: try: ek.ek(os.remove, cache_file_path) except OSError, e: logger.log(u"Unable to clean " + cache_dir + ": " + repr(e) + " / " + str(e), logger.WARNING) break
def _ep_data(self, ep_obj): """ Creates an elementTree XML structure for a Kodi-style episode.nfo and returns the resulting data object. show_obj: a TVEpisode instance to create the NFO for """ eps_to_write = [ep_obj] + ep_obj.relatedEps indexer_lang = ep_obj.show.lang lINDEXER_API_PARMS = sickbeard.indexerApi(ep_obj.show.indexer).api_params.copy() lINDEXER_API_PARMS["actors"] = True if indexer_lang and not indexer_lang == "en": lINDEXER_API_PARMS["language"] = indexer_lang if ep_obj.show.dvdorder != 0: lINDEXER_API_PARMS["dvdorder"] = True try: t = sickbeard.indexerApi(ep_obj.show.indexer).indexer(**lINDEXER_API_PARMS) myShow = t[ep_obj.show.indexerid] except sickbeard.indexer_shownotfound, e: raise exceptions.ShowNotFoundException(e.message)
def searchIndexerForShowID(regShowName, indexer=None, indexer_id=None, ui=True): showNames = list(set([re.sub('[. -]', ' ', regShowName), regShowName])) # Query Indexers for each search term and build the list of results for indexer in sickbeard.indexerApi().indexers if not indexer else [int(indexer)]: # Query Indexers for each search term and build the list of results lINDEXER_API_PARMS = sickbeard.indexerApi(indexer).api_params.copy() if ui:lINDEXER_API_PARMS['custom_ui'] = classes.ShowListUI t = sickbeard.indexerApi(indexer).indexer(**lINDEXER_API_PARMS) for name in showNames: logger.log(u"Trying to find " + name + " on " + sickbeard.indexerApi(indexer).name, logger.DEBUG) try: if indexer_id: search = t[indexer_id] else: search = t[name] if isinstance(search, dict): search = [search] # add search results for i in range(len(search)): part = search[i] seriesname = part['seriesname'].encode('UTF-8').lower() name = name.encode('UTF-8').lower() if (name in seriesname) or (indexer_id is not None and part['id'] == indexer_id): return [sickbeard.indexerApi(indexer).config['id'], part['id']] except KeyError:break except Exception:continue
def _xem_exceptions_fetcher(): exception_dict = {} if shouldRefresh('xem'): success = False for indexer in sickbeard.indexerApi().indexers: logger.log(u"Checking for XEM scene exception updates for " + sickbeard.indexerApi(indexer).name) url = "http://thexem.de/map/allNames?origin=%s&seasonNumbers=1" % sickbeard.indexerApi(indexer).config[ 'xem_origin'] url_data = helpers.getURL(url, json=True) if url_data is None: logger.log(u"Check scene exceptions update failed for " + sickbeard.indexerApi( indexer).name + ", Unable to get URL: " + url, logger.ERROR) continue if url_data['result'] == 'failure': continue for indexerid, names in url_data['data'].items(): exception_dict[int(indexerid)] = names success = True if success: setLastRefresh('xem') return exception_dict
def searchIndexerForShowID(regShowName, indexer=None, indexer_id=None, ui=None): showNames = list(set([re.sub('[. -]', ' ', regShowName), regShowName])) # Query Indexers for each search term and build the list of results for indexer in sickbeard.indexerApi().indexers if not indexer else [int(indexer)]: # Query Indexers for each search term and build the list of results lINDEXER_API_PARMS = sickbeard.indexerApi(indexer).api_params.copy() if ui: lINDEXER_API_PARMS['custom_ui'] = ui t = sickbeard.indexerApi(indexer).indexer(**lINDEXER_API_PARMS) for name in showNames: logger.log(u"Trying to find " + name + " on " + sickbeard.indexerApi(indexer).name, logger.DEBUG) try: search = t[indexer_id] if indexer_id else t[name] # add search results for i in range(len(search)): part = search[i] seriesname = part['seriesname'].lower() if str(name).lower() == seriesname or (indexer_id and part['id'] == indexer_id): return [sickbeard.indexerApi(indexer).config['id'], part['id']] except KeyError: if indexer: break else: continue except Exception: continue
def get_show_by_name(name, showList, useIndexer=False): logger.log(u"Trying to get the indexerid for " + name, logger.DEBUG) if showList: for show in showList: if _check_against_names(name, show): logger.log(u"Matched " + name + " in the showlist to the show " + show.name, logger.DEBUG) return show if useIndexer: for indexer in sickbeard.indexerApi().indexers: try: lINDEXER_API_PARMS = sickbeard.indexerApi(indexer).api_params.copy() lINDEXER_API_PARMS['custom_ui'] = classes.ShowListUI lINDEXER_API_PARMS['search_all_languages'] = True t = sickbeard.indexerApi(indexer).indexer(**lINDEXER_API_PARMS) showObj = t[name] except:continue if showObj: showResult = findCertainShow(sickbeard.showList, int(showObj["id"])) if showResult is not None: return showResult return None
def _xem_exceptions_fetcher(): global xem_exception_dict if shouldRefresh('xem'): for indexer in sickbeard.indexerApi().indexers: logger.log(u"Checking for XEM scene exception updates for " + sickbeard.indexerApi(indexer).name) url = "http://thexem.de/map/allNames?origin=%s&seasonNumbers=1" % sickbeard.indexerApi(indexer).config[ 'xem_origin'] parsedJSON = helpers.getURL(url, json=True) if not parsedJSON: logger.log(u"Check scene exceptions update failed for " + sickbeard.indexerApi( indexer).name + ", Unable to get URL: " + url, logger.ERROR) continue if parsedJSON['result'] == 'failure': continue for indexerid, names in parsedJSON['data'].items(): xem_exception_dict[int(indexerid)] = names setLastRefresh('xem') return xem_exception_dict
def _xem_exceptions_fetcher(): if shouldRefresh('xem'): for indexer in sickbeard.indexerApi().indexers: logger.log(u'Checking for XEM scene exception updates for {0}'.format (sickbeard.indexerApi(indexer).name)) url = 'http://thexem.de/map/allNames?origin={0}&seasonNumbers=1'.format(sickbeard.indexerApi(indexer).config['xem_origin']) parsedJSON = helpers.getURL(url, session=xem_session, timeout=90, returns='json') if not parsedJSON: logger.log(u'Check scene exceptions update failed for {0}, Unable to get URL: {1}'.format (sickbeard.indexerApi(indexer).name, url), logger.DEBUG) continue if parsedJSON['result'] == 'failure': continue if not parsedJSON['data']: logger.log(u'No data returned from XEM when checking scene exceptions. Update failed for {0}'.format (sickbeard.indexerApi(indexer).name), logger.DEBUG) continue for indexerid, names in iteritems(parsedJSON['data']): try: xem_exception_dict[int(indexerid)] = names except Exception as error: logger.log(u'XEM: Rejected entry: indexerid:{0}; names:{1}'.format(indexerid, names), logger.WARNING) logger.log(u'XEM: Rejected entry error message:{0}'.format(error), logger.DEBUG) setLastRefresh('xem') return xem_exception_dict
def _season_posters_dict(self, show_obj, season): """ Should return a dict like: result = {<season number>: {1: '<url 1>', 2: <url 2>, ...},} """ # This holds our resulting dictionary of season art result = {} indexer_lang = show_obj.lang try: # There's gotta be a better way of doing this but we don't wanna # change the language value elsewhere lINDEXER_API_PARMS = sickbeard.indexerApi(show_obj.indexer).api_params.copy() lINDEXER_API_PARMS['banners'] = True if indexer_lang and not indexer_lang == 'en': lINDEXER_API_PARMS['language'] = indexer_lang if show_obj.dvdorder != 0: lINDEXER_API_PARMS['dvdorder'] = True t = sickbeard.indexerApi(show_obj.indexer).indexer(**lINDEXER_API_PARMS) indexer_show_obj = t[show_obj.indexerid] except (sickbeard.indexer_error, IOError), e: logger.log(u"Unable to look up show on " + sickbeard.indexerApi( show_obj.indexer).name + ", not downloading images: " + ex(e), logger.ERROR) return result
def _xem_exceptions_fetcher(): global xem_exception_dict global xem_session if shouldRefresh('xem'): for indexer in sickbeard.indexerApi().indexers: logger.log(u"Checking for XEM scene exception updates for " + sickbeard.indexerApi(indexer).name) url = "http://thexem.de/map/allNames?origin=%s&seasonNumbers=1" % sickbeard.indexerApi(indexer).config[ 'xem_origin'] parsedJSON = helpers.getURL(url, session=xem_session, json=True) if not parsedJSON: logger.log(u"Check scene exceptions update failed for " + sickbeard.indexerApi( indexer).name + ", Unable to get URL: " + url, logger.ERROR) continue if parsedJSON['result'] == 'failure': continue for indexerid, names in parsedJSON['data'].iteritems(): try: xem_exception_dict[int(indexerid)] = names except Exception as e: logger.log(u"XEM: Rejected entry: indexerid:{0}; names:{1}".format(indexerid, names), logger.WARNING) logger.log(u"XEM: Rejected entry error message:{0}".format(str(e)), logger.DEBUG) setLastRefresh('xem') return xem_exception_dict
def _season_posters_dict(self, show_obj, season): """ Should return a dict like: result = {<season number>: {1: '<url 1>', 2: <url 2>, ...},} """ # This holds our resulting dictionary of season art result = {} indexer_lang = show_obj.lang try: # There's gotta be a better way of doing this but we don't wanna # change the language value elsewhere lINDEXER_API_PARMS = sickbeard.indexerApi(show_obj.indexer).api_params.copy() lINDEXER_API_PARMS['banners'] = True if indexer_lang and not indexer_lang == sickbeard.INDEXER_DEFAULT_LANGUAGE: lINDEXER_API_PARMS['language'] = indexer_lang if show_obj.dvdorder != 0: lINDEXER_API_PARMS['dvdorder'] = True t = sickbeard.indexerApi(show_obj.indexer).indexer(**lINDEXER_API_PARMS) indexer_show_obj = t[show_obj.indexerid] except (sickbeard.indexer_error, IOError) as e: logger.log(u"Unable to look up show on " + sickbeard.indexerApi( show_obj.indexer).name + ", not downloading images: " + ex(e), logger.WARNING) logger.log(u"%s may be experiencing some problems. Try again later." % sickbeard.indexerApi(show_obj.indexer).name, logger.DEBUG) return result # if we have no season banners then just finish if not getattr(indexer_show_obj, '_banners', None): return result if 'season' not in indexer_show_obj['_banners'] or 'season' not in indexer_show_obj['_banners']['season']: return result # Give us just the normal poster-style season graphics seasonsArtObj = indexer_show_obj['_banners']['season']['season'] # Returns a nested dictionary of season art with the season # number as primary key. It's really overkill but gives the option # to present to user via ui to pick down the road. result[season] = {} # find the correct season in the TVDB object and just copy the dict into our result dict for seasonArtID in seasonsArtObj.keys(): if int(seasonsArtObj[seasonArtID]['season']) == season and seasonsArtObj[seasonArtID]['language'] == sickbeard.INDEXER_DEFAULT_LANGUAGE: result[season][seasonArtID] = seasonsArtObj[seasonArtID]['_bannerpath'] return result
def execute(self): ShowQueueItem.execute(self) logger.log(u"Beginning update of " + self.show.name) logger.log(u"Retrieving show info from " + sickbeard.indexerApi(self.show.indexer).name + "", logger.DEBUG) try: self.show.loadFromIndexer(cache=not self.force) except sickbeard.indexer_error, e: logger.log(u"Unable to contact " + sickbeard.indexerApi(self.show.indexer).name + ", aborting: " + ex(e), logger.WARNING) return
def run(self): ShowQueueItem.run(self) logger.log(u'Beginning update of ' + self.show.name) logger.log(u'Retrieving show info from ' + sickbeard.indexerApi(self.show.indexer).name + '', logger.DEBUG) try: result = self.show.loadFromIndexer(cache=not self.force) if None is not result: return except sickbeard.indexer_error, e: logger.log(u'Unable to contact ' + sickbeard.indexerApi(self.show.indexer).name + ', aborting: ' + ex(e), logger.WARNING) return
def addEpisodeToTraktWatchList(self): if sickbeard.TRAKT_SYNC_WATCHLIST and sickbeard.USE_TRAKT: logger.log(u"WATCHLIST::ADD::START - Look for Episodes to Add to Trakt Watchlist", logger.DEBUG) myDB = db.DBConnection() sql_selection = 'select tv_shows.indexer, tv_shows.startyear, showid, show_name, season, episode from tv_episodes,tv_shows where tv_shows.indexer_id = tv_episodes.showid and tv_episodes.status in (' + ','.join([str(x) for x in Quality.SNATCHED + Quality.SNATCHED_PROPER + [WANTED]]) + ')' episodes = myDB.select(sql_selection) if episodes is not None: trakt_data = [] for cur_episode in episodes: trakt_id = sickbeard.indexerApi(cur_episode["indexer"]).config['trakt_id'] if not self._checkInList(trakt_id, str(cur_episode["showid"]), str(cur_episode["season"]), str(cur_episode["episode"])): logger.log(u"Adding Episode %s S%02dE%02d to watchlist" % (cur_episode["show_name"], cur_episode["season"], cur_episode["episode"]), logger.DEBUG) trakt_data.append((cur_episode["showid"], cur_episode["indexer"], cur_episode["show_name"], cur_episode["startyear"], cur_episode["season"], cur_episode["episode"])) if len(trakt_data): try: data = self.trakt_bulk_data_generate(trakt_data) self.trakt_api.traktRequest("sync/watchlist", data, method='POST') self._getEpisodeWatchlist() except traktException as e: logger.log(u"Could not connect to Trakt service. Error %s" % ex(e), logger.WARNING) logger.log(u"WATCHLIST::ADD::FINISH - Look for Episodes to Add to Trakt Watchlist", logger.DEBUG)
def updateShows(self): logger.log(u"SHOW_WATCHLIST::CHECK::START - Trakt Show Watchlist", logger.DEBUG) if not len(self.ShowWatchlist): logger.log(u"No shows found in your watchlist, aborting watchlist update", logger.DEBUG) return indexer = int(sickbeard.TRAKT_DEFAULT_INDEXER) trakt_id = sickbeard.indexerApi(indexer).config['trakt_id'] for show_el in self.ShowWatchlist[trakt_id]: indexer_id = int(str(show_el)) show = self.ShowWatchlist[trakt_id][show_el] # logger.log(u"Checking Show: %s %s %s" % (trakt_id, indexer_id, show['title']),logger.DEBUG) if int(sickbeard.TRAKT_METHOD_ADD) != 2: self.addDefaultShow(indexer, indexer_id, show['title'], SKIPPED) else: self.addDefaultShow(indexer, indexer_id, show['title'], WANTED) if int(sickbeard.TRAKT_METHOD_ADD) == 1: newShow = Show.find(sickbeard.showList, indexer_id) if newShow is not None: setEpisodeToWanted(newShow, 1, 1) else: self.todoWanted.append((indexer_id, 1, 1)) logger.log(u"SHOW_WATCHLIST::CHECK::FINISH - Trakt Show Watchlist", logger.DEBUG)
def addShowToTraktWatchList(self): if sickbeard.TRAKT_SYNC_WATCHLIST and sickbeard.USE_TRAKT: logger.log(u"SHOW_WATCHLIST::ADD::START - Look for Shows to Add to Trakt Watchlist", logger.DEBUG) if sickbeard.showList is not None: trakt_data = [] for show in sickbeard.showList: trakt_id = sickbeard.indexerApi(show.indexer).config['trakt_id'] if not self._checkInList(trakt_id, str(show.indexerid), '0', '0', List='Show'): logger.log(u"Adding Show: Indexer %s %s - %s to Watchlist" % (trakt_id, str(show.indexerid), show.name), logger.DEBUG) show_el = {'title': show.name, 'year': show.startyear, 'ids': {}} if trakt_id == 'tvdb_id': show_el['ids']['tvdb'] = show.indexerid else: show_el['ids']['tvrage'] = show.indexerid trakt_data.append(show_el) if len(trakt_data): try: data = {'shows': trakt_data} self.trakt_api.traktRequest("sync/watchlist", data, method='POST') self._getShowWatchlist() except traktException as e: logger.log(u"Could not connect to Trakt service. Error: %s" % ex(e), logger.WARNING) logger.log(u"SHOW_WATCHLIST::ADD::FINISH - Look for Shows to Add to Trakt Watchlist", logger.DEBUG)
def addShowToTraktLibrary(self, show_obj): """ Sends a request to trakt indicating that the given show and all its episodes is part of our library. show_obj: The TVShow object to add to trakt """ data = {} if not self.findShow(show_obj.indexer, show_obj.indexerid): trakt_id = sickbeard.indexerApi(show_obj.indexer).config['trakt_id'] # URL parameters data = { 'shows': [ { 'title': show_obj.name, 'year': show_obj.startyear, 'ids': {} } ] } if trakt_id == 'tvdb_id': data['shows'][0]['ids']['tvdb'] = show_obj.indexerid else: data['shows'][0]['ids']['tvrage'] = show_obj.indexerid if len(data): logger.log(u"Adding %s to trakt.tv library" % show_obj.name, logger.DEBUG) try: self.trakt_api.traktRequest("sync/collection", data, method='POST') except traktException as e: logger.log(u"Could not connect to Trakt service. Aborting adding show %s to Trakt library. Error: %s" % (show_obj.name, repr(e)), logger.WARNING) return
def removeEpisodeFromTraktCollection(self): if sickbeard.TRAKT_SYNC_REMOVE and sickbeard.TRAKT_SYNC and sickbeard.USE_TRAKT: logger.log(u"COLLECTION::REMOVE::START - Look for Episodes to Remove From Trakt Collection", logger.DEBUG) myDB = db.DBConnection() sql_selection = 'select tv_shows.indexer, tv_shows.startyear, showid, show_name, season, episode, tv_episodes.status, tv_episodes.location from tv_episodes,tv_shows where tv_shows.indexer_id = tv_episodes.showid' episodes = myDB.select(sql_selection) if episodes is not None: trakt_data = [] for cur_episode in episodes: trakt_id = sickbeard.indexerApi(cur_episode["indexer"]).config['trakt_id'] if self._checkInList(trakt_id, str(cur_episode["showid"]), str(cur_episode["season"]), str(cur_episode["episode"]), List='Collection'): if cur_episode["location"] == '': logger.log(u"Removing Episode %s S%02dE%02d from collection" % (cur_episode["show_name"], cur_episode["season"], cur_episode["episode"]), logger.DEBUG) trakt_data.append((cur_episode["showid"], cur_episode["indexer"], cur_episode["show_name"], cur_episode["startyear"], cur_episode["season"], cur_episode["episode"])) if len(trakt_data): try: data = self.trakt_bulk_data_generate(trakt_data) self.trakt_api.traktRequest("sync/collection/remove", data, method='POST') self._getShowCollection() except traktException as e: logger.log(u"Could not connect to Trakt service. Error: %s" % ex(e), logger.WARNING) logger.log(u"COLLECTION::REMOVE::FINISH - Look for Episodes to Remove From Trakt Collection", logger.DEBUG)
def validateShow(show, season=None, episode=None): indexer_lang = show.lang try: lINDEXER_API_PARMS = sickbeard.indexerApi(show.indexer).api_params.copy() if indexer_lang and not indexer_lang == 'en': lINDEXER_API_PARMS['language'] = indexer_lang t = sickbeard.indexerApi(show.indexer).indexer(**lINDEXER_API_PARMS) if season is None and episode is None: return t return t[show.indexerid][season][episode] except (sickbeard.indexer_episodenotfound, sickbeard.indexer_seasonnotfound): pass
def removeShowFromTraktLibrary(self, show_obj): if self.findShow(show_obj.indexer, show_obj.indexerid): trakt_id = sickbeard.indexerApi(show_obj.indexer).config['trakt_id'] # URL parameters data = { 'shows': [ { 'title': show_obj.name, 'year': show_obj.startyear, 'ids': {} } ] } if trakt_id == 'tvdb_id': data['shows'][0]['ids']['tvdb'] = show_obj.indexerid else: data['shows'][0]['ids']['tvrage'] = show_obj.indexerid logger.log(u"Removing %s from trakt.tv library" % show_obj.name, logger.DEBUG) try: self.trakt_api.traktRequest("sync/collection/remove", data, method='POST') except traktException as e: logger.log(u"Could not connect to Trakt service. Aborting removing show %s from Trakt library. Error: %s" % (show_obj.name, repr(e)), logger.WARNING)
self.show.saveToDB() except Exception, e: logger.log(u"Error saving the show to the database: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) self._finishEarly() raise # add it to the show list sickbeard.showList.append(self.show) try: self.show.loadEpisodesFromIndexer() except Exception, e: logger.log( u"Error with " + sickbeard.indexerApi(self.show.indexer).name + ", not creating episode list: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) try: self.show.loadEpisodesFromDir() except Exception, e: logger.log(u"Error searching dir for episodes: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) # if they gave a custom status then change all the eps to it if self.default_status != SKIPPED: logger.log( u"Setting all episodes to the specified default status: " + str(self.default_status))
def execute(self): ShowQueueItem.execute(self) logger.log(u"Starting to add show " + self.showDir) # make sure the Indexer IDs are valid try: lINDEXER_API_PARMS = sickbeard.indexerApi( self.indexer).api_params.copy() if self.lang: lINDEXER_API_PARMS['language'] = self.lang logger.log(u"" + sickbeard.indexerApi(self.indexer).name + ": " + repr(lINDEXER_API_PARMS)) t = sickbeard.indexerApi( self.indexer).indexer(**lINDEXER_API_PARMS) s = t[self.indexer_id] # this usually only happens if they have an NFO in their show dir which gave us a Indexer ID that has no proper english version of the show if getattr(s, 'seriesname', None) is None: logger.log( u"Show in " + self.showDir + " has no name on " + sickbeard.indexerApi(self.indexer).name + ", probably the wrong language used to search with.", logger.ERROR) ui.notifications.error( "Unable to add show", "Show in " + self.showDir + " has no name on " + sickbeard.indexerApi(self.indexer).name + ", probably the wrong language. Delete .nfo and add manually in the correct language." ) self._finishEarly() return # if the show has no episodes/seasons if not s: logger.log( u"Show " + str(s['seriesname']) + " is on " + sickbeard.indexerApi(self.indexer).name + " but contains no season/episode data.", logger.ERROR) ui.notifications.error( "Unable to add show", "Show " + str(s['seriesname']) + " is on " + sickbeard.indexerApi(self.indexer).name + " but contains no season/episode data.") self._finishEarly() return except Exception, e: logger.log( u"Unable to find show ID:" + str(self.indexer_id) + " on Indexer: " + sickbeard.indexerApi(self.indexer).name, logger.ERROR) ui.notifications.error( "Unable to add show", "Unable to look up the show in " + self.showDir + " on " + sickbeard.indexerApi(self.indexer).name + " using ID " + str(self.indexer_id) + ", not using the NFO. Delete .nfo and try adding manually again." ) self._finishEarly() return
def _show_data(self, show_obj): """ Creates an elementTree XML structure for a MediaBrowser-style series.xml returns the resulting data object. show_obj: a TVShow instance to create the NFO for """ indexer_lang = show_obj.lang lINDEXER_API_PARMS = sickbeard.indexerApi( show_obj.indexer).api_params.copy() lINDEXER_API_PARMS['actors'] = True if indexer_lang and not indexer_lang == sickbeard.INDEXER_DEFAULT_LANGUAGE: lINDEXER_API_PARMS['language'] = indexer_lang if show_obj.dvdorder != 0: lINDEXER_API_PARMS['dvdorder'] = True t = sickbeard.indexerApi( show_obj.indexer).indexer(**lINDEXER_API_PARMS) 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: myShow = t[int(show_obj.indexerid)] except sickbeard.indexer_shownotfound: logger.log( u"Unable to find show with id " + str(show_obj.indexerid) + " on tvdb, skipping it", logger.ERROR) raise except sickbeard.indexer_error: logger.log(u"TVDB is down, can't use its data to make the NFO", logger.ERROR) raise # check for title and id if not (getattr(myShow, 'seriesname', None) and getattr(myShow, 'id', None)): logger.log(u"Incomplete info for show with id " + str(show_obj.indexerid) + " on " + sickbeard.indexerApi(show_obj.indexer).name + ", skipping it") return False SeriesName = etree.SubElement(tv_node, "title") SeriesName.text = myShow['seriesname'] if getattr(myShow, "genre", None): Genres = etree.SubElement(tv_node, "genres") for genre in myShow['genre'].split('|'): if genre and genre.strip(): cur_genre = etree.SubElement(Genres, "Genre") cur_genre.text = genre.strip() if getattr(myShow, 'firstaired', None): FirstAired = etree.SubElement(tv_node, "premiered") FirstAired.text = myShow['firstaired'] if getattr(myShow, "firstaired", None): try: year_text = str( datetime.datetime.strptime(myShow["firstaired"], dateFormat).year) if year_text: year = etree.SubElement(tv_node, "year") year.text = year_text except Exception: pass if getattr(myShow, 'overview', None): plot = etree.SubElement(tv_node, "plot") plot.text = myShow["overview"] if getattr(myShow, 'rating', None): try: rating = int(float(myShow['rating']) * 10) except ValueError: rating = 0 if rating: Rating = etree.SubElement(tv_node, "rating") Rating.text = str(rating) if getattr(myShow, 'status', None): Status = etree.SubElement(tv_node, "status") Status.text = myShow['status'] if getattr(myShow, "contentrating", None): mpaa = etree.SubElement(tv_node, "mpaa") mpaa.text = myShow["contentrating"] if getattr(myShow, 'imdb_id', None): imdb_id = etree.SubElement(tv_node, "id") imdb_id.attrib["moviedb"] = "imdb" imdb_id.text = myShow['imdb_id'] if getattr(myShow, 'id', None): indexerid = etree.SubElement(tv_node, "indexerid") indexerid.text = myShow['id'] if getattr(myShow, 'runtime', None): Runtime = etree.SubElement(tv_node, "runtime") Runtime.text = myShow['runtime'] if getattr(myShow, '_actors', None): cast = etree.SubElement(tv_node, "cast") for actor in myShow['_actors']: if 'name' in actor and actor['name'].strip(): cur_actor = etree.SubElement(cast, "actor") cur_actor.text = actor['name'].strip() helpers.indentXML(rootNode) data = etree.ElementTree(rootNode) return data
def xem_refresh(indexer_id, indexer, force=False): """ Refresh data from xem for a tv show @param indexer_id: int """ if None is indexer_id: return indexer_id = int(indexer_id) indexer = int(indexer) if 'xem_origin' not in sickbeard.indexerApi( indexer).config or indexer_id not in xem_ids_list.get(indexer, []): return # XEM API URL url = 'http://thexem.de/map/all?id=%s&origin=%s&destination=scene' % ( indexer_id, sickbeard.indexerApi(indexer).config['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 = ?', [indexer, indexer_id]) 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' % (indexer_id, sickbeard.indexerApi(indexer).name), logger.DEBUG) # mark refreshed my_db.upsert( 'xem_refresh', { 'indexer': indexer, 'last_refreshed': int(time.mktime(datetime.datetime.today().timetuple())) }, {'indexer_id': indexer_id}) try: parsed_json = sickbeard.helpers.getURL(url, json=True, timeout=90) if not parsed_json or '' == parsed_json: logger.log( u'No XEM data for show %s on %s' % (indexer_id, sickbeard.indexerApi(indexer).name), logger.MESSAGE) return if 'success' in parsed_json['result']: cl = [] for entry in parsed_json['data']: if 'scene' in entry: cl.append([ 'UPDATE tv_episodes SET scene_season = ?, scene_episode = ?, scene_absolute_number = ? WHERE showid = ? AND season = ? AND episode = ?', [ entry['scene']['season'], entry['scene']['episode'], entry['scene']['absolute'], indexer_id, entry[sickbeard.indexerApi( indexer).config['xem_origin']]['season'], entry[sickbeard.indexerApi( indexer).config['xem_origin']]['episode'] ] ]) if 'scene_2' in entry: # for doubles cl.append([ 'UPDATE tv_episodes SET scene_season = ?, scene_episode = ?, scene_absolute_number = ? WHERE showid = ? AND season = ? AND episode = ?', [ entry['scene_2']['season'], entry['scene_2']['episode'], entry['scene_2']['absolute'], indexer_id, entry[sickbeard.indexerApi( indexer).config['xem_origin']]['season'], entry[sickbeard.indexerApi( indexer).config['xem_origin']]['episode'] ] ]) 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' % (indexer_id, sickbeard.indexerApi(indexer).name), logger.DEBUG) except Exception as e: logger.log( u'Exception while refreshing XEM data for show ' + str(indexer_id) + ' on ' + sickbeard.indexerApi(indexer).name + ': ' + ex(e), logger.WARNING) logger.log(traceback.format_exc(), logger.ERROR)
class TIVOMetadata(generic.GenericMetadata): """ Metadata generation class for TIVO The following file structure is used: show_root/Season ##/filename.ext (*) show_root/Season ##/.meta/filename.ext.txt (episode metadata) This class only generates episode specific metadata files, it does NOT generate a default.txt file. """ def __init__(self, show_metadata=False, episode_metadata=False, fanart=False, poster=False, banner=False, episode_thumbnails=False, season_posters=False, season_banners=False, season_all_poster=False, season_all_banner=False): generic.GenericMetadata.__init__(self, show_metadata, episode_metadata, fanart, poster, banner, episode_thumbnails, season_posters, season_banners, season_all_poster, season_all_banner) self.name = 'TIVO' self._ep_nfo_extension = "txt" # web-ui metadata template self.eg_show_metadata = "<i>not supported</i>" self.eg_episode_metadata = "Season##\\.meta\\<i>filename</i>.ext.txt" self.eg_fanart = "<i>not supported</i>" self.eg_poster = "<i>not supported</i>" self.eg_banner = "<i>not supported</i>" self.eg_episode_thumbnails = "<i>not supported</i>" self.eg_season_posters = "<i>not supported</i>" self.eg_season_banners = "<i>not supported</i>" self.eg_season_all_poster = "<i>not supported</i>" self.eg_season_all_banner = "<i>not supported</i>" # Override with empty methods for unsupported features def retrieveShowMetadata(self, folder): # no show metadata generated, we abort this lookup function return (None, None, None) def create_show_metadata(self, show_obj, force=False): pass def get_show_file_path(self, show_obj): pass def create_fanart(self, show_obj): pass def create_poster(self, show_obj): pass def create_banner(self, show_obj): pass def create_episode_thumb(self, ep_obj): pass def get_episode_thumb_path(self, ep_obj): pass def create_season_posters(self, ep_obj): pass def create_season_banners(self, ep_obj): pass def create_season_all_poster(self, show_obj): pass def create_season_all_banner(self, show_obj): pass # Override generic class def get_episode_file_path(self, ep_obj): """ Returns a full show dir/.meta/episode.txt path for Tivo episode metadata files. Note, that pyTivo requires the metadata filename to include the original extention. ie If the episode name is foo.avi, the metadata name is foo.avi.txt ep_obj: a TVEpisode object to get the path for """ if ek.ek(os.path.isfile, ep_obj.location): metadata_file_name = ek.ek( os.path.basename, ep_obj.location) + "." + self._ep_nfo_extension metadata_dir_name = ek.ek(os.path.join, ek.ek(os.path.dirname, ep_obj.location), '.meta') metadata_file_path = ek.ek(os.path.join, metadata_dir_name, metadata_file_name) else: logger.log( u"Episode location doesn't exist: " + str(ep_obj.location), logger.DEBUG) return '' return metadata_file_path def _ep_data(self, ep_obj): """ Creates a key value structure for a Tivo episode metadata file and returns the resulting data object. ep_obj: a TVEpisode instance to create the metadata file for. Lookup the show in http://thetvdb.com/ using the python library: https://github.com/dbr/indexer_api/ The results are saved in the object myShow. The key values for the tivo metadata file are from: http://pytivo.sourceforge.net/wiki/index.php/Metadata """ data = "" eps_to_write = [ep_obj] + ep_obj.relatedEps indexer_lang = ep_obj.show.lang try: lINDEXER_API_PARMS = sickbeard.indexerApi( ep_obj.show.indexer).api_params.copy() lINDEXER_API_PARMS['actors'] = True if indexer_lang and not indexer_lang == 'en': lINDEXER_API_PARMS['language'] = indexer_lang if ep_obj.show.dvdorder != 0: lINDEXER_API_PARMS['dvdorder'] = True t = sickbeard.indexerApi( ep_obj.show.indexer).indexer(**lINDEXER_API_PARMS) myShow = t[ep_obj.show.indexerid] except sickbeard.indexer_shownotfound, e: raise exceptions.ShowNotFoundException(str(e)) except sickbeard.indexer_error, e: logger.log( u"Unable to connect to " + sickbeard.indexerApi(ep_obj.show.indexer).name + " while creating meta files - skipping - " + str(e), logger.ERROR) return False
def get_show_by_name(name, checkExceptions=False, checkIndexers=False): in_cache = False foundResult = None logger.log( u"Checking the cache for:" + str(name), logger.DEBUG) cacheResult = sickbeard.name_cache.retrieveNameFromCache(name) if cacheResult: foundResult = findCertainShow(sickbeard.showList, cacheResult) if foundResult: in_cache = True logger.log( u"Cache lookup found Indexer ID:" + repr( foundResult.indexerid) + ", using that for " + name, logger.DEBUG) if not foundResult: logger.log( u"Checking the database for:" + str(name), logger.DEBUG) dbResult = searchDBForShow(name) if dbResult: foundResult = findCertainShow(sickbeard.showList, dbResult[1]) if foundResult: logger.log( u"Database lookup found Indexer ID:" + str( foundResult.indexerid) + ", using that for " + name, logger.DEBUG) if not foundResult and checkExceptions: if not foundResult: logger.log( u"Checking the scene exceptions list for:" + str(name), logger.DEBUG) for show in sickbeard.showList: if _check_against_names(name, show): logger.log( u"Scene exceptions lookup found Indexer ID:" + str(show.indexerid) + ", using that for " + name, logger.DEBUG) foundResult = show if not foundResult and checkIndexers: logger.log( u"Checking the Indexers for:" + str(name), logger.DEBUG) for indexer in sickbeard.indexerApi().indexers: try: lINDEXER_API_PARMS = sickbeard.indexerApi(indexer).api_params.copy() lINDEXER_API_PARMS['custom_ui'] = classes.ShowListUI lINDEXER_API_PARMS['search_all_languages'] = True t = sickbeard.indexerApi(indexer).indexer(**lINDEXER_API_PARMS) showObj = t[name] except: continue if showObj: foundResult = findCertainShow(sickbeard.showList, int(showObj["id"])) if foundResult: logger.log( u"Indexers lookup found Indexer ID:" + str( foundResult.indexerid) + ", using that for " + name, logger.DEBUG) # add to name cache if we didn't get it from the cache if foundResult and not in_cache: sickbeard.name_cache.addNameToCache(name, foundResult.indexerid) return foundResult
def run(self): # pylint: disable=too-many-branches, too-many-statements, too-many-return-statements super(QueueItemAdd, self).run() if self.showDir: try: assert isinstance(self.showDir, six.text_type) except AssertionError: logger.log(traceback.format_exc(), logger.WARNING) self._finish_early() return logger.log('Starting to add show {0}'.format( 'by ShowDir: {0}'.format(self.showDir) if self. showDir else 'by Indexer Id: {0}'.format(self.indexer_id))) # make sure the Indexer IDs are valid try: lINDEXER_API_PARMS = sickbeard.indexerApi( self.indexer).api_params.copy() lINDEXER_API_PARMS[ 'language'] = self.lang or sickbeard.INDEXER_DEFAULT_LANGUAGE logger.log('{0}: {1!r}'.format( sickbeard.indexerApi(self.indexer).name, lINDEXER_API_PARMS)) t = sickbeard.indexerApi( self.indexer).indexer(**lINDEXER_API_PARMS) s = t[self.indexer_id] # Let's try to create the show Dir if it's not provided. This way we force the show dir to build build using the # Indexers provided series name if self.root_dir and not self.showDir: show_name = get_showname_from_indexer(self.indexer, self.indexer_id, self.lang) if not show_name: logger.log( 'Unable to get a show {0}, can\'t add the show'.format( self.showDir)) self._finish_early() return self.showDir = ek(os.path.join, self.root_dir, sanitize_filename(show_name)) dir_exists = makeDir(self.showDir) if not dir_exists: logger.log( 'Unable to create the folder {0}, can\'t add the show'. format(self.showDir)) self._finish_early() return chmodAsParent(self.showDir) # this usually only happens if they have an NFO in their show dir which gave us a Indexer ID that has no proper english version of the show if getattr(s, 'seriesname', None) is None: # noinspection PyPep8 error_string = 'Show in {0} has no name on {1}, probably searched with the wrong language. Delete .nfo and add manually in the correct language.'.format( self.showDir, sickbeard.indexerApi(self.indexer).name) logger.log(error_string, logger.WARNING) ui.notifications.error('Unable to add show', error_string) self._finish_early() return # if the show has no episodes/seasons if not s: error_string = 'Show {0} is on {1} but contains no season/episode data.'.format( s[b'seriesname'], sickbeard.indexerApi(self.indexer).name) logger.log(error_string) ui.notifications.error('Unable to add show', error_string) self._finish_early() return except Exception as error: error_string = 'Unable to look up the show in {0} on {1} using ID {2}, not using the NFO. Delete .nfo and try adding manually again.'.format( self.showDir, sickbeard.indexerApi(self.indexer).name, self.indexer_id) logger.log('{0}: {1}'.format(error_string, error), logger.ERROR) ui.notifications.error('Unable to add show', error_string) if sickbeard.USE_TRAKT: trakt_id = sickbeard.indexerApi( self.indexer).config[b'trakt_id'] trakt_api = TraktAPI(sickbeard.SSL_VERIFY, sickbeard.TRAKT_TIMEOUT) title = self.showDir.split('/')[-1] data = {'shows': [{'title': title, 'ids': {}}]} if trakt_id == 'tvdb_id': data['shows'][0]['ids']['tvdb'] = self.indexer_id else: data['shows'][0]['ids']['tvrage'] = self.indexer_id trakt_api.traktRequest('sync/watchlist/remove', data, method='POST') self._finish_early() return try: try: newShow = TVShow(self.indexer, self.indexer_id, self.lang) except MultipleShowObjectsException as error: # If we have the show in our list, but the location is wrong, lets fix it and refresh! existing_show = Show.find(sickbeard.showList, self.indexer_id) # noinspection PyProtectedMember if existing_show and not ek(os.path.isdir, existing_show._location): # pylint: disable=protected-access newShow = existing_show else: raise error newShow.loadFromIndexer() self.show = newShow # set up initial values self.show.location = self.showDir self.show.subtitles = self.subtitles if self.subtitles is not None else sickbeard.SUBTITLES_DEFAULT self.show.subtitles_sr_metadata = self.subtitles_sr_metadata self.show.quality = self.quality if self.quality else sickbeard.QUALITY_DEFAULT self.show.season_folders = self.season_folders if self.season_folders is not None else sickbeard.SEASON_FOLDERS_DEFAULT self.show.anime = self.anime if self.anime is not None else sickbeard.ANIME_DEFAULT self.show.scene = self.scene if self.scene is not None else sickbeard.SCENE_DEFAULT self.show.paused = self.paused if self.paused is not None else False # set up default new/missing episode status logger.log( 'Setting all episodes to the specified default status: {0}'. format(self.show.default_ep_status)) self.show.default_ep_status = self.default_status if self.show.anime: self.show.release_groups = BlackAndWhiteList( self.show.indexerid) if self.blacklist: self.show.release_groups.set_black_keywords(self.blacklist) if self.whitelist: self.show.release_groups.set_white_keywords(self.whitelist) # # be smart-ish about this # if self.show.genre and 'talk show' in self.show.genre.lower(): # self.show.air_by_date = 1 # if self.show.genre and 'documentary' in self.show.genre.lower(): # self.show.air_by_date = 0 # if self.show.classification and 'sports' in self.show.classification.lower(): # self.show.sports = 1 except sickbeard.indexer_exception as error: error_string = 'Unable to add {0} due to an error with {1}'.format( self.show.name if self.show else 'show', sickbeard.indexerApi(self.indexer).name) logger.log('{0}: {1}'.format(error_string, error), logger.ERROR) ui.notifications.error('Unable to add show', error_string) self._finish_early() return except MultipleShowObjectsException: error_string = 'The show in {0} is already in your show list, skipping'.format( self.showDir) logger.log(error_string, logger.WARNING) ui.notifications.error('Show skipped', error_string) self._finish_early() return except Exception as error: logger.log('Error trying to add show: {0}'.format(error), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) self._finish_early() raise logger.log('Retrieving show info from IMDb', logger.DEBUG) try: self.show.loadIMDbInfo() except imdb_exceptions.IMDbError as error: logger.log(' Something wrong on IMDb api: {0}'.format(error), logger.WARNING) except Exception as error: logger.log('Error loading IMDb info: {0}'.format(error), logger.ERROR) try: self.show.saveToDB() except Exception as error: logger.log( 'Error saving the show to the database: {0}'.format(error), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) self._finish_early() raise # add it to the show list if not Show.find(sickbeard.showList, self.indexer_id): sickbeard.showList.append(self.show) try: self.show.loadEpisodesFromIndexer() except Exception as error: logger.log( 'Error with {0}, not creating episode list: {1}'.format( sickbeard.indexerApi(self.show.indexer).name, error), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) # update internal name cache name_cache.buildNameCache(self.show) try: self.show.loadEpisodesFromDir() except Exception as error: logger.log('Error searching dir for episodes: {0}'.format(error), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) # if they set default ep status to WANTED then run the backlog to search for episodes # FIXME: This needs to be a backlog queue item!!! if self.show.default_ep_status == WANTED: logger.log( 'Launching backlog for this show since its episodes are WANTED' ) sickbeard.backlogSearchScheduler.action.searchBacklog([self.show]) self.show.writeMetadata() self.show.updateMetadata() self.show.populateCache() self.show.flushEpisodes() if sickbeard.USE_TRAKT: # if there are specific episodes that need to be added by trakt sickbeard.traktCheckerScheduler.action.manageNewShow(self.show) # add show to trakt.tv library if sickbeard.TRAKT_SYNC: sickbeard.traktCheckerScheduler.action.addShowToTraktLibrary( self.show) if sickbeard.TRAKT_SYNC_WATCHLIST: logger.log('update watchlist') notifiers.trakt_notifier.update_watchlist(show_obj=self.show) # Load XEM data to DB for show scene_numbering.xem_refresh(self.show.indexerid, self.show.indexer, force=True) # check if show has XEM mapping so we can determine if searches should go by scene numbering or indexer numbering. if not self.scene and scene_numbering.get_xem_numbering_for_show( self.show.indexerid, self.show.indexer): self.show.scene = 1 # After initial add, set to default_status_after. self.show.default_ep_status = self.default_status_after super(QueueItemAdd, self).finish() self.finish()
def _show_data(self, show_obj): """ Creates an elementTree XML structure for a MediaBrowser-style series.xml returns the resulting data object. show_obj: a TVShow instance to create the NFO for """ indexer_lang = show_obj.lang # There's gotta be a better way of doing this but we don't wanna # change the language value elsewhere lINDEXER_API_PARMS = sickbeard.indexerApi( show_obj.indexer).api_params.copy() lINDEXER_API_PARMS['actors'] = True if indexer_lang and not indexer_lang == 'en': lINDEXER_API_PARMS['language'] = indexer_lang if show_obj.dvdorder != 0: lINDEXER_API_PARMS['dvdorder'] = True t = sickbeard.indexerApi( show_obj.indexer).indexer(**lINDEXER_API_PARMS) tv_node = etree.Element("Series") try: myShow = t[int(show_obj.indexerid)] except sickbeard.indexer_shownotfound: logger.log( u"Unable to find show with id " + str(show_obj.indexerid) + " on " + sickbeard.indexerApi(show_obj.indexer).name + ", skipping it", logger.ERROR) raise except sickbeard.indexer_error: logger.log( u"" + sickbeard.indexerApi(show_obj.indexer).name + " is down, can't use its data to make the NFO", logger.ERROR) raise # check for title and id if getattr(myShow, 'seriesname', None) is None or getattr( myShow, 'id', None) is None: logger.log( u"Incomplete info for show with id " + str(show_obj.indexerid) + " on " + sickbeard.indexerApi(show_obj.indexer).name + ", skipping it", logger.ERROR) return False indexerid = etree.SubElement(tv_node, "id") if getattr(myShow, 'id', None) is not None: indexerid.text = str(myShow['id']) indexer = etree.SubElement(tv_node, "indexer") if show_obj.indexer != None: indexer.text = str(show_obj.indexer) SeriesName = etree.SubElement(tv_node, "SeriesName") if getattr(myShow, 'seriesname', None) is not None: SeriesName.text = myShow['seriesname'] Status = etree.SubElement(tv_node, "Status") if getattr(myShow, 'status', None) is not None: Status.text = myShow['status'] Network = etree.SubElement(tv_node, "Network") if getattr(myShow, 'network', None) is not None: Network.text = myShow['network'] Airs_Time = etree.SubElement(tv_node, "Airs_Time") if getattr(myShow, 'airs_time', None) is not None: Airs_Time.text = myShow['airs_time'] Airs_DayOfWeek = etree.SubElement(tv_node, "Airs_DayOfWeek") if getattr(myShow, 'airs_dayofweek', None) is not None: Airs_DayOfWeek.text = myShow['airs_dayofweek'] FirstAired = etree.SubElement(tv_node, "FirstAired") if getattr(myShow, 'firstaired', None) is not None: FirstAired.text = myShow['firstaired'] ContentRating = etree.SubElement(tv_node, "ContentRating") MPAARating = etree.SubElement(tv_node, "MPAARating") certification = etree.SubElement(tv_node, "certification") if getattr(myShow, 'contentrating', None) is not None: ContentRating.text = myShow['contentrating'] MPAARating.text = myShow['contentrating'] certification.text = myShow['contentrating'] MetadataType = etree.SubElement(tv_node, "Type") MetadataType.text = "Series" Overview = etree.SubElement(tv_node, "Overview") if getattr(myShow, 'overview', None) is not None: Overview.text = myShow['overview'] PremiereDate = etree.SubElement(tv_node, "PremiereDate") if getattr(myShow, 'firstaired', None) is not None: PremiereDate.text = myShow['firstaired'] Rating = etree.SubElement(tv_node, "Rating") if getattr(myShow, 'rating', None) is not None: Rating.text = myShow['rating'] ProductionYear = etree.SubElement(tv_node, "ProductionYear") if getattr(myShow, 'firstaired', None) is not None: try: year_text = str( datetime.datetime.strptime(myShow['firstaired'], '%Y-%m-%d').year) if year_text: ProductionYear.text = year_text except: pass RunningTime = etree.SubElement(tv_node, "RunningTime") Runtime = etree.SubElement(tv_node, "Runtime") if getattr(myShow, 'runtime', None) is not None: RunningTime.text = myShow['runtime'] Runtime.text = myShow['runtime'] IMDB_ID = etree.SubElement(tv_node, "IMDB_ID") IMDB = etree.SubElement(tv_node, "IMDB") IMDbId = etree.SubElement(tv_node, "IMDbId") if getattr(myShow, 'imdb_id', None) is not None: IMDB_ID.text = myShow['imdb_id'] IMDB.text = myShow['imdb_id'] IMDbId.text = myShow['imdb_id'] Zap2ItId = etree.SubElement(tv_node, "Zap2ItId") if getattr(myShow, 'zap2it_id', None) is not None: Zap2ItId.text = myShow['zap2it_id'] Genres = etree.SubElement(tv_node, "Genres") for genre in myShow['genre'].split('|'): if genre: cur_genre = etree.SubElement(Genres, "Genre") cur_genre.text = genre Genre = etree.SubElement(tv_node, "Genre") if getattr(myShow, 'genre', None) is not None: Genre.text = "|".join([x for x in myShow["genre"].split('|') if x]) Studios = etree.SubElement(tv_node, "Studios") Studio = etree.SubElement(Studios, "Studio") if getattr(myShow, 'network', None) is not None: Studio.text = myShow['network'] Persons = etree.SubElement(tv_node, "Persons") if getattr(myShow, 'actors', None) is not None: for actor in myShow['_actors']: cur_actor = etree.SubElement(Persons, "Person") cur_actor_name = etree.SubElement(cur_actor, "Name") cur_actor_name.text = actor['name'].strip() 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 = actor['role'] if cur_actor_role_text != None: cur_actor_role.text = cur_actor_role_text helpers.indentXML(tv_node) data = etree.ElementTree(tv_node) return data
logger.ERROR) return False rootNode = etree.Element("Item") # write an MediaBrowser XML containing info for all matching episodes for curEpToWrite in eps_to_write: try: myEp = myShow[curEpToWrite.season][curEpToWrite.episode] except (sickbeard.indexer_episodenotfound, sickbeard.indexer_seasonnotfound): logger.log(u"Unable to find episode " + str(curEpToWrite.season) + "x" + str(curEpToWrite.episode) + " on " + sickbeard.indexerApi(ep_obj.show.indexer).name + ".. has it been removed? Should I delete from db?") return None if curEpToWrite == ep_obj: # root (or single) episode # default to today's date for specials if firstaired is not set if getattr(myEp, 'firstaired', None) is None and ep_obj.season == 0: myEp['firstaired'] = str(datetime.date.fromordinal(1)) if getattr(myEp, 'episodename', None) is None or getattr( myEp, 'firstaired', None) is None: return None
def fix_xem_numbering(indexer_id, indexer): """ Returns a dict of (season, episode) : (sceneSeason, sceneEpisode) mappings for an entire show. Both the keys and values of the dict are tuples. Will be empty if there are no scene numbers set in xem """ if indexer_id is None: return {} indexer_id = int(indexer_id) indexer = int(indexer) # query = [{ # "name": self.show.name, # "seasons": [{ # "episodes": [{ # "episode_number": None, # "name": None # }], # "season_number": None, # }], # "/tv/tv_program/number_of_seasons": [], # "/tv/tv_program/number_of_episodes": [], # "/tv/tv_program/thetvdb_id": [], # "/tv/tv_program/tvrage_id": [], # "type": "/tv/tv_program", # }] # # # url = 'https://www.googleapis.com/freebase/v1/mqlread' # api_key = "AIzaSyCCHNp4dhVHxJYzbLiCE4y4a1rgTnX4fDE" # params = { # 'query': json.dumps(query), # 'key': api_key # } # # # def get_from_api(url, params=None): # """Build request and return results # """ # import xmltodict # # response = requests.get(url, params=params) # if response.status_code == 200: # try: # return response.json() # except ValueError: # return xmltodict.parse(response.text)['Data'] # # # Get query results # tmp = get_from_api(url, params=params)['result'] myDB = db.DBConnection() rows = myDB.select( 'SELECT season, episode, absolute_number, scene_season, scene_episode, scene_absolute_number FROM tv_episodes WHERE indexer = ? and showid = ?', [indexer, indexer_id]) last_absolute_number = None last_scene_season = None last_scene_episode = None last_scene_absolute_number = None update_absolute_number = False update_scene_season = False update_scene_episode = False update_scene_absolute_number = False logger.log( u'Fixing any XEM scene mapping issues for show %s on %s' % ( indexer_id, sickbeard.indexerApi(indexer).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 showid = ? AND season = ? AND episode = ?", [absolute_number, indexer_id, season, episode] ]) update_absolute_number = False if update_scene_season: cl.append([ "UPDATE tv_episodes SET scene_season = ? WHERE showid = ? AND season = ? AND episode = ?", [scene_season, indexer_id, season, episode] ]) update_scene_season = False if update_scene_episode: cl.append([ "UPDATE tv_episodes SET scene_episode = ? WHERE showid = ? AND season = ? AND episode = ?", [scene_episode, indexer_id, season, episode] ]) update_scene_episode = False if update_scene_absolute_number: cl.append([ "UPDATE tv_episodes SET scene_absolute_number = ? WHERE showid = ? AND season = ? AND episode = ?", [scene_absolute_number, indexer_id, season, episode] ]) update_scene_absolute_number = False if len(cl) > 0: myDB = db.DBConnection() myDB.mass_action(cl)
def xem_refresh(indexer_id, indexer, force=False): """ Refresh data from xem for a tv show @param indexer_id: int """ if indexer_id is None: return indexer_id = int(indexer_id) indexer = int(indexer) # XEM API URL url = "http://thexem.de/map/all?id=%s&origin=%s&destination=scene" % ( indexer_id, sickbeard.indexerApi(indexer).config['xem_origin']) MAX_REFRESH_AGE_SECS = 86400 # 1 day myDB = db.DBConnection() rows = myDB.select( "SELECT last_refreshed FROM xem_refresh WHERE indexer = ? and indexer_id = ?", [indexer, indexer_id]) if rows: lastRefresh = int(rows[0]['last_refreshed']) refresh = int(time.mktime(datetime.datetime.today().timetuple()) ) > lastRefresh + MAX_REFRESH_AGE_SECS else: refresh = True if refresh or force: logger.log( u'Looking up XEM scene mapping for show %s on %s' % ( indexer_id, sickbeard.indexerApi(indexer).name, ), logger.DEBUG) # mark refreshed myDB.upsert( "xem_refresh", { 'indexer': indexer, 'last_refreshed': int(time.mktime(datetime.datetime.today().timetuple())) }, {'indexer_id': indexer_id}) try: from .scene_exceptions import xem_session parsedJSON = sickbeard.helpers.getURL(url, session=xem_session, json=True) if not parsedJSON or parsedJSON == '': logger.log( u'No XEM data for show "%s on %s"' % ( indexer_id, sickbeard.indexerApi(indexer).name, ), logger.INFO) return if 'success' in parsedJSON['result']: cl = [] for entry in parsedJSON['data']: if 'scene' in entry: cl.append([ "UPDATE tv_episodes SET scene_season = ?, scene_episode = ?, scene_absolute_number = ? WHERE showid = ? AND season = ? AND episode = ?", [ entry['scene']['season'], entry['scene']['episode'], entry['scene']['absolute'], indexer_id, entry[sickbeard.indexerApi( indexer).config['xem_origin']]['season'], entry[sickbeard.indexerApi( indexer).config['xem_origin']]['episode'] ] ]) if 'scene_2' in entry: # for doubles cl.append([ "UPDATE tv_episodes SET scene_season = ?, scene_episode = ?, scene_absolute_number = ? WHERE showid = ? AND season = ? AND episode = ?", [ entry['scene_2']['season'], entry['scene_2']['episode'], entry['scene_2']['absolute'], indexer_id, entry[sickbeard.indexerApi( indexer).config['xem_origin']]['season'], entry[sickbeard.indexerApi( indexer).config['xem_origin']]['episode'] ] ]) if len(cl) > 0: myDB = db.DBConnection() myDB.mass_action(cl) else: logger.log( u"Empty lookup result - no XEM data for show %s on %s" % ( indexer_id, sickbeard.indexerApi(indexer).name, ), logger.DEBUG) except Exception, e: logger.log( u"Exception while refreshing XEM data for show " + str(indexer_id) + " on " + sickbeard.indexerApi(indexer).name + ": " + ex(e), logger.WARNING) logger.log(traceback.format_exc(), logger.DEBUG)
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 indexer in sickbeard.indexerApi().indexers: if shouldRefresh(sickbeard.indexerApi(indexer).name): logger.log(u'Checking for scene exception updates for %s' % sickbeard.indexerApi(indexer).name) url = sickbeard.indexerApi(indexer).config['scene_url'] url_data = helpers.getURL(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: setLastRefresh(sickbeard.indexerApi(indexer).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.decode('utf-8') indexer_id, sep, aliases = cur_line.partition( ':') # @UnusedVariable if not aliases: continue indexer_id = int(indexer_id) # regex out the list of shows, taking \' into account # alias_list = [re.sub(r'\\(.)', r'\1', x) for x in re.findall(r"'(.*?)(?<!\\)',?", aliases)] alias_list = [{ re.sub(r'\\(.)', r'\1', x): -1 } for x in re.findall(r"'(.*?)(?<!\\)',?", aliases)] exception_dict[indexer_id] = alias_list del alias_list 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_indexer_id 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 * FROM scene_exceptions WHERE indexer_id = ?', [cur_indexer_id]) ] if cur_indexer_id not in exception_dict: continue for cur_exception_dict in exception_dict[cur_indexer_id]: try: cur_exception, cur_season = cur_exception_dict.items()[0] except 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 not isinstance(cur_exception, unicode): cur_exception = unicode(cur_exception, 'utf-8', 'replace') cl.append([ 'INSERT INTO scene_exceptions (indexer_id, show_name, season) VALUES (?,?,?)', [cur_indexer_id, cur_exception, cur_season] ]) changed_exceptions = True 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 __str__(self): return str(self.date) + " " + self.name + " " + str( self.season) + "x" + str(self.episode) + " of " + str( self.indexerid) + " from " + str( sickbeard.indexerApi(self.indexer).name)
def fix_xem_numbering(indexer_id, indexer): # pylint:disable=too-many-locals, too-many-branches, too-many-statements """ Returns a dict of (season, episode) : (sceneSeason, sceneEpisode) mappings for an entire show. Both the keys and values of the dict are tuples. Will be empty if there are no scene numbers set in xem """ if indexer_id is None: return {} indexer_id = int(indexer_id) indexer = int(indexer) main_db_con = db.DBConnection() rows = main_db_con.select( 'SELECT season, episode, absolute_number, scene_season, scene_episode, scene_absolute_number FROM tv_episodes WHERE indexer = ? and showid = ?', [indexer, indexer_id]) last_absolute_number = None last_scene_season = None last_scene_episode = None last_scene_absolute_number = None update_absolute_number = False update_scene_season = False update_scene_episode = False update_scene_absolute_number = False logger.log( u'Fixing any XEM scene mapping issues for show %s on %s' % ( indexer_id, sickbeard.indexerApi(indexer).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 showid = ? AND season = ? AND episode = ?", [absolute_number, indexer_id, season, episode] ]) update_absolute_number = False if update_scene_season: cl.append([ "UPDATE tv_episodes SET scene_season = ? WHERE showid = ? AND season = ? AND episode = ?", [scene_season, indexer_id, season, episode] ]) update_scene_season = False if update_scene_episode: cl.append([ "UPDATE tv_episodes SET scene_episode = ? WHERE showid = ? AND season = ? AND episode = ?", [scene_episode, indexer_id, season, episode] ]) update_scene_episode = False if update_scene_absolute_number: cl.append([ "UPDATE tv_episodes SET scene_absolute_number = ? WHERE showid = ? AND season = ? AND episode = ?", [scene_absolute_number, indexer_id, season, episode] ]) update_scene_absolute_number = False if len(cl) > 0: main_db_con = db.DBConnection() main_db_con.mass_action(cl)
def run(self): # pylint: disable=too-many-branches, too-many-statements super(QueueItemUpdate, self).run() logger.log('Beginning update of {0}'.format(self.show.name), logger.DEBUG) logger.log( 'Retrieving show info from {0}'.format( sickbeard.indexerApi(self.show.indexer).name), logger.DEBUG) try: self.show.loadFromIndexer(cache=not self.force) except sickbeard.indexer_error as error: logger.log( 'Unable to contact {0}, aborting: {1}'.format( sickbeard.indexerApi(self.show.indexer).name, error), logger.WARNING) super(QueueItemUpdate, self).finish() self.finish() return except sickbeard.indexer_attributenotfound as error: logger.log( 'Data retrieved from {0} was incomplete, aborting: {1}'.format( sickbeard.indexerApi(self.show.indexer).name, error), logger.ERROR) super(QueueItemUpdate, self).finish() self.finish() return logger.log('Retrieving show info from IMDb', logger.DEBUG) try: self.show.loadIMDbInfo() except imdb_exceptions.IMDbError as error: logger.log('Something wrong on IMDb api: {0}'.format(error), logger.WARNING) except Exception as error: logger.log('Error loading IMDb info: {0}'.format(error), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) # have to save show before reading episodes from db try: self.show.saveToDB() except Exception as error: logger.log( 'Error saving show info to the database: {0}'.format(error), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) # get episode list from DB logger.log('Loading all episodes from the database', logger.DEBUG) DBEpList = self.show.loadEpisodesFromDB() # get episode list from TVDB logger.log( 'Loading all episodes from {0}'.format( sickbeard.indexerApi(self.show.indexer).name), logger.DEBUG) try: IndexerEpList = self.show.loadEpisodesFromIndexer( cache=not self.force) except sickbeard.indexer_exception as error: logger.log( 'Unable to get info from {0}, the show info will not be refreshed: {1}' .format(sickbeard.indexerApi(self.show.indexer).name, error), logger.ERROR) IndexerEpList = None if not IndexerEpList: logger.log( 'No data returned from {0}, unable to update this show.'. format(sickbeard.indexerApi(self.show.indexer).name), logger.ERROR) else: # for each ep we found on the Indexer delete it from the DB list for curSeason in IndexerEpList: for curEpisode in IndexerEpList[curSeason]: curEp = self.show.getEpisode(curSeason, curEpisode) curEp.saveToDB() if curSeason in DBEpList and curEpisode in DBEpList[ curSeason]: del DBEpList[curSeason][curEpisode] # remaining episodes in the DB list are not on the indexer, just delete them from the DB for curSeason in DBEpList: for curEpisode in DBEpList[curSeason]: logger.log( 'Permanently deleting episode {0:02d}E{1:02d} from the database' .format(curSeason, curEpisode), logger.INFO) curEp = self.show.getEpisode(curSeason, curEpisode) try: curEp.deleteEpisode() except EpisodeDeletedException: pass # save show again, in case episodes have changed try: self.show.saveToDB() except Exception as error: logger.log( 'Error saving show info to the database: {0}'.format(error), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) logger.log('Finished update of {0}'.format(self.show.name), logger.DEBUG) sickbeard.showQueueScheduler.action.refresh_show(self.show, self.force) super(QueueItemUpdate, self).finish() self.finish()
class MediaBrowserMetadata(generic.GenericMetadata): """ Metadata generation class for Media Browser 2.x/3.x - Standard Mode. The following file structure is used: show_root/series.xml (show metadata) show_root/folder.jpg (poster) show_root/backdrop.jpg (fanart) show_root/Season ##/folder.jpg (season thumb) show_root/Season ##/filename.ext (*) show_root/Season ##/metadata/filename.xml (episode metadata) show_root/Season ##/metadata/filename.jpg (episode thumb) """ def __init__(self, show_metadata=False, episode_metadata=False, fanart=False, poster=False, banner=False, episode_thumbnails=False, season_posters=False, season_banners=False, season_all_poster=False, season_all_banner=False): generic.GenericMetadata.__init__(self, show_metadata, episode_metadata, fanart, poster, banner, episode_thumbnails, season_posters, season_banners, season_all_poster, season_all_banner) self.name = 'MediaBrowser' self._ep_nfo_extension = 'xml' self._show_metadata_filename = 'series.xml' self.fanart_name = "backdrop.jpg" self.poster_name = "folder.jpg" # web-ui metadata template self.eg_show_metadata = "series.xml" self.eg_episode_metadata = "Season##\\metadata\\<i>filename</i>.xml" self.eg_fanart = "backdrop.jpg" self.eg_poster = "folder.jpg" self.eg_banner = "banner.jpg" self.eg_episode_thumbnails = "Season##\\metadata\\<i>filename</i>.jpg" self.eg_season_posters = "Season##\\folder.jpg" self.eg_season_banners = "Season##\\banner.jpg" self.eg_season_all_poster = "<i>not supported</i>" self.eg_season_all_banner = "<i>not supported</i>" # Override with empty methods for unsupported features def retrieveShowMetadata(self, folder): # while show metadata is generated, it is not supported for our lookup return (None, None, None) def create_season_all_poster(self, show_obj): pass def create_season_all_banner(self, show_obj): pass def get_episode_file_path(self, ep_obj): """ Returns a full show dir/metadata/episode.xml path for MediaBrowser episode metadata files ep_obj: a TVEpisode object to get the path for """ if ek.ek(os.path.isfile, ep_obj.location): xml_file_name = helpers.replaceExtension( ek.ek(os.path.basename, ep_obj.location), self._ep_nfo_extension) metadata_dir_name = ek.ek(os.path.join, ek.ek(os.path.dirname, ep_obj.location), 'metadata') xml_file_path = ek.ek(os.path.join, metadata_dir_name, xml_file_name) else: logger.log( u"Episode location doesn't exist: " + str(ep_obj.location), logger.DEBUG) return '' return xml_file_path def get_episode_thumb_path(self, ep_obj): """ Returns a full show dir/metadata/episode.jpg path for MediaBrowser episode thumbs. ep_obj: a TVEpisode object to get the path from """ if ek.ek(os.path.isfile, ep_obj.location): tbn_file_name = helpers.replaceExtension( ek.ek(os.path.basename, ep_obj.location), 'jpg') metadata_dir_name = ek.ek(os.path.join, ek.ek(os.path.dirname, ep_obj.location), 'metadata') tbn_file_path = ek.ek(os.path.join, metadata_dir_name, tbn_file_name) else: return None return tbn_file_path def get_season_poster_path(self, show_obj, season): """ Season thumbs for MediaBrowser go in Show Dir/Season X/folder.jpg If no season folder exists, None is returned """ dir_list = [ x for x in ek.ek(os.listdir, show_obj.location) if ek.ek(os.path.isdir, ek.ek(os.path.join, show_obj.location, x)) ] season_dir_regex = '^Season\s+(\d+)$' season_dir = None for cur_dir in dir_list: # MediaBrowser 1.x only supports 'Specials' # MediaBrowser 2.x looks to only support 'Season 0' # MediaBrowser 3.x looks to mimic XBMC/Plex support if season == 0 and cur_dir == "Specials": season_dir = cur_dir break match = re.match(season_dir_regex, cur_dir, re.I) if not match: continue cur_season = int(match.group(1)) if cur_season == season: season_dir = cur_dir break if not season_dir: logger.log( u"Unable to find a season dir for season " + str(season), logger.DEBUG) return None logger.log( u"Using " + str(season_dir) + "/folder.jpg as season dir for season " + str(season), logger.DEBUG) return ek.ek(os.path.join, show_obj.location, season_dir, 'folder.jpg') def get_season_banner_path(self, show_obj, season): """ Season thumbs for MediaBrowser go in Show Dir/Season X/banner.jpg If no season folder exists, None is returned """ dir_list = [ x for x in ek.ek(os.listdir, show_obj.location) if ek.ek(os.path.isdir, ek.ek(os.path.join, show_obj.location, x)) ] season_dir_regex = '^Season\s+(\d+)$' season_dir = None for cur_dir in dir_list: # MediaBrowser 1.x only supports 'Specials' # MediaBrowser 2.x looks to only support 'Season 0' # MediaBrowser 3.x looks to mimic XBMC/Plex support if season == 0 and cur_dir == "Specials": season_dir = cur_dir break match = re.match(season_dir_regex, cur_dir, re.I) if not match: continue cur_season = int(match.group(1)) if cur_season == season: season_dir = cur_dir break if not season_dir: logger.log( u"Unable to find a season dir for season " + str(season), logger.DEBUG) return None logger.log( u"Using " + str(season_dir) + "/banner.jpg as season dir for season " + str(season), logger.DEBUG) return ek.ek(os.path.join, show_obj.location, season_dir, 'banner.jpg') def _show_data(self, show_obj): """ Creates an elementTree XML structure for a MediaBrowser-style series.xml returns the resulting data object. show_obj: a TVShow instance to create the NFO for """ indexer_lang = show_obj.lang # There's gotta be a better way of doing this but we don't wanna # change the language value elsewhere lINDEXER_API_PARMS = sickbeard.indexerApi( show_obj.indexer).api_params.copy() lINDEXER_API_PARMS['actors'] = True if indexer_lang and not indexer_lang == 'en': lINDEXER_API_PARMS['language'] = indexer_lang if show_obj.dvdorder != 0: lINDEXER_API_PARMS['dvdorder'] = True t = sickbeard.indexerApi( show_obj.indexer).indexer(**lINDEXER_API_PARMS) tv_node = etree.Element("Series") try: myShow = t[int(show_obj.indexerid)] except sickbeard.indexer_shownotfound: logger.log( u"Unable to find show with id " + str(show_obj.indexerid) + " on " + sickbeard.indexerApi(show_obj.indexer).name + ", skipping it", logger.ERROR) raise except sickbeard.indexer_error: logger.log( u"" + sickbeard.indexerApi(show_obj.indexer).name + " is down, can't use its data to make the NFO", logger.ERROR) raise # check for title and id if getattr(myShow, 'seriesname', None) is None or getattr( myShow, 'id', None) is None: logger.log( u"Incomplete info for show with id " + str(show_obj.indexerid) + " on " + sickbeard.indexerApi(show_obj.indexer).name + ", skipping it", logger.ERROR) return False indexerid = etree.SubElement(tv_node, "id") if getattr(myShow, 'id', None) is not None: indexerid.text = str(myShow['id']) indexer = etree.SubElement(tv_node, "indexer") if show_obj.indexer != None: indexer.text = str(show_obj.indexer) SeriesName = etree.SubElement(tv_node, "SeriesName") if getattr(myShow, 'seriesname', None) is not None: SeriesName.text = myShow['seriesname'] Status = etree.SubElement(tv_node, "Status") if getattr(myShow, 'status', None) is not None: Status.text = myShow['status'] Network = etree.SubElement(tv_node, "Network") if getattr(myShow, 'network', None) is not None: Network.text = myShow['network'] Airs_Time = etree.SubElement(tv_node, "Airs_Time") if getattr(myShow, 'airs_time', None) is not None: Airs_Time.text = myShow['airs_time'] Airs_DayOfWeek = etree.SubElement(tv_node, "Airs_DayOfWeek") if getattr(myShow, 'airs_dayofweek', None) is not None: Airs_DayOfWeek.text = myShow['airs_dayofweek'] FirstAired = etree.SubElement(tv_node, "FirstAired") if getattr(myShow, 'firstaired', None) is not None: FirstAired.text = myShow['firstaired'] ContentRating = etree.SubElement(tv_node, "ContentRating") MPAARating = etree.SubElement(tv_node, "MPAARating") certification = etree.SubElement(tv_node, "certification") if getattr(myShow, 'contentrating', None) is not None: ContentRating.text = myShow['contentrating'] MPAARating.text = myShow['contentrating'] certification.text = myShow['contentrating'] MetadataType = etree.SubElement(tv_node, "Type") MetadataType.text = "Series" Overview = etree.SubElement(tv_node, "Overview") if getattr(myShow, 'overview', None) is not None: Overview.text = myShow['overview'] PremiereDate = etree.SubElement(tv_node, "PremiereDate") if getattr(myShow, 'firstaired', None) is not None: PremiereDate.text = myShow['firstaired'] Rating = etree.SubElement(tv_node, "Rating") if getattr(myShow, 'rating', None) is not None: Rating.text = myShow['rating'] ProductionYear = etree.SubElement(tv_node, "ProductionYear") if getattr(myShow, 'firstaired', None) is not None: try: year_text = str( datetime.datetime.strptime(myShow['firstaired'], '%Y-%m-%d').year) if year_text: ProductionYear.text = year_text except: pass RunningTime = etree.SubElement(tv_node, "RunningTime") Runtime = etree.SubElement(tv_node, "Runtime") if getattr(myShow, 'runtime', None) is not None: RunningTime.text = myShow['runtime'] Runtime.text = myShow['runtime'] IMDB_ID = etree.SubElement(tv_node, "IMDB_ID") IMDB = etree.SubElement(tv_node, "IMDB") IMDbId = etree.SubElement(tv_node, "IMDbId") if getattr(myShow, 'imdb_id', None) is not None: IMDB_ID.text = myShow['imdb_id'] IMDB.text = myShow['imdb_id'] IMDbId.text = myShow['imdb_id'] Zap2ItId = etree.SubElement(tv_node, "Zap2ItId") if getattr(myShow, 'zap2it_id', None) is not None: Zap2ItId.text = myShow['zap2it_id'] Genres = etree.SubElement(tv_node, "Genres") for genre in myShow['genre'].split('|'): if genre: cur_genre = etree.SubElement(Genres, "Genre") cur_genre.text = genre Genre = etree.SubElement(tv_node, "Genre") if getattr(myShow, 'genre', None) is not None: Genre.text = "|".join([x for x in myShow["genre"].split('|') if x]) Studios = etree.SubElement(tv_node, "Studios") Studio = etree.SubElement(Studios, "Studio") if getattr(myShow, 'network', None) is not None: Studio.text = myShow['network'] Persons = etree.SubElement(tv_node, "Persons") if getattr(myShow, 'actors', None) is not None: for actor in myShow['_actors']: cur_actor = etree.SubElement(Persons, "Person") cur_actor_name = etree.SubElement(cur_actor, "Name") cur_actor_name.text = actor['name'].strip() 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 = actor['role'] if cur_actor_role_text != None: cur_actor_role.text = cur_actor_role_text helpers.indentXML(tv_node) data = etree.ElementTree(tv_node) return data def _ep_data(self, ep_obj): """ Creates an elementTree XML structure for a MediaBrowser style episode.xml and returns the resulting data object. show_obj: a TVShow instance to create the NFO for """ eps_to_write = [ep_obj] + ep_obj.relatedEps persons_dict = {} persons_dict['Director'] = [] persons_dict['GuestStar'] = [] persons_dict['Writer'] = [] indexer_lang = ep_obj.show.lang try: lINDEXER_API_PARMS = sickbeard.indexerApi( ep_obj.show.indexer).api_params.copy() lINDEXER_API_PARMS['actors'] = True if indexer_lang and not indexer_lang == 'en': lINDEXER_API_PARMS['language'] = indexer_lang if ep_obj.show.dvdorder != 0: lINDEXER_API_PARMS['dvdorder'] = True t = sickbeard.indexerApi( ep_obj.show.indexer).indexer(**lINDEXER_API_PARMS) myShow = t[ep_obj.show.indexerid] except sickbeard.indexer_shownotfound, e: raise exceptions.ShowNotFoundException(e.message) except sickbeard.indexer_error, e: logger.log( u"Unable to connect to " + sickbeard.indexerApi(ep_obj.show.indexer).name + " while creating meta files - skipping - " + ex(e), logger.ERROR) return False
def retrieve_exceptions(): # pylint:disable=too-many-locals, too-many-branches """ 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. """ do_refresh = False for indexer in sickbeard.indexerApi().indexers: if shouldRefresh(sickbeard.indexerApi(indexer).name): do_refresh = True if do_refresh: loc = sickbeard.indexerApi(INDEXER_TVDB).config['scene_loc'] logger.log("Checking for scene exception updates from {0}".format(loc)) session = sickbeard.indexerApi(INDEXER_TVDB).session proxy = sickbeard.PROXY_SETTING if proxy and sickbeard.PROXY_INDEXERS: session.proxies = { "http": proxy, "https": proxy, } try: jdata = helpers.getURL(loc, session=session, returns='json') except Exception: jdata = None if not jdata: # When jdata is None, trouble connecting to github, or reading file failed logger.log( "Check scene exceptions update failed. Unable to update from {0}" .format(loc), logger.DEBUG) else: for indexer in sickbeard.indexerApi().indexers: try: setLastRefresh(sickbeard.indexerApi(indexer).name) for indexer_id in jdata[sickbeard.indexerApi( indexer).config['xem_origin']]: alias_list = [ { scene_exception: int(scene_season) } for scene_season in jdata[sickbeard.indexerApi( indexer).config['xem_origin']][indexer_id] for scene_exception in jdata[sickbeard.indexerApi( indexer).config['xem_origin']][indexer_id] [scene_season] ] exception_dict[indexer_id] = alias_list except Exception: continue # 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] 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] += anidb_exception_dict[anidb_ex] else: exception_dict[anidb_ex] = anidb_exception_dict[anidb_ex] queries = [] cache_db_con = db.DBConnection('cache.db') for cur_indexer_id in exception_dict: sql_ex = cache_db_con.select( "SELECT show_name FROM scene_exceptions WHERE indexer_id = ?;", [cur_indexer_id]) existing_exceptions = [x[b"show_name"] for x in sql_ex] if cur_indexer_id not in exception_dict: continue for cur_exception_dict in exception_dict[cur_indexer_id]: for ex in six.iteritems(cur_exception_dict): cur_exception, curSeason = ex if cur_exception not in existing_exceptions: queries.append([ "INSERT OR IGNORE INTO scene_exceptions (indexer_id, show_name, season) VALUES (?,?,?);", [cur_indexer_id, cur_exception, curSeason] ]) if queries: cache_db_con.mass_action(queries) logger.log("Updated scene exceptions", logger.DEBUG) # cleanup exception_dict.clear() anidb_exception_dict.clear() xem_exception_dict.clear()
def run(self): ShowQueueItem.run(self) if not sickbeard.indexerApi(self.show.indexer).config['active']: logger.log( 'Indexer %s is marked inactive, aborting update for show %s and continue with refresh.' % (sickbeard.indexerApi( self.show.indexer).config['name'], self.show.name)) sickbeard.showQueueScheduler.action.refreshShow( self.show, self.force, self.scheduled_update, after_update=True) return logger.log('Beginning update of %s' % self.show.name) logger.log( 'Retrieving show info from %s' % sickbeard.indexerApi(self.show.indexer).name, logger.DEBUG) try: result = self.show.loadFromIndexer(cache=not self.force) if None is not result: return except sickbeard.indexer_error as e: logger.log( 'Unable to contact %s, aborting: %s' % (sickbeard.indexerApi(self.show.indexer).name, ex(e)), logger.WARNING) return except sickbeard.indexer_attributenotfound as e: logger.log( 'Data retrieved from %s was incomplete, aborting: %s' % (sickbeard.indexerApi(self.show.indexer).name, ex(e)), logger.ERROR) return if self.force_web: self.show.load_imdb_info() try: self.show.saveToDB() except 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) DBEpList = self.show.loadEpisodesFromDB(update=True) # get episode list from TVDB logger.log( 'Loading all episodes from %s' % sickbeard.indexerApi(self.show.indexer).name, logger.DEBUG) try: IndexerEpList = self.show.loadEpisodesFromIndexer( cache=not self.force, update=True) except sickbeard.indexer_exception as e: logger.log( 'Unable to get info from %s, the show info will not be refreshed: %s' % (sickbeard.indexerApi(self.show.indexer).name, ex(e)), logger.ERROR) IndexerEpList = None if None is IndexerEpList: logger.log( 'No data returned from %s, unable to update episodes for show: %s' % (sickbeard.indexerApi(self.show.indexer).name, self.show.name), logger.ERROR) elif not IndexerEpList or 0 == len(IndexerEpList): logger.log( 'No episodes returned from %s for show: %s' % (sickbeard.indexerApi(self.show.indexer).name, self.show.name), logger.WARNING) else: # for each ep we found on TVDB delete it from the DB list for curSeason in IndexerEpList: for curEpisode in IndexerEpList[curSeason]: logger.log( 'Removing %sx%s from the DB list' % (curSeason, curEpisode), logger.DEBUG) if curSeason in DBEpList and curEpisode in DBEpList[ curSeason]: del DBEpList[curSeason][curEpisode] # for the remaining episodes in the DB list just delete them from the DB for curSeason in DBEpList: for curEpisode in DBEpList[curSeason]: curEp = self.show.getEpisode(curSeason, curEpisode) status = sickbeard.common.Quality.splitCompositeStatus( curEp.status)[0] if should_delete_episode(status): logger.log( 'Permanently deleting episode %sx%s from the database' % (curSeason, curEpisode), logger.MESSAGE) try: curEp.deleteEpisode() except exceptions.EpisodeDeletedException: pass else: logger.log( 'Not deleting episode %sx%s from the database because status is: %s' % (curSeason, curEpisode, statusStrings[status]), logger.MESSAGE) if self.priority != generic_queue.QueuePriorities.NORMAL: self.kwargs['priority'] = self.priority sickbeard.showQueueScheduler.action.refreshShow( self.show, self.force, self.scheduled_update, after_update=True, force_image_cache=self.force_web, **self.kwargs)
class QueueItemAdd(ShowQueueItem): def __init__(self, indexer, indexer_id, showDir, default_status, quality, flatten_folders, lang, subtitles, anime, scene): self.indexer = indexer self.indexer_id = indexer_id self.showDir = showDir self.default_status = default_status self.quality = quality self.flatten_folders = flatten_folders self.lang = lang self.subtitles = subtitles self.anime = anime self.scene = scene self.show = None # this will initialize self.show to None ShowQueueItem.__init__(self, ShowQueueActions.ADD, self.show) def _getName(self): """ Returns the show name if there is a show object created, if not returns the dir that the show is being added to. """ if self.show == None: return self.showDir return self.show.name show_name = property(_getName) def _isLoading(self): """ Returns True if we've gotten far enough to have a show object, or False if we still only know the folder name. """ if self.show == None: return True return False isLoading = property(_isLoading) def execute(self): ShowQueueItem.execute(self) logger.log(u"Starting to add show " + self.showDir) # make sure the Indexer IDs are valid try: lINDEXER_API_PARMS = sickbeard.indexerApi(self.indexer).api_params.copy() if self.lang: lINDEXER_API_PARMS['language'] = self.lang logger.log(u"" + str(sickbeard.indexerApi(self.indexer).name) + ": " + repr(lINDEXER_API_PARMS)) t = sickbeard.indexerApi(self.indexer).indexer(**lINDEXER_API_PARMS) s = t[self.indexer_id] # this usually only happens if they have an NFO in their show dir which gave us a Indexer ID that has no proper english version of the show if getattr(s, 'seriesname', None) is None: logger.log(u"Show in " + self.showDir + " has no name on " + str(sickbeard.indexerApi(self.indexer).name) + ", probably the wrong language used to search with.", logger.ERROR) ui.notifications.error("Unable to add show", "Show in " + self.showDir + " has no name on " + str(sickbeard.indexerApi( self.indexer).name) + ", probably the wrong language. Delete .nfo and add manually in the correct language.") self._finishEarly() return # if the show has no episodes/seasons if not s: logger.log(u"Show " + str(s['seriesname']) + " is on " + str(sickbeard.indexerApi(self.indexer).name) + " but contains no season/episode data.", logger.ERROR) ui.notifications.error("Unable to add show", "Show " + str(s['seriesname']) + " is on " + str(sickbeard.indexerApi( self.indexer).name) + " but contains no season/episode data.") self._finishEarly() return except Exception, e: logger.log(u"Unable to find show ID:" + str(self.indexer_id) + " on Indexer: " + str(sickbeard.indexerApi(self.indexer).name), logger.ERROR) ui.notifications.error("Unable to add show", "Unable to look up the show in " + self.showDir + " on " + str(sickbeard.indexerApi( self.indexer).name) + " using ID " + str( self.indexer_id) + ", not using the NFO. Delete .nfo and try adding manually again.") self._finishEarly() return try: # clear the name cache name_cache.clearCache() newShow = TVShow(self.indexer, self.indexer_id, self.lang) newShow.loadFromIndexer() self.show = newShow # set up initial values self.show.location = self.showDir self.show.subtitles = self.subtitles if self.subtitles != None else sickbeard.SUBTITLES_DEFAULT self.show.quality = self.quality if self.quality else sickbeard.QUALITY_DEFAULT self.show.flatten_folders = self.flatten_folders if self.flatten_folders != None else sickbeard.FLATTEN_FOLDERS_DEFAULT self.show.anime = self.anime if self.anime != None else sickbeard.ANIME_DEFAULT self.show.scene = self.scene if self.scene != None else sickbeard.SCENE_DEFAULT self.show.paused = False # be smartish about this if self.show.genre and "talk show" in self.show.genre.lower(): self.show.air_by_date = 1 if self.show.genre and "documentary" in self.show.genre.lower(): self.show.air_by_date = 0 if self.show.classification and "sports" in self.show.classification.lower(): self.show.sports = 1 except sickbeard.indexer_exception, e: logger.log( u"Unable to add show due to an error with " + sickbeard.indexerApi(self.indexer).name + ": " + ex(e), logger.ERROR) if self.show: ui.notifications.error( "Unable to add " + str(self.show.name) + " due to an error with " + sickbeard.indexerApi( self.indexer).name + "") else: ui.notifications.error( "Unable to add show due to an error with " + sickbeard.indexerApi(self.indexer).name + "") self._finishEarly() return
def run(self): ShowQueueItem.run(self) logger.log(u"Starting to add show {0}".format("by ShowDir: {0}".format(self.showDir) if self.showDir else "by Indexer Id: {0}".format(self.indexer_id))) # make sure the Indexer IDs are valid try: lINDEXER_API_PARMS = sickbeard.indexerApi(self.indexer).api_params.copy() if self.lang: lINDEXER_API_PARMS['language'] = self.lang logger.log(u"" + str(sickbeard.indexerApi(self.indexer).name) + ": " + repr(lINDEXER_API_PARMS)) t = sickbeard.indexerApi(self.indexer).indexer(**lINDEXER_API_PARMS) s = t[self.indexer_id] # Let's try to create the show Dir if it's not provided. This way we force the show dir to build build using the # Indexers provided series name if not self.showDir and self.root_dir: show_name = get_showname_from_indexer(self.indexer, self.indexer_id, self.lang) if show_name: self.showDir = ek(os.path.join, self.root_dir, sanitize_filename(show_name)) dir_exists = makeDir(self.showDir) if not dir_exists: logger.log(u"Unable to create the folder {0}, can't add the show".format(self.showDir)) return chmodAsParent(self.showDir) else: logger.log(u"Unable to get a show {0}, can't add the show".format(self.showDir)) return # this usually only happens if they have an NFO in their show dir which gave us a Indexer ID that has no proper english version of the show if getattr(s, 'seriesname', None) is None: logger.log(u"Show in {} has no name on {}, probably searched with the wrong language.".format (self.showDir, sickbeard.indexerApi(self.indexer).name), logger.ERROR) ui.notifications.error("Unable to add show", "Show in " + self.showDir + " has no name on " + str(sickbeard.indexerApi( self.indexer).name) + ", probably the wrong language. Delete .nfo and add manually in the correct language.") self._finishEarly() return # if the show has no episodes/seasons if not s: logger.log(u"Show " + str(s['seriesname']) + " is on " + str( sickbeard.indexerApi(self.indexer).name) + " but contains no season/episode data.") ui.notifications.error("Unable to add show", "Show " + str(s['seriesname']) + " is on " + str(sickbeard.indexerApi( self.indexer).name) + " but contains no season/episode data.") self._finishEarly() return except Exception as e: logger.log(u"%s Error while loading information from indexer %s. Error: %r" % (self.indexer_id, sickbeard.indexerApi(self.indexer).name, ex(e)), logger.ERROR) # logger.log(u"Show name with ID %s doesn't exist on %s anymore. If you are using trakt, it will be removed from your TRAKT watchlist. If you are adding manually, try removing the nfo and adding again" % # (self.indexer_id, sickbeard.indexerApi(self.indexer).name), logger.WARNING) ui.notifications.error( "Unable to add show", "Unable to look up the show in %s on %s using ID %s, not using the NFO. Delete .nfo and try adding manually again." % (self.showDir, sickbeard.indexerApi(self.indexer).name, self.indexer_id) ) if sickbeard.USE_TRAKT: trakt_id = sickbeard.indexerApi(self.indexer).config['trakt_id'] trakt_api = TraktAPI(sickbeard.SSL_VERIFY, sickbeard.TRAKT_TIMEOUT) title = self.showDir.split("/")[-1] data = { 'shows': [ { 'title': title, 'ids': {} } ] } if trakt_id == 'tvdb_id': data['shows'][0]['ids']['tvdb'] = self.indexer_id else: data['shows'][0]['ids']['tvrage'] = self.indexer_id trakt_api.traktRequest("sync/watchlist/remove", data, method='POST') self._finishEarly() return try: newShow = TVShow(self.indexer, self.indexer_id, self.lang) newShow.loadFromIndexer() self.show = newShow # set up initial values self.show.location = self.showDir self.show.subtitles = self.subtitles if self.subtitles is not None else sickbeard.SUBTITLES_DEFAULT self.show.quality = self.quality if self.quality else sickbeard.QUALITY_DEFAULT self.show.flatten_folders = self.flatten_folders if self.flatten_folders is not None else sickbeard.FLATTEN_FOLDERS_DEFAULT self.show.anime = self.anime if self.anime is not None else sickbeard.ANIME_DEFAULT self.show.scene = self.scene if self.scene is not None else sickbeard.SCENE_DEFAULT self.show.paused = self.paused if self.paused is not None else False self.show.frenchsearch = 0 # set up default new/missing episode status logger.log(u"Setting all episodes to the specified default status: " + str(self.show.default_ep_status)) self.show.default_ep_status = self.default_status if self.show.anime: self.show.release_groups = BlackAndWhiteList(self.show.indexerid) if self.blacklist: self.show.release_groups.set_black_keywords(self.blacklist) if self.whitelist: self.show.release_groups.set_white_keywords(self.whitelist) # # be smartish about this # if self.show.genre and "talk show" in self.show.genre.lower(): # self.show.air_by_date = 1 # if self.show.genre and "documentary" in self.show.genre.lower(): # self.show.air_by_date = 0 # if self.show.classification and "sports" in self.show.classification.lower(): # self.show.sports = 1 except sickbeard.indexer_exception as e: logger.log( u"Unable to add show due to an error with " + sickbeard.indexerApi(self.indexer).name + ": " + ex(e), logger.ERROR) if self.show: ui.notifications.error( "Unable to add " + str(self.show.name) + " due to an error with " + sickbeard.indexerApi( self.indexer).name + "") else: ui.notifications.error( "Unable to add show due to an error with " + sickbeard.indexerApi(self.indexer).name + "") self._finishEarly() return except MultipleShowObjectsException: logger.log(u"The show in " + self.showDir + " is already in your show list, skipping", logger.WARNING) ui.notifications.error('Show skipped', "The show in " + self.showDir + " is already in your show list") self._finishEarly() return except Exception as e: logger.log(u"Error trying to add show: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) self._finishEarly() raise logger.log(u"Retrieving show info from IMDb", logger.DEBUG) try: self.show.loadIMDbInfo() except imdb_exceptions.IMDbError as e: logger.log(u" Something wrong on IMDb api: " + ex(e), logger.WARNING) except Exception as e: logger.log(u"Error loading IMDb info: " + ex(e), logger.ERROR) try: self.show.saveToDB() except Exception as e: logger.log(u"Error saving the show to the database: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) self._finishEarly() raise # add it to the show list sickbeard.showList.append(self.show) try: self.show.loadEpisodesFromIndexer() except Exception as e: logger.log( u"Error with " + sickbeard.indexerApi(self.show.indexer).name + ", not creating episode list: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) # update internal name cache name_cache.buildNameCache(self.show) try: self.show.loadEpisodesFromDir() except Exception as e: logger.log(u"Error searching dir for episodes: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) # if they set default ep status to WANTED then run the backlog to search for episodes # FIXME: This needs to be a backlog queue item!!! if self.show.default_ep_status == WANTED: logger.log(u"Launching backlog for this show since its episodes are WANTED") sickbeard.backlogSearchScheduler.action.searchBacklog([self.show]) self.show.writeMetadata() self.show.updateMetadata() self.show.populateCache() self.show.flushEpisodes() if sickbeard.USE_TRAKT: # if there are specific episodes that need to be added by trakt sickbeard.traktCheckerScheduler.action.manageNewShow(self.show) # add show to trakt.tv library if sickbeard.TRAKT_SYNC: sickbeard.traktCheckerScheduler.action.addShowToTraktLibrary(self.show) if sickbeard.TRAKT_SYNC_WATCHLIST: logger.log(u"update watchlist") notifiers.trakt_notifier.update_watchlist(show_obj=self.show) # Load XEM data to DB for show sickbeard.scene_numbering.xem_refresh(self.show.indexerid, self.show.indexer, force=True) # check if show has XEM mapping so we can determin if searches should go by scene numbering or indexer numbering. if not self.scene and sickbeard.scene_numbering.get_xem_numbering_for_show(self.show.indexerid, self.show.indexer): self.show.scene = 1 # After initial add, set to default_status_after. self.show.default_ep_status = self.default_status_after self.finish()
def mapIndexersToShow(showObj): mapped = {} # init mapped indexers object for indexer in sickbeard.indexerApi().indexers: mapped[indexer] = showObj.indexerid if int(indexer) == int( showObj.indexer) else 0 myDB = db.DBConnection() sqlResults = myDB.select( "SELECT * FROM indexer_mapping WHERE indexer_id = ? AND indexer = ?", [showObj.indexerid, showObj.indexer]) # for each mapped entry for curResult in sqlResults: nlist = [i for i in curResult if i is not None] # Check if its mapped with both tvdb and tvrage. if len(nlist) >= 4: logger.log( u"Found indexer mapping in cache for show: " + showObj.name, logger.DEBUG) mapped[int(curResult['mindexer'])] = int(curResult['mindexer_id']) return mapped else: sql_l = [] for indexer in sickbeard.indexerApi().indexers: if indexer == showObj.indexer: mapped[indexer] = showObj.indexerid continue lINDEXER_API_PARMS = sickbeard.indexerApi( indexer).api_params.copy() lINDEXER_API_PARMS['custom_ui'] = classes.ShowListUI t = sickbeard.indexerApi(indexer).indexer(**lINDEXER_API_PARMS) try: mapped_show = t[showObj.name] except Exception: logger.log( u"Unable to map " + sickbeard.indexerApi(showObj.indexer).name + "->" + sickbeard.indexerApi(indexer).name + " for show: " + showObj.name + ", skipping it", logger.DEBUG) continue if mapped_show and len(mapped_show) == 1: logger.log( u"Mapping " + sickbeard.indexerApi(showObj.indexer).name + "->" + sickbeard.indexerApi(indexer).name + " for show: " + showObj.name, logger.DEBUG) mapped[indexer] = int(mapped_show[0]['id']) logger.log( u"Adding indexer mapping to DB for show: " + showObj.name, logger.DEBUG) sql_l.append([ "INSERT OR IGNORE INTO indexer_mapping (indexer_id, indexer, mindexer_id, mindexer) VALUES (?,?,?,?)", [ showObj.indexerid, showObj.indexer, int(mapped_show[0]['id']), indexer ] ]) if len(sql_l) > 0: myDB = db.DBConnection() myDB.mass_action(sql_l) return mapped
def run(self): ShowQueueItem.run(self) logger.log('Starting to add show %s' % self.showDir) # make sure the Indexer IDs are valid try: lINDEXER_API_PARMS = sickbeard.indexerApi( self.indexer).api_params.copy() if self.lang: lINDEXER_API_PARMS['language'] = self.lang logger.log(u'' + str(sickbeard.indexerApi(self.indexer).name) + ': ' + repr(lINDEXER_API_PARMS)) t = sickbeard.indexerApi( self.indexer).indexer(**lINDEXER_API_PARMS) s = t[self.indexer_id, 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.indexerApi(self.indexer).name), logger.ERROR) self._finishEarly() return # this usually only happens if they have an NFO in their show dir which gave us a Indexer ID that has no proper english version of the show if getattr(s, 'seriesname', None) is None: logger.log( 'Show in %s has no name on %s, probably the wrong language used to search with.' % (self.showDir, sickbeard.indexerApi(self.indexer).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.indexerApi(self.indexer).name)) self._finishEarly() return except Exception as e: logger.log( 'Unable to find show ID:%s on Indexer: %s' % (self.indexer_id, sickbeard.indexerApi(self.indexer).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.indexerApi( self.indexer).name, self.indexer_id)) self._finishEarly() return try: newShow = TVShow(self.indexer, self.indexer_id, self.lang) newShow.loadFromIndexer() self.show = newShow # set up initial values self.show.location = self.showDir self.show.subtitles = self.subtitles if None is not self.subtitles else sickbeard.SUBTITLES_DEFAULT self.show.quality = self.quality if self.quality else sickbeard.QUALITY_DEFAULT self.show.upgrade_once = self.upgrade_once self.show.flatten_folders = self.flatten_folders if None is not self.flatten_folders else sickbeard.FLATTEN_FOLDERS_DEFAULT self.show.anime = self.anime if None is not self.anime else sickbeard.ANIME_DEFAULT self.show.scene = self.scene if None is not self.scene else sickbeard.SCENE_DEFAULT self.show.paused = self.paused if None is not self.paused else False self.show.prune = self.prune if None is not self.prune else 0 self.show.tag = self.tag if None is not self.tag else 'Show List' if self.show.anime: self.show.release_groups = BlackAndWhiteList( self.show.indexerid) if self.blacklist: self.show.release_groups.set_black_keywords(self.blacklist) if self.whitelist: self.show.release_groups.set_white_keywords(self.whitelist) # be smartish about this if self.show.genre and 'talk show' in self.show.genre.lower(): self.show.air_by_date = 1 if self.show.genre and 'documentary' in self.show.genre.lower(): self.show.air_by_date = 0 if self.show.classification and 'sports' in self.show.classification.lower( ): self.show.sports = 1 except sickbeard.indexer_exception as e: logger.log( 'Unable to add show due to an error with %s: %s' % (sickbeard.indexerApi(self.indexer).name, ex(e)), logger.ERROR) if self.show: ui.notifications.error( 'Unable to add %s due to an error with %s' % (self.show.name, sickbeard.indexerApi(self.indexer).name)) else: ui.notifications.error( 'Unable to add show due to an error with %s' % sickbeard.indexerApi(self.indexer).name) self._finishEarly() return except exceptions.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 except Exception as e: logger.log('Error trying to add show: %s' % ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.ERROR) self._finishEarly() raise self.show.load_imdb_info() try: self.show.saveToDB() except 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) try: self.show.loadEpisodesFromIndexer() except Exception as e: logger.log( 'Error with %s, not creating episode list: %s' % (sickbeard.indexerApi(self.show.indexer).name, ex(e)), logger.ERROR) logger.log(traceback.format_exc(), logger.ERROR) try: self.show.loadEpisodesFromDir() except 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 showid = ? AND season != 0', [self.default_status, SKIPPED, self.show.indexerid]) # if they gave a number to start or number to end as wanted, then change those eps to it def get_wanted(db_obj, wanted_max, latest): actual = 0 if wanted_max: select_id = 'FROM [tv_episodes] t5 JOIN (SELECT t3.indexerid, t3.status, t3.season*1000000+t3.episode AS t3_se, t2.start_season FROM [tv_episodes] t3'\ + ' JOIN (SELECT t1.showid, M%s(t1.season) AS start_season' % ('IN', 'AX')[latest]\ + ', MAX(t1.airdate) AS airdate, t1.episode, t1.season*1000000+t1.episode AS se FROM [tv_episodes] t1'\ + ' WHERE %s=t1.showid' % self.show.indexerid\ + ' AND 0<t1.season AND t1.status NOT IN (%s)) AS t2' % UNAIRED\ + ' ON t2.showid=t3.showid AND 0<t3.season AND t2.se>=t3_se ORDER BY t3_se %sSC' % ('A', 'DE')[latest]\ + ' %s) as t4' % (' LIMIT %s' % wanted_max, '')[-1 == wanted_max]\ + ' ON t4.indexerid=t5.indexerid'\ + '%s' % ('', ' AND t4.start_season=t5.season')[-1 == wanted_max]\ + ' AND t4.status NOT IN (%s)' % ','.join([str(x) for x in sickbeard.common.Quality.DOWNLOADED + [WANTED]]) select = 'SELECT t5.indexerid as indexerid, t5.season as season, t5.episode as episode, t5.status as status ' + select_id update = 'UPDATE [tv_episodes] SET status=%s WHERE indexerid IN (SELECT t5.indexerid %s)' % ( WANTED, select_id) wanted_updates = db_obj.select(select) db_obj.action(update) result = db_obj.select( 'SELECT changes() as last FROM [tv_episodes]') for cur_result in result: actual = cur_result['last'] break action_log = 'didn\'t find any episodes that need to be set wanted' if actual: action_log = ('updated %s %s episodes > %s' % ( (((('%s of %s' % (actual, wanted_max)), ('%s of max %s limited' % (actual, wanted_max)))[10 == wanted_max]), ('max %s available' % actual))[-1 == wanted_max], ('first season', 'latest')[latest], ','.join( [('S%02dE%02d=%d' % (a['season'], a['episode'], a['status'])) for a in wanted_updates]))) logger.log('Get wanted ' + action_log) return actual items_wanted = get_wanted(my_db, self.default_wanted_begin, latest=False) items_wanted += get_wanted(my_db, self.default_wanted_latest, latest=True) self.show.writeMetadata() self.show.updateMetadata() self.show.populateCache() self.show.flushEpisodes() # load ids self.show.ids # if sickbeard.USE_TRAKT: # # if there are specific episodes that need to be added by trakt # sickbeard.traktCheckerScheduler.action.manageNewShow(self.show) # # # add show to trakt.tv library # if sickbeard.TRAKT_SYNC: # sickbeard.traktCheckerScheduler.action.addShowToTraktLibrary(self.show) # Load XEM data to DB for show sickbeard.scene_numbering.xem_refresh(self.show.indexerid, self.show.indexer, force=True) if self.show.scene: # enable/disable scene flag based on if show has an explicit _scene_ mapping at XEM self.show.scene = sickbeard.scene_numbering.has_xem_scene_mapping( self.show.indexerid, self.show.indexer) # if "scene" numbering is disabled during add show, output availability to log if None is not self.scene and not self.show.scene and \ self.show.indexerid in sickbeard.scene_exceptions.xem_ids_list[self.show.indexer]: 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.saveToDB() except 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) self.show.loadEpisodesFromDB() 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]) #@UndefinedVariable ui.notifications.message( 'Show added/search', 'Adding and searching for episodes of' + msg) else: ui.notifications.message('Show added', 'Adding' + msg) self.finish()
def _ep_data(self, ep_obj): """ Creates an elementTree XML structure for a MediaBrowser style episode.xml and returns the resulting data object. show_obj: a TVShow instance to create the NFO for """ eps_to_write = [ep_obj] + ep_obj.relatedEps indexer_lang = ep_obj.show.lang try: # There's gotta be a better way of doing this but we don't wanna # change the language value elsewhere lINDEXER_API_PARMS = sickbeard.indexerApi( ep_obj.show.indexer).api_params.copy() if indexer_lang and not indexer_lang == 'en': lINDEXER_API_PARMS['language'] = indexer_lang if ep_obj.show.dvdorder != 0: lINDEXER_API_PARMS['dvdorder'] = True t = sickbeard.indexerApi( ep_obj.show.indexer).indexer(**lINDEXER_API_PARMS) myShow = t[ep_obj.show.indexerid] except sickbeard.indexer_shownotfound as e: raise exceptions.ShowNotFoundException(e.message) except sickbeard.indexer_error as e: logger.log( u"Unable to connect to TVDB while creating meta files - skipping - " + ex(e), logger.ERROR) return False if not self._valid_show(myShow, ep_obj.show): 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 curEpToWrite in eps_to_write: try: myEp = myShow[curEpToWrite.season][curEpToWrite.episode] except (StandardError, Exception): logger.log( u"Unable to find episode " + str(curEpToWrite.season) + "x" + str(curEpToWrite.episode) + " on tvdb... has it been removed? Should I delete from db?" ) return None if curEpToWrite == ep_obj: # root (or single) episode # default to today's date for specials if firstaired is not set if myEp['firstaired'] == None and ep_obj.season == 0: myEp['firstaired'] = str(datetime.date.fromordinal(1)) if myEp['episodename'] == None or myEp['firstaired'] == None: return None episode = movie EpisodeName = etree.SubElement(episode, "title") if curEpToWrite.name != None: EpisodeName.text = curEpToWrite.name else: EpisodeName.text = "" SeasonNumber = etree.SubElement(episode, "season") SeasonNumber.text = str(curEpToWrite.season) EpisodeNumber = etree.SubElement(episode, "episode") EpisodeNumber.text = str(ep_obj.episode) year = etree.SubElement(episode, "year") if myShow["firstaired"] != None: try: year_text = str( datetime.datetime.strptime(myShow["firstaired"], '%Y-%m-%d').year) if year_text: year.text = year_text except: pass plot = etree.SubElement(episode, "plot") if myShow["overview"] != None: plot.text = myShow["overview"] Overview = etree.SubElement(episode, "episodeplot") if curEpToWrite.description != None: Overview.text = curEpToWrite.description else: Overview.text = "" mpaa = etree.SubElement(episode, "mpaa") if myShow["contentrating"] != None: mpaa.text = myShow["contentrating"] if not ep_obj.relatedEps: if myEp["rating"] != None: try: rating = int((float(myEp['rating']) * 10)) except ValueError: rating = 0 Rating = etree.SubElement(episode, "rating") rating_text = str(rating) if rating_text != None: Rating.text = rating_text director = etree.SubElement(episode, "director") director_text = myEp['director'] if director_text != None: director.text = director_text credits = etree.SubElement(episode, "credits") credits_text = myEp['writer'] if credits_text != None: credits.text = credits_text cast = etree.SubElement(episode, "cast") self.add_actor_element(myShow, etree, cast) else: # append data from (if any) related episodes if curEpToWrite.name: if not EpisodeName.text: EpisodeName.text = curEpToWrite.name else: EpisodeName.text = EpisodeName.text + ", " + curEpToWrite.name if curEpToWrite.description: if not Overview.text: Overview.text = curEpToWrite.description else: Overview.text = Overview.text + "\r" + curEpToWrite.description helpers.indentXML(rootNode) data = etree.ElementTree(rootNode) return data
try: self.show.saveToDB() except Exception, e: logger.log(u"Error saving the show to the database: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) self._finishEarly() raise # add it to the show list sickbeard.showList.append(self.show) try: self.show.loadEpisodesFromIndexer() except Exception, e: logger.log( u"Error with " + sickbeard.indexerApi(self.show.indexer).name + ", not creating episode list: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) # before we parse local files lets update exceptions sickbeard.scene_exceptions.retrieve_exceptions() try: self.show.loadEpisodesFromDir() except Exception, e: logger.log(u"Error searching dir for episodes: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) # if they gave a custom status then change all the eps to it if self.default_status != SKIPPED: logger.log(u"Setting all episodes to the specified default status: " + str(self.default_status))
def _show_data(self, show_obj): """ Creates an elementTree XML structure for a MediaBrowser-style series.xml returns the resulting data object. show_obj: a TVShow instance to create the NFO for """ indexer_lang = show_obj.lang lINDEXER_API_PARMS = sickbeard.indexerApi( show_obj.indexer).api_params.copy() lINDEXER_API_PARMS['actors'] = True if indexer_lang and not indexer_lang == 'en': lINDEXER_API_PARMS['language'] = indexer_lang if show_obj.dvdorder != 0: lINDEXER_API_PARMS['dvdorder'] = True t = sickbeard.indexerApi( show_obj.indexer).indexer(**lINDEXER_API_PARMS) 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: myShow = t[int(show_obj.indexerid)] except sickbeard.indexer_shownotfound: logger.log( u"Unable to find show with id " + str(show_obj.indexerid) + " on tvdb, skipping it", logger.ERROR) raise except sickbeard.indexer_error: logger.log(u"TVDB is down, can't use its data to make the NFO", logger.ERROR) raise if not self._valid_show(myShow, show_obj): return # check for title and id try: if myShow['seriesname'] == None or myShow[ 'seriesname'] == "" or myShow['id'] == None or myShow[ 'id'] == "": logger.log( u"Incomplete info for show with id " + str(show_obj.indexerid) + " on tvdb, skipping it", logger.ERROR) return False except sickbeard.indexer_attributenotfound: logger.log( u"Incomplete info for show with id " + str(show_obj.indexerid) + " on tvdb, skipping it", logger.ERROR) return False SeriesName = etree.SubElement(tv_node, "title") if myShow['seriesname'] != None: SeriesName.text = myShow['seriesname'] else: SeriesName.text = "" Genres = etree.SubElement(tv_node, "genres") if myShow["genre"] != None: for genre in myShow['genre'].split('|'): if genre and genre.strip(): cur_genre = etree.SubElement(Genres, "Genre") cur_genre.text = genre.strip() FirstAired = etree.SubElement(tv_node, "premiered") if myShow['firstaired'] != None: FirstAired.text = myShow['firstaired'] year = etree.SubElement(tv_node, "year") if myShow["firstaired"] != None: try: year_text = str( datetime.datetime.strptime(myShow["firstaired"], '%Y-%m-%d').year) if year_text: year.text = year_text except: pass if myShow['rating'] != None: try: rating = int((float(myShow['rating']) * 10)) except ValueError: rating = 0 Rating = etree.SubElement(tv_node, "rating") rating_text = str(rating) if rating_text != None: Rating.text = rating_text Status = etree.SubElement(tv_node, "status") if myShow['status'] != None: Status.text = myShow['status'] mpaa = etree.SubElement(tv_node, "mpaa") if myShow["contentrating"] != None: mpaa.text = myShow["contentrating"] IMDB_ID = etree.SubElement(tv_node, "id") if myShow['imdb_id'] != None: IMDB_ID.attrib["moviedb"] = "imdb" IMDB_ID.text = myShow['imdb_id'] indexerid = etree.SubElement(tv_node, "indexerid") if myShow['id'] != None: indexerid.text = myShow['id'] Runtime = etree.SubElement(tv_node, "runtime") if myShow['runtime'] != None: Runtime.text = myShow['runtime'] cast = etree.SubElement(tv_node, "cast") self.add_actor_element(myShow, etree, cast) helpers.indentXML(rootNode) data = etree.ElementTree(rootNode) return data
def _ep_data(self, ep_obj): """ Creates an elementTree XML structure for a MediaBrowser style episode.xml and returns the resulting data object. show_obj: a TVShow instance to create the NFO for """ eps_to_write = [ep_obj] + ep_obj.relatedEps persons_dict = {'Director': [], 'GuestStar': [], 'Writer': []} indexer_lang = ep_obj.show.lang try: lINDEXER_API_PARMS = sickbeard.indexerApi( ep_obj.show.indexer).api_params.copy() lINDEXER_API_PARMS['actors'] = True if indexer_lang and not indexer_lang == sickbeard.INDEXER_DEFAULT_LANGUAGE: lINDEXER_API_PARMS['language'] = indexer_lang if ep_obj.show.dvdorder != 0: lINDEXER_API_PARMS['dvdorder'] = True t = sickbeard.indexerApi( ep_obj.show.indexer).indexer(**lINDEXER_API_PARMS) myShow = t[ep_obj.show.indexerid] except sickbeard.indexer_shownotfound as e: raise ShowNotFoundException(e.message) except sickbeard.indexer_error as e: logger.log( u"Unable to connect to " + sickbeard.indexerApi(ep_obj.show.indexer).name + " while creating meta files - skipping - {}".format(ex(e)), logger.ERROR) return False rootNode = etree.Element("Item") # write an MediaBrowser XML containing info for all matching episodes for curEpToWrite in eps_to_write: try: myEp = myShow[curEpToWrite.season][curEpToWrite.episode] except (sickbeard.indexer_episodenotfound, sickbeard.indexer_seasonnotfound): logger.log( u"Unable to find episode %dx%d on %s... has it been removed? Should I delete from db?" % (curEpToWrite.season, curEpToWrite.episode, sickbeard.indexerApi(ep_obj.show.indexer).name)) return None if curEpToWrite == ep_obj: # root (or single) episode # default to today's date for specials if firstaired is not set if ep_obj.season == 0 and not getattr(myEp, 'firstaired', None): myEp['firstaired'] = str(datetime.date.fromordinal(1)) if not (getattr(myEp, 'episodename', None) and getattr(myEp, 'firstaired', None)): return None episode = rootNode if curEpToWrite.name: EpisodeName = etree.SubElement(episode, "EpisodeName") EpisodeName.text = curEpToWrite.name EpisodeNumber = etree.SubElement(episode, "EpisodeNumber") EpisodeNumber.text = str(ep_obj.episode) if ep_obj.relatedEps: EpisodeNumberEnd = etree.SubElement( episode, "EpisodeNumberEnd") EpisodeNumberEnd.text = str(curEpToWrite.episode) SeasonNumber = etree.SubElement(episode, "SeasonNumber") SeasonNumber.text = str(curEpToWrite.season) if not ep_obj.relatedEps and getattr(myEp, 'absolute_number', None): absolute_number = etree.SubElement(episode, "absolute_number") absolute_number.text = str(myEp['absolute_number']) if curEpToWrite.airdate != datetime.date.fromordinal(1): FirstAired = etree.SubElement(episode, "FirstAired") FirstAired.text = str(curEpToWrite.airdate) MetadataType = etree.SubElement(episode, "Type") MetadataType.text = "Episode" if curEpToWrite.description: Overview = etree.SubElement(episode, "Overview") Overview.text = curEpToWrite.description if not ep_obj.relatedEps: if getattr(myEp, 'rating', None): Rating = etree.SubElement(episode, "Rating") Rating.text = myEp['rating'] if getattr(myShow, 'imdb_id', None): IMDB_ID = etree.SubElement(episode, "IMDB_ID") IMDB_ID.text = myShow['imdb_id'] IMDB = etree.SubElement(episode, "IMDB") IMDB.text = myShow['imdb_id'] IMDbId = etree.SubElement(episode, "IMDbId") IMDbId.text = myShow['imdb_id'] indexerid = etree.SubElement(episode, "id") indexerid.text = str(curEpToWrite.indexerid) Persons = etree.SubElement(episode, "Persons") if getattr(myShow, '_actors', None): for actor in myShow['_actors']: if not ('name' in actor and actor['name'].strip()): continue cur_actor = etree.SubElement(Persons, "Person") cur_actor_name = etree.SubElement(cur_actor, "Name") cur_actor_name.text = actor['name'].strip() cur_actor_type = etree.SubElement(cur_actor, "Type") cur_actor_type.text = "Actor" if 'role' in actor and actor['role'].strip(): cur_actor_role = etree.SubElement( cur_actor, "Role") cur_actor_role.text = actor['role'].strip() Language = etree.SubElement(episode, "Language") try: Language.text = myEp['language'] except Exception: Language.text = sickbeard.INDEXER_DEFAULT_LANGUAGE # 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 = thumb_text else: # append data from (if any) related episodes EpisodeNumberEnd.text = str(curEpToWrite.episode) if curEpToWrite.name: if not EpisodeName.text: EpisodeName.text = curEpToWrite.name else: EpisodeName.text = EpisodeName.text + ", " + curEpToWrite.name if curEpToWrite.description: if not Overview.text: Overview.text = curEpToWrite.description else: Overview.text = Overview.text + "\r" + curEpToWrite.description # collect all directors, guest stars and writers if getattr(myEp, 'director', None): persons_dict['Director'] += [ x.strip() for x in myEp['director'].split('|') if x.strip() ] if getattr(myEp, 'gueststars', None): persons_dict['GuestStar'] += [ x.strip() for x in myEp['gueststars'].split('|') if x.strip() ] if getattr(myEp, 'writer', None): persons_dict['Writer'] += [ x.strip() for x in myEp['writer'].split('|') if x.strip() ] # fill in Persons section with collected directors, guest starts and writers for person_type, names in persons_dict.iteritems(): # 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 = cur_name cur_person_type = etree.SubElement(Person, "Type") cur_person_type.text = person_type helpers.indentXML(rootNode) data = etree.ElementTree(rootNode) return data
raise exceptions.ShowNotFoundException(e.message) except sickbeard.indexer_error, e: logger.log(u"Unable to connect to " + sickbeard.indexerApi( ep_obj.show.indexer).name + " while creating meta files - skipping - " + ex(e), logger.ERROR) return False rootNode = etree.Element("Item") # write an MediaBrowser XML containing info for all matching episodes for curEpToWrite in eps_to_write: try: myEp = myShow[curEpToWrite.season][curEpToWrite.episode] except (sickbeard.indexer_episodenotfound, sickbeard.indexer_seasonnotfound): logger.log(u"Unable to find episode " + str(curEpToWrite.season) + "x" + str( curEpToWrite.episode) + " on " + sickbeard.indexerApi( ep_obj.show.indexer).name + ".. has it been removed? Should I delete from db?") return None if curEpToWrite == ep_obj: # root (or single) episode # default to today's date for specials if firstaired is not set if getattr(myEp, 'firstaired', None) is None and ep_obj.season == 0: myEp['firstaired'] = str(datetime.date.fromordinal(1)) if getattr(myEp, 'episodename', None) is None or getattr(myEp, 'firstaired', None) is None: return None episode = rootNode EpisodeName = etree.SubElement(episode, "EpisodeName")
def _parse_string(self, name): if not name: return matches = [] bestResult = None for (cur_regex_num, cur_regex_name, cur_regex) in self.compiled_regexes: match = cur_regex.match(name) if not match: continue result = ParseResult(name) result.which_regex = [cur_regex_name] result.score = 0 - cur_regex_num named_groups = match.groupdict().keys() if 'series_name' in named_groups: result.series_name = match.group('series_name') if result.series_name: result.series_name = self.clean_series_name( result.series_name) result.score += 1 if 'series_num' in named_groups and match.group('series_num'): result.score += 1 if 'season_num' in named_groups: tmp_season = int(match.group('season_num')) if cur_regex_name == 'bare' and tmp_season in (19, 20): continue result.season_number = tmp_season result.score += 1 if 'ep_num' in named_groups: ep_num = self._convert_number(match.group('ep_num')) if 'extra_ep_num' in named_groups and match.group( 'extra_ep_num'): result.episode_numbers = range( ep_num, self._convert_number(match.group('extra_ep_num')) + 1) result.score += 1 else: result.episode_numbers = [ep_num] result.score += 1 if 'ep_ab_num' in named_groups: ep_ab_num = self._convert_number(match.group('ep_ab_num')) if 'extra_ab_ep_num' in named_groups and match.group( 'extra_ab_ep_num'): result.ab_episode_numbers = range( ep_ab_num, self._convert_number(match.group('extra_ab_ep_num')) + 1) result.score += 1 else: result.ab_episode_numbers = [ep_ab_num] result.score += 1 if 'air_date' in named_groups: air_date = match.group('air_date') try: result.air_date = parser.parse(air_date, fuzzy=True).date() result.score += 1 except: continue if 'extra_info' in named_groups: tmp_extra_info = match.group('extra_info') # Show.S04.Special or Show.S05.Part.2.Extras is almost certainly not every episode in the season if tmp_extra_info and cur_regex_name == 'season_only' and re.search( r'([. _-]|^)(special|extra)s?\w*([. _-]|$)', tmp_extra_info, re.I): continue result.extra_info = tmp_extra_info result.score += 1 if 'release_group' in named_groups: result.release_group = match.group('release_group') result.score += 1 if 'version' in named_groups: # assigns version to anime file if detected using anime regex. Non-anime regex receives -1 version = match.group('version') if version: result.version = version else: result.version = 1 else: result.version = -1 matches.append(result) if len(matches): # pick best match with highest score based on placement bestResult = max(sorted(matches, reverse=True, key=lambda x: x.which_regex), key=lambda x: x.score) show = None if not self.naming_pattern: # try and create a show object for this result show = helpers.get_show(bestResult.series_name, self.tryIndexers) # confirm passed in show object indexer id matches result show object indexer id if show: if self.showObj and show.indexerid != self.showObj.indexerid: show = None bestResult.show = show elif not show and self.showObj: bestResult.show = self.showObj # if this is a naming pattern test or result doesn't have a show object then return best result if not bestResult.show or self.naming_pattern: return bestResult # get quality bestResult.quality = common.Quality.nameQuality( name, bestResult.show.is_anime) new_episode_numbers = [] new_season_numbers = [] new_absolute_numbers = [] # if we have an air-by-date show then get the real season/episode numbers if bestResult.is_air_by_date: airdate = bestResult.air_date.toordinal() myDB = db.DBConnection() sql_result = myDB.select( "SELECT season, episode FROM tv_episodes WHERE showid = ? and indexer = ? and airdate = ?", [ bestResult.show.indexerid, bestResult.show.indexer, airdate ]) season_number = None episode_numbers = [] if sql_result: season_number = int(sql_result[0][0]) episode_numbers = [int(sql_result[0][1])] if not season_number or not len(episode_numbers): try: lINDEXER_API_PARMS = sickbeard.indexerApi( bestResult.show.indexer).api_params.copy() if bestResult.show.lang: lINDEXER_API_PARMS[ 'language'] = bestResult.show.lang t = sickbeard.indexerApi( bestResult.show.indexer).indexer( **lINDEXER_API_PARMS) epObj = t[bestResult.show.indexerid].airedOn( bestResult.air_date)[0] season_number = int(epObj["seasonnumber"]) episode_numbers = [int(epObj["episodenumber"])] except sickbeard.indexer_episodenotfound: logger.log( u"Unable to find episode with date " + str(bestResult.air_date) + " for show " + bestResult.show.name + ", skipping", logger.WARNING) episode_numbers = [] except sickbeard.indexer_error, e: logger.log( u"Unable to contact " + sickbeard.indexerApi( bestResult.show.indexer).name + ": " + ex(e), logger.WARNING) episode_numbers = [] for epNo in episode_numbers: s = season_number e = epNo if self.convert: (s, e) = scene_numbering.get_indexer_numbering( bestResult.show.indexerid, bestResult.show.indexer, season_number, epNo) new_episode_numbers.append(e) new_season_numbers.append(s) elif bestResult.show.is_anime and len( bestResult.ab_episode_numbers): scene_season = scene_exceptions.get_scene_exception_by_name( bestResult.series_name)[1] for epAbsNo in bestResult.ab_episode_numbers: a = epAbsNo if self.convert: a = scene_numbering.get_indexer_absolute_numbering( bestResult.show.indexerid, bestResult.show.indexer, epAbsNo, True, scene_season) (s, e) = helpers.get_all_episodes_from_absolute_number( bestResult.show, [a]) new_absolute_numbers.append(a) new_episode_numbers.extend(e) new_season_numbers.append(s) elif bestResult.season_number and len(bestResult.episode_numbers): for epNo in bestResult.episode_numbers: s = bestResult.season_number e = epNo if self.convert: (s, e) = scene_numbering.get_indexer_numbering( bestResult.show.indexerid, bestResult.show.indexer, bestResult.season_number, epNo) if bestResult.show.is_anime: a = helpers.get_absolute_number_from_season_and_episode( bestResult.show, s, e) if a: new_absolute_numbers.append(a) new_episode_numbers.append(e) new_season_numbers.append(s) # need to do a quick sanity check heregex. It's possible that we now have episodes # from more than one season (by tvdb numbering), and this is just too much # for sickbeard, so we'd need to flag it. new_season_numbers = list( set(new_season_numbers)) # remove duplicates if len(new_season_numbers) > 1: raise InvalidNameException( "Scene numbering results episodes from " "seasons %s, (i.e. more than one) and " "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): bestResult.ab_episode_numbers = new_absolute_numbers if len(new_season_numbers) and len(new_episode_numbers): bestResult.episode_numbers = new_episode_numbers bestResult.season_number = new_season_numbers[0] if self.convert: logger.log( u"Converted parsed result " + bestResult.original_name + " into " + str(bestResult).decode('utf-8', 'xmlcharrefreplace'), logger.DEBUG)
def run(self): ShowQueueItem.run(self) logger.log(u"Beginning update of " + self.show.name, logger.DEBUG) logger.log(u"Retrieving show info from " + sickbeard.indexerApi(self.show.indexer).name + "", logger.DEBUG) try: self.show.loadFromIndexer(cache=not self.force) except sickbeard.indexer_error as e: logger.log(u"Unable to contact " + sickbeard.indexerApi(self.show.indexer).name + ", aborting: " + ex(e), logger.WARNING) return except sickbeard.indexer_attributenotfound as e: logger.log(u"Data retrieved from " + sickbeard.indexerApi( self.show.indexer).name + " was incomplete, aborting: " + ex(e), logger.ERROR) return logger.log(u"Retrieving show info from IMDb", logger.DEBUG) try: self.show.loadIMDbInfo() except imdb_exceptions.IMDbError as e: logger.log(u" Something wrong on IMDb api: " + ex(e), logger.WARNING) except Exception as e: logger.log(u"Error loading IMDb info: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) # have to save show before reading episodes from db try: self.show.saveToDB() except Exception as e: logger.log(u"Error saving show info to the database: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) # get episode list from DB logger.log(u"Loading all episodes from the database", logger.DEBUG) DBEpList = self.show.loadEpisodesFromDB() # get episode list from TVDB logger.log(u"Loading all episodes from " + sickbeard.indexerApi(self.show.indexer).name + "", logger.DEBUG) try: IndexerEpList = self.show.loadEpisodesFromIndexer(cache=not self.force) except sickbeard.indexer_exception as e: logger.log(u"Unable to get info from " + sickbeard.indexerApi( self.show.indexer).name + ", the show info will not be refreshed: " + ex(e), logger.ERROR) IndexerEpList = None if IndexerEpList is None: logger.log(u"No data returned from " + sickbeard.indexerApi( self.show.indexer).name + ", unable to update this show", logger.ERROR) else: # for each ep we found on the Indexer delete it from the DB list for curSeason in IndexerEpList: for curEpisode in IndexerEpList[curSeason]: curEp = self.show.getEpisode(curSeason, curEpisode) curEp.saveToDB() if curSeason in DBEpList and curEpisode in DBEpList[curSeason]: del DBEpList[curSeason][curEpisode] # remaining episodes in the DB list are not on the indexer, just delete them from the DB for curSeason in DBEpList: for curEpisode in DBEpList[curSeason]: logger.log(u"Permanently deleting episode " + str(curSeason) + "x" + str( curEpisode) + " from the database", logger.INFO) curEp = self.show.getEpisode(curSeason, curEpisode) try: curEp.deleteEpisode() except EpisodeDeletedException: pass # save show again, in case episodes have changed try: self.show.saveToDB() except Exception as e: logger.log(u"Error saving show info to the database: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) logger.log(u"Finished update of " + self.show.name, logger.DEBUG) sickbeard.showQueueScheduler.action.refreshShow(self.show, self.force) self.finish()
t = sickbeard.indexerApi( show_obj.indexer).indexer(**lINDEXER_API_PARMS) indexer_show_obj = t[show_obj.indexerid] except (sickbeard.indexer_error, IOError), e: logger.log( u"Unable to look up show on " + sickbeard.indexerApi(show_obj.indexer).name + ", not downloading images: " + ex(e), logger.ERROR) return None if image_type not in ('fanart', 'poster', 'banner', 'poster_thumb', 'banner_thumb'): logger.log( u"Invalid image type " + str(image_type) + ", couldn't find it in the " + sickbeard.indexerApi(show_obj.indexer).name + " object", logger.ERROR) return None if image_type == 'poster_thumb': if getattr(indexer_show_obj, 'poster', None) is not None: image_url = re.sub('posters', '_cache/posters', indexer_show_obj['poster']) elif image_type == 'banner_thumb': if getattr(indexer_show_obj, 'banner', None) is not None: image_url = re.sub('graphical', '_cache/graphical', indexer_show_obj['banner']) else: if getattr(indexer_show_obj, image_type, None) is not None: image_url = indexer_show_obj[image_type]