Beispiel #1
0
    def onInit(self):
        '''triggered when the dialog is drawn'''
        if self.listitem:
            self.clearList()
            kodidb = KodiDb()
            if isinstance(self.listitem, dict):
                self.listitem = kodidb.prepare_listitem(self.listitem)
                self.listitem = kodidb.create_listitem(self.listitem, False)
            del kodidb
            self.addItem(self.listitem)

        # disable some controls if existing
        disable_controls = [9, 7, 101, 6]
        for item in disable_controls:
            try:
                self.getControl(item).setVisible(False)
            except Exception:
                pass

        # enable some controls if existing
        disable_controls = [351, 352]
        for item in disable_controls:
            try:
                self.getControl(item).setVisible(True)
                self.getControl(item).setEnabled(True)
            except Exception:
                pass
 def __init__(self):
     self.cache = SimpleCache()
     self.kodidb = KodiDb()
     self.win = xbmcgui.Window(10000)
     self.addon = xbmcaddon.Addon(ADDON_ID)
     self.kodimonitor = xbmc.Monitor()
     self.all_backgrounds_labels = []
     self.smartshortcuts = SmartShortCuts(self)
     self.wallimages = WallImages(self)
     self.winprops = {}
Beispiel #3
0
    def __init__(self):
        self.cache = SimpleCache()
        self.kodi_db = KodiDb()
        self.win = xbmcgui.Window(10000)
        try:
            self.params = dict(
                urlparse.parse_qsl(sys.argv[2].replace(
                    '?', '').lower().decode("utf-8")))
            log_msg("plugin called with parameters: %s" % self.params)
            self.main()
        except Exception as exc:
            log_exception(__name__, exc)
            xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))

        # cleanup when done processing
        self.close()
 def playlists_nodes(self):
     '''build smart shortcuts listing for playlists'''
     nodes = []
     if xbmc.getCondVisibility("Skin.HasSetting(SmartShortcuts.playlists)"):
         # build node listing
         count = 0
         import xml.etree.ElementTree as xmltree
         paths = [('special://videoplaylists/', 'Videos'),
                  ('special://musicplaylists/', 'Music')]
         for playlistpath in paths:
             if xbmcvfs.exists(playlistpath[0]):
                 media_array = KodiDb().files(playlistpath[0])
                 for item in media_array:
                     try:
                         label = ""
                         if item["file"].endswith(
                                 ".xsp") and "Emby" not in item["file"]:
                             playlist = item["file"]
                             contents = xbmcvfs.File(playlist, 'r')
                             contents_data = contents.read()
                             contents.close()
                             xmldata = xmltree.fromstring(contents_data)
                             media_type = "unknown"
                             label = item["label"]
                             for line in xmldata.getiterator():
                                 if line.tag == "smartplaylist":
                                     media_type = line.attrib['type']
                                 if line.tag == "name":
                                     label = line.text
                             key = "playlist.%s" % count
                             item_path = "ActivateWindow(%s,%s,return)" % (
                                 playlistpath[1], playlist)
                             self.bgupdater.set_winprop(
                                 "%s.label" % key, label)
                             self.bgupdater.set_winprop(
                                 "%s.title" % key, label)
                             self.bgupdater.set_winprop(
                                 "%s.action" % key, item_path)
                             self.bgupdater.set_winprop(
                                 "%s.path" % key, item_path)
                             self.bgupdater.set_winprop(
                                 "%s.content" % key, playlist)
                             self.bgupdater.set_winprop(
                                 "%s.type" % key, media_type)
                             nodes.append(
                                 ("%s.image" % key, playlist, label))
                             if key not in self.toplevel_nodes:
                                 self.toplevel_nodes.append(key)
                             count += 1
                     except Exception:
                         log_msg(
                             "Error while processing smart shortcuts for playlist %s  --> "
                             "This file seems to be corrupted, please remove it from your system "
                             "to prevent any further errors." %
                             item["file"], xbmc.LOGWARNING)
         self.all_nodes["playlists"] = nodes
Beispiel #5
0
 def open_item(self):
     '''open selected item'''
     control_id = self.getFocusId()
     listitem = self.getControl(control_id).getSelectedItem()
     if "videodb:" in listitem.getfilename():
         # tvshow: open path
         xbmc.executebuiltin('ReplaceWindow(Videos,"%s")' %
                             self.listitem.getfilename())
         self.close_dialog()
     elif "actor" in listitem.getProperty("DBTYPE"):
         # cast dialog
         xbmc.executebuiltin("ActivateWindow(busydialog)")
         from dialogselect import DialogSelect
         results = []
         kodidb = KodiDb()
         name = listitem.getLabel().decode("utf-8")
         items = kodidb.castmedia(name)
         items = process_method_on_list(kodidb.prepare_listitem, items)
         for item in items:
             if item["file"].startswith("videodb://"):
                 item["file"] = "ActivateWindow(Videos,%s,return)" % item[
                     "file"]
             else:
                 item["file"] = 'PlayMedia("%s")' % item["file"]
             results.append(kodidb.create_listitem(item, False))
         # finished lookup - display listing with results
         xbmc.executebuiltin("dialog.Close(busydialog)")
         dialog = DialogSelect("DialogSelect.xml",
                               "",
                               listing=results,
                               windowtitle=name,
                               richlayout=True)
         dialog.doModal()
         result = dialog.result
         del dialog
         if result:
             xbmc.executebuiltin(result.getfilename())
             self.close_dialog()
     else:
         # video file: start playback
         xbmc.executebuiltin('PlayMedia("%s")' % listitem.getfilename())
         self.close_dialog()
    def __init__(self):
        '''Initialization and main code run'''
        self.win = xbmcgui.Window(10000)
        self.addon = xbmcaddon.Addon(ADDON_ID)
        self.kodidb = KodiDb()
        self.cache = SimpleCache()

        self.params = self.get_params()
        log_msg("MainModule called with parameters: %s" % self.params)
        action = self.params.get("action", "")
        # launch module for action provided by this script
        try:
            getattr(self, action)()
        except AttributeError:
            log_exception(__name__, "No such action: %s" % action)
        except Exception as exc:
            log_exception(__name__, exc)
        finally:
            xbmc.executebuiltin("dialog.Close(busydialog)")

        # do cleanup
        self.close()
    def __init__(self):
        self.cache = SimpleCache()
        self.kodi_db = KodiDb()
        self.win = xbmcgui.Window(10000)
        try:
            self.params = dict(urlparse.parse_qsl(sys.argv[2].replace("?", "").lower().decode("utf-8")))
            log_msg("plugin called with parameters: %s" % self.params)
            self.main()
        except Exception as exc:
            log_exception(__name__, exc)
            xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))

        # cleanup when done processing
        self.close()
 def open_item(self):
     '''open selected item'''
     control_id = self.getFocusId()
     listitem = self.getControl(control_id).getSelectedItem()
     if "videodb:" in listitem.getfilename():
         #tvshow: open path
         xbmc.executebuiltin('ReplaceWindow(Videos,"%s")' % self.listitem.getfilename())
         self.close_dialog()
     elif "actor" in listitem.getProperty("DBTYPE"):
         #cast dialog
         xbmc.executebuiltin("ActivateWindow(busydialog)")
         from dialogselect import DialogSelect
         results = []
         kodidb = KodiDb()
         name = listitem.getLabel().decode("utf-8")
         items = kodidb.castmedia(name)
         items = process_method_on_list(kodidb.prepare_listitem, items)
         for item in items:
             if item["file"].startswith("videodb://"):
                 item["file"] = "ActivateWindow(Videos,%s,return)" % item["file"]
             else:
                 item["file"] = 'PlayMedia("%s")' % item["file"]
             results.append(kodidb.create_listitem(item, False))
         # finished lookup - display listing with results
         xbmc.executebuiltin("dialog.Close(busydialog)")
         dialog = DialogSelect("DialogSelect.xml", "", listing=results, windowtitle=name, richlayout=True)
         dialog.doModal()
         result = dialog.result
         del dialog
         if result:
             xbmc.executebuiltin(result.getfilename())
             self.close_dialog()
     else:
         # video file: start playback
         xbmc.executebuiltin('PlayMedia("%s")' % listitem.getfilename())
         self.close_dialog()
    def __init__(self):
        '''Initialization and main code run'''
        self.win = xbmcgui.Window(10000)
        self.addon = xbmcaddon.Addon(ADDON_ID)
        self.kodidb = KodiDb()
        self.cache = SimpleCache()

        self.params = self.get_params()
        log_msg("MainModule called with parameters: %s" % self.params)
        action = self.params.get("action", "")
        # launch module for action provided by this script
        try:
            getattr(self, action)()
        except AttributeError:
            log_exception(__name__, "No such action: %s" % action)
        except Exception as exc:
            log_exception(__name__, exc)
        finally:
            xbmc.executebuiltin("dialog.Close(busydialog)")

        # do cleanup
        self.close()
 def favourites_nodes(self):
     '''build smart shortcuts for favourites'''
     if xbmc.getCondVisibility("Skin.HasSetting(SmartShortcuts.favorites)"):
         # build node listing
         nodes = []
         favs = KodiDb().favourites()
         for count, fav in enumerate(favs):
             if fav["type"] == "window":
                 content = fav["windowparameter"]
                 # check if this is a valid path with content
                 if ("script://" not in content.lower()
                         and "mode=9" not in content.lower()
                         and "search" not in content.lower()
                         and "play" not in content.lower()):
                     item_path = "ActivateWindow(%s,%s,return)" % (
                         fav["window"], content)
                     if "&" in content and "?" in content and "=" in content and not content.endswith(
                             "/"):
                         content += "&widget=true"
                     media_type = detect_plugin_content(content)
                     if media_type:
                         key = "favorite.%s" % count
                         self.bgupdater.set_winprop("%s.label" % key,
                                                    fav["label"])
                         self.bgupdater.set_winprop("%s.title" % key,
                                                    fav["label"])
                         self.bgupdater.set_winprop("%s.action" % key,
                                                    item_path)
                         self.bgupdater.set_winprop("%s.path" % key,
                                                    item_path)
                         self.bgupdater.set_winprop("%s.content" % key,
                                                    content)
                         self.bgupdater.set_winprop("%s.type" % key,
                                                    media_type)
                         if key not in self.toplevel_nodes:
                             self.toplevel_nodes.append(key)
                         nodes.append(
                             ("%s.image" % key, content, fav["label"]))
         self.all_nodes["favourites"] = nodes
class SearchBackgroundThread(threading.Thread):
    '''Background thread to complement our search dialog,
    fills the listing while UI keeps responsive'''
    active = True
    dialog = None
    search_string = ""

    def __init__(self, *args):
        xbmc.log("SearchBackgroundThread Init")
        threading.Thread.__init__(self, *args)
        self.kodidb = KodiDb()
        self.actors = []
        thread.start_new_thread(self.set_actors, ())

    def set_search(self, searchstr):
        '''set search query'''
        self.search_string = searchstr

    def stop_running(self):
        '''stop thread end exit'''
        self.active = False

    def set_dialog(self, dialog):
        '''set the active dialog to perform actions'''
        self.dialog = dialog
        
    def set_actors(self):
        '''fill list with all actors'''
        self.actors = self.kodidb.actors()

    def run(self):
        '''Main run loop for the background thread'''
        last_searchstring = ""
        monitor = xbmc.Monitor()
        while not monitor.abortRequested() and self.active:
            if self.search_string != last_searchstring:
                last_searchstring = self.search_string
                self.do_search(self.search_string)
            monitor.waitForAbort(1)
        del monitor

    def do_search(self, search_term):
        '''scrape results for search query'''

        movies_list = self.dialog.getControl(3110)
        series_list = self.dialog.getControl(3111)
        cast_list = self.dialog.getControl(3112)

        # clear current values
        movies_list.reset()
        series_list.reset()
        cast_list.reset()

        if len(search_term) == 0:
            return

        filters = [{"operator": "contains", "field": "title", "value": search_term}]

        # Process movies
        items = self.kodidb.movies(filters=filters)
        items = process_method_on_list(self.kodidb.prepare_listitem, items)
        result = []
        for item in items:
            result.append(self.kodidb.create_listitem(item, False))
        movies_list.addItems(result)

        # Process tvshows
        items = self.kodidb.tvshows(filters=filters)
        items = process_method_on_list(self.kodidb.prepare_listitem, items)
        result = []
        for item in items:
            item["file"] = 'videodb://tvshows/titles/%s' % item['tvshowid']
            item["isFolder"] = True
            result.append(self.kodidb.create_listitem(item, False))
        series_list.addItems(result)

        # Process cast
        result = []
        for item in self.actors:
            if search_term.lower() in item["label"].lower():
                item = self.kodidb.prepare_listitem(item)
                item["file"] = "RunScript(script.skin.helper.service,action=getcastmedia,name=%s)" % item["label"]
                result.append(self.kodidb.create_listitem(item, False))
        cast_list.addItems(result)
