# -*- coding: utf-8 -*-
# ------------------------------------------------------------
# pelisalacarta 4
# Copyright 2015 tvalacarta@gmail.com
# http://blog.tvalacarta.info/plugin-xbmc/pelisalacarta/
#
# Distributed under the terms of GNU General Public License v3 (GPLv3)
# http://www.gnu.org/licenses/gpl-3.0.html
# ------------------------------------------------------------
# This file is part of pelisalacarta 4.
#
# pelisalacarta 4 is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pelisalacarta 4 is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with pelisalacarta 4.  If not, see <http://www.gnu.org/licenses/>.
# --------------------------------------------------------------------------------

import copy
import re
import time

from core import jsontools
from core import logger
from core import scrapertools
from core.item import InfoLabels

# -----------------------------------------------------------------------------------------------------------
# Conjunto de funciones relacionadas con las infoLabels.
#   version 1.0:
#       Version inicial
#
#   Incluyen:
#       set_infoLabels(source, seekTmdb, idioma_busqueda): Obtiene y fija (item.infoLabels) los datos extras de una o
# varias series, capitulos o peliculas.
#       set_infoLabels_item(item, seekTmdb, idioma_busqueda): Obtiene y fija (item.infoLabels) los datos extras de una
# serie, capitulo o pelicula.
#       set_infoLabels_itemlist(item_list, seekTmdb, idioma_busqueda): Obtiene y fija (item.infoLabels) los datos
# extras de una lista de series, capitulos o peliculas.
#       infoLabels_tostring(item): Retorna un str con la lista ordenada con los infoLabels del item
#
#   Uso:
#       tmdb.set_infoLabels(item, seekTmdb = True)
#
#       Obtener datos basicos de una pelicula:
#           Antes de llamar al metodo set_infoLabels el titulo a buscar debe estar en item.fulltitle
#           o en item.contentTitle y el año en item.infoLabels['year'].
#
#       Obtener datos basicos de una serie:
#           Antes de llamar al metodo set_infoLabels el titulo a buscar debe estar en item.show o en
# item.contentSerieName.
#
#       Obtener mas datos de una pelicula o serie:
#           Despues de obtener los datos basicos en item.infoLabels['tmdb'] tendremos el codigo de la serie o pelicula.
#           Tambien podriamos directamente fijar este codigo, si se conoce, o utilizar los codigo correspondientes de:
#           IMDB (en item.infoLabels['IMDBNumber'] o item.infoLabels['code'] o item.infoLabels['imdb_id']), TVDB
# (solo series, en item.infoLabels['tvdb_id']),
#           Freebase (solo series, en item.infoLabels['freebase_mid']),TVRage (solo series, en
# item.infoLabels['tvrage_id'])
#
#       Obtener datos de una temporada:
#           Antes de llamar al metodo set_infoLabels el titulo de la serie debe estar en item.show o en
# item.contentSerieName,
#           el codigo TMDB de la serie debe estar en item.infoLabels['tmdb'] (puede fijarse automaticamente mediante
# la consulta de datos basica)
#           y el numero de temporada debe estar en item.infoLabels['season'].
#
#       Obtener datos de un episodio:
#           Antes de llamar al metodo set_infoLabels el titulo de la serie debe estar en item.show o en
# item.contentSerieName,
#           el codigo TMDB de la serie debe estar en item.infoLabels['tmdb'] (puede fijarse automaticamente mediante la
# consulta de datos basica),
#           el numero de temporada debe estar en item.infoLabels['season'] y el numero de episodio debe estar en
# item.infoLabels['episode'].
#
#
# --------------------------------------------------------------------------------------------------------------

otmdb_global = None


def set_infoLabels(source, seekTmdb=True, idioma_busqueda='es'):
    """
    Dependiendo del tipo de dato de source obtiene y fija (item.infoLabels) los datos extras de una o varias series,
    capitulos o peliculas.

    @param source: variable que contiene la información para establecer infoLabels
    @type source: list, item
    @param seekTmdb: si es True hace una busqueda en www.themoviedb.org para obtener los datos, en caso contrario
        obtiene los datos del propio Item.
    @type seekTmdb: bool
    @param idioma_busqueda: fija el valor de idioma en caso de busqueda en www.themoviedb.org
    @type idioma_busqueda: str
    @return: un numero o lista de numeros con el resultado de las llamadas a set_infoLabels_item
    @rtype: int, list
    """
    start_time = time.time()
    if type(source) == list:
        ret = set_infoLabels_itemlist(source, seekTmdb, idioma_busqueda)
        logger.debug("Se han obtenido los datos de %i enlaces en %f segundos" % (len(source), time.time() - start_time))
    else:
        ret = set_infoLabels_item(source, seekTmdb, idioma_busqueda)
        logger.debug("Se han obtenido los datos del enlace en %f segundos" % (time.time() - start_time))
    return ret


def set_infoLabels_itemlist(item_list, seekTmdb=False, idioma_busqueda='es'):
    """
    De manera concurrente, obtiene los datos de los items incluidos en la lista item_list.

    La API tiene un limite de 40 peticiones por IP cada 10'' y por eso la lista no deberia tener mas de 30 items
    para asegurar un buen funcionamiento de esta funcion.

    :param item_list: listado de objetos Item que representan peliculas, series o capitulos. El atributo
        infoLabels de cada objeto Item sera modificado incluyendo los datos extras localizados.
    :type item_list: list
    :param seekTmdb: Si es True hace una busqueda en www.themoviedb.org para obtener los datos, en caso contrario
        obtiene los datos del propio Item si existen.
    :type seekTmdb: bool
    :param idioma_busqueda: Codigo del idioma segun ISO 639-1, en caso de busqueda en www.themoviedb.org.
    :type idioma_busqueda: str

    :return: Una lista de numeros cuyo valor absoluto representa la cantidad de elementos incluidos en el atributo
        infoLabels de cada Item. Este numero sera positivo si los datos se han obtenido de www.themoviedb.org y
        negativo en caso contrario.
    :rtype: list
    """
    import threading

    semaforo = threading.Semaphore(20)
    lock = threading.Lock()
    r_list = list()
    i = 0
    l_hilo = list()

    def sub_thread(item, _i, _seekTmdb):
        semaforo.acquire()
        ret = set_infoLabels_item(item, _seekTmdb, idioma_busqueda, lock)
        # logger.debug(str(ret) + "item: " + item.tostring())
        semaforo.release()
        r_list.append((_i, item, ret))

    for item in item_list:
        t = threading.Thread(target=sub_thread, args=(item, i, seekTmdb))
        t.start()
        i += 1
        l_hilo.append(t)

    # esperar q todos los hilos terminen
    for x in l_hilo:
        x.join()

    # Ordenar lista de resultados por orden de llamada para mantener el mismo orden q item_list
    r_list.sort(key=lambda i: i[0])

    # Reconstruir y devolver la lista solo con los resultados de las llamadas individuales
    return [ii[2] for ii in r_list]


