diff --git a/src/core/routes/Routes.py b/src/core/routes/Routes.py index 306996b..61e3f25 100644 --- a/src/core/routes/Routes.py +++ b/src/core/routes/Routes.py @@ -1,5 +1,5 @@ # Python imports -import os, json, secrets, re, shutil +import os, json, secrets, requests, shutil, re # Lib imports from flask import request, session, render_template, send_from_directory, redirect @@ -11,9 +11,11 @@ from flask_login import current_user from core import app, logger, oidc, db, Favorites # Get from __init__ from core.utils import MessageHandler # Get simple message processor from core.utils.shellfm import WindowController # Get file manager controller +from core.utils.tmdbscraper import scraper # Get media art scraper msgHandler = MessageHandler() +tmdb = scraper.get_tmdb_scraper() window_controllers = {} # valid_fname_pat = re.compile(r"/^[a-zA-Z0-9-_\[\]\(\)| ]+$/") valid_fname_pat = re.compile(r"[a-z0-9A-Z-_\[\]\(\)\| ]{4,20}") @@ -81,16 +83,54 @@ def listFiles(_hash = None): return msgHandler.createMessageJSON("danger", error_msg) - sub_path = view.get_current_sub_path() + sub_path = view.get_current_sub_path() + current_directory = sub_path.split("/")[-1] + if "(" in current_directory and ")" in current_directory: + title = current_directory.split("(")[0].strip() + startIndex = current_directory.index('(') + 1 + endIndex = current_directory.index(')') + date = current_directory[startIndex:endIndex] + video_data = tmdb.search(title, date) + background_url = video_data[0]["backdrop_path"] + background_pth = view.get_current_directory() + "/" + "000.jpg" + + if not os.path.isfile(background_pth): + r = requests.get(background_url, stream = True) + + if r.status_code == 200: + r.raw.decode_content = True + with open(background_pth,'wb') as f: + shutil.copyfileobj(r.raw, f) + + view.load_directory() + print('Cover Background Image sucessfully retreived...') + else: + print('Cover Background Image Couldn\'t be retreived...') + + files = view.get_files_formatted() fave = db.session.query(Favorites).filter_by(link = sub_path).first() in_fave = "true" if fave else "false" + files.update({'in_fave': in_fave}) return files else: msg = "Can't manage the request type..." return msgHandler.createMessageJSON("danger", msg) +@app.route('/api/get-posters', methods=['GET', 'POST']) +def getPosters(): + if request.method == 'POST': + view = get_window_controller().get_window(1).get_view(0) + videos = view.get_videos() + + print(videos) + # tmdb.search("Matrix") + return videos + else: + msg = "Can't manage the request type..." + return msgHandler.createMessageJSON("danger", msg) + @app.route('/api/file-manager-action/<_type>/<_hash>', methods=['GET', 'POST']) def fileManagerAction(_type, _hash = None): view = get_window_controller().get_window(1).get_view(0) diff --git a/src/core/utils/tmdbscraper/scraper.py b/src/core/utils/tmdbscraper/scraper.py new file mode 100644 index 0000000..db25e2b --- /dev/null +++ b/src/core/utils/tmdbscraper/scraper.py @@ -0,0 +1,22 @@ +import json +import sys + +from .lib.tmdbscraper.tmdb import TMDBMovieScraper +from .lib.tmdbscraper.fanarttv import get_details as get_fanarttv_artwork +from .lib.tmdbscraper.imdbratings import get_details as get_imdb_details +from .lib.tmdbscraper.traktratings import get_trakt_ratinginfo +from .scraper_datahelper import combine_scraped_details_info_and_ratings, \ + combine_scraped_details_available_artwork, find_uniqueids_in_text, get_params +from .scraper_config import configure_scraped_details, PathSpecificSettings, \ + configure_tmdb_artwork, is_fanarttv_configured + + + + + + +def get_tmdb_scraper(): + language = 'en-US' + certcountry = 'us' + ADDON_SETTINGS = None + return TMDBMovieScraper(ADDON_SETTINGS, language, certcountry) diff --git a/src/core/utils/tmdbscraper/scraper_config.py b/src/core/utils/tmdbscraper/scraper_config.py new file mode 100644 index 0000000..76a042b --- /dev/null +++ b/src/core/utils/tmdbscraper/scraper_config.py @@ -0,0 +1,111 @@ +def configure_scraped_details(details, settings): + details = _configure_rating_prefix(details, settings) + details = _configure_keeporiginaltitle(details, settings) + details = _configure_trailer(details, settings) + details = _configure_multiple_studios(details, settings) + details = _configure_default_rating(details, settings) + details = _configure_tags(details, settings) + return details + +def configure_tmdb_artwork(details, settings): + if 'available_art' not in details: + return details + + art = details['available_art'] + fanart_enabled = settings.getSettingBool('fanart') + if not fanart_enabled: + if 'fanart' in art: + del art['fanart'] + if 'set.fanart' in art: + del art['set.fanart'] + if not settings.getSettingBool('landscape'): + if 'landscape' in art: + if fanart_enabled: + art['fanart'] = art.get('fanart', []) + art['landscape'] + del art['landscape'] + if 'set.landscape' in art: + if fanart_enabled: + art['set.fanart'] = art.get('set.fanart', []) + art['set.landscape'] + del art['set.landscape'] + + return details + +def is_fanarttv_configured(settings): + return settings.getSettingBool('enable_fanarttv_artwork') + +def _configure_rating_prefix(details, settings): + if details['info'].get('mpaa'): + details['info']['mpaa'] = settings.getSettingString('certprefix') + details['info']['mpaa'] + return details + +def _configure_keeporiginaltitle(details, settings): + if settings.getSettingBool('keeporiginaltitle'): + details['info']['title'] = details['info']['originaltitle'] + return details + +def _configure_trailer(details, settings): + if details['info'].get('trailer') and not settings.getSettingBool('trailer'): + del details['info']['trailer'] + return details + +def _configure_multiple_studios(details, settings): + if not settings.getSettingBool('multiple_studios'): + details['info']['studio'] = details['info']['studio'][:1] + return details + +def _configure_default_rating(details, settings): + imdb_default = bool(details['ratings'].get('imdb')) and settings.getSettingString('RatingS') == 'IMDb' + trakt_default = bool(details['ratings'].get('trakt')) and settings.getSettingString('RatingS') == 'Trakt' + default_rating = 'themoviedb' + if imdb_default: + default_rating = 'imdb' + elif trakt_default: + default_rating = 'trakt' + if default_rating not in details['ratings']: + default_rating = list(details['ratings'].keys())[0] if details['ratings'] else None + for rating_type in details['ratings'].keys(): + details['ratings'][rating_type]['default'] = rating_type == default_rating + return details + +def _configure_tags(details, settings): + if not settings.getSettingBool('add_tags'): + del details['info']['tag'] + return details + +# pylint: disable=invalid-name +try: + basestring +except NameError: # py2 / py3 + basestring = str + +#pylint: disable=redefined-builtin +class PathSpecificSettings(object): + # read-only shim for typed `xbmcaddon.Addon().getSetting*` methods + def __init__(self, settings_dict, log_fn): + self.data = settings_dict + self.log = log_fn + + def getSettingBool(self, id): + return self._inner_get_setting(id, bool, False) + + def getSettingInt(self, id): + return self._inner_get_setting(id, int, 0) + + def getSettingNumber(self, id): + return self._inner_get_setting(id, float, 0.0) + + def getSettingString(self, id): + return self._inner_get_setting(id, basestring, '') + + def _inner_get_setting(self, setting_id, setting_type, default): + value = self.data.get(setting_id) + if isinstance(value, setting_type): + return value + self._log_bad_value(value, setting_id) + return default + + def _log_bad_value(self, value, setting_id): + if value is None: + self.log("requested setting ({0}) was not found.".format(setting_id)) + else: + self.log('failed to load value "{0}" for setting {1}'.format(value, setting_id)) diff --git a/src/core/utils/tmdbscraper/scraper_datahelper.py b/src/core/utils/tmdbscraper/scraper_datahelper.py new file mode 100644 index 0000000..23504e0 --- /dev/null +++ b/src/core/utils/tmdbscraper/scraper_datahelper.py @@ -0,0 +1,54 @@ +import re +try: + from urlparse import parse_qsl +except ImportError: # py2 / py3 + from urllib.parse import parse_qsl + +# get addon params from the plugin path querystring +def get_params(argv): + result = {'handle': int(argv[0])} + if len(argv) < 2 or not argv[1]: + return result + + result.update(parse_qsl(argv[1].lstrip('?'))) + return result + +def combine_scraped_details_info_and_ratings(original_details, additional_details): + def update_or_set(details, key, value): + if key in details: + details[key].update(value) + else: + details[key] = value + + if additional_details: + if additional_details.get('info'): + update_or_set(original_details, 'info', additional_details['info']) + if additional_details.get('ratings'): + update_or_set(original_details, 'ratings', additional_details['ratings']) + return original_details + +def combine_scraped_details_available_artwork(original_details, additional_details): + if additional_details and additional_details.get('available_art'): + available_art = additional_details['available_art'] + if not original_details.get('available_art'): + original_details['available_art'] = available_art + else: + for arttype, artlist in available_art.items(): + original_details['available_art'][arttype] = \ + artlist + original_details['available_art'].get(arttype, []) + + return original_details + +def find_uniqueids_in_text(input_text): + result = {} + res = re.search(r'(themoviedb.org/movie/)([0-9]+)', input_text) + if (res): + result['tmdb'] = res.group(2) + res = re.search(r'imdb....?/title/tt([0-9]+)', input_text) + if (res): + result['imdb'] = 'tt' + res.group(1) + else: + res = re.search(r'imdb....?/Title\?t{0,2}([0-9]+)', input_text) + if (res): + result['imdb'] = 'tt' + res.group(1) + return result diff --git a/src/core/utils/tmdbscraper/scraper_xbmc.py b/src/core/utils/tmdbscraper/scraper_xbmc.py new file mode 100644 index 0000000..c976407 --- /dev/null +++ b/src/core/utils/tmdbscraper/scraper_xbmc.py @@ -0,0 +1,175 @@ +import json +import sys +import xbmc +import xbmcaddon +import xbmcgui +import xbmcplugin + +from lib.tmdbscraper.tmdb import TMDBMovieScraper +from lib.tmdbscraper.fanarttv import get_details as get_fanarttv_artwork +from lib.tmdbscraper.imdbratings import get_details as get_imdb_details +from lib.tmdbscraper.traktratings import get_trakt_ratinginfo +from scraper_datahelper import combine_scraped_details_info_and_ratings, \ + combine_scraped_details_available_artwork, find_uniqueids_in_text, get_params +from scraper_config import configure_scraped_details, PathSpecificSettings, \ + configure_tmdb_artwork, is_fanarttv_configured + +ADDON_SETTINGS = xbmcaddon.Addon() +ID = ADDON_SETTINGS.getAddonInfo('id') + +def log(msg, level=xbmc.LOGDEBUG): + xbmc.log(msg='[{addon}]: {msg}'.format(addon=ID, msg=msg), level=level) + +def get_tmdb_scraper(settings): + language = settings.getSettingString('language') + certcountry = settings.getSettingString('tmdbcertcountry') + return TMDBMovieScraper(ADDON_SETTINGS, language, certcountry) + +def search_for_movie(title, year, handle, settings): + log("Find movie with title '{title}' from year '{year}'".format(title=title, year=year), xbmc.LOGINFO) + title = _strip_trailing_article(title) + search_results = get_tmdb_scraper(settings).search(title, year) + if not search_results: + return + if 'error' in search_results: + header = "The Movie Database Python error searching with web service TMDB" + xbmcgui.Dialog().notification(header, search_results['error'], xbmcgui.NOTIFICATION_WARNING) + log(header + ': ' + search_results['error'], xbmc.LOGWARNING) + return + + for movie in search_results: + listitem = _searchresult_to_listitem(movie) + uniqueids = {'tmdb': str(movie['id'])} + xbmcplugin.addDirectoryItem(handle=handle, url=build_lookup_string(uniqueids), + listitem=listitem, isFolder=True) + +_articles = [prefix + article for prefix in (', ', ' ') for article in ("the", "a", "an")] +def _strip_trailing_article(title): + title = title.lower() + for article in _articles: + if title.endswith(article): + return title[:-len(article)] + return title + +def _searchresult_to_listitem(movie): + movie_info = {'title': movie['title']} + movie_label = movie['title'] + + movie_year = movie['release_date'].split('-')[0] if movie.get('release_date') else None + if movie_year: + movie_label += ' ({})'.format(movie_year) + movie_info['year'] = movie_year + + listitem = xbmcgui.ListItem(movie_label, offscreen=True) + + listitem.setInfo('video', movie_info) + if movie['poster_path']: + listitem.setArt({'thumb': movie['poster_path']}) + + return listitem + +# Low limit because a big list of artwork can cause trouble in some cases +# (a column can be too large for the MySQL integration), +# and how useful is a big list anyway? Not exactly rhetorical, this is an experiment. +IMAGE_LIMIT = 10 + +def add_artworks(listitem, artworks): + for arttype, artlist in artworks.items(): + if arttype == 'fanart': + continue + for image in artlist[:IMAGE_LIMIT]: + listitem.addAvailableArtwork(image['url'], arttype) + + fanart_to_set = [{'image': image['url'], 'preview': image['preview']} + for image in artworks['fanart'][:IMAGE_LIMIT]] + listitem.setAvailableFanart(fanart_to_set) + +def get_details(input_uniqueids, handle, settings): + if not input_uniqueids: + return False + details = get_tmdb_scraper(settings).get_details(input_uniqueids) + if not details: + return False + if 'error' in details: + header = "The Movie Database Python error with web service TMDB" + xbmcgui.Dialog().notification(header, details['error'], xbmcgui.NOTIFICATION_WARNING) + log(header + ': ' + details['error'], xbmc.LOGWARNING) + return False + + details = configure_tmdb_artwork(details, settings) + + if settings.getSettingString('RatingS') == 'IMDb' or settings.getSettingBool('imdbanyway'): + imdbinfo = get_imdb_details(details['uniqueids']) + if 'error' in imdbinfo: + header = "The Movie Database Python error with website IMDB" + log(header + ': ' + imdbinfo['error'], xbmc.LOGWARNING) + else: + details = combine_scraped_details_info_and_ratings(details, imdbinfo) + + if settings.getSettingString('RatingS') == 'Trakt' or settings.getSettingBool('traktanyway'): + traktinfo = get_trakt_ratinginfo(details['uniqueids']) + details = combine_scraped_details_info_and_ratings(details, traktinfo) + + if is_fanarttv_configured(settings): + fanarttv_info = get_fanarttv_artwork(details['uniqueids'], + settings.getSettingString('fanarttv_clientkey'), + settings.getSettingString('fanarttv_language'), + details['_info']['set_tmdbid']) + details = combine_scraped_details_available_artwork(details, fanarttv_info) + + details = configure_scraped_details(details, settings) + + listitem = xbmcgui.ListItem(details['info']['title'], offscreen=True) + listitem.setInfo('video', details['info']) + listitem.setCast(details['cast']) + listitem.setUniqueIDs(details['uniqueids'], 'tmdb') + add_artworks(listitem, details['available_art']) + + for rating_type, value in details['ratings'].items(): + if 'votes' in value: + listitem.setRating(rating_type, value['rating'], value['votes'], value['default']) + else: + listitem.setRating(rating_type, value['rating'], defaultt=value['default']) + + xbmcplugin.setResolvedUrl(handle=handle, succeeded=True, listitem=listitem) + return True + +def find_uniqueids_in_nfo(nfo, handle): + uniqueids = find_uniqueids_in_text(nfo) + if uniqueids: + listitem = xbmcgui.ListItem(offscreen=True) + xbmcplugin.addDirectoryItem( + handle=handle, url=build_lookup_string(uniqueids), listitem=listitem, isFolder=True) + +def build_lookup_string(uniqueids): + return json.dumps(uniqueids) + +def parse_lookup_string(uniqueids): + try: + return json.loads(uniqueids) + except ValueError: + log("Can't parse this lookup string, is it from another add-on?\n" + uniqueids, xbmc.LOGWARNING) + return None + +def run(): + params = get_params(sys.argv[1:]) + enddir = True + if 'action' in params: + settings = ADDON_SETTINGS if not params.get('pathSettings') else \ + PathSpecificSettings(json.loads(params['pathSettings']), lambda msg: log(msg, xbmc.LOGWARNING)) + action = params["action"] + if action == 'find' and 'title' in params: + search_for_movie(params["title"], params.get("year"), params['handle'], settings) + elif action == 'getdetails' and 'url' in params: + enddir = not get_details(parse_lookup_string(params["url"]), params['handle'], settings) + elif action == 'NfoUrl' and 'nfo' in params: + find_uniqueids_in_nfo(params["nfo"], params['handle']) + else: + log("unhandled action: " + action, xbmc.LOGWARNING) + else: + log("No action in 'params' to act on", xbmc.LOGWARNING) + if enddir: + xbmcplugin.endOfDirectory(params['handle']) + +if __name__ == '__main__': + run()