class MainModule:
    '''mainmodule provides the script methods for the skinhelper addon'''
    def __init__(self):
        '''Initialization and main code run'''
        self.win = xbmcgui.Window(10000)
        self.addon = xbmcaddon.Addon(ADDON_ID)
        self.kodidb = KodiDb()
        self.cache = SimpleCache()

        self.params = self.get_params()
        log_msg("MainModule called with parameters: %s" % self.params)
        action = self.params.get("action", "")
        # launch module for action provided by this script
        try:
            getattr(self, action)()
        except AttributeError:
            log_exception(__name__, "No such action: %s" % action)
        except Exception as exc:
            log_exception(__name__, exc)
        finally:
            xbmc.executebuiltin("dialog.Close(busydialog)")

        # do cleanup
        self.close()

    def close(self):
        '''Cleanup Kodi Cpython instances on exit'''
        self.cache.close()
        del self.win
        del self.addon
        del self.kodidb
        log_msg("MainModule exited")

    @classmethod
    def get_params(self):
        '''extract the params from the called script path'''
        params = {}
        for arg in sys.argv[1:]:
            paramname = arg.split('=')[0]
            paramvalue = arg.replace(paramname + "=", "")
            paramname = paramname.lower()
            if paramname == "action":
                paramvalue = paramvalue.lower()
            params[paramname] = paramvalue
        return params

    def deprecated_method(self, newaddon):
        '''
            used when one of the deprecated methods is called
            print warning in log and call the external script with the same parameters
        '''
        action = self.params.get("action")
        log_msg(
            "Deprecated method: %s. Please call %s directly" %
            (action, newaddon), xbmc.LOGWARNING)
        paramstring = ""
        for key, value in self.params.iteritems():
            paramstring += ",%s=%s" % (key, value)
        if xbmc.getCondVisibility("System.HasAddon(%s)" % newaddon):
            xbmc.executebuiltin("RunAddon(%s%s)" % (newaddon, paramstring))
        else:
            # trigger install of the addon
            if KODI_VERSION > 16:
                xbmc.executebuiltin("InstallAddon(%s)" % newaddon)
            else:
                xbmc.executebuiltin("RunPlugin(plugin://%s)" % newaddon)

    @staticmethod
    def musicsearch():
        '''helper to go directly to music search dialog'''
        xbmc.executebuiltin("ActivateWindow(Music)")
        xbmc.executebuiltin("SendClick(8)")

    def setview(self):
        '''sets the selected viewmode for the container'''
        xbmc.executebuiltin("ActivateWindow(busydialog)")
        content_type = get_current_content_type()
        if not content_type:
            content_type = "files"
        current_view = xbmc.getInfoLabel("Container.Viewmode").decode("utf-8")
        view_id, view_label = self.selectview(content_type, current_view)
        current_forced_view = xbmc.getInfoLabel(
            "Skin.String(SkinHelper.ForcedViews.%s)" % content_type)

        if view_id is not None:
            # also store forced view
            if (content_type and current_forced_view
                    and current_forced_view != "None"
                    and xbmc.getCondVisibility(
                        "Skin.HasSetting(SkinHelper.ForcedViews.Enabled)")):
                xbmc.executebuiltin(
                    "Skin.SetString(SkinHelper.ForcedViews.%s,%s)" %
                    (content_type, view_id))
                xbmc.executebuiltin(
                    "Skin.SetString(SkinHelper.ForcedViews.%s.label,%s)" %
                    (content_type, view_label))
                self.win.setProperty("SkinHelper.ForcedView", view_id)
                if not xbmc.getCondVisibility(
                        "Control.HasFocus(%s)" % current_forced_view):
                    xbmc.sleep(100)
                    xbmc.executebuiltin("Container.SetViewMode(%s)" % view_id)
                    xbmc.executebuiltin("SetFocus(%s)" % view_id)
            else:
                self.win.clearProperty("SkinHelper.ForcedView")
            # set view
            xbmc.executebuiltin("Container.SetViewMode(%s)" % view_id)

    def selectview(self,
                   content_type="other",
                   current_view=None,
                   display_none=False):
        '''reads skinfile with all views to present a dialog to choose from'''
        cur_view_select_id = None
        label = ""
        all_views = []
        if display_none:
            listitem = xbmcgui.ListItem(label="None")
            listitem.setProperty("id", "None")
            all_views.append(listitem)
        # read the special skin views file
        views_file = xbmc.translatePath(
            'special://skin/extras/views.xml').decode("utf-8")
        if xbmcvfs.exists(views_file):
            doc = parse(views_file)
            listing = doc.documentElement.getElementsByTagName('view')
            itemcount = 0
            for view in listing:
                label = xbmc.getLocalizedString(
                    int(view.attributes['languageid'].nodeValue))
                viewid = view.attributes['value'].nodeValue
                mediatypes = view.attributes['type'].nodeValue.lower().split(
                    ",")
                if label.lower() == current_view.lower(
                ) or viewid == current_view:
                    cur_view_select_id = itemcount
                    if display_none:
                        cur_view_select_id += 1
                if (("all" in mediatypes or content_type.lower() in mediatypes)
                        and (not "!" + content_type.lower() in mediatypes)
                        and not xbmc.getCondVisibility(
                            "Skin.HasSetting(SkinHelper.view.Disabled.%s)" %
                            viewid)):
                    image = "special://skin/extras/viewthumbs/%s.jpg" % viewid
                    listitem = xbmcgui.ListItem(label=label, iconImage=image)
                    listitem.setProperty("viewid", viewid)
                    listitem.setProperty("icon", image)
                    all_views.append(listitem)
                    itemcount += 1
        dialog = DialogSelect("DialogSelect.xml",
                              "",
                              listing=all_views,
                              windowtitle=self.addon.getLocalizedString(32012),
                              richlayout=True)
        dialog.autofocus_id = cur_view_select_id
        dialog.doModal()
        result = dialog.result
        del dialog
        if result:
            viewid = result.getProperty("viewid")
            label = result.getLabel().decode("utf-8")
            return (viewid, label)
        else:
            return (None, None)

    def enableviews(self):
        '''show select dialog to enable/disable views'''
        all_views = []
        views_file = xbmc.translatePath(
            'special://skin/extras/views.xml').decode("utf-8")
        if xbmcvfs.exists(views_file):
            doc = parse(views_file)
            listing = doc.documentElement.getElementsByTagName('view')
            for view in listing:
                view_id = view.attributes['value'].nodeValue
                label = xbmc.getLocalizedString(
                    int(view.attributes['languageid'].nodeValue))
                desc = label + " (" + str(view_id) + ")"
                listitem = xbmcgui.ListItem(label=label, label2=desc)
                listitem.setProperty("viewid", view_id)
                if not xbmc.getCondVisibility(
                        "Skin.HasSetting(SkinHelper.view.Disabled.%s)" %
                        view_id):
                    listitem.select(selected=True)
                all_views.append(listitem)

        dialog = DialogSelect("DialogSelect.xml",
                              "",
                              listing=all_views,
                              windowtitle=self.addon.getLocalizedString(32013),
                              multiselect=True)
        dialog.doModal()
        result = dialog.result
        del dialog
        if result:
            for item in result:
                view_id = item.getProperty("viewid")
                if item.isSelected():
                    # view is enabled
                    xbmc.executebuiltin(
                        "Skin.Reset(SkinHelper.view.Disabled.%s)" % view_id)
                else:
                    # view is disabled
                    xbmc.executebuiltin(
                        "Skin.SetBool(SkinHelper.view.Disabled.%s)" % view_id)

    def setforcedview(self):
        '''helper that sets a forced view for a specific content type'''
        content_type = self.params.get("contenttype")
        if content_type:
            current_view = xbmc.getInfoLabel(
                "Skin.String(SkinHelper.ForcedViews.%s)" % content_type)
            if not current_view:
                current_view = "0"
            view_id, view_label = self.selectview(content_type, current_view,
                                                  True)
            if view_id:
                xbmc.executebuiltin(
                    "Skin.SetString(SkinHelper.ForcedViews.%s,%s)" %
                    (content_type, view_id))
                xbmc.executebuiltin(
                    "Skin.SetString(SkinHelper.ForcedViews.%s.label,%s)" %
                    (content_type, view_label))

    @staticmethod
    def get_youtube_listing(searchquery):
        '''get items from youtube plugin by query'''
        lib_path = u"plugin://plugin.video.youtube/kodion/search/query/?q=%s" % searchquery
        return KodiDb().files(lib_path)

    def searchyoutube(self):
        '''helper to search youtube for the given title'''
        xbmc.executebuiltin("ActivateWindow(busydialog)")
        title = self.params.get("title", "")
        window_header = self.params.get("header", "")
        results = []
        for media in self.get_youtube_listing(title):
            if not media["filetype"] == "directory":
                label = media["label"]
                label2 = media["plot"]
                image = ""
                if media.get('art'):
                    if media['art'].get('thumb'):
                        image = (media['art']['thumb'])
                listitem = xbmcgui.ListItem(label=label,
                                            label2=label2,
                                            iconImage=image)
                listitem.setProperty("path", media["file"])
                results.append(listitem)

        # finished lookup - display listing with results
        xbmc.executebuiltin("dialog.Close(busydialog)")
        dialog = DialogSelect("DialogSelect.xml",
                              "",
                              listing=results,
                              windowtitle=window_header,
                              multiselect=False,
                              richlayout=True)
        dialog.doModal()
        result = dialog.result
        del dialog
        if result:
            if xbmc.getCondVisibility(
                    "Window.IsActive(script-skin_helper_service-CustomInfo.xml) | "
                    "Window.IsActive(movieinformation)"):
                xbmc.executebuiltin("Dialog.Close(movieinformation)")
                xbmc.executebuiltin(
                    "Dialog.Close(script-skin_helper_service-CustomInfo.xml)")
                xbmc.sleep(1000)
            xbmc.executebuiltin('PlayMedia("%s")' % result.getProperty("path"))
            del result

    def getcastmedia(self):
        '''helper to show a dialog with all media for a specific actor'''
        xbmc.executebuiltin("ActivateWindow(busydialog)")
        name = self.params.get("name", "")
        window_header = self.params.get("name", "")
        results = []
        items = self.kodidb.castmedia(name)
        items = process_method_on_list(self.kodidb.prepare_listitem, items)
        for item in items:
            if item["file"].startswith("videodb://"):
                item[
                    "file"] = "ActivateWindow(Videos,%s,return)" % item["file"]
            else:
                item["file"] = 'PlayMedia("%s")' % item["file"]
            results.append(self.kodidb.create_listitem(item, False))
        # finished lookup - display listing with results
        xbmc.executebuiltin("dialog.Close(busydialog)")
        dialog = DialogSelect("DialogSelect.xml",
                              "",
                              listing=results,
                              windowtitle=window_header,
                              richlayout=True)
        dialog.doModal()
        result = dialog.result
        del dialog
        if result:
            while xbmc.getCondVisibility("System.HasModalDialog"):
                xbmc.executebuiltin("Action(Back)")
                xbmc.sleep(300)
            xbmc.executebuiltin(result.getfilename())
            del result

    def setfocus(self):
        '''helper to set focus on a list or control'''
        control = self.params.get("control")
        fallback = self.params.get("fallback")
        position = self.params.get("position", "0")
        relativeposition = self.params.get("relativeposition")
        if relativeposition:
            position = int(relativeposition) - 1
        count = 0
        if control:
            while not xbmc.getCondVisibility("Control.HasFocus(%s)" % control):
                if xbmc.getCondVisibility("Window.IsActive(busydialog)"):
                    continue
                elif count == 20 or (xbmc.getCondVisibility(
                        "!Control.IsVisible(%s) | "
                        "!IntegerGreaterThan(Container(%s).NumItems,0)" %
                    (control, control))):
                    if fallback:
                        xbmc.executebuiltin("Control.SetFocus(%s)" % fallback)
                    break
                else:
                    xbmc.executebuiltin("Control.SetFocus(%s,%s)" %
                                        (control, position))
                    xbmc.sleep(50)
                    count += 1
                    break

    def setwidgetcontainer(self):
        '''helper that reports the current selected widget container/control'''
        controls = self.params.get("controls", "").split("-")
        if controls:
            xbmc.sleep(50)
            for i in range(10):
                for control in controls:
                    if xbmc.getCondVisibility(
                            "Control.IsVisible(%s) + IntegerGreaterThan(Container(%s).NumItems,0)"
                            % (control, control)):
                        self.win.setProperty("SkinHelper.WidgetContainer",
                                             control)
                        return
                xbmc.sleep(50)
        self.win.clearProperty("SkinHelper.WidgetContainer")

    def saveskinimage(self):
        '''let the user select an image and save it to addon_data for easy backup'''
        skinstring = self.params.get("skinstring", "")
        allow_multi = self.params.get("multi", "") == "true"
        header = self.params.get("header", "")
        value = SkinSettings().save_skin_image(skinstring, allow_multi, header)
        if value:
            xbmc.executebuiltin(
                "Skin.SetString(%s,%s)" %
                (skinstring.encode("utf-8"), value.encode("utf-8")))

    @staticmethod
    def checkskinsettings():
        '''performs check of all default skin settings and labels'''
        SkinSettings().correct_skin_settings()

    def setskinsetting(self):
        '''allows the user to set a skin setting with a select dialog'''
        setting = self.params.get("setting", "")
        org_id = self.params.get("id", "")
        if "$" in org_id:
            org_id = xbmc.getInfoLabel(org_id).decode("utf-8")
        header = self.params.get("header", "")
        SkinSettings().set_skin_setting(setting=setting,
                                        window_header=header,
                                        original_id=org_id)

    def setskinconstant(self):
        '''allows the user to set a skin constant with a select dialog'''
        setting = self.params.get("setting", "")
        value = self.params.get("value", "")
        header = self.params.get("header", "")
        SkinSettings().set_skin_constant(setting, header, value)

    def setskinconstants(self):
        '''allows the skinner to set multiple skin constants'''
        settings = self.params.get("settings", "").split("|")
        values = self.params.get("values", "").split("|")
        SkinSettings().set_skin_constants(settings, values)

    def setskinshortcutsproperty(self):
        '''allows the user to make a setting for skinshortcuts using the special skinsettings dialogs'''
        setting = self.params.get("setting", "")
        prop = self.params.get("property", "")
        header = self.params.get("header", "")
        SkinSettings().set_skinshortcuts_property(setting, header, prop)

    def togglekodisetting(self):
        '''toggle kodi setting'''
        settingname = self.params.get("setting", "")
        cur_value = xbmc.getCondVisibility("system.getbool(%s)" % settingname)
        if cur_value:
            new_value = "false"
        else:
            new_value = "true"
        xbmc.executeJSONRPC(
            '{"jsonrpc":"2.0", "id":1, "method":"Settings.SetSettingValue","params":{"setting":"%s","value":%s}}'
            % (settingname, new_value))

    def setkodisetting(self):
        '''set kodi setting'''
        settingname = self.params.get("setting", "")
        value = self.params.get("value", "")
        is_int = False
        try:
            valueint = int(value)
            is_int = True
            del valueint
        except Exception:
            pass
        if value.lower() == "true":
            value = 'true'
        elif value.lower() == "false":
            value = 'false'
        elif is_int:
            value = '"%s"' % value
        xbmc.executeJSONRPC(
            '{"jsonrpc":"2.0", "id":1, "method":"Settings.SetSettingValue",\
            "params":{"setting":"%s","value":%s}}' % (settingname, value))

    def playtrailer(self):
        '''auto play windowed trailer inside video listing'''
        if not xbmc.getCondVisibility(
                "Player.HasMedia | Container.Scrolling | Container.OnNext | "
                "Container.OnPrevious | !IsEmpty(Window(Home).Property(traileractionbusy))"
        ):
            self.win.setProperty("traileractionbusy", "traileractionbusy")
            widget_container = self.params.get("widgetcontainer", "")
            trailer_mode = self.params.get("mode", "").replace("auto_", "")
            allow_youtube = self.params.get("youtube", "") == "true"
            if not trailer_mode:
                trailer_mode = "windowed"
            if widget_container:
                widget_container_prefix = "Container(%s)." % widget_container
            else:
                widget_container_prefix = ""

            li_title = xbmc.getInfoLabel(
                "%sListItem.Title" % widget_container_prefix).decode('utf-8')
            li_trailer = xbmc.getInfoLabel(
                "%sListItem.Trailer" % widget_container_prefix).decode('utf-8')
            if not li_trailer and allow_youtube:
                youtube_result = self.get_youtube_listing("%s Trailer" %
                                                          li_title)
                if youtube_result:
                    li_trailer = youtube_result[0].get("file")
            # always wait a bit to prevent trailer start playing when we're scrolling the list
            xbmc.Monitor().waitForAbort(3)
            if li_trailer and (li_title == xbmc.getInfoLabel(
                    "%sListItem.Title" %
                    widget_container_prefix).decode('utf-8')):
                if trailer_mode == "fullscreen" and li_trailer:
                    xbmc.executebuiltin('PlayMedia("%s")' % li_trailer)
                else:
                    xbmc.executebuiltin('PlayMedia("%s",1)' % li_trailer)
                self.win.setProperty("TrailerPlaying", trailer_mode)
            self.win.clearProperty("traileractionbusy")

    def colorpicker(self):
        '''legacy'''
        self.deprecated_method("script.skin.helper.colorpicker")

    def backup(self):
        '''legacy'''
        self.deprecated_method("script.skin.helper.skinbackup")

    def restore(self):
        '''legacy'''
        self.deprecated_method("script.skin.helper.skinbackup")

    def reset(self):
        '''legacy'''
        self.deprecated_method("script.skin.helper.skinbackup")

    def colorthemes(self):
        '''legacy'''
        self.deprecated_method("script.skin.helper.skinbackup")

    def createcolortheme(self):
        '''legacy'''
        self.deprecated_method("script.skin.helper.skinbackup")

    def restorecolortheme(self):
        '''legacy'''
        self.deprecated_method("script.skin.helper.skinbackup")

    def conditionalbackgrounds(self):
        '''legacy'''
        self.deprecated_method("script.skin.helper.backgrounds")

    def splashscreen(self):
        '''helper to show a user defined splashscreen in the skin'''
        import time
        splashfile = self.params.get("file", "")
        duration = int(self.params.get("duration", 5))
        if (splashfile.lower().endswith("jpg")
                or splashfile.lower().endswith("gif")
                or splashfile.lower().endswith("png")
                or splashfile.lower().endswith("tiff")):
            # this is an image file
            self.win.setProperty("SkinHelper.SplashScreen", splashfile)
            # for images we just wait for X seconds to close the splash again
            start_time = time.time()
            while (time.time() - start_time) <= duration:
                xbmc.sleep(500)
        else:
            # for video or audio we have to wait for the player to finish...
            xbmc.Player().play(splashfile, windowed=True)
            xbmc.sleep(500)
            while xbmc.getCondVisibility("Player.HasMedia"):
                xbmc.sleep(150)
        # replace startup window with home
        startupwindow = xbmc.getInfoLabel("System.StartupWindow")
        xbmc.executebuiltin("ReplaceWindow(%s)" % startupwindow)
        autostart_playlist = xbmc.getInfoLabel(
            "$ESCINFO[Skin.String(autostart_playlist)]")
        if autostart_playlist:
            xbmc.executebuiltin("PlayMedia(%s)" % autostart_playlist)

    def videosearch(self):
        '''show the special search dialog'''
        xbmc.executebuiltin("ActivateWindow(busydialog)")
        from resources.lib.searchdialog import SearchDialog
        search_dialog = SearchDialog(
            "script-skin_helper_service-CustomSearch.xml",
            self.addon.getAddonInfo('path').decode("utf-8"), "Default",
            "1080i")
        search_dialog.doModal()
        del search_dialog

    def showinfo(self):
        '''shows our special videoinfo dialog'''
        dbid = self.params.get("dbid", "")
        dbtype = self.params.get("dbtype", "")
        from infodialog import show_infodialog
        show_infodialog(dbid, dbtype)

    def deletedir(self):
        '''helper to delete a directory, input can be normal filesystem path or vfs'''
        del_path = self.params.get("path")
        if del_path:
            ret = xbmcgui.Dialog().yesno(
                heading=xbmc.getLocalizedString(122),
                line1=u"%s[CR]%s" % (xbmc.getLocalizedString(125), del_path))
            if ret:
                success = recursive_delete_dir(del_path)
                if success:
                    xbmcgui.Dialog().ok(
                        heading=xbmc.getLocalizedString(19179),
                        line1=self.addon.getLocalizedString(32014))
                else:
                    xbmcgui.Dialog().ok(heading=xbmc.getLocalizedString(16205),
                                        line1=xbmc.getLocalizedString(32015))

    def overlaytexture(self):
        '''legacy: helper to let the user choose a background overlay from a skin defined folder'''
        skinstring = self.params.get("skinstring", "BackgroundOverlayTexture")
        self.params["skinstring"] = skinstring
        self.params["resourceaddon"] = "resource.images.backgroundoverlays"
        self.params["customfolder"] = "special://skin/extras/bgoverlays/"
        self.params["allowmulti"] = "false"
        self.params["header"] = self.addon.getLocalizedString(32002)
        self.selectimage()

    def busytexture(self):
        '''legacy: helper which lets the user select a busy spinner from predefined spinners in the skin'''
        skinstring = self.params.get("skinstring", "SkinHelper.SpinnerTexture")
        self.params["skinstring"] = skinstring
        self.params["resourceaddon"] = "resource.images.busyspinners"
        self.params["customfolder"] = "special://skin/extras/busy_spinners/"
        self.params["allowmulti"] = "true"
        self.params["header"] = self.addon.getLocalizedString(32006)
        self.selectimage()

    def selectimage(self):
        '''helper which lets the user select an image or imagepath from resourceaddons or custom path'''
        skinsettings = SkinSettings()
        skinstring = self.params.get("skinstring", "")
        skinshortcutsprop = self.params.get("skinshortcutsproperty", "")
        current_value = self.params.get("currentvalue", "")
        resource_addon = self.params.get("resourceaddon", "")
        allow_multi = self.params.get("allowmulti", "false") == "true"
        windowheader = self.params.get("header", "")
        skinhelper_backgrounds = self.params.get("skinhelperbackgrounds",
                                                 "false") == "true"
        label, value = skinsettings.select_image(
            skinstring,
            allow_multi=allow_multi,
            windowheader=windowheader,
            resource_addon=resource_addon,
            skinhelper_backgrounds=skinhelper_backgrounds,
            current_value=current_value)
        if label:
            if skinshortcutsprop:
                # write value to skinshortcuts prop
                from skinshortcuts import set_skinshortcuts_property
                set_skinshortcuts_property(skinshortcutsprop, value, label)
            else:
                # write the values to skin strings
                if value.startswith("$INFO"):
                    # we got an dynamic image from window property
                    skinsettings.set_skin_variable(skinstring, value)
                    value = "$VAR[%s]" % skinstring
                skinstring = skinstring.encode("utf-8")
                label = label.encode("utf-8")
                xbmc.executebuiltin("Skin.SetString(%s.label,%s)" %
                                    (skinstring, label))
                xbmc.executebuiltin("Skin.SetString(%s.name,%s)" %
                                    (skinstring, label))
                xbmc.executebuiltin("Skin.SetString(%s,%s)" %
                                    (skinstring, value))
                xbmc.executebuiltin("Skin.SetString(%s.path,%s)" %
                                    (skinstring, value))
        del skinsettings

    def dialogok(self):
        '''helper to show an OK dialog with a message'''
        headertxt = self.params.get("header")
        bodytxt = self.params.get("message")
        if bodytxt.startswith(" "):
            bodytxt = bodytxt[1:]
        if headertxt.startswith(" "):
            headertxt = headertxt[1:]
        dialog = xbmcgui.Dialog()
        dialog.ok(heading=headertxt, line1=bodytxt)
        del dialog

    def dialogyesno(self):
        '''helper to show a YES/NO dialog with a message'''
        headertxt = self.params.get("header")
        bodytxt = self.params.get("message")
        yesactions = self.params.get("yesaction", "").split("|")
        noactions = self.params.get("noaction", "").split("|")
        if bodytxt.startswith(" "):
            bodytxt = bodytxt[1:]
        if headertxt.startswith(" "):
            headertxt = headertxt[1:]
        if xbmcgui.Dialog().yesno(heading=headertxt, line1=bodytxt):
            for action in yesactions:
                xbmc.executebuiltin(action.encode("utf-8"))
        else:
            for action in noactions:
                xbmc.executebuiltin(action.encode("utf-8"))

    def textviewer(self):
        '''helper to show a textviewer dialog with a message'''
        headertxt = self.params.get("header", "")
        bodytxt = self.params.get("message", "")
        if bodytxt.startswith(" "):
            bodytxt = bodytxt[1:]
        if headertxt.startswith(" "):
            headertxt = headertxt[1:]
        xbmcgui.Dialog().textviewer(headertxt, bodytxt)

    def fileexists(self):
        '''helper to let the skinner check if a file exists
        and write the outcome to a window prop or skinstring'''
        filename = self.params.get("file")
        skinstring = self.params.get("skinstring")
        windowprop = self.params.get("winprop")
        if xbmcvfs.exists(filename):
            if windowprop:
                self.win.setProperty(windowprop, "exists")
            if skinstring:
                xbmc.executebuiltin("Skin.SetString(%s,exists)" % skinstring)
        else:
            if windowprop:
                self.win.clearProperty(windowprop)
            if skinstring:
                xbmc.executebuiltin("Skin.Reset(%s)" % skinstring)

    def stripstring(self):
        '''helper to allow the skinner to strip a string and write results to a skin string'''
        splitchar = self.params.get("splitchar")
        if splitchar.upper() == "[SPACE]":
            splitchar = " "
        skinstring = self.params.get("string")
        if not skinstring:
            skinstring = self.params.get("skinstring")
        output = self.params.get("output")
        index = self.params.get("index", 0)
        skinstring = skinstring.split(splitchar)[int(index)]
        self.win.setProperty(output, skinstring)

    def getfilename(self, filename=""):
        '''helper to display a sanitized filename in the vidoeinfo dialog'''
        output = self.params.get("output")
        if not filename:
            filename = xbmc.getInfoLabel("ListItem.FileNameAndPath")
        if not filename:
            filename = xbmc.getInfoLabel("ListItem.FileName")
        if "filename=" in filename:
            url_params = dict(urlparse.parse_qsl(filename))
            filename = url_params.get("filename")
        self.win.setProperty(output, filename)

    def getplayerfilename(self):
        '''helper to parse the filename from a plugin (e.g. emby) filename'''
        filename = xbmc.getInfoLabel("Player.FileNameAndPath")
        if not filename:
            filename = xbmc.getInfoLabel("Player.FileName")
        self.getfilename(filename)

    def getpercentage(self):
        '''helper to calculate the percentage of 2 numbers and write results to a skinstring'''
        total = int(params.get("total"))
        count = int(params.get("count"))
        roundsteps = self.params.get("roundsteps")
        skinstring = self.params.get("skinstring")
        percentage = int(round((1.0 * count / total) * 100))
        if roundsteps:
            roundsteps = int(roundsteps)
            percentage = percentage + (roundsteps - percentage) % roundsteps
        xbmc.executebuiltin("Skin.SetString(%s,%s)" % (skinstring, percentage))

    def setresourceaddon(self):
        '''helper to let the user choose a resource addon and set that as skin string'''
        from resourceaddons import setresourceaddon
        addontype = self.params.get("addontype", "")
        skinstring = self.params.get("skinstring", "")
        setresourceaddon(addontype, skinstring)

    def checkresourceaddons(self):
        '''allow the skinner to perform a basic check if some required resource addons are available'''
        from resourceaddons import checkresourceaddons
        addonslist = self.params.get("addonslist", [])
        if addonslist:
            addonslist = addonslist.split("|")
        checkresourceaddons(addonslist)
