#!/usr/bin/python # -*- coding: utf-8 -*- ''' script.skin.helper.service Helper service and scripts for Kodi skins webservice.py Simple webservice to directly retrieve metadata from artwork module ''' import cherrypy import threading from utils import log_msg, log_exception, json import xbmc import xbmcvfs import sys # port is hardcoded as there is no way in Kodi to pass a INFO-label inside a panel, # otherwise the portnumber could be passed to the skin through a skin setting or window prop PORT = 52307 class Root: __mutils = None def __init__(self, mutils): self.__mutils = mutils @cherrypy.expose def default(self, path): '''all other requests go here''' log_msg("Webservice: Unknown method called ! (%s)" % path, xbmc.LOGWARNING) raise cherrypy.HTTPError(404, "Unknown method called") @cherrypy.expose def getartwork(self, **kwargs): '''get video artwork and metadata''' log_msg("webservice.getartwork called with args: %s" % kwargs) title = kwargs.get("title", "") year = kwargs.get("year", "") media_type = kwargs.get("mediatype", "") imdb_id = kwargs.get("imdbid", "") artwork = {} if not kwargs.get("type"): kwargs["json"] = "true" if not imdb_id: omdb_details = self.__mutils.get_omdb_info(imdb_id, title, year, media_type) if omdb_details: imdb_id = omdb_details.get("imdbnumber") if not media_type: media_type = omdb_details.get("media_type","") if imdb_id: artwork = self.__mutils.get_extended_artwork(imdb_id, "", "", media_type) return self.handle_artwork(artwork, kwargs) def genreimages(self, params): '''get images for given genre''' log_msg("webservice.genreimages called with args: %s" % params) genre = params.get("title", "") arttype = params.get("type", "").split(".")[0] mediatype = params["mediatype"] randomize = params["randomize"] artwork = {} lib_path = u"plugin://script.skin.helper.service/?action=genrebackground"\ "&genre=%s&arttype=%s&mediatype=%s&random=%s" % (genre, arttype, mediatype, randomize) for count, item in enumerate(self.__mutils.kodidb.files(lib_path, limits=(0, 5))): artwork["%s.%s" % (arttype, count)] = item["file"] return self.handle_artwork(artwork, params) @cherrypy.expose def getmoviegenreimages(self, **kwargs): kwargs["mediatype"] = "movies" kwargs["randomize"] = "false" return self.genreimages(kwargs) @cherrypy.expose def gettvshowgenreimages(self, **kwargs): kwargs["mediatype"] = "tvshows" kwargs["randomize"] = "false" return self.genreimages(**kwargs) @cherrypy.expose def getmoviegenreimagesrandom(self, **kwargs): kwargs["mediatype"] = "movies" kwargs["randomize"] = "true" return self.genreimages(kwargs) @cherrypy.expose def gettvshowgenreimagesrandom(self, **kwargs): kwargs["mediatype"] = "tvshows" kwargs["randomize"] = "true" return self.genreimages(**kwargs) @cherrypy.expose def getpvrthumb(self, **kwargs): '''get pvr images''' log_msg("webservice.getpvrthumb called with args: %s" % kwargs) title = kwargs.get("title", "") channel = kwargs.get("channel", "") genre = kwargs.get("genre", "") artwork = self.__mutils.get_pvr_artwork(title, channel, genre) return self.handle_artwork(artwork, kwargs) @cherrypy.expose def getallpvrthumb(self, **kwargs): '''get all pvr images''' kwargs["json"] = "true" return self.getpvrthumb(**kwargs) @cherrypy.expose def getmusicart(self, **kwargs): '''get pvr images''' log_msg("webservice.getmusicart called with args: %s" % kwargs) artist = kwargs.get("artist", "") album = kwargs.get("album", "") track = kwargs.get("track", "") artwork = self.__mutils.get_music_artwork(artist, album, track) return self.handle_artwork(artwork, kwargs) @cherrypy.expose def getthumb(self, **kwargs): '''get generic thumb image from google/youtube''' log_msg("webservice.getthumb called with args: %s" % kwargs) title = kwargs.get("title", "") preferred_types, is_json_request, fallback = self.get_common_params(kwargs) image = self.__mutils.google.search_image(title) if not image: image = fallback return self.handle_image(image) @cherrypy.expose def getvarimage(self, **kwargs): '''get image from kodi variable/resource addon''' log_msg("webservice.getvarimage called with args: %s" % kwargs) preferred_types, is_json_request, fallback = self.get_common_params(kwargs) title = kwargs.get("title", "") title = title.replace("{", "[").replace("}", "]") image = xbmc.getInfoLabel(title) if not xbmcvfs.exists(image): if "resource.images" in image: log_msg( "Texture packed resource addons are not supported by the webservice! - %s" % image, xbmc.LOGWARNING) image = "" if not image: image = fallback return self.handle_image(image) def handle_artwork(self, artwork, params): '''handle the requested images''' preferred_types, is_json_request, fallback = self.get_common_params(params) if is_json_request: return self.handle_json(artwork) else: image = self.get_image(artwork, preferred_types, fallback) return self.handle_image(image) def handle_image(self, image): '''serve image''' if image: # send single image try: ext = image.split(".")[-1] cherrypy.response.headers['Content-Type'] = 'image/%s' % ext modified = xbmcvfs.Stat(image).st_mtime() cherrypy.response.headers['Last-Modified'] = "%s" % modified image = xbmcvfs.File(image) cherrypy.response.headers['Content-Length'] = str(image.size()) if cherrypy.request.method.upper() == 'GET': img_data = image.readBytes() image.close() return str(img_data) else: image.close() except Exception as exc: log_exception(__name__, exc) else: raise cherrypy.HTTPError(404, "No image found matching the criteria") def handle_json(self, artwork): '''send the complete details as json object''' artwork = json.dumps(artwork) cherrypy.response.headers['Content-Type'] = 'application/json' cherrypy.response.headers['Content-Length'] = len(artwork) if cherrypy.request.method.upper() == 'GET': return artwork @staticmethod def get_common_params(params): '''parse the common parameters from the arguments''' preferred_types = params.get("type") if preferred_types: preferred_types = preferred_types.split(",") else: preferred_types = [] fallback = params.get("fallback", "") is_json_request = params.get("json", "") == "true" if fallback and not xbmcvfs.exists(fallback): fallback = "special://skin/media/" + fallback if not xbmcvfs.exists(fallback): fallback = "" log_msg( "Webservice --> Non existent fallback image detected - " "please use a full path to the fallback image!", xbmc.LOGWARNING) return preferred_types, is_json_request, fallback @staticmethod def get_image(artwork, preferred_types, fallback): '''get the preferred image from the results''' image = "" if artwork and artwork.get("art"): artwork = artwork["art"] if preferred_types: for pref_type in preferred_types: if not image: image = artwork.get(pref_type, "") elif not image and artwork.get("landscape"): image = artwork["landscape"] elif not image and artwork.get("fanart"): image = artwork["fanart"] elif not image and artwork.get("poster"): image = artwork["poster"] elif not image and artwork.get("thumb"): image = artwork["thumb"] # set fallback image if nothing else worked if not image or not xbmcvfs.exists(image): image = fallback return image class WebService(threading.Thread): __root = None def __init__(self, metadatautils): self.__root = Root(metadatautils) cherrypy.config.update({ 'engine.autoreload.on' : False, 'log.screen': False, 'engine.timeout_monitor.frequency': 5, 'server.shutdown_timeout': 1, }) threading.Thread.__init__(self) def run(self): log_msg("Starting WebService on port %s" % PORT, xbmc.LOGNOTICE) conf = { 'global': { 'server.socket_host': '0.0.0.0', 'server.socket_port': PORT }, '/': {} } cherrypy.quickstart(self.__root, '/', conf) def stop(self): cherrypy.engine.exit() self.join(0) del self.__root