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 = app.indexerApi(show_obj.indexer).api_params.copy() lINDEXER_API_PARMS['banners'] = True if indexer_lang and not indexer_lang == app.INDEXER_DEFAULT_LANGUAGE: lINDEXER_API_PARMS['language'] = indexer_lang if show_obj.dvdorder != 0: lINDEXER_API_PARMS['dvdorder'] = True t = app.indexerApi(show_obj.indexer).indexer(**lINDEXER_API_PARMS) indexer_show_obj = t[show_obj.indexerid] except (app.indexer_error, IOError) as e: logger.log(u"Unable to look up show on " + app.indexerApi( show_obj.indexer).name + ", not downloading images: " + ex(e), logger.WARNING) logger.log(u"%s may be experiencing some problems. Try again later." % app.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 " + app.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 _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 = app.indexerApi(show_obj.indexer).api_params.copy() lINDEXER_API_PARMS['banners'] = True if indexer_lang and not indexer_lang == app.INDEXER_DEFAULT_LANGUAGE: lINDEXER_API_PARMS['language'] = indexer_lang if show_obj.dvdorder != 0: lINDEXER_API_PARMS['dvdorder'] = True t = app.indexerApi(show_obj.indexer).indexer(**lINDEXER_API_PARMS) indexer_show_obj = t[show_obj.indexerid] except (app.indexer_error, IOError) as e: logger.log(u"Unable to look up show on " + app.indexerApi( show_obj.indexer).name + ", not downloading images: " + ex(e), logger.WARNING) logger.log(u"%s may be experiencing some problems. Try again later." % app.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'] == app.INDEXER_DEFAULT_LANGUAGE: result[season][seasonArtID] = seasonsArtObj[seasonArtID]['_bannerpath'] return result
def remove_show_trakt_library(self, show_obj): """Remove Show from trakt collections""" if self.find_show(show_obj.indexerid): trakt_id = app.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('Removing {0} from Trakt library'.format(show_obj.name), logger.DEBUG) # Remove all episodes from the Trakt collection for this show try: self.remove_episode_trakt_collection(filter_show=show_obj) except TraktException as e: logger.log('Could not connect to Trakt. Aborting removing episodes for show {0} from Trakt library. Error: {1}'. format(show_obj.name, repr(e)), logger.WARNING) try: self.trakt_api.request('sync/collection/remove', data, method='POST') except TraktException as e: logger.log('Could not connect to Trakt. Aborting removing show {0} from Trakt library. Error: {1}'. format(show_obj.name, repr(e)), logger.WARNING)
def add_show_trakt_library(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.find_show(show_obj.indexerid): trakt_id = app.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 data: logger.log('Adding {0} to Trakt library'.format(show_obj.name), logger.DEBUG) try: self.trakt_api.request('sync/collection', data, method='POST') except TraktException as e: logger.log('Could not connect to Trakt. Aborting adding show {0} to Trakt library. Error: {1}'.format(show_obj.name, repr(e)), logger.WARNING) return
def add_episode_trakt_collection(self): """Add all episodes from local library to Trakt collections. Enabled through app.TRAKT_SYNC_WATCHLIST setting""" if app.TRAKT_SYNC and app.USE_TRAKT: main_db_con = db.DBConnection() selection_status = ['?' for _ in Quality.DOWNLOADED + Quality.ARCHIVED] sql_selection = b'select tv_shows.indexer, tv_shows.startyear, showid, show_name, season, ' \ b'episode from tv_episodes,tv_shows where tv_shows.indexer_id = tv_episodes.showid ' \ b"and tv_episodes.status in ({0}) and tv_episodes.location <> ''".format(','.join(selection_status)) episodes = main_db_con.select(sql_selection, Quality.DOWNLOADED + Quality.ARCHIVED) if episodes: trakt_data = [] for cur_episode in episodes: trakt_id = app.indexerApi(cur_episode[b'indexer']).config['trakt_id'] if not self._check_list(trakt_id, cur_episode[b'showid'], cur_episode[b'season'], cur_episode[b'episode'], List='Collection'): logger.log('Adding Episode {show} {ep} to collection'.format (show=cur_episode[b'show_name'], ep=episode_num(cur_episode[b'season'], cur_episode[b'episode'])), logger.DEBUG) trakt_data.append((cur_episode[b'showid'], cur_episode[b'indexer'], cur_episode[b'show_name'], cur_episode[b'startyear'], cur_episode[b'season'], cur_episode[b'episode'])) if trakt_data: try: data = self.trakt_bulk_data_generate(trakt_data) self.trakt_api.request('sync/collection', data, method='POST') self._get_show_collection() except TraktException as e: logger.log('Could not connect to Trakt. Error: {0}'.format(ex(e)), logger.WARNING)
def add_episode_watchlist(self): if app.TRAKT_SYNC_WATCHLIST and app.USE_TRAKT: main_db_con = db.DBConnection() selection_status = [b'?' for _ in Quality.SNATCHED + Quality.SNATCHED_PROPER + [WANTED]] sql_selection = b'select tv_shows.indexer, tv_shows.startyear, showid, show_name, season, episode from tv_episodes, ' \ b'tv_shows where tv_shows.indexer_id = tv_episodes.showid and tv_episodes.status in ({0})'.format(b','.join(selection_status)) episodes = main_db_con.select(sql_selection, Quality.SNATCHED + Quality.SNATCHED_PROPER + [WANTED]) if episodes: trakt_data = [] for cur_episode in episodes: trakt_id = app.indexerApi(cur_episode[b'indexer']).config['trakt_id'] if not self._check_list(trakt_id, cur_episode[b'showid'], cur_episode[b'season'], cur_episode[b'episode']): logger.log('Adding Episode {show} {ep} to watchlist'.format (show=cur_episode[b'show_name'], ep=episode_num(cur_episode[b'season'], cur_episode[b'episode'])), logger.DEBUG) trakt_data.append((cur_episode[b'showid'], cur_episode[b'indexer'], cur_episode[b'show_name'], cur_episode[b'startyear'], cur_episode[b'season'], cur_episode[b'episode'])) if trakt_data: try: data = self.trakt_bulk_data_generate(trakt_data) self.trakt_api.request('sync/watchlist', data, method='POST') self._get_episode_watchlist() except TraktException as e: logger.log('Could not connect to Trakt. Error: {0}'.format(ex(e)), logger.WARNING)
def add_show_watchlist(self): if app.TRAKT_SYNC_WATCHLIST and app.USE_TRAKT: logger.log('Syncing shows to Trakt watchlist', logger.DEBUG) if app.showList: trakt_data = [] for show_obj in app.showList: trakt_id = app.indexerApi(show_obj.indexer).config['trakt_id'] if not self._check_list(trakt_id, show_obj.indexerid, 0, 0, List='Show'): logger.log('Adding Show {0} with ID: {1} to Trakt watchlist'.format(show_obj.name, show_obj.indexerid), logger.DEBUG) show_el = {'title': show_obj.name, 'year': show_obj.startyear, 'ids': {}} if trakt_id == 'tvdb_id': show_el['ids']['tvdb'] = show_obj.indexerid else: show_el['ids']['tvrage'] = show_obj.indexerid trakt_data.append(show_el) if trakt_data: try: data = {'shows': trakt_data} self.trakt_api.request('sync/watchlist', data, method='POST') self._get_show_watchlist() except TraktException as e: logger.log('Could not connect to Trakt. Error: {0}'.format(ex(e)), logger.WARNING)
def fetch_trakt_shows(self): if not self.show_watchlist: logger.log('No shows found in your watchlist, aborting watchlist update', logger.DEBUG) else: indexer = int(app.TRAKT_DEFAULT_INDEXER) trakt_id = app.indexerApi(indexer).config['trakt_id'] for watchlisted_show in self.show_watchlist[trakt_id]: indexer_id = int(watchlisted_show) show_obj = self.show_watchlist[trakt_id][watchlisted_show] if show_obj['year'] and show_obj['slug'].endswith(str(show_obj['year'])): show_name = '{0} ({1})'.format(show_obj['title'], show_obj['year']) else: show_name = show_obj['title'] if int(app.TRAKT_METHOD_ADD) != 2: self.add_show(indexer, indexer_id, show_name, SKIPPED) else: self.add_show(indexer, indexer_id, show_name, WANTED) if int(app.TRAKT_METHOD_ADD) == 1: new_show = Show.find(app.showList, indexer_id) if new_show: setEpisodeToWanted(new_show, 1, 1) else: self.todoWanted.append(indexer_id, 1, 1)
def __str__(self): return u'{date} {name} {season}x{episode} of {series_id} from {indexer}'.format( date=self.date, name=self.name, season=self.season, episode=self.episode, series_id=self.indexerid, indexer=app.indexerApi(self.indexer).name)
def _get_xem_exceptions(): xem_exceptions = {} if should_refresh('xem'): for indexer in app.indexerApi().indexers: logger.log( 'Checking for XEM scene exceptions updates for {0}'.format( app.indexerApi(indexer).name)) xem_url = 'http://thexem.de/map/allNames?origin={0}&seasonNumbers=1'.format( app.indexerApi(indexer).config['xem_origin']) response = helpers.getURL(xem_url, session=xem_session, timeout=60, returns='response') try: jdata = response.json() except (ValueError, AttributeError) as error: logger.log( 'Check scene exceptions update failed for {0}. Unable to get URL: {1}' .format(app.indexerApi(indexer).name, xem_url), logger.DEBUG) continue if not jdata['data'] or jdata['result'] == 'failure': logger.log( 'No data returned from XEM while checking for scene exceptions. ' 'Update failed for {0}'.format( app.indexerApi(indexer).name), logger.DEBUG) continue for indexer_id, exceptions in iteritems(jdata['data']): try: xem_exceptions[indexer_id] = exceptions except Exception as error: logger.log( 'XEM: Rejected entry: Indexer ID: {0}, Exceptions: {1}' .format(indexer_id, exceptions), logger.WARNING) logger.log( 'XEM: Rejected entry error message: {0}'.format(error), logger.ERROR) set_last_refresh('xem') return xem_exceptions
def _get_custom_exceptions(): custom_exceptions = {} do_refresh = False for indexer in app.indexerApi().indexers: if should_refresh(app.indexerApi(indexer).name): do_refresh = True break if do_refresh: location = app.indexerApi(INDEXER_TVDB).config['scene_loc'] logger.log( 'Checking for scene exception updates from {0}'.format(location)) response = helpers.getURL(location, session=app.indexerApi(INDEXER_TVDB).session, timeout=60, returns='response') try: jdata = response.json() except (ValueError, AttributeError) as error: logger.log( 'Check scene exceptions update failed. Unable to update from {0}. Error: {1}' .format(location, error), logger.DEBUG) return custom_exceptions for indexer in app.indexerApi().indexers: try: for indexer_id in jdata[app.indexerApi( indexer).config['xem_origin']]: alias_list = [{ scene_exception: int(scene_season) } for scene_season in jdata[app.indexerApi( indexer).config['xem_origin']][indexer_id] for scene_exception in jdata[app.indexerApi( indexer).config['xem_origin']] [indexer_id][scene_season]] custom_exceptions[indexer_id] = alias_list except Exception as error: logger.log( 'Unable to update scene exceptions for {0}. Error: {1}'. format(indexer, error), logger.ERROR) continue set_last_refresh(app.indexerApi(indexer).name) return custom_exceptions
def newShow(self, show_to_add=None, other_shows=None, search_string=None): """ Display the new show page which collects a tvdb id, folder, and extra options and posts them to addNewShow """ t = PageTemplate(rh=self, filename='addShows_newShow.mako') indexer, show_dir, indexer_id, show_name = self.split_extra_show( show_to_add) use_provided_info = bool(indexer_id and indexer and show_name) # use the given show_dir for the indexer search if available if not show_dir: if search_string: default_show_name = search_string else: default_show_name = '' elif not show_name: default_show_name = re.sub( r' \(\d{4}\)', '', os.path.basename(os.path.normpath(show_dir)).replace('.', ' ')) else: default_show_name = show_name # carry a list of other dirs if given if not other_shows: other_shows = [] elif not isinstance(other_shows, list): other_shows = [other_shows] provided_indexer_id = int(indexer_id or 0) provided_indexer_name = show_name provided_indexer = int(indexer or app.INDEXER_DEFAULT) return t.render(enable_anime_options=True, use_provided_info=use_provided_info, default_show_name=default_show_name, other_shows=other_shows, provided_show_dir=show_dir, provided_indexer_id=provided_indexer_id, provided_indexer_name=provided_indexer_name, provided_indexer=provided_indexer, indexers=app.indexerApi().indexers, whitelist=[], blacklist=[], groups=[], title='New Show', header='New Show', topmenu='home', controller='addShows', action='newShow')
def update_library(ep_obj): """ Sends a request to trakt indicating that the given episode is part of our library. ep_obj: The TVEpisode object to add to trakt """ trakt_id = app.indexerApi(ep_obj.show.indexer).config['trakt_id'] # Create a trakt settings dict trakt_settings = {'trakt_api_secret': app.TRAKT_API_SECRET, 'trakt_api_key': app.TRAKT_API_KEY, 'trakt_access_token': app.TRAKT_ACCESS_TOKEN} trakt_api = TraktApi(app.SSL_VERIFY, app.TRAKT_TIMEOUT, **trakt_settings) if app.USE_TRAKT: try: # URL parameters data = { 'shows': [ { 'title': ep_obj.show.name, 'year': ep_obj.show.startyear, 'ids': {}, } ] } if trakt_id == 'tvdb_id': data['shows'][0]['ids']['tvdb'] = ep_obj.show.indexerid else: data['shows'][0]['ids']['tvrage'] = ep_obj.show.indexerid if app.TRAKT_SYNC_WATCHLIST: if app.TRAKT_REMOVE_SERIESLIST: trakt_api.request('sync/watchlist/remove', data, method='POST') # Add Season and Episode + Related Episodes data['shows'][0]['seasons'] = [{'number': ep_obj.season, 'episodes': []}] for relEp_Obj in [ep_obj] + ep_obj.related_episodes: data['shows'][0]['seasons'][0]['episodes'].append({'number': relEp_Obj.episode}) if app.TRAKT_SYNC_WATCHLIST: if app.TRAKT_REMOVE_WATCHLIST: trakt_api.request('sync/watchlist/remove', data, method='POST') # update library trakt_api.request('sync/collection', data, method='POST') except (TraktException, AuthException, ServerBusy) as trakt_ex: logger.log('Could not connect to Trakt service: {0}'.format(ex(trakt_ex)), logger.WARNING)
def searchIndexersForShowName(search_term, lang=None, indexer=None): if not lang or lang == 'null': lang = app.INDEXER_DEFAULT_LANGUAGE search_term = search_term.encode('utf-8') search_terms = [search_term] # If search term ends with what looks like a year, enclose it in () matches = re.match(r'^(.+ |)([12][0-9]{3})$', search_term) if matches: search_terms.append('%s(%s)' % (matches.group(1), matches.group(2))) for searchTerm in search_terms: # If search term begins with an article, let's also search for it without matches = re.match(r'^(?:a|an|the) (.+)$', searchTerm, re.I) if matches: search_terms.append(matches.group(1)) results = {} final_results = [] # Query Indexers for each search term and build the list of results for indexer in app.indexerApi().indexers if not int(indexer) else [ int(indexer) ]: l_indexer_api_parms = app.indexerApi(indexer).api_params.copy() l_indexer_api_parms['language'] = lang l_indexer_api_parms['custom_ui'] = classes.AllShowsListUI t = app.indexerApi(indexer).indexer(**l_indexer_api_parms) logger.log( u'Searching for Show with searchterm(s): %s on Indexer: %s' % (search_terms, app.indexerApi(indexer).name), logger.DEBUG) for searchTerm in search_terms: try: indexer_results = t[searchTerm] # add search results results.setdefault(indexer, []).extend(indexer_results) except indexer_exception as msg: logger.log( u'Error searching for show: {error}'.format(error=msg)) for i, shows in iteritems(results): final_results.extend({(app.indexerApi(i).name, i, app.indexerApi(i).config['show_url'], int(show['id']), show['seriesname'], show['firstaired']) for show in shows}) lang_id = app.indexerApi().config['langabbv_to_id'][lang] return json.dumps({'results': final_results, 'langid': lang_id})
def trakt_show_data_generate(data): showList = [] for indexer, indexerid, title, year in data: trakt_id = app.indexerApi(indexer).config['trakt_id'] show = {'title': title, 'year': year, 'ids': {}} if trakt_id == 'tvdb_id': show['ids']['tvdb'] = indexerid else: show['ids']['tvrage'] = indexerid showList.append(show) post_data = {'shows': showList} return post_data
def trakt_bulk_data_generate(data): # pylint: disable=too-many-locals """ Build the JSON structure to send back to Trakt """ uniqueShows = {} uniqueSeasons = {} for showid, indexerid, show_name, startyear, season, episode in data: if showid not in uniqueShows: uniqueShows[showid] = {'title': show_name, 'year': startyear, 'ids': {}, 'seasons': []} trakt_id = app.indexerApi(indexerid).config['trakt_id'] if trakt_id == 'tvdb_id': uniqueShows[showid]['ids']['tvdb'] = showid else: uniqueShows[showid]['ids']['tvrage'] = showid uniqueSeasons[showid] = [] # Get the unique seasons per Show for showid, indexerid, show_name, startyear, season, episode in data: if season not in uniqueSeasons[showid]: uniqueSeasons[showid].append(season) # build the query showList = [] seasonsList = {} for searchedShow in uniqueShows: seasonsList[searchedShow] = [] for searchedSeason in uniqueSeasons[searchedShow]: episodesList = [] for showid, indexerid, show_name, startyear, season, episode in data: if season == searchedSeason and showid == searchedShow: episodesList.append({'number': episode}) show = uniqueShows[searchedShow] show['seasons'].append({'number': searchedSeason, 'episodes': episodesList}) showList.append(show) post_data = {'shows': showList} return post_data
def fetch_trakt_episodes(self): """ Sets episodes to wanted that are in trakt watchlist """ logger.log(u"Retrieving episodes to sync with Trakt episode's watchlist", logger.DEBUG) if not self.episode_watchlist: logger.log('No episode found in your watchlist, aborting episode update', logger.DEBUG) return managed_show = [] indexer = int(app.TRAKT_DEFAULT_INDEXER) trakt_id = app.indexerApi(indexer).config['trakt_id'] for watchlist_item in self.episode_watchlist[trakt_id]: indexer_id = int(watchlist_item) show = self.episode_watchlist[trakt_id][watchlist_item] new_show = Show.find(app.showList, indexer_id) try: if not new_show: if indexer_id not in managed_show: self.add_show(indexer, indexer_id, show['title'], SKIPPED) managed_show.append(indexer_id) for season_item in show['seasons']: season = int(season_item) for episode_item in show['seasons'][season_item]['episodes']: self.todoWanted.append((indexer_id, season, int(episode_item))) else: if new_show.indexer == indexer: for season_item in show['seasons']: season = int(season_item) for episode_item in show['seasons'][season_item]['episodes']: setEpisodeToWanted(new_show, season, int(episode_item)) except TypeError: logger.log('Could not parse the output from trakt for {0} '.format(show['title']), logger.DEBUG)
def remove_episode_trakt_collection(self, filter_show=None): if app.TRAKT_SYNC_REMOVE and app.TRAKT_SYNC and app.USE_TRAKT: params = [] main_db_con = db.DBConnection() sql_selection = b'select tv_shows.indexer, tv_shows.startyear, showid, show_name, season, episode, tv_episodes.status,' \ b'tv_episodes.location from tv_episodes, tv_shows where tv_shows.indexer_id = tv_episodes.showid' if filter_show: sql_selection += b' AND tv_shows.indexer_id = ? AND tv_shows.indexer = ?' params = [filter_show.indexerid, filter_show.indexer] episodes = main_db_con.select(sql_selection, params) if episodes: trakt_data = [] for cur_episode in episodes: trakt_id = app.indexerApi(cur_episode[b'indexer']).config['trakt_id'] if self._check_list(trakt_id, cur_episode[b'showid'], cur_episode[b'season'], cur_episode[b'episode'], List='Collection'): if cur_episode[b'location'] == '': logger.log('Removing Episode {show} {ep} from collection'.format (show=cur_episode[b'show_name'], ep=episode_num(cur_episode[b'season'], cur_episode[b'episode'])), logger.DEBUG) trakt_data.append((cur_episode[b'showid'], cur_episode[b'indexer'], cur_episode[b'show_name'], cur_episode[b'startyear'], cur_episode[b'season'], cur_episode[b'episode'])) if trakt_data: try: data = self.trakt_bulk_data_generate(trakt_data) self.trakt_api.request('sync/collection/remove', data, method='POST') self._get_show_collection() except TraktException as e: logger.log('Could not connect to Trakt. Error: {0}'.format(ex(e)), logger.WARNING)
def run(self): ShowQueueItem.run(self) logger.log( u'{id}: Beginning update of {show}'.format(id=self.show.indexerid, show=self.show.name), logger.DEBUG) logger.log( u'{id}: Retrieving show info from {indexer}'.format( id=self.show.indexerid, indexer=app.indexerApi(self.show.indexer).name), logger.DEBUG) try: self.show.load_from_indexer() except app.indexer_error as e: logger.log( u'{id}: Unable to contact {indexer}. Aborting: {error_msg}'. format(id=self.show.indexerid, indexer=app.indexerApi(self.show.indexer).name, error_msg=ex(e)), logger.WARNING) return except app.indexer_attributenotfound as e: logger.log( u'{id}: Data retrieved from {indexer} was incomplete. Aborting: {error_msg}' .format(id=self.show.indexerid, indexer=app.indexerApi(self.show.indexer).name, error_msg=ex(e)), logger.WARNING) return logger.log( u'{id}: Retrieving show info from IMDb'.format( id=self.show.indexerid), logger.DEBUG) try: self.show.load_imdb_info() except imdb_exceptions.IMDbError as e: logger.log( u'{id}: Something wrong on IMDb api: {error_msg}'.format( id=self.show.indexerid, error_msg=ex(e)), logger.WARNING) except Exception as e: logger.log( u'{id}: Error loading IMDb info: {error_msg}'.format( id=self.show.indexerid, error_msg=ex(e)), logger.WARNING) # have to save show before reading episodes from db try: logger.log( u'{id}: Saving new IMDb show info to database'.format( id=self.show.indexerid), logger.DEBUG) self.show.save_to_db() except Exception as e: logger.log( u"{id}: Error saving new IMDb show info to database: {error_msg}" .format(id=self.show.indexerid, error_msg=ex(e)), logger.WARNING) logger.log(traceback.format_exc(), logger.ERROR) # get episode list from DB DBEpList = self.show.load_episodes_from_db() # get episode list from TVDB try: IndexerEpList = self.show.load_episodes_from_indexer() except app.indexer_exception as e: logger.log( u'{id}: Unable to get info from {indexer}. The show info will not be refreshed. ' u'Error: {error_msg}'.format(id=self.show.indexerid, indexer=app.indexerApi( self.show.indexer).name, error_msg=ex(e)), logger.WARNING) IndexerEpList = None if IndexerEpList is None: logger.log( u'{id}: No data returned from {indexer}. Unable to update this show' .format(id=self.show.indexerid, indexer=app.indexerApi(self.show.indexer).name), logger.WARNING) else: # for each ep we found on the Indexer delete it from the DB list for cur_season in IndexerEpList: for cur_episode in IndexerEpList[cur_season]: if cur_season in DBEpList and cur_episode in DBEpList[ cur_season]: del DBEpList[cur_season][cur_episode] else: # Create the episode objectes for episodes that are not going to be deleted curEp = self.show.get_episode(cur_season, cur_episode) # remaining episodes in the DB list are not on the indexer, just delete them from the DB for cur_season in DBEpList: for cur_episode in DBEpList[cur_season]: logger.log( u'{id}: Permanently deleting episode {show} {ep} from the database' .format(id=self.show.indexerid, show=self.show.name, ep=episode_num(cur_season, cur_episode)), logger.DEBUG) # Create the ep object only because Im going to delete it curEp = self.show.get_episode(cur_season, cur_episode) try: curEp.delete_episode() except EpisodeDeletedException: pass # Save only after all changes were applied try: logger.log( u'{id}: Saving all updated show info to database'.format( id=self.show.indexerid), logger.DEBUG) self.show.save_to_db() except Exception as e: logger.log( u'{id}: Error saving all updated show info to database: {error_msg}' .format(id=self.show.indexerid, error_msg=ex(e)), logger.WARNING) logger.log(traceback.format_exc(), logger.ERROR) logger.log( u'{id}: Finished update of {show}'.format(id=self.show.indexerid, show=self.show.name), logger.DEBUG) # Refresh show needs to be forced since current execution locks the queue app.showQueueScheduler.action.refreshShow(self.show, True) 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 """ show_id = show_obj.indexerid 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 l_indexer_api_params = app.indexerApi( show_obj.indexer).api_params.copy() l_indexer_api_params['actors'] = True if indexer_lang and not indexer_lang == app.INDEXER_DEFAULT_LANGUAGE: l_indexer_api_params['language'] = indexer_lang if show_obj.dvdorder != 0: l_indexer_api_params['dvdorder'] = True t = app.indexerApi(show_obj.indexer).indexer(**l_indexer_api_params) tv_node = etree.Element('Series') try: my_show = t[int(show_id)] except app.indexer_shownotfound: logger.log( u'Unable to find {indexer} show {id}, skipping it'.format( indexer=app.indexerApi(show_obj.indexer).name, id=show_id), logger.ERROR) raise except app.indexer_error: logger.log( u'{indexer} is down, can\'t use its data to add this show'. format(indexer=app.indexerApi(show_obj.indexer).name), logger.ERROR) raise # check for title and id if not (getattr(my_show, 'seriesname', None) and getattr(my_show, 'id', None)): logger.log( u'Incomplete info for {indexer} show {id}, skipping it'.format( indexer=app.indexerApi(show_obj.indexer).name, id=show_id), logger.ERROR) return False if getattr(my_show, 'id', None): indexerid = etree.SubElement(tv_node, 'id') indexerid.text = str(my_show['id']) if getattr(my_show, 'seriesname', None): series_name = etree.SubElement(tv_node, 'SeriesName') series_name.text = my_show['seriesname'] if getattr(my_show, 'status', None): status = etree.SubElement(tv_node, 'Status') status.text = my_show['status'] if getattr(my_show, 'network', None): network = etree.SubElement(tv_node, 'Network') network.text = my_show['network'] if getattr(my_show, 'airs_time', None): airs_time = etree.SubElement(tv_node, 'Airs_Time') airs_time.text = my_show['airs_time'] if getattr(my_show, 'airs_dayofweek', None): airs_day_of_week = etree.SubElement(tv_node, 'Airs_DayOfWeek') airs_day_of_week.text = my_show['airs_dayofweek'] first_aired = etree.SubElement(tv_node, 'FirstAired') if getattr(my_show, 'firstaired', None): first_aired.text = my_show['firstaired'] if getattr(my_show, 'contentrating', None): content_rating = etree.SubElement(tv_node, 'ContentRating') content_rating.text = my_show['contentrating'] mpaa = etree.SubElement(tv_node, 'MPAARating') mpaa.text = my_show['contentrating'] certification = etree.SubElement(tv_node, 'certification') certification.text = my_show['contentrating'] metadata_type = etree.SubElement(tv_node, 'Type') metadata_type.text = 'Series' if getattr(my_show, 'overview', None): overview = etree.SubElement(tv_node, 'Overview') overview.text = my_show['overview'] if getattr(my_show, 'firstaired', None): premiere_date = etree.SubElement(tv_node, 'PremiereDate') premiere_date.text = my_show['firstaired'] if getattr(my_show, 'rating', None): rating = etree.SubElement(tv_node, 'Rating') rating.text = my_show['rating'] if getattr(my_show, 'firstaired', None): try: year_text = str( datetime.datetime.strptime(my_show['firstaired'], dateFormat).year) if year_text: production_year = etree.SubElement(tv_node, 'ProductionYear') production_year.text = year_text except Exception: pass if getattr(my_show, 'runtime', None): running_time = etree.SubElement(tv_node, 'RunningTime') running_time.text = my_show['runtime'] runtime = etree.SubElement(tv_node, 'Runtime') runtime.text = my_show['runtime'] if getattr(my_show, 'imdb_id', None): imdb_id = etree.SubElement(tv_node, 'IMDB_ID') imdb_id.text = my_show['imdb_id'] imdb_id = etree.SubElement(tv_node, 'IMDB') imdb_id.text = my_show['imdb_id'] imdb_id = etree.SubElement(tv_node, 'IMDbId') imdb_id.text = my_show['imdb_id'] if getattr(my_show, 'zap2it_id', None): zap2it_id = etree.SubElement(tv_node, 'Zap2ItId') zap2it_id.text = my_show['zap2it_id'] if getattr(my_show, 'genre', None) and isinstance( my_show['genre'], string_types): genres = etree.SubElement(tv_node, 'Genres') for genre in my_show['genre'].split('|'): if genre.strip(): cur_genre = etree.SubElement(genres, 'Genre') cur_genre.text = genre.strip() genre = etree.SubElement(tv_node, 'Genre') genre.text = '|'.join( [x.strip() for x in my_show['genre'].split('|') if x.strip()]) if getattr(my_show, 'network', None): studios = etree.SubElement(tv_node, 'Studios') studio = etree.SubElement(studios, 'Studio') studio.text = my_show['network'] if getattr(my_show, '_actors', None): persons = etree.SubElement(tv_node, 'Persons') for actor in my_show['_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() 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.related_episodes persons_dict = {'Director': [], 'GuestStar': [], 'Writer': []} indexer_lang = ep_obj.show.lang l_indexer_api_params = app.indexerApi( ep_obj.show.indexer).api_params.copy() l_indexer_api_params[b'actors'] = True if indexer_lang and not indexer_lang == app.INDEXER_DEFAULT_LANGUAGE: l_indexer_api_params[b'language'] = indexer_lang if ep_obj.show.dvdorder != 0: l_indexer_api_params[b'dvdorder'] = True try: t = app.indexerApi( ep_obj.show.indexer).indexer(**l_indexer_api_params) my_show = t[ep_obj.show.indexerid] except app.indexer_shownotfound as e: raise ShowNotFoundException(e.message) except app.indexer_error: logger.log( u'Unable to connect to {indexer} while creating meta files - skipping it.' .format(indexer=app.indexerApi(ep_obj.show.indexer).name), logger.WARNING) return root_node = etree.Element('Item') # write an MediaBrowser XML containing info for all matching episodes for ep_to_write in eps_to_write: try: my_ep = my_show[ep_to_write.season][ep_to_write.episode] except (app.indexer_episodenotfound, app.indexer_seasonnotfound): logger.log( u'Unable to find episode {ep_num} on {indexer}... ' u'has it been removed? Should I delete from db?'.format( ep_num=episode_num(ep_to_write.season, ep_to_write.episode), indexer=app.indexerApi(ep_obj.show.indexer).name)) return None if ep_to_write == ep_obj: # root (or single) episode # default to today's date for specials if firstaired is not set if ep_to_write.season == 0 and not getattr( my_ep, 'firstaired', None): my_ep['firstaired'] = str(datetime.date.fromordinal(1)) if not (getattr(my_ep, 'episodename', None) and getattr(my_ep, 'firstaired', None)): return None episode = root_node if ep_to_write.name: episode_name = etree.SubElement(episode, 'EpisodeName') episode_name.text = ep_to_write.name episode_number = etree.SubElement(episode, 'EpisodeNumber') episode_number.text = str(ep_obj.episode) if ep_obj.related_episodes: episode_number_end = etree.SubElement( episode, 'EpisodeNumberEnd') episode_number_end.text = str(ep_to_write.episode) season_number = etree.SubElement(episode, 'SeasonNumber') season_number.text = str(ep_to_write.season) if not ep_obj.related_episodes and getattr( my_ep, 'absolute_number', None): absolute_number = etree.SubElement(episode, 'absolute_number') absolute_number.text = str(my_ep['absolute_number']) if ep_to_write.airdate != datetime.date.fromordinal(1): first_aired = etree.SubElement(episode, 'FirstAired') first_aired.text = str(ep_to_write.airdate) metadata_type = etree.SubElement(episode, 'Type') metadata_type.text = 'Episode' if ep_to_write.description: overview = etree.SubElement(episode, 'Overview') overview.text = ep_to_write.description if not ep_obj.related_episodes: if getattr(my_ep, 'rating', None): rating = etree.SubElement(episode, 'Rating') rating.text = my_ep['rating'] if getattr(my_show, 'imdb_id', None): IMDB_ID = etree.SubElement(episode, 'IMDB_ID') IMDB_ID.text = my_show['imdb_id'] IMDB = etree.SubElement(episode, 'IMDB') IMDB.text = my_show['imdb_id'] IMDbId = etree.SubElement(episode, 'IMDbId') IMDbId.text = my_show['imdb_id'] indexer_id = etree.SubElement(episode, 'id') indexer_id.text = str(ep_to_write.indexerid) persons = etree.SubElement(episode, 'Persons') if getattr(my_show, '_actors', None): for actor in my_show['_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 = my_ep['language'] except Exception: language.text = app.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 episode_number_end.text = str(ep_to_write.episode) if ep_to_write.name: if not episode_name.text: episode_name.text = ep_to_write.name else: episode_name.text = u', '.join( [episode_name.text, ep_to_write.name]) if ep_to_write.description: if not overview.text: overview.text = ep_to_write.description else: overview.text = u'\r'.join( [overview.text, ep_to_write.description]) # collect all directors, guest stars and writers if getattr(my_ep, 'director', None): persons_dict['Director'] += [ x.strip() for x in my_ep['director'].split('|') if x.strip() ] if getattr(my_ep, 'gueststars', None): persons_dict['GuestStar'] += [ x.strip() for x in my_ep['gueststars'].split('|') if x.strip() ] if getattr(my_ep, 'writer', None): persons_dict['Writer'] += [ x.strip() for x in my_ep['writer'].split('|') if x.strip() ] # fill in Persons section with collected directors, guest starts and writers for person_type, names in iteritems(persons_dict): # remove doubles names = list(set(names)) for cur_name in names: person = etree.SubElement(persons, 'Person') cur_person_name = etree.SubElement(person, 'Name') cur_person_name.text = cur_name cur_person_type = etree.SubElement(person, 'Type') cur_person_type.text = person_type # Make it purdy helpers.indentXML(root_node) data = etree.ElementTree(root_node) return data
def getIndexerLanguages(): result = app.indexerApi().config['valid_languages'] return json.dumps({'results': result})
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.related_episodes indexer_lang = ep_obj.show.lang l_indexer_api_params = app.indexerApi( ep_obj.show.indexer).api_params.copy() l_indexer_api_params[b'actors'] = True if indexer_lang and not indexer_lang == app.INDEXER_DEFAULT_LANGUAGE: l_indexer_api_params[b'language'] = indexer_lang if ep_obj.show.dvdorder != 0: l_indexer_api_params[b'dvdorder'] = True try: t = app.indexerApi( ep_obj.show.indexer).indexer(**l_indexer_api_params) my_show = t[ep_obj.show.indexerid] except app.indexer_shownotfound as e: raise ShowNotFoundException(e.message) except app.indexer_error: logger.log( u'Unable to connect to {indexer} while creating meta files - skipping it.' .format(indexer=app.indexerApi(ep_obj.show.indexer).name), logger.WARNING) return False root_node = etree.Element('details') # write an WDTV XML containing info for all matching episodes for ep_to_write in eps_to_write: try: my_ep = my_show[ep_to_write.season][ep_to_write.episode] except (app.indexer_episodenotfound, app.indexer_seasonnotfound): logger.log( u'Unable to find episode {ep_num} on {indexer}... ' u'has it been removed? Should I delete from db?'.format( ep_num=ep_num(ep_to_write.season, ep_to_write.episode), indexer=app.indexerApi(ep_obj.show.indexer).name)) return None if ep_obj.season == 0 and not getattr(my_ep, 'firstaired', None): my_ep['firstaired'] = str(datetime.date.fromordinal(1)) if not (getattr(my_ep, 'episodename', None) and getattr(my_ep, 'firstaired', None)): return None if len(eps_to_write) > 1: episode = etree.SubElement(root_node, 'details') else: episode = root_node # TODO: get right EpisodeID episode_id = etree.SubElement(episode, 'id') episode_id.text = str(ep_to_write.indexerid) title = etree.SubElement(episode, 'title') title.text = ep_obj.pretty_name() if getattr(my_show, 'seriesname', None): series_name = etree.SubElement(episode, 'series_name') series_name.text = my_show['seriesname'] if ep_to_write.name: episode_name = etree.SubElement(episode, 'episode_name') episode_name.text = ep_to_write.name season_number = etree.SubElement(episode, 'season_number') season_number.text = str(ep_to_write.season) episode_num = etree.SubElement(episode, 'episode_number') episode_num.text = str(ep_to_write.episode) first_aired = etree.SubElement(episode, 'firstaired') if ep_to_write.airdate != datetime.date.fromordinal(1): first_aired.text = str(ep_to_write.airdate) if getattr(my_show, 'firstaired', None): try: year_text = str( datetime.datetime.strptime(my_show['firstaired'], dateFormat).year) if year_text: year = etree.SubElement(episode, 'year') year.text = year_text except Exception: pass if ep_to_write.season != 0 and getattr(my_show, 'runtime', None): runtime = etree.SubElement(episode, 'runtime') runtime.text = my_show['runtime'] if getattr(my_show, 'genre', None): genre = etree.SubElement(episode, 'genre') genre.text = ' / '.join([ x.strip() for x in my_show['genre'].split('|') if x.strip() ]) if getattr(my_ep, 'director', None): director = etree.SubElement(episode, 'director') director.text = my_ep['director'] if getattr(my_show, '_actors', None): for actor in my_show['_actors']: if not ('name' in actor and actor['name'].strip()): continue cur_actor = etree.SubElement(episode, 'actor') cur_actor_name = etree.SubElement(cur_actor, 'name') cur_actor_name.text = actor['name'] if 'role' in actor and actor['role'].strip(): cur_actor_role = etree.SubElement(cur_actor, 'role') cur_actor_role.text = actor['role'].strip() if ep_to_write.description: overview = etree.SubElement(episode, 'overview') overview.text = ep_to_write.description # Make it purdy helpers.indentXML(root_node) data = etree.ElementTree(root_node) return data
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 = app.indexerApi(self.indexer).api_params.copy() if self.lang: lINDEXER_API_PARMS['language'] = self.lang logger.log(u"" + str(app.indexerApi(self.indexer).name) + ": " + repr(lINDEXER_API_PARMS)) t = app.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, app.indexerApi(self.indexer).name), logger.ERROR) ui.notifications.error( "Unable to add show", "Show in " + self.showDir + " has no name on " + str(app.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(app.indexerApi(self.indexer).name) + " but contains no season/episode data.") ui.notifications.error( "Unable to add show", "Show " + str(s['seriesname']) + " is on " + str(app.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, app.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, api.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, app.indexerApi( self.indexer).name, self.indexer_id)) if app.USE_TRAKT: trakt_id = app.indexerApi(self.indexer).config['trakt_id'] trakt_api = TraktApi(app.SSL_VERIFY, app.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 try: trakt_api.traktRequest("sync/watchlist/remove", data, method='POST') except TraktException as e: logger.log( "Could not remove show '{0}' from watchlist. Error: {1}" .format(title, e), logger.WARNING) self._finishEarly() return try: newShow = TVShow(self.indexer, self.indexer_id, self.lang) newShow.load_from_indexer() self.show = newShow # set up initial values self.show.location = self.showDir self.show.subtitles = self.subtitles if self.subtitles is not None else app.SUBTITLES_DEFAULT self.show.quality = self.quality if self.quality else app.QUALITY_DEFAULT self.show.flatten_folders = self.flatten_folders if self.flatten_folders is not None else app.FLATTEN_FOLDERS_DEFAULT self.show.anime = self.anime if self.anime is not None else app.ANIME_DEFAULT self.show.scene = self.scene if self.scene is not None else app.SCENE_DEFAULT self.show.paused = self.paused if self.paused is not None else False # set up default new/missing episode status logger.log( u"Setting all episodes to the specified default status: " + str(self.show.default_ep_status)) self.show.default_ep_status = self.default_status if self.show.anime: self.show.release_groups = BlackAndWhiteList( self.show.indexerid) if self.blacklist: self.show.release_groups.set_black_keywords(self.blacklist) if self.whitelist: self.show.release_groups.set_white_keywords(self.whitelist) # # be smartish about this # if self.show.genre and "talk show" in self.show.genre.lower(): # self.show.air_by_date = 1 # if self.show.genre and "documentary" in self.show.genre.lower(): # self.show.air_by_date = 0 # if self.show.classification and "sports" in self.show.classification.lower(): # self.show.sports = 1 except app.indexer_exception as e: logger.log( u"Unable to add show due to an error with " + app.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 " + app.indexerApi(self.indexer).name + "") else: ui.notifications.error( "Unable to add show due to an error with " + app.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.load_imdb_info() 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.save_to_db() 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 app.showList.append(self.show) try: self.show.load_episodes_from_indexer() except Exception as e: logger.log( u"Error with " + app.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.load_episodes_from_dir() 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" ) app.backlogSearchScheduler.action.searchBacklog([self.show]) self.show.write_metadata() self.show.update_metadata() self.show.populate_cache() self.show.flush_episodes() if app.USE_TRAKT: # if there are specific episodes that need to be added by trakt app.traktCheckerScheduler.action.manage_new_show(self.show) # add show to trakt.tv library if app.TRAKT_SYNC: app.traktCheckerScheduler.action.add_show_trakt_library( self.show) if app.TRAKT_SYNC_WATCHLIST: logger.log(u"update watchlist") notifiers.trakt_notifier.update_watchlist(show_obj=self.show) # Load XEM data to DB for show scene_numbering.xem_refresh(self.show.indexerid, self.show.indexer, force=True) # check if show has XEM mapping so we can determin if searches should go by scene numbering or indexer numbering. if not self.scene and scene_numbering.get_xem_numbering_for_show( self.show.indexerid, self.show.indexer): self.show.scene = 1 # After initial add, set to default_status_after. self.show.default_ep_status = self.default_status_after self.finish()
def _ep_data(self, ep_obj): """ Creates an elementTree XML structure for an KODI-style episode.nfo and returns the resulting data object. show_obj: a TVEpisode instance to create the NFO for """ eps_to_write = [ep_obj] + ep_obj.related_episodes indexer_lang = ep_obj.show.lang # There's gotta be a better way of doing this but we don't wanna # change the language value elsewhere l_indexer_api_params = app.indexerApi( ep_obj.show.indexer).api_params.copy() l_indexer_api_params[b'actors'] = True if indexer_lang and not indexer_lang == app.INDEXER_DEFAULT_LANGUAGE: l_indexer_api_params[b'language'] = indexer_lang if ep_obj.show.dvdorder != 0: l_indexer_api_params[b'dvdorder'] = True try: t = app.indexerApi( ep_obj.show.indexer).indexer(**l_indexer_api_params) my_show = t[ep_obj.show.indexerid] except app.indexer_shownotfound as e: raise ShowNotFoundException(e.message) except app.indexer_error: logger.log( u'Unable to connect to {indexer} while creating meta files - skipping it.' .format(indexer=app.indexerApi(ep_obj.show.indexer).name), logger.WARNING) return if len(eps_to_write) > 1: root_node = etree.Element('kodimultiepisode') else: root_node = etree.Element('episodedetails') # write an NFO containing info for all matching episodes for ep_to_write in eps_to_write: try: my_ep = my_show[ep_to_write.season][ep_to_write.episode] except (app.indexer_episodenotfound, app.indexer_seasonnotfound): logger.log( u'Unable to find episode {ep_num} on {indexer}... ' u'has it been removed? Should I delete from db?'.format( ep_num=episode_num(ep_to_write.season, ep_to_write.episode), indexer=app.indexerApi(ep_obj.show.indexer).name)) return None if not getattr(my_ep, 'firstaired', None): my_ep['firstaired'] = str(datetime.date.fromordinal(1)) if not getattr(my_ep, 'episodename', None): logger.log(u'Not generating nfo because the ep has no title', logger.DEBUG) return None logger.log( u'Creating metadata for episode {ep_num}'.format( ep_num=episode_num(ep_obj.season, ep_obj.episode)), logger.DEBUG) if len(eps_to_write) > 1: episode = etree.SubElement(root_node, 'episodedetails') else: episode = root_node if getattr(my_ep, 'episodename', None): title = etree.SubElement(episode, 'title') title.text = my_ep['episodename'] if getattr(my_show, 'seriesname', None): showtitle = etree.SubElement(episode, 'showtitle') showtitle.text = my_show['seriesname'] season = etree.SubElement(episode, 'season') season.text = str(ep_to_write.season) episodenum = etree.SubElement(episode, 'episode') episodenum.text = str(ep_to_write.episode) uniqueid = etree.SubElement(episode, 'uniqueid') uniqueid.text = str(ep_to_write.indexerid) if ep_to_write.airdate != datetime.date.fromordinal(1): aired = etree.SubElement(episode, 'aired') aired.text = str(ep_to_write.airdate) if getattr(my_ep, 'overview', None): plot = etree.SubElement(episode, 'plot') plot.text = my_ep['overview'] if ep_to_write.season and getattr(my_show, 'runtime', None): runtime = etree.SubElement(episode, 'runtime') runtime.text = my_show['runtime'] if getattr(my_ep, 'airsbefore_season', None): displayseason = etree.SubElement(episode, 'displayseason') displayseason.text = my_ep['airsbefore_season'] if getattr(my_ep, 'airsbefore_episode', None): displayepisode = etree.SubElement(episode, 'displayepisode') displayepisode.text = my_ep['airsbefore_episode'] if getattr(my_ep, 'filename', None): thumb = etree.SubElement(episode, 'thumb') thumb.text = my_ep['filename'].strip() # watched = etree.SubElement(episode, 'watched') # watched.text = 'false' if getattr(my_ep, 'rating', None): rating = etree.SubElement(episode, 'rating') rating.text = my_ep['rating'] if getattr(my_ep, 'writer', None) and isinstance( my_ep['writer'], string_types): for writer in self._split_info(my_ep['writer']): cur_writer = etree.SubElement(episode, 'credits') cur_writer.text = writer if getattr(my_ep, 'director', None) and isinstance( my_ep['director'], string_types): for director in self._split_info(my_ep['director']): cur_director = etree.SubElement(episode, 'director') cur_director.text = director if getattr(my_ep, 'gueststars', None) and isinstance( my_ep['gueststars'], string_types): for actor in self._split_info(my_ep['gueststars']): cur_actor = etree.SubElement(episode, 'actor') cur_actor_name = etree.SubElement(cur_actor, 'name') cur_actor_name.text = actor if getattr(my_show, '_actors', None): for actor in my_show['_actors']: cur_actor = etree.SubElement(episode, 'actor') if 'name' in actor and actor['name'].strip(): cur_actor_name = etree.SubElement(cur_actor, 'name') cur_actor_name.text = actor['name'].strip() else: continue if 'role' in actor and actor['role'].strip(): cur_actor_role = etree.SubElement(cur_actor, 'role') cur_actor_role.text = actor['role'].strip() if 'image' in actor and actor['image'].strip(): cur_actor_thumb = etree.SubElement(cur_actor, 'thumb') cur_actor_thumb.text = actor['image'].strip() # Make it purdy helpers.indentXML(root_node) data = etree.ElementTree(root_node) return data
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, app.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 cl: main_db_con = db.DBConnection() main_db_con.mass_action(cl)
def xem_refresh(indexer_id, indexer, force=False): """ Refresh data from xem for a tv show :param indexer_id: int """ if not indexer_id or indexer_id < 1: return indexer_id = int(indexer_id) indexer = int(indexer) MAX_REFRESH_AGE_SECS = 86400 # 1 day main_db_con = db.DBConnection() rows = main_db_con.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, app.indexerApi(indexer).name,), logger.DEBUG) # mark refreshed main_db_con.upsert( "xem_refresh", {'indexer': indexer, 'last_refreshed': int(time.mktime(datetime.datetime.today().timetuple()))}, {'indexer_id': indexer_id} ) try: # XEM MAP URL url = "http://thexem.de/map/havemap?origin=%s" % app.indexerApi(indexer).config['xem_origin'] parsedJSON = app.helpers.getURL(url, session=xem_session, returns='json') if not parsedJSON or 'result' not in parsedJSON or 'success' not in parsedJSON['result'] or 'data' not in parsedJSON or str(indexer_id) not in parsedJSON['data']: return # XEM API URL url = "http://thexem.de/map/all?id=%s&origin=%s&destination=scene" % (indexer_id, app.indexerApi(indexer).config['xem_origin']) parsedJSON = app.helpers.getURL(url, session=xem_session, returns='json') if not parsedJSON or 'result' not in parsedJSON or 'success' not in parsedJSON['result']: logger.log(u'No XEM data for show "%s on %s"' % (indexer_id, app.indexerApi(indexer).name,), logger.INFO) return 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[app.indexerApi(indexer).config['xem_origin']]['season'], entry[app.indexerApi(indexer).config['xem_origin']]['episode']] ]) cl.append([ "UPDATE tv_episodes SET absolute_number = ? WHERE showid = ? AND season = ? AND episode = ? AND absolute_number = 0", [entry[app.indexerApi(indexer).config['xem_origin']]['absolute'], indexer_id, entry[app.indexerApi(indexer).config['xem_origin']]['season'], entry[app.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[app.indexerApi(indexer).config['xem_origin']]['season'], entry[app.indexerApi(indexer).config['xem_origin']]['episode']] ]) if cl: main_db_con = db.DBConnection() main_db_con.mass_action(cl) except Exception as e: logger.log( u"Exception while refreshing XEM data for show " + str(indexer_id) + " on " + app.indexerApi( indexer).name + ": " + ex(e), logger.WARNING) logger.log(traceback.format_exc(), logger.DEBUG)
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.related_episodes indexer_lang = ep_obj.show.lang try: l_indexer_api_params = app.indexerApi( ep_obj.show.indexer).api_params.copy() l_indexer_api_params['actors'] = True if indexer_lang and not indexer_lang == app.INDEXER_DEFAULT_LANGUAGE: l_indexer_api_params['language'] = indexer_lang if ep_obj.show.dvdorder != 0: l_indexer_api_params['dvdorder'] = True t = app.indexerApi( ep_obj.show.indexer).indexer(**l_indexer_api_params) my_show = t[ep_obj.show.indexerid] except app.indexer_shownotfound as e: raise ShowNotFoundException(e.message) except app.indexer_error: logger.log( u'Unable to connect to {indexer} while creating meta files - skipping it.' .format(indexer=app.indexerApi(ep_obj.show.indexer).name), logger.WARNING) return False for ep_to_write in eps_to_write: try: my_ep = my_show[ep_to_write.season][ep_to_write.episode] except (app.indexer_episodenotfound, app.indexer_seasonnotfound): logger.log( u'Unable to find episode {ep_num} on {indexer}... ' u'has it been removed? Should I delete from db?'.format( ep_num=episode_num(ep_to_write.season, ep_to_write.episode), indexer=app.indexerApi(ep_obj.show.indexer).name)) return None if ep_obj.season == 0 and not getattr(my_ep, 'firstaired', None): my_ep['firstaired'] = str(datetime.date.fromordinal(1)) if not (getattr(my_ep, 'episodename', None) and getattr(my_ep, 'firstaired', None)): return None if getattr(my_show, 'seriesname', None): data += ('title : {title}\n'.format( title=my_show['seriesname'])) data += ('seriesTitle : {title}\n'.format( title=my_show['seriesname'])) data += ('episodeTitle : {title}\n'.format( title=ep_to_write._format_pattern('%Sx%0E %EN'))) # This should be entered for episodic shows and omitted for movies. The standard tivo format is to enter # the season number followed by the episode number for that season. For example, enter 201 for season 2 # episode 01. # This only shows up if you go into the Details from the Program screen. # This seems to disappear once the video is transferred to TiVo. # NOTE: May not be correct format, missing season, but based on description from wiki leaving as is. data += ('episodeNumber : {ep_num}\n'.format( ep_num=ep_to_write.episode)) # Must be entered as true or false. If true, the year from originalAirDate will be shown in parentheses # after the episode's title and before the description on the Program screen. # FIXME: Hardcode isEpisode to true for now, not sure how to handle movies data += 'isEpisode : true\n' # Write the synopsis of the video here # Micrsoft Word's smartquotes can die in a fire. sanitized_description = ep_to_write.description # Replace double curly quotes sanitized_description = sanitized_description.replace( u'\u201c', '\'').replace(u'\u201d', '\'') # Replace single curly quotes sanitized_description = sanitized_description.replace( u'\u2018', '\'').replace(u'\u2019', '\'').replace(u'\u02BC', '\'') data += ('description : {desc}\n'.format( desc=sanitized_description)) # Usually starts with 'SH' and followed by 6-8 digits. # TiVo uses zap2it for their data, so the series id is the zap2it_id. if getattr(my_show, 'zap2it_id', None): data += ('seriesId : {zap2it}\n'.format( zap2it=my_show['zap2it_id'])) # This is the call sign of the channel the episode was recorded from. if getattr(my_show, 'network', None): data += ('callsign : {network}\n'.format( network=my_show['network'])) # This must be entered as yyyy-mm-ddThh:mm:ssZ (the t is capitalized and never changes, the Z is also # capitalized and never changes). This is the original air date of the episode. # NOTE: Hard coded the time to T00:00:00Z as we really don't know when during the day the first run happened. if ep_to_write.airdate != datetime.date.fromordinal(1): data += ('originalAirDate : {airdate}T00:00:00Z\n'.format( airdate=ep_to_write.airdate)) # This shows up at the beginning of the description on the Program screen and on the Details screen. if getattr(my_show, '_actors', None): for actor in my_show['_actors']: if 'name' in actor and actor['name'].strip(): data += ('vActor : {actor}\n'.format( actor=actor['name'].strip())) # This is shown on both the Program screen and the Details screen. if getattr(my_ep, 'rating', None): try: rating = float(my_ep['rating']) except ValueError: rating = 0.0 # convert 10 to 4 star rating. 4 * rating / 10 # only whole numbers or half numbers work. multiply by 2, round, divide by 2.0 rating = round(8 * rating / 10) / 2.0 data += ('starRating : {rating}\n'.format(rating=rating)) # This is shown on both the Program screen and the Details screen. # It uses the standard TV rating system of: TV-Y7, TV-Y, TV-G, TV-PG, TV-14, TV-MA and TV-NR. if getattr(my_show, 'contentrating', None): data += ('tvRating : {rating}\n'.format( rating=my_show['contentrating'])) # This field can be repeated as many times as necessary or omitted completely. if ep_obj.show.genre: for genre in ep_obj.show.genre.split('|'): if genre: data += ('vProgramGenre : {genre}\n'.format( genre=genre)) # NOTE: The following are metadata keywords are not used # displayMajorNumber # showingBits # displayMinorNumber # colorCode # vSeriesGenre # vGuestStar, vDirector, vExecProducer, vProducer, vWriter, vHost, vChoreographer # partCount # partIndex return data
def _show_data(self, show_obj): """ Creates an elementTree XML structure for an KODI-style tvshow.nfo and returns the resulting data object. show_obj: a TVShow instance to create the NFO for """ show_id = show_obj.indexerid indexer_lang = show_obj.lang l_indexer_api_params = app.indexerApi( show_obj.indexer).api_params.copy() l_indexer_api_params['actors'] = True if indexer_lang and not indexer_lang == app.INDEXER_DEFAULT_LANGUAGE: l_indexer_api_params['language'] = indexer_lang if show_obj.dvdorder != 0: l_indexer_api_params['dvdorder'] = True t = app.indexerApi(show_obj.indexer).indexer(**l_indexer_api_params) tv_node = etree.Element('tvshow') try: my_show = t[int(show_id)] except app.indexer_shownotfound: logger.log( u'Unable to find {indexer} show {id}, skipping it'.format( indexer=app.indexerApi(show_obj.indexer).name, id=show_id), logger.ERROR) raise except app.indexer_error: logger.log( u'{indexer} is down, can\'t use its data to add this show'. format(indexer=app.indexerApi(show_obj.indexer).name), logger.ERROR) raise # check for title and id if not (getattr(my_show, 'seriesname', None) and getattr(my_show, 'id', None)): logger.log( u'Incomplete info for {indexer} show {id}, skipping it'.format( indexer=app.indexerApi(show_obj.indexer).name, id=show_id), logger.ERROR) return False title = etree.SubElement(tv_node, 'title') title.text = my_show['seriesname'] if getattr(my_show, 'rating', None): rating = etree.SubElement(tv_node, 'rating') rating.text = my_show['rating'] if getattr(my_show, 'firstaired', None): try: year_text = str( datetime.datetime.strptime(my_show['firstaired'], dateFormat).year) if year_text: year = etree.SubElement(tv_node, 'year') year.text = year_text except Exception: pass if getattr(my_show, 'overview', None): plot = etree.SubElement(tv_node, 'plot') plot.text = my_show['overview'] if getattr(my_show, 'id', None): episode_guide = etree.SubElement(tv_node, 'episodeguide') episode_guide_url = etree.SubElement(episode_guide, 'url') episode_guide_url.text = '{url}{id}/all/en.zip'.format( url=app.indexerApi(show_obj.indexer).config['base_url'], id=my_show['id']) if getattr(my_show, 'contentrating', None): mpaa = etree.SubElement(tv_node, 'mpaa') mpaa.text = my_show['contentrating'] if getattr(my_show, 'id', None): indexer_id = etree.SubElement(tv_node, 'id') indexer_id.text = str(my_show['id']) if getattr(my_show, 'genre', None) and isinstance( my_show['genre'], string_types): for genre in self._split_info(my_show['genre']): cur_genre = etree.SubElement(tv_node, 'genre') cur_genre.text = genre if 'country_codes' in show_obj.imdb_info: for country in self._split_info( show_obj.imdb_info['country_codes']): try: cur_country_name = Country(country.upper()).name.title() except Exception: continue cur_country = etree.SubElement(tv_node, 'country') cur_country.text = cur_country_name if getattr(my_show, 'firstaired', None): premiered = etree.SubElement(tv_node, 'premiered') premiered.text = my_show['firstaired'] if getattr(my_show, 'network', None): studio = etree.SubElement(tv_node, 'studio') studio.text = my_show['network'].strip() if getattr(my_show, 'writer', None) and isinstance( my_show['writer'], string_types): for writer in self._split_info(my_show['writer']): cur_writer = etree.SubElement(tv_node, 'credits') cur_writer.text = writer if getattr(my_show, 'director', None) and isinstance( my_show['director'], string_types): for director in self._split_info(my_show['director']): cur_director = etree.SubElement(tv_node, 'director') cur_director.text = director if getattr(my_show, '_actors', None): for actor in my_show['_actors']: cur_actor = etree.SubElement(tv_node, 'actor') if 'name' in actor and actor['name'].strip(): cur_actor_name = etree.SubElement(cur_actor, 'name') cur_actor_name.text = actor['name'].strip() else: continue if 'role' in actor and actor['role'].strip(): cur_actor_role = etree.SubElement(cur_actor, 'role') cur_actor_role.text = actor['role'].strip() if 'image' in actor and actor['image'].strip(): cur_actor_thumb = etree.SubElement(cur_actor, 'thumb') cur_actor_thumb.text = actor['image'].strip() # Make it purdy helpers.indentXML(tv_node) data = etree.ElementTree(tv_node) return data
def run(self, force=False): self.amActive = True bad_indexer = [INDEXER_TVRAGE] update_datetime = datetime.datetime.now() update_date = update_datetime.date() # update_timestamp = calendar.timegm(update_datetime.timetuple()) update_timestamp = time.mktime(update_datetime.timetuple()) cache_db_con = db.DBConnection('cache.db') result = cache_db_con.select( "SELECT `time` FROM lastUpdate WHERE provider = 'theTVDB'") if result: last_update = int(result[0]['time']) else: last_update = update_timestamp - 86400 cache_db_con.action( "INSERT INTO lastUpdate (provider,`time`) VALUES (?, ?)", ['theTVDB', last_update]) # refresh network timezones network_timezones.update_network_dict() # sure, why not? if app.USE_FAILED_DOWNLOADS: failed_history.trimHistory() update_delta = update_timestamp - last_update if update_delta >= 691200: # 8 days ( 7 days + 1 day of buffer time) update_file = 'updates_month.xml' elif update_delta >= 90000: # 25 hours ( 1 day + 1 hour of buffer time) update_file = 'updates_week.xml' else: update_file = 'updates_day.xml' # url = 'http://thetvdb.com/api/Updates.php?type=series&time=%s' % last_update url = 'http://thetvdb.com/api/{0}/updates/{1}'.format( app.indexerApi(INDEXER_TVDB).api_params['apikey'], update_file) data = helpers.getURL(url, session=self.session, returns='text') if not data: logger.info( u'Could not get the recently updated show data from {indexer}. Retrying later. Url was: {logurl}', indexer=app.indexerApi(INDEXER_TVDB).name, logurl=url) self.amActive = False return updated_shows = [] try: tree = ET.fromstring(data) for show in tree.findall("Series"): updated_shows.append(int(show.find('id').text)) except SyntaxError: pass logger.info(u'Doing full update on all shows') pi_list = [] for cur_show in app.showList: if cur_show.indexer in bad_indexer: logger.warning( u'Indexer is no longer available for show [ {show} ] ', show=cur_show.name) else: indexer_name = app.indexerApi(cur_show.indexer).name try: if indexer_name == 'theTVDB': if cur_show.indexerid in updated_shows: # If the cur_show is not 'paused' then add to the showQueueSchedular if not cur_show.paused: pi_list.append( app.showQueueScheduler.action.updateShow( cur_show)) else: logger.info( u'Show update skipped, show: {show} is paused.', show=cur_show.name) else: cur_show.next_episode() if cur_show.should_update(update_date=update_date): try: pi_list.append( app.showQueueScheduler.action.updateShow( cur_show)) except CantUpdateShowException as e: logger.debug(u'Unable to update show: {error}', error=e) else: logger.debug( u'Not updating episodes for show {show} because the last or next episode is not within the grace period.', show=cur_show.name) except (CantUpdateShowException, CantRefreshShowException) as e: logger.warning(u'Automatic update failed. Error: {error}', error=e) except Exception as e: logger.error(u'Automatic update failed: Error: {error}', error=e) ui.ProgressIndicators.setIndicator( 'dailyUpdate', ui.QueueProgressIndicator("Daily Update", pi_list)) cache_db_con.action( "UPDATE lastUpdate SET `time` = ? WHERE provider=?", [update_timestamp, 'theTVDB']) logger.info(u'Completed full update on all shows') self.amActive = False