def set_infoLabels_item(item, seekTmdb=True, idioma_busqueda='es', lock=None):
    # -----------------------------------------------------------------------------------------------------------
    # Obtiene y fija (item.infoLabels) los datos extras de una serie, capitulo o pelicula.
    #
    #   Parametros:
    #       item: (Item) Objeto Item que representa un pelicula, serie o capitulo. El atributo infoLabels sera
    #               modificado incluyendo los datos extras localizados.
    #       (opcional) seekTmdb: (bool) Si es True hace una busqueda en www.themoviedb.org para obtener los datos,
    #                   en caso contrario obtiene los datos del propio Item si existen.
    #       (opcional) idioma_busqueda: (str) Codigo del idioma segun ISO 639-1, en caso de busqueda en
    # www.themoviedb.org.
    #   Retorna:
    #       Un numero cuyo valor absoluto representa la cantidad de elementos incluidos en el atributo
    # item.infoLabels.
    #       Este numero sera positivo si los datos se han obtenido de www.themoviedb.org y negativo en caso contrario.
    # ---------------------------------------------------------------------------------------------------------
    global otmdb_global

    def __leer_datos(otmdb_aux):
        item.infoLabels = otmdb_aux.get_infoLabels(item.infoLabels)
        if item.infoLabels['thumbnail']:
            item.thumbnail = item.infoLabels['thumbnail']
        if item.infoLabels['fanart']:
            item.fanart = item.infoLabels['fanart']

    if seekTmdb:
        # Comprobamos q tipo de contenido es...
        if item.contentType == 'movie':
            tipo_busqueda = 'movie'
        else:
            tipo_busqueda = 'tv'


        if item.infoLabels['season']:
            try:
                numtemporada = int(item.infoLabels['season'])
            except ValueError:
                logger.debug("El numero de temporada no es valido")
                return -1 * len(item.infoLabels)

            if lock:
                lock.acquire()

            if not otmdb_global or (item.infoLabels['tmdb_id'] and
                                    otmdb_global.result.get("id") != item.infoLabels['tmdb_id']) \
                                or (otmdb_global.texto_buscado and
                                    otmdb_global.texto_buscado != item.infoLabels['tvshowtitle']):
                if item.infoLabels['tmdb_id']:
                    otmdb_global = Tmdb(id_Tmdb=item.infoLabels['tmdb_id'], tipo=tipo_busqueda,
                                        idioma_busqueda=idioma_busqueda)
                else:
                    otmdb_global = Tmdb(texto_buscado=item.infoLabels['tvshowtitle'], tipo=tipo_busqueda,
                                        idioma_busqueda=idioma_busqueda, year=item.infoLabels['year'])

                __leer_datos(otmdb_global)

            temporada = otmdb_global.get_temporada(numtemporada)

            if lock:
                lock.release()

            if item.infoLabels['episode']:
                try:
                    episode = int(item.infoLabels['episode'])
                except ValueError:
                    logger.debug("El número de episodio (%s) no es valido" % repr(item.infoLabels['episode']))
                    return -1 * len(item.infoLabels)

                # Tenemos numero de temporada y numero de episodio validos...
                # ... buscar datos episodio
                item.infoLabels['mediatype'] = 'episode'
                episodio = otmdb_global.get_episodio(numtemporada, episode)

                if episodio:
                    # Actualizar datos
                    __leer_datos(otmdb_global)
                    item.infoLabels['title'] = episodio['episodio_titulo']
                    if episodio['episodio_sinopsis']:
                        item.infoLabels['plot'] = episodio['episodio_sinopsis']
                    if episodio['episodio_imagen']:
                        item.infoLabels['poster_path'] = episodio['episodio_imagen']
                        item.thumbnail = item.infoLabels['poster_path']
                    if episodio['episodio_air_date']:
                        item.infoLabels['aired'] = episodio['episodio_air_date']
                    if episodio['episodio_vote_average']:
                        item.infoLabels['rating'] = episodio['episodio_vote_average']
                        item.infoLabels['votes'] = episodio['episodio_vote_count']

                    return len(item.infoLabels)


            else:
                # Tenemos numero de temporada valido pero no numero de episodio...
                # ... buscar datos temporada
                item.infoLabels['mediatype'] = 'season'
                temporada = otmdb_global.get_temporada(numtemporada)

                if temporada:
                    # Actualizar datos
                    __leer_datos(otmdb_global)
                    item.infoLabels['title'] = temporada['name']
                    if temporada['overview']:
                        item.infoLabels['plot'] = temporada['overview']
                    if temporada['air_date']:
                        date = temporada['air_date'].split('-')
                        item.infoLabels['aired'] = date[2] + "/" + date[1] + "/" + date[0]
                    if temporada['poster_path']:
                        item.infoLabels['poster_path'] = 'http://image.tmdb.org/t/p/original' + temporada['poster_path']
                        item.thumbnail = item.infoLabels['poster_path']
                    return len(item.infoLabels)

        # Buscar...
        else:
            otmdb = copy.copy(otmdb_global)
            #if otmdb is None: # Se elimina por q sino falla al añadir series por falta de imdb, pero por contra provoca mas llamadas
            # Busquedas por ID...
            if item.infoLabels['tmdb_id']:
                # ...Busqueda por tmdb_id
                otmdb = Tmdb(id_Tmdb=item.infoLabels['tmdb_id'], tipo=tipo_busqueda,
                             idioma_busqueda=idioma_busqueda)

            elif item.infoLabels['imdb_id']:
                # ...Busqueda por imdb code
                otmdb = Tmdb(external_id=item.infoLabels['imdb_id'], external_source="imdb_id",
                             tipo=tipo_busqueda,
                             idioma_busqueda=idioma_busqueda)

            elif tipo_busqueda == 'tv':  # buscar con otros codigos
                if item.infoLabels['tvdb_id']:
                    # ...Busqueda por tvdb_id
                    otmdb = Tmdb(external_id=item.infoLabels['tvdb_id'], external_source="tvdb_id",
                                 tipo=tipo_busqueda,
                                 idioma_busqueda=idioma_busqueda)
                elif item.infoLabels['freebase_mid']:
                    # ...Busqueda por freebase_mid
                    otmdb = Tmdb(external_id=item.infoLabels['freebase_mid'], external_source="freebase_mid",
                                 tipo=tipo_busqueda, idioma_busqueda=idioma_busqueda)
                elif item.infoLabels['freebase_id']:
                    # ...Busqueda por freebase_id
                    otmdb = Tmdb(external_id=item.infoLabels['freebase_id'], external_source="freebase_id",
                                 tipo=tipo_busqueda, idioma_busqueda=idioma_busqueda)
                elif item.infoLabels['tvrage_id']:
                    # ...Busqueda por tvrage_id
                    otmdb = Tmdb(external_id=item.infoLabels['tvrage_id'], external_source="tvrage_id",
                                 tipo=tipo_busqueda, idioma_busqueda=idioma_busqueda)

            if otmdb is None:
                # No se ha podido buscar por ID...
                # hacerlo por titulo
                if tipo_busqueda == 'tv':
                    # Busqueda de serie por titulo y filtrando sus resultados si es necesario
                    otmdb = Tmdb(texto_buscado=item.infoLabels['tvshowtitle'], tipo=tipo_busqueda,
                                 idioma_busqueda=idioma_busqueda, filtro=item.infoLabels.get('filtro', {}),
                                 year=item.infoLabels['year'])
                else:
                    # Busqueda de pelicula por titulo...
                    if item.infoLabels['year'] or item.infoLabels['filtro']:
                        # ...y año o filtro
                        if item.contentTitle:
                            titulo_buscado = item.contentTitle
                        else:
                            titulo_buscado = item.fulltitle

                        otmdb = Tmdb(texto_buscado=titulo_buscado, tipo=tipo_busqueda,
                                     idioma_busqueda=idioma_busqueda,
                                     filtro=item.infoLabels.get('filtro', {}),
                                     year=item.infoLabels['year'])

                if otmdb.get_id() and not lock:
                    # Si la busqueda ha dado resultado y no se esta buscando una lista de items,
                    # realizar otra busqueda para ampliar la informacion
                    otmdb = Tmdb(id_Tmdb=otmdb.result.get("id"), tipo=tipo_busqueda,
                                 idioma_busqueda=idioma_busqueda)

            if otmdb is not None and otmdb.get_id():
                # La busqueda ha encontrado un resultado valido
                __leer_datos(otmdb)
                return len(item.infoLabels)

    # La busqueda en tmdb esta desactivada o no ha dado resultado
    # item.contentType = item.infoLabels['mediatype']
    return -1 * len(item.infoLabels)