class PluginContent:
    """Hidden plugin entry point providing some helper features"""

    params = {}
    win = None

    def __init__(self):
        self.cache = SimpleCache()
        self.kodi_db = KodiDb()
        self.win = xbmcgui.Window(10000)
        try:
            self.params = dict(urlparse.parse_qsl(sys.argv[2].replace("?", "").lower().decode("utf-8")))
            log_msg("plugin called with parameters: %s" % self.params)
            self.main()
        except Exception as exc:
            log_exception(__name__, exc)
            xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))

        # cleanup when done processing
        self.close()

    def close(self):
        """Cleanup Kodi Cpython instances"""
        self.cache.close()
        del self.win

    def main(self):
        """main action, load correct function"""
        action = self.params.get("action", "")
        if self.win.getProperty("SkinHelperShutdownRequested"):
            # do not proceed if kodi wants to exit
            log_msg("%s --> Not forfilling request: Kodi is exiting" % __name__, xbmc.LOGWARNING)
            xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))
        else:
            try:
                if hasattr(self.__class__, action):
                    # launch module for action provided by this plugin
                    getattr(self, action)()
                else:
                    # legacy (widget) path called !!!
                    self.load_widget()
            except Exception as exc:
                log_exception(__name__, exc)

    def load_widget(self):
        """legacy entrypoint called (widgets are moved to seperate addon), start redirect..."""
        action = self.params.get("action", "")
        newaddon = "script.skin.helper.widgets"
        log_msg(
            "Deprecated method: %s. Please reassign your widgets to get rid of this message. -"
            "This automatic redirect will be removed in the future" % (action),
            xbmc.LOGWARNING,
        )
        paramstring = ""
        for key, value in self.params.iteritems():
            paramstring += ",%s=%s" % (key, value)
        if xbmc.getCondVisibility("System.HasAddon(%s)" % newaddon):
            # TEMP !!! for backwards compatability reasons only - to be removed in the near future!!
            import imp

            addon = xbmcaddon.Addon(newaddon)
            addon_path = addon.getAddonInfo("path").decode("utf-8")
            imp.load_source("plugin", os.path.join(addon_path, "plugin.py"))
            from plugin import main

            main.Main()
            del addon
        else:
            # trigger install of the addon
            if KODI_VERSION >= 17:
                xbmc.executebuiltin("InstallAddon(%s)" % newaddon)
            else:
                xbmc.executebuiltin("RunPlugin(plugin://%s)" % newaddon)

    def playchannel(self):
        """play channel from widget helper"""
        params = {"item": {"channelid": int(self.params["channelid"])}}
        self.kodi_db.set_json("Player.Open", params)

    def playrecording(self):
        """retrieve the recording and play to get resume working"""
        recording = self.kodi_db.recording(self.params["recordingid"])
        params = {"item": {"recordingid": recording["recordingid"]}}
        self.kodi_db.set_json("Player.Open", params)
        # manually seek because passing resume to the player json cmd doesn't seem to work
        if recording["resume"].get("position"):
            for i in range(50):
                if xbmc.getCondVisibility("Player.HasVideo"):
                    break
                xbmc.sleep(50)
            xbmc.Player().seekTime(recording["resume"].get("position"))

    def launch(self):
        """launch any builtin action using a plugin listitem"""
        if "runscript" in self.params["path"]:
            self.params["path"] = self.params["path"].replace("?", ",")
        xbmc.executebuiltin(self.params["path"])

    def playalbum(self):
        """helper to play an entire album"""
        xbmc.executeJSONRPC(
            '{ "jsonrpc": "2.0", "method": "Player.Open", "params": { "item": { "albumid": %d } }, "id": 1 }'
            % int(self.params["albumid"])
        )

    def smartshortcuts(self):
        """called from skinshortcuts to retrieve listing of all smart shortcuts"""
        import skinshortcuts

        skinshortcuts.get_smartshortcuts(self.params.get("path", ""))

    @staticmethod
    def backgrounds():
        """called from skinshortcuts to retrieve listing of all backgrounds"""
        import skinshortcuts

        skinshortcuts.get_backgrounds()

    def widgets(self):
        """called from skinshortcuts to retrieve listing of all widgetss"""
        import skinshortcuts

        skinshortcuts.get_widgets(self.params.get("path", ""), self.params.get("sublevel", ""))

    def resourceimages(self):
        """retrieve listing of specific resource addon images"""
        from resourceaddons import get_resourceimages

        addontype = self.params.get("addontype", "")
        for item in get_resourceimages(addontype, True):
            listitem = xbmcgui.ListItem(item[0], label2=item[2], path=item[1], iconImage=item[3])
            xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=item[1], listitem=listitem, isFolder=False)
        xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))

    def extrafanart(self):
        """helper to display extrafanart in multiimage control in the skin"""
        fanarts = eval(self.params["fanarts"])
        # process extrafanarts
        for item in fanarts:
            listitem = xbmcgui.ListItem(item, path=item)
            listitem.setProperty("mimetype", "image/jpeg")
            xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=item, listitem=listitem)
        xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))

    def genrebackground(self):
        """helper to display images for a specific genre in multiimage control in the skin"""
        genre = self.params.get("genre").split(".")[0]
        arttype = self.params.get("arttype", "fanart")
        randomize = self.params.get("random", "false") == "true"
        mediatype = self.params.get("mediatype", "movies")
        if genre and genre != "..":
            filters = [{"operator": "is", "field": "genre", "value": genre}]
            if randomize:
                sort = {"method": "random", "order": "descending"}
            else:
                sort = None
            items = getattr(self.kodi_db, mediatype)(sort=sort, filters=filters, limits=(0, 50))
            for item in items:
                image = get_clean_image(item["art"].get(arttype, ""))
                if image:
                    image = get_clean_image(item["art"][arttype])
                    listitem = xbmcgui.ListItem(image, path=image)
                    listitem.setProperty("mimetype", "image/jpeg")
                    xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=image, listitem=listitem)
        xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))

    def getcastmedia(self):
        """helper to display get all media for a specific actor"""
        name = self.params.get("name")
        if name:
            all_items = self.kodi_db.castmedia(name)
            all_items = process_method_on_list(self.kodi_db.prepare_listitem, all_items)
            all_items = process_method_on_list(self.kodi_db.create_listitem, all_items)
            xbmcplugin.addDirectoryItems(int(sys.argv[1]), all_items, len(all_items))
        xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))

    def getcast(self):
        """helper to get all cast for a given media item"""
        db_id = None
        all_cast = []
        all_cast_names = list()
        cache_str = ""
        download_thumbs = self.params.get("downloadthumbs", "") == "true"
        extended_cast_action = self.params.get("castaction", "") == "extendedinfo"
        tmdb = Tmdb()
        movie = self.params.get("movie")
        tvshow = self.params.get("tvshow")
        episode = self.params.get("episode")
        movieset = self.params.get("movieset")

        try:  # try to parse db_id
            if movieset:
                cache_str = "movieset.castcache-%s-%s" % (self.params["movieset"], download_thumbs)
                db_id = int(movieset)
            elif tvshow:
                cache_str = "tvshow.castcache-%s-%s" % (self.params["tvshow"], download_thumbs)
                db_id = int(tvshow)
            elif movie:
                cache_str = "movie.castcache-%s-%s" % (self.params["movie"], download_thumbs)
                db_id = int(movie)
            elif episode:
                cache_str = "episode.castcache-%s-%s" % (self.params["episode"], download_thumbs)
                db_id = int(episode)
        except Exception:
            pass

        cachedata = self.cache.get(cache_str)
        if cachedata:
            # get data from cache
            all_cast = cachedata
        else:
            # retrieve data from json api...
            if movie and db_id:
                all_cast = self.kodi_db.movie(db_id)["cast"]
            elif movie and not db_id:
                filters = [{"operator": "is", "field": "title", "value": movie}]
                result = self.kodi_db.movies(filters=filters)
                all_cast = result[0]["cast"] if result else []
            elif tvshow and db_id:
                all_cast = self.kodi_db.tvshow(db_id)["cast"]
            elif tvshow and not db_id:
                filters = [{"operator": "is", "field": "title", "value": tvshow}]
                result = self.kodi_db.tvshows(filters=filters)
                all_cast = result[0]["cast"] if result else []
            elif episode and db_id:
                all_cast = self.kodi_db.episode(db_id)["cast"]
            elif episode and not db_id:
                filters = [{"operator": "is", "field": "title", "value": episode}]
                result = self.kodi_db.episodes(filters=filters)
                all_cast = result[0]["cast"] if result else []
            elif movieset:
                if not db_id:
                    for item in self.kodi_db.moviesets():
                        if item["title"].lower() == movieset.lower():
                            db_id = item["setid"]
                if db_id:
                    json_result = self.kodi_db.movieset(db_id, include_set_movies_fields=["cast"])
                    if "movies" in json_result:
                        for movie in json_result["movies"]:
                            all_cast += movie["cast"]

            # optional: download missing actor thumbs
            if all_cast and download_thumbs:
                for cast in all_cast:
                    if cast.get("thumbnail"):
                        cast["thumbnail"] = get_clean_image(cast.get("thumbnail"))
                    if not cast.get("thumbnail"):
                        artwork = tmdb.get_actor(cast["name"])
                        cast["thumbnail"] = artwork.get("thumb", "")
            # lookup tmdb if item is requested that is not in local db
            if not all_cast:
                tmdbdetails = {}
                if movie and not db_id:
                    tmdbdetails = tmdb.search_movie(movie)
                elif tvshow and not db_id:
                    tmdbdetails = tmdb.search_tvshow(tvshow)
                if tmdbdetails.get("cast"):
                    all_cast = tmdbdetails["cast"]
            # save to cache
            self.cache.set(cache_str, all_cast)

        # process listing with the results...
        for cast in all_cast:
            if cast.get("name") not in all_cast_names:
                liz = xbmcgui.ListItem(label=cast.get("name"), label2=cast.get("role"), iconImage=cast.get("thumbnail"))
                if extended_cast_action:
                    url = "RunScript(script.extendedinfo,info=extendedactorinfo,name=%s)" % cast.get("name")
                    url = "plugin://script.skin.helper.service/?action=launch&path=%s" % url
                    is_folder = False
                else:
                    url = "RunScript(script.skin.helper.service,action=getcastmedia,name=%s)" % cast.get("name")
                    url = "plugin://script.skin.helper.service/?action=launch&path=%s" % urlencode(url)
                    is_folder = False
                all_cast_names.append(cast.get("name"))
                liz.setThumbnailImage(cast.get("thumbnail"))
                xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=url, listitem=liz, isFolder=is_folder)
        xbmcplugin.endOfDirectory(int(sys.argv[1]))

    @staticmethod
    def alphabet():
        """display an alphabet scrollbar in listings"""
        all_letters = []
        if xbmc.getInfoLabel("Container.NumItems"):
            for i in range(int(xbmc.getInfoLabel("Container.NumItems"))):
                all_letters.append(xbmc.getInfoLabel("Listitem(%s).SortLetter" % i).upper())
            start_number = ""
            for number in ["2", "3", "4", "5", "6", "7", "8", "9"]:
                if number in all_letters:
                    start_number = number
                    break
            for letter in [
                start_number,
                "A",
                "B",
                "C",
                "D",
                "E",
                "F",
                "G",
                "H",
                "I",
                "J",
                "K",
                "L",
                "M",
                "N",
                "O",
                "P",
                "Q",
                "R",
                "S",
                "T",
                "U",
                "V",
                "W",
                "X",
                "Y",
                "Z",
            ]:
                if letter == start_number:
                    label = "#"
                else:
                    label = letter
                listitem = xbmcgui.ListItem(label=label)
                if letter not in all_letters:
                    lipath = "noop"
                    listitem.setProperty("NotAvailable", "true")
                else:
                    lipath = "plugin://script.skin.helper.service/?action=alphabetletter&letter=%s" % letter
                xbmcplugin.addDirectoryItem(int(sys.argv[1]), lipath, listitem, isFolder=False)
        xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))

    def alphabetletter(self):
        """used with the alphabet scrollbar to jump to a letter"""
        if KODI_VERSION > 16:
            xbmcplugin.setResolvedUrl(handle=int(sys.argv[1]), succeeded=False, listitem=xbmcgui.ListItem())
        letter = self.params.get("letter", "").upper()
        jumpcmd = ""
        if letter in ["A", "B", "C", "2"]:
            jumpcmd = "2"
        elif letter in ["D", "E", "F", "3"]:
            jumpcmd = "3"
        elif letter in ["G", "H", "I", "4"]:
            jumpcmd = "4"
        elif letter in ["J", "K", "L", "5"]:
            jumpcmd = "5"
        elif letter in ["M", "N", "O", "6"]:
            jumpcmd = "6"
        elif letter in ["P", "Q", "R", "S", "7"]:
            jumpcmd = "7"
        elif letter in ["T", "U", "V", "8"]:
            jumpcmd = "8"
        elif letter in ["W", "X", "Y", "Z", "9"]:
            jumpcmd = "9"
        if jumpcmd:
            xbmc.executebuiltin("SetFocus(50)")
            for i in range(40):
                xbmc.executeJSONRPC(
                    '{ "jsonrpc": "2.0", "method": "Input.ExecuteAction",\
                    "params": { "action": "jumpsms%s" }, "id": 1 }'
                    % (jumpcmd)
                )
                xbmc.sleep(50)
                if xbmc.getInfoLabel("ListItem.Sortletter").upper() == letter:
                    break
 def get_youtube_listing(searchquery):
     '''get items from youtube plugin by query'''
     lib_path = u"plugin://plugin.video.youtube/kodion/search/query/?q=%s" % searchquery
     return KodiDb().files(lib_path)
