Added cover art and background art scraper

This commit is contained in:
itdominator 2021-11-11 23:32:59 -06:00
parent a937d1f313
commit 57d52fcddf
5 changed files with 404 additions and 2 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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))

View File

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

View File

@ -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()