- Nuove visualizzazioni Server\n- Fix Gestione Viste\n- Aggiunto Pluto TV\n- Fix e migliorie varie\n\n
1875 lines
86 KiB
Python
1875 lines
86 KiB
Python
# -*- coding: utf-8 -*-
|
||
|
||
import datetime
|
||
import sys, requests
|
||
PY3 = False
|
||
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
|
||
|
||
if PY3:
|
||
import urllib.parse as urllib # It is very slow in PY2. In PY3 it is native
|
||
from concurrent import futures
|
||
else:
|
||
import urllib # We use the native of PY2 which is faster
|
||
from concurrent_py2 import futures
|
||
|
||
from future.builtins import range
|
||
from future.builtins import object
|
||
|
||
import ast, copy, re, time
|
||
|
||
from core import filetools, httptools, jsontools, scrapertools
|
||
from core.item import InfoLabels
|
||
from platformcode import config, logger, platformtools
|
||
import threading
|
||
|
||
info_language = ["de", "en", "es", "fr", "it", "pt"] # from videolibrary.json
|
||
def_lang = info_language[config.get_setting("info_language", "videolibrary")]
|
||
|
||
host = 'https://api.themoviedb.org/3'
|
||
api = 'a1ab8b8669da03637a4b98fa39c39228'
|
||
|
||
"""
|
||
Set of functions related to infoLabels.
|
||
version 1.0:
|
||
Initial version
|
||
|
||
Include:
|
||
- set_infoLabels (source, seekTmdb, search_language): Gets and sets (item.infoLabels) the extra data of one or several series, chapters or movies.
|
||
- set_infoLabels_item (item, seekTmdb, search_language): Gets and sets (item.infoLabels) the extra data of a series, chapter or movie.
|
||
- set_infoLabels_itemlist (item_list, seekTmdb, search_language): Gets and sets (item.infoLabels) the data extras from a list of series, chapters or movies.
|
||
- infoLabels_tostring (item): Returns a str with the list ordered with the item's infoLabels
|
||
|
||
Usage:
|
||
- tmdb.set_infoLabels (item, seekTmdb = True)
|
||
|
||
Get basic data from a movie:
|
||
Before calling the set_infoLabels method the title to search for must be in item.contentTitle and the year in item.infoLabels ['year'].
|
||
|
||
Obtain basic data from a series:
|
||
Before calling the set_infoLabels method the title to search for must be in item.show or in item.contentSerieName.
|
||
|
||
Get more data from a movie or series:
|
||
After obtaining the basic data in item.infoLabels ['tmdb'] we will have the code of the series or movie.
|
||
We could also directly set this code, if known, or use the corresponding code of:
|
||
IMDB (in item.infoLabels ['IMDBNumber'] or item.infoLabels ['code'] or item.infoLabels ['imdb_id']), TVDB (only series, in item.infoLabels ['tvdb_id']),
|
||
Freebase (series only, on item.infoLabels ['freebase_mid']), TVRage (series only, on item.infoLabels ['tvrage_id'])
|
||
|
||
Get data from a season:
|
||
Before calling the set_infoLabels method the series title must be in item.show or in item.contentSerieName,
|
||
the series TMDB code must be in item.infoLabels ['tmdb'] (it can be set automatically by the basic data query)
|
||
and the season number must be in item.infoLabels ['season'].
|
||
|
||
Get data from an episode:
|
||
Before calling the set_infoLabels method the series title must be in item.show or in item.contentSerieName,
|
||
the TMDB code of the series must be in item.infoLabels ['tmdb'] (it can be set automatically using the basic data query),
|
||
the season number must be in item.infoLabels ['season'] and the episode number must be in item.infoLabels ['episode'].
|
||
"""
|
||
otmdb_global = None
|
||
from core import db
|
||
|
||
|
||
def clean_cache():
|
||
db['tmdb_cache'].clear()
|
||
|
||
|
||
# The function name is the name of the decorator and receives the function that decorates.
|
||
def cache_response(fn):
|
||
logger.debug()
|
||
|
||
# import time
|
||
# start_time = time.time()
|
||
|
||
def wrapper(*args, **kwargs):
|
||
def check_expired(saved_date):
|
||
valided = False
|
||
|
||
cache_expire = config.get_setting("tmdb_cache_expire", default=0)
|
||
current_date = datetime.datetime.now()
|
||
elapsed = current_date - saved_date
|
||
|
||
# 1 day
|
||
if cache_expire == 0:
|
||
if elapsed > datetime.timedelta(days=1):
|
||
valided = False
|
||
else:
|
||
valided = True
|
||
# 7 days
|
||
elif cache_expire == 1:
|
||
if elapsed > datetime.timedelta(days=7):
|
||
valided = False
|
||
else:
|
||
valided = True
|
||
|
||
# 15 days
|
||
elif cache_expire == 2:
|
||
if elapsed > datetime.timedelta(days=15):
|
||
valided = False
|
||
else:
|
||
valided = True
|
||
|
||
# 1 month - 30 days
|
||
elif cache_expire == 3:
|
||
# we do not take into account February or months with 31 days
|
||
if elapsed > datetime.timedelta(days=30):
|
||
valided = False
|
||
else:
|
||
valided = True
|
||
# no expire
|
||
elif cache_expire == 4:
|
||
valided = True
|
||
|
||
return valided
|
||
|
||
result = {}
|
||
try:
|
||
|
||
# cache is not active
|
||
if not config.get_setting("tmdb_cache", default=False) or not kwargs.get('cache', True):
|
||
logger.debug('no cache')
|
||
result = fn(*args)
|
||
else:
|
||
|
||
url = args[0].replace('&year=-', '').replace('&primary_release_year=-', '').replace('&first_air_date_year=-', '')
|
||
# if PY3: url = str.encode(url)
|
||
|
||
row = db['tmdb_cache'].get(url)
|
||
|
||
if row and check_expired(row[1]):
|
||
result = row[0]
|
||
|
||
# si no se ha obtenido información, llamamos a la funcion
|
||
if not result.get('results'):
|
||
result = fn(*args)
|
||
db['tmdb_cache'][url] = [result, datetime.datetime.now()]
|
||
|
||
# elapsed_time = time.time() - start_time
|
||
# logger.debug("TARDADO %s" % elapsed_time)
|
||
|
||
# error getting data
|
||
except Exception as ex:
|
||
message = "An exception of type {} occured. Arguments:\n{}".format(type(ex).__name__, repr(ex.args))
|
||
logger.error("error in:", message)
|
||
|
||
return result
|
||
|
||
return wrapper
|
||
|
||
|
||
def set_infoLabels(source, seekTmdb=True, search_language=def_lang, forced=False):
|
||
"""
|
||
Depending on the data type of source, it obtains and sets (item.infoLabels) the extra data of one or more series, chapters or movies.
|
||
|
||
@param source: variable that contains the information to set infoLabels
|
||
@type source: list, item
|
||
@param seekTmdb: if it is True, it searches www.themoviedb.org to obtain the data, otherwise it obtains the data of the Item itself.
|
||
@type seekTmdb: bool
|
||
@param search_language: set the language value in case of search at www.themoviedb.org
|
||
@type search_language: str
|
||
@return: a number or list of numbers with the result of the calls to set_infoLabels_item
|
||
@rtype: int, list
|
||
"""
|
||
|
||
if not config.get_setting('tmdb_active') and not forced:
|
||
return
|
||
|
||
start_time = time.time()
|
||
if type(source) == list:
|
||
ret = set_infoLabels_itemlist(source, seekTmdb, search_language)
|
||
logger.debug("The data of {} links were obtained in {} seconds".format(len(source), time.time() - start_time))
|
||
else:
|
||
ret = set_infoLabels_item(source, seekTmdb, search_language)
|
||
logger.debug("The data were obtained in {} seconds".format(time.time() - start_time))
|
||
return ret
|
||
|
||
|
||
def set_infoLabels_itemlist(itemlist, seekTmdb=False, search_language=def_lang, forced=False):
|
||
"""
|
||
Concurrently, it gets the data of the items included in the item_list.
|
||
|
||
The API in the past had a limit of 40 requests per IP every 10 '', now there's no limit (https://developers.themoviedb.org/3/getting-started/request-rate-limiting)
|
||
If a limit will be re-added uncomment "tmdb_threads" and related code
|
||
|
||
@param item_list: list of Item objects that represent movies, series or chapters. The infoLabels attribute of each Item object will be modified including the extra localized data.
|
||
@type item_list: list
|
||
@param seekTmdb: If it is True, it searches www.themoviedb.org to obtain the data, otherwise it obtains the data of the Item itself if they exist.
|
||
@type seekTmdb: bool
|
||
@param search_language: Language code according to ISO 639-1, in case of search at www.themoviedb.org.
|
||
@type search_language: str
|
||
|
||
@return: A list of numbers whose absolute value represents the number of elements included in the infoLabels attribute of each Item. This number will be positive if the data has been obtained from www.themoviedb.org and negative otherwise.
|
||
@rtype: list
|
||
"""
|
||
|
||
if not config.get_setting('tmdb_active') and not forced:
|
||
return
|
||
|
||
r_list = list()
|
||
|
||
def sub_thread(_item, _i, _seekTmdb):
|
||
ret = 0
|
||
try:
|
||
ret = set_infoLabels_item(_item, _seekTmdb, search_language)
|
||
except:
|
||
import traceback
|
||
logger.error(traceback.format_exc(1))
|
||
|
||
return (_i, _item, ret)
|
||
# from core.support import dbg;dbg()
|
||
# for i, item in enumerate(itemlist):
|
||
# r_list.append(sub_thread(item, i, seekTmdb))
|
||
with futures.ThreadPoolExecutor() as executor:
|
||
searchList = [executor.submit(sub_thread, item, i, seekTmdb) for i, item in enumerate(itemlist)]
|
||
for res in futures.as_completed(searchList):
|
||
r_list.append(res.result())
|
||
|
||
|
||
# Sort results list by call order to keep the same order q itemlist
|
||
r_list.sort(key=lambda i: i[0])
|
||
|
||
# Rebuild and return list only with results of individual calls
|
||
return [it[2] for it in r_list]
|
||
|
||
|
||
def set_infoLabels_item(item, seekTmdb=True, search_language=def_lang):
|
||
"""
|
||
Gets and sets (item.infoLabels) the extra data of a series, chapter or movie.
|
||
|
||
@param item: Item object that represents a movie, series or chapter. The infoLabels attribute will be modified including the extra localized data.
|
||
@type item: Item
|
||
@param seekTmdb: If it is True, it searches www.themoviedb.org to obtain the data, otherwise it obtains the data of the Item itself if they exist.
|
||
@type seekTmdb: bool
|
||
@param search_language: Language code according to ISO 639-1, in case of search at www.themoviedb.org.
|
||
@type search_language: str
|
||
@return: A number whose absolute value represents the number of elements included in the item.infoLabels attribute. This number will be positive if the data has been obtained from www.themoviedb.org and negative otherwise.
|
||
@rtype: int
|
||
"""
|
||
global otmdb_global
|
||
|
||
def read_data(otmdb_aux):
|
||
# item.infoLabels = otmdb_aux.get_infoLabels(item.infoLabels)
|
||
infoLabels = otmdb_aux.get_infoLabels(item.infoLabels)
|
||
if not infoLabels.get('plot'): infoLabels['plot'] = otmdb_aux.get_plot('en')
|
||
item.infoLabels = infoLabels
|
||
if item.infoLabels.get('thumbnail'):
|
||
item.thumbnail = item.infoLabels['thumbnail']
|
||
if item.infoLabels.get('fanart'):
|
||
item.fanart = item.infoLabels['fanart']
|
||
|
||
if seekTmdb:
|
||
def search(otmdb_global, search_type):
|
||
if item.infoLabels.get('season'):
|
||
try:
|
||
seasonNumber = int(item.infoLabels['season'])
|
||
except ValueError:
|
||
logger.debug("The season number is not valid.")
|
||
return -1 * len(item.infoLabels)
|
||
|
||
if not otmdb_global or (item.infoLabels['tmdb_id'] and str(otmdb_global.result.get("id")) != item.infoLabels['tmdb_id']) \
|
||
or (otmdb_global.searched_text and otmdb_global.searched_text != item.infoLabels['tvshowtitle']):
|
||
if item.infoLabels['tmdb_id']:
|
||
otmdb_global = Tmdb(id_Tmdb=item.infoLabels['tmdb_id'], search_type=search_type,
|
||
search_language=search_language)
|
||
else:
|
||
otmdb_global = Tmdb(searched_text=scrapertools.unescape(item.infoLabels['tvshowtitle']), search_type=search_type,
|
||
search_language=search_language, year=item.infoLabels['year'])
|
||
|
||
read_data(otmdb_global)
|
||
|
||
if item.infoLabels['episode']:
|
||
try:
|
||
ep = int(item.infoLabels['episode'])
|
||
except ValueError:
|
||
logger.debug("The episode number ({}) is not valid".format(repr(item.infoLabels['episode'])))
|
||
return -1 * len(item.infoLabels)
|
||
|
||
# We have valid season number and episode number...
|
||
# ... search episode data
|
||
item.infoLabels['mediatype'] = 'episode'
|
||
episode = otmdb_global.get_episode(seasonNumber, ep)
|
||
|
||
if episode:
|
||
# Update data
|
||
read_data(otmdb_global)
|
||
item.infoLabels['mediatype'] = 'episode'
|
||
if episode.get('episode_title'):
|
||
item.infoLabels['title'] = episode['episode_title']
|
||
if episode.get('episode_plot'):
|
||
item.infoLabels['plot'] = episode['episode_plot']
|
||
if episode.get('episode_image'):
|
||
item.infoLabels['poster_path'] = episode['episode_image']
|
||
item.thumbnail = item.infoLabels['poster_path']
|
||
if episode.get('episode_air_date'):
|
||
item.infoLabels['aired'] = episode['episode_air_date']
|
||
if episode.get('episode_vote_average'):
|
||
item.infoLabels['rating'] = episode['episode_vote_average']
|
||
if episode.get('episode_vote_count'):
|
||
item.infoLabels['votes'] = episode['episode_vote_count']
|
||
if episode.get('episode_id'):
|
||
item.infoLabels['episode_id'] = episode['episode_id']
|
||
if episode.get('episode_imdb_id'):
|
||
item.infoLabels['episode_imdb_id'] = episode['episode_imdb_id']
|
||
if episode.get('episode_tvdb_id'):
|
||
item.infoLabels['episode_tvdb_id'] = episode['episode_tvdb_id']
|
||
if episode.get('episode_posters'):
|
||
item.infoLabels['posters'] = episode['episode_posters']
|
||
|
||
return len(item.infoLabels)
|
||
|
||
else:
|
||
# We have a valid season number but no episode number...
|
||
# ... search season data
|
||
item.infoLabels['mediatype'] = 'season'
|
||
season = otmdb_global.get_season(seasonNumber)
|
||
# enseason = otmdb_global.get_season(seasonNumber, language='en')
|
||
if not isinstance(season, dict):
|
||
season = ast.literal_eval(season.decode('utf-8'))
|
||
# if not isinstance(enseason, dict):
|
||
# enseason = ast.literal_eval(enseason.decode('utf-8'))
|
||
|
||
if season:
|
||
# Update data
|
||
read_data(otmdb_global)
|
||
seasonTitle = season.get("name", '')
|
||
seasonPlot = season.get("overview" , '')
|
||
seasonDate = season.get("air_date", '')
|
||
seasonPoster = season.get('poster_path', '')
|
||
seasonPosters = []
|
||
for image in season['images']['posters']:
|
||
seasonPosters.append('https://image.tmdb.org/t/p/original' + image['file_path'])
|
||
|
||
item.infoLabels['title'] = seasonTitle
|
||
item.infoLabels['plot'] = seasonPlot
|
||
date = seasonDate
|
||
if date:
|
||
date.split('-')
|
||
item.infoLabels['aired'] = date[2] + "/" + date[1] + "/" + date[0]
|
||
|
||
if seasonPoster:
|
||
item.infoLabels['poster_path'] = 'https://image.tmdb.org/t/p/original' + seasonPoster
|
||
item.thumbnail = item.infoLabels['poster_path']
|
||
if seasonPosters:
|
||
if seasonPoster: seasonPosters.insert(0, seasonPoster)
|
||
item.infoLabels['posters'] = seasonPosters
|
||
|
||
return len(item.infoLabels)
|
||
|
||
# Search...
|
||
else:
|
||
otmdb = copy.copy(otmdb_global)
|
||
# Search by ID...
|
||
if item.infoLabels['tmdb_id']:
|
||
# ...Search for tmdb_id
|
||
otmdb = Tmdb(id_Tmdb=item.infoLabels['tmdb_id'], search_type=search_type,
|
||
search_language=search_language)
|
||
|
||
elif item.infoLabels['imdb_id']:
|
||
# ...Search by imdb code
|
||
otmdb = Tmdb(external_id=item.infoLabels['imdb_id'], external_source="imdb_id", search_type=search_type,
|
||
search_language=search_language)
|
||
|
||
elif search_type == 'tv': # bsearch with other codes
|
||
if item.infoLabels['tvdb_id']:
|
||
# ...Search for tvdb_id
|
||
otmdb = Tmdb(external_id=item.infoLabels['tvdb_id'], external_source="tvdb_id",
|
||
search_type=search_type, search_language=search_language)
|
||
elif item.infoLabels['freebase_mid']:
|
||
# ...Search for freebase_mid
|
||
otmdb = Tmdb(external_id=item.infoLabels['freebase_mid'], external_source="freebase_mid",
|
||
search_type=search_type, search_language=search_language)
|
||
elif item.infoLabels['freebase_id']:
|
||
# ...Search by freebase_id
|
||
otmdb = Tmdb(external_id=item.infoLabels['freebase_id'], external_source="freebase_id",
|
||
search_type=search_type, search_language=search_language)
|
||
elif item.infoLabels['tvrage_id']:
|
||
# ...Search by tvrage_id
|
||
otmdb = Tmdb(external_id=item.infoLabels['tvrage_id'], external_source="tvrage_id",
|
||
search_type=search_type, search_language=search_language)
|
||
|
||
# if otmdb is None:
|
||
if not item.infoLabels['tmdb_id'] and not item.infoLabels['imdb_id'] and not item.infoLabels['tvdb_id']\
|
||
and not item.infoLabels['freebase_mid'] and not item.infoLabels['freebase_id'] and not item.infoLabels['tvrage_id']:
|
||
# Could not search by ID ...
|
||
# do it by title
|
||
if search_type == 'tv':
|
||
# Serial search by title and filtering your results if necessary
|
||
searched_title = scrapertools.unescape(item.infoLabels['tvshowtitle'])
|
||
else:
|
||
# Movie search by title ...
|
||
# if item.infoLabels['year'] or item.infoLabels['filtro']:
|
||
# ...and year or filter
|
||
searched_title = scrapertools.unescape(item.infoLabels['title'])
|
||
# from core.support import dbg;dbg()
|
||
otmdb = Tmdb(searched_text=searched_title, search_type=search_type, search_language=search_language,
|
||
filtro=item.infoLabels.get('filtro', {}), year=item.infoLabels['year'])
|
||
if otmdb is not None and not otmdb.get_id():
|
||
otmdb = Tmdb(searched_text=searched_title, search_type=search_type, search_language=search_language,
|
||
filtro=item.infoLabels.get('filtro', {}))
|
||
if otmdb is not None:
|
||
if otmdb.get_id() and config.get_setting("tmdb_plus_info", default=False):
|
||
# If the search has been successful and you are not looking for a list of items,
|
||
# carry out another search to expand the information
|
||
if search_type == 'multi':
|
||
search_type = otmdb.result.get('media_type')
|
||
|
||
otmdb = Tmdb(id_Tmdb=otmdb.result.get("id"), search_type=search_type,
|
||
search_language=search_language)
|
||
|
||
if otmdb is not None and otmdb.get_id():
|
||
# The search has found a valid result
|
||
read_data(otmdb)
|
||
return len(item.infoLabels)
|
||
|
||
def unify():
|
||
new_title = scrapertools.title_unify(item.fulltitle)
|
||
if new_title != item.fulltitle:
|
||
item.infoLabels['tvshowtitle'] = scrapertools.title_unify(item.infoLabels['tvshowtitle'])
|
||
item.infoLabels['title'] = scrapertools.title_unify(item.infoLabels['title'])
|
||
# item.fulltitle = new_title
|
||
return True
|
||
# We check what type of content it is...
|
||
# from core.support import dbg;dbg()
|
||
if item.contentType == 'movie':
|
||
search_type = 'movie'
|
||
elif item.contentType == 'undefined': # don't know
|
||
search_type = 'multi'
|
||
else:
|
||
search_type = 'tv'
|
||
|
||
ret = search(otmdb_global, search_type)
|
||
if not ret: # try with unified title
|
||
backup = [item.fulltitle, item.infoLabels['tvshowtitle'], item.infoLabels['title']]
|
||
if unify():
|
||
ret = search(otmdb_global, search_type)
|
||
if not ret:
|
||
item.fulltitle, item.infoLabels['tvshowtitle'], item.infoLabels['title'] = backup
|
||
return ret
|
||
# Search in tmdb is deactivated or has not given result
|
||
# item.contentType = item.infoLabels['mediatype']
|
||
return -1 * len(item.infoLabels)
|
||
|
||
|
||
def find_and_set_infoLabels(item):
|
||
logger.debug()
|
||
|
||
global otmdb_global
|
||
tmdb_result = None
|
||
|
||
if item.contentType == "movie":
|
||
search_type = "movie"
|
||
content_type = config.get_localized_string(60247)
|
||
title = item.contentTitle
|
||
else:
|
||
search_type = "tv"
|
||
content_type = config.get_localized_string(60298)
|
||
title = item.contentSerieName
|
||
|
||
# If the title includes the (year) we will remove it
|
||
year = scrapertools.find_single_match(title, "^.+?\s*(\(\d{4}\))$")
|
||
if year:
|
||
title = title.replace(year, "").strip()
|
||
item.infoLabels['year'] = year[1:-1]
|
||
|
||
if not item.infoLabels.get("tmdb_id") or not item.infoLabels.get("tmdb_id")[0].isdigit():
|
||
if item.infoLabels.get("imdb_id"): otmdb_global = Tmdb(external_id=item.infoLabels.get("imdb_id"), external_source="imdb_id", search_type=search_type)
|
||
else: otmdb_global = Tmdb(searched_text=scrapertools.unescape(title), search_type=search_type, year=item.infoLabels['year'])
|
||
|
||
elif not otmdb_global or str(otmdb_global.result.get("id")) != item.infoLabels['tmdb_id']:
|
||
otmdb_global = Tmdb(id_Tmdb=item.infoLabels['tmdb_id'], search_type=search_type, search_language=def_lang)
|
||
|
||
results = otmdb_global.get_list_results()
|
||
if len(results) > 1:
|
||
# select tmdb_id at the first position
|
||
if item.infoLabels['selected_tmdb_id']:
|
||
results.insert(0, results.pop([r.get('id') for r in results].index(int(item.infoLabels['selected_tmdb_id']))))
|
||
tmdb_result = platformtools.show_video_info(results, item=item, caption= content_type % title)
|
||
elif len(results) > 0:
|
||
tmdb_result = results[0]
|
||
|
||
if isinstance(item.infoLabels, InfoLabels):
|
||
infoLabels = item.infoLabels
|
||
else:
|
||
infoLabels = InfoLabels()
|
||
|
||
if tmdb_result:
|
||
infoLabels['tmdb_id'] = tmdb_result['id']
|
||
# all look if it can be removed and get only from get_nfo ()
|
||
infoLabels['url_scraper'] = ["https://www.themoviedb.org/{}/{}".format(search_type, infoLabels['tmdb_id'])]
|
||
if infoLabels['tvdb_id']:
|
||
infoLabels['url_scraper'].append("http://thetvdb.com/index.php?tab=series&id=" + infoLabels['tvdb_id'])
|
||
item.infoLabels = infoLabels
|
||
set_infoLabels_item(item)
|
||
|
||
return True
|
||
|
||
else:
|
||
item.infoLabels = infoLabels
|
||
return False
|
||
|
||
|
||
def get_nfo(item, search_groups=False):
|
||
"""
|
||
Returns the information necessary for the result to be scraped into the kodi video library, for tmdb it works only by passing it the url.
|
||
@param item: element that contains the data necessary to generate the info
|
||
@type item: Item
|
||
@rtype: str
|
||
@return:
|
||
"""
|
||
|
||
if search_groups:
|
||
from platformcode.autorenumber import RENUMBER, GROUP
|
||
path = filetools.join(config.get_data_path(), "settings_channels", item.channel + "_data.json")
|
||
if filetools.exists(path):
|
||
g = jsontools.load(filetools.read(path)).get(RENUMBER,{}).get(item.fulltitle.strip(),{}).get(GROUP,'')
|
||
if g:
|
||
if type(g) == list: g = ', '.join(g)
|
||
return g + '\n'
|
||
|
||
groups = get_groups(item)
|
||
|
||
if groups:
|
||
Id = select_group(groups, item)
|
||
if Id == 'original':
|
||
info_nfo = ', '.join(item.infoLabels['url_scraper'])
|
||
return info_nfo + '\n'
|
||
elif Id :
|
||
info_nfo = 'https://www.themoviedb.org/tv/{}/episode_group/{}'.format(item.infoLabels['tmdb_id'], Id)
|
||
return info_nfo + '\n'
|
||
else: return
|
||
|
||
info_nfo = ', '.join(item.infoLabels['url_scraper'])
|
||
|
||
return info_nfo + '\n'
|
||
|
||
def get_groups(item):
|
||
valid_groups = []
|
||
|
||
url = '{}/tv/{}/episode_groups?api_key={}&language={}'.format(host, item.infoLabels['tmdb_id'], api, def_lang)
|
||
groups = requests.get(url).json().get('results',[])
|
||
|
||
for g in groups:
|
||
seasons = []
|
||
add = False
|
||
Id = g.get('id','')
|
||
group = get_group(Id)
|
||
for gr in group:
|
||
if gr['episodes']:
|
||
season = gr['episodes'][0]['season_number']
|
||
if season not in seasons:
|
||
seasons.append(season)
|
||
add = True
|
||
else:
|
||
add = False
|
||
break
|
||
if add: valid_groups.append(g)
|
||
return valid_groups
|
||
|
||
def select_group(groups, item):
|
||
selected = -1
|
||
url = '{}/tv/{}?api_key={}&language={}'.format(host, item.infoLabels['tmdb_id'], api, def_lang)
|
||
res = requests.get(url).json()
|
||
selections = [['Original',res.get('number_of_seasons',0), res.get('number_of_episodes',0), '', item.thumbnail]]
|
||
ids = ['original']
|
||
for group in groups:
|
||
ID = group.get('id','')
|
||
if ID:
|
||
selections.append([group.get('name',''), group.get('group_count',0), group.get('episode_count',0), group.get('description',''), item.thumbnail])
|
||
ids.append(ID)
|
||
if selections and ids:
|
||
selected = platformtools.dialog_select_group(config.get_localized_string(70831), selections)
|
||
if selected > -1:
|
||
return ids[selected]
|
||
return ''
|
||
|
||
def get_group(Id):
|
||
# from core.support import dbg;dbg()
|
||
url = '{}/tv/episode_group/{}?api_key={}&language={}'.format(host, Id, api, def_lang)
|
||
group = requests.get(url).json().get('groups',[])
|
||
return group
|
||
|
||
def completar_codigos(item):
|
||
"""
|
||
If necessary, check if the tvdb identifier exists and if it does not exist try to find it
|
||
"""
|
||
if item.contentType != "movie" and not item.infoLabels['tvdb_id']:
|
||
# Launch search for imdb_id on tvdb
|
||
from core.tvdb import Tvdb
|
||
ob = Tvdb(imdb_id=item.infoLabels['imdb_id'])
|
||
item.infoLabels['tvdb_id'] = ob.get_id()
|
||
if item.infoLabels['tvdb_id']:
|
||
url_scraper = "http://thetvdb.com/index.php?tab=series&id=" + item.infoLabels['tvdb_id']
|
||
if url_scraper not in item.infoLabels['url_scraper']:
|
||
item.infoLabels['url_scraper'].append(url_scraper)
|
||
|
||
|
||
def discovery(item, dict_=False, cast=False):
|
||
from core.item import Item
|
||
|
||
if dict_:
|
||
if item.page:
|
||
if not item.discovery: item.discovery={}
|
||
item.discovery['page'] = item.page
|
||
listado = Tmdb(discover = dict_, cast=cast)
|
||
|
||
elif item.search_type == 'discover':
|
||
listado = Tmdb(discover={'url':'discover/' + item.type, 'with_genres':item.list_type, 'language':def_lang, 'page':item.page})
|
||
|
||
elif item.search_type == 'list':
|
||
if item.page == '':
|
||
item.page = '1'
|
||
listado = Tmdb(discover={'url': item.list_type, 'language':def_lang, 'page':item.page})
|
||
|
||
return listado
|
||
|
||
def get_dic_genres(search_type):
|
||
lang = def_lang
|
||
# from core.support import dbg;dbg()
|
||
genres = Tmdb(search_type=search_type)
|
||
|
||
return genres.dic_genres[lang]
|
||
|
||
|
||
# Auxiliary class
|
||
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):
|
||
"""
|
||
default values in case the requested key does not exist
|
||
"""
|
||
if key in ['genre_ids', 'genre', 'genres']:
|
||
return list()
|
||
|
||
elif key == 'images_posters':
|
||
posters = dict()
|
||
if 'images' in list(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 list(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 list(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:
|
||
# The rest of the keys return empty strings by default
|
||
return ""
|
||
|
||
def __str__(self):
|
||
return self.tostring(separador=',\n')
|
||
|
||
def tostring(self, separador=',\n'):
|
||
ls = []
|
||
for i in list(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 for the API based addon from https://www.themoviedb.org/
|
||
# version 1.4:
|
||
# - Documented limitation of API use (see below).
|
||
# - Added get_season () method
|
||
# version 1.3:
|
||
# - Fixed error when returning None the path_poster and backdrop_path
|
||
# - Fixed a bug that caused the list of genres to accumulate from one call to another
|
||
# - Added get_genres () method
|
||
# - Added optional parameters alternative_language to the get_plot () method
|
||
#
|
||
#
|
||
# Usage:
|
||
# Construction methods:
|
||
# Tmdb (search_text, type)
|
||
# Parameters:
|
||
# searched_text: (str) Text or part of the text to search
|
||
# type: ("movie" or "tv") Type of result searched for movies or series. Default "movie"
|
||
# (optional) language_search: (str) language code according to ISO 639-1
|
||
# (optional) include_adult: (bool) Adult content is included in the search or not. Default
|
||
# 'False'
|
||
# (optional) year: (str) Release year.
|
||
# (optional) page: (int) When there are many results for a search these are organized by pages.
|
||
# We can load the page we want, although by default it is always the first page.
|
||
# Return:
|
||
# This call returns a Tmdb object containing the first page of the search result 'search_text'
|
||
# on the themoviedb.org website. The more optional parameters are included, the more precise the search will be.
|
||
# Also the object is initialized with the first result of the first page of results.
|
||
# Tmdb (id_Tmdb, type)
|
||
# Parameters:
|
||
# id_Tmdb: (str) Identifier code of a certain movie or series at themoviedb.org
|
||
# type: ("movie" or "tv") Type of result searched for movies or series. Default "movie"
|
||
# (optional) language_search: (str) language code according to ISO 639-1
|
||
# Return:
|
||
# This call returns a Tmdb object that contains the result of searching for a movie or series with the
|
||
# identifier id_Tmd
|
||
# on the themoviedb.org website.
|
||
# Tmdb (external_id, external_source, type)
|
||
# Parameters:
|
||
# external_id: (str) Identifier code of a certain movie or series on the web referenced by
|
||
# 'external_source'.
|
||
# external_source: (For series: "imdb_id", "freebase_mid", "freebase_id", "tvdb_id", "tvrage_id"; For
|
||
# movies: "imdb_id")
|
||
# type: ("movie" or "tv") Type of result searched for movies or series. Default "movie"
|
||
# (optional) language_search: (str) language code according to ISO 639-1
|
||
# Return:
|
||
# This call returns a Tmdb object that contains the result of searching for a movie or series with the
|
||
# identifier 'external_id' of
|
||
# the website referenced by 'external_source' on the themoviedb.org website.
|
||
#
|
||
# Main methods:
|
||
# get_id (): Returns a str with the Tmdb identifier of the loaded movie or series or an empty string if there were no
|
||
# nothing loaded.
|
||
# get_plot (alternate_language): Returns a str with the synopsis of the series or movie loaded.
|
||
# get_poster (response_type, size): Get the poster or a list of posters.
|
||
# get_backdrop (response_type, size): Get a background image or a list of background images.
|
||
# get_season (season): Get a dictionary with season-specific data.
|
||
# get_episode (season, episode): Get a dictionary with specific data of the episode.
|
||
# get_genres (): Returns a str with the list of genres to which the movie or series belongs.
|
||
#
|
||
#
|
||
# Other methods:
|
||
# load_result (result, page): When the search returns several results we can select which result
|
||
# concrete and from which page to load the data.
|
||
#
|
||
# Limitations:
|
||
# The use of the API imposes a limit of 20 simultaneous connections (concurrency) or 30 requests in 10 seconds per IP
|
||
# Information about the api: http://docs.themoviedb.apiary.io
|
||
# -------------------------------------------------------------------------------------------------------------------
|
||
|
||
|
||
class Tmdb(object):
|
||
# Class attribute
|
||
dic_genres = {}
|
||
'''
|
||
dic_genres={"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.cast = kwargs.get('cast', False)
|
||
self.results = []
|
||
self.result = ResultDictDefault()
|
||
self.total_pages = 0
|
||
self.total_results = 0
|
||
|
||
self.season = {}
|
||
self.searched_text = kwargs.get('searched_text', '')
|
||
|
||
self.search_id = kwargs.get('id_Tmdb', '')
|
||
self.search_text = re.sub('\[\\\?(B|I|COLOR)\s?[^\]]*\]', '', self.searched_text).strip()
|
||
self.search_type = kwargs.get('search_type', '')
|
||
self.search_language = kwargs.get('search_language', def_lang)
|
||
self.fallback_language = 'en'
|
||
# self.search_include_adult = kwargs.get('include_adult', False)
|
||
self.search_year = kwargs.get('year', '')
|
||
self.search_filter = kwargs.get('filtro', {})
|
||
self.discover = kwargs.get('discover', {})
|
||
|
||
# Refill gender dictionary if necessary
|
||
if (self.search_type == 'movie' or self.search_type == "tv") and (self.search_language not in Tmdb.dic_genres or self.search_type not in Tmdb.dic_genres[self.search_language]):
|
||
self.filling_dic_genres(self.search_type, self.search_language)
|
||
|
||
if not self.search_type:
|
||
self.search_type = 'movie'
|
||
|
||
if self.search_id:
|
||
# Search by tmdb identifier
|
||
self.__by_id()
|
||
|
||
elif self.search_text:
|
||
# Search by text
|
||
self.__search(page=self.page)
|
||
|
||
elif 'external_source' in kwargs and 'external_id' in kwargs:
|
||
# Search by external identifier according to type.
|
||
# TV Series: imdb_id, freebase_mid, freebase_id, tvdb_id, tvrage_id
|
||
# Movies: imdb_id
|
||
if (self.search_type == 'movie' and kwargs.get('external_source') == "imdb_id") or (self.search_type == 'tv' and kwargs.get('external_source') in ("imdb_id", "freebase_mid", "freebase_id", "tvdb_id", "tvrage_id")):
|
||
self.search_id = kwargs.get('external_id')
|
||
self.__by_id(source=kwargs.get('external_source'))
|
||
|
||
elif self.discover:
|
||
self.__discover()
|
||
|
||
else:
|
||
logger.debug("Created empty object")
|
||
|
||
@staticmethod
|
||
@cache_response
|
||
def get_json(url, cache=True):
|
||
# from core.support import dbg;dbg()
|
||
try:
|
||
result = httptools.downloadpage(url, cookies=False, ignore_response_code=True)
|
||
|
||
res_headers = result.headers
|
||
dict_data = result.json
|
||
#logger.debug("result_data es %s" % dict_data)
|
||
|
||
if "status_code" in dict_data:
|
||
#logger.debug("\nError de tmdb: %s %s" % (dict_data["status_code"], dict_data["status_message"]))
|
||
|
||
if dict_data["status_code"] == 25:
|
||
while "status_code" in dict_data and dict_data["status_code"] == 25:
|
||
wait = int(res_headers['retry-after'])
|
||
#logger.error("Limit reached, we wait to call back on ...%s" % wait)
|
||
time.sleep(wait)
|
||
# logger.debug("RE Call #%s" % d)
|
||
result = httptools.downloadpage(url, cookies=False)
|
||
|
||
res_headers = result.headers
|
||
# logger.debug("res_headers es %s" % res_headers)
|
||
dict_data = result.json
|
||
# logger.debug("result_data es %s" % dict_data)
|
||
|
||
# error getting data
|
||
except Exception as ex:
|
||
message = "An exception of type %s occured. Arguments:\n%s" % (type(ex).__name__, repr(ex.args))
|
||
logger.error("error in: %s" % message)
|
||
dict_data = {}
|
||
|
||
return dict_data
|
||
|
||
@classmethod
|
||
def filling_dic_genres(cls, search_type='movie', language=def_lang):
|
||
# Fill dictionary of genres of the type and language passed as parameters
|
||
if language not in cls.dic_genres:
|
||
cls.dic_genres[language] = {}
|
||
|
||
if search_type not in cls.dic_genres[language]:
|
||
cls.dic_genres[language][search_type] = {}
|
||
url = ('{}/genre/{}/list?api_key={}&language={}'.format(host, search_type, api, language))
|
||
try:
|
||
logger.debug("[Tmdb.py] Filling in dictionary of genres")
|
||
|
||
result = cls.get_json(url)
|
||
if not isinstance(result, dict):
|
||
result = ast.literal_eval(result.decode('utf-8'))
|
||
list_genres = result.get("genres", {})
|
||
|
||
for i in list_genres:
|
||
cls.dic_genres[language][search_type][str(i["id"])] = i["name"]
|
||
except:
|
||
logger.error("Error generating dictionaries")
|
||
import traceback
|
||
logger.error(traceback.format_exc())
|
||
|
||
def __by_id(self, source='tmdb'):
|
||
# from core.support import dbg;dbg()
|
||
|
||
if self.search_id:
|
||
if source == "tmdb":
|
||
url = ('{}/{}/{}?api_key={}&language={}&append_to_response=images,videos,external_ids,credits&include_image_language={},en,null'.format(host, self.search_type, self.search_id, api, self.search_language, self.search_language))
|
||
searching = "id_Tmdb: {}".format(self.search_id)
|
||
else:
|
||
url = ('{}/find/{}?external_source={}&api_key={}8&language={}'.format(host, self.search_id, source, api, self.search_language))
|
||
searching = "{}: {}".format(source.capitalize(), self.search_id)
|
||
|
||
logger.debug("[Tmdb.py] Searching %s:\n%s" % (searching, url))
|
||
result = self.get_json(url)
|
||
if not isinstance(result, dict):
|
||
result = ast.literal_eval(result.decode('utf-8'))
|
||
|
||
if result:
|
||
if source != "tmdb":
|
||
if self.search_type == "movie":
|
||
result = result["movie_results"][0]
|
||
else:
|
||
if result["tv_results"]:
|
||
result = result["tv_results"][0]
|
||
else:
|
||
result = result['tv_episode_results'][0]
|
||
|
||
result = self.get_mpaa(result)
|
||
|
||
self.results = [result]
|
||
self.total_results = 1
|
||
self.total_pages = 1
|
||
self.result = ResultDictDefault(result)
|
||
self.result['media_type'] = self.search_type.replace('tv', 'tvshow')
|
||
|
||
else:
|
||
# No search results
|
||
msg = "The search of %s gave no results" % searching
|
||
logger.debug(msg)
|
||
|
||
def __search(self, index_results=0, page=1):
|
||
self.result = ResultDictDefault()
|
||
results = []
|
||
text_simple = self.search_text.lower()
|
||
text_quote = urllib.quote(text_simple)
|
||
total_results = 0
|
||
total_pages = 0
|
||
searching = ""
|
||
|
||
if self.search_text:
|
||
url = ('{}/search/{}?api_key={}&query={}&language={}&include_adult={}&page={}'.format(host, self.search_type, api, text_quote, self.search_language, True, page))
|
||
|
||
if self.search_year:
|
||
if self.search_type == 'movie':
|
||
url += '&primary_release_year=%s' % self.search_year
|
||
else:
|
||
url += '&first_air_date_year=%s' % self.search_year
|
||
|
||
searching = self.search_text.capitalize()
|
||
logger.debug("[Tmdb.py] Searching %s on page %s:\n%s" % (searching, page, url))
|
||
result = self.get_json(url)
|
||
if not isinstance(result, dict):
|
||
result = ast.literal_eval(result.decode('utf-8'))
|
||
|
||
total_results = result.get("total_results", 0)
|
||
total_pages = result.get("total_pages", 0)
|
||
|
||
if total_results > 0:
|
||
results = [r for r in result["results"] if r.get('first_air_date', r.get('release_date', ''))]
|
||
|
||
if self.search_filter and total_results > 1:
|
||
for key, value in list(dict(self.search_filter).items()):
|
||
for r in results[:]:
|
||
if not r[key]:
|
||
r[key] = str(r[key])
|
||
if key not in r or value not in r[key]:
|
||
results.remove(r)
|
||
total_results -= 1
|
||
|
||
if results:
|
||
if index_results >= len(results):
|
||
# A higher number of results has been requested than those obtained
|
||
logger.error(
|
||
"The search for '%s' gave %s results for the page %s \n It is impossible to show the result number %s"
|
||
% (searching, len(results), page, index_results))
|
||
return 0
|
||
|
||
# We sort result based on fuzzy match to detect most similar
|
||
if len(results) > 1:
|
||
from lib.fuzzy_match import algorithims
|
||
if self.search_type == 'multi':
|
||
results.sort(key=lambda r: algorithims.trigram(text_simple, r.get('name', '') if r.get('media_type') == 'tv' else r.get('title', '')), reverse=True)
|
||
else:
|
||
results.sort(key=lambda r: algorithims.trigram(text_simple, r.get('name', '') if self.search_type == 'tv' else r.get('title', '')), reverse=True)
|
||
|
||
# We return the number of results of this page
|
||
self.results = results
|
||
self.total_results = total_results
|
||
self.total_pages = total_pages
|
||
self.result = ResultDictDefault(self.results[index_results])
|
||
# self.result['mediatype'] = self.result['media_type']
|
||
|
||
if not config.get_setting('tmdb_plus_info'):
|
||
self.result = self.get_mpaa(self.result)
|
||
return len(self.results)
|
||
|
||
else:
|
||
# No search results
|
||
msg = "The search for '%s' gave no results for page %s" % (searching, page)
|
||
logger.error(msg)
|
||
return 0
|
||
|
||
def __discover(self, index_results=0):
|
||
self.result = ResultDictDefault()
|
||
results = []
|
||
total_results = 0
|
||
total_pages = 0
|
||
|
||
# Exampleself.discover: {'url': 'discover/movie', 'with_cast': '1'}
|
||
# url: API method to run
|
||
# rest of keys: Search parameters concatenated to the url
|
||
type_search = self.discover.get('url', '')
|
||
if type_search:
|
||
params = []
|
||
for key, value in list(self.discover.items()):
|
||
if key != "url":
|
||
params.append(key + "=" + str(value))
|
||
|
||
url = ('{}/{}?api_key={}&{}'.format(host, type_search, api, "&".join(params)))
|
||
|
||
logger.debug("[Tmdb.py] Searching %s:\n%s" % (type_search, url))
|
||
result = self.get_json(url, cache=False)
|
||
if not isinstance(result, dict):
|
||
result = ast.literal_eval(result.decode('utf-8'))
|
||
|
||
total_results = result.get("total_results", -1)
|
||
total_pages = result.get("total_pages", 1)
|
||
|
||
if total_results > 0 or self.cast:
|
||
if self.cast:
|
||
results = result['cast']
|
||
total_results = len(results)
|
||
else:
|
||
results = result["results"]
|
||
if self.search_filter and results:
|
||
# TODO documentar esta parte
|
||
for key, value in list(dict(self.search_filter).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 = result
|
||
|
||
if index_results >= len(results):
|
||
logger.error(
|
||
"The search for '%s' did not give %s results" % (type_search, index_results))
|
||
return 0
|
||
|
||
# We return the number of results of this page
|
||
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 search results
|
||
logger.error("The search for '%s' gave no results" % type_search)
|
||
return 0
|
||
|
||
def load_result(self, index_results=0, page=1):
|
||
# If there are no results, there is only one or if the number of results on this page is less than the index sought to exit
|
||
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_results(self, num_result=20):
|
||
# logger.debug("self %s" % str(self))
|
||
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_result(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_genres(self, origen=None):
|
||
"""
|
||
:param origen: Source dictionary where the infoLabels are obtained, by default self.result
|
||
:type origen: Dict
|
||
:return: Returns the list of genres to which the movie or series belongs.
|
||
:rtype: str
|
||
"""
|
||
genre_list = []
|
||
|
||
if not origen:
|
||
origen = self.result
|
||
|
||
if "genre_ids" in origen:
|
||
# Search list of genres by IDs
|
||
for i in origen.get("genre_ids"):
|
||
try:
|
||
genre_list.append(Tmdb.dic_genres[self.search_language][self.search_type][str(i)])
|
||
except:
|
||
pass
|
||
|
||
elif "genre" in origen or "genres" in origen:
|
||
# Search genre list (object list {id, name})
|
||
v = origen["genre"]
|
||
v.extend(origen["genres"])
|
||
for i in v:
|
||
genre_list.append(i['name'])
|
||
|
||
return ', '.join(genre_list)
|
||
|
||
def search_by_id(self, id, source='tmdb', search_type='movie'):
|
||
self.search_id = id
|
||
self.search_type = search_type
|
||
self.__by_id(source=source)
|
||
|
||
def get_id(self):
|
||
"""
|
||
:return: Returns the Tmdb identifier of the loaded movie or series 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_plot(self, language_alternative=''):
|
||
"""
|
||
|
||
:param language_alternative: Language code, according to ISO 639-1, if there is no synopsis in the language set for the search.
|
||
By default, the original language is used. If None is used as the alternative_language, it will only search in the set language.
|
||
:type language_alternative: str
|
||
:return: Returns the synopsis of a movie or series
|
||
:rtype: str
|
||
"""
|
||
ret = ""
|
||
# from core.support import dbg;dbg()
|
||
|
||
if 'id' in self.result:
|
||
ret = self.result.get('overview')
|
||
if ret == "" and str(language_alternative).lower() != 'none':
|
||
# Let's launch a search for id and reread the synopsis again
|
||
self.search_id = str(self.result["id"])
|
||
if language_alternative:
|
||
self.search_language = language_alternative
|
||
else:
|
||
self.search_language = self.result['original_language']
|
||
|
||
url = ('{}/{}/{}?api_key={}&language={}'.format(host, self.search_type, self.search_id, api, self.search_language))
|
||
|
||
result = self.get_json(url)
|
||
if not isinstance(result, dict):
|
||
result = ast.literal_eval(result.decode('utf-8'))
|
||
|
||
if 'overview' in result:
|
||
self.result['overview'] = result['overview']
|
||
ret = self.result['overview']
|
||
|
||
return ret
|
||
|
||
def get_poster(self, response_type="str", size="original"):
|
||
"""
|
||
|
||
@param response_type: Data type returned by this method. Default "str"
|
||
@type response_type: list, str
|
||
@param size: ("w45", "w92", "w154", "w185", "w300", "w342", "w500", "w600", "h632", "w780", "w1280", "original")
|
||
Indicates the width (w) or height (h) of the image to download. Default "original"
|
||
@return: If the response_type is "list" it returns a list with all the urls of the poster images of the specified size.
|
||
If the response_type is "str" it returns the url of the poster image, most valued, of the specified size.
|
||
If the specified size does not exist, the images are returned to the original size.
|
||
@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 = 'https://image.tmdb.org/t/p/' + size + self.result["poster_path"]
|
||
|
||
if response_type == 'str':
|
||
return poster_path
|
||
elif not self.result["id"]:
|
||
return []
|
||
|
||
if len(self.result['images_posters']) == 0:
|
||
# We are going to launch a search by id and reread again
|
||
self.search_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":
|
||
# We cannot order sizes larger than the 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('https://image.tmdb.org/t/p/' + size + imagen_path)
|
||
else:
|
||
ret.append(poster_path)
|
||
|
||
return ret
|
||
|
||
def get_backdrop(self, response_type="str", size="original"):
|
||
"""
|
||
Returns the images of type backdrop
|
||
@param response_type: Data type returned by this method. Default "str"
|
||
@type response_type: list, str
|
||
@param size: ("w45", "w92", "w154", "w185", "w300", "w342", "w500", "w600", "h632", "w780", "w1280", "original")
|
||
Indicates the width (w) or height (h) of the image to download. Default "original"
|
||
@type size: str
|
||
@return: If the response_type is "list" it returns a list with all the urls of the backdrop images of the specified size.
|
||
If the response_type is "str" it returns the url of the backdrop type image, most valued, of the specified size.
|
||
If the specified size does not exist, the images are returned to the original size.
|
||
@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 = 'get_posterget_poster' + size + self.result["backdrop_path"]
|
||
|
||
if response_type == 'str':
|
||
return backdrop_path
|
||
elif self.result["id"] == "":
|
||
return []
|
||
|
||
if len(self.result['images_backdrops']) == 0:
|
||
# Let's launch a search by id and reread everything
|
||
self.search_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":
|
||
# We cannot order sizes larger than the 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('https://image.tmdb.org/t/p/' + size + imagen_path)
|
||
else:
|
||
ret.append(backdrop_path)
|
||
|
||
return ret
|
||
|
||
def get_season(self, seasonNumber=1, language=''):
|
||
# --------------------------------------------------------------------------------------------------------------------------------------------
|
||
# Parameters:
|
||
# season number: (int) Season number. Default 1.
|
||
# Return: (Dec)
|
||
# Returns a dictionary with data about the season.
|
||
# You can get more information about the returned data at:
|
||
# 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.search_type != "tv":
|
||
return {}
|
||
|
||
seasonNumber = int(seasonNumber)
|
||
if seasonNumber < 0:
|
||
seasonNumber = 1
|
||
search_language = language if language else self.search_language
|
||
|
||
if not self.season.get(seasonNumber, {}) or language:
|
||
# If there is no information about the requested season, check the website
|
||
|
||
# http://api.themoviedb.org/3/tv/1407/season/1?api_key=a1ab8b8669da03637a4b98fa39c39228&language=es&
|
||
# append_to_response=credits
|
||
url = "{}/tv/{}/season/{}?api_key={}&language={}&append_to_response=videos,images,credits,external_ids&include_image_language={},en,null".format(host, self.result["id"], seasonNumber, api, search_language, search_language)
|
||
# fallbackUrl = "{}/tv/{}/season/{}?api_key={}&language=en&append_to_response=videos,images,credits&include_image_language={},en,null".format(host, self.result["id"], seasonNumber, api, search_language)
|
||
logger.debug('TMDB URL', url)
|
||
|
||
searching = "id_Tmdb: " + str(self.result["id"]) + " season: " + str(seasonNumber) + "\nURL: " + url
|
||
logger.debug("[Tmdb.py] Searching " + searching)
|
||
# from core.support import dbg;dbg()
|
||
try:
|
||
self.season[seasonNumber] = self.get_json(url)
|
||
if not isinstance(self.season[seasonNumber], dict):
|
||
self.season[seasonNumber] = ast.literal_eval(self.season[seasonNumber].decode('utf-8'))
|
||
|
||
except:
|
||
logger.error("Unable to get the season")
|
||
self.season[seasonNumber] = {"status_code": 15, "status_message": "Failed"}
|
||
self.season[seasonNumber] = {"episodes": {}}
|
||
|
||
if "status_code" in self.season[seasonNumber]:
|
||
# An error has occurred
|
||
msg = config.get_localized_string(70496) + searching + config.get_localized_string(70497)
|
||
msg += "\nTmdb error: %s %s" % (
|
||
self.season[seasonNumber]["status_code"], self.season[seasonNumber]["status_message"])
|
||
logger.debug(msg)
|
||
self.season[seasonNumber] = {}
|
||
|
||
return self.season[seasonNumber]
|
||
|
||
def get_collection(self, _id=''):
|
||
ret = {}
|
||
if not _id:
|
||
collection = self.result.get('belongs_to_collection', {})
|
||
if collection:
|
||
_id = collection.get('id')
|
||
if _id:
|
||
url = '{}/collection/{}?api_key={}&language={}&append_to_response=images&include_image_language={},en,null'.format(host, _id, api, self.search_language, self.search_language)
|
||
tanslationurl = '{}/collection/{}/translations?api_key={}'.format(host, _id, api)
|
||
info = self.get_json(url)
|
||
for t in self.get_json(tanslationurl).get('translations'):
|
||
if t.get('iso_639_1') == self.fallback_language:
|
||
translation = t.get('data',{})
|
||
break
|
||
ret['set'] = info.get('name') if info.get('name') else translation.get('name')
|
||
ret['setid'] = _id
|
||
ret['setoverview'] = info.get('overview') if info.get('overview') else translation.get('overview')
|
||
posters = ['https://image.tmdb.org/t/p/original' + info.get('poster_path')] if info.get('poster_path') else []
|
||
fanarts = ['https://image.tmdb.org/t/p/original' + info.get('backdrop_path')] if info.get('backdrop_path') else []
|
||
for image in info['images']['posters']:
|
||
posters.append('https://image.tmdb.org/t/p/original' + image['file_path'])
|
||
for image in info['images']['backdrops']:
|
||
fanarts.append('https://image.tmdb.org/t/p/original' + image['file_path'])
|
||
ret['setposters'] = posters
|
||
ret['setfanarts'] = fanarts
|
||
return ret
|
||
|
||
def get_episode(self, seasonNumber=1, chapter=1):
|
||
# --------------------------------------------------------------------------------------------------------------------------------------------
|
||
# Parameters:
|
||
# season number (optional): (int) Season number. Default 1.
|
||
# chapter: (int) Chapter number. Default 1.
|
||
# Return: (Dec)
|
||
# Returns a dictionary with the following elements:
|
||
# "season_name", "season_synopsis", "season_poster", "season_num_ episodes" (int),
|
||
# "season_air_date", "episode_vote_count", "episode_vote_average",
|
||
# "episode_title", "episode_synopsis", "episode_image", "episode_air_date",
|
||
# "episode_crew" and "episode_guest_stars",
|
||
# With chapter == -1 the dictionary will only have the elements referring to the season
|
||
# --------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
||
if not self.result["id"] or self.search_type != "tv":
|
||
return {}
|
||
|
||
try:
|
||
chapter = int(chapter)
|
||
season = int(seasonNumber)
|
||
except ValueError:
|
||
logger.debug("The episode or season number is not valid")
|
||
return {}
|
||
|
||
# season = self.get_season(seasonNumber)
|
||
# # enseason = self.get_season(seasonNumber, language='en')
|
||
# # if not isinstance(season, dict):
|
||
# # season = ast.literal_eval(season.decode('utf-8'))
|
||
# # if not isinstance(enseason, dict):
|
||
# # enseason = ast.literal_eval(enseason.decode('utf-8'))
|
||
# if not season:
|
||
# # An error has occurred
|
||
# return {}
|
||
|
||
# if len(season["episodes"]) == 0:
|
||
# # An error has occurred
|
||
# logger.error("Episode %d of the season %d not found." % (chapter, seasonNumber))
|
||
# return {}
|
||
|
||
# elif len(season["episodes"]) < chapter and season["episodes"][-1]['episode_number'] >= chapter:
|
||
# n = None
|
||
# for i, chapters in enumerate(season["episodes"]):
|
||
# if chapters['episode_number'] == chapter:
|
||
# n = i + 1
|
||
# break
|
||
# if n != None:
|
||
# chapter = n
|
||
# else:
|
||
# logger.error("Episode %d of the season %d not found." % (chapter, seasonNumber))
|
||
# return {}
|
||
|
||
# elif len(season["episodes"]) < chapter:
|
||
# logger.error("Episode %d of the season %d not found." % (chapter, seasonNumber))
|
||
# return {}
|
||
|
||
# ret_dic = get_season_dic(season)
|
||
|
||
# if chapter == 0:
|
||
# # If we only look for season data, include the technical team that has intervened in any chapter
|
||
# dic_aux = dict((i['id'], i) for i in ret_dic["season_crew"])
|
||
# for e in season["episodes"]:
|
||
# for crew in e['crew']:
|
||
# if crew['id'] not in list(dic_aux.keys()):
|
||
# dic_aux[crew['id']] = crew
|
||
# ret_dic["season_crew"] = list(dic_aux.values())
|
||
|
||
|
||
# Obtain chapter data if applicable
|
||
# from core.support import dbg;dbg()
|
||
ret_dic = {}
|
||
if chapter > 0:
|
||
# episode = season["episodes"][chapter - 1]
|
||
url = "{}/tv/{}/season/{}/episode/{}?api_key={}&language={}&append_to_response=videos,images,credits,external_ids&include_image_language={},en,null".format(host, self.result["id"], seasonNumber, chapter, api, self.search_language, self.search_language)
|
||
episode = self.get_json(url)
|
||
# logger.debug('EPISODE', url)
|
||
|
||
episodeTitle = episode.get("name", '')
|
||
episodeId = episode.get('id', '')
|
||
episodePlot = episode.get('overview', '')
|
||
episodeDate = episode.get('air_date', '')
|
||
episodeImage = episode.get('still_path', '')
|
||
episodeCrew = episode.get('crew', [])
|
||
episodeStars = episode.get('guest_stars', [])
|
||
episodeVoteCount = episode.get('vote_count', 0)
|
||
episodeVoteAverage = episode.get('vote_average', 0)
|
||
externalIds = episode.get('external_ids', {})
|
||
imdb_id = externalIds.get('imdb_id')
|
||
tvdb_id = externalIds.get('tvdb_id')
|
||
posters = []
|
||
for image in episode.get('images',{}).get('stills',{}):
|
||
posters.append('https://image.tmdb.org/t/p/original' + image['file_path'])
|
||
|
||
ret_dic["episode_title"] = episodeTitle
|
||
ret_dic["episode_plot"] = episodePlot
|
||
|
||
if episodeImage:
|
||
ret_dic["episode_image"] = 'https://image.tmdb.org/t/p/original' + episodeImage
|
||
else:
|
||
ret_dic["episode_image"] = ""
|
||
if episodeDate:
|
||
date = episodeDate.split("-")
|
||
ret_dic["episode_air_date"] = date[2] + "/" + date[1] + "/" + date[0]
|
||
else:
|
||
ret_dic["episode_air_date"] = ""
|
||
if posters:
|
||
ret_dic['episode_posters'] = posters
|
||
|
||
ret_dic["episode_crew"] = episodeCrew
|
||
if episodeStars:
|
||
ret_dic["episode_actors"] = [[k['name'], k['character'], 'https://image.tmdb.org/t/p/original/' + k['profile_path'] if k['profile_path'] else '', k['order']] for k in episodeStars]
|
||
ret_dic["episode_vote_count"] = episodeVoteCount
|
||
ret_dic["episode_vote_average"] = episodeVoteAverage
|
||
ret_dic["episode_id"] = episodeId
|
||
ret_dic["episode_imdb_id"] = imdb_id
|
||
ret_dic["episode_tvdb_id"] = tvdb_id
|
||
|
||
|
||
return ret_dic
|
||
|
||
def get_list_episodes(self):
|
||
url = '{}/tv/{}?api_key={}&language={}'.format(host, self.search_id, api, self.search_language)
|
||
results = requests.get(url).json().get('seasons', [])
|
||
seasons = []
|
||
if results and 'Error' not in results:
|
||
for season in results:
|
||
url = '{}/tv/{}/season/{}?api_key={}&language={}'.format(host, self.search_id, season['season_number'], api, self.search_language)
|
||
try: start_from = requests.get(url).json()['episodes'][0]['episode_number']
|
||
except: start_from = 1
|
||
seasons.append({'season_number':season['season_number'], 'episode_count':season['episode_count'], 'start_from':start_from})
|
||
return seasons
|
||
|
||
def get_videos(self):
|
||
"""
|
||
:return: Returns an ordered list (language / resolution / type) of Dict objects in which each of its elements corresponds to a trailer, teaser or clip from youtube.
|
||
:rtype: list of Dict
|
||
"""
|
||
ret = []
|
||
|
||
if self.result['id']:
|
||
if self.result['videos']:
|
||
self.result["videos"] = self.result["videos"]['results']
|
||
else:
|
||
self.result["videos"] = []
|
||
# First video search in the search language
|
||
url = "{}/{}/{}/videos?api_key={}&language={}".format(host, self.search_type, self.result['id'], api, self.search_language)
|
||
|
||
dict_videos = self.get_json(url)
|
||
if not isinstance(dict_videos, dict):
|
||
dict_videos = ast.literal_eval(dict_videos.decode('utf-8'))
|
||
|
||
if dict_videos['results']:
|
||
dict_videos['results'] = sorted(dict_videos['results'], key=lambda x: (x['type'], x['size']))
|
||
self.result["videos"] = dict_videos['results']
|
||
|
||
# If the search language is not English, do a second video search in English
|
||
if self.search_language != 'en':
|
||
url = "{}/{}/{}/videos?api_key={}".format(host, self.search_type, self.result['id'], api)
|
||
|
||
dict_videos = self.get_json(url)
|
||
if not isinstance(dict_videos, dict):
|
||
dict_videos = ast.literal_eval(dict_videos.decode('utf-8'))
|
||
|
||
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 the searches have obtained results, return a list of objects
|
||
for i in self.result['videos']:
|
||
if i['site'] == "YouTube":
|
||
ret.append({'name': i['name'],
|
||
'url': "plugin://plugin.video.youtube/play/?video_id={}".format(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: Extra information about the movie, series, season or chapter.
|
||
:type infoLabels: Dict
|
||
:param origen: Source dictionary where the infoLabels are obtained, by default self.result
|
||
:type origen: Dict
|
||
:return: Returns the extra information obtained from the current object. If the infoLables parameter was passed, the returned value will be read as a duly updated parameter.
|
||
:rtype: Dict
|
||
"""
|
||
|
||
if infoLabels:
|
||
ret_infoLabels = InfoLabels(infoLabels)
|
||
else:
|
||
ret_infoLabels = InfoLabels()
|
||
# Start Listings
|
||
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_director_image = ret_infoLabels.get('director_image', [])
|
||
l_director_id = ret_infoLabels.get('director_id', [])
|
||
l_writer = [i.strip() for i in ret_infoLabels['writer'].split(',') if ret_infoLabels['writer']]
|
||
l_writer_image = ret_infoLabels.get('writer_image', [])
|
||
l_writer_id = ret_infoLabels.get('writer_id', [])
|
||
l_castandrole = ret_infoLabels.get('castandrole', [])
|
||
|
||
if not origen:
|
||
origen = self.result
|
||
|
||
if 'credits' in list(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']
|
||
|
||
if 'images' in list(origen.keys()):
|
||
dic_origen_credits = origen['images']
|
||
origen['posters'] = dic_origen_credits.get('posters', [])
|
||
origen['fanarts'] = dic_origen_credits.get('backdrops', [])
|
||
del origen['images']
|
||
|
||
items = list(origen.items())
|
||
|
||
# Season / episode information
|
||
if ret_infoLabels['season'] and self.season.get(ret_infoLabels['season']):
|
||
# If there is data loaded for the indicated season
|
||
|
||
episodio = -1
|
||
if ret_infoLabels['episode']:
|
||
episodio = ret_infoLabels['episode']
|
||
|
||
items.extend(list(self.get_episode(ret_infoLabels['season'], episodio).items()))
|
||
|
||
|
||
for k, v in items:
|
||
if not v:
|
||
continue
|
||
elif isinstance(v, str):
|
||
v = re.sub(r"\n|\r|\t", "", v)
|
||
# fix
|
||
if v == "None":
|
||
continue
|
||
|
||
if k == 'media_type':
|
||
# from core.support import dbg;dbg()
|
||
ret_infoLabels['mediatype'] = v if v in ['tv', 'tvshow'] else 'movie'
|
||
|
||
elif k == 'overview':
|
||
if origen:
|
||
ret_infoLabels['plot'] = v
|
||
else:
|
||
ret_infoLabels['plot'] = self.get_plot()
|
||
|
||
elif k == 'runtime': # Duration for movies
|
||
ret_infoLabels['duration'] = int(v) * 60
|
||
|
||
elif k == 'episode_run_time': # Duration for episodes
|
||
try:
|
||
for v_alt in v: # It comes as a list (?!)
|
||
ret_infoLabels['duration'] = int(v_alt) * 60
|
||
except:
|
||
pass
|
||
|
||
elif k == 'release_date':
|
||
ret_infoLabels['year'] = int(v[:4])
|
||
ret_infoLabels['premiered'] = v
|
||
|
||
elif k == 'first_air_date':
|
||
ret_infoLabels['year'] = int(v[:4])
|
||
ret_infoLabels['aired'] = v
|
||
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 in ['poster_path', 'profile_path']:
|
||
ret_infoLabels['thumbnail'] = 'https://image.tmdb.org/t/p/original' + v
|
||
|
||
elif k == 'backdrop_path':
|
||
ret_infoLabels['fanart'] = 'https://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_genres(origen)
|
||
|
||
elif k == 'name' or k == 'title':
|
||
ret_infoLabels['title'] = v
|
||
|
||
elif k == 'tagline':
|
||
ret_infoLabels['tagline'] = v
|
||
|
||
elif k == 'production_companies':
|
||
ret_infoLabels['studio'] = ", ".join(i['name'] for i in v)
|
||
|
||
elif k == 'credits_cast' or k == 'season_cast' or k == 'episode_guest_stars':
|
||
dic_aux = dict((name, [character, thumb, order, id]) for (name, character, thumb, order, id) in l_castandrole)
|
||
l_castandrole.extend([(p['name'], p.get('character', '') or p.get('character_name', ''), 'https://image.tmdb.org/t/p/original' + p.get('profile_path', '') if p.get('profile_path', '') else '', p.get('order'), p.get('id')) \
|
||
for p in v if 'name' in p and p['name'] not in list(dic_aux.keys())])
|
||
|
||
elif k == 'videos':
|
||
if not isinstance(v, list):
|
||
v = v.get('results', [])
|
||
for i in v:
|
||
if i.get("site", "") == "YouTube":
|
||
ret_infoLabels['trailer'] = "plugin://plugin.video.youtube/play/?video_id=" + v[0]["key"]
|
||
break
|
||
|
||
elif k == 'posters':
|
||
ret_infoLabels['posters'] = ['https://image.tmdb.org/t/p/original' + p["file_path"] for p in v]
|
||
|
||
elif k == 'fanarts':
|
||
ret_infoLabels['fanarts'] = ['https://image.tmdb.org/t/p/original' + p["file_path"] for p in v]
|
||
|
||
elif k == 'belongs_to_collection':
|
||
c = Tmdb.get_collection(self, v.get('id',''))
|
||
for k, v in c.items():
|
||
ret_infoLabels[k] = v
|
||
|
||
elif k == 'production_countries' or k == 'origin_country':
|
||
# support.dbg()
|
||
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 'name' in i:
|
||
# pais = Tmdb.dic_country.get(i['iso_3166_1'], i['iso_3166_1'])
|
||
l_country = list(set(l_country + [i['name']]))
|
||
|
||
elif k == 'credits_crew' or k == 'episode_crew' or k == 'season_crew':
|
||
for crew in v:
|
||
if crew['job'].lower() == 'director':
|
||
# from core.support import dbg;dbg()
|
||
l_director = list(set(l_director + [crew['name']]))
|
||
l_director_image += ['https://image.tmdb.org/t/p/original' + crew['profile_path'] if crew['profile_path'] else '']
|
||
l_director_id += [crew['id']]
|
||
|
||
elif crew['job'].lower() in ('screenplay', 'writer'):
|
||
l_writer = list(set(l_writer + [crew['name']]))
|
||
l_writer_image += ['https://image.tmdb.org/t/p/original' + crew['profile_path'] if crew['profile_path'] else '']
|
||
l_writer_id += [crew['id']]
|
||
|
||
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
|
||
|
||
# Sort the lists and convert them to str if necessary
|
||
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(l_director)
|
||
ret_infoLabels['director_image'] = l_director_image
|
||
ret_infoLabels['director_id'] = l_director_id
|
||
if l_writer:
|
||
ret_infoLabels['writer'] = ', '.join(l_writer)
|
||
ret_infoLabels['writer_image'] = l_writer_image
|
||
ret_infoLabels['writer_id'] = l_writer_id
|
||
|
||
return ret_infoLabels
|
||
|
||
def get_mpaa(self, result):
|
||
if result.get('id'):
|
||
Mpaaurl = '{}/{}/{}/{}?api_key={}'.format(host, self.search_type, result['id'], 'release_dates' if self.search_type == 'movie' else 'content_ratings', api)
|
||
Mpaas = self.get_json(Mpaaurl).get('results',[])
|
||
for m in Mpaas:
|
||
if m.get('iso_3166_1','').lower() == 'us':
|
||
result['mpaa'] = m.get('rating', m.get('release_dates', [{}])[0].get('certification'))
|
||
break
|
||
return result
|
||
|
||
|
||
def get_season_dic(season):
|
||
ret_dic = dict()
|
||
# logger.debug(jsontools.dump(season))
|
||
# Get data for this season
|
||
|
||
seasonTitle = season.get("name", '')
|
||
seasonPlot = season.get("overview" , '')
|
||
seasonId = season.get("id", '')
|
||
seasonEpisodes = len(season.get("episodes",[]))
|
||
seasonDate = season.get("air_date", '')
|
||
seasonPoster = season.get('poster_path', '')
|
||
seasonCredits = season.get('credits', {})
|
||
seasonPosters = season.get('images',{}).get('posters',{})
|
||
seasonFanarts = season.get('images',{}).get('backdrops',{})
|
||
seasonTrailers = season.get('videos',{}).get('results',[])
|
||
|
||
ret_dic["season_title"] = seasonTitle
|
||
ret_dic["season_plot"] = seasonPlot
|
||
ret_dic["season_id"] = seasonId
|
||
ret_dic["season_episodes_number"] = seasonEpisodes
|
||
|
||
if seasonDate:
|
||
date = seasonDate.split("-")
|
||
ret_dic["season_air_date"] = date[2] + "/" + date[1] + "/" + date[0]
|
||
else:
|
||
ret_dic["season_air_date"] = ''
|
||
if seasonPoster:
|
||
ret_dic["season_poster"] = 'https://image.tmdb.org/t/p/original' + seasonPoster
|
||
else:
|
||
ret_dic["season_poster"] = ''
|
||
|
||
if seasonPosters:
|
||
ret_dic['season_posters'] = ['https://image.tmdb.org/t/p/original' + p["file_path"] for p in seasonPosters]
|
||
if seasonFanarts:
|
||
ret_dic['season_fanarts'] = ['https://image.tmdb.org/t/p/original' + p["file_path"] for p in seasonFanarts]
|
||
if seasonTrailers:
|
||
ret_dic['season_trailer'] = []
|
||
for i in seasonTrailers:
|
||
if i.get("site", "") == "YouTube":
|
||
ret_dic['season_trailer'] = "plugin://plugin.video.youtube/play/?video_id=" + seasonTrailers[0]["key"]
|
||
break
|
||
|
||
dic_aux = seasonCredits if seasonCredits else {}
|
||
ret_dic["season_cast"] = dic_aux.get('cast', [])
|
||
ret_dic["season_crew"] = dic_aux.get('crew', [])
|
||
return ret_dic
|
||
|
||
def parse_fallback_info(info, fallbackInfo):
|
||
info_dict = {}
|
||
for key, value in info.items():
|
||
if not value:
|
||
value = fallbackInfo[key]
|
||
info_dict[key] = value
|
||
episodes = info_dict['episodes']
|
||
|
||
episodes_list = []
|
||
for i, episode in enumerate(episodes):
|
||
episode_dict = {}
|
||
for key, value in episode.items():
|
||
if not value:
|
||
value = fallbackInfo['episodes'][i][key]
|
||
episode_dict[key] = value
|
||
episodes_list.append(episode_dict)
|
||
|
||
info_dict['episodes'] = episodes_list
|
||
return info_dict |