class MainModule:
    '''mainmodule provides the script methods for the skinhelper addon'''

    def __init__(self):
        '''Initialization and main code run'''
        self.win = xbmcgui.Window(10000)
        self.addon = xbmcaddon.Addon(ADDON_ID)
        self.kodidb = KodiDb()
        self.cache = SimpleCache()

        self.params = self.get_params()
        log_msg("MainModule called with parameters: %s" % self.params)
        action = self.params.get("action", "")
        # launch module for action provided by this script
        try:
            getattr(self, action)()
        except AttributeError:
            log_exception(__name__, "No such action: %s" % action)
        except Exception as exc:
            log_exception(__name__, exc)
        finally:
            xbmc.executebuiltin("dialog.Close(busydialog)")

        # do cleanup
        self.close()

    def close(self):
        '''Cleanup Kodi Cpython instances on exit'''
        self.cache.close()
        del self.win
        del self.addon
        del self.kodidb
        log_msg("MainModule exited")

    @classmethod
    def get_params(self):
        '''extract the params from the called script path'''
        params = {}
        for arg in sys.argv[1:]:
            paramname = arg.split('=')[0]
            paramvalue = arg.replace(paramname + "=", "")
            paramname = paramname.lower()
            if paramname == "action":
                paramvalue = paramvalue.lower()
            params[paramname] = paramvalue
        return params

    def deprecated_method(self, newaddon):
        '''
            used when one of the deprecated methods is called
            print warning in log and call the external script with the same parameters
        '''
        action = self.params.get("action")
        log_msg("Deprecated method: %s. Please call %s directly" % (action, newaddon), xbmc.LOGWARNING)
        paramstring = ""
        for key, value in self.params.iteritems():
            paramstring += ",%s=%s" % (key, value)
        if xbmc.getCondVisibility("System.HasAddon(%s)" % newaddon):
            xbmc.executebuiltin("RunAddon(%s%s)" % (newaddon, paramstring))
        else:
            # trigger install of the addon
            if KODI_VERSION >= 17:
                xbmc.executebuiltin("InstallAddon(%s)" % newaddon)
            else:
                xbmc.executebuiltin("RunPlugin(plugin://%s)" % newaddon)

    @staticmethod
    def musicsearch():
        '''helper to go directly to music search dialog'''
        xbmc.executebuiltin("ActivateWindow(Music)")
        xbmc.executebuiltin("SendClick(8)")

    def setview(self):
        '''sets the selected viewmode for the container'''
        xbmc.executebuiltin("ActivateWindow(busydialog)")
        content_type = get_current_content_type()
        if not content_type:
            content_type = "files"
        current_view = xbmc.getInfoLabel("Container.Viewmode").decode("utf-8")
        view_id, view_label = self.selectview(content_type, current_view)
        current_forced_view = xbmc.getInfoLabel("Skin.String(SkinHelper.ForcedViews.%s)" % content_type)

        if view_id is not None:
            # also store forced view
            if (content_type and current_forced_view and current_forced_view != "None" and
                    xbmc.getCondVisibility("Skin.HasSetting(SkinHelper.ForcedViews.Enabled)")):
                xbmc.executebuiltin("Skin.SetString(SkinHelper.ForcedViews.%s,%s)" % (content_type, view_id))
                xbmc.executebuiltin("Skin.SetString(SkinHelper.ForcedViews.%s.label,%s)" % (content_type, view_label))
                self.win.setProperty("SkinHelper.ForcedView", view_id)
                if not xbmc.getCondVisibility("Control.HasFocus(%s)" % current_forced_view):
                    xbmc.sleep(100)
                    xbmc.executebuiltin("Container.SetViewMode(%s)" % view_id)
                    xbmc.executebuiltin("SetFocus(%s)" % view_id)
            else:
                self.win.clearProperty("SkinHelper.ForcedView")
            # set view
            xbmc.executebuiltin("Container.SetViewMode(%s)" % view_id)

    def selectview(self, content_type="other", current_view=None, display_none=False):
        '''reads skinfile with all views to present a dialog to choose from'''
        cur_view_select_id = None
        label = ""
        all_views = []
        if display_none:
            listitem = xbmcgui.ListItem(label="None")
            listitem.setProperty("id", "None")
            all_views.append(listitem)
        # read the special skin views file
        views_file = xbmc.translatePath('special://skin/extras/views.xml').decode("utf-8")
        if xbmcvfs.exists(views_file):
            doc = parse(views_file)
            listing = doc.documentElement.getElementsByTagName('view')
            itemcount = 0
            for view in listing:
                label = xbmc.getLocalizedString(int(view.attributes['languageid'].nodeValue))
                viewid = view.attributes['value'].nodeValue
                mediatypes = view.attributes['type'].nodeValue.lower().split(",")
                if label.lower() == current_view.lower() or viewid == current_view:
                    cur_view_select_id = itemcount
                    if display_none:
                        cur_view_select_id += 1
                if (("all" in mediatypes or content_type.lower() in mediatypes) and
                    (not "!" + content_type.lower() in mediatypes) and not
                        xbmc.getCondVisibility("Skin.HasSetting(SkinHelper.view.Disabled.%s)" % viewid)):
                    image = "special://skin/extras/viewthumbs/%s.jpg" % viewid
                    listitem = xbmcgui.ListItem(label=label, iconImage=image)
                    listitem.setProperty("viewid", viewid)
                    listitem.setProperty("icon", image)
                    all_views.append(listitem)
                    itemcount += 1
        dialog = DialogSelect("DialogSelect.xml", "", listing=all_views,
                              windowtitle=self.addon.getLocalizedString(32012), richlayout=True)
        dialog.autofocus_id = cur_view_select_id
        dialog.doModal()
        result = dialog.result
        del dialog
        if result:
            viewid = result.getProperty("viewid")
            label = result.getLabel().decode("utf-8")
            return (viewid, label)
        else:
            return (None, None)

    def enableviews(self):
        '''show select dialog to enable/disable views'''
        all_views = []
        views_file = xbmc.translatePath('special://skin/extras/views.xml').decode("utf-8")
        if xbmcvfs.exists(views_file):
            doc = parse(views_file)
            listing = doc.documentElement.getElementsByTagName('view')
            for view in listing:
                view_id = view.attributes['value'].nodeValue
                label = xbmc.getLocalizedString(int(view.attributes['languageid'].nodeValue))
                desc = label + " (" + str(view_id) + ")"
                listitem = xbmcgui.ListItem(label=label, label2=desc)
                listitem.setProperty("viewid", view_id)
                if not xbmc.getCondVisibility("Skin.HasSetting(SkinHelper.view.Disabled.%s)" % view_id):
                    listitem.select(selected=True)
                all_views.append(listitem)

        dialog = DialogSelect(
            "DialogSelect.xml",
            "",
            listing=all_views,
            windowtitle=self.addon.getLocalizedString(32013),
            multiselect=True)
        dialog.doModal()
        result = dialog.result
        del dialog
        if result:
            for item in result:
                view_id = item.getProperty("viewid")
                if item.isSelected():
                    # view is enabled
                    xbmc.executebuiltin("Skin.Reset(SkinHelper.view.Disabled.%s)" % view_id)
                else:
                    # view is disabled
                    xbmc.executebuiltin("Skin.SetBool(SkinHelper.view.Disabled.%s)" % view_id)

    def setforcedview(self):
        '''helper that sets a forced view for a specific content type'''
        content_type = self.params.get("contenttype")
        if content_type:
            current_view = xbmc.getInfoLabel("Skin.String(SkinHelper.ForcedViews.%s)" % content_type)
            if not current_view:
                current_view = "0"
            view_id, view_label = self.selectview(content_type, current_view, True)
            if view_id:
                xbmc.executebuiltin("Skin.SetString(SkinHelper.ForcedViews.%s,%s)" % (content_type, view_id))
                xbmc.executebuiltin("Skin.SetString(SkinHelper.ForcedViews.%s.label,%s)" % (content_type, view_label))

    @staticmethod
    def get_youtube_listing(searchquery):
        '''get items from youtube plugin by query'''
        lib_path = u"plugin://plugin.video.youtube/kodion/search/query/?q=%s" % searchquery
        return KodiDb().files(lib_path)

    def searchyoutube(self):
        '''helper to search youtube for the given title'''
        xbmc.executebuiltin("ActivateWindow(busydialog)")
        title = self.params.get("title", "")
        window_header = self.params.get("header", "")
        results = []
        for media in self.get_youtube_listing(title):
            if not media["filetype"] == "directory":
                label = media["label"]
                label2 = media["plot"]
                image = ""
                if media.get('art'):
                    if media['art'].get('thumb'):
                        image = (media['art']['thumb'])
                listitem = xbmcgui.ListItem(label=label, label2=label2, iconImage=image)
                listitem.setProperty("path", media["file"])
                results.append(listitem)

        # finished lookup - display listing with results
        xbmc.executebuiltin("dialog.Close(busydialog)")
        dialog = DialogSelect("DialogSelect.xml", "", listing=results, windowtitle=window_header, multiselect=False)
        dialog.doModal()
        result = dialog.result
        del dialog
        if result:
            if xbmc.getCondVisibility(
                    "Window.IsActive(script-skin_helper_service-CustomInfo.xml) | "
                    "Window.IsActive(movieinformation)"):
                xbmc.executebuiltin("Dialog.Close(movieinformation)")
                xbmc.executebuiltin("Dialog.Close(script-skin_helper_service-CustomInfo.xml)")
                xbmc.sleep(1000)
            xbmc.executebuiltin('PlayMedia("%s")' % result.getProperty("path"))
            del result

    def getcastmedia(self):
        '''helper to show a dialog with all media for a specific actor'''
        xbmc.executebuiltin("ActivateWindow(busydialog)")
        name = self.params.get("name", "")
        window_header = self.params.get("name", "")
        results = []
        items = self.kodidb.castmedia(name)
        items = process_method_on_list(self.kodidb.prepare_listitem, items)
        for item in items:
            if item["file"].startswith("videodb://"):
                item["file"] = "ActivateWindow(Videos,%s,return)" % item["file"]
            else:
                item["file"] = 'PlayMedia("%s")' % item["file"]
            results.append(self.kodidb.create_listitem(item, False))
        # finished lookup - display listing with results
        xbmc.executebuiltin("dialog.Close(busydialog)")
        dialog = DialogSelect("DialogSelect.xml", "", listing=results, windowtitle=window_header, richlayout=True)
        dialog.doModal()
        result = dialog.result
        del dialog
        if result:
            while xbmc.getCondVisibility("System.HasModalDialog"):
                xbmc.executebuiltin("Action(Back)")
                xbmc.sleep(300)
            xbmc.executebuiltin(result.getfilename())
            del result

    def setfocus(self):
        '''helper to set focus on a list or control'''
        control = self.params.get("control")
        fallback = self.params.get("fallback")
        count = 0
        if control:
            while not xbmc.getCondVisibility("Control.HasFocus(%s)" % control):
                if xbmc.getCondVisibility("Window.IsActive(busydialog)"):
                    continue
                elif count == 20 or (xbmc.getCondVisibility(
                        "!Control.IsVisible(%s) | "
                        "!IntegerGreaterThan(Container(%s).NumItems,0)" % (control, control))):
                    if fallback:
                        xbmc.executebuiltin("Control.SetFocus(%s)" % fallback)
                    break
                else:
                    xbmc.executebuiltin("Control.SetFocus(%s)" % control)
                    xbmc.sleep(50)
                    count += 1
                    break

    def setwidgetcontainer(self):
        '''helper that reports the current selected widget container/control'''
        controls = self.params.get("controls", "").split("-")
        if controls:
            xbmc.sleep(50)
            for i in range(10):
                for control in controls:
                    if xbmc.getCondVisibility("Control.IsVisible(%s) + IntegerGreaterThan(Container(%s).NumItems,0)"
                                              % (control, control)):
                        self.win.setProperty("SkinHelper.WidgetContainer", control)
                        return
                xbmc.sleep(50)
        self.win.clearProperty("SkinHelper.WidgetContainer")

    def saveskinimage(self):
        '''let the user select an image and save it to addon_data for easy backup'''
        skinstring = self.params.get("skinstring", "")
        allow_multi = self.params.get("multi", "") == "true"
        header = self.params.get("header", "")
        value = SkinSettings().save_skin_image(skinstring, allow_multi, header)
        if value:
            xbmc.executebuiltin("Skin.SetString(%s,%s)" %(skinstring.encode("utf-8"), value.encode("utf-8")))

    @staticmethod
    def checkskinsettings():
        '''performs check of all default skin settings and labels'''
        SkinSettings().correct_skin_settings()

    def setskinsetting(self):
        '''allows the user to set a skin setting with a select dialog'''
        setting = self.params.get("setting", "")
        org_id = self.params.get("id", "")
        if "$" in org_id:
            org_id = xbmc.getInfoLabel(org_id).decode("utf-8")
        header = self.params.get("header", "")
        SkinSettings().set_skin_setting(setting=setting, window_header=header, original_id=org_id)

    def setskinconstant(self):
        '''allows the user to set a skin constant with a select dialog'''
        setting = self.params.get("setting", "").split("|")
        value = self.params.get("value", "").split("|")
        header = self.params.get("header", "")
        SkinSettings().set_skin_constant(setting, header, value)

    def setskinconstants(self):
        '''allows the skinner to set multiple skin constants'''
        settings = self.params.get("settings", "")
        values = self.params.get("values", "")
        SkinSettings().set_skin_constants(settings, values)

    def setskinshortcutsproperty(self):
        '''allows the user to make a setting for skinshortcuts using the special skinsettings dialogs'''
        setting = self.params.get("setting", "")
        prop = self.params.get("property", "")
        header = self.params.get("header", "")
        SkinSettings().set_skinshortcuts_property(setting, header, prop)

    def togglekodisetting(self):
        '''toggle kodi setting'''
        settingname = self.params.get("setting", "")
        cur_value = xbmc.getCondVisibility("system.getbool(%s)" % settingname)
        if cur_value:
            new_value = "false"
        else:
            new_value = "true"
        xbmc.executeJSONRPC(
            '{"jsonrpc":"2.0", "id":1, "method":"Settings.SetSettingValue","params":{"setting":"%s","value":%s}}' %
            (settingname, new_value))

    def setkodisetting(self):
        '''set kodi setting'''
        settingname = self.params.get("setting", "")
        value = self.params.get("value", "")
        is_int = False
        try:
            valueint = int(value)
            is_int = True
            del valueint
        except Exception:
            pass
        if value.lower() == "true":
            value = 'true'
        elif value.lower() == "false":
            value = 'false'
        elif is_int:
            value = '"%s"' % value
        xbmc.executeJSONRPC('{"jsonrpc":"2.0", "id":1, "method":"Settings.SetSettingValue",\
            "params":{"setting":"%s","value":%s}}' % (settingname, value))

    def playtrailer(self):
        '''auto play windowed trailer inside video listing'''
        if not xbmc.getCondVisibility("Player.HasMedia | Container.Scrolling | Container.OnNext | "
                                      "Container.OnPrevious | !IsEmpty(Window(Home).Property(traileractionbusy))"):
            self.win.setProperty("traileractionbusy", "traileractionbusy")
            widget_container = self.params.get("widgetcontainer", "")
            trailer_mode = self.params.get("mode", "").replace("auto_", "")
            allow_youtube = self.params.get("youtube", "") == "true"
            if not trailer_mode:
                trailer_mode = "windowed"
            if widget_container:
                widget_container_prefix = "Container(%s)." % widget_container
            else:
                widget_container_prefix = ""

            li_title = xbmc.getInfoLabel("%sListItem.Title" % widget_container_prefix).decode('utf-8')
            li_trailer = xbmc.getInfoLabel("%sListItem.Trailer" % widget_container_prefix).decode('utf-8')
            if not li_trailer and allow_youtube:
                youtube_result = self.get_youtube_listing("%s Trailer" % li_title)
                if youtube_result:
                    li_trailer = youtube_result[0].get("file")
            # always wait a bit to prevent trailer start playing when we're scrolling the list
            xbmc.Monitor().waitForAbort(3)
            if li_trailer and (li_title == xbmc.getInfoLabel("%sListItem.Title"
                                                             % widget_container_prefix).decode('utf-8')):
                if trailer_mode == "fullscreen" and li_trailer:
                    xbmc.executebuiltin('PlayMedia("%s")' % li_trailer)
                else:
                    xbmc.executebuiltin('PlayMedia("%s",1)' % li_trailer)
                self.win.setProperty("TrailerPlaying", trailer_mode)
            self.win.clearProperty("traileractionbusy")

    def colorpicker(self):
        '''legacy'''
        self.deprecated_method("script.skin.helper.colorpicker")

    def backup(self):
        '''legacy'''
        self.deprecated_method("script.skin.helper.skinbackup")

    def restore(self):
        '''legacy'''
        self.deprecated_method("script.skin.helper.skinbackup")

    def reset(self):
        '''legacy'''
        self.deprecated_method("script.skin.helper.skinbackup")

    def colorthemes(self):
        '''legacy'''
        self.deprecated_method("script.skin.helper.skinbackup")

    def createcolortheme(self):
        '''legacy'''
        self.deprecated_method("script.skin.helper.skinbackup")

    def restorecolortheme(self):
        '''legacy'''
        self.deprecated_method("script.skin.helper.skinbackup")

    def conditionalbackgrounds(self):
        '''legacy'''
        self.deprecated_method("script.skin.helper.backgrounds")

    def splashscreen(self):
        '''helper to show a user defined splashscreen in the skin'''
        import time
        splashfile = self.params.get("file", "")
        duration = int(self.params.get("duration", 5))
        if (splashfile.lower().endswith("jpg") or splashfile.lower().endswith("gif") or
                splashfile.lower().endswith("png") or splashfile.lower().endswith("tiff")):
            # this is an image file
            self.win.setProperty("SkinHelper.SplashScreen", splashfile)
            # for images we just wait for X seconds to close the splash again
            start_time = time.time()
            while (time.time() - start_time) <= duration:
                xbmc.sleep(500)
        else:
            # for video or audio we have to wait for the player to finish...
            xbmc.Player().play(splashfile, windowed=True)
            xbmc.sleep(500)
            while xbmc.getCondVisibility("Player.HasMedia"):
                xbmc.sleep(150)
        # replace startup window with home
        startupwindow = xbmc.getInfoLabel("System.StartupWindow")
        xbmc.executebuiltin("ReplaceWindow(%s)" % startupwindow)
        autostart_playlist = xbmc.getInfoLabel("$ESCINFO[Skin.String(autostart_playlist)]")
        if autostart_playlist:
            xbmc.executebuiltin("PlayMedia(%s)" % autostart_playlist)

    def videosearch(self):
        '''show the special search dialog'''
        xbmc.executebuiltin("ActivateWindow(busydialog)")
        from resources.lib.searchdialog import SearchDialog
        search_dialog = SearchDialog("script-skin_helper_service-CustomSearch.xml",
                                     self.addon.getAddonInfo('path').decode("utf-8"), "Default", "1080i")
        search_dialog.doModal()
        del search_dialog

    def showinfo(self):
        '''shows our special videoinfo dialog'''
        dbid = self.params.get("dbid", "")
        dbtype = self.params.get("dbtype", "")
        from infodialog import show_infodialog
        show_infodialog(dbid, dbtype)

    def deletedir(self):
        '''helper to delete a directory, input can be normal filesystem path or vfs'''
        del_path = self.params.get("path")
        if del_path:
            ret = xbmcgui.Dialog().yesno(heading=xbmc.getLocalizedString(122),
                                         line1=u"%s[CR]%s" % (xbmc.getLocalizedString(125), del_path))
            if ret:
                success = recursive_delete_dir(del_path)
                if success:
                    xbmcgui.Dialog().ok(heading=xbmc.getLocalizedString(19179),
                                        line1=self.addon.getLocalizedString(32014))
                else:
                    xbmcgui.Dialog().ok(heading=xbmc.getLocalizedString(16205),
                                        line1=xbmc.getLocalizedString(32015))

    def overlaytexture(self):
        '''legacy: helper to let the user choose a background overlay from a skin defined folder'''
        skinstring = self.params.get("skinstring", "BackgroundOverlayTexture")
        self.params["skinstring"] = skinstring
        self.params["resourceaddon"] = "resource.images.backgroundoverlays"
        self.params["customfolder"] = "special://skin/extras/bgoverlays/"
        self.params["allowmulti"] = "false"
        self.params["header"] = self.addon.getLocalizedString(32002)
        self.selectimage()

    def busytexture(self):
        '''legacy: helper which lets the user select a busy spinner from predefined spinners in the skin'''
        skinstring = self.params.get("skinstring", "SkinHelper.SpinnerTexture")
        self.params["skinstring"] = skinstring
        self.params["resourceaddon"] = "resource.images.busyspinners"
        self.params["customfolder"] = "special://skin/extras/busy_spinners/"
        self.params["allowmulti"] = "true"
        self.params["header"] = self.addon.getLocalizedString(32006)
        self.selectimage()

    def selectimage(self):
        '''helper which lets the user select an image or imagepath from resourceaddons or custom path'''
        skinsettings = SkinSettings()
        skinstring = self.params.get("skinstring", "")
        skinshortcutsprop = self.params.get("skinshortcutsproperty", "")
        current_value = self.params.get("currentvalue", "")
        resource_addon = self.params.get("resourceaddon", "")
        allow_multi = self.params.get("allowmulti", "false") == "true"
        windowheader = self.params.get("header", "")
        skinhelper_backgrounds = self.params.get("skinhelperbackgrounds", "false") == "true"
        label, value = skinsettings.select_image(
            skinstring, allow_multi=allow_multi, windowheader=windowheader, resource_addon=resource_addon,
            skinhelper_backgrounds=skinhelper_backgrounds, current_value=current_value)
        if label:
            if skinshortcutsprop:
                # write value to skinshortcuts prop
                from skinshortcuts import set_skinshortcuts_property
                set_skinshortcuts_property(skinshortcutsprop, value, label)
            else:
                # write the values to skin strings
                if value.startswith("$INFO"):
                    # we got an dynamic image from window property
                    skinsettings.set_skin_variable(skinstring, value)
                    value = "$VAR[%s]" % skinstring
                skinstring = skinstring.encode("utf-8")
                label = label.encode("utf-8")
                xbmc.executebuiltin("Skin.SetString(%s.label,%s)" % (skinstring, label))
                xbmc.executebuiltin("Skin.SetString(%s.name,%s)" % (skinstring, label))
                xbmc.executebuiltin("Skin.SetString(%s,%s)" % (skinstring, value))
                xbmc.executebuiltin("Skin.SetString(%s.path,%s)" % (skinstring, value))
        del skinsettings

    def dialogok(self):
        '''helper to show an OK dialog with a message'''
        headertxt = self.params.get("header")
        bodytxt = self.params.get("message")
        if bodytxt.startswith(" "):
            bodytxt = bodytxt[1:]
        if headertxt.startswith(" "):
            headertxt = headertxt[1:]
        dialog = xbmcgui.Dialog()
        dialog.ok(heading=headertxt, line1=bodytxt)
        del dialog

    def dialogyesno(self):
        '''helper to show a YES/NO dialog with a message'''
        headertxt = self.params.get("header")
        bodytxt = self.params.get("message")
        yesactions = self.params.get("yesaction", "").split("|")
        noactions = self.params.get("noaction", "").split("|")
        if bodytxt.startswith(" "):
            bodytxt = bodytxt[1:]
        if headertxt.startswith(" "):
            headertxt = headertxt[1:]
        if xbmcgui.Dialog().yesno(heading=headertxt, line1=bodytxt):
            for action in yesactions:
                xbmc.executebuiltin(action.encode("utf-8"))
        else:
            for action in noactions:
                xbmc.executebuiltin(action.encode("utf-8"))

    def textviewer(self):
        '''helper to show a textviewer dialog with a message'''
        headertxt = self.params.get("header", "")
        bodytxt = self.params.get("message", "")
        if bodytxt.startswith(" "):
            bodytxt = bodytxt[1:]
        if headertxt.startswith(" "):
            headertxt = headertxt[1:]
        xbmcgui.Dialog().textviewer(headertxt, bodytxt)

    def fileexists(self):
        '''helper to let the skinner check if a file exists
        and write the outcome to a window prop or skinstring'''
        filename = self.params.get("file")
        skinstring = self.params.get("skinstring")
        windowprop = self.params.get("winprop")
        if xbmcvfs.exists(filename):
            if windowprop:
                self.win.setProperty(windowprop, "exists")
            if skinstring:
                xbmc.executebuiltin("Skin.SetString(%s,exists)" % skinstring)
        else:
            if windowprop:
                self.win.clearProperty(windowprop)
            if skinstring:
                xbmc.executebuiltin("Skin.Reset(%s)" % skinstring)

    def stripstring(self):
        '''helper to allow the skinner to strip a string and write results to a skin string'''
        splitchar = self.params.get("splitchar")
        if splitchar.upper() == "[SPACE]":
            splitchar = " "
        skinstring = self.params.get("string")
        if not skinstring:
            skinstring = self.params.get("skinstring")
        output = self.params.get("output")
        index = self.params.get("index", 0)
        skinstring = skinstring.split(splitchar)[int(index)]
        self.win.setProperty(output, skinstring)

    def getfilename(self, filename=""):
        '''helper to display a sanitized filename in the vidoeinfo dialog'''
        output = self.params.get("output")
        if not filename:
            filename = xbmc.getInfoLabel("ListItem.FileNameAndPath")
        if not filename:
            filename = xbmc.getInfoLabel("ListItem.FileName")
        if "filename=" in filename:
            url_params = dict(urlparse.parse_qsl(filename))
            filename = url_params.get("filename")
        self.win.setProperty(output, filename)

    def getplayerfilename(self):
        '''helper to parse the filename from a plugin (e.g. emby) filename'''
        filename = xbmc.getInfoLabel("Player.FileNameAndPath")
        if not filename:
            filename = xbmc.getInfoLabel("Player.FileName")
        self.getfilename(filename)

    def getpercentage(self):
        '''helper to calculate the percentage of 2 numbers and write results to a skinstring'''
        total = int(params.get("total"))
        count = int(params.get("count"))
        roundsteps = self.params.get("roundsteps")
        skinstring = self.params.get("skinstring")
        percentage = int(round((1.0 * count / total) * 100))
        if roundsteps:
            roundsteps = int(roundsteps)
            percentage = percentage + (roundsteps - percentage) % roundsteps
        xbmc.executebuiltin("Skin.SetString(%s,%s)" % (skinstring, percentage))

    def setresourceaddon(self):
        '''helper to let the user choose a resource addon and set that as skin string'''
        from resourceaddons import setresourceaddon
        addontype = self.params.get("addontype", "")
        skinstring = self.params.get("skinstring", "")
        setresourceaddon(addontype, skinstring)

    def checkresourceaddons(self):
        '''allow the skinner to perform a basic check if some required resource addons are available'''
        from resourceaddons import checkresourceaddons
        addonslist = self.params.get("addonslist", [])
        if addonslist:
            addonslist = addonslist.split("|")
        checkresourceaddons(addonslist)
 def __init__(self, *args):
     xbmc.log("SearchBackgroundThread Init")
     threading.Thread.__init__(self, *args)
     self.kodidb = KodiDb()
     self.actors = []
     thread.start_new_thread(self.set_actors, ())
