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
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 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 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 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 count, item in enumerate(fanarts): listitem = xbmcgui.ListItem("fanart%s" % count, 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