def search(self, series): """Search tvmaze.com for the series name. :param series: the query for the series name :return: An ordered dict with the show searched for. In the format of OrderedDict{"series": [list of shows]} """ log.debug('Searching for show {0}', series) results = None # If search term is digit's only, store it and attempt to search by id. show_by_id = None try: if series.isdigit(): show_by_id = self._get_show_by_id(series, request_language=self.config['language']) results = self._show_search(series, request_language=self.config['language']) except IndexerShowNotFound: pass if not results and not show_by_id: raise IndexerShowNotFound( 'Tvmaze show search failed in getting a result for search term {search}'.format(search=series) ) mapped_results = [] if results: mapped_results = self._map_results(results, self.series_map, '|') # The search by id result, is already mapped. We can just add it to the array with results. if show_by_id: mapped_results.append(show_by_id['series']) return OrderedDict({'series': mapped_results})['series']
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})
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 not results: raise IndexerShowNotFound( 'Show search failed in getting a result with reason: Not found' ) return results
def _get_show_by_id(self, tvdb_id, request_language='de'): # pylint: disable=unused-argument """Retrieve Glotz show information by tvdb id. :param tvdb_id: The shows tvdb id :return: An ordered dict with the show searched for. """ results = None if tvdb_id: log.debug('Getting all show data for {0}', tvdb_id) try: results = self.glotz_api.get_show(tvdb_id=tvdb_id, language=request_language) except IDNotFound as error: 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 results: log.debug('Getting aliases for show {0}', tvdb_id) results.aliases = self.glotz_api.get_show_aliases(tvdb_id) if not results: log.debug('Getting show data for {0} on Glotz failed', tvdb_id) return mapped_results = self._map_results(results, self.series_map) return OrderedDict({'series': mapped_results})
def _get_series(self, series): """Search for the series name. If a custom_ui UI is configured, it uses this to select the correct series. If not, and interactive == True, ConsoleUI is used, if not BaseUI is used to select the first result. :param series: the query for the series name :return: A list of series mapped to a UI (for example: a BaseUI or custom_ui). """ all_series = self.search(series) if not all_series: log.debug('Series result returned zero') raise IndexerShowNotFound( 'Show search returned zero results (cannot find show on Indexer)' ) if not isinstance(all_series, list): all_series = [all_series] if self.config['custom_ui'] is not None: log.debug('Using custom UI: {0!r}', self.config['custom_ui']) custom_ui = self.config['custom_ui'] ui = custom_ui(config=self.config) else: if not self.config['interactive']: log.debug('Auto-selecting first search result using BaseUI') ui = BaseUI(config=self.config) else: log.debug('Interactively selecting show using ConsoleUI') ui = ConsoleUI(config=self.config) # pylint: disable=redefined-variable-type return ui.select_series(all_series)
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._map_results(results, self.series_map, '|') return OrderedDict({'series': mapped_results})
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
def _show_search(self, show, request_language='de'): """ Use the Glotz 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. As Glotz is primarily a German indexer, default is German :return: A list of Show objects. """ try: results = self.glotz_api.get_show_list(show, request_language) except ShowNotFound as error: # Use error.value because Glotz 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
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
def _show_search(self, series): """ Use the Imdb API to search for a show. :param series: The series name that's searched for as a string :return: A list of Show objects.series_map """ try: results = self.imdb_api.search_for_title(series) except LookupError as error: raise IndexerShowNotFound( 'Could not get any results searching for {series} using indexer Imdb. Cause: {cause!r}' .format(series=series, cause=error)) except (AttributeError, RequestException) as error: raise IndexerUnavailable( 'Could not get any results searching for {series} using indexer Imdb. Cause: {cause!r}' .format(series=series, cause=error)) if results: return results else: return None
def _parse_actors(self, imdb_id): """Get and parse actors using the get_title_credits route. Actors are retrieved using t['show name]['_actors']. Any key starting with an underscore has been processed (not the raw data from the indexer) """ log.debug('Getting actors for {0}', imdb_id) try: actors = self.imdb_api.get_title_credits( ImdbIdentifier(imdb_id).imdb_id) except LookupError as error: raise IndexerShowNotFound( 'Could not find show {imdb_id} using indexer Imdb. Cause: {cause!r}' .format(imdb_id=imdb_id, cause=error)) except (AttributeError, RequestException) as error: raise IndexerUnavailable( 'Could not get actors for show {imdb_id} using indexer Imdb. Cause: {cause!r}' .format(imdb_id=imdb_id, cause=error)) if not actors.get('credits') or not actors['credits'].get('cast'): return cur_actors = Actors() for order, cur_actor in enumerate(actors['credits']['cast'][:25]): save_actor = Actor() save_actor['id'] = cur_actor['id'].split('/')[-2] save_actor['image'] = cur_actor.get('image', {}).get('url', None) save_actor['name'] = cur_actor['name'] save_actor['role'] = cur_actor['characters'][0] if cur_actor.get( 'characters') else '' save_actor['sortorder'] = order cur_actors.append(save_actor) self._set_show_data(imdb_id, '_actors', cur_actors)
def _parse_images(self, imdb_id, language='en'): """Parse Show and Season posters. Any key starting with an underscore has been processed (not the raw data from the XML) This interface will be improved in future versions. Available sources: amazon, custom, getty, paidcustomer, presskit, userupload. Available types: behind_the_scenes, event, poster, product, production_art, publicity, still_frame """ log.debug('Getting show banners for {0}', imdb_id) try: images = self.imdb_api.get_title_images( ImdbIdentifier(imdb_id).imdb_id) except LookupError as error: raise IndexerShowNotFound( 'Could not find show {imdb_id} using indexer Imdb. Cause: {cause!r}' .format(imdb_id=imdb_id, cause=error)) except (AttributeError, RequestException) as error: raise IndexerUnavailable( 'Could not get images for show {imdb_id} using indexer Imdb. Cause: {cause!r}' .format(imdb_id=imdb_id, cause=error)) image_mapping = { 'poster': 'poster', 'production_art': 'fanart' } # Removed 'still_frame': 'fanart', thumb_height = 640 _images = {} try: for image in images.get('images', []): image_type = image_mapping.get(image.get('type')) if image_type not in ('poster', 'fanart'): continue image_type_thumb = image_type + '_thumb' if image_type not in _images: _images[image_type] = {} _images[image_type + '_thumb'] = {} # Store the images for each resolution available # Always provide a resolution or 'original'. resolution = '{0}x{1}'.format(image['width'], image['height']) thumb_width = int( (float(image['width']) / image['height']) * thumb_height) resolution_thumb = '{0}x{1}'.format(thumb_width, thumb_height) if resolution not in _images[image_type]: _images[image_type][resolution] = {} _images[image_type_thumb][resolution_thumb] = {} bid = image['id'].split('/')[-1] if image_type in ['season', 'seasonwide']: if int(image.sub_key ) not in _images[image_type][resolution]: _images[image_type][resolution][int( image.sub_key)] = {} if bid not in _images[image_type][resolution][int( image.sub_key)]: _images[image_type][resolution][int( image.sub_key)][bid] = {} base_path = _images[image_type_thumb][resolution][int( image.sub_key)][bid] else: if bid not in _images[image_type][resolution]: _images[image_type][resolution][bid] = {} _images[image_type_thumb][resolution_thumb][bid] = {} base_path = _images[image_type][resolution][bid] base_path_thumb = _images[image_type_thumb][ resolution_thumb][bid] base_path['bannertype'] = image_type base_path['bannertype2'] = resolution base_path['_bannerpath'] = image.get('url') base_path['bannerpath'] = image.get('url').split('/')[-1] base_path['languages'] = image.get('languages') base_path['source'] = image.get('source') base_path['id'] = bid base_path_thumb['bannertype'] = image_type_thumb base_path_thumb['bannertype2'] = resolution_thumb base_path_thumb['_bannerpath'] = image['url'].split( 'V1')[0] + 'V1_SY{0}_AL_.jpg'.format(thumb_height) base_path_thumb['bannerpath'] = image['url'].split('V1')[ 0] + 'V1_SY{0}_AL_.jpg'.format(thumb_height).split('/')[-1] base_path_thumb['id'] = bid except Exception as error: log.warning( 'Could not parse Poster for show id: {0}, with exception: {1!r}', imdb_id, error) return def _get_poster_thumb(thumbs): for bid in thumbs.values(): for image in bid.values(): return image.get('bannerpath') if _images.get('poster_thumb'): self._set_show_data(imdb_id, 'poster_thumb', _get_poster_thumb(_images.get('poster_thumb'))) self._save_images(imdb_id, _images, language=language) self._set_show_data(imdb_id, '_banners', _images)
def _get_show_by_id(self, imdb_id): # pylint: disable=unused-argument """Retrieve imdb show information by imdb id, or if no imdb id provided by passed external id. :param imdb_id: The shows imdb id :return: An ordered dict with the show searched for. """ results = None log.debug('Getting all show data for {0}', imdb_id) try: results = self.imdb_api.get_title(ImdbIdentifier(imdb_id).imdb_id) except LookupError as error: raise IndexerShowNotFound( 'Could not find show {imdb_id} using indexer Imdb. Cause: {cause!r}' .format(imdb_id=imdb_id, cause=error)) except (AttributeError, RequestException) as error: raise IndexerUnavailable( 'Could not find show {imdb_id} using indexer Imdb. Cause: {cause!r}' .format(imdb_id=imdb_id, cause=error)) if not results: return mapped_results = self._map_results(results, self.series_map) if not mapped_results: return try: # Get firstaired releases = self.imdb_api.get_title_releases( ImdbIdentifier(imdb_id).imdb_id) except LookupError as error: raise IndexerShowNotFound( 'Could not find show {imdb_id} using indexer Imdb. Cause: {cause!r}' .format(imdb_id=imdb_id, cause=error)) except (AttributeError, RequestException) as error: raise IndexerUnavailable( 'Could not get title releases for show {imdb_id} using indexer Imdb. Cause: {cause!r}' .format(imdb_id=imdb_id, cause=error)) if releases.get('releases'): first_released = sorted([r['date'] for r in releases['releases']])[0] mapped_results['firstaired'] = first_released try: companies = self.imdb_api.get_title_companies( ImdbIdentifier(imdb_id).imdb_id) # If there was a release check if it was distributed. if companies.get('distribution'): origins = self.imdb_api.get_title_versions( ImdbIdentifier(imdb_id).imdb_id)['origins'][0] released_in_regions = [ dist for dist in companies['distribution'] if dist.get('regions') and origins in dist['regions'] ] # Used item.get('startYear') because a startYear is not always available. first_release = sorted(released_in_regions, key=lambda x: x.get('startYear')) if first_release: mapped_results['network'] = first_release[0]['company'][ 'name'] except (AttributeError, LookupError, RequestException): log.info('No company data available for {0}, cant get a network', imdb_id) return OrderedDict({'series': mapped_results})