Beispiel #1
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))
            raise IndexerShowNotFound(
                'Show search failed in getting a result with reason: {0}'.
                format(error.reason))
        except RequestException as error:
            raise IndexerException(
                'Show search failed in getting a result with error: {0!r}'.
                format(error))

        if results:
            return results
        else:
            return OrderedDict({'data': None})
Beispiel #2
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 Exception as e:
            raise IndexerException('Show search failed in getting a result with error: {0!r}'.format(e))

        if results:
            return results
        else:
            return OrderedDict({'data': None})
Beispiel #3
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)
                    )
                raise IndexerShowNotFound(
                    'Show search failed in getting a result with reason: {reason} ({status})'.format(
                        reason=error.reason, status=error.status
                    )
                )
            except RequestException as error:
                raise IndexerException('Show search failed in getting a result with error: {0!r}'.format(error))

        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})
Beispiel #4
0
    def _get_series_season_updates(self, tmdb_id, start_date=None, end_date=None):
        """
        Retrieve all updates (show,season,episode) from TMDB.

        :return: A list of updated seasons for a show id.
        """
        results = []
        page = 1
        total_pages = 1

        try:
            while page <= total_pages:
                # Requesting for the changes on a specific showid, will result in json with changes per season.
                updates = self.tmdb.TV(tmdb_id).changes(start_date=start_date, end_date=end_date)
                if updates and updates.get('changes'):
                    for items in [update['items'] for update in updates['changes'] if update['key'] == 'season']:
                        for season in items:
                            results += [season['value']['season_number']]
                    total_pages = updates.get('total_pages', 0)
                page += 1
        except RequestException as error:
            raise IndexerException('Could not get latest series season updates for series {series}. Cause: {cause}'.format(
                series=tmdb_id, cause=error
            ))

        return set(results)
Beispiel #5
0
    def get_id_by_external(self, **kwargs):
        """Search tmdb for a show, using an external id.

        Accepts as kwargs, so you'l need to add the externals as key/values.
        :param tvrage_id: The tvrage id.
        :param tvdb_id: The tvdb id.
        :param imdb_id: An imdb id (inc. tt).
        :returns: A dict with externals, including the tvmaze id.
        """
        try:
            wanted_externals = ['tvdb_id', 'imdb_id', 'tvrage_id']
            for external_id in wanted_externals:
                if kwargs.get(external_id):
                    result = self.tmdb.Find(kwargs.get(external_id)).info(**{'external_source': external_id})
                    if result.get('tv_results') and result['tv_results'][0]:
                        # Get the external id's for the passed shows id.
                        externals = self.tmdb.TV(result['tv_results'][0]['id']).external_ids()
                        externals = {tmdb_external_id: external_value
                                     for tmdb_external_id, external_value
                                     in viewitems(externals)
                                     if external_value and tmdb_external_id in wanted_externals}
                        externals['tmdb_id'] = result['tv_results'][0]['id']
                        return externals
            return {}
        except RequestException as error:
            raise IndexerException("Could not get external id's. Cause: {cause}".format(cause=error))
Beispiel #6
0
    def _parse_actors(self, tmdb_id):
        """Parse actors XML."""
        log.debug('Getting actors for {0}', tmdb_id)

        # TMDB also support passing language here as a param.
        try:
            credits = self.tmdb.TV(tmdb_id).credits(language=self.config['language'])  # pylint: disable=W0622
        except RequestException as error:
            raise IndexerException('Could not get actors. Cause: {cause}'.format(cause=error))

        if not credits or not credits.get('cast'):
            log.debug('Actors result returned zero')
            return

        cur_actors = Actors()
        for cur_actor in credits.get('cast'):
            new_actor = Actor()
            new_actor['id'] = cur_actor['credit_id']
            new_actor['image'] = \
                '{base_url}{image_size}{profile_path}'.format(base_url=self.tmdb_configuration.images['base_url'],
                                                              image_size='original',
                                                              profile_path=cur_actor['profile_path'])
            new_actor['name'] = cur_actor['name']
            new_actor['role'] = cur_actor['character']
            new_actor['sortorder'] = cur_actor['order']
            cur_actors.append(new_actor)
        self._set_show_data(tmdb_id, '_actors', cur_actors)