Beispiel #17
0
class SearchBackgroundThread(threading.Thread):
    '''Background thread to complement our search dialog,
    fills the listing while UI keeps responsive'''
    active = True
    dialog = None
    search_string = ""

    def __init__(self, *args):
        xbmc.log("SearchBackgroundThread Init")
        threading.Thread.__init__(self, *args)
        self.kodidb = KodiDb()
        self.actors = []
        thread.start_new_thread(self.set_actors, ())

    def set_search(self, searchstr):
        '''set search query'''
        self.search_string = searchstr

    def stop_running(self):
        '''stop thread end exit'''
        self.active = False

    def set_dialog(self, dialog):
        '''set the active dialog to perform actions'''
        self.dialog = dialog

    def set_actors(self):
        '''fill list with all actors'''
        self.actors = self.kodidb.actors()

    def run(self):
        '''Main run loop for the background thread'''
        last_searchstring = ""
        monitor = xbmc.Monitor()
        while not monitor.abortRequested() and self.active:
            if self.search_string != last_searchstring:
                last_searchstring = self.search_string
                self.do_search(self.search_string)
            monitor.waitForAbort(1)
        del monitor

    def do_search(self, search_term):
        '''scrape results for search query'''

        movies_list = self.dialog.getControl(3110)
        series_list = self.dialog.getControl(3111)
        cast_list = self.dialog.getControl(3112)

        # clear current values
        movies_list.reset()
        series_list.reset()
        cast_list.reset()

        if len(search_term) == 0:
            return

        filters = [{
            "operator": "contains",
            "field": "title",
            "value": search_term
        }]

        # Process movies
        items = self.kodidb.movies(filters=filters)
        items = process_method_on_list(self.kodidb.prepare_listitem, items)
        result = []
        for item in items:
            result.append(self.kodidb.create_listitem(item, False))
        movies_list.addItems(result)

        # Process tvshows
        items = self.kodidb.tvshows(filters=filters)
        items = process_method_on_list(self.kodidb.prepare_listitem, items)
        result = []
        for item in items:
            item["file"] = 'videodb://tvshows/titles/%s' % item['tvshowid']
            item["isFolder"] = True
            result.append(self.kodidb.create_listitem(item, False))
        series_list.addItems(result)

        # Process cast
        result = []
        for item in self.actors:
            if search_term.lower() in item["label"].lower():
                item = self.kodidb.prepare_listitem(item)
                item[
                    "file"] = "RunScript(script.skin.helper.service,action=getcastmedia,name=%s)" % item[
                        "label"]
                result.append(self.kodidb.create_listitem(item, False))
        cast_list.addItems(result)
