Example #1
0
    def _get_show_by_id(self, tmdb_id, request_language='en', extra_info=None):
        """Retrieve tmdb show information by tmdb id.

        :param tmdb_id: The show's tmdb id
        :param request_language: Language to get the show in
        :type request_language: string or unicode
        :extra_info: Extra details of the show to get (e.g. ['content_ratings', 'external_ids'])
        :type extra_info: list, tuple or None
        :return: An ordered dict with the show searched for.
        """
        if extra_info and isinstance(extra_info, (list, tuple)):
            extra_info = ','.join(extra_info)

        log.debug('Getting all show data for {0}', tmdb_id)
        try:
            results = self.tmdb.TV(tmdb_id).info(
                language='{0},null'.format(request_language),
                append_to_response=extra_info
            )
            if not results:
                return
        except RequestException as error:
            raise IndexerUnavailable('Show info retrieval failed using indexer TMDB. Cause: {cause!r}'.format(
                cause=error
            ))

        mapped_results = self._map_results(results, self.series_map, '|')

        return OrderedDict({'series': mapped_results})
Example #2
0
    def _get_show_by_id(self, tvdbv2_id, request_language='en'):  # pylint: disable=unused-argument
        """Retrieve tvdbv2 show information by tvdbv2 id, or if no tvdbv2 id provided by passed external id.

        :param tvdbv2_id: The shows tvdbv2 id
        :return: An ordered dict with the show searched for.
        """
        results = None
        if tvdbv2_id:
            log.debug('Getting all show data for {0}', tvdbv2_id)
            try:
                results = self.config['session'].series_api.series_id_get(
                    tvdbv2_id, accept_language=request_language)
            except ApiException as error:
                if error.status == 401:
                    raise IndexerAuthFailed(
                        'Authentication failed, possible bad API key. Reason: {reason} ({status})'
                        .format(reason=error.reason, status=error.status))
                if error.status == 404:
                    raise IndexerShowNotFound(
                        'Show search failed in getting a result with reason: {reason} ({status})'
                        .format(reason=error.reason, status=error.status))
                raise IndexerUnavailable(error.reason)

        if not results:
            return

        if not getattr(results.data, 'series_name', None):
            raise IndexerShowNotFoundInLanguage(
                'Missing attribute series_name, cant index in language: {0}'.
                format(request_language), request_language)

        mapped_results = self._object_to_dict(results, self.series_map, '|')
        return OrderedDict({'series': mapped_results})
Example #3
0
    def _show_search(self, show, request_language='en'):
        """Use TMDB API to search for a show.

        :param show: The show name that's searched for as a string
        :param request_language: Language in two letter code. TMDB fallsback to en itself.
        :return: A list of Show objects.
        """
        try:
            # get paginated pages
            page = 1
            last = 1
            results = []
            while page <= last:
                search_result = self.tmdb.Search().tv(query=show,
                                                      language=request_language,
                                                      page=page)
                last = search_result.get('total_pages', 0)
                results += search_result.get('results')
                page += 1
        except RequestException as error:
            raise IndexerUnavailable('Show search failed using indexer TMDB. Cause: {cause}'.format(cause=error))

        if results:
            return results
        else:
            return OrderedDict({'data': None})
Example #4
0
    def _get_show_by_id(self, tvmaze_id, request_language='en'):  # pylint: disable=unused-argument
        """
        Retrieve tvmaze show information by tvmaze id, or if no tvmaze id provided by passed external id.

        :param tvmaze_id: The shows tvmaze id
        :return: An ordered dict with the show searched for.
        """
        results = None
        if tvmaze_id:
            log.debug('Getting all show data for {0}', tvmaze_id)

            try:
                results = self.tvmaze_api.get_show(maze_id=tvmaze_id)
            except ShowNotFound as error:
                # Use error.value because TVMaze API exceptions may be utf-8 encoded when using __str__
                raise IndexerShowNotFound(
                    'Show search failed in getting a result with reason: {0}'.format(error.value)
                )
            except BaseError as error:
                raise IndexerUnavailable('Show search failed in getting a result with error: {0!r}'.format(error))

        if not results:
            return

        mapped_results = self._map_results(results, self.series_map)
        return OrderedDict({'series': mapped_results})
