1048 lines
38 KiB
Python
1048 lines
38 KiB
Python
# -*- coding: utf-8 -*-
|
||
# ------------------------------------------------------------
|
||
# tvdb
|
||
# ------------------------------------------------------------
|
||
# Scraper for thetvdb.com using API v2.1
|
||
# Used to obtain series data for the video library
|
||
# ------------------------------------------------------------
|
||
|
||
import sys
|
||
if sys.version_info[0] >= 3: PY3 = True
|
||
else: PY3 = False
|
||
|
||
from future import standard_library
|
||
standard_library.install_aliases()
|
||
from future.builtins import object
|
||
|
||
import urllib.request, urllib.error, urllib.parse
|
||
|
||
import re, requests
|
||
|
||
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.debug()
|
||
# from core.support import dbg;dbg()
|
||
# logger.debug("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
|
||
# If the title includes the (year) we will remove it
|
||
year = scrapertools.find_single_match(title, r"^.+?\s*(\(\d{4}\))$")
|
||
if year:
|
||
title = title.replace(year, "").strip()
|
||
item.infoLabels['year'] = year[1:-1]
|
||
|
||
if item.infoLabels.get("tvdb_id", '') in ['', 'None']:
|
||
if item.infoLabels['year']:
|
||
otvdb_global = Tvdb(search=title, year=item.infoLabels['year'])
|
||
elif item.infoLabels.get("imdb_id"):
|
||
otvdb_global = Tvdb(imdb_id=item.infoLabels.get("imdb_id"))
|
||
else:
|
||
otvdb_global = Tvdb(search=title)
|
||
|
||
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) + '\n' + config.get_localized_string(60295))
|
||
results, info_load = otvdb_global.get_list_results()
|
||
logger.debug("results: %s" % results)
|
||
|
||
if not item.contentSeason:
|
||
p_dialog.update(100, config.get_localized_string(60296) + '\n' + 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)
|
||
# if not tvdb_result:
|
||
# res = platformtools.dialog_info(item, 'tvdb')
|
||
# if not res.exit: return find_and_set_infoLabels(res)
|
||
elif len(results) > 0:
|
||
tvdb_result = results[0]
|
||
|
||
# else:
|
||
# res = platformtools.dialog_info(item, 'tvdb')
|
||
# if not res.exit: return find_and_set_infoLabels(res)
|
||
|
||
# todo revisar
|
||
if isinstance(item.infoLabels, InfoLabels):
|
||
logger.debug("is an instance of infoLabels")
|
||
infoLabels = item.infoLabels
|
||
else:
|
||
logger.debug("NOT an instance of 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):
|
||
"""
|
||
Gets and sets (item.infoLabels) the extra data of a series, chapter or movie.
|
||
@param item: Object that represents a movie, series or chapter. The infoLabels attribute will be modified including the extra localized data.
|
||
@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 and item.contentType != 'tvshow':
|
||
try:
|
||
int_season = int(item.infoLabels['season'])
|
||
except ValueError:
|
||
logger.debug("The season number is not valid")
|
||
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("The episode number (%s) is not valid" % repr(item.infoLabels['episode']))
|
||
item.contentType = item.infoLabels['mediatype']
|
||
return -1 * len(item.infoLabels)
|
||
|
||
# We have a valid season number and episode number ...
|
||
# ... search episode data
|
||
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)
|
||
|
||
# wait for all the threads to end
|
||
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)
|
||
|
||
# all go over values to insert into 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
|
||
|
||
# data for nfo
|
||
item.season_id = data_episode["airedSeasonID"]
|
||
item.episode_id = data_episode["id"]
|
||
|
||
return len(item.infoLabels)
|
||
|
||
else:
|
||
# We have a valid season number but no episode number ...
|
||
# ... search season data
|
||
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)
|
||
|
||
# Search...
|
||
else:
|
||
# Search by 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'])
|
||
|
||
# Unable to search by ID ... done by title
|
||
if otvdb_global is None:
|
||
otvdb_global = Tvdb(search=item.infoLabels['tvshowtitle'])
|
||
|
||
if otvdb_global and otvdb_global.get_id():
|
||
__leer_datos(otvdb_global)
|
||
# The search has found a valid result
|
||
return len(item.infoLabels)
|
||
|
||
|
||
def get_nfo(item, search_groups=False):
|
||
"""
|
||
Returns the information necessary for the result to be scraped into the kodi video library,
|
||
|
||
@param item: element that contains the data necessary to generate the 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):
|
||
"""
|
||
If necessary, check if the tmdb identifier exists and if it does not exist try to find it
|
||
@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(r'\[\\\?(B|I|COLOR)\s?[^\]]*\]', '', kwargs.get('search', ''))
|
||
self.list_episodes = {}
|
||
self.episodes = {}
|
||
|
||
if kwargs.get('tvdb_id', ''):
|
||
# Search by tvdb identifier
|
||
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 by text
|
||
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 search results
|
||
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.debug()
|
||
if TOKEN == "":
|
||
cls.__login()
|
||
else:
|
||
# if the date does not correspond to the current one we call refresh_token, since the token expires in 24 hours
|
||
from time import gmtime, strftime
|
||
current_date = strftime("%Y-%m-%d", gmtime())
|
||
|
||
if config.get_setting("tvdb_token_date", "") != current_date:
|
||
# if the token has been renewed we save the new date
|
||
if cls.__refresh_token():
|
||
config.set_setting("tvdb_token_date", current_date)
|
||
|
||
@staticmethod
|
||
def __login():
|
||
# logger.debug()
|
||
global TOKEN
|
||
|
||
apikey = "106B699FDC04301C"
|
||
|
||
url = HOST + "/login"
|
||
params = {"apikey": apikey}
|
||
if PY3: params = jsontools.dump(params).encode()
|
||
else: params = jsontools.dump(params)
|
||
|
||
try:
|
||
dict_html = requests.post(url, data=params, headers=DEFAULT_HEADERS).json()
|
||
|
||
except Exception as ex:
|
||
message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args))
|
||
logger.error("error: %s" % message)
|
||
|
||
else:
|
||
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.debug()
|
||
global TOKEN
|
||
is_success = False
|
||
|
||
url = HOST + "/refresh_token"
|
||
try:
|
||
req = requests.get(url, headers=DEFAULT_HEADERS)
|
||
|
||
|
||
except req as err:
|
||
logger.error("err.code %s" % err.status_code)
|
||
# if there is error 401 it is that the token has passed the time and we have to call login again
|
||
if err.status_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: %s" % message)
|
||
|
||
else:
|
||
dict_html = req.json()
|
||
# 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
|
||
else:
|
||
cls.__login()
|
||
|
||
return is_success
|
||
|
||
def get_info_episode(self, _id, season=1, episode=1, lang=DEFAULT_LANG, id_episode=None):
|
||
"""
|
||
Returns the data of an episode.
|
||
@param _id: series identifier
|
||
@type _id: str
|
||
@param season: season number [default = 1]
|
||
@type season: int
|
||
@param episode: episode number [default = 1]
|
||
@type episode: int
|
||
@param lang: language code to search
|
||
@type lang: str
|
||
@param id_episode: episode code.
|
||
@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.debug()
|
||
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 = requests.get(url, headers=DEFAULT_HEADERS)
|
||
|
||
except Exception as ex:
|
||
message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args))
|
||
logger.error("error: %s" % message)
|
||
|
||
else:
|
||
dict_html = req.json()
|
||
if 'Error' in dict_html:
|
||
logger.debug("code %s " % dict_html['Error'])
|
||
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):
|
||
"""
|
||
Returns the list of episodes of a series.
|
||
@param _id: series identifier
|
||
@type _id: str
|
||
@param page: page number to search [default = 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.debug()
|
||
|
||
|
||
url = HOST + "/series/%s/episodes?page=%s" % (_id, page)
|
||
logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS))
|
||
js = requests.get(url, headers=DEFAULT_HEADERS).json()
|
||
self.list_episodes[page] = js if 'Error' not in js else {}
|
||
return self.list_episodes[page]
|
||
|
||
def get_episode_by_id(self, _id, lang=DEFAULT_LANG, semaforo=None):
|
||
"""
|
||
Get the data of an episode
|
||
@param _id: episode identifier
|
||
@type _id: str
|
||
@param lang: language code
|
||
@param semaforo: semaphore for 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.debug()
|
||
|
||
url = HOST + "/episodes/%s" % _id
|
||
|
||
# from core.support import dbg;dbg()
|
||
|
||
try:
|
||
DEFAULT_HEADERS["Accept-Language"] = lang
|
||
logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS))
|
||
req = requests.get(url, headers=DEFAULT_HEADERS)
|
||
|
||
except Exception as ex:
|
||
# if isinstance(ex, urllib).HTTPError:
|
||
logger.debug("code %s " % 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 = req.json()
|
||
# logger.debug("dict_html %s" % dict_html)
|
||
self.episodes[_id] = dict_html.pop("data") if 'Error' not in dict_html else {}
|
||
|
||
if semaforo:
|
||
semaforo.release()
|
||
|
||
def __search(self, name, imdb_id, zap2it_id, lang=DEFAULT_LANG):
|
||
"""
|
||
Search for a series through a series of parameters.
|
||
@param name: name to search
|
||
@type name: str
|
||
@param imdb_id: imdb identification code
|
||
@type imdb_id: str
|
||
@param zap2it_id: zap2it identification code
|
||
@type zap2it_id: str
|
||
@param lang: language code
|
||
@type lang: str
|
||
|
||
data:{
|
||
"aliases": [
|
||
"string"
|
||
],
|
||
"banner": "string",
|
||
"firstAired": "string",
|
||
"id": 0,
|
||
"network": "string",
|
||
"overview": "string",
|
||
"seriesName": "string",
|
||
"status": "string"
|
||
}
|
||
"""
|
||
logger.debug()
|
||
|
||
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))
|
||
|
||
dict_html = requests.get(url, headers=DEFAULT_HEADERS).json()
|
||
|
||
|
||
if 'Error' in dict_html:
|
||
# if isinstance(ex, urllib.parse).HTTPError:
|
||
logger.debug("code %s " % dict_html['Error'])
|
||
|
||
else:
|
||
|
||
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("result %s" % resultado)
|
||
self.list_results = resultado
|
||
self.result = resultado[index]
|
||
|
||
def __get_by_id(self, _id, lang=DEFAULT_LANG, from_get_list=False):
|
||
"""
|
||
Gets the data for a string by identifier.
|
||
@param _id: series code
|
||
@type _id: str
|
||
@param lang: language code
|
||
@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.debug()
|
||
resultado = {}
|
||
|
||
url = HOST + "/series/%s" % _id
|
||
|
||
try:
|
||
DEFAULT_HEADERS["Accept-Language"] = lang
|
||
req = requests.get(url, headers=DEFAULT_HEADERS)
|
||
logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS))
|
||
|
||
except Exception as ex:
|
||
# if isinstance(ex, urllib).HTTPError:
|
||
logger.debug("code %s " % ex)
|
||
|
||
message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args))
|
||
logger.error("error: %s" % message)
|
||
|
||
else:
|
||
dict_html = req.json()
|
||
if "Error" in dict_html and "invalidLanguage" in dict_html["Error"]:
|
||
return {}
|
||
resultado1 = dict_html["data"]
|
||
if not resultado1 and from_get_list:
|
||
return self.__get_by_id(_id, "en")
|
||
|
||
logger.debug("Result %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("total result %s" % resultado)
|
||
self.list_results = [resultado]
|
||
self.result = resultado
|
||
|
||
return resultado
|
||
|
||
def get_images(self, _id, image="poster", season=1, lang="en"):
|
||
"""
|
||
Gets an image type for a string for a language.
|
||
@param _id: series identifier
|
||
@type _id: str
|
||
@param image: search code, ["poster" (default), "fanart", "season"]
|
||
@type image: str
|
||
@type season: season number
|
||
@param lang: language code for which you are searching
|
||
@type lang: str
|
||
@return: dictionary with the type of images chosen.
|
||
@rtype: dict
|
||
|
||
"""
|
||
logger.debug()
|
||
|
||
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))
|
||
|
||
res = requests.get(url, headers=DEFAULT_HEADERS)
|
||
|
||
except Exception as ex:
|
||
# if isinstance(ex, urllib).HTTPError:
|
||
logger.debug("code %s " % ex)
|
||
|
||
message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args))
|
||
logger.error("error: %s" % message)
|
||
|
||
|
||
else:
|
||
dict_html = res.json()
|
||
if 'Error' in dict_html:
|
||
# if isinstance(ex, urllib.parse).HTTPError:
|
||
logger.debug("code %s " % dict_html['Error'])
|
||
else:
|
||
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):
|
||
"""
|
||
gets casting for a series
|
||
@param _id: series code
|
||
@type _id: str
|
||
@param lang: language code to search
|
||
@type lang: str
|
||
@return: dictionary with actors
|
||
@rtype: dict
|
||
"""
|
||
logger.debug()
|
||
|
||
url = HOST + "/series/%s/actors" % _id
|
||
DEFAULT_HEADERS["Accept-Language"] = lang
|
||
logger.debug("url: %s, \nheaders: %s" % (url, DEFAULT_HEADERS))
|
||
try:
|
||
req = requests.get(url, headers=DEFAULT_HEADERS)
|
||
except Exception as ex:
|
||
logger.debug("code %s " % 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 = req.json()
|
||
if 'Error' in dict_html:
|
||
logger.debug("code %s " % dict_html['Error'])
|
||
else:
|
||
dict_html["cast"] = dict_html.pop("data")
|
||
self.result.update(dict_html)
|
||
|
||
def get_id(self):
|
||
"""
|
||
@return: Returns the Tvdb identifier of the loaded string or an empty string in case nothing was loaded.
|
||
You can use this method to find out if a search has been successful or not.
|
||
@rtype: str
|
||
"""
|
||
return str(self.result.get('id', ""))
|
||
|
||
def get_list_results(self):
|
||
"""
|
||
Returns the results we found for a series.
|
||
@rtype: list
|
||
@return: list of results
|
||
"""
|
||
logger.debug()
|
||
list_results = []
|
||
|
||
# if we have a result and it has seriesName, we already have the info of the series, it is not necessary to search again
|
||
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: Extra information about the movie, series, season or chapter.
|
||
@type infoLabels: dict
|
||
@param origen: Diccionario origen de donde se obtiene los infoLabels, por omision self.result
|
||
@type origen: dict
|
||
@return: Returns the extra information obtained from the current object. If the infoLables parameter was passed,
|
||
the value returned will be read as a parameter duly updated.
|
||
@rtype: dict
|
||
"""
|
||
|
||
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'
|
||
|
||
# Start Listings
|
||
l_castandrole = ret_infoLabels.get('castandrole', [])
|
||
|
||
# logger.debug("self.result %s" % self.result)
|
||
|
||
if not origen:
|
||
origen = self.result
|
||
|
||
ret_infoLabels['title'] = origen['seriesName']
|
||
ret_infoLabels['tvdb_id'] = origen['id']
|
||
thumbs = requests.get(HOST + '/series/' + str(origen['id']) + '/images/query?keyType=poster').json()
|
||
if 'data' in thumbs:
|
||
ret_infoLabels['thumbnail'] = HOST_IMAGE + thumbs['data'][0]['fileName']
|
||
elif 'poster' in origen and origen['poster']:
|
||
ret_infoLabels['thumbnail'] = HOST_IMAGE + origen['poster']
|
||
fanarts = requests.get(HOST + '/series/' + str(origen['id']) + '/images/query?keyType=fanart').json()
|
||
if 'data' in fanarts:
|
||
ret_infoLabels['fanart'] = HOST_IMAGE + fanarts['data'][0]['fileName']
|
||
elif 'fanart' in origen and origen['fanart']:
|
||
ret_infoLabels['fanart'] = HOST_IMAGE + origen['fanart']
|
||
if 'overview' in origen and origen['overview']:
|
||
ret_infoLabels['plot'] = origen['overview']
|
||
if 'duration' in origen and origen['duration']:
|
||
ret_infoLabels['duration'] = int(origen['duration']) * 60
|
||
if 'firstAired' in origen and origen['firstAired']:
|
||
ret_infoLabels['year'] = int(origen['firstAired'][:4])
|
||
ret_infoLabels['premiered'] = origen['firstAired'].split("-")[2] + "/" + origen['firstAired'].split("-")[1] + "/" + origen['firstAired'].split("-")[0]
|
||
if 'siteRating' in origen and origen['siteRating']:
|
||
ret_infoLabels['rating'] = float(origen['siteRating'])
|
||
if 'siteRatingCount' in origen and origen['siteRatingCount']:
|
||
ret_infoLabels['votes'] = origen['siteRatingCount']
|
||
if 'status' in origen and origen['status']:
|
||
ret_infoLabels['status'] = origen['status']
|
||
if 'network' in origen and origen['network']:
|
||
ret_infoLabels['studio'] = origen['network']
|
||
if 'imdbId' in origen and origen['rating']:
|
||
ret_infoLabels['imdb_id'] = origen['imdbId']
|
||
if 'rating' in origen and origen['rating']:
|
||
ret_infoLabels['mpaa'] = origen['rating']
|
||
if 'genre' in origen and origen['genre']:
|
||
for genre in origen['genre']:
|
||
genre_list = ""
|
||
genre_list += genre + ', '
|
||
ret_infoLabels['genre'] = genre_list.rstrip(', ')
|
||
if 'cast' in origen and origen['cast']:
|
||
dic_aux = dict((name, character) for (name, character) in l_castandrole)
|
||
l_castandrole.extend([(p['name'], p['role']) for p in origen['cast'] if p['name'] not in list(dic_aux.keys())])
|
||
ret_infoLabels['castandrole'] = l_castandrole
|
||
|
||
|
||
return ret_infoLabels
|