def movie_list_add(options): with Session() as session: try: movie_list = get_list_by_exact_name(options.list_name) except NoResultFound: console('Could not find movie list with name {}, creating'.format(options.list_name)) movie_list = MovieListList(name=options.list_name) session.merge(movie_list) title, year = split_title_year(options.movie_title) console('Trying to lookup movie %s title' % title) entry = lookup_movie(title=title, session=session, identifiers=options.identifiers) if not entry: console('movie lookup failed for movie %s, aborting') return title = entry['movie_name'] movie = get_movie_by_title(list_id=movie_list.id, title=title, session=session) if not movie: console("Adding movie with title {} to list {}".format(title, movie_list.name)) movie = MovieListMovie(title=entry['movie_name'], year=year, list_id=movie_list.id) else: console("Movie with title {} already exist in list {}".format(title, movie_list.name)) id_list = [] if options.identifiers: id_list = options.identifiers else: for _id in MovieListBase().supported_ids: if entry.get(_id): id_list.append({_id: entry.get(_id)}) if id_list: console('Setting movie identifiers:', id_list) movie.ids = get_db_movie_identifiers(identifier_list=id_list, session=session) session.merge(movie) console('Successfully added movie {} to movie list {} '.format(title, movie_list.name))
def get_trakt_id_from_title(title, media_type, year=None): if not title: raise LookupError('No lookup arguments provided.') requests_session = get_session() # Try finding trakt id based on title and year parsed_title, y = split_title_year(title) y = year or y try: params = {'query': parsed_title, 'type': media_type, 'year': y} log.debug('Type of title: %s', type(parsed_title)) log.debug( 'Searching with params: %s', ', '.join('{}={}'.format(k, v) for (k, v) in params.items()), ) results = requests_session.get(get_api_url('search'), params=params).json() except requests.RequestException as e: raise LookupError('Searching trakt for %s failed with error: %s' % (title, e)) for result in results: if year and result[media_type]['year'] != year: continue if parsed_title.lower() == result[media_type]['title'].lower(): return result[media_type]['ids']['trakt'] # grab the first result if there is no exact match if results: return results[0][media_type]['ids']['trakt']
def post(self, list_id, session=None): """ Add movies to list by ID """ try: movie_list = ml.get_list_by_id(list_id=list_id, session=session) except NoResultFound: return {'status': 'error', 'message': 'list_id %d does not exist' % list_id}, 404 data = request.json # Validates ID type based on allowed ID # TODO pass this to json schema validation for id_name in data.get('movie_identifiers'): if set(id_name.keys()) & set(allowed_ids) == set([]): return {'status': 'error', 'message': 'movie identifier %s is not allowed' % id_name}, 501 if 'movie_name' in data: title, year = data['movie_name'], data.get('movie_year') else: title, year = split_title_year(data['title']) movie = ml.get_movie_by_title(list_id=list_id, title=title, session=session) if movie: return {'status': 'error', 'message': 'movie with name "%s" already exist in list %d' % (title, list_id)}, 500 movie = ml.MovieListMovie() movie.title = title movie.year = year movie.ids = ml.get_db_movie_identifiers(identifier_list=data.get('movie_identifiers'), session=session) movie.list_id = list_id session.add(movie) session.commit() response = jsonify({'movie': movie.to_dict()}) response.status_code = 201 return response
def get_item_from_cache(table, session, title=None, year=None, trakt_ids=None): """ Get the cached info for a given show/movie from the database. :param table: Either TraktMovie or TraktShow :param title: Title of the show/movie :param year: First release year :param trakt_ids: instance of TraktShowIds or TraktMovieIds :param session: database session object :return: query result """ result = None if trakt_ids: result = ( session.query(table) .filter( or_(getattr(table, col) == val for col, val in trakt_ids.to_dict().items() if val) ) .first() ) elif title: title, y = split_title_year(title) year = year or y query = session.query(table).filter(table.title == title) if year: query = query.filter(table.year == year) result = query.first() return result
def prepare_lookup_for_tvmaze(**lookup_params): """ Return a dict of params which is valid with tvmaze API lookups :param lookup_params: Search parameters :return: Dict of tvmaze recognizable key words """ prepared_params = {} title = None series_name = ( lookup_params.get('series_name') or lookup_params.get('show_name') or lookup_params.get('title') ) if series_name: title, _ = split_title_year(series_name) # Support for when title is just a number if not title: title = series_name # Ensure we send native types to tvmaze lib as it does not handle new types very well prepared_params['tvmaze_id'] = lookup_params.get('tvmaze_id') prepared_params['thetvdb_id'] = lookup_params.get('tvdb_id') or lookup_params.get( 'trakt_series_tvdb_id' ) prepared_params['tvrage_id'] = lookup_params.get('tvrage_id') or lookup_params.get( 'trakt_series_tvrage_id' ) prepared_params['imdb_id'] = lookup_params.get('imdb_id') prepared_params['show_name'] = native(title) if title else None return prepared_params
def prepare_lookup_for_pytvmaze(**lookup_params): """ Return a dict of params which is valid with pytvmaze get_show method :param lookup_params: Search parameters :return: Dict of pytvmaze recognizable key words """ prepared_params = {} title = None year_match = None series_name = lookup_params.get('series_name') or lookup_params.get('show_name') or lookup_params.get('title') if series_name: title, year_match = split_title_year(series_name) # Support for when title is just a number if not title: title = series_name prepared_params['maze_id'] = lookup_params.get('tvmaze_id') prepared_params['tvdb_id'] = lookup_params.get('tvdb_id') or lookup_params.get('trakt_series_tvdb_id') prepared_params['tvrage_id'] = lookup_params.get('tvrage_id') or lookup_params.get('trakt_series_tvrage_id') prepared_params['imdb_id'] = lookup_params.get('imdb_id') prepared_params['show_name'] = title or None prepared_params['show_year'] = lookup_params.get('trakt_series_year') or lookup_params.get( 'year') or lookup_params.get('imdb_year') or year_match prepared_params['show_network'] = lookup_params.get('network') or lookup_params.get('trakt_series_network') prepared_params['show_country'] = lookup_params.get('country') or lookup_params.get('trakt_series_country') prepared_params['show_language'] = lookup_params.get('language') # Include cast information by default prepared_params['embed'] = 'cast' return prepared_params
def movie_list_del(options): with Session() as session: try: movie_list = db.get_list_by_exact_name(options.list_name) except NoResultFound: console('Could not find movie list with name {}'.format(options.list_name)) return try: movie_exist = db.get_movie_by_id( list_id=movie_list.id, movie_id=int(options.movie), session=session ) except NoResultFound: console( 'Could not find movie with ID {} in list `{}`'.format( int(options.movie), options.list_name ) ) return except ValueError: title, year = split_title_year(options.movie) movie_exist = db.get_movie_by_title_and_year( list_id=movie_list.id, title=title, year=year, session=session ) if not movie_exist: console( 'Could not find movie with title {} in list {}'.format( options.movie, options.list_name ) ) return else: console('Removing movie {} from list {}'.format(movie_exist.title, options.list_name)) session.delete(movie_exist)
def items(self): if self._items is None: try: req = TVDBRequest(username=self.config['username'], account_id=self.config['account_id']).get( 'user/favorites') series_ids = [int(f_id) for f_id in req['favorites'] if f_id != ''] except RequestException as e: raise PluginError('Error retrieving favorites from thetvdb: %s' % str(e)) self._items = [] for series_id in series_ids: # Lookup the series name from the id try: series = lookup_series(tvdb_id=series_id) except LookupError as e: log.error('Error looking up %s from thetvdb: %s' % (series_id, e.args[0])) else: series_name = series.name if self.config.get('strip_dates'): # Remove year from end of series name if present series_name, _ = split_title_year(series_name) entry = Entry() entry['title'] = entry['series_name'] = series_name entry['url'] = 'http://thetvdb.com/index.php?tab=series&id={}'.format(str(series.id)) entry['tvdb_id'] = str(series.id) self._items.append(entry) return self._items
def movie_list_add(options): with Session() as session: try: movie_list = get_list_by_exact_name(options.list_name) except NoResultFound: console('Could not find movie list with name {}, creating'.format(options.list_name)) movie_list = MovieListList(name=options.list_name) session.add(movie_list) session.merge(movie_list) title, year = split_title_year(options.movie_title) movie_exist = get_movie_by_title(list_id=movie_list.id, title=title, session=session) if movie_exist: console("Movie with the title {} already exist with list {}. Will replace identifiers if given".format( title, movie_list.name)) output = 'Successfully updated movie {} to movie list {} '.format(title, movie_list.name) else: console("Adding movie with title {} to list {}".format(title, movie_list.name)) movie_exist = MovieListMovie(title=title, year=year, list_id=movie_list.id) session.add(movie_exist) output = 'Successfully added movie {} to movie list {} '.format(title, movie_list.name) if options.identifiers: identifiers = [parse_identifier(identifier) for identifier in options.identifiers if options.identifiers] console('Adding identifiers {} to movie {}'.format(identifiers, title)) movie_exist.ids = get_db_movie_identifiers(identifier_list=identifiers, session=session) console(output)
def prepare_lookup_for_pytvmaze(**lookup_params): """ Return a dict of params which is valid with pytvmaze get_show method :param lookup_params: Search parameters :return: Dict of pytvmaze recognizable key words """ prepared_params = {} series_name = lookup_params.get("series_name") or lookup_params.get("show_name") or lookup_params.get("title") title, year_match = split_title_year(series_name) # Support for when title is just a number if not title: title = series_name prepared_params["maze_id"] = lookup_params.get("tvmaze_id") prepared_params["tvdb_id"] = lookup_params.get("tvdb_id") or lookup_params.get("trakt_series_tvdb_id") prepared_params["tvrage_id"] = lookup_params.get("tvrage_id") or lookup_params.get("trakt_series_tvrage_id") prepared_params["show_name"] = title prepared_params["show_year"] = ( lookup_params.get("trakt_series_year") or lookup_params.get("year") or lookup_params.get("imdb_year") or year_match ) prepared_params["show_network"] = lookup_params.get("network") or lookup_params.get("trakt_series_network") prepared_params["show_country"] = lookup_params.get("country") or lookup_params.get("trakt_series_country") prepared_params["show_language"] = lookup_params.get("language") # Include cast information by default prepared_params["embed"] = "cast" return prepared_params
def _find_entry(self, entry, session=None): """Finds `MovieListMovie` corresponding to this entry, if it exists.""" for id_name in SUPPORTED_IDS: if entry.get(id_name): log.debug('trying to match movie based off id %s: %s', id_name, entry[id_name]) res = (self._db_list(session).movies.join(MovieListMovie.ids).filter( and_( MovieListID.id_name == id_name, MovieListID.id_value == entry[id_name])) .first()) if res: log.debug('found movie %s', res) return res # Fall back to title/year match if entry.get('movie_name') and entry.get('movie_year'): name, year = entry['movie_name'], entry['movie_year'] else: name, year = split_title_year(entry['title']) if not name: log.verbose('no movie name to match, skipping') return log.debug('trying to match movie based of name: %s and year: %d', name, year) res = (self._db_list(session).movies.filter(func.lower(MovieListMovie.title) == name.lower()) .filter(MovieListMovie.year == year).first()) if res: log.debug('found movie %s', res) return res
def lazy_loader(self, entry): """Does the lookup for this entry and populates the entry fields.""" lookup = plugin.get('api_bluray', self).lookup try: with Session() as session: title, year = split_title_year(entry['title']) movie = lookup(title=title, year=year, session=session) entry.update_using_map(self.field_map, movie) except LookupError: log_once('Bluray lookup failed for %s' % entry['title'], log, logging.WARN)
def estimate(self, entry): if not all(field in entry for field in ['series_name', 'series_season']): return series_name = entry['series_name'] season = entry['series_season'] episode_number = entry.get('series_episode') title, year_match = split_title_year(series_name) # This value should be added to input plugins to trigger a season lookuo season_pack = entry.get('season_pack_lookup') kwargs = { 'tvmaze_id': entry.get('tvmaze_id'), 'tvdb_id': entry.get('tvdb_id') or entry.get('trakt_series_tvdb_id'), 'tvrage_id': entry.get('tvrage_id') or entry.get('trakt_series_tvrage_id'), 'imdb_id': entry.get('imdb_id'), 'show_name': title, 'show_year': entry.get('trakt_series_year') or entry.get('year') or entry.get('imdb_year') or year_match, 'show_network': entry.get('network') or entry.get('trakt_series_network'), 'show_country': entry.get('country') or entry.get('trakt_series_country'), 'show_language': entry.get('language'), 'series_season': season, 'series_episode': episode_number, 'series_name': series_name, } api_tvmaze = plugin.get('api_tvmaze', self) if season_pack: lookup = api_tvmaze.season_lookup log.debug('Searching api_tvmaze for season') else: log.debug('Searching api_tvmaze for episode') lookup = api_tvmaze.episode_lookup for k, v in list(kwargs.items()): if v: log.debug('%s: %s', k, v) try: entity = lookup(**kwargs) except LookupError as e: log.debug(str(e)) return if entity and entity.airdate: log.debug('received air-date: %s', entity.airdate) return entity.airdate return
def movie_list_del(options): with Session() as session: try: movie_list = get_list_by_exact_name(options.list_name) except NoResultFound: console('Could not find movie list with name {}'.format(options.list_name)) return title = split_title_year(options.movie_title)[0] movie_exist = get_movie_by_title(list_id=movie_list.id, title=title, session=session) if movie_exist: console('Removing movie %s from list %s' % (options.movie_title, options.list_name)) session.delete(movie_exist) else: console('Could not find movie with title %s in list %s' % (options.movie_title, options.list_name)) return
def _find_entry(self, entry, session=None): """Finds `MovieListMovie` corresponding to this entry, if it exists.""" for id_name in SUPPORTED_IDS: if id_name in entry: # TODO: Make this real res = (self._db_list(session).movies.filter(MovieListID.id_name == id_name) .filter(MovieListID.id_value == entry[id_name]).first()) if res: return res # Fall back to title/year match if 'movie_name' in entry and 'movie_year' in entry: name, year = entry['movie_name'], entry['movie_year'] else: name, year = split_title_year(entry['title']) res = (self._db_list(session).movies.filter(MovieListMovie.title == name) .filter(MovieListMovie.year == year).first()) return res
def movie_list_add(options): with Session() as session: try: movie_list = db.get_list_by_exact_name(options.list_name, session=session) except NoResultFound: console('Could not find movie list with name {}, creating'.format(options.list_name)) movie_list = db.MovieListList(name=options.list_name) session.add(movie_list) session.commit() title, year = split_title_year(options.movie_title) console('Trying to lookup movie title: `{}`'.format(title)) movie_lookup = lookup_movie(title=title, session=session, identifiers=options.identifiers) if not movie_lookup: console( 'ERROR: movie lookup failed for movie {}, aborting'.format(options.movie_title) ) return title = movie_lookup['movie_name'] movie = db.get_movie_by_title_and_year( list_id=movie_list.id, title=title, year=year, session=session ) if not movie: console("Adding movie with title {} to list {}".format(title, movie_list.name)) movie = db.MovieListMovie(title=title, year=year, list_id=movie_list.id) else: console("Movie with title {} already exist in list {}".format(title, movie_list.name)) id_list = [] if options.identifiers: id_list = options.identifiers else: for _id in db.MovieListBase().supported_ids: if movie_lookup.get(_id): id_list.append({_id: movie_lookup.get(_id)}) if id_list: console('Setting movie identifiers:') for ident in id_list: for key in ident: console('{}: {}'.format(key, ident[key])) movie.ids = db.get_db_movie_identifiers(identifier_list=id_list, session=session) session.merge(movie) console('Successfully added movie {} to movie list {} '.format(title, movie_list.name))
def add(self, entry, session=None): # Check if this is already in the list, refresh info if so db_list = self._db_list(session=session) db_movie = self._find_entry(entry, session=session) # Just delete and re-create to refresh if db_movie: session.delete(db_movie) db_movie = MovieListMovie() if 'movie_name' in entry: db_movie.title, db_movie.year = entry['movie_name'], entry.get('movie_year') else: db_movie.title, db_movie.year = split_title_year(entry['title']) for id_name in SUPPORTED_IDS: if id_name in entry: db_movie.ids.append(MovieListID(id_name=id_name, id_value=entry[id_name])) log.debug('adding entry %s', entry) db_list.movies.append(db_movie) session.commit() return db_movie.to_entry()
def _find_entry(self, entry, session=None): """Finds `MovieListMovie` corresponding to this entry, if it exists.""" for id_name in SUPPORTED_IDS: if id_name in entry: log.debug('finding movie based off id %s:%s', id_name, entry[id_name]) res = (self._db_list(session).movies.join(MovieListMovie.ids).filter( and_( MovieListID.id_name == id_name, MovieListID.id_value == entry[id_name])) .first()) if res: log.debug('found movie %s', res) return res # Fall back to title/year match if 'movie_name' in entry and 'movie_year' in entry: name, year = entry['movie_name'], entry['movie_year'] else: name, year = split_title_year(entry['title']) res = (self._db_list(session).movies.filter(MovieListMovie.title == name) .filter(MovieListMovie.year == year).first()) return res
def find_series_id(name): """Looks up the tvdb id for a series""" try: series = TVDBRequest().get('search/series', name=name) except requests.RequestException as e: raise LookupError('Unable to get search results for %s: %s' % (name, e)) name = name.lower() if not series: raise LookupError('No results found for %s' % name) # Cleanup results for sorting for s in series: if s['firstAired']: s['firstAired'] = datetime.strptime(s['firstAired'], "%Y-%m-%d") else: s['firstAired'] = datetime(1970, 1, 1) s['names'] = [a.lower() for a in s.get('aliases')] if s.get('aliases') else [] if s.get('seriesName'): s['names'].append(s.get('seriesName').lower()) s['running'] = True if s['status'] == 'Continuing' else False for n in s['names']: # Exact matching by stripping our the year title, year = split_title_year(n) if title not in s['names']: s['names'].append(title) # Sort by status, aired_date series = sorted(series, key=lambda x: (x['running'], x['firstAired']), reverse=True) for s in series: # Exact match if name in s['names']: return s['id'] # If there is no exact match, pick the first result return series[0]['id']
def add(self, entry): with Session() as session: # Check if this is already in the list, refresh info if so db_list = self._db_list(session=session) db_movie = self._find_entry(entry, session=session) # Just delete and re-create to refresh if db_movie: session.delete(db_movie) db_movie = MovieListMovie() if 'movie_name' in entry: db_movie.title, db_movie.year = entry['movie_name'], entry.get( 'movie_year') else: db_movie.title, db_movie.year = split_title_year( entry['title']) for id_name in MovieListBase().supported_ids: if id_name in entry: db_movie.ids.append( MovieListID(id_name=id_name, id_value=entry[id_name])) log.debug('adding entry %s', entry) db_list.movies.append(db_movie) session.commit() return db_movie.to_entry()
def items(self): if self._items is None: try: req = plugin_api_tvdb.TVDBRequest( username=self.config['username'], account_id=self.config['account_id'], api_key=self.config['api_key'], ).get('user/favorites') series_ids = [ int(f_id) for f_id in req['favorites'] if f_id != '' ] except RequestException as e: raise PluginError( 'Error retrieving favorites from thetvdb: %s' % str(e)) self._items = [] for series_id in series_ids: # Lookup the series name from the id try: series = plugin_api_tvdb.lookup_series( tvdb_id=series_id, language=self.config.get('language')) except LookupError as e: logger.error('Error looking up {} from thetvdb: {}', series_id, e.args[0]) else: series_name = series.name if self.config.get('strip_dates'): # Remove year from end of series name if present series_name, _ = split_title_year(series_name) entry = Entry() entry['title'] = entry['series_name'] = series_name entry[ 'url'] = 'http://thetvdb.com/index.php?tab=series&id={}'.format( str(series.id)) entry['tvdb_id'] = str(series.id) self._items.append(entry) return self._items
def prepare_lookup_for_pytvmaze(**lookup_params): """ Return a dict of params which is valid with pytvmaze get_show method :param lookup_params: Search parameters :return: Dict of pytvmaze recognizable key words """ prepared_params = {} title = None year_match = None series_name = lookup_params.get('series_name') or lookup_params.get( 'show_name') or lookup_params.get('title') if series_name: title, year_match = split_title_year(series_name) # Support for when title is just a number if not title: title = series_name prepared_params['maze_id'] = lookup_params.get('tvmaze_id') prepared_params['tvdb_id'] = lookup_params.get( 'tvdb_id') or lookup_params.get('trakt_series_tvdb_id') prepared_params['tvrage_id'] = lookup_params.get( 'tvrage_id') or lookup_params.get('trakt_series_tvrage_id') prepared_params['imdb_id'] = lookup_params.get('imdb_id') prepared_params['show_name'] = title or None prepared_params['show_year'] = lookup_params.get( 'trakt_series_year') or lookup_params.get('year') or lookup_params.get( 'imdb_year') or year_match prepared_params['show_network'] = lookup_params.get( 'network') or lookup_params.get('trakt_series_network') prepared_params['show_country'] = lookup_params.get( 'country') or lookup_params.get('trakt_series_country') prepared_params['show_language'] = lookup_params.get('language') # Include cast information by default prepared_params['embed'] = 'cast' return prepared_params
def estimate(self, entry): if not all(field in entry for field in ['series_name', 'series_season', 'series_episode']): return series_name = entry['series_name'] season = entry['series_season'] episode_number = entry['series_episode'] title, year_match = split_title_year(series_name) kwargs = {} kwargs['tvmaze_id'] = entry.get('tvmaze_id') kwargs['tvdb_id'] = entry.get('tvdb_id') or entry.get('trakt_series_tvdb_id') kwargs['tvrage_id'] = entry.get('tvrage_id') or entry.get('trakt_series_tvrage_id') kwargs['imdb_id'] = entry.get('imdb_id') kwargs['show_name'] = title kwargs['show_year'] = entry.get('trakt_series_year') or entry.get('year') or entry.get( 'imdb_year') or year_match kwargs['show_network'] = entry.get('network') or entry.get('trakt_series_network') kwargs['show_country'] = entry.get('country') or entry.get('trakt_series_country') kwargs['show_language'] = entry.get('language') kwargs['series_season'] = season kwargs['series_episode'] = episode_number kwargs['series_name'] = series_name log.debug( 'Searching TVMaze for airdate of {0} season {1} episode {2}'.format(series_name, season, episode_number)) for k, v in list(kwargs.items()): if v: log.debug('{0}: {1}'.format(k, v)) try: episode = lookup(**kwargs) except LookupError as e: log.debug(e) return if episode and episode.airdate: log.debug('received airdate: {0}'.format(episode.airdate)) return episode.airdate return
def prepare_lookup_for_tvmaze(**lookup_params): """ Return a dict of params which is valid with tvmaze API lookups :param lookup_params: Search parameters :return: Dict of tvmaze recognizable key words """ prepared_params = {} title = None series_name = lookup_params.get('series_name') or lookup_params.get('show_name') or lookup_params.get('title') if series_name: title, _ = split_title_year(series_name) # Support for when title is just a number if not title: title = series_name # Ensure we send native types to tvmaze lib as it does not handle new types very well prepared_params['tvmaze_id'] = lookup_params.get('tvmaze_id') prepared_params['thetvdb_id'] = lookup_params.get('tvdb_id') or lookup_params.get('trakt_series_tvdb_id') prepared_params['tvrage_id'] = lookup_params.get('tvrage_id') or lookup_params.get('trakt_series_tvrage_id') prepared_params['imdb_id'] = lookup_params.get('imdb_id') prepared_params['show_name'] = native(title) if title else None return prepared_params
def movie_list_add(options, session=None): try: movie_list = get_list_by_exact_name(options.list_name) except NoResultFound: console('Could not find movie list with name {}, creating'.format(options.list_name)) movie_list = MovieListList(name=options.list_name) session.add(movie_list) session.commit() title, year = split_title_year(options.movie_title) movie_exist = get_movie_by_title(list_id=movie_list.id, title=title, session=session) if movie_exist: console("Movie with the title {} already exist with list {}. Will replace identifiers if given".format( title, movie_list.name)) output = 'Successfully updated movie {} to movie list {} '.format(title, movie_list.name) else: console("Adding movie with title {} to list {}".format(title, movie_list.name)) movie_exist = MovieListMovie(title=title, year=year, list_id=movie_list.id) session.add(movie_exist) output = 'Successfully added movie {} to movie list {} '.format(title, movie_list.name) if options.identifiers: identifiers = [parse_identifier(identifier) for identifier in options.identifiers if options.identifiers] console('Adding identifiers {} to movie {}'.format(identifiers, title)) movie_exist.ids = get_db_movie_identifiers(identifier_list=identifiers, session=session) console(output)
def get_item_from_cache(table, session, title=None, year=None, trakt_ids=None): """ Get the cached info for a given show/movie from the database. :param table: Either TraktMovie or TraktShow :param title: Title of the show/movie :param year: First release year :param trakt_ids: instance of TraktShowIds or TraktMovieIds :param session: database session object :return: query result """ result = None if trakt_ids: result = (session.query(table).filter( or_( getattr(table, col) == val for col, val in trakt_ids.to_dict().items() if val)).first()) elif title: title, y = split_title_year(title) year = year or y query = session.query(table).filter(table.title == title) if year: query = query.filter(table.year == year) result = query.first() return result
def test_split_year_title(self, title, expected_title, expected_year): assert split_title_year(title) == (expected_title, expected_year)
def items(self): if self._items is None: if self.config['list'] in ['collection', 'watched'] and self.config['type'] == 'auto': raise plugin.PluginError('`type` cannot be `auto` for %s list.' % self.config['list']) endpoint = self.get_list_endpoint() log.verbose('Retrieving `%s` list `%s`', self.config['type'], self.config['list']) try: result = self.session.get(get_api_url(endpoint)) try: data = result.json() except ValueError: log.debug('Could not decode json from response: %s', result.text) raise plugin.PluginError('Error getting list from trakt.') except RequestException as e: raise plugin.PluginError('Could not retrieve list from trakt (%s)' % e) if not data: log.warning('No data returned from trakt for %s list %s.', self.config['type'], self.config['list']) return [] entries = [] list_type = (self.config['type']).rstrip('s') for item in data: if self.config['type'] == 'auto': list_type = item['type'] # Collection and watched lists don't return 'type' along with the items (right now) if 'type' in item and item['type'] != list_type: log.debug('Skipping %s because it is not a %s', item[item['type']].get('title', 'unknown'), list_type) continue if list_type != 'episode' and not item[list_type]['title']: # Skip shows/movies with no title log.warning('Item in trakt list does not appear to have a title, skipping.') continue entry = Entry() if list_type == 'episode': entry['url'] = 'https://trakt.tv/shows/%s/seasons/%s/episodes/%s' % ( item['show']['ids']['slug'], item['episode']['season'], item['episode']['number']) else: entry['url'] = 'https://trakt.tv/%ss/%s' % (list_type, item[list_type]['ids'].get('slug')) entry.update_using_map(field_maps[list_type], item) # get movie name translation language = self.config.get('language') if list_type == 'movie' and language: endpoint = ['movies', entry['trakt_movie_id'], 'translations', language] try: result = self.session.get(get_api_url(endpoint)) try: translation = result.json() except ValueError: raise plugin.PluginError('Error decoding movie translation from trakt: %s.' % result.text) except RequestException as e: raise plugin.PluginError('Could not retrieve movie translation from trakt: %s' % str(e)) if not translation: log.warning('No translation data returned from trakt for movie %s.', entry['title']) else: log.verbose('Found `%s` translation for movie `%s`: %s', language, entry['movie_name'], translation[0]['title']) entry['title'] = translation[0]['title'] if entry.get('movie_year'): entry['title'] += ' (' + str(entry['movie_year']) + ')' entry['movie_name'] = translation[0]['title'] # Override the title if strip_dates is on. TODO: a better way? if self.config.get('strip_dates'): if list_type in ['show', 'movie']: entry['title'] = item[list_type]['title'] elif list_type == 'episode': entry['title'] = '{show[title]} S{episode[season]:02}E{episode[number]:02}'.format(**item) if item['episode']['title']: entry['title'] += ' {episode[title]}'.format(**item) if entry.isvalid(): if self.config.get('strip_dates'): # Remove year from end of name if present entry['title'] = split_title_year(entry['title'])[0] entries.append(entry) else: log.debug('Invalid entry created? %s', entry) self._items = entries return self._items
def estimate(self, entry): if not all(field in entry for field in ['series_name', 'series_season']): return series_name = entry['series_name'] season = entry['series_season'] episode_number = entry.get('series_episode') title, year_match = split_title_year(series_name) # This value should be added to input plugins to trigger a season lookuo season_pack = entry.get('season_pack_lookup') kwargs = { 'tvmaze_id': entry.get('tvmaze_id'), 'tvdb_id': entry.get('tvdb_id') or entry.get('trakt_series_tvdb_id'), 'tvrage_id': entry.get('tvrage_id') or entry.get('trakt_series_tvrage_id'), 'imdb_id': entry.get('imdb_id'), 'show_name': title, 'show_year': entry.get('trakt_series_year') or entry.get('year') or entry.get('imdb_year') or year_match, 'show_network': entry.get('network') or entry.get('trakt_series_network'), 'show_country': entry.get('country') or entry.get('trakt_series_country'), 'show_language': entry.get('language'), 'series_season': season, 'series_episode': episode_number, 'series_name': series_name, } api_tvmaze = plugin.get('api_tvmaze', self) if season_pack: lookup = api_tvmaze.season_lookup logger.debug('Searching api_tvmaze for season') else: logger.debug('Searching api_tvmaze for episode') lookup = api_tvmaze.episode_lookup for k, v in list(kwargs.items()): if v: logger.debug('{}: {}', k, v) entity_data = {'data_exists': True, 'entity_date': None} entity = {} try: entity = lookup(**kwargs) except LookupError as e: logger.debug(str(e)) entity_data['data_exists'] = False if entity and entity.airdate: logger.debug('received air-date: {}', entity.airdate) entity_data['entity_date'] = entity.airdate if entity_data['data_exists'] == False: # Make Lookup to series to see if failed because of no episode or no data lookup = api_tvmaze.series_lookup series = {} try: series = lookup(**kwargs) except LookupError as e: entity_data['data_exists'] = False if not series: logger.debug('No data in tvmaze for series: {}', series_name) entity_data['data_exists'] = False else: logger.debug( 'No information to episode, but series {} exists in tvmaze', series_name) entity_data['data_exists'] = True return entity_data
def filter_strip_year(name: str) -> str: return split_title_year(name).title
def submit(self, entries, remove=False): """Submits movies or episodes to trakt api.""" found = {} for entry in entries: if self.config['type'] in ['auto', 'shows', 'seasons', 'episodes'] and entry.get('series_name') is not None: show_name, show_year = split_title_year(entry['series_name']) show = {'title': show_name, 'ids': get_entry_ids(entry)} if show_year: show['year'] = show_year if self.config['type'] in ['auto', 'seasons', 'episodes'] and entry.get('series_season') is not None: season = {'number': entry['series_season']} if self.config['type'] in ['auto', 'episodes'] and entry.get('series_episode') is not None: season['episodes'] = [{'number': entry['series_episode']}] show['seasons'] = [season] if self.config['type'] in ['seasons', 'episodes'] and 'seasons' not in show: log.debug('Not submitting `%s`, no season found.' % entry['title']) continue if self.config['type'] == 'episodes' and 'episodes' not in show: log.debug('Not submitting `%s`, no episode number found.' % entry['title']) continue found.setdefault('shows', []).append(show) elif self.config['type'] in ['auto', 'movies']: movie = {'ids': get_entry_ids(entry)} if not movie['ids']: if entry.get('movie_name') is not None: movie['title'] = entry.get('movie_name') or entry.get('imdb_name') movie['year'] = entry.get('movie_year') or entry.get('imdb_year') else: log.debug('Not submitting `%s`, no movie name or id found.' % entry['title']) continue found.setdefault('movies', []).append(movie) if not (found.get('shows') or found.get('movies')): log.debug('Nothing to submit to trakt.') return if self.config['list'] in ['collection', 'watchlist', 'watched']: args = ('sync', 'history' if self.config['list'] == 'watched' else self.config['list']) else: args = ('users', self.config['username'], 'lists', make_list_slug(self.config['list']), 'items') if remove: args += ('remove',) url = get_api_url(args) log.debug('Submitting data to trakt.tv (%s): %s' % (url, found)) try: result = self.session.post(url, data=json.dumps(found), raise_status=False) except RequestException as e: log.error('Error submitting data to trakt.tv: %s' % e) return if 200 <= result.status_code < 300: action = 'deleted' if remove else 'added' res = result.json() # Default to 0 for all categories, even if trakt response didn't include them for cat in ('movies', 'shows', 'episodes', 'seasons'): res[action].setdefault(cat, 0) log.info('Successfully {0} to/from list {1}: {movies} movie(s), {shows} show(s), {episodes} episode(s), ' '{seasons} season(s).'.format(action, self.config['list'], **res[action])) for k, r in res['not_found'].items(): if r: log.debug('not found %s: %s' % (k, r)) # TODO: Improve messages about existing and unknown results # Mark the results expired if we added or removed anything if sum(res[action].values()) > 0: self.invalidate_cache() elif result.status_code == 404: log.error('List does not appear to exist on trakt: %s' % self.config['list']) elif result.status_code == 401: log.error('Authentication error: have you authorized Flexget on Trakt.tv?') log.debug('trakt response: ' + result.text) else: log.error('Unknown error submitting data to trakt.tv: %s' % result.text)
def filter_get_year(name: str) -> str: return split_title_year(name).year
def test_split_year_title_parenthesis_and_year(self): movie = 'The Human Centipede III (Final Sequence) (2015)' expected = 'The Human Centipede III (Final Sequence)', 2015 assert split_title_year(movie) == expected
def test_split_year_title_year_scene(self): movie = 'The.Matrix.1999' expected = 'The.Matrix.', 1999 assert split_title_year(movie) == expected
def test_split_year_title_year_parenthesis(self): movie = 'The Matrix (1999)' expected = 'The Matrix', 1999 assert split_title_year(movie) == expected
def items(self): if self._items is None: if self.config['list'] in ['collection', 'watched', 'trending', 'popular'] and self.config['type'] == 'auto': raise plugin.PluginError( '`type` cannot be `auto` for %s list.' % self.config['list'] ) endpoint = self.get_list_endpoint() log.verbose('Retrieving `%s` list `%s`', self.config['type'], self.config['list']) try: result = self.session.get(db.get_api_url(endpoint)) try: data = result.json() except ValueError: log.debug('Could not decode json from response: %s', result.text) raise plugin.PluginError('Error getting list from trakt.') current_page = int(result.headers.get('X-Pagination-Page', 1)) current_page_count = int(result.headers.get('X-Pagination-Page-Count', 1)) if current_page < current_page_count: # Pagination, gotta get it all, but we'll limit it to 1000 per page # but we'll have to start over from 0 data = [] limit = 1000 pagination_item_count = int(result.headers.get('X-Pagination-Item-Count', 0)) number_of_pages = math.ceil(pagination_item_count / limit) log.debug( 'Response is paginated. Number of items: %s, number of pages: %s', pagination_item_count, number_of_pages, ) page = int(result.headers.get('X-Pagination-Page')) while page <= number_of_pages: paginated_result = self.session.get( db.get_api_url(endpoint), params={'limit': limit, 'page': page} ) page += 1 try: data.extend(paginated_result.json()) except ValueError: log.debug( 'Could not decode json from response: %s', paginated_result.text ) raise plugin.PluginError('Error getting list from trakt.') except RequestException as e: raise plugin.PluginError('Could not retrieve list from trakt (%s)' % e) if not data: log.warning( 'No data returned from trakt for %s list %s.', self.config['type'], self.config['list'], ) return [] entries = [] list_type = (self.config['type']).rstrip('s') for item in data: if self.config['type'] == 'auto': list_type = item['type'] if self.config['list'] == 'popular': item = {list_type: item} # Collection and watched lists don't return 'type' along with the items (right now) if 'type' in item and item['type'] != list_type: log.debug( 'Skipping %s because it is not a %s', item[item['type']].get('title', 'unknown'), list_type, ) continue if list_type != 'episode' and not item[list_type]['title']: # Skip shows/movies with no title log.warning('Item in trakt list does not appear to have a title, skipping.') continue entry = Entry() if list_type == 'episode': entry['url'] = 'https://trakt.tv/shows/%s/seasons/%s/episodes/%s' % ( item['show']['ids']['slug'], item['episode']['season'], item['episode']['number'], ) else: entry['url'] = 'https://trakt.tv/%ss/%s' % ( list_type, item[list_type]['ids'].get('slug'), ) entry.update_using_map(field_maps[list_type], item) # get movie name translation language = self.config.get('language') if list_type == 'movie' and language: endpoint = ['movies', entry['trakt_movie_id'], 'translations', language] try: result = self.session.get(db.get_api_url(endpoint)) try: translation = result.json() except ValueError: raise plugin.PluginError( 'Error decoding movie translation from trakt: %s.' % result.text ) except RequestException as e: raise plugin.PluginError( 'Could not retrieve movie translation from trakt: %s' % str(e) ) if not translation: log.warning( 'No translation data returned from trakt for movie %s.', entry['title'] ) else: log.verbose( 'Found `%s` translation for movie `%s`: %s', language, entry['movie_name'], translation[0]['title'], ) entry['title'] = translation[0]['title'] if entry.get('movie_year'): entry['title'] += ' (' + str(entry['movie_year']) + ')' entry['movie_name'] = translation[0]['title'] # Override the title if strip_dates is on. TODO: a better way? if self.config.get('strip_dates'): if list_type in ['show', 'movie']: entry['title'] = item[list_type]['title'] elif list_type == 'episode': entry[ 'title' ] = '{show[title]} S{episode[season]:02}E{episode[number]:02}'.format( **item ) if item['episode']['title']: entry['title'] += ' {episode[title]}'.format(**item) if entry.isvalid(): if self.config.get('strip_dates'): # Remove year from end of name if present entry['title'] = split_title_year(entry['title'])[0] entries.append(entry) if self.config.get('limit') and len(entries) >= self.config.get('limit'): break else: log.debug('Invalid entry created? %s', entry) self._items = entries return self._items
def estimate(self, entry): if not all(field in entry for field in ['series_name', 'series_season']): return series_name = entry['series_name'] season = entry['series_season'] episode_number = entry.get('series_episode') title, year_match = split_title_year(series_name) # This value should be added to input plugins to trigger a season lookup season_pack = entry.get('season_pack_lookup') kwargs = { 'title': title, 'year': entry.get('trakt_series_year') or entry.get('year') or entry.get('imdb_year') or year_match, 'trakt_slug': entry.get('trakt_slug'), 'tmdb_id': entry.get('tmdb_id'), 'tvdb_id': entry.get('tvdb_id') or entry.get('trakt_series_tvdb_id'), 'imdb_id': entry.get('imdb_id'), 'tvrage_id': entry.get('tvrage_id') or entry.get('trakt_series_tvrage_id'), } api_trakt = plugin.get_plugin_by_name('api_trakt').instance log.debug('Searching api_trakt for series') for k, v in list(kwargs.items()): if v: log.debug('%s: %s', k, v) with Session(expire_on_commit=False) as session: try: trakt_series = api_trakt.lookup_series(session=session, **kwargs) if trakt_series is None: return trakt_season = trakt_series.get_season(season, session) if trakt_season is None: log.debug('%s doesn\'t have a season %s in trakt' % (series_name, season)) return datetime.max if season_pack: entity = trakt_season else: entity = trakt_series.get_episode(season, episode_number, session) if entity is None: log.debug( '%s doesn\'t have a season %s episode %s in trakt' % (series_name, season, episode_number)) return datetime.max except LookupError as e: log.debug(str(e)) return if entity and entity.first_aired: log.debug('received first-aired: %s', entity.first_aired) return entity.first_aired return
def get_items(self): """Iterator over etrieved itesms from the trakt api.""" if self.config['list'] in ['collection', 'watched', 'trending', 'popular'] and self.config['type'] == 'auto': raise plugin.PluginError( '`type` cannot be `auto` for %s list.' % self.config['list'] ) limit_per_page = 1000 endpoint = self.get_list_endpoint() list_type = (self.config['type']).rstrip('s') log.verbose('Retrieving `%s` list `%s`', self.config['type'], self.config['list']) try: page = 1 collecting_finished = False while not collecting_finished: result = self.session.get( db.get_api_url(endpoint), params={'limit': limit_per_page, 'page': page} ) page = int(result.headers.get('X-Pagination-Page', 1)) number_of_pages = int(result.headers.get('X-Pagination-Page-Count', 1)) if page == 2: # If there is more than one page (more than 1000 items) warn user they may want to limit log.verbose('There are a large number of items in trakt `%s` list. You may want to enable `limit`' ' plugin to reduce the amount of entries in this task.', self.config['list']) collecting_finished = page >= number_of_pages page += 1 try: trakt_items = result.json() except ValueError: log.debug( 'Could not decode json from response: %s', result.text ) raise plugin.PluginError('Error getting list from trakt.') if not trakt_items: log.warning( 'No data returned from trakt for %s list %s.', self.config['type'], self.config['list'], ) return for item in trakt_items: if self.config['type'] == 'auto': list_type = item['type'] if self.config['list'] == 'popular': item = {list_type: item} # Collection and watched lists don't return 'type' along with the items (right now) if 'type' in item and item['type'] != list_type: log.debug( 'Skipping %s because it is not a %s', item[item['type']].get('title', 'unknown'), list_type, ) continue if list_type != 'episode' and not item[list_type]['title']: # Skip shows/movies with no title log.warning('Item in trakt list does not appear to have a title, skipping.') continue entry = Entry() if list_type == 'episode': entry['url'] = 'https://trakt.tv/shows/%s/seasons/%s/episodes/%s' % ( item['show']['ids']['slug'], item['episode']['season'], item['episode']['number'], ) else: entry['url'] = 'https://trakt.tv/%ss/%s' % ( list_type, item[list_type]['ids'].get('slug'), ) entry.update_using_map(field_maps[list_type], item) # get movie name translation language = self.config.get('language') if list_type == 'movie' and language: endpoint = ['movies', entry['trakt_movie_id'], 'translations', language] try: result = self.session.get(db.get_api_url(endpoint)) try: translation = result.json() except ValueError: raise plugin.PluginError( 'Error decoding movie translation from trakt: %s.' % result.text ) except RequestException as e: raise plugin.PluginError( 'Could not retrieve movie translation from trakt: %s' % str(e) ) if not translation: log.warning( 'No translation data returned from trakt for movie %s.', entry['title'] ) else: log.verbose( 'Found `%s` translation for movie `%s`: %s', language, entry['movie_name'], translation[0]['title'], ) entry['title'] = translation[0]['title'] if entry.get('movie_year'): entry['title'] += ' (' + str(entry['movie_year']) + ')' entry['movie_name'] = translation[0]['title'] # Override the title if strip_dates is on. TODO: a better way? if self.config.get('strip_dates'): if list_type in ['show', 'movie']: entry['title'] = item[list_type]['title'] elif list_type == 'episode': entry[ 'title' ] = '{show[title]} S{episode[season]:02}E{episode[number]:02}'.format( **item ) if item['episode']['title']: entry['title'] += ' {episode[title]}'.format(**item) if entry.isvalid(): if self.config.get('strip_dates'): # Remove year from end of name if present entry['title'] = split_title_year(entry['title'])[0] yield entry else: log.debug('Invalid entry created? %s', entry) except RequestException as e: raise plugin.PluginError('Could not retrieve list from trakt (%s)' % e)
def items(self): if self._items is None: if self.config['list'] in ['collection', 'watched'] and self.config['type'] == 'auto': raise plugin.PluginError('`type` cannot be `auto` for %s list.' % self.config['list']) endpoint = self.get_list_endpoint() log.verbose('Retrieving `%s` list `%s`', self.config['type'], self.config['list']) try: result = self.session.get(get_api_url(endpoint)) try: data = result.json() except ValueError: log.debug('Could not decode json from response: %s', result.text) raise plugin.PluginError('Error getting list from trakt.') except RequestException as e: raise plugin.PluginError('Could not retrieve list from trakt (%s)' % e) if not data: log.warning('No data returned from trakt for %s list %s.', self.config['type'], self.config['list']) return [] entries = [] list_type = (self.config['type']).rstrip('s') for item in data: if self.config['type'] == 'auto': list_type = item['type'] # Collection and watched lists don't return 'type' along with the items (right now) if 'type' in item and item['type'] != list_type: log.debug('Skipping %s because it is not a %s', item[item['type']].get('title', 'unknown'), list_type) continue if list_type != 'episode' and not item[list_type]['title']: # Skip shows/movies with no title log.warning('Item in trakt list does not appear to have a title, skipping.') continue entry = Entry() if list_type == 'episode': entry['url'] = 'https://trakt.tv/shows/%s/seasons/%s/episodes/%s' % ( item['show']['ids']['slug'], item['episode']['season'], item['episode']['number']) else: entry['url'] = 'https://trakt.tv/%ss/%s' % (list_type, item[list_type]['ids'].get('slug')) entry.update_using_map(field_maps[list_type], item) # Override the title if strip_dates is on. TODO: a better way? if self.config.get('strip_dates'): if list_type in ['show', 'movie']: entry['title'] = item[list_type]['title'] elif list_type == 'episode': entry['title'] = '{show[title]} S{episode[season]:02}E{episode[number]:02}'.format(**item) if item['episode']['title']: entry['title'] += ' {episode[title]}'.format(**item) if entry.isvalid(): if self.config.get('strip_dates'): # Remove year from end of name if present entry['title'] = split_title_year(entry['title'])[0] entries.append(entry) else: log.debug('Invalid entry created? %s', entry) self._items = entries return self._items
def test_split_year_title_no_year(self): movie = 'The Matrix' expected = 'The Matrix', None assert split_title_year(movie) == expected
def submit(self, entries, remove=False): """Submits movies or episodes to trakt api.""" found = {} for entry in entries: if self.config['type'] in ['auto', 'shows', 'seasons', 'episodes' ] and entry.get('series_name'): show_name, show_year = split_title_year(entry['series_name']) show = {'title': show_name, 'ids': get_entry_ids(entry)} if show_year: show['year'] = show_year if self.config['type'] in [ 'auto', 'seasons', 'episodes' ] and entry.get('series_season') is not None: season = {'number': entry['series_season']} if self.config['type'] in [ 'auto', 'episodes' ] and entry.get('series_episode') is not None: season['episodes'] = [{ 'number': entry['series_episode'] }] show['seasons'] = [season] if self.config['type'] in ['seasons', 'episodes' ] and 'seasons' not in show: log.debug('Not submitting `%s`, no season found.' % entry['title']) continue if self.config['type'] == 'episodes' and 'episodes' not in show[ 'seasons'][0]: log.debug('Not submitting `%s`, no episode number found.' % entry['title']) continue found.setdefault('shows', []).append(show) elif self.config['type'] in ['auto', 'movies']: movie = {'ids': get_entry_ids(entry)} if not movie['ids']: if entry.get('movie_name') is not None: movie['title'] = entry.get('movie_name') or entry.get( 'imdb_name') movie['year'] = entry.get('movie_year') or entry.get( 'imdb_year') else: log.debug( 'Not submitting `%s`, no movie name or id found.' % entry['title']) continue found.setdefault('movies', []).append(movie) if not (found.get('shows') or found.get('movies')): log.debug('Nothing to submit to trakt.') return if self.config['list'] in ['collection', 'watchlist', 'watched']: args = ('sync', 'history' if self.config['list'] == 'watched' else self.config['list']) else: args = ('users', self.config['username'], 'lists', make_list_slug(self.config['list']), 'items') if remove: args += ('remove', ) url = get_api_url(args) log.debug('Submitting data to trakt.tv (%s): %s' % (url, found)) try: result = self.session.post(url, data=json.dumps(found), raise_status=False) except RequestException as e: log.error('Error submitting data to trakt.tv: %s' % e) return if 200 <= result.status_code < 300: action = 'deleted' if remove else 'added' res = result.json() # Default to 0 for all categories, even if trakt response didn't include them for cat in ('movies', 'shows', 'episodes', 'seasons'): res[action].setdefault(cat, 0) log.info( 'Successfully {0} to/from list {1}: {movies} movie(s), {shows} show(s), {episodes} episode(s), ' '{seasons} season(s).'.format(action, self.config['list'], **res[action])) for k, r in res['not_found'].items(): if r: log.debug('not found %s: %s' % (k, r)) # TODO: Improve messages about existing and unknown results # Mark the results expired if we added or removed anything if sum(res[action].values()) > 0: self.invalidate_cache() elif result.status_code == 404: log.error('List does not appear to exist on trakt: %s' % self.config['list']) elif result.status_code == 401: log.error( 'Authentication error: have you authorized Flexget on Trakt.tv?' ) log.debug('trakt response: ' + result.text) else: log.error('Unknown error submitting data to trakt.tv: %s' % result.text)
def test_split_year_title_year(self): movie = 'The Matrix 1999' expected = 'The Matrix', 1999 assert split_title_year(movie) == expected