class BackgroundsUpdater():
    '''Background service providing rotating backgrounds to Kodi skins'''
    exit = False
    all_backgrounds = {}
    all_backgrounds2 = {}
    backgrounds_delay = 0
    walls_delay = 30
    enable_walls = False
    all_backgrounds_keys = {}
    prefetch_images = 30  # number of images to cache in memory for each library path
    pvr_bg_recordingsonly = False
    custom_picturespath = ""

    def __init__(self):
        self.cache = SimpleCache()
        self.kodidb = KodiDb()
        self.win = xbmcgui.Window(10000)
        self.addon = xbmcaddon.Addon(ADDON_ID)
        self.kodimonitor = xbmc.Monitor()
        self.all_backgrounds_labels = []
        self.smartshortcuts = SmartShortCuts(self)
        self.wallimages = WallImages(self)
        self.winprops = {}

    def stop(self):
        '''stop running our background service '''
        log_msg("BackgroundsUpdater - stop called", xbmc.LOGNOTICE)
        self.smartshortcuts.exit = True
        self.wallimages.exit = True
        self.exit = True
        xbmc.sleep(100)  # allow threads to process the stop request
        del self.smartshortcuts
        del self.wallimages
        del self.win
        del self.kodimonitor
        del self.addon

    def run(self):
        '''called to start our background service '''
        log_msg("BackgroundsUpdater - started", xbmc.LOGNOTICE)
        self.winpropcache()
        self.get_config()
        backgrounds_task_interval = 0
        walls_task_interval = 0
        delayed_task_interval = 112

        while not self.kodimonitor.abortRequested():

            # Process backgrounds only if we're not watching fullscreen video
            if xbmc.getCondVisibility(
                "![Window.IsActive(fullscreenvideo) | Window.IsActive(script.pseudotv.TVOverlay.xml) | "
                    "Window.IsActive(script.pseudotv.live.TVOverlay.xml)] | "
                    "Window.IsActive(script.pseudotv.live.EPG.xml)"):

                # background stuff like reading the skin settings and generating smart shortcuts
                if delayed_task_interval >= 120:
                    delayed_task_interval = 0
                    self.get_config()
                    self.report_allbackgrounds()
                    self.smartshortcuts.build_smartshortcuts()
                    self.winpropcache(True)

                # force refresh smart shortcuts on request
                if self.win.getProperty("refreshsmartshortcuts"):
                    self.win.clearProperty("refreshsmartshortcuts")
                    self.smartshortcuts.build_smartshortcuts()

                # Update home backgrounds every interval (if enabled by skinner)
                if self.backgrounds_delay and backgrounds_task_interval >= self.backgrounds_delay:
                    backgrounds_task_interval = 0
                    self.update_backgrounds()

                # Update wall images every interval (if enabled by skinner)
                if self.enable_walls and self.walls_delay and (walls_task_interval >= self.walls_delay):
                    walls_task_interval = 0
                    thread.start_new_thread(self.wallimages.update_wallbackgrounds, ())
                    self.wallimages.update_manualwalls()

            self.kodimonitor.waitForAbort(1)
            backgrounds_task_interval += 1
            walls_task_interval += 1
            delayed_task_interval += 1

        # abort requested
        self.stop()

    def get_config(self):
        '''gets various settings for the script as set by the skinner or user'''

        # set all backgrounds in global cache for quick startup
        self.winpropcache(True)

        # skinner (or user) enables the random fanart images by setting the randomfanartdelay skin string
        try:
            self.backgrounds_delay = int(xbmc.getInfoLabel("Skin.String(SkinHelper.RandomFanartDelay)"))
        except Exception:
            pass

        self.walls_delay = int(self.addon.getSetting("wallimages_delay"))
        self.wallimages.max_wallimages = int(self.addon.getSetting("max_wallimages"))
        self.pvr_bg_recordingsonly = self.addon.getSetting("pvr_bg_recordingsonly") == "true"
        self.enable_walls = xbmc.getCondVisibility("Skin.HasSetting(SkinHelper.EnableWallBackgrounds)")
        if self.addon.getSetting("enable_custom_images_path") == "true":
            self.custom_picturespath = self.addon.getSetting("custom_images_path")
        else:
            self.custom_picturespath = ""
        try:
            # skinner can enable manual wall images generation so check for these settings
            # store in memory so wo do not have to query the skin settings too often
            if self.walls_delay:
                for key in self.all_backgrounds_keys.iterkeys():
                    limitrange = xbmc.getInfoLabel("Skin.String(%s.EnableWallImages)" % key)
                    if limitrange:
                        self.wallimages.manual_walls[key] = int(limitrange)
        except Exception as exc:
            log_exception(__name__, exc)

    def report_allbackgrounds(self):
        '''sets a list of all known backgrounds as winprop to be retrieved from skinshortcuts'''
        self.set_winprop("SkinHelper.AllBackgrounds", repr(self.all_backgrounds_labels))

    def set_winprop(self, key, value):
        '''sets a window property and writes it to our global list'''
        self.winprops[key] = value
        if isinstance(value, unicode):
            value = value.encode("utf-8")
        self.win.setProperty(key, value)

    def winpropcache(self, setcache=False):
        '''sets/gets the current window props in a global cache to load them immediately at startup'''
        if setcache:
            self.cache.set("skinhelper.backgrounds", self.winprops)
        else:
            cache = self.cache.get("skinhelper.backgrounds")
            if cache:
                for key, value in cache.iteritems():
                    if value:
                        if isinstance(value, unicode):
                            value = value.encode("utf-8")
                        self.win.setProperty(key.encode("utf-8"), value)

    def get_images_from_vfspath(self, lib_path):
        '''get all images from the given vfs path'''
        result = []
        # safety check: check if no library windows are active to prevent any addons setting the view
        if (xbmc.getCondVisibility("Window.IsMedia") and "plugin" in lib_path) or self.exit:
            return result

        lib_path = get_content_path(lib_path)

        if "plugin.video.emby" in lib_path and "browsecontent" in lib_path and "filter" not in lib_path:
            lib_path = lib_path + "&filter=random"

        items = self.kodidb.get_json("Files.GetDirectory", returntype="", optparam=("directory", lib_path),
                                     fields=["title", "art", "thumbnail", "fanart"],
                                     sort={"method": "random", "order": "descending"},
                                     limits=(0, self.prefetch_images))

        for media in items:
            image = {}
            if media['label'].lower() == "next page":
                continue
            if media.get('art'):
                if media['art'].get('fanart'):
                    image["fanart"] = get_clean_image(media['art']['fanart'])
                elif media['art'].get('tvshow.fanart'):
                    image["fanart"] = get_clean_image(media['art']['tvshow.fanart'])
                elif media['art'].get('artist.fanart'):
                    image["fanart"] = get_clean_image(media['art']['artist.fanart'])
                if media['art'].get('thumb'):
                    image["thumbnail"] = get_clean_image(media['art']['thumb'])
            if not image.get('fanart') and media.get("fanart"):
                image["fanart"] = get_clean_image(media['fanart'])
            if not image.get("thumbnail") and media.get("thumbnail"):
                image["thumbnail"] = get_clean_image(media["thumbnail"])

            # only append items which have a fanart image
            if image.get("fanart"):
                # also append other art to the dict
                image["title"] = media.get('title', '')
                if not image.get("title"):
                    image["title"] = media.get('label', '')
                image["landscape"] = get_clean_image(media.get('art', {}).get('landscape', ''))
                image["poster"] = get_clean_image(media.get('art', {}).get('poster', ''))
                image["clearlogo"] = get_clean_image(media.get('art', {}).get('clearlogo', ''))
                result.append(image)
        random.shuffle(result)
        return result

    def get_pictures(self):
        '''get images we can use as pictures background'''
        images = []
        # load the pictures from the custom path or from all picture sources
        if self.custom_picturespath:
            # load images from custom path
            files = xbmcvfs.listdir(self.custom_picturespath)[1]
            random.shuffle(files)
            # pick max 20 images from path
            for file in files[:20]:
                if file.lower().endswith(".jpg") or file.lower().endswith(".png"):
                    image = os.path.join(self.custom_picturespath, file.decode("utf-8"))
                    images.append({"fanart": image, "title": file.decode("utf-8")})
        else:
            # load pictures from all picture sources
            media_array = self.kodidb.get_json('Files.GetSources', optparam=("media", "pictures"))
            randomdirs = []
            for source in media_array:
                if 'file' in source:
                    if "plugin://" not in source["file"]:
                        dirs = xbmcvfs.listdir(source["file"])[0]
                        random.shuffle(dirs)  # randomize output
                        if dirs:
                            # pick 10 subdirectories
                            for randomdir in dirs[:10]:
                                randomdir = os.path.join(source["file"], randomdir.decode("utf-8"))
                                randomdirs.append(randomdir)
                        # append root to dirs so we can also list images in the root
                        randomdirs.append(source["file"])
                        # pick 5 images from each dir
                        for item in randomdirs:
                            files2 = xbmcvfs.listdir(item)[1]
                            random.shuffle(files2)
                            for count, filename in enumerate(files2):
                                if (filename.endswith(".jpg") or filename.endswith(".png")) and count < 6:
                                    filename = filename.decode("utf-8")
                                    image = os.path.join(item, filename)
                                    images.append({"fanart": image, "title": filename})
        return images

    def set_background(self, win_prop, lib_path, fallback_image="", label=None):
        '''set the window property for the background image'''
        image = None
        if win_prop in self.all_backgrounds2:
            # pick one random image from the small list using normal random function
            if len(self.all_backgrounds2[win_prop]) > 0:
                image = random.choice(self.all_backgrounds2[win_prop])
        elif win_prop in self.all_backgrounds and len(self.all_backgrounds[win_prop]) > 0:
            # list is already in memory and still contains images, grab the next item in line
            image = self.all_backgrounds[win_prop][0]
            # delete image from list when we've used it so we have truly randomized images
            del self.all_backgrounds[win_prop][0]
        else:
            # no images in memory - load them from vfs
            if isinstance(lib_path, list):
                images = self.get_global_backgrounds(lib_path)
            elif lib_path == "pictures":
                images = self.get_pictures()
            elif lib_path == "pvr":
                images = self.get_pvr_backgrounds()
            else:
                images = self.get_images_from_vfspath(lib_path)
            # store images in memory
            if len(images) < self.prefetch_images:
                # this path did not return enough images so we store it in a different list
                # which will not be flushed
                self.all_backgrounds2[win_prop] = images
                if images:
                    image = random.choice(images)
            else:
                # normal approach: store the current set of images in a list
                # images are taken from that list one-by-one untill it's empty
                # once empty a fresh pair of images will be retrieved for the path
                # this way we have fully randomized images while there's no need
                # to store a big pile of data in memory
                image = images[0]
                del images[0]
                self.all_backgrounds[win_prop] = images
            # also store the key + label in a list for skinshortcuts - only if the path actually has images
            if image:
                if not any(win_prop in item for item in self.all_backgrounds_labels):
                    if label and isinstance(label, int):
                        label = xbmc.getInfoLabel("$ADDON[%s %s]" % (ADDON_ID, label))
                    elif not label:
                        label = win_prop
                    self.all_backgrounds_labels.append((win_prop, label))
        # set the image
        if image:
            for key, value in image.iteritems():  # image is actually a dict
                if key == "fanart":
                    self.set_winprop(win_prop, value)
                else:  # set additional image properties
                    self.set_winprop("%s.%s" % (win_prop, key), value)
        else:
            # no image - use fallback_image
            self.set_winprop(win_prop, fallback_image)

    def get_global_backgrounds(self, keys):
        '''get random backgrounds from multiple other collections'''
        images = []
        for key in keys:
            if key in self.all_backgrounds and self.all_backgrounds[key]:
                images += self.all_backgrounds[key]
        random.shuffle(images)
        return images

    def get_pvr_backgrounds(self):
        '''get the images for pvr items by using the skinhelper widgets as source'''
        images = []
        widgetreload = self.win.getProperty("widgetreload2")
        rec_images = self.get_images_from_vfspath(
            "plugin://script.skin.helper.widgets/?mediatype=pvr"
            "&action=recordings&limit=50&reload=%s" % widgetreload)
        if rec_images:  # result can be None
            images = rec_images
        if not self.pvr_bg_recordingsonly:
            tv_images = self.get_images_from_vfspath(
                "plugin://script.skin.helper.widgets/?mediatype=pvr"
                "&action=channels&limit=25&reload=%s" % widgetreload)
            if tv_images:  # result can be None
                images += tv_images
        return images

    def update_backgrounds(self):
        '''update all our provided backgrounds'''

        # conditional background
        self.win.setProperty("SkinHelper.ConditionalBackground", get_cond_background())

        # movies backgrounds
        if xbmc.getCondVisibility("Library.HasContent(movies)"):
            # random/all movies
            self.set_background("SkinHelper.AllMoviesBackground", "videodb://movies/titles/", label=32010)
            # in progress movies
            self.set_background(
                "SkinHelper.InProgressMoviesBackground",
                "videodb://movies/titles/?xsp=%s" %
                urlencode(
                    '{"limit":50,"order":{"direction":"ascending","method":"random"},'
                    '"rules":{"and":[{"field":"inprogress","operator":"true","value":[]}]},"type":"movies"}'),
                label=32012)
            # recent movies
            self.set_background("SkinHelper.RecentMoviesBackground", "videodb://recentlyaddedmovies/", label=32011)
            # unwatched movies
            self.set_background(
                "SkinHelper.UnwatchedMoviesBackground",
                "videodb://movies/titles/?xsp=%s" %
                urlencode(
                    '{"limit":50,"order":{"direction":"ascending","method":"random"},'
                    '"rules":{"and":[{"field":"playcount","operator":"is","value":0}]},"type":"movies"}'), label=32013)

        # tvshows backgrounds
        if xbmc.getCondVisibility("Library.HasContent(tvshows)"):
            # random/all tvshows
            self.set_background("SkinHelper.AllTvShowsBackground", "videodb://tvshows/titles/", label=32014)
            # in progress tv shows
            self.set_background(
                "SkinHelper.InProgressShowsBackground",
                "videodb://tvshows/titles/?xsp=%s" %
                urlencode(
                    '{"limit":50,"order":{"direction":"ascending","method":"random"},'
                    '"rules":{"and":[{"field":"inprogress","operator":"true","value":[]}]},"type":"tvshows"}'),
                label=32016)
            # recent episodes
            self.set_background("SkinHelper.RecentEpisodesBackground", "videodb://recentlyaddedepisodes/", label=32015)

        # all musicvideos
        if xbmc.getCondVisibility("Library.HasContent(musicvideos)"):
            self.set_background("SkinHelper.AllMusicVideosBackground", "videodb://musicvideos/titles", label=32018)

        # all music
        if xbmc.getCondVisibility("Library.HasContent(music)"):
            # music artists
            self.set_background("SkinHelper.AllMusicBackground", "musicdb://artists/", label=32019)
            # recent albums
            self.set_background(
                "SkinHelper.RecentMusicBackground", "musicdb://recentlyaddedalbums/", label=32023)
            # random songs
            self.set_background(
                "SkinHelper.AllMusicSongsBackground", "musicdb://songs/", label=32022)

        # tmdb backgrounds (extendedinfo)
        if xbmc.getCondVisibility("System.HasAddon(script.extendedinfo)"):
            self.set_background(
                "SkinHelper.TopRatedMovies",
                "plugin://script.extendedinfo/?info=topratedmovies",
                label=32020)
            self.set_background(
                "SkinHelper.TopRatedShows",
                "plugin://script.extendedinfo/?info=topratedtvshows",
                label=32021)

        # pictures background
        self.set_background("SkinHelper.PicturesBackground", "pictures", label=32017)

        # pvr background
        if xbmc.getCondVisibility("PVR.HasTvChannels"):
            self.set_background("SkinHelper.PvrBackground", "pvr", label=32024)

        # smartshortcuts backgrounds
        for node in self.smartshortcuts.get_smartshortcuts_nodes():
            self.set_background(node[0], node[1], label=node[2])

        # global backgrounds
        self.set_background("SkinHelper.GlobalFanartBackground",
                            ["SkinHelper.AllMoviesBackground", "SkinHelper.AllTvShowsBackground",
                             "SkinHelper.AllMusicVideosBackground", "SkinHelper.AllMusicBackground"],
                            label=32009)
        self.set_background("SkinHelper.AllVideosBackground",
                            ["SkinHelper.AllMoviesBackground", "SkinHelper.AllTvShowsBackground",
                             "SkinHelper.AllMusicVideosBackground"], label=32025)
        self.set_background(
            "SkinHelper.AllVideosBackground2", [
                "SkinHelper.AllMoviesBackground", "SkinHelper.AllTvShowsBackground"], label=32026)
        self.set_background(
            "SkinHelper.RecentVideosBackground",
            ["SkinHelper.RecentMoviesBackground", "SkinHelper.RecentEpisodesBackground"], label=32027)
        self.set_background(
            "SkinHelper.InProgressVideosBackground",
            ["SkinHelper.InProgressMoviesBackground", "SkinHelper.InProgressShowsBackground"], label=32028)
