def _get_shortcuts( self, group, defaultGroup = None, isXML = False, profileDir = None, defaultsOnly = False ): # This will load the shortcut file # Additionally, if the override files haven't been loaded, we'll load them too log( "Loading shortcuts for group " + group ) if profileDir is None: profileDir = xbmc.translatePath( "special://profile/" ).decode( "utf-8" ) userShortcuts = os.path.join( profileDir, "addon_data", __addonid__, self.slugify( group ) + ".DATA.xml" )#.encode('utf-8') skinShortcuts = os.path.join( __skinpath__ , self.slugify( group ) + ".DATA.xml")#.encode('utf-8') defaultShortcuts = os.path.join( __defaultpath__ , self.slugify( group ) + ".DATA.xml" )#.encode('utf-8') if defaultGroup is not None: skinShortcuts = os.path.join( __skinpath__ , self.slugify( defaultGroup ) + ".DATA.xml")#.encode('utf-8') defaultShortcuts = os.path.join( __defaultpath__ , self.slugify( defaultGroup ) + ".DATA.xml" )#.encode('utf-8') if defaultsOnly: paths = [skinShortcuts, defaultShortcuts ] else: paths = [userShortcuts, skinShortcuts, defaultShortcuts ] for path in paths: path = try_decode( path ) tree = None if xbmcvfs.exists( path ): file = xbmcvfs.File( path ).read() self._save_hash( path, file ) tree = xmltree.parse( path ) if tree is not None: # If this is a user-selected list of shortcuts... if path == userShortcuts: if group == "mainmenu": self._get_skin_required( tree, group, profileDir ) # Process shortcuts, marked as user-selected self._process_shortcuts( tree, group, profileDir, True ) else: if group == "mainmenu": self._get_skin_required( tree, group, profileDir ) self._process_shortcuts( tree, group, profileDir ) log( " - Loaded file " + path ) return tree else: self._save_hash( path, None ) # No file loaded log( " - No shortcuts" ) return xmltree.ElementTree( xmltree.Element( "shortcuts" ) )
def buildElement( self, item, groupName, visibilityCondition, profileVisibility, submenuVisibility = None, itemid=-1, options=[] ): # This function will build an element for the passed Item in newelement = xmltree.Element( "item" ) if itemid is not -1: newelement.set( "id", str( itemid ) ) # Label and label2 xmltree.SubElement( newelement, "label" ).text = DATA.local( item.find( "label" ).text )[1] xmltree.SubElement( newelement, "label2" ).text = DATA.local( item.find( "label2" ).text )[1] # Icon and thumb icon = item.find( "override-icon" ) if icon is None: icon = item.find( "icon" ) if icon is None: xmltree.SubElement( newelement, "icon" ).text = "DefaultShortcut.png" else: xmltree.SubElement( newelement, "icon" ).text = icon.text thumb = item.find( "thumb" ) if thumb is not None: xmltree.SubElement( newelement, "thumb" ).text = item.find( "thumb" ).text # labelID and defaultID labelID = xmltree.SubElement( newelement, "property" ) labelID.text = item.find( "labelID" ).text labelID.set( "name", "labelID" ) defaultID = xmltree.SubElement( newelement, "property" ) defaultID.text = item.find( "defaultID" ).text defaultID.set( "name", "defaultID" ) # Additional properties properties = eval( item.find( "additional-properties" ).text ) if len( properties ) != 0: repr( properties ) for property in properties: if property[0] == "node.visible": visibleProperty = xmltree.SubElement( newelement, "visible" ) visibleProperty.text = try_decode( property[1] ) else: additionalproperty = xmltree.SubElement( newelement, "property" ) additionalproperty.set( "name", property[0].decode( "utf-8" ) ) additionalproperty.text = DATA.local( property[1] )[1] # If this is a widget or background, set a skin setting to say it's enabled if property[0] == "widget": xbmc.executebuiltin( "Skin.SetBool(skinshortcuts-widget-" + property[1] + ")" ) # And if it's the main menu, list it if groupName == "mainmenu": xbmc.executebuiltin( "Skin.SetString(skinshortcuts-widget-" + str( self.widgetCount ) + "," + property[ 1 ] + ")" ) self.widgetCount += 1 elif property[0] == "background": try: xbmc.executebuiltin( "Skin.SetBool(skinshortcuts-background-" + property[1] + ")" ) except UnicodeEncodeError: xbmc.executebuiltin( "Skin.SetBool(skinshortcuts-background-" + property[1].encode('utf-8') + ")" ) # If this is the main menu, and we're cloning widgets or backgrounds... if groupName == "mainmenu": if "clonewidgets" in options: if property[0] == "widget" or property[0] == "widgetName" or property[0] == "widgetType" or property[0] == "widgetPlaylist": self.MAINWIDGET[ property[0] ] = property[1] if "clonebackgrounds" in options: if property[0] == "background" or property[0] == "backgroundName" or property[0] == "backgroundPlaylist" or property[0] == "backgroundPlaylistName": self.MAINBACKGROUND[ property[0] ] = property[1] # For backwards compatibility, save widgetPlaylist as widgetPath too if property[ 0 ] == "widgetPlaylist": additionalproperty = xmltree.SubElement( newelement, "property" ) additionalproperty.set( "name", "widgetPath" ) try: additionalproperty.text = DATA.local( property[1].decode( "utf-8" ) )[1] except: additionalproperty.text = DATA.local( property[1] )[1] # Primary visibility visibility = item.find( "visibility" ) if visibility is not None: xmltree.SubElement( newelement, "visible" ).text = visibility.text #additional onclick (group overrides) onclicks = item.findall( "additional-action" ) for onclick in onclicks: onclickelement = xmltree.SubElement( newelement, "onclick" ) onclickelement.text = onclick.text if "condition" in onclick.attrib: onclickelement.set( "condition", onclick.attrib.get( "condition" ) ) # Onclick onclicks = item.findall( "override-action" ) if len( onclicks ) == 0: onclicks = item.findall( "action" ) for onclick in onclicks: onclickelement = xmltree.SubElement( newelement, "onclick" ) # PVR Action if onclick.text.startswith( "pvr-channel://" ): # PVR action onclickelement.text = "RunScript(script.skinshortcuts,type=launchpvr&channel=" + onclick.text.replace( "pvr-channel://", "" ) + ")" elif onclick.text.startswith( "ActivateWindow(" ) and xbmc.translatePath( "special://skin/" ) in onclick.text: # Skin-relative links try: actionParts = onclick.text[15:-1].split( "," ) actionParts[1] = actionParts[1].replace( xbmc.translatePath( "special://skin/" ), "" ) path = actionParts[1].split( os.sep ) newAction = "special://skin" for actionPart in actionParts[1].split( os.sep ): if actionPart != "": newAction = newAction + "/" + actionPart if len( actionParts ) == 2: onclickelement.text = "ActivateWindow(" + actionParts[0] + "," + newAction + ")" else: onclickelement.text = "ActivateWindow(" + actionParts[0] + "," + newAction + "," + actionParts[2] + ")" except: pass else: onclickelement.text = onclick.text # Also add it as a path property if not self.propertyExists( "path", newelement ): # we only add the path property if there isn't already one in the list because it has to be unique in Kodi lists pathelement = xmltree.SubElement( newelement, "property" ) pathelement.set( "name", "path" ) pathelement.text = onclickelement.text # Get 'list' property (the action property of an ActivateWindow shortcut) if not self.propertyExists( "list", newelement ): # we only add the list property if there isn't already one in the list because it has to be unique in Kodi lists listElement = xmltree.SubElement( newelement, "property" ) listElement.set( "name", "list" ) listElement.text = DATA.getListProperty( onclickelement.text ) if onclick.text == "ActivateWindow(Settings)": self.hasSettings = True if "condition" in onclick.attrib: onclickelement.set( "condition", onclick.attrib.get( "condition" ) ) if len( self.checkForShortcuts ) != 0: # Check if we've been asked to watch for this shortcut newCheckForShortcuts = [] for checkforShortcut in self.checkForShortcuts: if onclick.text.lower() == checkforShortcut[ 0 ]: # They match, change the value to True newCheckForShortcuts.append( ( checkforShortcut[ 0 ], checkforShortcut[ 1 ], "True" ) ) else: newCheckForShortcuts.append( checkforShortcut ) self.checkForShortcuts = newCheckForShortcuts # Visibility if visibilityCondition is not None: visibilityElement = xmltree.SubElement( newelement, "visible" ) if profileVisibility is not None: visibilityElement.text = profileVisibility + " + [" + visibilityCondition + "]" else: visibilityElement.text = visibilityCondition issubmenuElement = xmltree.SubElement( newelement, "property" ) issubmenuElement.set( "name", "isSubmenu" ) issubmenuElement.text = "True" elif profileVisibility is not None: visibilityElement = xmltree.SubElement( newelement, "visible" ) visibilityElement.text = profileVisibility # Submenu visibility if submenuVisibility is not None: submenuVisibilityElement = xmltree.SubElement( newelement, "property" ) submenuVisibilityElement.set( "name", "submenuVisibility" ) if submenuVisibility.isdigit(): submenuVisibilityElement.text = "$NUMBER[" + submenuVisibility + "]" else: submenuVisibilityElement.text = DATA.slugify( submenuVisibility ) # Group name group = xmltree.SubElement( newelement, "property" ) group.set( "name", "group" ) group.text = try_decode( groupName ) if groupName == "mainmenu": self.MAINWIDGET = {} self.MAINBACKGROUND = {} # If this isn't the main menu, and we're cloning widgets or backgrounds... if groupName != "mainmenu": if "clonewidgets" in options and len( self.MAINWIDGET ) is not 0: for key in self.MAINWIDGET: additionalproperty = xmltree.SubElement( newelement, "property" ) additionalproperty.set( "name", key ) additionalproperty.text = try_decode( self.MAINWIDGET[ key ] ) if "clonebackgrounds" in options and len( self.MAINBACKGROUND ) is not 0: for key in self.MAINBACKGROUND: additionalproperty = xmltree.SubElement( newelement, "property" ) additionalproperty.set( "name", key ) additionalproperty.text = DATA.local( self.MAINBACKGROUND[ key ] )[1] propertyPatterns = self.getPropertyPatterns(labelID.text, groupName) if len(propertyPatterns) > 0: propertyReplacements = self.getPropertyReplacements(newelement) for propertyName in propertyPatterns: propertyPattern = propertyPatterns[propertyName][0] for original, replacement in propertyReplacements: regexpPattern = re.compile(re.escape(original), re.IGNORECASE) propertyPattern = regexpPattern.sub(replacement, propertyPattern) additionalproperty = xmltree.SubElement(newelement, "property") additionalproperty.set("name", propertyName.decode("utf-8")) additionalproperty.text = propertyPattern.decode("utf-8") return newelement
def setProperties( self, properties, values, labelID, group, DATA ): # This function will take a list of properties and values and apply them to the # main menu item with the given labelID if not group: group = "mainmenu" # Decode values properties = try_decode( properties ) values = try_decode( values ) labelID = try_decode( labelID ) group = try_decode( group ) # Split up property names and values propertyNames = properties.split( "|" ) propertyValues = values.replace( "::INFO::", "$INFO" ).split( "|" ) labelIDValues = labelID.split( "|" ) if len( labelIDValues ) == 0: # No labelID passed in, lets assume we were called in error return if len( propertyNames ) == 0: # No values passed in, lets assume we were called in error return # Get user confirmation that they want to make these changes message = "Set %s property to %s?" %( propertyNames[ 0 ], propertyValues[ 0 ] ) if len( propertyNames ) == 2: message += "[CR](and 1 other property)" elif len( propertyNames ) > 2: message += "[CR](and %d other properties)" %( len( propertyNames ) -1 ) shouldRun = xbmcgui.Dialog().yesno( __addon__.getAddonInfo( "name" ), message ) if not shouldRun: return # Load the properties currentProperties, defaultProperties = DATA._get_additionalproperties( xbmc.translatePath( "special://profile/" ).decode( "utf-8" ) ) otherProperties, requires, templateOnly = DATA._getPropertyRequires() # If there aren't any currentProperties, use the defaultProperties instead if currentProperties == [None]: currentProperties = defaultProperties # Pull out all properties into multi-dimensional dicts allProps = {} allProps[ group ] = {} for currentProperty in currentProperties: # If the group isn't in allProps, add it if currentProperty[ 0 ] not in allProps.keys(): allProps[ currentProperty [ 0 ] ] = {} # If the labelID isn't in the allProps[ group ], add it if currentProperty[ 1 ] not in allProps[ currentProperty[ 0 ] ].keys(): allProps[ currentProperty[ 0 ] ][ currentProperty[ 1 ] ] = {} # And add the property to allProps[ group ][ labelID ] if currentProperty[ 3 ] is not None: allProps[ currentProperty[ 0 ] ][ currentProperty [ 1 ] ][ currentProperty[ 2 ] ] = currentProperty[ 3 ] # Loop through the properties we've been asked to set for count, propertyName in enumerate(propertyNames): # Set the new value log( "Setting %s to %s" %( propertyName, propertyValues[ count ] ) ) if len( labelIDValues ) != 1: labelID = labelIDValues[ count ] if labelID not in allProps[ group ].keys(): allProps[ group ][ labelID ] = {} allProps[ group ][ labelID ][ propertyName ] = propertyValues[ count ] # Remove any properties whose requirements haven't been met for key in otherProperties: if key in allProps[ group ][ labelID ].keys() and key in requires.keys() and requires[ key ] not in allProps[ group ][ labelID ].keys(): # This properties requirements aren't met log( "Removing value %s" %( key ) ) allProps[ group ][ labelID ].pop( key ) # Build the list of all properties to save saveData = [] for saveGroup in allProps: for saveLabelID in allProps[ saveGroup ]: for saveProperty in allProps[ saveGroup ][ saveLabelID ]: saveData.append( [ saveGroup, saveLabelID, saveProperty, allProps[ saveGroup ][ saveLabelID ][ saveProperty ] ] ) # Save the new properties try: f = xbmcvfs.File( os.path.join( __datapath__ , xbmc.getSkinDir().decode('utf-8') + ".properties" ), 'w' ) f.write( repr( saveData ).replace( "],", "],\n" ) ) f.close() log( "Properties file saved succesfully" ) except: print_exc() log( "### ERROR could not save file %s" % __datapath__ ) # The properties will only be used if the .DATA.xml file exists in the addon_data folder( otherwise # the script will use the default values), so we're going to open and write the 'group' that has been # passed to us menuitems = DATA._get_shortcuts( group, processShortcuts = False ) DATA.indent( menuitems.getroot() ) path = xbmc.translatePath( os.path.join( "special://profile", "addon_data", __addonid__, "%s.DATA.xml" %( DATA.slugify( group, True ) ) ).encode('utf-8') ) menuitems.write( path, encoding="UTF-8" ) log( "Properties updated" ) # Mark that the menu needs to be rebuilt xbmcgui.Window( 10000 ).setProperty( "skinshortcuts-reloadmainmenu", "True" )
def addToMenu( self, path, label, icon, content, window, DATA ): log( repr( window ) ) log( repr( label ) ) log( repr( path ) ) log( repr( content ) ) # Show a waiting dialog dialog = xbmcgui.DialogProgress() dialog.create( path, __language__( 32063 ) ) # Work out if it's a single item, or a node isNode = False json_query = xbmc.executeJSONRPC('{ "jsonrpc": "2.0", "id": 0, "method": "Files.GetDirectory", "params": { "properties": ["title", "file", "thumbnail"], "directory": "' + path + '", "media": "files" } }') json_query = unicode(json_query, 'utf-8', errors='ignore') json_response = simplejson.loads(json_query) labels = [] paths = [] nodePaths = [] # Now we've retrieved the path, decode everything for writing path = try_decode( path ) label = try_decode( label ) icon = try_decode( icon ) # Add all directories returned by the json query if json_response.has_key('result') and json_response['result'].has_key('files') and json_response['result']['files'] is not None: labels = [ __language__(32058) ] paths = [ "ActivateWindow(%s,%s,return)" %( window, path ) ] for item in json_response['result']['files']: if item[ "filetype" ] == "directory": isNode = True labels.append( item[ "label" ] ) nodePaths.append( "ActivateWindow(%s,%s,return)" %( window, item[ "file" ] ) ) else: log( "Invalid JSON response returned" ) # Add actions based on content if content == "albums": labels.append( "Play" ) paths.append( "RunScript(script.skinshortcuts,type=launchalbum&album=%s)" %( self.extractID( path ) ) ) if window == 10002: labels.append( "Slideshow" ) paths.append( "SlideShow(%s,notrandom)" %( path ) ) labels.append( "Slideshow (random)" ) paths.append( "SlideShow(%s,random)" %( path ) ) labels.append( "Slideshow (recursive)" ) paths.append( "SlideShow(%s,recursive,notrandom)" %( path ) ) labels.append( "Slideshow (recursive, random)" ) paths.append( "SlideShow(%s,recursive,random)" %( path ) ) if path.endswith( ".xsp" ): labels.append( "Play" ) paths.append( "PlayMedia(%s)" %( path ) ) allMenuItems = [ xbmcgui.ListItem(label=__language__( 32112 )) ] # Main menu allLabelIDs = [ "mainmenu" ] if isNode: allMenuItems.append( xbmcgui.ListItem(label=__language__( 32113 ) ) ) # Main menu + autofill submenu allLabelIDs.append( "mainmenu" ) # Get main menu items menuitems = DATA._get_shortcuts( "mainmenu", processShortcuts = False ) DATA._clear_labelID() for menuitem in menuitems.findall( "shortcut" ): # Get existing items labelID's allMenuItems.append( xbmcgui.ListItem(label=DATA.local( menuitem.find( "label" ).text )[2], iconImage=menuitem.find( "icon" ).text) ) allLabelIDs.append( DATA._get_labelID( DATA.local( menuitem.find( "label" ).text )[3], menuitem.find( "action" ).text ) ) # Close progress dialog dialog.close() # Show a select dialog so the user can pick where in the menu to add the item w = ShowDialog( "DialogSelect.xml", __cwd__, listing=allMenuItems, windowtitle=__language__( 32114 ) ) w.doModal() selectedMenu = w.result del w if selectedMenu == -1 or selectedMenu is None: # User cancelled return action = paths[ 0 ] if isNode and selectedMenu == 1: # We're auto-filling submenu, so add all sub-nodes as possible default actions paths = paths + nodePaths if len( paths ) > 1: # There are multiple actions to choose from selectedAction = xbmcgui.Dialog().select( __language__( 32095 ), labels ) if selectedAction == -1 or selectedAction is None: # User cancelled return True action = paths[ selectedAction ] # Add the shortcut to the menu the user has selected # Load existing main menu items menuitems = DATA._get_shortcuts( allLabelIDs[ selectedMenu ], processShortcuts = False ) DATA._clear_labelID() # Generate a new labelID newLabelID = DATA._get_labelID( label, action ) # Write the updated mainmenu.DATA.xml newelement = xmltree.SubElement( menuitems.getroot(), "shortcut" ) xmltree.SubElement( newelement, "label" ).text = label xmltree.SubElement( newelement, "label2" ).text = "32024" # Custom shortcut xmltree.SubElement( newelement, "icon" ).text = icon xmltree.SubElement( newelement, "thumb" ) xmltree.SubElement( newelement, "action" ).text = action DATA.indent( menuitems.getroot() ) path = xbmc.translatePath( os.path.join( "special://profile", "addon_data", __addonid__, "%s.DATA.xml" %( DATA.slugify( allLabelIDs[ selectedMenu ], True ) ) ).encode('utf-8') ) menuitems.write( path, encoding="UTF-8" ) if isNode and selectedMenu == 1: # We're also going to write a submenu menuitems = xmltree.ElementTree( xmltree.Element( "shortcuts" ) ) for item in json_response['result']['files']: if item[ "filetype" ] == "directory": newelement = xmltree.SubElement( menuitems.getroot(), "shortcut" ) xmltree.SubElement( newelement, "label" ).text = item[ "label" ] xmltree.SubElement( newelement, "label2" ).text = "32024" # Custom shortcut xmltree.SubElement( newelement, "icon" ).text = item[ "thumbnail" ] xmltree.SubElement( newelement, "thumb" ) xmltree.SubElement( newelement, "action" ).text = "ActivateWindow(%s,%s,return)" %( window, item[ "file" ] ) DATA.indent( menuitems.getroot() ) path = xbmc.translatePath( os.path.join( "special://profile", "addon_data", __addonid__, DATA.slugify( newLabelID, True ) + ".DATA.xml" ).encode('utf-8') ) menuitems.write( path, encoding="UTF-8" ) # Mark that the menu needs to be rebuilt xbmcgui.Window( 10000 ).setProperty( "skinshortcuts-reloadmainmenu", "True" ) # And tell the user it all worked xbmcgui.Dialog().ok( __addon__.getAddonInfo( "name" ), __language__(32090) )
def setProperties(self, properties, values, labelID, group, DATA): # This function will take a list of properties and values and apply them to the # main menu item with the given labelID if not group: group = "mainmenu" # Decode values properties = try_decode(properties) values = try_decode(values) labelID = try_decode(labelID) group = try_decode(group) # Split up property names and values propertyNames = properties.split("|") propertyValues = values.replace("::INFO::", "$INFO").split("|") labelIDValues = labelID.split("|") if len(labelIDValues) == 0: # No labelID passed in, lets assume we were called in error return if len(propertyNames) == 0: # No values passed in, lets assume we were called in error return # Get user confirmation that they want to make these changes message = "Set %s property to %s?" % (propertyNames[0], propertyValues[0]) if len(propertyNames) == 2: message += "[CR](and 1 other property)" elif len(propertyNames) > 2: message += "[CR](and %d other properties)" % (len(propertyNames) - 1) shouldRun = xbmcgui.Dialog().yesno(ADDON.getAddonInfo("name"), message) if not shouldRun: return # Load the properties currentProperties, defaultProperties = DATA._get_additionalproperties( xbmc.translatePath("special://profile/").decode("utf-8")) otherProperties, requires, templateOnly = DATA._getPropertyRequires() # If there aren't any currentProperties, use the defaultProperties instead if currentProperties == [None]: currentProperties = defaultProperties # Pull out all properties into multi-dimensional dicts allProps = {} allProps[group] = {} for currentProperty in currentProperties: # If the group isn't in allProps, add it if currentProperty[0] not in allProps.keys(): allProps[currentProperty[0]] = {} # If the labelID isn't in the allProps[ group ], add it if currentProperty[1] not in allProps[currentProperty[0]].keys(): allProps[currentProperty[0]][currentProperty[1]] = {} # And add the property to allProps[ group ][ labelID ] if currentProperty[3] is not None: allProps[currentProperty[0]][currentProperty[1]][ currentProperty[2]] = currentProperty[3] # Loop through the properties we've been asked to set for count, propertyName in enumerate(propertyNames): # Set the new value log("Setting %s to %s" % (propertyName, propertyValues[count])) if len(labelIDValues) != 1: labelID = labelIDValues[count] if labelID not in allProps[group].keys(): allProps[group][labelID] = {} allProps[group][labelID][propertyName] = propertyValues[count] # Remove any properties whose requirements haven't been met for key in otherProperties: if key in allProps[group][labelID].keys( ) and key in requires.keys( ) and requires[key] not in allProps[group][labelID].keys(): # This properties requirements aren't met log("Removing value %s" % (key)) allProps[group][labelID].pop(key) # Build the list of all properties to save saveData = [] for saveGroup in allProps: for saveLabelID in allProps[saveGroup]: for saveProperty in allProps[saveGroup][saveLabelID]: saveData.append([ saveGroup, saveLabelID, saveProperty, allProps[saveGroup][saveLabelID][saveProperty] ]) # Save the new properties try: f = xbmcvfs.File( os.path.join(DATAPATH, xbmc.getSkinDir().decode('utf-8') + ".properties"), 'w') f.write(repr(saveData).replace("],", "],\n")) f.close() log("Properties file saved succesfully") except: print_exc() log("### ERROR could not save file %s" % DATAPATH) # The properties will only be used if the .DATA.xml file exists in the addon_data folder( otherwise # the script will use the default values), so we're going to open and write the 'group' that has been # passed to us menuitems = DATA._get_shortcuts(group, processShortcuts=False) DATA.indent(menuitems.getroot()) path = xbmc.translatePath( os.path.join("special://profile", "addon_data", ADDONID, "%s.DATA.xml" % (DATA.slugify(group, True))).encode('utf-8')) menuitems.write(path, encoding="UTF-8") log("Properties updated") # Mark that the menu needs to be rebuilt xbmcgui.Window(10000).setProperty("skinshortcuts-reloadmainmenu", "True")
def addToMenu(self, path, label, icon, content, window, DATA): log(repr(window)) log(repr(label)) log(repr(path)) log(repr(content)) # Show a waiting dialog dialog = xbmcgui.DialogProgress() dialog.create(path, LANGUAGE(32063)) # Work out if it's a single item, or a node isNode = False jsonPath = path.replace("\\", "\\\\") json_query = xbmc.executeJSONRPC( '{ "jsonrpc": "2.0", "id": 0, "method": "Files.GetDirectory", "params": { "properties": ["title", "file", "thumbnail"], "directory": "' + jsonPath + '", "media": "files" } }') json_query = unicode(json_query, 'utf-8', errors='ignore') json_response = simplejson.loads(json_query) labels = [] paths = [] nodePaths = [] # Now we've retrieved the path, decode everything for writing path = try_decode(path) label = try_decode(label) icon = try_decode(icon) # Add all directories returned by the json query if json_response.has_key('result') and json_response['result'].has_key( 'files') and json_response['result']['files'] is not None: labels = [LANGUAGE(32058)] paths = ["ActivateWindow(%s,%s,return)" % (window, path)] for item in json_response['result']['files']: if item["filetype"] == "directory": isNode = True labels.append(item["label"]) nodePaths.append("ActivateWindow(%s,%s,return)" % (window, item["file"])) else: # Unable to add to get directory listings log("Invalid JSON response returned") log(repr(simplejson)) # And tell the user it failed xbmcgui.Dialog().ok(ADDON.getAddonInfo("name"), ADDON.getLocalizedString(32115)) return # Add actions based on content if content == "albums": labels.append("Play") paths.append( "RunScript(script.skinshortcuts,type=launchalbum&album=%s)" % (self.extractID(path))) if window == 10002: labels.append("Slideshow") paths.append("SlideShow(%s,notrandom)" % (path)) labels.append("Slideshow (random)") paths.append("SlideShow(%s,random)" % (path)) labels.append("Slideshow (recursive)") paths.append("SlideShow(%s,recursive,notrandom)" % (path)) labels.append("Slideshow (recursive, random)") paths.append("SlideShow(%s,recursive,random)" % (path)) if path.endswith(".xsp"): labels.append("Play") paths.append("PlayMedia(%s)" % (path)) allMenuItems = [xbmcgui.ListItem(label=LANGUAGE(32112))] # Main menu allLabelIDs = ["mainmenu"] if isNode: allMenuItems.append(xbmcgui.ListItem( label=LANGUAGE(32113))) # Main menu + autofill submenu allLabelIDs.append("mainmenu") # Get main menu items menuitems = DATA._get_shortcuts("mainmenu", processShortcuts=False) DATA._clear_labelID() for menuitem in menuitems.findall("shortcut"): # Get existing items labelID's allMenuItems.append( xbmcgui.ListItem(label=DATA.local( menuitem.find("label").text)[2], iconImage=menuitem.find("icon").text)) allLabelIDs.append( DATA._get_labelID( DATA.local(menuitem.find("label").text)[3], menuitem.find("action").text)) # Close progress dialog dialog.close() # Show a select dialog so the user can pick where in the menu to add the item w = ShowDialog("DialogSelect.xml", CWD, listing=allMenuItems, windowtitle=LANGUAGE(32114)) w.doModal() selectedMenu = w.result del w if selectedMenu == -1 or selectedMenu is None: # User cancelled return action = paths[0] if isNode and selectedMenu == 1: # We're auto-filling submenu, so add all sub-nodes as possible default actions paths = paths + nodePaths if len(paths) > 1: # There are multiple actions to choose from selectedAction = xbmcgui.Dialog().select(LANGUAGE(32095), labels) if selectedAction == -1 or selectedAction is None: # User cancelled return True action = paths[selectedAction] # Add the shortcut to the menu the user has selected # Load existing main menu items menuitems = DATA._get_shortcuts(allLabelIDs[selectedMenu], processShortcuts=False) DATA._clear_labelID() # Generate a new labelID newLabelID = DATA._get_labelID(label, action) # Write the updated mainmenu.DATA.xml newelement = xmltree.SubElement(menuitems.getroot(), "shortcut") xmltree.SubElement(newelement, "label").text = label xmltree.SubElement(newelement, "label2").text = "32024" # Custom shortcut xmltree.SubElement(newelement, "icon").text = icon xmltree.SubElement(newelement, "thumb") xmltree.SubElement(newelement, "action").text = action DATA.indent(menuitems.getroot()) path = xbmc.translatePath( os.path.join( "special://profile", "addon_data", ADDONID, "%s.DATA.xml" % (DATA.slugify( allLabelIDs[selectedMenu], True))).encode('utf-8')) menuitems.write(path, encoding="UTF-8") if isNode and selectedMenu == 1: # We're also going to write a submenu menuitems = xmltree.ElementTree(xmltree.Element("shortcuts")) for item in json_response['result']['files']: if item["filetype"] == "directory": newelement = xmltree.SubElement(menuitems.getroot(), "shortcut") xmltree.SubElement(newelement, "label").text = item["label"] xmltree.SubElement( newelement, "label2").text = "32024" # Custom shortcut xmltree.SubElement(newelement, "icon").text = item["thumbnail"] xmltree.SubElement(newelement, "thumb") xmltree.SubElement( newelement, "action").text = "ActivateWindow(%s,%s,return)" % ( window, item["file"]) DATA.indent(menuitems.getroot()) path = xbmc.translatePath( os.path.join("special://profile", "addon_data", ADDONID, DATA.slugify(newLabelID, True) + ".DATA.xml").encode('utf-8')) menuitems.write(path, encoding="UTF-8") # Mark that the menu needs to be rebuilt xbmcgui.Window(10000).setProperty("skinshortcuts-reloadmainmenu", "True") # And tell the user it all worked xbmcgui.Dialog().ok(ADDON.getAddonInfo("name"), LANGUAGE(32090))
def local( self, data ): # This is our function to manage localisation # It accepts strings in one of the following formats: # #####, ::LOCAL::#####, ::SCRIPT::##### # $LOCALISE[#####], $SKIN[####|skin.id|last translation] # $ADDON[script.skinshortcuts #####] # If returns a list containing: # [Number/$SKIN, $LOCALIZE/$ADDON/Local string, Local string] # [Used for saving, used for building xml, used for displaying in dialog] if data is None: return ["","","",""] data = try_decode( data ) skinid = None lasttranslation = None # Get just the integer of the string, for the input forms where this is valid if not data.find( "::SCRIPT::" ) == -1: data = data[10:] elif not data.find( "::LOCAL::" ) == -1: data = data[9:] elif not data.find( "$LOCALIZE[" ) == -1: data = data.replace( "$LOCALIZE[", "" ).replace( "]", "" ).replace( " ", "" ) elif not data.find( "$ADDON[script.skinshortcuts" ) == -1: data = data.replace( "$ADDON[script.skinshortcuts", "" ).replace( "]", "" ).replace( " ", "" ) # Get the integer and skin id, from $SKIN input forms elif not data.find( "$SKIN[" ) == -1: splitdata = data[6:-1].split( "|" ) data = splitdata[0] skinid = splitdata[1] lasttranslation = splitdata[2] if data.isdigit(): if int( data ) >= 31000 and int( data ) < 32000: # A number from a skin - we're going to return a $SKIN[#####|skin.id|last translation] unit if skinid is None: # Set the skinid to the current skin id skinid = xbmc.getSkinDir() # If we're on the same skin as the skinid, get the latest translation if skinid == xbmc.getSkinDir(): lasttranslation = xbmc.getLocalizedString( int( data ) ) returnString = "$SKIN[" + data + "|" + skinid + "|" + lasttranslation + "]" return [ returnString, "$LOCALIZE[" + data + "]", lasttranslation, data ] returnString = "$SKIN[" + data + "|" + skinid + "|" + lasttranslation + "]" return [ returnString, lasttranslation, lasttranslation, data ] elif int( data ) >= 32000 and int( data ) < 33000: # A number from the script return [ data, "$ADDON[script.skinshortcuts " + data + "]", __language__( int( data ) ), data ] else: # A number from XBMC itself (probably) return [ data, "$LOCALIZE[" + data + "]", xbmc.getLocalizedString( int( data ) ), data ] # This isn't anything we can localize, just return it (in triplicate ;)) return[ data, data, data, data ]
def writexml( self, profilelist, mainmenuID, groups, numLevels, buildMode, progress, options, minitems ): # Reset the hashlist, add the profile list and script version hashlist.list = [] hashlist.list.append( ["::PROFILELIST::", profilelist] ) hashlist.list.append( ["::SCRIPTVER::", __addonversion__] ) hashlist.list.append( ["::XBMCVER::", __xbmcversion__] ) if int( __xbmcversion__ ) <= 15: hashlist.list.append( ["::MUSICCONTENT::", xbmc.getCondVisibility( "Library.HasContent(Music)" ) ] ) # Clear any skin settings for backgrounds and widgets DATA._reset_backgroundandwidgets() self.widgetCount = 1 # Create a new tree and includes for the various groups tree = xmltree.ElementTree( xmltree.Element( "includes" ) ) root = tree.getroot() # Create a Template object and pass it the root Template = template.Template() Template.includes = root # Get any shortcuts we're checking for self.checkForShortcuts = [] overridestree = DATA._get_overrides_skin() checkForShorctcutsOverrides = overridestree.getroot().findall( "checkforshortcut" ) for checkForShortcutOverride in checkForShorctcutsOverrides: if "property" in checkForShortcutOverride.attrib: # Add this to the list of shortcuts we'll check for self.checkForShortcuts.append( ( checkForShortcutOverride.text.lower(), checkForShortcutOverride.attrib.get( "property" ), "False" ) ) mainmenuTree = xmltree.SubElement( root, "include" ) mainmenuTree.set( "name", "skinshortcuts-mainmenu" ) submenuTrees = [] for level in range( 0, int( numLevels) + 1 ): subelement = xmltree.SubElement(root, "include") subtree = xmltree.SubElement( root, "include" ) if level == 0: subtree.set( "name", "skinshortcuts-submenu" ) else: subtree.set( "name", "skinshortcuts-submenu-" + str( level ) ) if not subtree in submenuTrees: submenuTrees.append( subtree ) if buildMode == "single": allmenuTree = xmltree.SubElement( root, "include" ) allmenuTree.set( "name", "skinshortcuts-allmenus" ) profilePercent = 100 / len( profilelist ) profileCount = -1 submenuNodes = {} for profile in profilelist: log( "Building menu for profile %s" %( profile[ 2 ] ) ) # Load profile details profileDir = profile[0] profileVis = profile[1] profileCount += 1 # Reset whether we have settings self.hasSettings = False # Reset any checkForShortcuts to say we haven't found them newCheckForShortcuts = [] for checkforShortcut in self.checkForShortcuts: newCheckForShortcuts.append( ( checkforShortcut[ 0 ], checkforShortcut[ 1 ], "False" ) ) self.checkForShortcuts = newCheckForShortcuts # Clear any previous labelID's DATA._clear_labelID() # Clear any additional properties, which may be for a different profile DATA.currentProperties = None # Create objects to hold the items menuitems = [] templateMainMenuItems = xmltree.Element( "includes" ) # If building the main menu, split the mainmenu shortcut nodes into the menuitems list fullMenu = False if groups == "" or groups.split( "|" )[0] == "mainmenu": # Set a skinstring that marks that we're providing the whole menu xbmc.executebuiltin( "Skin.SetBool(SkinShortcuts-FullMenu)" ) for node in DATA._get_shortcuts( "mainmenu", None, True, profile[0] ).findall( "shortcut" ): menuitems.append( node ) fullMenu = True else: # Clear any skinstring marking that we're providing the whole menu xbmc.executebuiltin( "Skin.Reset(SkinShortcuts-FullMenu)" ) # If building specific groups, split them into the menuitems list count = 0 if groups != "": for group in groups.split( "|" ): if count != 0 or group != "mainmenu": menuitems.append( group ) if len( menuitems ) == 0: # No groups to build break itemidmainmenu = 0 percent = profilePercent / len( menuitems ) i = 0 for item in menuitems: i += 1 itemidmainmenu += 1 progress.update( ( profilePercent * profileCount) + percent * i ) submenuDefaultID = None if not isinstance( item, basestring ): # This is a main menu item (we know this because it's an element, not a string) submenu = item.find( "labelID" ).text # Build the menu item menuitem = self.buildElement( item, "mainmenu", None, profile[1], DATA.slugify( submenu, convertInteger=True ), itemid = itemidmainmenu, options = options ) # Add the menu item to the various includes, retaining a reference to them mainmenuItemA = copy.deepcopy( menuitem ) mainmenuTree.append( mainmenuItemA ) if buildMode == "single": mainmenuItemB = copy.deepcopy( menuitem ) allmenuTree.append( mainmenuItemB ) templateMainMenuItems.append( copy.deepcopy( menuitem ) ) # Get submenu defaultID submenuDefaultID = item.find( "defaultID" ).text else: # It's an additional menu, so get its labelID submenu = DATA._get_labelID( item, None ) # Build the submenu count = 0 # Used to keep track of additional submenu for submenuTree in submenuTrees: submenuVisibilityName = submenu if count == 1: submenu = submenu + "." + str( count ) elif count != 0: submenu = submenu[:-1] + str( count ) submenuVisibilityName = submenu[:-2] # Get the tree's we're going to write the menu to if submenu in submenuNodes: justmenuTreeA = submenuNodes[ submenu ][ 0 ] justmenuTreeB = submenuNodes[ submenu ][ 1 ] else: # Create these nodes justmenuTreeA = xmltree.SubElement( root, "include" ) justmenuTreeB = xmltree.SubElement( root, "include" ) justmenuTreeA.set( "name", "skinshortcuts-group-" + DATA.slugify( submenu ) ) justmenuTreeB.set( "name", "skinshortcuts-group-alt-" + DATA.slugify( submenu ) ) submenuNodes[ submenu ] = [ justmenuTreeA, justmenuTreeB ] itemidsubmenu = 0 # Get the shortcuts for the submenu if count == 0: submenudata = DATA._get_shortcuts( submenu, submenuDefaultID, True, profile[0] ) else: submenudata = DATA._get_shortcuts( submenu, None, True, profile[0] ) if type( submenudata ) == list: submenuitems = submenudata else: submenuitems = submenudata.findall( "shortcut" ) # Are there any submenu items for the main menu? if count == 0: if len( submenuitems ) != 0: try: hasSubMenu = xmltree.SubElement( mainmenuItemA, "property" ) hasSubMenu.set( "name", "hasSubmenu" ) hasSubMenu.text = "True" if buildMode == "single": hasSubMenu = xmltree.SubElement( mainmenuItemB, "property" ) hasSubMenu.set( "name", "hasSubmenu" ) hasSubMenu.text = "True" except: # There probably isn't a main menu pass else: try: hasSubMenu = xmltree.SubElement( mainmenuItemA, "property" ) hasSubMenu.set( "name", "hasSubmenu" ) hasSubMenu.text = "False" if buildMode == "single": hasSubMenu = xmltree.SubElement( mainmenuItemB, "property" ) hasSubMenu.set( "name", "hasSubmenu" ) hasSubMenu.text = "False" except: # There probably isn't a main menu pass # If we're building a single menu, update the onclicks of the main menu if buildMode == "single" and not len( submenuitems ) == 0: for onclickelement in mainmenuItemB.findall( "onclick" ): if "condition" in onclickelement.attrib: onclickelement.set( "condition", "StringCompare(Window(10000).Property(submenuVisibility)," + DATA.slugify( submenuVisibilityName, convertInteger=True ) + ") + [" + onclickelement.attrib.get( "condition" ) + "]" ) newonclick = xmltree.SubElement( mainmenuItemB, "onclick" ) newonclick.text = "SetProperty(submenuVisibility," + DATA.slugify( submenuVisibilityName, convertInteger=True ) + ",10000)" newonclick.set( "condition", onclickelement.attrib.get( "condition" ) ) else: onclickelement.set( "condition", "StringCompare(Window(10000).Property(submenuVisibility)," + DATA.slugify( submenuVisibilityName, convertInteger=True ) + ")" ) newonclick = xmltree.SubElement( mainmenuItemB, "onclick" ) newonclick.text = "SetProperty(submenuVisibility," + DATA.slugify( submenuVisibilityName, convertInteger=True ) + ",10000)" # Build the submenu items for submenuItem in submenuitems: itemidsubmenu += 1 # Build the item without any visibility conditions menuitem = self.buildElement( submenuItem, submenu, None, profile[1], itemid = itemidsubmenu, options = options ) isSubMenuElement = xmltree.SubElement( menuitem, "property" ) isSubMenuElement.set( "name", "isSubmenu" ) isSubMenuElement.text = "True" # Add it, with appropriate visibility conditions, to the various submenu includes justmenuTreeA.append( copy.deepcopy( menuitem ) ) menuitemCopy = copy.deepcopy( menuitem ) visibilityElement = menuitemCopy.find( "visible" ) visibilityElement.text = "[%s] + %s" %( visibilityElement.text, "StringCompare(Window(10000).Property(submenuVisibility)," + DATA.slugify( submenuVisibilityName, convertInteger=True ) + ")" ) justmenuTreeB.append( menuitemCopy ) if buildMode == "single": # Add the property 'submenuVisibility' allmenuTreeCopy = copy.deepcopy( menuitemCopy ) submenuVisibility = xmltree.SubElement( allmenuTreeCopy, "property" ) submenuVisibility.set( "name", "submenuVisibility" ) submenuVisibility.text = DATA.slugify( submenuVisibilityName, convertInteger=True ) allmenuTree.append( allmenuTreeCopy ) menuitemCopy = copy.deepcopy( menuitem ) visibilityElement = menuitemCopy.find( "visible" ) visibilityElement.text = "[%s] + %s" %( visibilityElement.text, "StringCompare(Container(" + mainmenuID + ").ListItem.Property(submenuVisibility)," + DATA.slugify( submenuVisibilityName, convertInteger=True ) + ")" ) submenuTree.append( menuitemCopy ) # Build the template for the submenu Template.parseItems( "submenu", count, justmenuTreeA, profile[ 2 ], profile[ 1 ], "StringCompare(Container(" + mainmenuID + ").ListItem.Property(submenuVisibility)," + DATA.slugify( submenuVisibilityName, convertInteger=True ) + ")", item ) count += 1 if self.hasSettings == False: # Check if the overrides asks for a forced settings... overridestree = DATA._get_overrides_skin() forceSettings = overridestree.getroot().find( "forcesettings" ) if forceSettings is not None: # We want a settings option to be added newelement = xmltree.SubElement( mainmenuTree, "item" ) xmltree.SubElement( newelement, "label" ).text = "$LOCALIZE[10004]" xmltree.SubElement( newelement, "icon" ).text = "DefaultShortcut.png" xmltree.SubElement( newelement, "onclick" ).text = "ActivateWindow(settings)" xmltree.SubElement( newelement, "visible" ).text = profile[1] if buildMode == "single": newelement = xmltree.SubElement( mainmenuTree, "item" ) xmltree.SubElement( newelement, "label" ).text = "$LOCALIZE[10004]" xmltree.SubElement( newelement, "icon" ).text = "DefaultShortcut.png" xmltree.SubElement( newelement, "onclick" ).text = "ActivateWindow(settings)" xmltree.SubElement( newelement, "visible" ).text = profile[1] if len( self.checkForShortcuts ) != 0: # Add a value to the variable for all checkForShortcuts for checkForShortcut in self.checkForShortcuts: if profile[ 1 ] is not None and xbmc.getCondVisibility( profile[ 1 ] ): # Current profile - set the skin bool if checkForShortcut[ 2 ] == "True": xbmc.executebuiltin( "Skin.SetBool(%s)" %( checkForShortcut[ 1 ] ) ) else: xbmc.executebuiltin( "Skin.Reset(%s)" %( checkForShortcut[ 1 ] ) ) # Save this to the hashes file, so we can set it on profile changes hashlist.list.append( [ "::SKINBOOL::", [ profile[ 1 ], checkForShortcut[ 1 ], checkForShortcut[ 2 ] ] ] ) # Build the template for the main menu Template.parseItems( "mainmenu", 0, templateMainMenuItems, profile[ 2 ], profile[ 1 ], "", "", mainmenuID ) # If we haven't built enough main menu items, copy the ones we have while itemidmainmenu < minitems and fullMenu and len( mainmenuTree ) != 0: updatedMenuTree = copy.deepcopy( mainmenuTree ) for item in updatedMenuTree: itemidmainmenu += 1 # Update ID item.set( "id", str( itemidmainmenu ) ) for idElement in item.findall( "property" ): if idElement.attrib.get( "name" ) == "id": idElement.text = "$NUM[%s]" %( str( itemidmainmenu ) ) mainmenuTree.append( item ) # Build any 'Other' templates Template.writeOthers() progress.update( 100, message = __language__( 32098 ) ) # Get the skins addon.xml file addonpath = xbmc.translatePath( os.path.join( "special://skin/", 'addon.xml').encode("utf-8") ).decode("utf-8") addon = xmltree.parse( addonpath ) extensionpoints = addon.findall( "extension" ) paths = [] for extensionpoint in extensionpoints: if extensionpoint.attrib.get( "point" ) == "xbmc.gui.skin": resolutions = extensionpoint.findall( "res" ) for resolution in resolutions: path = xbmc.translatePath( os.path.join( try_decode( self.skinDir ) , try_decode( resolution.attrib.get( "folder" ) ), "script-skinshortcuts-includes.xml").encode("utf-8") ).decode('utf-8') paths.append( path ) skinVersion = addon.getroot().attrib.get( "version" ) # Save the tree DATA.indent( tree.getroot() ) for path in paths: tree.write( path, encoding="UTF-8" ) # Save the hash of the file we've just written with open(path, "r+") as f: DATA._save_hash( path, f.read() ) f.close() # Save the hashes # Append the skin version to the hashlist hashlist.list.append( ["::SKINVER::", skinVersion] ) # Save the hashes file = xbmcvfs.File( os.path.join( __masterpath__ , xbmc.getSkinDir() + ".hash" ), "w" ) file.write( repr( hashlist.list ) ) file.close