Example #5
0
    def get_last_updated_series(self,
                                from_time,
                                weeks=1,
                                filter_show_list=None):
        """Retrieve a list with updated shows.

        :param from_time: epoch timestamp, with the start date/time
        :param weeks: number of weeks to get updates for.
        :param filter_show_list: Optional list of show objects, to use for filtering the returned list.
        :returns: A list of show_id's.
        """
        total_updates = []
        updates = True

        count = 0
        try:
            while updates and count < weeks:
                updates = self.config['session'].updates_api.updated_query_get(
                    from_time).data
                if updates:
                    last_update_ts = max(x.last_updated for x in updates)
                    from_time = last_update_ts
                    total_updates += [int(_.id) for _ in updates]
                count += 1
        except ApiException as e:
            if e.status == 401:
                raise IndexerAuthFailed(
                    'Authentication failed, possible bad api key. reason: {reason} ({status})'
                    .format(reason=e.reason, status=e.status))
            raise IndexerUnavailable(
                'Error connecting to Tvdb api. Caused by: {0}'.format(
                    e.reason))
        except RequestException as e:
            raise IndexerUnavailable(
                'Error connecting to Tvdb api. Caused by: {0}'.format(
                    e.reason))

        if total_updates and filter_show_list:
            new_list = []
            for show in filter_show_list:
                if show.indexerid in total_updates:
                    new_list.append(show.indexerid)
            total_updates = new_list

        return total_updates
Example #6
0
    def __init__(self, *args, **kwargs):  # pylint: disable=too-many-locals,too-many-arguments
        """Tmdb api constructor."""
        super(Tmdb, self).__init__(*args, **kwargs)

        self.indexer = 4
        self.tmdb = tmdb
        self.tmdb.API_KEY = TMDB_API_KEY
        self.tmdb.REQUESTS_SESSION = self.config['session']
        self.tmdb_configuration = self.tmdb.Configuration()
        try:
            self.response = self.tmdb_configuration.info()
        except RequestException as e:
            raise IndexerUnavailable(
                'Indexer TMDB is unavailable at this time. Cause: {cause}'.
                format(cause=e))

        self.config['artwork_prefix'] = '{base_url}{image_size}{file_path}'

        # An api to indexer series/episode object mapping
        self.series_map = {
            'id': 'id',
            'name': 'seriesname',
            'original_name': 'aliasnames',
            'overview': 'overview',
            'air_date': 'firstaired',
            'first_air_date': 'firstaired',
            'backdrop_path': 'fanart',
            'url': 'show_url',
            'episode_number': 'episodenumber',
            'season_number': 'seasonnumber',
            'dvd_episode_number': 'dvd_episodenumber',
            'last_air_date': 'airs_dayofweek',
            'last_updated': 'lastupdated',
            'network_id': 'networkid',
            'vote_average': 'contentrating',
            'poster_path': 'poster',
            'genres': 'genre',
            'type': 'classification',
            'networks': 'network',
            'episode_run_time': 'runtime'
        }

        self.episodes_map = {
            'id': 'id',
            'name': 'episodename',
            'overview': 'overview',
            'air_date': 'firstaired',
            'episode_run_time': 'runtime',
            'episode_number': 'episodenumber',
            'season_number': 'seasonnumber',
            'vote_average': 'contentrating',
            'still_path': 'filename'
        }
Example #7
0
    def _get_show_by_id(self, tmdb_id, request_language='en'):  # pylint: disable=unused-argument
        """Retrieve tmdb show information by tmdb id.

        :param tmdb_id: The show's tmdb id
        :return: An ordered dict with the show searched for.
        """
        log.debug('Getting all show data for {0}', tmdb_id)
        try:
            results = self.tmdb.TV(tmdb_id).info(language='{0},null'.format(request_language))
            if not results:
                return
        except RequestException as error:
            raise IndexerUnavailable('Show info retrieval failed using indexer TMDB. Cause: {cause!r}'.format(
                cause=error
            ))

        mapped_results = self._map_results(results, self.series_map, '|')

        return OrderedDict({'series': mapped_results})
Example #8
0
    def _show_search(self, show, request_language='en'):
        """
        Use the TVMaze API to search for a show.

        :param show: The show name that's searched for as a string
        :param request_language: Language in two letter code. TVMaze fallsback to en itself.
        :return: A list of Show objects.
        """
        try:
            results = self.tvmaze_api.get_show_list(show)
        except ShowNotFound as error:
            # Use error.value because TVMaze API exceptions may be utf-8 encoded when using __str__
            raise IndexerShowNotFound(
                'Show search failed in getting a result with reason: {0}'.format(error.value)
            )
        except BaseError as error:
            raise IndexerUnavailable('Show search failed in getting a result with error: {0!r}'.format(error))

        return results