Beispiel #19
0
 def __init__(self, *args):
     xbmc.log("SearchBackgroundThread Init")
     threading.Thread.__init__(self, *args)
     self.kodidb = KodiDb()
     self.actors = []
     thread.start_new_thread(self.set_actors, ())
Beispiel #20
0
class PluginContent:
    '''Hidden plugin entry point providing some helper features'''
    params = {}
    win = None

    def __init__(self):
        self.cache = SimpleCache()
        self.kodi_db = KodiDb()
        self.win = xbmcgui.Window(10000)
        try:
            self.params = dict(
                urlparse.parse_qsl(sys.argv[2].replace(
                    '?', '').lower().decode("utf-8")))
            log_msg("plugin called with parameters: %s" % self.params)
            self.main()
        except Exception as exc:
            log_exception(__name__, exc)
            xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))

        # cleanup when done processing
        self.close()

    def close(self):
        '''Cleanup Kodi Cpython instances'''
        self.cache.close()
        del self.win

    def main(self):
        '''main action, load correct function'''
        action = self.params.get("action", "")
        if self.win.getProperty("SkinHelperShutdownRequested"):
            # do not proceed if kodi wants to exit
            log_msg(
                "%s --> Not forfilling request: Kodi is exiting" % __name__,
                xbmc.LOGWARNING)
            xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))
        else:
            try:
                if hasattr(self.__class__, action):
                    # launch module for action provided by this plugin
                    getattr(self, action)()
                else:
                    # legacy (widget) path called !!!
                    self.load_widget()
            except Exception as exc:
                log_exception(__name__, exc)

    def load_widget(self):
        '''legacy entrypoint called (widgets are moved to seperate addon), start redirect...'''
        action = self.params.get("action", "")
        newaddon = "script.skin.helper.widgets"
        log_msg(
            "Deprecated method: %s. Please reassign your widgets to get rid of this message. -"
            "This automatic redirect will be removed in the future" % (action),
            xbmc.LOGWARNING)
        paramstring = ""
        for key, value in self.params.iteritems():
            paramstring += ",%s=%s" % (key, value)
        if xbmc.getCondVisibility("System.HasAddon(%s)" % newaddon):
            # TEMP !!! for backwards compatability reasons only - to be removed in the near future!!
            import imp
            addon = xbmcaddon.Addon(newaddon)
            addon_path = addon.getAddonInfo('path').decode("utf-8")
            imp.load_source('plugin', os.path.join(addon_path, "plugin.py"))
            from plugin import main
            main.Main()
            del addon
        else:
            # trigger install of the addon
            if KODI_VERSION > 16:
                xbmc.executebuiltin("InstallAddon(%s)" % newaddon)
            else:
                xbmc.executebuiltin("RunPlugin(plugin://%s)" % newaddon)

    def playchannel(self):
        '''play channel from widget helper'''
        params = {"item": {"channelid": int(self.params["channelid"])}}
        self.kodi_db.set_json("Player.Open", params)

    def playrecording(self):
        '''retrieve the recording and play to get resume working'''
        recording = self.kodi_db.recording(self.params["recordingid"])
        params = {"item": {"recordingid": recording["recordingid"]}}
        self.kodi_db.set_json("Player.Open", params)
        # manually seek because passing resume to the player json cmd doesn't seem to work
        if recording["resume"].get("position"):
            for i in range(50):
                if xbmc.getCondVisibility("Player.HasVideo"):
                    break
                xbmc.sleep(50)
            xbmc.Player().seekTime(recording["resume"].get("position"))

    def launch(self):
        '''launch any builtin action using a plugin listitem'''
        if "runscript" in self.params["path"]:
            self.params["path"] = self.params["path"].replace("?", ",")
        xbmc.executebuiltin(self.params["path"])

    def playalbum(self):
        '''helper to play an entire album'''
        xbmc.executeJSONRPC(
            '{ "jsonrpc": "2.0", "method": "Player.Open", "params": { "item": { "albumid": %d } }, "id": 1 }'
            % int(self.params["albumid"]))

    def smartshortcuts(self):
        '''called from skinshortcuts to retrieve listing of all smart shortcuts'''
        import skinshortcuts
        skinshortcuts.get_smartshortcuts(self.params.get("path", ""))

    @staticmethod
    def backgrounds():
        '''called from skinshortcuts to retrieve listing of all backgrounds'''
        import skinshortcuts
        skinshortcuts.get_backgrounds()

    def widgets(self):
        '''called from skinshortcuts to retrieve listing of all widgetss'''
        import skinshortcuts
        skinshortcuts.get_widgets(self.params.get("path", ""),
                                  self.params.get("sublevel", ""))

    def resourceimages(self):
        '''retrieve listing of specific resource addon images'''
        from resourceaddons import get_resourceimages
        addontype = self.params.get("addontype", "")
        for item in get_resourceimages(addontype, True):
            listitem = xbmcgui.ListItem(item[0],
                                        label2=item[2],
                                        path=item[1],
                                        iconImage=item[3])
            xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),
                                        url=item[1],
                                        listitem=listitem,
                                        isFolder=False)
        xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))

    def extrafanart(self):
        '''helper to display extrafanart in multiimage control in the skin'''
        fanarts = eval(self.params["fanarts"])
        # process extrafanarts
        for item in fanarts:
            listitem = xbmcgui.ListItem(item, path=item)
            listitem.setProperty('mimetype', 'image/jpeg')
            xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),
                                        url=item,
                                        listitem=listitem)
        xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))

    def genrebackground(self):
        '''helper to display images for a specific genre in multiimage control in the skin'''
        genre = self.params.get("genre").split(".")[0]
        arttype = self.params.get("arttype", "fanart")
        randomize = self.params.get("random", "false") == "true"
        mediatype = self.params.get("mediatype", "movies")
        if genre and genre != "..":
            filters = [{"operator": "is", "field": "genre", "value": genre}]
            if randomize:
                sort = {"method": "random", "order": "descending"}
            else:
                sort = {"method": "sorttitle", "order": "ascending"}
            items = getattr(self.kodi_db, mediatype)(sort=sort,
                                                     filters=filters,
                                                     limits=(0, 50))
            for item in items:
                image = get_clean_image(item["art"].get(arttype, ""))
                if image:
                    image = get_clean_image(item["art"][arttype])
                    listitem = xbmcgui.ListItem(image, path=image)
                    listitem.setProperty('mimetype', 'image/jpeg')
                    xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),
                                                url=image,
                                                listitem=listitem)
        xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))

    def getcastmedia(self):
        '''helper to display get all media for a specific actor'''
        name = self.params.get("name")
        if name:
            all_items = self.kodi_db.castmedia(name)
            all_items = process_method_on_list(self.kodi_db.prepare_listitem,
                                               all_items)
            all_items = process_method_on_list(self.kodi_db.create_listitem,
                                               all_items)
            xbmcplugin.addDirectoryItems(int(sys.argv[1]), all_items,
                                         len(all_items))
        xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))

    def getcast(self):
        '''helper to get all cast for a given media item'''
        db_id = None
        all_cast = []
        all_cast_names = list()
        cache_str = ""
        download_thumbs = self.params.get("downloadthumbs", "") == "true"
        extended_cast_action = self.params.get("castaction",
                                               "") == "extendedinfo"
        tmdb = Tmdb()
        movie = self.params.get("movie")
        tvshow = self.params.get("tvshow")
        episode = self.params.get("episode")
        movieset = self.params.get("movieset")

        try:  # try to parse db_id
            if movieset:
                cache_str = "movieset.castcache-%s-%s" % (
                    self.params["movieset"], download_thumbs)
                db_id = int(movieset)
            elif tvshow:
                cache_str = "tvshow.castcache-%s-%s" % (self.params["tvshow"],
                                                        download_thumbs)
                db_id = int(tvshow)
            elif movie:
                cache_str = "movie.castcache-%s-%s" % (self.params["movie"],
                                                       download_thumbs)
                db_id = int(movie)
            elif episode:
                cache_str = "episode.castcache-%s-%s" % (
                    self.params["episode"], download_thumbs)
                db_id = int(episode)
        except Exception:
            pass

        cachedata = self.cache.get(cache_str)
        if cachedata:
            # get data from cache
            all_cast = cachedata
        else:
            # retrieve data from json api...
            if movie and db_id:
                all_cast = self.kodi_db.movie(db_id)["cast"]
            elif movie and not db_id:
                filters = [{
                    "operator": "is",
                    "field": "title",
                    "value": movie
                }]
                result = self.kodi_db.movies(filters=filters)
                all_cast = result[0]["cast"] if result else []
            elif tvshow and db_id:
                all_cast = self.kodi_db.tvshow(db_id)["cast"]
            elif tvshow and not db_id:
                filters = [{
                    "operator": "is",
                    "field": "title",
                    "value": tvshow
                }]
                result = self.kodi_db.tvshows(filters=filters)
                all_cast = result[0]["cast"] if result else []
            elif episode and db_id:
                all_cast = self.kodi_db.episode(db_id)["cast"]
            elif episode and not db_id:
                filters = [{
                    "operator": "is",
                    "field": "title",
                    "value": episode
                }]
                result = self.kodi_db.episodes(filters=filters)
                all_cast = result[0]["cast"] if result else []
            elif movieset:
                if not db_id:
                    for item in self.kodi_db.moviesets():
                        if item["title"].lower() == movieset.lower():
                            db_id = item["setid"]
                if db_id:
                    json_result = self.kodi_db.movieset(
                        db_id, include_set_movies_fields=["cast"])
                    if "movies" in json_result:
                        for movie in json_result['movies']:
                            all_cast += movie['cast']

            # optional: download missing actor thumbs
            if all_cast and download_thumbs:
                for cast in all_cast:
                    if cast.get("thumbnail"):
                        cast["thumbnail"] = get_clean_image(
                            cast.get("thumbnail"))
                    if not cast.get("thumbnail"):
                        artwork = tmdb.get_actor(cast["name"])
                        cast["thumbnail"] = artwork.get("thumb", "")
            # lookup tmdb if item is requested that is not in local db
            if not all_cast:
                tmdbdetails = {}
                if movie and not db_id:
                    tmdbdetails = tmdb.search_movie(movie)
                elif tvshow and not db_id:
                    tmdbdetails = tmdb.search_tvshow(tvshow)
                if tmdbdetails.get("cast"):
                    all_cast = tmdbdetails["cast"]
            # save to cache
            self.cache.set(cache_str, all_cast)

        # process listing with the results...
        for cast in all_cast:
            if cast.get("name") not in all_cast_names:
                liz = xbmcgui.ListItem(label=cast.get("name"),
                                       label2=cast.get("role"),
                                       iconImage=cast.get("thumbnail"))
                if extended_cast_action:
                    url = "RunScript(script.extendedinfo,info=extendedactorinfo,name=%s)" % cast.get(
                        "name")
                    url = "plugin://script.skin.helper.service/?action=launch&path=%s" % url
                    is_folder = False
                else:
                    url = "RunScript(script.skin.helper.service,action=getcastmedia,name=%s)" % cast.get(
                        "name")
                    url = "plugin://script.skin.helper.service/?action=launch&path=%s" % urlencode(
                        url)
                    is_folder = False
                all_cast_names.append(cast.get("name"))
                liz.setThumbnailImage(cast.get("thumbnail"))
                xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),
                                            url=url,
                                            listitem=liz,
                                            isFolder=is_folder)
        xbmcplugin.endOfDirectory(int(sys.argv[1]))

    @staticmethod
    def alphabet():
        '''display an alphabet scrollbar in listings'''
        all_letters = []
        if xbmc.getInfoLabel("Container.NumItems"):
            for i in range(int(xbmc.getInfoLabel("Container.NumItems"))):
                all_letters.append(
                    xbmc.getInfoLabel("Listitem(%s).SortLetter" % i).upper())
            start_number = ""
            for number in ["2", "3", "4", "5", "6", "7", "8", "9"]:
                if number in all_letters:
                    start_number = number
                    break
            for letter in [
                    start_number, "A", "B", "C", "D", "E", "F", "G", "H", "I",
                    "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U",
                    "V", "W", "X", "Y", "Z"
            ]:
                if letter == start_number:
                    label = "#"
                else:
                    label = letter
                listitem = xbmcgui.ListItem(label=label)
                if letter not in all_letters:
                    lipath = "noop"
                    listitem.setProperty("NotAvailable", "true")
                else:
                    lipath = "plugin://script.skin.helper.service/?action=alphabetletter&letter=%s" % letter
                xbmcplugin.addDirectoryItem(int(sys.argv[1]),
                                            lipath,
                                            listitem,
                                            isFolder=False)
        xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))

    def alphabetletter(self):
        '''used with the alphabet scrollbar to jump to a letter'''
        if KODI_VERSION > 16:
            xbmcplugin.setResolvedUrl(handle=int(sys.argv[1]),
                                      succeeded=False,
                                      listitem=xbmcgui.ListItem())
        letter = self.params.get("letter", "").upper()
        jumpcmd = ""
        if letter in ["A", "B", "C", "2"]:
            jumpcmd = "2"
        elif letter in ["D", "E", "F", "3"]:
            jumpcmd = "3"
        elif letter in ["G", "H", "I", "4"]:
            jumpcmd = "4"
        elif letter in ["J", "K", "L", "5"]:
            jumpcmd = "5"
        elif letter in ["M", "N", "O", "6"]:
            jumpcmd = "6"
        elif letter in ["P", "Q", "R", "S", "7"]:
            jumpcmd = "7"
        elif letter in ["T", "U", "V", "8"]:
            jumpcmd = "8"
        elif letter in ["W", "X", "Y", "Z", "9"]:
            jumpcmd = "9"
        if jumpcmd:
            xbmc.executebuiltin("SetFocus(50)")
            for i in range(40):
                xbmc.executeJSONRPC(
                    '{ "jsonrpc": "2.0", "method": "Input.ExecuteAction",\
                    "params": { "action": "jumpsms%s" }, "id": 1 }' %
                    (jumpcmd))
                xbmc.sleep(50)
                if xbmc.getInfoLabel("ListItem.Sortletter").upper() == letter:
                    break