# Clase auxiliar
class ResultDictDefault(dict):
    #Python 2.4
    def __getitem__(self, key):
        try:
          return super(ResultDictDefault, self).__getitem__(key)
        except:
          return self.__missing__(key)
          
    def __missing__(self, key):
        '''
        valores por defecto en caso de que la clave solicitada no exista
        '''
        if key in ['genre_ids', 'genre', 'genres']:
            return list()
        elif key == 'images_posters':
            posters = dict()
            if 'images' in super(ResultDictDefault,self).keys() and \
                'posters' in super(ResultDictDefault,self).__getitem__('images'):
                    posters = super(ResultDictDefault,self).__getitem__('images')['posters']
                    super(ResultDictDefault, self).__setattr__("images_posters", posters)

            return posters

        elif key == "images_backdrops":
            backdrops = dict()
            if 'images' in super(ResultDictDefault, self).keys() and \
                'backdrops' in super(ResultDictDefault, self).__getitem__('images'):
                    backdrops = super(ResultDictDefault, self).__getitem__('images')['backdrops']
                    super(ResultDictDefault, self).__setattr__("images_backdrops", backdrops)

            return backdrops

        elif key == "images_profiles":
            profiles = dict()
            if 'images' in super(ResultDictDefault, self).keys() and \
                'profiles' in super(ResultDictDefault, self).__getitem__('images'):
                    profiles = super(ResultDictDefault, self).__getitem__('images')['profiles']
                    super(ResultDictDefault, self).__setattr__("images_profiles", profiles)

            return profiles

        else:
            # El resto de claves devuelven cadenas vacias por defecto
            return ""

    def __str__(self):
        return self.tostring(separador= ',\n')

    def tostring(self,separador= ',\n'):
        ls = []
        for i in super(ResultDictDefault, self).items():
            i_str = str(i)[1:-1]
            if isinstance(i[0], str):
                old = i[0] + "',"
                new = i[0] + "':"
            else:
                old = str(i[0]) + ","
                new = str(i[0]) + ":"
            ls.append(i_str.replace(old, new, 1))

        return "{%s}" % separador.join(ls)


# ---------------------------------------------------------------------------------------------------------------
# class Tmdb:
#   Scraper para pelisalacarta basado en el Api de https://www.themoviedb.org/
#   version 1.4:
#       - Documentada limitacion de uso de la API (ver mas abajo).
#       - Añadido metodo get_temporada()
#   version 1.3:
#       - Corregido error al devolver None el path_poster y el backdrop_path
#       - Corregido error que hacia que en el listado de generos se fueran acumulando de una llamada a otra
#       - Añadido metodo get_generos()
#       - Añadido parametros opcional idioma_alternativo al metodo get_sinopsis()
#
#
#   Uso:
#   Metodos constructores:
#    Tmdb(texto_buscado, tipo)
#        Parametros:
#            texto_buscado:(str) Texto o parte del texto a buscar
#            tipo: ("movie" o "tv") Tipo de resultado buscado peliculas o series. Por defecto "movie"
#            (opcional) idioma_busqueda: (str) codigo del idioma segun ISO 639-1
#            (opcional) include_adult: (bool) Se incluyen contenidos para adultos en la busqueda o no. Por defecto
# 'False'
#            (opcional) year: (str) Año de lanzamiento.
#            (opcional) page: (int) Cuando hay muchos resultados para una busqueda estos se organizan por paginas.
#                            Podemos cargar la pagina que deseemos aunque por defecto siempre es la primera.
#        Return:
#            Esta llamada devuelve un objeto Tmdb que contiene la primera pagina del resultado de buscar 'texto_buscado'
#            en la web themoviedb.org. Cuantos mas parametros opcionales se incluyan mas precisa sera la busqueda.
#            Ademas el objeto esta inicializado con el primer resultado de la primera pagina de resultados.
#    Tmdb(id_Tmdb,tipo)
#       Parametros:
#           id_Tmdb: (str) Codigo identificador de una determinada pelicula o serie en themoviedb.org
#           tipo: ("movie" o "tv") Tipo de resultado buscado peliculas o series. Por defecto "movie"
#           (opcional) idioma_busqueda: (str) codigo del idioma segun ISO 639-1
#       Return:
#           Esta llamada devuelve un objeto Tmdb que contiene el resultado de buscar una pelicula o serie con el
# identificador id_Tmd
#           en la web themoviedb.org.
#    Tmdb(external_id, external_source, tipo)
#       Parametros:
#           external_id: (str) Codigo identificador de una determinada pelicula o serie en la web referenciada por
# 'external_source'.
#           external_source: (Para series:"imdb_id","freebase_mid","freebase_id","tvdb_id","tvrage_id"; Para
# peliculas:"imdb_id")
#           tipo: ("movie" o "tv") Tipo de resultado buscado peliculas o series. Por defecto "movie"
#           (opcional) idioma_busqueda: (str) codigo del idioma segun ISO 639-1
#       Return:
#           Esta llamada devuelve un objeto Tmdb que contiene el resultado de buscar una pelicula o serie con el
# identificador 'external_id' de
#           la web referenciada por 'external_source' en la web themoviedb.org.
#
#   Metodos principales:
#    get_id(): Retorna un str con el identificador Tmdb de la pelicula o serie cargada o una cadena vacia si no hubiese
# nada cargado.
#    get_sinopsis(idioma_alternativo): Retorna un str con la sinopsis de la serie o pelicula cargada.
#    get_poster (tipo_respuesta,size): Obtiene el poster o un listado de posters.
#    get_backdrop (tipo_respuesta,size): Obtiene una imagen de fondo o un listado de imagenes de fondo.
#    get_temporada(temporada): Obtiene un diccionario con datos especificos de la temporada.
#    get_episodio (temporada, capitulo): Obtiene un diccionario con datos especificos del episodio.
#    get_generos(): Retorna un str con la lista de generos a los que pertenece la pelicula o serie.
#
#
#   Otros metodos:
#    load_resultado(resultado, page): Cuando la busqueda devuelve varios resultados podemos seleccionar que resultado
# concreto y de que pagina cargar los datos.
#
#   Limitaciones:
#   El uso de la API impone un limite de 20 conexiones simultaneas (concurrencia) o 30 peticiones en 10 segundos por IP
# Informacion sobre la api : http://docs.themoviedb.apiary.io
# -------------------------------------------------------------------------------------------------------------------