Example #9
0
    def _show_search(self, show, request_language='en'):
        """Use the pytvdbv2 API to search for a show.

        @param show: The show name that's searched for as a string
        @return: A list of Show objects.
        """
        try:
            results = self.config['session'].search_api.search_series_get(
                name=show, accept_language=request_language)
        except ApiException as error:
            if error.status == 401:
                raise IndexerAuthFailed(
                    'Authentication failed, possible bad API key. Reason: {reason} ({status})'
                    .format(reason=error.reason, status=error.status))
            if error.status == 404:
                raise IndexerShowNotFound(
                    'Show search failed in getting a result with reason: {reason} ({status})'
                    .format(reason=error.reason, status=error.status))
            raise IndexerUnavailable(error.reason)

        return results
Example #10
0
    def _query_series(self,
                      tvdb_id,
                      specials=False,
                      aired_season=None,
                      full_info=False):
        """Query against episodes for the given series.

        :param tvdb_id: tvdb series id.
        :param specials: enable/disable download of specials. Currently not used.
        :param aired_season: the episodes returned for a specific aired season.
        :param full_info: add full information to the episodes
        :return: An ordered dict of {'episode': [list of episode dicts]}
        """
        results = []
        if aired_season:
            aired_season = [
                aired_season
            ] if not isinstance(aired_season, list) else aired_season

        # Parse episode data
        log.debug('Getting all episodes of {0}', tvdb_id)

        # get paginated pages
        page = 1
        last = 1
        try:
            if aired_season:
                for season in aired_season:
                    page = 1
                    last = 1
                    while page <= last:
                        paged_episodes = self.config[
                            'session'].series_api.series_id_episodes_query_get(
                                tvdb_id,
                                page=page,
                                aired_season=season,
                                accept_language=self.config['language'])
                        results += paged_episodes.data
                        last = paged_episodes.links.last
                        page += 1
            else:
                while page <= last:
                    paged_episodes = self.config[
                        'session'].series_api.series_id_episodes_query_get(
                            tvdb_id,
                            page=page,
                            accept_language=self.config['language'])
                    results += paged_episodes.data
                    last = paged_episodes.links.last
                    page += 1

            if results and full_info:
                results = self._get_episodes_info(tvdb_id,
                                                  results,
                                                  season=aired_season)

        except ApiException as e:
            log.debug('Error trying to index the episodes')
            if e.status == 401:
                raise IndexerAuthFailed(
                    'Authentication failed, possible bad api key. reason: {reason} ({status})'
                    .format(reason=e.reason, status=e.status))
            raise IndexerShowIncomplete(
                'Show episode search exception, '
                'could not get any episodes. Did a {search_type} search. Exception: {e}'
                .format(search_type='full' if not aired_season else
                        'season {season}'.format(season=aired_season),
                        e=e.reason))
        except RequestException as error:
            raise IndexerUnavailable(
                'Error connecting to Tvdb api. Caused by: {error!r}'.format(
                    error=error))

        if not results:
            log.debug('Series results incomplete')
            raise IndexerShowIncomplete(
                'Show episode search returned incomplete results, '
                'could not get any episodes. Did a {search_type} search.'.
                format(search_type='full' if not aired_season else
                       'season {season}'.format(season=aired_season)))

        mapped_episodes = self._object_to_dict(results, self.series_map, '|')
        return OrderedDict({
            'episode':
            mapped_episodes
            if isinstance(mapped_episodes, list) else [mapped_episodes]
        })
