def lookup(self, entry, search_allowed=True, key=None): """ Perform Rotten Tomatoes lookup for entry. :param entry: Entry instance :param search_allowed: Allow fallback to search :param key: optionally specify an API key to use :raises PluginError: Failure reason """ if not key: key = self.key or plugin_api_rottentomatoes.API_KEY movie = plugin_api_rottentomatoes.lookup_movie( smart_match=entry['title'], rottentomatoes_id=entry.get('rt_id', eval_lazy=False), only_cached=(not search_allowed), api_key=key, ) logger.debug('Got movie: {}', movie) entry.update_using_map(self.field_map, movie) if not entry.get('imdb_id', eval_lazy=False): for alt_id in movie.alternate_ids: if alt_id.name == 'imdb': entry['imdb_id'] = 'tt' + alt_id.id break
def series_lookup(self, entry, language, field_map, session=None): try: series = plugin_api_tvdb.lookup_series( entry.get('series_name', eval_lazy=False), tvdb_id=entry.get('tvdb_id', eval_lazy=False), language=entry.get('language', language), session=session, ) entry.update_using_map(field_map, series) except LookupError as e: logger.debug('Error looking up tvdb series information for {}: {}', entry['title'], e.args[0]) return entry
def on_task_metainfo(self, task, config): # check if disabled (value set to false) if config is False: return for entry in task.entries: if isinstance(entry.get('quality', eval_lazy=False), str): logger.debug( 'Quality is already set to {} for {}, but has not been instantiated properly.', entry['quality'], entry['title'], ) entry['quality'] = qualities.Quality( entry.get('quality', eval_lazy=False)) else: entry.add_lazy_fields(self.get_quality, ['quality'])
def get_subtitles(self, entry): if (entry.get('subtitles', eval_lazy=False) or not ('location' in entry) or ('$RECYCLE.BIN' in entry['location']) or not os.path.exists(entry['location'])): return from subliminal import scan_video from subliminal.core import refine, search_external_subtitles try: video = scan_video(entry['location']) # grab external and internal subtitles subtitles = video.subtitle_languages refiner = ('metadata', ) refine(video, episode_refiners=refiner, movie_refiners=refiner) subtitles |= set( search_external_subtitles(entry['location']).values()) if subtitles: # convert to human-readable strings subtitles = [str(l) for l in subtitles] entry['subtitles'] = subtitles logger.debug('Found subtitles {} for {}', '/'.join(subtitles), entry['title']) except Exception as e: logger.error('Error checking local subtitles for {}: {}', entry['title'], e)
def get_media_id(self, entry): # Try to generate a media id based on available parser fields media_id = None if entry.get('movie_name'): media_id = entry['movie_name'] if entry.get('movie_year'): media_id = f'{media_id} {entry["movie_year"]}' elif entry.get('series_name'): media_id = entry['series_name'] if entry.get('series_year'): media_id = f'{media_id} {entry["series_year"]}' if entry.get('series_episode'): media_id = f'{media_id} S{(entry.get("series_season") or 0):02}E{entry["series_episode"]:02}' elif entry.get('series_season'): media_id = f'{media_id} S{entry["series_season"]:02}E00' elif entry.get('series_date'): media_id = f'{media_id} {entry["series_date"]}' else: # We do not want a media id for series media_id = None if media_id: media_id = media_id.strip().lower() entry['media_id'] = media_id
def on_task_metainfo(self, task, config): if not config: return for entry in task.entries: if (entry.get('series_name') or entry.get('tvdb_id', eval_lazy=False) or entry.get('tvmaze_id', eval_lazy=False) or entry.get('tvrage_id', eval_lazy=False)): entry.add_lazy_fields(self.lazy_series_lookup, self.series_map) if entry.get('season_pack', eval_lazy=False): entry.add_lazy_fields(self.lazy_season_lookup, self.season_map) if ('series_season' in entry and 'series_episode' in entry) or ('series_date' in entry): entry.add_lazy_fields(self.lazy_episode_lookup, self.episode_map)
def lazy_loader(self, entry, language): """Does the lookup for this entry and populates the entry fields.""" lookup = plugin.get('api_tmdb', self).lookup imdb_id = entry.get('imdb_id', eval_lazy=False) or extract_id( entry.get('imdb_url', eval_lazy=False)) try: with Session() as session: movie = lookup( smart_match=entry['title'], tmdb_id=entry.get('tmdb_id', eval_lazy=False), imdb_id=imdb_id, language=language, session=session, ) entry.update_using_map(self.field_map, movie) except LookupError: log_once('TMDB lookup failed for %s' % entry['title'], logger, 'WARNING')
def lazy_series_lookup(self, entry): """Does the lookup for this entry and populates the entry fields.""" series_lookup = plugin.get('api_tvmaze', self).series_lookup with Session() as session: lookupargs = { 'title': entry.get('series_name', eval_lazy=False), 'year': entry.get('year', eval_lazy=False), 'tvmaze_id': entry.get('tvmaze_id', eval_lazy=False), 'tvdb_id': entry.get('tvdb_id', eval_lazy=False), 'tvrage_id': entry.get('tvrage_idk', eval_lazy=False), 'session': session, } try: series = series_lookup(**lookupargs) except LookupError as e: logger.debug(e) else: entry.update_using_map(self.series_map, series) return entry
def get_quality(self, entry): if entry.get('quality', eval_lazy=False): logger.debug( 'Quality is already set to {} for {}, skipping quality detection.', entry['quality'], entry['title'], ) return entry['quality'] = qualities.Quality(entry['title']) if entry['quality']: logger.trace('Found quality {} for {}', entry['quality'], entry['title'])
def lazy_episode_lookup(self, entry, language): try: season_offset = entry.get('thetvdb_lookup_season_offset', 0) episode_offset = entry.get('thetvdb_lookup_episode_offset', 0) if not isinstance(season_offset, int): logger.error('thetvdb_lookup_season_offset must be an integer') season_offset = 0 if not isinstance(episode_offset, int): logger.error( 'thetvdb_lookup_episode_offset must be an integer') episode_offset = 0 if season_offset != 0 or episode_offset != 0: logger.debug( f'Using offset for tvdb lookup: season: {season_offset}, ' f'episode: {episode_offset}') lookupargs = { 'name': entry.get('series_name', eval_lazy=False), 'tvdb_id': entry.get('tvdb_id', eval_lazy=False), 'language': entry.get('language', language), } if entry['series_id_type'] == 'ep': lookupargs[ 'season_number'] = entry['series_season'] + season_offset lookupargs['episode_number'] = entry[ 'series_episode'] + episode_offset elif entry['series_id_type'] == 'sequence': lookupargs[ 'absolute_number'] = entry['series_id'] + episode_offset elif entry['series_id_type'] == 'date': # TODO: Should thetvdb_lookup_episode_offset be used for date lookups as well? lookupargs['first_aired'] = entry['series_date'] episode = plugin_api_tvdb.lookup_episode(**lookupargs) entry.update_using_map(self.episode_map, episode) except LookupError as e: logger.debug( 'Error looking up tvdb episode information for {}: {}', entry['title'], e.args[0])
def on_task_metainfo(self, task, config): if not config: return language = config['language'] if not isinstance(config, bool) else 'en' for entry in task.entries: # If there is information for a series lookup, register our series lazy fields if entry.get('series_name') or entry.get('tvdb_id', eval_lazy=False): entry.add_lazy_fields(self.lazy_series_lookup, self.series_map, kwargs={'language': language}) entry.add_lazy_fields( self.lazy_series_actor_lookup, self.series_actor_map, kwargs={'language': language}, ) entry.add_lazy_fields( self.lazy_series_poster_lookup, self.series_poster_map, kwargs={'language': language}, ) # If there is season and ep info as well, register episode lazy fields if entry.get('series_id_type') in ('ep', 'sequence', 'date'): if entry.get('season_pack'): logger.verbose( 'TheTVDB API does not support season lookup at this time, skipping {}', entry, ) else: entry.add_lazy_fields( self.lazy_episode_lookup, self.episode_map, kwargs={'language': language}, )
def lazy_episode_lookup(self, entry): episode_lookup = plugin.get('api_tvmaze', self).episode_lookup with Session(expire_on_commit=False) as session: lookupargs = { 'title': entry.get('series_name', eval_lazy=False), 'year': entry.get('year', eval_lazy=False), 'tvmaze_id': entry.get('tvmaze_id', eval_lazy=False), 'tvdb_id': entry.get('tvdb_id', eval_lazy=False), 'tvrage_id': entry.get('tvrage_id', eval_lazy=False), 'series_season': entry.get('series_season', eval_lazy=False), 'series_episode': entry.get('series_episode', eval_lazy=False), 'series_date': entry.get('series_date', eval_lazy=False), 'series_id_type': entry.get('series_id_type', eval_lazy=False), 'session': session, } try: episode = episode_lookup(**lookupargs) except LookupError as e: logger.debug(e) else: entry.update_using_map(self.episode_map, episode) return entry
def modify(self, entry, config, errors=True): """This can be called from a plugin to add set values to an entry""" for field in config: # If this doesn't appear to be a jinja template, just set it right away. if not isinstance(config[field], str) or '{' not in config[field]: entry[field] = config[field] # Store original values before overwriting with a lazy field, so that set directives can reference # themselves. else: orig_value = entry.get(field, UNSET, eval_lazy=False) try: del entry[field] except KeyError: pass entry.add_lazy_fields( self.lazy_set, [field], kwargs={ 'config': config, 'field': field, 'orig_field_value': orig_value, 'errors': errors, }, )
def is_season(entry: entry.Entry) -> bool: return entry.get('series_season') and not is_episode(entry)
def lookup(self, entry, search_allowed=True, session=None): """ Perform imdb lookup for entry. :param entry: Entry instance :param search_allowed: Allow fallback to search :raises PluginError: Failure reason """ from flexget.manager import manager if entry.get('imdb_id', eval_lazy=False): logger.debug('No title passed. Lookup for {}', entry['imdb_id']) elif entry.get('imdb_url', eval_lazy=False): logger.debug('No title passed. Lookup for {}', entry['imdb_url']) elif entry.get('title', eval_lazy=False): logger.debug('lookup for {}', entry['title']) else: raise plugin.PluginError( 'looking up IMDB for entry failed, no title, imdb_url or imdb_id passed.' ) # if imdb_id is included, build the url. if entry.get('imdb_id', eval_lazy=False) and not entry.get( 'imdb_url', eval_lazy=False): entry['imdb_url'] = make_url(entry['imdb_id']) # make sure imdb url is valid if entry.get('imdb_url', eval_lazy=False): imdb_id = extract_id(entry['imdb_url']) if imdb_id: entry['imdb_url'] = make_url(imdb_id) else: logger.debug('imdb url {} is invalid, removing it', entry['imdb_url']) entry['imdb_url'] = '' # no imdb_url, check if there is cached result for it or if the # search is known to fail if not entry.get('imdb_url', eval_lazy=False): result = (session.query(db.SearchResult).filter( db.SearchResult.title == entry['title']).first()) if result: # TODO: 1.2 this should really be checking task.options.retry if result.fails and not manager.options.execute.retry: # this movie cannot be found, not worth trying again ... logger.debug('{} will fail lookup', entry['title']) raise plugin.PluginError('IMDB lookup failed for %s' % entry['title']) else: if result.url: logger.trace('Setting imdb url for {} from db', entry['title']) entry['imdb_id'] = result.imdb_id entry['imdb_url'] = result.url # no imdb url, but information required, try searching if not entry.get('imdb_url', eval_lazy=False) and search_allowed: logger.verbose('Searching from imdb `{}`', entry['title']) search = ImdbSearch() search_name = entry.get('movie_name', entry['title'], eval_lazy=False) search_result = search.smart_match(search_name) if search_result: entry['imdb_url'] = search_result['url'] # store url for this movie, so we don't have to search on every run result = db.SearchResult(entry['title'], entry['imdb_url']) session.add(result) session.commit() logger.verbose('Found {}', entry['imdb_url']) else: log_once( 'IMDB lookup failed for %s' % entry['title'], logger, 'WARNING', session=session, ) # store FAIL for this title result = db.SearchResult(entry['title']) result.fails = True session.add(result) session.commit() raise plugin.PluginError('Title `%s` lookup failed' % entry['title']) # check if this imdb page has been parsed & cached movie = session.query( db.Movie).filter(db.Movie.url == entry['imdb_url']).first() # If we have a movie from cache, we are done if movie and not movie.expired: entry.update_using_map(self.field_map, movie) return # Movie was not found in cache, or was expired if movie is not None: if movie.expired: logger.verbose('Movie `{}` details expired, refreshing ...', movie.title) # Remove the old movie, we'll store another one later. session.query(db.MovieLanguage).filter( db.MovieLanguage.movie_id == movie.id).delete() session.query( db.Movie).filter(db.Movie.url == entry['imdb_url']).delete() session.commit() # search and store to cache if 'title' in entry: logger.verbose('Parsing imdb for `{}`', entry['title']) else: logger.verbose('Parsing imdb for `{}`', entry['imdb_id']) try: movie = self._parse_new_movie(entry['imdb_url'], session) except UnicodeDecodeError: logger.error( 'Unable to determine encoding for {}. Installing chardet library may help.', entry['imdb_url'], ) # store cache so this will not be tried again movie = db.Movie() movie.url = entry['imdb_url'] session.add(movie) session.commit() raise plugin.PluginError('UnicodeDecodeError') except ValueError as e: # TODO: might be a little too broad catch, what was this for anyway? ;P if manager.options.debug: logger.exception(e) raise plugin.PluginError( 'Invalid parameter: %s' % entry['imdb_url'], logger) for att in [ 'title', 'score', 'votes', 'meta_score', 'year', 'genres', 'languages', 'actors', 'directors', 'writers', 'mpaa_rating', ]: logger.trace('movie.{}: {}', att, getattr(movie, att)) # Update the entry fields entry.update_using_map(self.field_map, movie)
def is_show(entry): return entry.get('series_name') or entry.get('tvdb_id', eval_lazy=False)
def is_season(entry): return entry.get('series_season') and not is_episode(entry)
def is_movie(entry: entry.Entry) -> bool: return bool(entry.get('movie_name'))
def is_movie(entry): return bool(entry.get('movie_name'))
def _get_lookup_args(self, entry): args = { 'title': entry.get('series_name', eval_lazy=False) or entry.get('title', eval_lazy=False), 'year': entry.get('year', eval_lazy=False), 'trakt_slug': (entry.get('trakt_show_slug', eval_lazy=False) or entry.get('trakt_movie_slug', eval_lazy=False)), 'tmdb_id': entry.get('tmdb_id', eval_lazy=False), 'tvdb_id': entry.get('tvdb_id', eval_lazy=False), 'imdb_id': entry.get('imdb_id', eval_lazy=False), 'tvrage_id': entry.get('tvrage_id', eval_lazy=False), } if entry.get('trakt_movie_id', eval_lazy=False): args['trakt_id'] = entry['trakt_movie_id'] elif entry.get('trakt_show_id', eval_lazy=False): args['trakt_id'] = entry['trakt_show_id'] elif is_movie(entry) and entry.get('trakt_movie_id', eval_lazy=True): args['trakt_id'] = entry['trakt_movie_id'] elif entry.get('trakt_show_id', eval_lazy=True): args['trakt_id'] = entry['trakt_show_id'] return args