Beispiel #7
0
    def _get_episodes(self, tvmaze_id, specials=False, aired_season=None):  # pylint: disable=unused-argument
        """
        Get all the episodes for a show by tvmaze id.

        :param tvmaze_id: Series tvmaze id.
        :return: An ordered dict with the show searched for. In the format of OrderedDict{"episode": [list of episodes]}
        """
        # Parse episode data
        log.debug('Getting all episodes of {0}', tvmaze_id)
        try:
            results = self.tvmaze_api.episode_list(tvmaze_id, specials=specials)
        except IDNotFound:
            log.debug('Episode search did not return any results.')
            return False
        except BaseError as e:
            raise IndexerException('Show episodes search failed in getting a result with error: {0!r}'.format(e))

        episodes = self._map_results(results, self.series_map)

        if not episodes:
            return False

        if not isinstance(episodes, list):
            episodes = [episodes]

        for cur_ep in episodes:
            if self.config['dvdorder']:
                log.debug('Using DVD ordering.')
                use_dvd = cur_ep.get('dvd_season') is not None and cur_ep.get('dvd_episodenumber') is not None
            else:
                use_dvd = False

            if use_dvd:
                seasnum, epno = cur_ep.get('dvd_season'), cur_ep.get('dvd_episodenumber')
            else:
                seasnum, epno = cur_ep.get('seasonnumber'), cur_ep.get('episodenumber')
                if self.config['dvdorder']:
                    log.warning('No DVD order for episode (season: {0}, episode: {1}). '
                                'Falling back to non-DVD order. '
                                'Please consider disabling DVD order for the show with TVmaze ID: {2}',
                                seasnum, epno, tvmaze_id)

            if seasnum is None or epno is None:
                log.warning('An episode has incomplete season/episode number (season: {0!r}, episode: {1!r})', seasnum, epno)
                continue  # Skip to next episode

            seas_no = int(seasnum)
            ep_no = int(epno)

            for k, v in viewitems(cur_ep):
                k = k.lower()

                if v is not None:
                    if k == 'image_medium':
                        self._set_item(tvmaze_id, seas_no, ep_no, 'filename', v)
                self._set_item(tvmaze_id, seas_no, ep_no, k, v)
Beispiel #8
0
    def _get_all_updates(self, start_date=None, end_date=None):
        """Retrieve all updates (show,season,episode) from TMDB."""
        results = []
        page = 1
        total_pages = 1

        try:
            while page <= total_pages:
                updates = self.tmdb.Changes().tv(start_date=start_date, end_date=end_date, page=page)
                if not updates or not updates.get('results'):
                    break
                results += [_.get('id') for _ in updates.get('results')]
                total_pages = updates.get('total_pages')
                page += 1
        except RequestException as error:
            raise IndexerException('Could not get latest updates. Cause: {cause}'.format(
                cause=error
            ))

        return set(results)