Example #11
0
    def _query_series(self,
                      tvdb_id,
                      specials=False,
                      aired_season=None,
                      full_info=False):
        """Query against episodes for the given series.

        :param tvdb_id: tvdb series id.
        :param specials: enable/disable download of specials. Currently not used.
        :param aired_season: the episodes returned for a specific aired season.
        :param full_info: add full information to the episodes
        :return: An ordered dict of {'episode': [list of episode dicts]}
        """
        results = []
        if aired_season:
            aired_season = [
                aired_season
            ] if not isinstance(aired_season, list) else aired_season

        # Parse episode data
        log.debug('Getting all episodes of {0}', tvdb_id)

        # get paginated pages
        page = 1
        last = 1
        try:
            if aired_season:
                for season in aired_season:
                    page = 1
                    last = 1
                    while page <= last:
                        paged_episodes = self.config[
                            'session'].series_api.series_id_episodes_query_get(
                                tvdb_id,
                                page=page,
                                aired_season=season,
                                accept_language=self.config['language'])
                        results += paged_episodes.data
                        last = paged_episodes.links.last
                        page += 1
            else:
                while page <= last:
                    paged_episodes = self.config[
                        'session'].series_api.series_id_episodes_query_get(
                            tvdb_id,
                            page=page,
                            accept_language=self.config['language'])
                    results += paged_episodes.data
                    last = paged_episodes.links.last
                    page += 1

            if results and full_info:
                results = self._get_episodes_info(tvdb_id,
                                                  results,
                                                  season=aired_season)

        except ApiException as error:
            log.debug('Error trying to index the episodes')
            if error.status == 401:
                raise IndexerAuthFailed(
                    'Authentication failed, possible bad API key. Reason: {reason} ({status})'
                    .format(reason=error.reason, status=error.status))

            if error.status == 404 and not self.shows[tvdb_id]['firstaired']:
                log.info(
                    'Show {name} does not have any episodes yet, adding it anyway',
                    {'name': self.shows[tvdb_id]['seriesname']})
            else:
                raise IndexerUnavailable(
                    'Error connecting to TVDB API. Reason: {reason}'.format(
                        reason=error.reason))

        mapped_episodes = self._object_to_dict(results, self.series_map, '|')
        return OrderedDict({
            'episode':
            mapped_episodes
            if isinstance(mapped_episodes, list) else [mapped_episodes]
        })
Example #12
0
    def _parse_images(self, tmdb_id):
        """Parse images.

        This interface will be improved in future versions.
        """
        key_mapping = {'file_path': 'bannerpath', 'vote_count': 'ratingcount', 'vote_average': 'rating', 'id': 'id'}
        image_sizes = {'fanart': 'backdrop_sizes', 'poster': 'poster_sizes'}
        typecasts = {'rating': float, 'ratingcount': int}

        log.debug('Getting show banners for {series}', series=tmdb_id)
        _images = {}

        # Let's get the different type of images available for this series
        params = {'include_image_language': '{search_language},null'.format(search_language=self.config['language'])}

        try:
            images = self.tmdb.TV(tmdb_id).images(params=params)
        except RequestException as error:
            raise IndexerUnavailable('Error trying to get images. Cause: {cause}'.format(cause=error))

        bid = images['id']
        for image_type, images in viewitems({'poster': images['posters'], 'fanart': images['backdrops']}):
            try:
                if images and image_type not in _images:
                    _images[image_type] = {}

                for image in images:
                    bid += 1
                    image_mapped = self._map_results(image, key_mappings=key_mapping)

                    for size in self.tmdb_configuration.images.get(image_sizes[image_type]):
                        if size == 'original':
                            width = image_mapped['width']
                            height = image_mapped['height']
                        else:
                            width = int(size[1:])
                            height = int(round(width / float(image_mapped['aspect_ratio'])))
                        resolution = '{0}x{1}'.format(width, height)

                        if resolution not in _images[image_type]:
                            _images[image_type][resolution] = {}

                        if bid not in _images[image_type][resolution]:
                            _images[image_type][resolution][bid] = {}

                        for k, v in viewitems(image_mapped):
                            if k is None or v is None:
                                continue

                            try:
                                typecast = typecasts[k]
                            except KeyError:
                                pass
                            else:
                                v = typecast(v)

                            _images[image_type][resolution][bid][k] = v
                            if k.endswith('path'):
                                new_key = '_{0}'.format(k)
                                log.debug('Adding base url for image: {0}', v)
                                _images[image_type][resolution][bid][new_key] = self.config['artwork_prefix'].format(
                                    base_url=self.tmdb_configuration.images['base_url'],
                                    image_size=size,
                                    file_path=v)

                        if size != 'original':
                            _images[image_type][resolution][bid]['rating'] = 0

            except Exception as error:
                log.warning('Could not parse Poster for show id: {0}, with exception: {1!r}', tmdb_id, error)
                return False

        season_images = self._parse_season_images(tmdb_id)
        if season_images:
            _images.update(season_images)

        self._save_images(tmdb_id, _images)
        self._set_show_data(tmdb_id, '_banners', _images)