diff --git a/README.md b/README.md index 9f0ca6a..d796660 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Copy the share/solarfm folder to your user .config/ directory too.
Install Setup
``` -sudo apt-get install python3.8 python3-setproctitle python3-gi wget ffmpegthumbnailer steamcmd +sudo apt-get install xclip python3.8 python3-setproctitle python3-gi wget ffmpegthumbnailer steamcmd ``` # Known Issues @@ -32,4 +32,4 @@ A selected file in the active quad-pane will move to trash since it is the defau ![1 SolarFM single pane. ](images/pic1.png) ![2 SolarFM double pane. ](images/pic2.png) ![3 SolarFM triple pane. ](images/pic3.png) -![4 SolarFM quad pane. ](images/pic4.png) +![4 SolarFM quad pane. ](images/pic4.png) \ No newline at end of file diff --git a/plugins/movie_tv_info/tmdbscraper/lib/__init__.py b/plugins/movie_tv_info/tmdbscraper/lib/__init__.py new file mode 100644 index 0000000..d36fa8c --- /dev/null +++ b/plugins/movie_tv_info/tmdbscraper/lib/__init__.py @@ -0,0 +1,3 @@ +""" + Pligin Module +""" diff --git a/plugins/movie_tv_info/tmdbscraper/lib/tmdbscraper/__init__.py b/plugins/movie_tv_info/tmdbscraper/lib/tmdbscraper/__init__.py new file mode 100644 index 0000000..f10f263 --- /dev/null +++ b/plugins/movie_tv_info/tmdbscraper/lib/tmdbscraper/__init__.py @@ -0,0 +1,17 @@ + +def get_imdb_id(uniqueids): + imdb_id = uniqueids.get('imdb') + if not imdb_id or not imdb_id.startswith('tt'): + return None + return imdb_id + +# example format for scraper results +_ScraperResults = { + 'info', + 'ratings', + 'uniqueids', + 'cast', + 'available_art', + 'error', + 'warning' # not handled +} diff --git a/plugins/movie_tv_info/tmdbscraper/lib/tmdbscraper/api_utils.py b/plugins/movie_tv_info/tmdbscraper/lib/tmdbscraper/api_utils.py new file mode 100644 index 0000000..31fe215 --- /dev/null +++ b/plugins/movie_tv_info/tmdbscraper/lib/tmdbscraper/api_utils.py @@ -0,0 +1,75 @@ +# coding: utf-8 +# +# Copyright (C) 2020, Team Kodi +# +# This program 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. +# +# This program 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 this program. If not, see . + +"""Functions to interact with various web site APIs.""" + +from __future__ import absolute_import, unicode_literals + +import json +# from pprint import pformat +try: #PY2 / PY3 + from urllib2 import Request, urlopen + from urllib2 import URLError + from urllib import urlencode +except ImportError: + from urllib.request import Request, urlopen + from urllib.error import URLError + from urllib.parse import urlencode +try: + from typing import Text, Optional, Union, List, Dict, Any # pylint: disable=unused-import + InfoType = Dict[Text, Any] # pylint: disable=invalid-name +except ImportError: + pass + +HEADERS = {} + + +def set_headers(headers): + HEADERS.update(headers) + + +def load_info(url, params=None, default=None, resp_type = 'json'): + # type: (Text, Optional[Dict[Text, Union[Text, List[Text]]]]) -> Union[dict, list] + """ + Load info from external api + + :param url: API endpoint URL + :param params: URL query params + :default: object to return if there is an error + :resp_type: what to return to the calling function + :return: API response or default on error + """ + theerror = '' + if params: + url = url + '?' + urlencode(params) + req = Request(url, headers=HEADERS) + try: + response = urlopen(req) + except URLError as e: + if hasattr(e, 'reason'): + theerror = {'error': 'failed to reach the remote site\nReason: {}'.format(e.reason)} + elif hasattr(e, 'code'): + theerror = {'error': 'remote site unable to fulfill the request\nError code: {}'.format(e.code)} + if default is not None: + return default + else: + return theerror + if resp_type.lower() == 'json': + resp = json.loads(response.read().decode('utf-8')) + else: + resp = response.read().decode('utf-8') + return resp diff --git a/plugins/movie_tv_info/tmdbscraper/lib/tmdbscraper/fanarttv.py b/plugins/movie_tv_info/tmdbscraper/lib/tmdbscraper/fanarttv.py new file mode 100644 index 0000000..f19cb1c --- /dev/null +++ b/plugins/movie_tv_info/tmdbscraper/lib/tmdbscraper/fanarttv.py @@ -0,0 +1,87 @@ +from . import api_utils +try: + from urllib import quote +except ImportError: # py2 / py3 + from urllib.parse import quote + +API_KEY = '384afe262ee0962545a752ff340e3ce4' +API_URL = 'https://webservice.fanart.tv/v3/movies/{}' + +ARTMAP = { + 'movielogo': 'clearlogo', + 'hdmovielogo': 'clearlogo', + 'hdmovieclearart': 'clearart', + 'movieart': 'clearart', + 'moviedisc': 'discart', + 'moviebanner': 'banner', + 'moviethumb': 'landscape', + 'moviebackground': 'fanart', + 'movieposter': 'poster' +} + +def get_details(uniqueids, clientkey, language, set_tmdbid): + media_id = _get_mediaid(uniqueids) + if not media_id: + return {} + + movie_data = _get_data(media_id, clientkey) + movieset_data = _get_data(set_tmdbid, clientkey) + if not movie_data and not movieset_data: + return {} + + movie_art = {} + movieset_art = {} + if movie_data: + movie_art = _parse_data(movie_data, language) + if movieset_data: + movieset_art = _parse_data(movieset_data, language) + movieset_art = {'set.' + key: value for key, value in movieset_art.items()} + + available_art = movie_art + available_art.update(movieset_art) + + return {'available_art': available_art} + +def _get_mediaid(uniqueids): + for source in ('tmdb', 'imdb', 'unknown'): + if source in uniqueids: + return uniqueids[source] + +def _get_data(media_id, clientkey): + headers = {'api-key': API_KEY} + if clientkey: + headers['client-key'] = clientkey + api_utils.set_headers(headers) + fanarttv_url = API_URL.format(media_id) + return api_utils.load_info(fanarttv_url, default={}) + +def _parse_data(data, language): + result = {} + for arttype, artlist in data.items(): + if arttype not in ARTMAP: + continue + for image in artlist: + image_lang = _get_imagelanguage(arttype, image) + if image_lang and image_lang != language: + continue + + generaltype = ARTMAP[arttype] + if generaltype == 'poster' and not image_lang: + generaltype = 'keyart' + if artlist and generaltype not in result: + result[generaltype] = [] + + url = quote(image['url'], safe="%/:=&?~#+!$,;'@()*[]") + resultimage = {'url': url, 'preview': url.replace('.fanart.tv/fanart/', '.fanart.tv/preview/')} + result[generaltype].append(resultimage) + + return result + +def _get_imagelanguage(arttype, image): + if 'lang' not in image or arttype == 'moviebackground': + return None + if arttype in ('movielogo', 'hdmovielogo', 'hdmovieclearart', 'movieart', 'moviebanner', + 'moviethumb', 'moviedisc'): + return image['lang'] if image['lang'] not in ('', '00') else 'en' + # movieposter may or may not have a title and thus need a language + return image['lang'] if image['lang'] not in ('', '00') else None diff --git a/plugins/movie_tv_info/tmdbscraper/lib/tmdbscraper/imdbratings.py b/plugins/movie_tv_info/tmdbscraper/lib/tmdbscraper/imdbratings.py new file mode 100644 index 0000000..eba96b1 --- /dev/null +++ b/plugins/movie_tv_info/tmdbscraper/lib/tmdbscraper/imdbratings.py @@ -0,0 +1,72 @@ +# -*- coding: UTF-8 -*- +# +# Copyright (C) 2020, Team Kodi +# +# This program 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. +# +# This program 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 this program. If not, see . +# +# IMDb ratings based on code in metadata.themoviedb.org.python by Team Kodi +# pylint: disable=missing-docstring + +import re +from . import api_utils +from . import get_imdb_id + +IMDB_RATINGS_URL = 'https://www.imdb.com/title/{}/' +IMDB_RATING_REGEX = re.compile(r'itemprop="ratingValue".*?>.*?([\d.]+).*?<') +IMDB_VOTES_REGEX = re.compile(r'itemprop="ratingCount".*?>.*?([\d,]+).*?<') +IMDB_TOP250_REGEX = re.compile(r'Top Rated Movies #(\d+)') + +def get_details(uniqueids): + imdb_id = get_imdb_id(uniqueids) + if not imdb_id: + return {} + votes, rating, top250 = _get_ratinginfo(imdb_id) + return _assemble_imdb_result(votes, rating, top250) + +def _get_ratinginfo(imdb_id): + response = api_utils.load_info(IMDB_RATINGS_URL.format(imdb_id), default = '', resp_type='text') + return _parse_imdb_result(response) + +def _assemble_imdb_result(votes, rating, top250): + result = {} + if top250: + result['info'] = {'top250': top250} + if votes and rating: + result['ratings'] = {'imdb': {'votes': votes, 'rating': rating}} + return result + +def _parse_imdb_result(input_html): + rating = _parse_imdb_rating(input_html) + votes = _parse_imdb_votes(input_html) + top250 = _parse_imdb_top250(input_html) + + return votes, rating, top250 + +def _parse_imdb_rating(input_html): + match = re.search(IMDB_RATING_REGEX, input_html) + if (match): + return float(match.group(1)) + return None + +def _parse_imdb_votes(input_html): + match = re.search(IMDB_VOTES_REGEX, input_html) + if (match): + return int(match.group(1).replace(',', '')) + return None + +def _parse_imdb_top250(input_html): + match = re.search(IMDB_TOP250_REGEX, input_html) + if (match): + return int(match.group(1)) + return None diff --git a/plugins/movie_tv_info/tmdbscraper/lib/tmdbscraper/tmdb.py b/plugins/movie_tv_info/tmdbscraper/lib/tmdbscraper/tmdb.py new file mode 100644 index 0000000..a7f4082 --- /dev/null +++ b/plugins/movie_tv_info/tmdbscraper/lib/tmdbscraper/tmdb.py @@ -0,0 +1,249 @@ +from datetime import datetime, timedelta +from . import tmdbapi + + +class TMDBMovieScraper(object): + def __init__(self, url_settings, language, certification_country): + self.url_settings = url_settings + self.language = language + self.certification_country = certification_country + self._urls = None + self.tmdbapi = tmdbapi + + @property + def urls(self): + if not self._urls: + self._urls = _load_base_urls(self.url_settings) + return self._urls + + def search(self, title, year=None): + search_media_id = _parse_media_id(title) + if search_media_id: + if search_media_id['type'] == 'tmdb': + result = _get_movie(search_media_id['id'], self.language, True) + result = [result] + else: + response = tmdbapi.find_movie_by_external_id(search_media_id['id'], language=self.language) + theerror = response.get('error') + if theerror: + return 'error: {}'.format(theerror) + result = response.get('movie_results') + if 'error' in result: + return result + else: + response = tmdbapi.search_movie(query=title, year=year, language=self.language) + theerror = response.get('error') + if theerror: + return 'error: {}'.format(theerror) + result = response['results'] + urls = self.urls + + def is_best(item): + return item['title'].lower() == title and ( + not year or item.get('release_date', '').startswith(year)) + if result and not is_best(result[0]): + best_first = next((item for item in result if is_best(item)), None) + if best_first: + result = [best_first] + [item for item in result if item is not best_first] + + for item in result: + if item.get('poster_path'): + item['poster_path'] = urls['preview'] + item['poster_path'] + if item.get('backdrop_path'): + item['backdrop_path'] = urls['preview'] + item['backdrop_path'] + return result + + def get_details(self, uniqueids): + media_id = uniqueids.get('tmdb') or uniqueids.get('imdb') + details = self._gather_details(media_id) + if not details: + return None + if details.get('error'): + return details + return self._assemble_details(**details) + + def _gather_details(self, media_id): + movie = _get_movie(media_id, self.language) + if not movie or movie.get('error'): + return movie + + # Don't specify language to get English text for fallback + movie_fallback = _get_movie(media_id) + + collection = _get_moviecollection(movie['belongs_to_collection'].get('id'), self.language) if \ + movie['belongs_to_collection'] else None + collection_fallback = _get_moviecollection(movie['belongs_to_collection'].get('id')) if \ + movie['belongs_to_collection'] else None + + return {'movie': movie, 'movie_fallback': movie_fallback, 'collection': collection, + 'collection_fallback': collection_fallback} + + def _assemble_details(self, movie, movie_fallback, collection, collection_fallback): + info = { + 'title': movie['title'], + 'originaltitle': movie['original_title'], + 'plot': movie.get('overview') or movie_fallback.get('overview'), + 'tagline': movie.get('tagline') or movie_fallback.get('tagline'), + 'studio': _get_names(movie['production_companies']), + 'genre': _get_names(movie['genres']), + 'country': _get_names(movie['production_countries']), + 'credits': _get_cast_members(movie['casts'], 'crew', 'Writing', ['Screenplay', 'Writer', 'Author']), + 'director': _get_cast_members(movie['casts'], 'crew', 'Directing', ['Director']), + 'premiered': movie['release_date'], + 'tag': _get_names(movie['keywords']['keywords']) + } + + if 'countries' in movie['releases']: + certcountry = self.certification_country.upper() + for country in movie['releases']['countries']: + if country['iso_3166_1'] == certcountry and country['certification']: + info['mpaa'] = country['certification'] + break + + trailer = _parse_trailer(movie.get('trailers', {}), movie_fallback.get('trailers', {})) + if trailer: + info['trailer'] = trailer + if collection: + info['set'] = collection.get('name') or collection_fallback.get('name') + info['setoverview'] = collection.get('overview') or collection_fallback.get('overview') + if movie.get('runtime'): + info['duration'] = movie['runtime'] * 60 + + ratings = {'themoviedb': {'rating': float(movie['vote_average']), 'votes': int(movie['vote_count'])}} + uniqueids = {'tmdb': movie['id'], 'imdb': movie['imdb_id']} + cast = [{ + 'name': actor['name'], + 'role': actor['character'], + 'thumbnail': self.urls['original'] + actor['profile_path'] + if actor['profile_path'] else "", + 'order': actor['order'] + } + for actor in movie['casts'].get('cast', []) + ] + available_art = _parse_artwork(movie, collection, self.urls, self.language) + + _info = {'set_tmdbid': movie['belongs_to_collection'].get('id') + if movie['belongs_to_collection'] else None} + + return {'info': info, 'ratings': ratings, 'uniqueids': uniqueids, 'cast': cast, + 'available_art': available_art, '_info': _info} + +def _parse_media_id(title): + if title.startswith('tt') and title[2:].isdigit(): + return {'type': 'imdb', 'id':title} # IMDB ID works alone because it is clear + title = title.lower() + if title.startswith('tmdb/') and title[5:].isdigit(): # TMDB ID + return {'type': 'tmdb', 'id':title[5:]} + elif title.startswith('imdb/tt') and title[7:].isdigit(): # IMDB ID with prefix to match + return {'type': 'imdb', 'id':title[5:]} + return None + +def _get_movie(mid, language=None, search=False): + details = None if search else \ + 'trailers,images,releases,casts,keywords' if language is not None else \ + 'trailers' + response = tmdbapi.get_movie(mid, language=language, append_to_response=details) + theerror = response.get('error') + if theerror: + return 'error: {}'.format(theerror) + else: + return response + +def _get_moviecollection(collection_id, language=None): + if not collection_id: + return None + details = 'images' + response = tmdbapi.get_collection(collection_id, language=language, append_to_response=details) + theerror = response.get('error') + if theerror: + return 'error: {}'.format(theerror) + else: + return response + +def _parse_artwork(movie, collection, urlbases, language): + if language: + # Image languages don't have regional variants + language = language.split('-')[0] + posters = [] + landscape = [] + fanart = [] + if 'images' in movie: + posters = _get_images_with_fallback(movie['images']['posters'], urlbases, language) + landscape = _get_images(movie['images']['backdrops'], urlbases, language) + fanart = _get_images(movie['images']['backdrops'], urlbases, None) + + setposters = [] + setlandscape = [] + setfanart = [] + if collection and 'images' in collection: + setposters = _get_images_with_fallback(collection['images']['posters'], urlbases, language) + setlandscape = _get_images(collection['images']['backdrops'], urlbases, language) + setfanart = _get_images(collection['images']['backdrops'], urlbases, None) + + return {'poster': posters, 'landscape': landscape, 'fanart': fanart, + 'set.poster': setposters, 'set.landscape': setlandscape, 'set.fanart': setfanart} + +def _get_images_with_fallback(imagelist, urlbases, language, language_fallback='en'): + images = _get_images(imagelist, urlbases, language) + + # Add backup images + if language != language_fallback: + images.extend(_get_images(imagelist, urlbases, language_fallback)) + + # Add any images if nothing set so far + if not images: + images = _get_images(imagelist, urlbases) + + return images + +def _get_images(imagelist, urlbases, language='_any'): + result = [] + for img in imagelist: + if language != '_any' and img['iso_639_1'] != language: + continue + result.append({ + 'url': urlbases['original'] + img['file_path'], + 'preview': urlbases['preview'] + img['file_path'], + }) + return result + +def _get_date_numeric(datetime_): + return (datetime_ - datetime(1970, 1, 1)).total_seconds() + +def _load_base_urls(url_settings): + urls = {} + # urls['original'] = url_settings.getSettingString('originalUrl') + # urls['preview'] = url_settings.getSettingString('previewUrl') + # last_updated = url_settings.getSettingString('lastUpdated') + urls['original'] = "" + urls['preview'] = "" + last_updated = "0" + + if not urls['original'] or not urls['preview'] or not last_updated or \ + float(last_updated) < _get_date_numeric(datetime.now() - timedelta(days=30)): + conf = tmdbapi.get_configuration() + if conf: + urls['original'] = conf['images']['secure_base_url'] + 'original' + urls['preview'] = conf['images']['secure_base_url'] + 'w780' + # url_settings.setSetting('originalUrl', urls['original']) + # url_settings.setSetting('previewUrl', urls['preview']) + # url_settings.setSetting('lastUpdated', str(_get_date_numeric(datetime.now()))) + return urls + +def _parse_trailer(trailers, fallback): + if trailers.get('youtube'): + return 'plugin://plugin.video.youtube/?action=play_video&videoid='+trailers['youtube'][0]['source'] + if fallback.get('youtube'): + return 'plugin://plugin.video.youtube/?action=play_video&videoid='+fallback['youtube'][0]['source'] + return None + +def _get_names(items): + return [item['name'] for item in items] if items else [] + +def _get_cast_members(casts, casttype, department, jobs): + result = [] + if casttype in casts: + for cast in casts[casttype]: + if cast['department'] == department and cast['job'] in jobs and cast['name'] not in result: + result.append(cast['name']) + return result diff --git a/plugins/movie_tv_info/tmdbscraper/lib/tmdbscraper/tmdbapi.py b/plugins/movie_tv_info/tmdbscraper/lib/tmdbscraper/tmdbapi.py new file mode 100644 index 0000000..1fda4b2 --- /dev/null +++ b/plugins/movie_tv_info/tmdbscraper/lib/tmdbscraper/tmdbapi.py @@ -0,0 +1,129 @@ +# -*- coding: UTF-8 -*- +# +# Copyright (C) 2020, Team Kodi +# +# This program 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. +# +# This program 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 this program. If not, see . +# pylint: disable=missing-docstring + +"""Functions to interact with TMDb API.""" + +from . import api_utils +try: + from typing import Optional, Text, Dict, List, Any # pylint: disable=unused-import + InfoType = Dict[Text, Any] # pylint: disable=invalid-name +except ImportError: + pass + + +HEADERS = ( + ('User-Agent', 'Kodi Movie scraper by Team Kodi'), + ('Accept', 'application/json'), +) +api_utils.set_headers(dict(HEADERS)) + +TMDB_PARAMS = {'api_key': 'f090bb54758cabf231fb605d3e3e0468'} +BASE_URL = 'https://api.themoviedb.org/3/{}' +SEARCH_URL = BASE_URL.format('search/movie') +FIND_URL = BASE_URL.format('find/{}') +MOVIE_URL = BASE_URL.format('movie/{}') +COLLECTION_URL = BASE_URL.format('collection/{}') +CONFIG_URL = BASE_URL.format('configuration') + + +def search_movie(query, year=None, language=None): + # type: (Text) -> List[InfoType] + """ + Search for a movie + + :param title: movie title to search + :param year: the year to search (optional) + :param language: the language filter for TMDb (optional) + :return: a list with found movies + """ + theurl = SEARCH_URL + params = _set_params(None, language) + params['query'] = query + if year is not None: + params['year'] = str(year) + return api_utils.load_info(theurl, params=params) + + +def find_movie_by_external_id(external_id, language=None): + # type: (Text) -> List[InfoType] + """ + Find movie based on external ID + + :param mid: external ID + :param language: the language filter for TMDb (optional) + :return: the movie or error + """ + theurl = FIND_URL.format(external_id) + params = _set_params(None, language) + params['external_source'] = 'imdb_id' + return api_utils.load_info(theurl, params=params) + + + +def get_movie(mid, language=None, append_to_response=None): + # type: (Text) -> List[InfoType] + """ + Get movie details + + :param mid: TMDb movie ID + :param language: the language filter for TMDb (optional) + :append_to_response: the additional data to get from TMDb (optional) + :return: the movie or error + """ + try: + theurl = MOVIE_URL.format(mid) + return api_utils.load_info(theurl, params=_set_params(append_to_response, language)) + except Exception as e: + print(repr(e)) + + +def get_collection(collection_id, language=None, append_to_response=None): + # type: (Text) -> List[InfoType] + """ + Get movie collection information + + :param collection_id: TMDb collection ID + :param language: the language filter for TMDb (optional) + :append_to_response: the additional data to get from TMDb (optional) + :return: the movie or error + """ + theurl = COLLECTION_URL.format(collection_id) + return api_utils.load_info(theurl, params=_set_params(append_to_response, language)) + + +def get_configuration(): + # type: (Text) -> List[InfoType] + """ + Get configuration information + + :return: configuration details or error + """ + return api_utils.load_info(CONFIG_URL, params=TMDB_PARAMS.copy()) + + +def _set_params(append_to_response, language): + params = TMDB_PARAMS.copy() + img_lang = 'en,null' + if language is not None: + params['language'] = language + img_lang = '%s,en,null' % language[0:2] + if append_to_response is not None: + params['append_to_response'] = append_to_response + if 'images' in append_to_response: + params['include_image_language'] = img_lang + return params diff --git a/plugins/movie_tv_info/tmdbscraper/lib/tmdbscraper/traktratings.py b/plugins/movie_tv_info/tmdbscraper/lib/tmdbscraper/traktratings.py new file mode 100644 index 0000000..7e24d5e --- /dev/null +++ b/plugins/movie_tv_info/tmdbscraper/lib/tmdbscraper/traktratings.py @@ -0,0 +1,55 @@ +# -*- coding: UTF-8 -*- +# +# Copyright (C) 2020, Team Kodi +# +# This program 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. +# +# This program 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 this program. If not, see . +# pylint: disable=missing-docstring + +"""Functions to interact with Trakt API.""" + +from __future__ import absolute_import, unicode_literals + +from . import api_utils +from . import get_imdb_id +try: + from typing import Optional, Text, Dict, List, Any # pylint: disable=unused-import + InfoType = Dict[Text, Any] # pylint: disable=invalid-name +except ImportError: + pass + + +HEADERS = ( + ('User-Agent', 'Kodi Movie scraper by Team Kodi'), + ('Accept', 'application/json'), + ('trakt-api-key', '5f2dc73b6b11c2ac212f5d8b4ec8f3dc4b727bb3f026cd254d89eda997fe64ae'), + ('trakt-api-version', '2'), + ('Content-Type', 'application/json'), +) +api_utils.set_headers(dict(HEADERS)) + +MOVIE_URL = 'https://api.trakt.tv/movies/{}' + + +def get_trakt_ratinginfo(uniqueids): + imdb_id = get_imdb_id(uniqueids) + result = {} + url = MOVIE_URL.format(imdb_id) + params = {'extended': 'full'} + movie_info = api_utils.load_info(url, params=params, default={}) + if(movie_info): + if 'votes' in movie_info and 'rating' in movie_info: + result['ratings'] = {'trakt': {'votes': int(movie_info['votes']), 'rating': float(movie_info['rating'])}} + elif 'rating' in movie_info: + result['ratings'] = {'trakt': {'rating': float(movie_info['rating'])}} + return result diff --git a/plugins/translate/brotli/_brotli.abi3.so b/plugins/translate/brotli/_brotli.abi3.so new file mode 100755 index 0000000..d1caea2 Binary files /dev/null and b/plugins/translate/brotli/_brotli.abi3.so differ