Beispiel #9
0
    def _show_search(self, show, request_language='en'):
        """
        Uses 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:
            raise IndexerShowNotFound(
                'Show search failed in getting a result with reason: {0}'.
                format(error))
        except BaseError as error:
            raise IndexerException(
                'Show search failed in getting a result with error: {0!r}'.
                format(error))

        if results:
            return results
        else:
            return None
Beispiel #10
0
    def _get_episodes(self, tmdb_id, specials=False, aired_season=None):  # pylint: disable=unused-argument
        """Get all the episodes for a show by TMDB id.

        :param tmdb_id: Series tmdb id.
        :return: An ordered dict with the show searched for. In the format of OrderedDict{"episode": [list of episodes]}
        """
        results = []
        if aired_season:
            aired_season = [aired_season] if not isinstance(aired_season, list) else aired_season
        else:
            if tmdb_id not in self.shows or not self.shows[tmdb_id].data.get('seasons'):
                self.config['episodes_enabled'] = False  # Don't want to get episodes, as where already doing that.
                self._get_show_data(tmdb_id)  # Get show data, with the list of seasons
            aired_season = [season['season_number'] for season in self.shows[tmdb_id].data.get('seasons', [])]

        if not aired_season:
            log.debug('Series does not have any seasons added on indexer TMDB.')
            return

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

        # get episodes for each season
        for season in aired_season:
            try:
                season_info = self.tmdb.TV_Seasons(tmdb_id, season).info(language=self.config['language'])
                results += season_info['episodes']
            except RequestException as error:
                raise IndexerException(
                    'Could not get episodes for series {series} using indexer TMDB. Cause: {cause}'.format(
                        series=tmdb_id, cause=error
                    )
                )

        if not results:
            log.debug('Series does not have any episodes added on indexer TMDB.')
            return

        mapped_episodes = self._map_results(results, self.episodes_map, '|')
        episode_data = OrderedDict({'episode': mapped_episodes})

        if 'episode' not in episode_data:
            return False

        episodes = episode_data['episode']
        if not isinstance(episodes, list):
            episodes = [episodes]

        for cur_ep in episodes:
            if self.config['dvdorder']:
                log.debug('Using DVD ordering.')
                use_dvd = cur_ep.get('dvd_season') is not None and cur_ep.get('dvd_episodenumber') is not None
            else:
                use_dvd = False

            if use_dvd:
                seasnum, epno = cur_ep.get('dvd_season'), cur_ep.get('dvd_episodenumber')
            else:
                seasnum, epno = cur_ep.get('seasonnumber'), cur_ep.get('episodenumber')
                if self.config['dvdorder']:
                    log.warning(
                        'No DVD order available for episode (season: {0}, episode: {1}). '
                        'Falling back to non-DVD order. '
                        'Please consider disabling DVD order for the show with TMDB ID: {2}',
                        seasnum, epno, tmdb_id
                    )

            if seasnum is None or epno is None:
                log.warning('Invalid episode numbering (season: {0!r}, episode: {1!r})', seasnum, epno)
                continue  # Skip to next episode

            seas_no = int(seasnum)
            ep_no = int(epno)

            image_width = {'fanart': 'w1280', 'poster': 'w780', 'filename': 'w300'}
            for k, v in viewitems(cur_ep):
                k = k.lower()

                if v is not None:
                    if k in ['filename', 'poster', 'fanart']:
                        # I'm using the default 'original' quality. But you could also check tmdb_configuration,
                        # for the available image sizes.
                        v = self.config['artwork_prefix'].format(base_url=self.tmdb_configuration.images['base_url'],
                                                                 image_size=image_width[k],
                                                                 file_path=v)
                self._set_item(tmdb_id, seas_no, ep_no, k, v)
Beispiel #11
0
    def _get_show_data(self, tmdb_id, language='en'):
        """Take a series ID, gets the epInfo URL and parses the TMDB json response.

        into the shows dict in layout:
        shows[series_id][season_number][episode_number]
        """
        if self.config['language'] is None:
            log.debug('Config language is none, using show language')
            if language is None:
                raise IndexerError("config['language'] was None, this should not happen")
            get_show_in_language = language
        else:
            log.debug('Configured language {0} override show language of {1}',
                      self.config['language'], language)
            get_show_in_language = self.config['language']

        # Parse show information
        log.debug('Getting all series data for {0}', tmdb_id)

        # Parse show information
        series_info = self._get_show_by_id(tmdb_id, request_language=get_show_in_language)

        if not series_info:
            log.debug('Series result returned zero')
            raise IndexerError('Series result returned zero')

        # Get MPAA rating if available
        try:
            content_ratings = self.tmdb.TV(tmdb_id).content_ratings()
            if content_ratings and content_ratings.get('results'):
                mpaa_rating = next((r['rating'] for r in content_ratings['results']
                                    if r['iso_3166_1'].upper() == 'US'), None)
                if mpaa_rating:
                    self._set_show_data(tmdb_id, 'contentrating', mpaa_rating)
        except RequestException as error:
            raise IndexerException('Could not get series data for series {series}. Cause: {cause}'.format(
                series=tmdb_id, cause=error
            ))

        # get series data / add the base_url to the image urls
        # Create a key/value dict, to map the image type to a default image width.
        # possitlbe widths can also be retrieved from self.configuration.images['poster_sizes'] and
        # self.configuration.images['still_sizes']
        image_width = {'fanart': 'w1280', 'poster': 'w500'}

        for k, v in viewitems(series_info['series']):
            if v is not None:
                if k in ['fanart', 'banner', 'poster']:
                    v = self.config['artwork_prefix'].format(base_url=self.tmdb_configuration.images['base_url'],
                                                             image_size=image_width[k],
                                                             file_path=v)

            self._set_show_data(tmdb_id, k, v)

        # Get external ids.
        # As the external id's are not part of the shows default response, we need to make an additional call for it.
        try:
            external_ids = self.tmdb.TV(tmdb_id).external_ids()
            self._set_show_data(tmdb_id, 'externals', external_ids)
        except RequestException as error:
            raise IndexerException("Could not get external id's for series {series} Cause: {cause}".format(
                series=tmdb_id, cause=error
            ))

        # get episode data
        if self.config['episodes_enabled']:
            self._get_episodes(tmdb_id, specials=False, aired_season=None)

        # Parse banners
        if self.config['banners_enabled']:
            self._parse_images(tmdb_id)

        # Parse actors
        if self.config['actors_enabled']:
            self._parse_actors(tmdb_id)

        return True