# -*- coding: utf-8 -*- # ------------------------------------------------------------ # tvdb # ------------------------------------------------------------ # Scraper para el site thetvdb.com usando API v2.1 # Utilizado para obtener datos de series para la videoteca # del addon y también Kodi. # ------------------------------------------------------------ from future import standard_library standard_library.install_aliases() #from builtins import str from future.builtins import object import urllib.request, urllib.error, urllib.parse import re from core import jsontools from core import scrapertools from core.item import InfoLabels from platformcode import config, logger from platformcode import platformtools HOST = "https://api.thetvdb.com" HOST_IMAGE = "http://thetvdb.com/banners/" TOKEN = config.get_setting("tvdb_token", default="") info_language = ["de", "en", "es", "fr", "it", "pt"] # from videolibrary.json DEFAULT_LANG = info_language[config.get_setting("info_language", "videolibrary")] DEFAULT_HEADERS = { 'Content-Type': 'application/json', 'Accept': 'application/json, application/vnd.thetvdb.v2.1.1', 'Accept-Language': DEFAULT_LANG, 'Authorization': 'Bearer ' + TOKEN, } # Traducciones - Inicio DICT_STATUS = {'Continuing': 'En emisión', 'Ended': 'Finalizada'} DICT_GENRE = { 'Action': 'Acción', 'Adventure': 'Aventura', 'Animation': 'Animación', 'Children': 'Niños', 'Comedy': 'Comedia', 'Crime': 'Crimen', 'Documentary': 'Documental', # 'Drama': 'Drama', 'Family': 'Familiar', 'Fantasy': 'Fantasía', 'Food': 'Comida', 'Game Show': 'Concurso', 'Home and Garden': 'Hogar y Jardín', # 'Horror': 'Horror', 'Mini-Series': 'Mini-Series', 'Mystery': 'Misterio', 'News': 'Noticias', # 'Reality': 'Telerrealidad', 'Romance': 'Romántico', 'Science-Fiction': 'Ciencia-Ficción', 'Soap': 'Telenovela', # 'Special Interest': 'Special Interest', 'Sport': 'Deporte', # 'Suspense': 'Suspense', 'Talk Show': 'Programa de Entrevistas', # 'Thriller': 'Thriller', 'Travel': 'Viaje', # 'Western': 'Western' } DICT_MPAA = {'TV-Y': 'Público pre-infantil: niños menores de 6 años', 'TV-Y7': 'Público infantil: desde 7 años', 'TV-G': 'Público general: sin supervisión familiar', 'TV-PG': 'Guía paterna: Supervisión paternal', 'TV-14': 'Mayores de 14 años', 'TV-MA': 'Mayores de 17 años'} # Traducciones - Fin otvdb_global = None def find_and_set_infoLabels(item): logger.info() # logger.info("item es %s" % item) p_dialog = None if not item.contentSeason: p_dialog = platformtools.dialog_progress_bg(config.get_localized_string(60296), config.get_localized_string(60293)) global otvdb_global tvdb_result = None title = item.contentSerieName # Si el titulo incluye el (año) se lo quitamos year = scrapertools.find_single_match(title, "^.+?\s*(\(\d{4}\))$") if year: title = title.replace(year, "").strip() item.infoLabels['year'] = year[1:-1] if not item.infoLabels.get("tvdb_id"): if not item.infoLabels.get("imdb_id"): otvdb_global = Tvdb(search=title, year=item.infoLabels['year']) else: otvdb_global = Tvdb(imdb_id=item.infoLabels.get("imdb_id")) elif not otvdb_global or otvdb_global.get_id() != item.infoLabels['tvdb_id']: otvdb_global = Tvdb(tvdb_id=item.infoLabels['tvdb_id']) if not item.contentSeason: p_dialog.update(50, config.get_localized_string(60296), config.get_localized_string(60295)) results, info_load = otvdb_global.get_list_results() logger.debug("results es %s" % results) if not item.contentSeason: p_dialog.update(100, config.get_localized_string(60296), config.get_localized_string(60297) % len(results)) p_dialog.close() if len(results) > 1: tvdb_result = platformtools.show_video_info(results, item=item, scraper=Tvdb, caption=config.get_localized_string(60298) % title) elif len(results) > 0: tvdb_result = results[0] # todo revisar if isinstance(item.infoLabels, InfoLabels): logger.debug("es instancia de infoLabels") infoLabels = item.infoLabels else: logger.debug("NO ES instancia de infoLabels") infoLabels = InfoLabels() if tvdb_result: infoLabels['tvdb_id'] = tvdb_result['id'] infoLabels['url_scraper'] = ["http://thetvdb.com/index.php?tab=series&id=%s" % infoLabels['tvdb_id']] if not info_load: if otvdb_global.get_id() != infoLabels['tvdb_id']: otvdb_global = Tvdb(tvdb_id=infoLabels['tvdb_id']) otvdb_global.get_images(infoLabels['tvdb_id'], image="poster") otvdb_global.get_images(infoLabels['tvdb_id'], image="fanart") otvdb_global.get_tvshow_cast(infoLabels['tvdb_id']) item.infoLabels = infoLabels set_infoLabels_item(item) return True else: item.infoLabels = infoLabels return False def set_infoLabels_item(item): """ Obtiene y fija (item.infoLabels) los datos extras de una serie, capitulo o pelicula. @param item: Objeto que representa un pelicula, serie o capitulo. El atributo infoLabels sera modificado incluyendo los datos extras localizados. @type item: Item """ global otvdb_global def __leer_datos(otvdb_aux): item.infoLabels = otvdb_aux.get_infoLabels(item.infoLabels) if 'infoLabels' in item and 'thumbnail' in item.infoLabels: item.thumbnail = item.infoLabels['thumbnail'] if 'infoLabels' in item and 'fanart' in item.infoLabels['fanart']: item.fanart = item.infoLabels['fanart'] if 'infoLabels' in item and 'season' in item.infoLabels: try: int_season = int(item.infoLabels['season']) except ValueError: logger.debug("El numero de temporada no es valido") item.contentType = item.infoLabels['mediatype'] return -1 * len(item.infoLabels) if not otvdb_global or \ (item.infoLabels['tvdb_id'] and otvdb_global.get_id() != item.infoLabels['tvdb_id']) \ or (otvdb_global.search_name and otvdb_global.search_name != item.infoLabels['tvshowtitle']): if item.infoLabels['tvdb_id']: otvdb_global = Tvdb(tvdb_id=item.infoLabels['tvdb_id']) else: otvdb_global = Tvdb(search=item.infoLabels['tvshowtitle']) __leer_datos(otvdb_global) if item.infoLabels['episode']: try: int_episode = int(item.infoLabels['episode']) except ValueError: logger.debug("El número de episodio (%s) no es valido" % repr(item.infoLabels['episode'])) item.contentType = item.infoLabels['mediatype'] return -1 * len(item.infoLabels) # Tenemos numero de temporada y numero de episodio validos... # ... buscar datos episodio item.infoLabels['mediatype'] = 'episode' lang = DEFAULT_LANG if otvdb_global.lang: lang = otvdb_global.lang page = 1 _id = None while not _id: list_episodes = otvdb_global.list_episodes.get(page) if not list_episodes: list_episodes = otvdb_global.get_list_episodes(otvdb_global.get_id(), page) import threading semaforo = threading.Semaphore(20) l_hilo = list() for e in list_episodes["data"]: t = threading.Thread(target=otvdb_global.get_episode_by_id, args=(e["id"], lang, semaforo)) t.start() l_hilo.append(t) # esperar q todos los hilos terminen for x in l_hilo: x.join() for e in list_episodes['data']: if e['airedSeason'] == int_season and e['airedEpisodeNumber'] == int_episode: _id = e['id'] break _next = list_episodes['links']['next'] if isinstance(_next, int): page = _next else: break data_episode = otvdb_global.get_info_episode(otvdb_global.get_id(), int_season, int_episode, lang, _id) # todo repasar valores que hay que insertar en infoLabels if data_episode: item.infoLabels['title'] = data_episode['episodeName'] # fix en casos que el campo desde la api era null--> None if data_episode["overview"] is not None: item.infoLabels['plot'] = data_episode["overview"] item.thumbnail = HOST_IMAGE + data_episode.get('filename', "") item.infoLabels["rating"] = data_episode.get("siteRating", "") item.infoLabels['director'] = ', '.join(sorted(data_episode.get('directors', []))) item.infoLabels['writer'] = ', '.join(sorted(data_episode.get("writers", []))) if data_episode["firstAired"]: item.infoLabels['premiered'] = data_episode["firstAired"].split("-")[2] + "/" + \ data_episode["firstAired"].split("-")[1] + "/" + \ data_episode["firstAired"].split("-")[0] item.infoLabels['aired'] = item.infoLabels['premiered'] guest_stars = data_episode.get("guestStars", []) l_castandrole = item.infoLabels.get("castandrole", []) l_castandrole.extend([(p, '') for p in guest_stars]) item.infoLabels['castandrole'] = l_castandrole # datos para nfo item.season_id = data_episode["airedSeasonID"] item.episode_id = data_episode["id"] return len(item.infoLabels) else: # Tenemos numero de temporada valido pero no numero de episodio... # ... buscar datos temporada item.infoLabels['mediatype'] = 'season' data_season = otvdb_global.get_images(otvdb_global.get_id(), "season", int_season) if data_season and 'image_season_%s' % int_season in data_season: item.thumbnail = HOST_IMAGE + data_season['image_season_%s' % int_season][0]['fileName'] return len(item.infoLabels) # Buscar... else: # Busquedas por ID... if (not otvdb_global or otvdb_global.get_id() != item.infoLabels['tvdb_id']) and item.infoLabels['tvdb_id']: otvdb_global = Tvdb(tvdb_id=item.infoLabels['tvdb_id']) elif not otvdb_global and item.infoLabels['imdb_id']: otvdb_global = Tvdb(imdb_id=item.infoLabels['imdb_id']) elif not otvdb_global and item.infoLabels['zap2it_id']: otvdb_global = Tvdb(zap2it_id=item.infoLabels['zap2it_id']) # No se ha podido buscar por ID... se hace por título if otvdb_global is None: otvdb_global = Tvdb(search=item.infoLabels['tvshowtitle']) if otvdb_global and otvdb_global.get_id(): __leer_datos(otvdb_global) # La busqueda ha encontrado un resultado valido return len(item.infoLabels) def get_nfo(item): """ Devuelve la información necesaria para que se scrapee el resultado en la videoteca de kodi, @param item: elemento que contiene los datos necesarios para generar la info @type item: Item @rtype: str @return: """ if "season" in item.infoLabels and "episode" in item.infoLabels: info_nfo = "http://thetvdb.com/?tab=episode&seriesid=%s&seasonid=%s&id=%s\n" \ % (item.infoLabels['tvdb_id'], item.season_id, item.episode_id) else: info_nfo = ', '.join(item.infoLabels['url_scraper']) + "\n" return info_nfo def completar_codigos(item): """ Si es necesario comprueba si existe el identificador de tmdb y sino existe trata de buscarlo @param item: tipo item @type item: Item """ if not item.infoLabels['tmdb_id']: listsources = [(item.infoLabels['tvdb_id'], "tvdb_id")] if item.infoLabels['imdb_id']: listsources.append((item.infoLabels['imdb_id'], "imdb_id")) from core.tmdb import Tmdb ob = Tmdb() for external_id, external_source in listsources: ob.search_by_id(id=external_id, source=external_source, tipo='tv') item.infoLabels['tmdb_id'] = ob.get_id() if item.infoLabels['tmdb_id']: url_scraper = "https://www.themoviedb.org/tv/%s" % item.infoLabels['tmdb_id'] item.infoLabels['url_scraper'].append(url_scraper) break class Tvdb(object): def __init__(self, **kwargs): self.__check_token() self.result = {} self.list_results = [] self.lang = "" self.search_name = kwargs['search'] = \ re.sub('\[\\\?(B|I|COLOR)\s?[^\]]*\]', '', kwargs.get('search', '')) self.list_episodes = {} self.episodes = {} if kwargs.get('tvdb_id', ''): # Busqueda por identificador tvdb self.__get_by_id(kwargs.get('tvdb_id', '')) if not self.list_results and config.get_setting("tvdb_retry_eng", "videolibrary"): from platformcode import platformtools platformtools.dialog_notification(config.get_localized_string(60299) % DEFAULT_LANG, config.get_localized_string(60302), sound=False) self.__get_by_id(kwargs.get('tvdb_id', ''), "en") self.lang = "en" elif self.search_name: # Busqueda por texto self.__search(kwargs.get('search', ''), kwargs.get('imdb_id', ''), kwargs.get('zap2it_id', '')) if not self.list_results and config.get_setting("tvdb_retry_eng", "videolibrary"): from platformcode import platformtools platformtools.dialog_notification(config.get_localized_string(60299) % DEFAULT_LANG, config.get_localized_string(60302)) self.__search(kwargs.get('search', ''), kwargs.get('imdb_id', ''), kwargs.get('zap2it_id', ''), "en") self.lang = "en" if not self.result: # No hay resultados de la busqueda if kwargs.get('tvdb_id', ''): buscando = kwargs.get('tvdb_id', '') else: buscando = kwargs.get('search', '') msg = config.get_localized_string(70266) % buscando logger.debug(msg) @classmethod def __check_token(cls): # logger.info() if TOKEN == "": cls.__login() else: # si la fecha no se corresponde con la actual llamamos a refresh_token, ya que el token expira en 24 horas from time import gmtime, strftime current_date = strftime("%Y-%m-%d", gmtime()) if config.get_setting("tvdb_token_date", "") != current_date: # si se ha renovado el token grabamos la nueva fecha if cls.__refresh_token(): config.set_setting("tvdb_token_date", current_date) @staticmethod def __login(): # logger.info() global TOKEN apikey = "106B699FDC04301C" url = HOST + "/login" params = {"apikey": apikey} try: req = urllib.request.Request(url, data=jsontools.dump(params), headers=DEFAULT_HEADERS) response = urllib.request.urlopen(req) html = response.read() response.close() except Exception as ex: message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args)) logger.error("error en: %s" % message) else: dict_html = jsontools.load(html) # logger.debug("dict_html %s" % dict_html) if "token" in dict_html: token = dict_html["token"] DEFAULT_HEADERS["Authorization"] = "Bearer " + token TOKEN = config.set_setting("tvdb_token", token) @classmethod def __refresh_token(cls): # logger.info() global TOKEN is_success = False url = HOST + "/refresh_token" try: req = urllib.request.Request(url, headers=DEFAULT_HEADERS) response = urllib.request.urlopen(req) html = response.read() response.close() except urllib.error.HTTPError as err: logger.error("err.code es %s" % err.code) # si hay error 401 es que el token se ha pasado de tiempo y tenemos que volver a llamar a login if err.code == 401: cls.__login() else: raise except Exception as ex: message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args)) logger.error("error en: %s" % message) else: dict_html = jsontools.load(html) # logger.error("tokencito %s" % dict_html) if "token" in dict_html: token = dict_html["token"] DEFAULT_HEADERS["Authorization"] = "Bearer " + token TOKEN = config.set_setting("tvdb_token", token) is_success = True return is_success def get_info_episode(self, _id, season=1, episode=1, lang=DEFAULT_LANG, id_episode=None): """ Devuelve los datos de un episodio. @param _id: identificador de la serie @type _id: str @param season: numero de temporada [por defecto = 1] @type season: int @param episode: numero de episodio [por defecto = 1] @type episode: int @param lang: codigo de idioma para buscar @type lang: str @param id_episode: codigo del episodio. @type id_episode: int @rtype: dict @return: "data": { "id": 0, "airedSeason": 0, "airedEpisodeNumber": 0, "episodeName": "string", "firstAired": "string", "guestStars": [ "string" ], "director": "string", # deprecated "directors": [ "string" ], "writers": [ "string" ], "overview": "string", "productionCode": "string", "showUrl": "string", "lastUpdated": 0, "dvdDiscid": "string", "dvdSeason": 0, "dvdEpisodeNumber": 0, "dvdChapter": 0, "absoluteNumber": 0, "filename": "string", "seriesId": "string", "lastUpdatedBy": "string", "airsAfterSeason": 0, "airsBeforeSeason": 0, "airsBeforeEpisode": 0, "thumbAuthor": 0, "thumbAdded": "string", "thumbWidth": "string", "thumbHeight": "string", "imdbId": "string", "siteRating": 0, "siteRatingCount": 0 }, "errors": { "invalidFilters": [ "string" ], "invalidLanguage": "string", "invalidQueryParams": [ "string" ] } """ logger.info() if id_episode and self.episodes.get(id_episode): return self.episodes.get(id_episode) params = {"airedSeason": "%s" % season, "airedEpisode": "%s" % episode} try: params = urllib.parse.urlencode(params) url = HOST + "/series/%s/episodes/query?%s" % (_id, params) DEFAULT_HEADERS["Accept-Language"] = lang logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS)) req = urllib.request.Request(url, headers=DEFAULT_HEADERS) response = urllib.request.urlopen(req) html = response.read() response.close() except Exception as ex: message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args)) logger.error("error en: %s" % message) else: dict_html = jsontools.load(html) if "data" in dict_html and "id" in dict_html["data"][0]: self.get_episode_by_id(dict_html["data"][0]["id"], lang) return dict_html["data"] def get_list_episodes(self, _id, page=1): """ Devuelve el listado de episodios de una serie. @param _id: identificador de la serie @type _id: str @param page: numero de pagina a buscar [por defecto = 1] @type page: int @rtype: dict @return: { "links": { "first": 0, "last": 0, "next": 0, "previous": 0 }, "data": [ { "absoluteNumber": 0, "airedEpisodeNumber": 0, "airedSeason": 0, "dvdEpisodeNumber": 0, "dvdSeason": 0, "episodeName": "string", "id": 0, "overview": "string", "firstAired": "string", "lastUpdated": 0 } ], "errors": { "invalidFilters": [ "string" ], "invalidLanguage": "string", "invalidQueryParams": [ "string" ] } } """ logger.info() try: url = HOST + "/series/%s/episodes?page=%s" % (_id, page) logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS)) req = urllib.request.Request(url, headers=DEFAULT_HEADERS) response = urllib.request.urlopen(req) html = response.read() response.close() except Exception as ex: message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args)) logger.error("error en: %s" % message) else: self.list_episodes[page] = jsontools.load(html) # logger.info("dict_html %s" % self.list_episodes) return self.list_episodes[page] def get_episode_by_id(self, _id, lang=DEFAULT_LANG, semaforo=None): """ Obtiene los datos de un episodio @param _id: identificador del episodio @type _id: str @param lang: código de idioma @param semaforo: semaforo para multihilos @type semaforo: threading.Semaphore @type lang: str @rtype: dict @return: { "data": { "id": 0, "airedSeason": 0, "airedEpisodeNumber": 0, "episodeName": "string", "firstAired": "string", "guestStars": [ "string" ], "director": "string", "directors": [ "string" ], "writers": [ "string" ], "overview": "string", "productionCode": "string", "showUrl": "string", "lastUpdated": 0, "dvdDiscid": "string", "dvdSeason": 0, "dvdEpisodeNumber": 0, "dvdChapter": 0, "absoluteNumber": 0, "filename": "string", "seriesId": "string", "lastUpdatedBy": "string", "airsAfterSeason": 0, "airsBeforeSeason": 0, "airsBeforeEpisode": 0, "thumbAuthor": 0, "thumbAdded": "string", "thumbWidth": "string", "thumbHeight": "string", "imdbId": "string", "siteRating": 0, "siteRatingCount": 0 }, "errors": { "invalidFilters": [ "string" ], "invalidLanguage": "string", "invalidQueryParams": [ "string" ] } } """ if semaforo: semaforo.acquire() logger.info() url = HOST + "/episodes/%s" % _id try: DEFAULT_HEADERS["Accept-Language"] = lang logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS)) req = urllib.request.Request(url, headers=DEFAULT_HEADERS) response = urllib.request.urlopen(req) html = response.read() response.close() except Exception as ex: # if isinstance(ex, urllib).HTTPError: logger.debug("code es %s " % ex.code) message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args)) logger.error("error en: %s" % message) else: dict_html = jsontools.load(html) dict_html = dict_html.pop("data") logger.info("dict_html %s" % dict_html) self.episodes[_id] = dict_html if semaforo: semaforo.release() def __search(self, name, imdb_id, zap2it_id, lang=DEFAULT_LANG): """ Busca una serie a través de una serie de parámetros. @param name: nombre a buscar @type name: str @param imdb_id: codigo identificativo de imdb @type imdb_id: str @param zap2it_id: codigo identificativo de zap2it @type zap2it_id: str @param lang: código de idioma @type lang: str data:{ "aliases": [ "string" ], "banner": "string", "firstAired": "string", "id": 0, "network": "string", "overview": "string", "seriesName": "string", "status": "string" } """ logger.info() try: params = {} if name: params["name"] = name elif imdb_id: params["imdbId"] = imdb_id elif zap2it_id: params["zap2itId"] = zap2it_id params = urllib.parse.urlencode(params) DEFAULT_HEADERS["Accept-Language"] = lang url = HOST + "/search/series?%s" % params logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS)) req = urllib.request.Request(url, headers=DEFAULT_HEADERS) response = urllib.request.urlopen(req) html = response.read() response.close() except Exception as ex: # if isinstance(ex, urllib.parse).HTTPError: logger.debug("code es %s " % ex.code) message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args)) logger.error("error en: %s" % message) else: dict_html = jsontools.load(html) if "errors" in dict_html and "invalidLanguage" in dict_html["errors"]: # no hay información en idioma por defecto return else: resultado = dict_html["data"] # todo revisar if len(resultado) > 1: index = 0 else: index = 0 logger.debug("resultado %s" % resultado) self.list_results = resultado self.result = resultado[index] def __get_by_id(self, _id, lang=DEFAULT_LANG, from_get_list=False): """ Obtiene los datos de una serie por identificador. @param _id: código de la serie @type _id: str @param lang: código de idioma @type lang: str @rtype: dict @return: { "data": { "id": 0, "seriesName": "string", "aliases": [ "string" ], "banner": "string", "seriesId": 0, "status": "string", "firstAired": "string", "network": "string", "networkId": "string", "runtime": "string", "genre": [ "string" ], "overview": "string", "lastUpdated": 0, "airsDayOfWeek": "string", "airsTime": "string", "rating": "string", "imdbId": "string", "zap2itId": "string", "added": "string", "siteRating": 0, "siteRatingCount": 0 }, "errors": { "invalidFilters": [ "string" ], "invalidLanguage": "string", "invalidQueryParams": [ "string" ] } } """ logger.info() resultado = {} url = HOST + "/series/%s" % _id try: DEFAULT_HEADERS["Accept-Language"] = lang req = urllib.request.Request(url, headers=DEFAULT_HEADERS) logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS)) response = urllib.request.urlopen(req) html = response.read() response.close() except Exception as ex: # if isinstance(ex, urllib).HTTPError: logger.debug("code es %s " % ex.code) message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args)) logger.error("error en: %s" % message) else: dict_html = jsontools.load(html) if "errors" in dict_html and "invalidLanguage" in dict_html["errors"]: return {} else: resultado1 = dict_html["data"] if not resultado1 and from_get_list: return self.__get_by_id(_id, "en") logger.debug("resultado %s" % dict_html) resultado2 = {"image_poster": [{'keyType': 'poster', 'fileName': 'posters/%s-1.jpg' % _id}]} resultado3 = {"image_fanart": [{'keyType': 'fanart', 'fileName': 'fanart/original/%s-1.jpg' % _id}]} resultado = resultado1.copy() resultado.update(resultado2) resultado.update(resultado3) logger.debug("resultado total %s" % resultado) self.list_results = [resultado] self.result = resultado return resultado def get_images(self, _id, image="poster", season=1, lang="en"): """ Obtiene un tipo de imagen para una serie para un idioma. @param _id: identificador de la serie @type _id: str @param image: codigo de busqueda, ["poster" (por defecto), "fanart", "season"] @type image: str @type season: numero de temporada @param lang: código de idioma para el que se busca @type lang: str @return: diccionario con el tipo de imagenes elegidas. @rtype: dict """ logger.info() if self.result.get('image_season_%s' % season): return self.result['image_season_%s' % season] params = {} if image == "poster": params["keyType"] = "poster" elif image == "fanart": params["keyType"] = "fanart" params["subKey"] = "graphical" elif image == "season": params["keyType"] = "season" params["subKey"] = "%s" % season image += "_%s" % season try: params = urllib.parse.urlencode(params) DEFAULT_HEADERS["Accept-Language"] = lang url = HOST + "/series/%s/images/query?%s" % (_id, params) logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS)) req = urllib.request.Request(url, headers=DEFAULT_HEADERS) response = urllib.request.urlopen(req) html = response.read() response.close() except Exception as ex: message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args)) logger.error("error en: %s" % message) return {} else: dict_html = jsontools.load(html) dict_html["image_" + image] = dict_html.pop("data") self.result.update(dict_html) return dict_html def get_tvshow_cast(self, _id, lang=DEFAULT_LANG): """ obtiene el casting de una serie @param _id: codigo de la serie @type _id: str @param lang: codigo idioma para buscar @type lang: str @return: diccionario con los actores @rtype: dict """ logger.info() url = HOST + "/series/%s/actors" % _id DEFAULT_HEADERS["Accept-Language"] = lang logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS)) req = urllib.request.Request(url, headers=DEFAULT_HEADERS) response = urllib.request.urlopen(req) html = response.read() response.close() dict_html = jsontools.load(html) dict_html["cast"] = dict_html.pop("data") self.result.update(dict_html) def get_id(self): """ @return: Devuelve el identificador Tvdb de la serie cargada o una cadena vacia en caso de que no hubiese nada cargado. Se puede utilizar este metodo para saber si una busqueda ha dado resultado o no. @rtype: str """ return str(self.result.get('id', "")) def get_list_results(self): """ Devuelve los resultados encontramos para una serie. @rtype: list @return: lista de resultados """ logger.info() list_results = [] # TODO revisar condicion # si tenemos un resultado y tiene seriesName, ya tenemos la info de la serie, no hace falta volver a buscar if len(self.list_results) == 1 and "seriesName" in self.result: list_results.append(self.result) info_load = True else: import threading semaforo = threading.Semaphore(20) l_hilo = list() r_list = list() def sub_thread(_id, i): semaforo.acquire() ret = self.__get_by_id(_id, DEFAULT_LANG, True) semaforo.release() r_list.append((ret, i)) for index, e in enumerate(self.list_results): t = threading.Thread(target=sub_thread, args=(e["id"], index)) t.start() l_hilo.append(t) for x in l_hilo: x.join() r_list.sort(key=lambda i: i[1]) list_results = [ii[0] for ii in r_list] info_load = False return list_results, info_load def get_infoLabels(self, infoLabels=None, origen=None): """ @param infoLabels: Informacion extra de la pelicula, serie, temporada o capitulo. @type infoLabels: dict @param origen: Diccionario origen de donde se obtiene los infoLabels, por omision self.result @type origen: dict @return: Devuelve la informacion extra obtenida del objeto actual. Si se paso el parametro infoLables, el valor devuelto sera el leido como parametro debidamente actualizado. @rtype: dict """ # TODO revisar if infoLabels: # logger.debug("es instancia de infoLabels") ret_infoLabels = InfoLabels(infoLabels) else: # logger.debug("NO ES instancia de infoLabels") ret_infoLabels = InfoLabels() # fix ret_infoLabels['mediatype'] = 'tvshow' # Iniciar listados l_castandrole = ret_infoLabels.get('castandrole', []) # logger.debug("self.result %s" % self.result) if not origen: origen = self.result # todo revisar # if 'credits' in origen.keys(): # dic_origen_credits = origen['credits'] # origen['credits_cast'] = dic_origen_credits.get('cast', []) # origen['credits_crew'] = dic_origen_credits.get('crew', []) # del origen['credits'] items = list(origen.items()) for k, v in items: if not v: continue if k == 'overview': ret_infoLabels['plot'] = v elif k == 'runtime': ret_infoLabels['duration'] = int(v) * 60 elif k == 'firstAired': ret_infoLabels['year'] = int(v[:4]) ret_infoLabels['premiered'] = v.split("-")[2] + "/" + v.split("-")[1] + "/" + v.split("-")[0] # todo revisar # elif k == 'original_title' or k == 'original_name': # ret_infoLabels['originaltitle'] = v elif k == 'siteRating': ret_infoLabels['rating'] = float(v) elif k == 'siteRatingCount': ret_infoLabels['votes'] = v elif k == 'status': # se traduce los estados de una serie ret_infoLabels['status'] = DICT_STATUS.get(v, v) # no soy partidario de poner la cadena como studio pero es como lo hace el scraper de manera genérica elif k == 'network': ret_infoLabels['studio'] = v elif k == 'image_poster': # obtenemos la primera imagen de la lista ret_infoLabels['thumbnail'] = HOST_IMAGE + v[0]['fileName'] elif k == 'image_fanart': # obtenemos la primera imagen de la lista ret_infoLabels['fanart'] = HOST_IMAGE + v[0]['fileName'] # # no disponemos de la imagen de fondo # elif k == 'banner': # ret_infoLabels['fanart'] = HOST_IMAGE + v elif k == 'id': ret_infoLabels['tvdb_id'] = v elif k == 'imdbId': ret_infoLabels['imdb_id'] = v # no se muestra # ret_infoLabels['code'] = v elif k in "rating": # traducimos la clasificación por edades (content rating system) ret_infoLabels['mpaa'] = DICT_MPAA.get(v, v) elif k in "genre": genre_list = "" for index, i in enumerate(v): if index > 0: genre_list += ", " # traducimos los generos genre_list += DICT_GENRE.get(i, i) ret_infoLabels['genre'] = genre_list elif k == 'seriesName': # or k == 'name' or k == 'title': # if len(origen.get('aliases', [])) > 0: # ret_infoLabels['title'] = v + " " + origen.get('aliases', [''])[0] # else: # ret_infoLabels['title'] = v # logger.info("el titulo es %s " % ret_infoLabels['title']) ret_infoLabels['title'] = v elif k == 'cast': dic_aux = dict((name, character) for (name, character) in l_castandrole) l_castandrole.extend([(p['name'], p['role']) for p in v if p['name'] not in list(dic_aux.keys())]) else: logger.debug("Atributos no añadidos: %s=%s" % (k, v)) pass # Ordenar las listas y convertirlas en str si es necesario if l_castandrole: ret_infoLabels['castandrole'] = l_castandrole logger.debug("ret_infoLabels %s" % ret_infoLabels) return ret_infoLabels