class Tmdb(object):
    # Atributo de clase
    dic_generos = {}
    '''
    dic_generos={"id_idioma1": {"tv": {"id1": "name1",
                                       "id2": "name2"
                                      },
                                "movie": {"id1": "name1",
                                          "id2": "name2"
                                          }
                                }
                }
    '''
    dic_country = {"AD": "Andorra", "AE": "Emiratos Árabes Unidos", "AF": "Afganistán", "AG": "Antigua y Barbuda",
                   "AI": "Anguila", "AL": "Albania", "AM": "Armenia", "AN": "Antillas Neerlandesas", "AO": "Angola",
                   "AQ": "Antártida", "AR": "Argentina", "AS": "Samoa Americana", "AT": "Austria", "AU": "Australia",
                   "AW": "Aruba", "AX": "Islas de Åland", "AZ": "Azerbayán", "BA": "Bosnia y Herzegovina",
                   "BD": "Bangladesh", "BE": "Bélgica", "BF": "Burkina Faso", "BG": "Bulgaria", "BI": "Burundi",
                   "BJ": "Benín", "BL": "San Bartolomé", "BM": "Islas Bermudas", "BN": "Brunéi", "BO": "Bolivia",
                   "BR": "Brasil", "BS": "Bahamas", "BT": "Bhután", "BV": "Isla Bouvet", "BW": "Botsuana",
                   "BY": "Bielorrusia", "BZ": "Belice", "CA": "Canadá", "CC": "Islas Cocos (Keeling)", "CD": "Congo",
                   "CF": "República Centroafricana", "CG": "Congo", "CH": "Suiza", "CI": "Costa de Marfil",
                   "CK": "Islas Cook", "CL": "Chile", "CM": "Camerún", "CN": "China", "CO": "Colombia",
                   "CR": "Costa Rica", "CU": "Cuba", "CV": "Cabo Verde", "CX": "Isla de Navidad", "CY": "Chipre",
                   "CZ": "República Checa", "DE": "Alemania", "DJ": "Yibuti", "DK": "Dinamarca", "DZ": "Algeria",
                   "EC": "Ecuador", "EE": "Estonia", "EG": "Egipto", "EH": "Sahara Occidental", "ER": "Eritrea",
                   "ES": "España", "ET": "Etiopía", "FI": "Finlandia", "FJ": "Fiyi", "FK": "Islas Malvinas",
                   "FM": "Micronesia", "FO": "Islas Feroe", "FR": "Francia", "GA": "Gabón", "GB": "Gran Bretaña",
                   "GD": "Granada", "GE": "Georgia", "GF": "Guayana Francesa", "GG": "Guernsey", "GH": "Ghana",
                   "GI": "Gibraltar", "GL": "Groenlandia", "GM": "Gambia", "GN": "Guinea", "GP": "Guadalupe",
                   "GQ": "Guinea Ecuatorial", "GR": "Grecia", "GS": "Islas Georgias del Sur y Sandwich del Sur",
                   "GT": "Guatemala", "GW": "Guinea-Bissau", "GY": "Guyana", "HK": "Hong kong",
                   "HM": "Islas Heard y McDonald", "HN": "Honduras", "HR": "Croacia", "HT": "Haití", "HU": "Hungría",
                   "ID": "Indonesia", "IE": "Irlanda", "IM": "Isla de Man", "IN": "India",
                   "IO": "Territorio Británico del Océano Índico", "IQ": "Irak", "IR": "Irán", "IS": "Islandia",
                   "IT": "Italia", "JE": "Jersey", "JM": "Jamaica", "JO": "Jordania", "JP": "Japón", "KG": "Kirgizstán",
                   "KH": "Camboya", "KM": "Comoras", "KP": "Corea del Norte", "KR": "Corea del Sur", "KW": "Kuwait",
                   "KY": "Islas Caimán", "KZ": "Kazajistán", "LA": "Laos", "LB": "Líbano", "LC": "Santa Lucía",
                   "LI": "Liechtenstein", "LK": "Sri lanka", "LR": "Liberia", "LS": "Lesoto", "LT": "Lituania",
                   "LU": "Luxemburgo", "LV": "Letonia", "LY": "Libia", "MA": "Marruecos", "MC": "Mónaco",
                   "MD": "Moldavia", "ME": "Montenegro", "MF": "San Martín (Francia)", "MG": "Madagascar",
                   "MH": "Islas Marshall", "MK": "Macedônia", "ML": "Mali", "MM": "Birmania", "MN": "Mongolia",
                   "MO": "Macao", "MP": "Islas Marianas del Norte", "MQ": "Martinica", "MR": "Mauritania",
                   "MS": "Montserrat", "MT": "Malta", "MU": "Mauricio", "MV": "Islas Maldivas", "MW": "Malawi",
                   "MX": "México", "MY": "Malasia", "NA": "Namibia", "NE": "Niger", "NG": "Nigeria", "NI": "Nicaragua",
                   "NL": "Países Bajos", "NO": "Noruega", "NP": "Nepal", "NR": "Nauru", "NU": "Niue",
                   "NZ": "Nueva Zelanda", "OM": "Omán", "PA": "Panamá", "PE": "Perú", "PF": "Polinesia Francesa",
                   "PH": "Filipinas", "PK": "Pakistán", "PL": "Polonia", "PM": "San Pedro y Miquelón",
                   "PN": "Islas Pitcairn", "PR": "Puerto Rico", "PS": "Palestina", "PT": "Portugal", "PW": "Palau",
                   "PY": "Paraguay", "QA": "Qatar", "RE": "Reunión", "RO": "Rumanía", "RS": "Serbia", "RU": "Rusia",
                   "RW": "Ruanda", "SA": "Arabia Saudita", "SB": "Islas Salomón", "SC": "Seychelles", "SD": "Sudán",
                   "SE": "Suecia", "SG": "Singapur", "SH": "Santa Elena", "SI": "Eslovenia", "SJ": "Svalbard y Jan Mayen",
                   "SK": "Eslovaquia", "SL": "Sierra Leona", "SM": "San Marino", "SN": "Senegal", "SO": "Somalia",
                   "SV": "El Salvador", "SY": "Siria", "SZ": "Swazilandia", "TC": "Islas Turcas y Caicos", "TD": "Chad",
                   "TF": "Territorios Australes y Antárticas Franceses", "TG": "Togo", "TH": "Tailandia",
                   "TJ": "Tadjikistán", "TK": "Tokelau", "TL": "Timor Oriental", "TM": "Turkmenistán", "TN": "Tunez",
                   "TO": "Tonga", "TR": "Turquía", "TT": "Trinidad y Tobago", "TV": "Tuvalu", "TW": "Taiwán",
                   "TZ": "Tanzania", "UA": "Ucrania", "UG": "Uganda", "UM": "Islas Ultramarinas Menores de Estados Unidos",
                   "UY": "Uruguay", "UZ": "Uzbekistán", "VA": "Ciudad del Vaticano", "VC": "San Vicente y las Granadinas",
                   "VE": "Venezuela", "VG": "Islas Vírgenes Británicas", "VI": "Islas Vírgenes de los Estados Unidos",
                   "VN": "Vietnam", "VU": "Vanuatu", "WF": "Wallis y Futuna", "WS": "Samoa", "YE": "Yemen",
                   "YT": "Mayotte", "ZA": "Sudáfrica", "ZM": "Zambia", "ZW": "Zimbabue", "BB": "Barbados","BH": "Bahrein",
                   "DM": "Dominica", "DO": "República Dominicana", "GU": "Guam", "IL": "Israel", "KE": "Kenia",
                   "KI": "Kiribati", "KN": "San Cristóbal y Nieves", "MZ": "Mozambique", "NC": "Nueva Caledonia",
                   "NF": "Isla Norfolk", "PG": "Papúa Nueva Guinea", "SR": "Surinám", "ST": "Santo Tomé y Príncipe",
                   "US": "EEUU"}

    def __init__(self, **kwargs):
        self.page = kwargs.get('page', 1)
        self.index_results = 0
        self.results = []
        self.result = ResultDictDefault()
        self.total_pages = 0
        self.total_results = 0

        self.temporada = {}
        self.texto_buscado = kwargs.get('texto_buscado', '')

        self.busqueda_id = kwargs.get('id_Tmdb', '')
        self.busqueda_texto = re.sub('\[\\\?(B|I|COLOR)\s?[^\]]*\]', '', self.texto_buscado)
        self.busqueda_tipo = kwargs.get('tipo', '')
        self.busqueda_idioma = kwargs.get('idioma_busqueda', 'es')
        self.busqueda_include_adult = kwargs.get('include_adult', False)
        self.busqueda_year = kwargs.get('year', '')
        self.busqueda_filtro = kwargs.get('filtro', {})
        self.discover = kwargs.get('discover', {})

        # Reellenar diccionario de generos si es necesario
        if (self.busqueda_tipo == 'movie' or self.busqueda_tipo == "tv") and \
            (self.busqueda_idioma not in Tmdb.dic_generos or
             self.busqueda_tipo not in Tmdb.dic_generos[self.busqueda_idioma]):
                self.rellenar_dic_generos(self.busqueda_tipo, self.busqueda_idioma)

        if not self.busqueda_tipo:
            self.busqueda_tipo = 'movie'

        if self.busqueda_id:
            # Busqueda por identificador tmdb
            self.__by_id()

        elif self.busqueda_texto:
            # Busqueda por texto
            self.__search(page=self.page)

        elif 'external_source' in kwargs and 'external_id' in kwargs:
            # Busqueda por identificador externo segun el tipo.
            # TV Series: imdb_id, freebase_mid, freebase_id, tvdb_id, tvrage_id
            # Movies: imdb_id
            if (self.busqueda_tipo == 'movie' and kwargs.get('external_source') == "imdb_id") or \
                    (self.busqueda_tipo == 'tv' and kwargs.get('external_source') in (
                            "imdb_id", "freebase_mid", "freebase_id", "tvdb_id", "tvrage_id")):
                self.busqueda_id = kwargs.get('external_id')
                self.__by_id(source=kwargs.get('external_source'))

        elif self.discover:
            self.__discover()

        else:
            logger.debug("Creado objeto vacio")

    def __call__(self, **kwargs):
        self.page = kwargs.get('page', 1)
        self.index_results = 0
        self.results = []
        self.result = ResultDictDefault()
        self.total_pages = 0
        self.total_results = 0

        self.temporada = {}
        self.texto_buscado = kwargs.get('texto_buscado', '')

        self.busqueda_id = kwargs.get('id_Tmdb', '')
        self.busqueda_texto = re.sub('\[\\\?(B|I|COLOR)\s?[^\]]*\]', '', self.texto_buscado)
        self.busqueda_tipo = kwargs.get('tipo', '')
        self.busqueda_idioma = kwargs.get('idioma_busqueda', 'es')
        self.busqueda_include_adult = kwargs.get('include_adult', False)
        self.busqueda_year = kwargs.get('year', '')
        self.busqueda_filtro = kwargs.get('filtro', {})
        self.discover = kwargs.get('discover', {})

        # Reellenar diccionario de generos si es necesario
        if (self.busqueda_tipo == 'movie' or self.busqueda_tipo == "tv") and \
            (self.busqueda_idioma not in Tmdb.dic_generos or
             self.busqueda_tipo not in Tmdb.dic_generos[self.busqueda_idioma]):
                self.rellenar_dic_generos(self.busqueda_tipo, self.busqueda_idioma)

        if not self.busqueda_tipo:
            self.busqueda_tipo = 'movie'

        if self.busqueda_id:
            # Busqueda por identificador tmdb
            self.__by_id()

        elif self.busqueda_texto:
            # Busqueda por texto
            self.__search(page=self.page)

        elif 'external_source' in kwargs and 'external_id' in kwargs:
            # Busqueda por identificador externo segun el tipo.
            # TV Series: imdb_id, freebase_mid, freebase_id, tvdb_id, tvrage_id
            # Movies: imdb_id
            if (self.busqueda_tipo == 'movie' and kwargs.get('external_source') == "imdb_id") or \
                    (self.busqueda_tipo == 'tv' and kwargs.get('external_source') in (
                            "imdb_id", "freebase_mid", "freebase_id", "tvdb_id", "tvrage_id")):
                self.busqueda_id = kwargs.get('external_id')
                self.__by_id(source=kwargs.get('external_source'))

        elif self.discover:
            self.__discover()

        else:
            logger.debug("Creado objeto vacio")

        return self

    @classmethod
    def rellenar_dic_generos(cls, tipo='movie', idioma='es'):
        resultado = {}

        # Si se busca en idioma catalán, se cambia a español para el diccionario de géneros
        if idioma == "ca":
            idioma = "es"

        # Rellenar diccionario de generos del tipo e idioma pasados como parametros
        if idioma not in cls.dic_generos:
            cls.dic_generos[idioma] = {}

        if tipo not in cls.dic_generos[idioma]:
            cls.dic_generos[idioma][tipo] = {}
            url = ('http://api.themoviedb.org/3/genre/%s/list?api_key=f7f51775877e0bb6703520952b3c7840&language=%s'
                   % (tipo, idioma))
            try:
                logger.info("[Tmdb.py] Rellenando dicionario de generos")
                resultado = jsontools.load_json(scrapertools.downloadpageWithoutCookies(url))
                lista_generos = resultado["genres"]

                for i in lista_generos:
                    cls.dic_generos[idioma][tipo][str(i["id"])] = i["name"]
            except:
                pass

            if "status_code" in resultado:
                msg = "Error de tmdb: %s %s" % (resultado["status_code"], resultado["status_message"])
                logger.error(msg)

    def __by_id(self, source='tmdb'):
        resultado = {}
        buscando = ""

        if self.busqueda_id:
            if source == "tmdb":
                # http://api.themoviedb.org/3/movie/1924?api_key=f7f51775877e0bb6703520952b3c7840&language=es
                #   &append_to_response=images,videos,external_ids,credits&include_image_language=es,null
                # http://api.themoviedb.org/3/tv/1407?api_key=f7f51775877e0bb6703520952b3c7840&language=es
                #   &append_to_response=images,videos,external_ids,credits&include_image_language=es,null
                url = ('http://api.themoviedb.org/3/%s/%s?api_key=f7f51775877e0bb6703520952b3c7840&language=%s'
                       '&append_to_response=images,videos,external_ids,credits&include_image_language=%s,null' %
                       (self.busqueda_tipo, self.busqueda_id, self.busqueda_idioma, self.busqueda_idioma))
                buscando = "id_Tmdb: %s" % self.busqueda_id
            else:
                # http://api.themoviedb.org/3/find/%s?external_source=imdb_id&api_key=f7f51775877e0bb6703520952b3c7840
                url = ('http://api.themoviedb.org/3/find/%s?external_source=%s&api_key=f7f51775877e0bb6703520952b3c7840'
                       '&language=%s' % (self.busqueda_id, source, self.busqueda_idioma))
                buscando = "%s: %s" %(source.capitalize(), self.busqueda_id)

            logger.info("[Tmdb.py] Buscando %s:\n%s" % (buscando, url))

            try:
                resultado = jsontools.load_json(scrapertools.downloadpageWithoutCookies(url))
                if source != "tmdb":
                    if self.busqueda_tipo == "movie":
                        resultado = resultado["movie_results"][0]
                    else:
                        resultado = resultado["tv_results"][0]
            except:
                resultado = {}

        if resultado and not "status_code" in resultado:
            self.results = [resultado]
            self.total_results = 1
            self.total_pages = 1
            self.result = ResultDictDefault(resultado)
        else:
            # No hay resultados de la busqueda
            msg = "La busqueda de %s no dio resultados." % buscando
            if "status_code" in resultado:
                msg += "\nError de tmdb: %s %s" % (resultado["status_code"], resultado["status_message"])
            logger.debug(msg)

    def __search(self, index_results=0, page=1):
        resultado = {}
        self.result = ResultDictDefault()
        results = []
        total_results = 0
        total_pages = 0
        buscando = ""

        if self.busqueda_texto:
            # http://api.themoviedb.org/3/search/movie?api_key=f7f51775877e0bb6703520952b3c7840&query=superman&language=es
            # &include_adult=false&page=1
            url = ('http://api.themoviedb.org/3/search/%s?api_key=f7f51775877e0bb6703520952b3c7840&query=%s&language=%s'
                   '&include_adult=%s&page=%s' % (self.busqueda_tipo, self.busqueda_texto.replace(' ', '%20'),
                                                  self.busqueda_idioma, self.busqueda_include_adult,page))

            if self.busqueda_year:
                url += '&year=%s' %(self.busqueda_year)

            buscando = self.busqueda_texto.capitalize()
            logger.info("[Tmdb.py] Buscando %s en pagina %s:\n%s" % (buscando, page, url))

            try:
                resultado = jsontools.load_json(scrapertools.downloadpageWithoutCookies(url))
                total_results = resultado["total_results"]
                total_pages = resultado["total_pages"]
            except:
                total_results = 0


            if total_results > 0:
                results = resultado["results"]

            if self.busqueda_filtro and results:
                # TODO documentar esta parte
                for key, value in dict(self.busqueda_filtro).items():
                    for r in results[:]:
                        if key not in r or r[key] != value:
                            results.remove(r)
                            total_results -= 1


        if results:
            if index_results >= len(results):
                # Se ha solicitado un numero de resultado mayor de los obtenidos
                logger.error(
                    "La busqueda de '%s' dio %s resultados para la pagina %s\nImposible mostrar el resultado numero %s"
                    % (buscando, len(results), page, index_results))
                return 0

            # Retornamos el numero de resultados de esta pagina
            self.results = results
            self.total_results = total_results
            self.total_pages = total_pages
            self.result = ResultDictDefault(self.results[index_results])
            return len(self.results)

        else:
            # No hay resultados de la busqueda
            msg = "La busqueda de '%s' no dio resultados para la pagina %s" % (buscando, page)
            if "status_code" in resultado:
                msg += "\nError de tmdb: %s %s" % (resultado["status_code"], resultado["status_message"])
            logger.error(msg)
            return 0

    def __discover(self, index_results=0):
        resultado = {}
        self.result = ResultDictDefault()
        results = []
        total_results = 0
        total_pages = 0

        # Ejemplo self.discover: {'url': 'discover/movie', 'with_cast': '1'}
        # url: Método de la api a ejecutar
        # resto de claves: Parámetros de la búsqueda concatenados a la url
        type_search = self.discover.get('url', '')
        if type_search:
            params = []
            for key, value in self.discover.items():
                if key != "url":
                    params.append(key + "=" + str(value))
            # http://api.themoviedb.org/3/discover/movie?api_key=f7f51775877e0bb6703520952b3c7840&query=superman&language=es
            url = ('http://api.themoviedb.org/3/%s?api_key=f7f51775877e0bb6703520952b3c7840&%s'
                  % (type_search, "&".join(params)))

            logger.info("[Tmdb.py] Buscando %s:\n%s" % (type_search, url))

            try:
                resultado = jsontools.load_json(scrapertools.downloadpageWithoutCookies(url))
                total_results = resultado["total_results"]
                total_pages = resultado["total_pages"]
            except:
                if resultado and not "status_code" in resultado:
                    total_results = -1
                    total_pages = 1
                else:
                    total_results = 0

            if total_results > 0:
                results = resultado["results"]
                if self.busqueda_filtro and results:
                    # TODO documentar esta parte
                    for key, value in dict(self.busqueda_filtro).items():
                        for r in results[:]:
                            if key not in r or r[key] != value:
                                results.remove(r)
                                total_results -= 1
            elif total_results == -1:
                results = resultado

            if index_results >= len(results):
                logger.error(
                    "La busqueda de '%s' no dio %s resultados" % (type_search, index_results))
                return 0

        # Retornamos el numero de resultados de esta pagina
        if results:
            self.results = results
            self.total_results = total_results
            self.total_pages = total_pages
            if total_results > 0:
                self.result = ResultDictDefault(self.results[index_results])
            else:
                self.result = results
            return len(self.results)
        else:
            # No hay resultados de la busqueda
            logger.error("La busqueda de '%s' no dio resultados" % type_search)
            return 0


    def load_resultado(self, index_results=0, page=1):
        # Si no hay resultados, solo hay uno o
        # si el numero de resultados de esta pagina es menor al indice buscado salir
        self.result = ResultDictDefault()
        num_result_page = len(self.results)

        if page > self.total_pages:
            return False

        if page != self.page:
            num_result_page = self.__search(index_results, page)

        if num_result_page == 0 or num_result_page <= index_results:
            return False

        self.page = page
        self.index_results = index_results
        self.result = ResultDictDefault(self.results[index_results])
        return True

    def get_list_resultados(self, num_result=20):
        logger.info("self %s" % str(self))
        # TODO documentar
        res = []

        if num_result <= 0:
            num_result = self.total_results
        num_result = min([num_result, self.total_results])

        cr = 0
        for p in range(1, self.total_pages + 1):
            for r in range(0, len(self.results)):
                try:
                    if self.load_resultado(r, p):
                        result = self.result.copy()

                        result['thumbnail'] = self.get_poster(size="w300")
                        result['fanart'] = self.get_backdrop()
                        res.append(result)
                        cr += 1
                        if cr >= num_result:
                            return res
                except:
                    continue

        return res

    def get_generos(self, origen=None):
        """
        :param origen: Diccionario origen de donde se obtiene los infoLabels, por omision self.result
        :type origen: Dict
        :return: Devuelve la lista de generos a los que pertenece la pelicula o serie.
        :rtype: str
        """
        genre_list =[]

        if not origen:
            origen = self.result

        if "genre_ids" in origen:
            # Buscar lista de generos por IDs
            for i in origen.get("genre_ids"):
                try:
                    genre_list.append(Tmdb.dic_generos[self.busqueda_idioma][self.busqueda_tipo][str(i)])
                except:
                    pass

        elif "genre" in origen or "genres" in origen:
            # Buscar lista de generos (lista de objetos {id,nombre})
            v = origen["genre"]
            v.extend(origen["genres"])
            for i in v:
                genre_list.append(i['name'])

        return ', '.join(genre_list)

    def get_id(self):
        """

        :return: Devuelve el identificador Tmdb de la pelicula o 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_sinopsis(self, idioma_alternativo=""):
        """

        :param idioma_alternativo: codigo del idioma, segun ISO 639-1, en el caso de que en el idioma fijado para la
            busqueda no exista sinopsis.
            Por defecto, se utiliza el idioma original. Si se utiliza None como idioma_alternativo, solo se buscara en
            el idioma fijado.
        :type idioma_alternativo: str
        :return: Devuelve la sinopsis de una pelicula o serie
        :rtype: str
        """
        resultado = {}
        ret = ""

        if 'id' in self.result:
            ret = self.result.get('overview')
            if ret == "" and str(idioma_alternativo).lower() != 'none':
                # Vamos a lanzar una busqueda por id y releer de nuevo la sinopsis
                self.busqueda_id = str(self.result["id"])
                if idioma_alternativo:
                    self.busqueda_idioma = idioma_alternativo
                else:
                    self.busqueda_idioma = self.result['original_language']

                url = ('http://api.themoviedb.org/3/%s/%s?api_key=f7f51775877e0bb6703520952b3c7840&language=%s' %
                       (self.busqueda_tipo, self.busqueda_id, self.busqueda_idioma))
                try:
                    resultado = jsontools.load_json(scrapertools.downloadpageWithoutCookies(url))
                except:
                    pass

                if 'overview' in resultado:
                    self.result['overview'] = resultado['overview']
                    ret = self.result['overview']

                if "status_code" in resultado:
                    msg = "Error de tmdb: %s %s" % (resultado["status_code"], resultado["status_message"])
                    logger.debug(msg)

        return ret

    def get_poster(self, tipo_respuesta="str", size="original"):
        """

        @param tipo_respuesta: Tipo de dato devuelto por este metodo. Por defecto "str"
        @type tipo_respuesta: list, str
        @param size: ("w45", "w92", "w154", "w185", "w300", "w342", "w500", "w600", "h632", "w780", "w1280", "original")
            Indica la anchura(w) o altura(h) de la imagen a descargar. Por defecto "original"
        @return: Si el tipo_respuesta es "list" devuelve un listado con todas las urls de las imagenes tipo poster del
            tamaño especificado.
            Si el tipo_respuesta es "str" devuelve la url de la imagen tipo poster, mas valorada, del tamaño
            especificado.
            Si el tamaño especificado no existe se retornan las imagenes al tamaño original.
        @rtype: list, str
        """
        ret = []
        if size not in ("w45", "w92", "w154", "w185", "w300", "w342", "w500", "w600", "h632", "w780", "w1280"):
            size = "original"

        if self.result["poster_path"] is None or self.result["poster_path"] == "":
            poster_path = ""
        else:
            poster_path = 'http://image.tmdb.org/t/p/' + size + self.result["poster_path"]

        if tipo_respuesta == 'str':
            return poster_path
        elif not self.result["id"]:
            return []

        if len(self.result['images_posters']) == 0:
            # Vamos a lanzar una busqueda por id y releer de nuevo
            self.busqueda_id = str(self.result["id"])
            self.__by_id()

        if len(self.result['images_posters']) > 0:
            for i in self.result['images_posters']:
                imagen_path = i['file_path']
                if size != "original":
                    # No podemos pedir tamaños mayores que el original
                    if size[1] == 'w' and int(imagen_path['width']) < int(size[1:]):
                        size = "original"
                    elif size[1] == 'h' and int(imagen_path['height']) < int(size[1:]):
                        size = "original"
                ret.append('http://image.tmdb.org/t/p/' + size + imagen_path)
        else:
            ret.append(poster_path)

        return ret

    def get_backdrop(self, tipo_respuesta="str", size="original"):
        """
        Devuelve las imagenes de tipo backdrop
        @param tipo_respuesta: Tipo de dato devuelto por este metodo. Por defecto "str"
        @type tipo_respuesta: list, str
        @param size: ("w45", "w92", "w154", "w185", "w300", "w342", "w500", "w600", "h632", "w780", "w1280", "original")
            Indica la anchura(w) o altura(h) de la imagen a descargar. Por defecto "original"
        @type size: str
        @return: Si el tipo_respuesta es "list" devuelve un listado con todas las urls de las imagenes tipo backdrop del
            tamaño especificado.
        Si el tipo_respuesta es "str" devuelve la url de la imagen tipo backdrop, mas valorada, del tamaño especificado.
        Si el tamaño especificado no existe se retornan las imagenes al tamaño original.
        @rtype: list, str
        """
        ret = []
        if size not in ("w45", "w92", "w154", "w185", "w300", "w342", "w500", "w600", "h632", "w780", "w1280"):
            size = "original"

        if self.result["backdrop_path"] is None or self.result["backdrop_path"] == "":
            backdrop_path = ""
        else:
            backdrop_path = 'http://image.tmdb.org/t/p/' + size + self.result["backdrop_path"]

        if tipo_respuesta == 'str':
            return backdrop_path
        elif self.result["id"] == "":
            return []

        if len(self.result['images_backdrops']) == 0:
            # Vamos a lanzar una busqueda por id y releer de nuevo todo
            self.busqueda_id = str(self.result["id"])
            self.__by_id()

        if len(self.result['images_backdrops']) > 0:
            for i in self.result['images_backdrops']:
                imagen_path = i['file_path']
                if size != "original":
                    # No podemos pedir tamaños mayores que el original
                    if size[1] == 'w' and int(imagen_path['width']) < int(size[1:]):
                        size = "original"
                    elif size[1] == 'h' and int(imagen_path['height']) < int(size[1:]):
                        size = "original"
                ret.append('http://image.tmdb.org/t/p/' + size + imagen_path)
        else:
            ret.append(backdrop_path)

        return ret

    def get_temporada(self, numtemporada=1):
        # --------------------------------------------------------------------------------------------------------------------------------------------
        #   Parametros:
        #       numtemporada: (int) Numero de temporada. Por defecto 1.
        #   Return: (dic)
        #       Devuelve un dicionario con datos sobre la temporada.
        #       Puede obtener mas informacion sobre los datos devueltos en:
        #           http://docs.themoviedb.apiary.io/#reference/tv-seasons/tvidseasonseasonnumber/get
        #           http://docs.themoviedb.apiary.io/#reference/tv-seasons/tvidseasonseasonnumbercredits/get
        # --------------------------------------------------------------------------------------------------------------------------------------------
        if not self.result["id"] or self.busqueda_tipo != "tv":
            return {}

        numtemporada = int(numtemporada)
        if numtemporada < 0:
            numtemporada = 1

        if not self.temporada.get(numtemporada,{}):
            # Si no hay datos sobre la temporada solicitada, consultar en la web

            # http://api.themoviedb.org/3/tv/1407/season/1?api_key=f7f51775877e0bb6703520952b3c7840&language=es&
            # append_to_response=credits
            url = "http://api.themoviedb.org/3/tv/%s/season/%s?api_key=f7f51775877e0bb6703520952b3c7840&language=%s" \
                  "&append_to_response=credits" % (self.result["id"], numtemporada, self.busqueda_idioma)

            buscando = "id_Tmdb: " + str(self.result["id"]) + " temporada: " + str(numtemporada) + "\nURL: " + url
            logger.info("[Tmdb.py] Buscando " + buscando)
            try:
                self.temporada[numtemporada] = jsontools.load_json(scrapertools.downloadpageWithoutCookies(url))
            except:
                self.temporada[numtemporada] = {"status_code": 15, "status_message": "Failed"}

            if "status_code" in self.temporada[numtemporada]:
                # Se ha producido un error
                msg = "La busqueda de " + buscando + " no dio resultados."
                msg += "\nError de tmdb: %s %s" % (self.temporada[numtemporada]["status_code"], self.temporada[numtemporada]["status_message"])
                logger.debug(msg)
                self.temporada[numtemporada] = {"episodes": {}}



        return self.temporada[numtemporada]

    def get_episodio(self, numtemporada=1, capitulo=1):
        # --------------------------------------------------------------------------------------------------------------------------------------------
        #   Parametros:
        #       numtemporada(opcional): (int) Numero de temporada. Por defecto 1.
        #       capitulo: (int) Numero de capitulo. Por defecto 1.
        #   Return: (dic)
        #       Devuelve un dicionario con los siguientes elementos:
        #           "temporada_nombre", "temporada_sinopsis", "temporada_poster", "temporada_num_episodios"(int),
        #           "temporada_air_date",  "episodio_vote_count", "episodio_vote_average",
        #           "episodio_titulo", "episodio_sinopsis", "episodio_imagen", "episodio_air_date",
        #           "episodio_crew" y "episodio_guest_stars",
        #       Con capitulo == -1 el diccionario solo tendra los elementos referentes a la temporada
        # --------------------------------------------------------------------------------------------------------------------------------------------
        if not self.result["id"] or self.busqueda_tipo != "tv":
            return {}

        try:
            capitulo = int(capitulo)
            numtemporada = int(numtemporada)
        except ValueError:
            logger.debug("El número de episodio o temporada no es valido")
            return {}

        temporada = self.get_temporada(numtemporada)
        if not temporada:
            # Se ha producido un error
            return {}

        if len(temporada["episodes"]) == 0 or len(temporada["episodes"]) < capitulo:
            # Se ha producido un error
            logger.error("Episodio %d de la temporada %d no encontrado." % (capitulo, numtemporada))
            return {}

        ret_dic = dict()
        # Obtener datos para esta temporada
        ret_dic["temporada_nombre"] = temporada["name"]
        ret_dic["temporada_sinopsis"] = temporada["overview"]
        ret_dic["temporada_num_episodios"] = len(temporada["episodes"])
        if temporada["air_date"]:
            date = temporada["air_date"].split("-")
            ret_dic["temporada_air_date"] = date[2] + "/" + date[1] + "/" + date[0]
        else:
            ret_dic["temporada_air_date"] = ""
        if temporada["poster_path"]:
            ret_dic["temporada_poster"] = 'http://image.tmdb.org/t/p/original' + temporada["poster_path"]
        else:
            ret_dic["temporada_poster"] = ""
        dic_aux = temporada.get('credits', {})
        ret_dic["temporada_cast"] = dic_aux.get('cast', [])
        ret_dic["temporada_crew"] = dic_aux.get('crew', [])
        if capitulo == -1:
            # Si solo buscamos datos de la temporada,
            # incluir el equipo tecnico que ha intervenido en algun capitulo
            dic_aux = dict((i['id'], i) for i in ret_dic["temporada_crew"])
            for e in temporada["episodes"]:
                for crew in e['crew']:
                    if crew['id'] not in dic_aux.keys():
                        dic_aux[crew['id']] = crew
            ret_dic["temporada_crew"] = dic_aux.values()



        # Obtener datos del capitulo si procede
        if capitulo != -1:
            episodio = temporada["episodes"][capitulo - 1]
            ret_dic["episodio_titulo"] = episodio["name"]
            ret_dic["episodio_sinopsis"] = episodio["overview"]
            if episodio["air_date"]:
                date = episodio["air_date"].split("-")
                ret_dic["episodio_air_date"] = date[2] + "/" + date[1] + "/" + date[0]
            else:
                ret_dic["episodio_air_date"] = ""
            ret_dic["episodio_crew"] = episodio["crew"]
            ret_dic["episodio_guest_stars"] = episodio["guest_stars"]
            ret_dic["episodio_vote_count"] = episodio["vote_count"]
            ret_dic["episodio_vote_average"] = episodio["vote_average"]
            if episodio["still_path"]:
                ret_dic["episodio_imagen"] = 'http://image.tmdb.org/t/p/original' + episodio["still_path"]
            else:
                ret_dic["episodio_imagen"] = ""


        return ret_dic

    def get_videos(self):
        """
        :return: Devuelve una lista ordenada (idioma/resolucion/tipo) de objetos Dict en la que cada uno de
        sus elementos corresponde con un trailer, teaser o clip de youtube.
        :rtype: list of Dict
        """
        ret = []
        if self.result['id']:
            if self.result['videos']:
                self.result["videos"] = self.result["videos"]['results']
            else:
                # Primera búsqueda de videos en el idioma de busqueda
                url = "http://api.themoviedb.org/3/%s/%s/videos?api_key=f7f51775877e0bb6703520952b3c7840&language=%s" \
                      % (self.busqueda_tipo, self.result['id'], self.busqueda_idioma)
                try:
                    dict_videos = jsontools.load_json(scrapertools.downloadpageWithoutCookies(url))
                except:
                    pass

                if dict_videos['results']:
                    dict_videos['results'] = sorted(dict_videos['results'], key=lambda x: (x['type'], x['size']))
                    self.result["videos"] = dict_videos['results']

            # Si el idioma de busqueda no es ingles, hacer una segunda búsqueda de videos en inglés
            if self.busqueda_idioma != 'en':
                url = "http://api.themoviedb.org/3/%s/%s/videos?api_key=f7f51775877e0bb6703520952b3c7840" \
                      % (self.busqueda_tipo, self.result['id'])
                try:
                    dict_videos = jsontools.load_json(scrapertools.downloadpageWithoutCookies(url))
                except:
                    pass

                if dict_videos['results']:
                    dict_videos['results'] = sorted(dict_videos['results'], key=lambda x: (x['type'], x['size']))
                    self.result["videos"].extend(dict_videos['results'])

            if "status_code" in dict_videos:
                msg = "Error de tmdb: %s %s" % (dict_videos["status_code"], dict_videos["status_message"])
                logger.debug(msg)

            # Si las busqueda han obtenido resultados devolver un listado de objetos
            for i in self.result['videos']:
                if i['site'] == "YouTube":
                    ret.append({'name': i['name'],
                                'url': "https://www.youtube.com/watch?v=%s" % i['key'],
                                'size': str(i['size']),
                                'type': i['type'],
                                'language': i['iso_639_1']})

        return ret

    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
        """

        if infoLabels:
            ret_infoLabels = InfoLabels(infoLabels)
        else:
            ret_infoLabels = InfoLabels()

        # Iniciar listados
        l_country = [i.strip() for i in ret_infoLabels['country'].split(',') if ret_infoLabels['country']]
        l_director = [i.strip() for i in ret_infoLabels['director'].split(',') if ret_infoLabels['director']]
        l_writer = [i.strip() for i in ret_infoLabels['writer'].split(',') if ret_infoLabels['writer']]
        l_castandrole = ret_infoLabels.get('castandrole', [])

        if not origen:
            origen = self.result

        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 = origen.items()

        # Informacion Temporada/episodio
        if ret_infoLabels['season'] and self.temporada.get(ret_infoLabels['season']):
            # Si hay datos cargados de la temporada indicada
            episodio = -1
            if ret_infoLabels['episode']: episodio = ret_infoLabels['episode']

            items.extend(self.get_episodio(ret_infoLabels['season'], episodio).items())


        for k, v in items:
            if not v:
                continue
            elif type(v) == str:
                v = re.sub(r"\n|\r|\t", "", v)

            if k == 'overview':
                if origen:
                    ret_infoLabels['plot'] = v
                else:
                    ret_infoLabels['plot'] = self.get_sinopsis()

            elif k == 'runtime':
                ret_infoLabels['duration'] = int(v) * 60

            elif k == 'release_date':
                ret_infoLabels['year'] = int(v[:4])
                ret_infoLabels['release_date'] = v.split("-")[2] + "/" + v.split("-")[1] + "/" + v.split("-")[0]

            elif k == 'first_air_date':
                ret_infoLabels['year'] = int(v[:4])
                ret_infoLabels['aired'] = v.split("-")[2] + "/" + v.split("-")[1] + "/" + v.split("-")[0]
                ret_infoLabels['premiered'] = ret_infoLabels['aired']

            elif k == 'original_title' or k == 'original_name':
                ret_infoLabels['originaltitle'] = v

            elif k == 'vote_average':
                ret_infoLabels['rating'] = float(v)

            elif k == 'vote_count':
                ret_infoLabels['votes'] = v

            elif k == 'poster_path':
                ret_infoLabels['thumbnail'] = 'http://image.tmdb.org/t/p/original' + v

            elif k == 'backdrop_path':
                ret_infoLabels['fanart'] = 'http://image.tmdb.org/t/p/original' + v

            elif k == 'id':
                ret_infoLabels['tmdb_id'] = v

            elif k == 'imdb_id':
                ret_infoLabels['imdb_id'] = v

            elif k == 'external_ids':
                if 'tvdb_id' in v: ret_infoLabels['tvdb_id'] = v['tvdb_id']
                if 'imdb_id' in v: ret_infoLabels['imdb_id'] = v['imdb_id']

            elif k in ['genres', "genre_ids", "genre"]:
                ret_infoLabels['genre'] = self.get_generos(origen)

            elif k == 'name' or k == 'title':
                ret_infoLabels['title'] = v

            elif k == 'production_companies':
                ret_infoLabels['studio'] = ", ".join(i['name'] for i in v)

            elif k == 'credits_cast' or k == 'temporada_cast' or k == 'episodio_guest_stars':
                dic_aux = dict((name, character) for (name, character) in l_castandrole)
                l_castandrole.extend([(p['name'], p['character']) for p in v if p['name'] not in dic_aux.keys()])

            elif k == 'videos':
                if not isinstance(v, list):
                    v = v.get('result', [])
                for i in v:
                    if i.get("site", "") == "YouTube":
                        ret_infoLabels['trailer'] = "https://www.youtube.com/watch?v=" + v[0]["key"]
                        break

            elif k == 'production_countries' or k == 'origin_country':
                if isinstance(v,str):
                    l_country = list(set(l_country + v.split(',')))

                elif isinstance(v,list) and len(v) > 0:
                    if isinstance(v[0],str):
                        l_country = list(set(l_country + v))
                    elif isinstance(v[0],dict):
                        # {'iso_3166_1': 'FR', 'name':'France'}
                        for i in v:
                            if i.has_key('iso_3166_1'):
                                pais = Tmdb.dic_country.get(i['iso_3166_1'], i['iso_3166_1'])
                                l_country = list(set(l_country + [pais]))

            elif k == 'credits_crew' or k == 'episodio_crew' or k == 'temporada_crew':
                for crew in v:
                    if crew['job'].lower() == 'director':
                        l_director = list(set(l_director + [crew['name']]))

                    elif crew['job'].lower() in ('screenplay', 'writer'):
                        l_writer = list(set(l_writer + [crew['name']]))

            elif k == 'created_by':
                for crew in v:
                    l_writer = list(set(l_writer + [crew['name']]))


            elif isinstance(v,str) or isinstance(v,int) or isinstance(v,float):
                ret_infoLabels[k] = v

            else:
                #logger.debug("Atributos no añadidos: " + k +'= '+ str(v))
                pass

        # Ordenar las listas y convertirlas en str si es necesario
        if l_castandrole:
            ret_infoLabels['castandrole'] = sorted(l_castandrole, key=lambda tup: tup[0])
        if l_country:
            ret_infoLabels['country'] = ', '.join(sorted(l_country))
        if l_director:
            ret_infoLabels['director'] = ', '.join(sorted(l_director))
        if l_writer:
            ret_infoLabels['writer'] = ', '.join(sorted(l_writer))


        return ret_infoLabels
