Advertisement
HitcherUK

Untitled

Mar 29th, 2015
477
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 86.84 KB | None | 0 0
  1. # coding=utf-8
  2. import os, sys, datetime, unicodedata
  3. import xbmc, xbmcgui, xbmcvfs, urllib
  4. import xml.etree.ElementTree as xmltree
  5. import thread
  6. from xml.dom.minidom import parse
  7. from xml.sax.saxutils import escape as escapeXML
  8. from traceback import print_exc
  9. from unidecode import unidecode
  10. import datafunctions, nodefunctions
  11. DATA = datafunctions.DataFunctions()
  12. NODE = nodefunctions.NodeFunctions()
  13.  
  14. if sys.version_info < (2, 7):
  15.     import simplejson
  16. else:
  17.     import json as simplejson
  18.  
  19. __addon__        = sys.modules[ "__main__" ].__addon__
  20. __addonid__      = sys.modules[ "__main__" ].__addonid__
  21. __addonversion__ = sys.modules[ "__main__" ].__addonversion__
  22. __cwd__          = __addon__.getAddonInfo('path').decode("utf-8")
  23. __datapath__     = os.path.join( xbmc.translatePath( "special://profile/addon_data/" ).decode('utf-8'), __addonid__ )
  24. __datapathalt__  = os.path.join( "special://profile/", "addon_data", __addonid__ )
  25. __skinpath__     = xbmc.translatePath( "special://skin/shortcuts/" ).decode('utf-8')
  26. __defaultpath__  = xbmc.translatePath( os.path.join( __cwd__, 'resources', 'shortcuts').encode("utf-8") ).decode("utf-8")
  27. __language__     = sys.modules[ "__main__" ].__language__
  28. __cwd__          = sys.modules[ "__main__" ].__cwd__
  29. __xbmcversion__  = xbmc.getInfoLabel( "System.BuildVersion" ).split(".")[0]
  30.  
  31. def log(txt):
  32.     if __xbmcversion__ == "13" or __addon__.getSetting( "enable_logging" ) == "true":
  33.         try:
  34.             if isinstance (txt,str):
  35.                 txt = txt.decode('utf-8')
  36.             message = u'%s: %s' % (__addonid__, txt)
  37.             xbmc.log(msg=message.encode('utf-8'), level=xbmc.LOGDEBUG)
  38.         except:
  39.             pass
  40.  
  41. def kodilistdir(path, walkEverything = False, stringForce = False):
  42.     json_query = xbmc.executeJSONRPC('{"jsonrpc":"2.0","method":"Files.GetDirectory","params":{"directory":"%s","media":"files"},"id":1}' % str(path))
  43.     json_query = unicode(json_query, 'utf-8', errors='ignore')
  44.     json_response = simplejson.loads(json_query)
  45.     dirs = []
  46.     files = []
  47.     if json_response.has_key('result') and json_response['result'].has_key('files') and json_response['result']['files'] is not None:
  48.         for item in json_response['result']['files']:
  49.             if item.has_key('file') and item.has_key('filetype') and item.has_key('label'):
  50.                 if item['filetype'] == 'directory' and (walkEverything or ((not item['file'].endswith('.xsp')) and (not item['file'].endswith('.xml/')) and (not item['file'].endswith('.xml')))):
  51.                     if stringForce and item['file'].startswith(stringForce):
  52.                         dirs.append({'path':xbmc.translatePath(item['file']), 'label':item['label']})
  53.                     else:
  54.                         dirs.append({'path':item['file'], 'label':item['label']})
  55.                 else:
  56.                     if stringForce and item['file'].startswith(stringForce):
  57.                         files.append({'path':xbmc.translatePath(item['file']), 'label':item['label']})
  58.                     else:
  59.                         files.append({'path':item['file'], 'label':item['label']})
  60.     return [path,dirs,files]
  61.        
  62. def kodiwalk(path, walkEverything = False, stringForce = False, paths = None):
  63.     if paths is None or len(paths) == 0:
  64.         paths = [kodilistdir(path, walkEverything, stringForce)]
  65.     else:
  66.         paths.append( kodilistdir(path, walkEverything, stringForce) )
  67.     dirs = paths[-1][1]
  68.     for dir in dirs:
  69.         if stringForce:
  70.             return kodiwalk(dir['path'].replace(xbmc.translatePath(stringForce),stringForce).replace('\\','/'), walkEverything, stringForce, paths)
  71.         else:
  72.             return kodiwalk(dir['path'], walkEverything, stringForce, paths)
  73.     return paths
  74.  
  75. class LibraryFunctions():
  76.     def __init__( self, *args, **kwargs ):
  77.        
  78.         # values to mark whether data from different areas of the library have been loaded
  79.         self.loadedCommon = False
  80.         self.loadedMoreCommands = False
  81.         self.loadedVideoLibrary = False
  82.         self.loadedMusicLibrary = False
  83.         self.loadedLibrarySources = False
  84.         self.loadedPVRLibrary = False
  85.         self.loadedRadioLibrary = False
  86.         self.loadedPlaylists = False
  87.         self.loadedAddOns = False
  88.         self.loadedFavourites = False
  89.         self.loadedUPNP = False
  90.         self.loadedSettings = False
  91.        
  92.         self.widgetPlaylistsList = []
  93.        
  94.         # Empty dictionary for different shortcut types
  95.         self.dictionaryGroupings = {"common":None, "commands":None, "video":None, "movie":None, "movie-flat":None, "tvshow":None, "tvshow-flat":None, "musicvideo":None, "musicvideo-flat":None, "customvideonode":None, "customvideonode-flat":None, "videosources":None, "pvr":None, "radio":None, "pvr-tv":None, "pvr-radio":None, "music":None, "musicsources":None, "picturesources":None, "playlist-video":None, "playlist-audio":None, "addon-program":None, "addon-video":None, "addon-audio":None, "addon-image":None, "favourite":None, "settings":None }
  96.         self.folders = {}
  97.         self.foldersCount = 0
  98.        
  99.         self.useDefaultThumbAsIcon = None
  100.        
  101.     def loadLibrary( self ):
  102.         # Load all library data, for use with threading
  103.         self.common()
  104.         self.more()
  105.         self.videolibrary()
  106.         self.musiclibrary()
  107.         self.pvrlibrary()
  108.         self.radiolibrary()
  109.         self.librarysources()
  110.         self.playlists()
  111.         self.addons()                
  112.         self.favourites()
  113.         self.settings()
  114.        
  115.         # Do a JSON query for upnp sources (so that they'll show first time the user asks to see them)
  116.         if self.loadedUPNP == False:
  117.             json_query = xbmc.executeJSONRPC('{ "jsonrpc": "2.0", "id": 0, "method": "Files.GetDirectory", "params": { "properties": ["title", "file", "thumbnail"], "directory": "upnp://", "media": "files" } }')
  118.             self.loadedUPNP = True
  119.  
  120.     # ==============================================
  121.     # === BUILD/DISPLAY AVAILABLE SHORTCUT NODES ===
  122.     # ==============================================
  123.    
  124.     def retrieveGroup( self, group, flat = True ):
  125.         trees = [DATA._get_overrides_skin(), DATA._get_overrides_script()]
  126.         nodes = None
  127.         for tree in trees:
  128.             if tree is not None:
  129.                 if flat:
  130.                     nodes = tree.find( "flatgroupings" )
  131.                     if nodes is not None:
  132.                         nodes = nodes.findall( "node" )
  133.                 else:
  134.                     nodes = tree.find( "groupings" )
  135.             if nodes is not None:
  136.                 break                
  137.                
  138.         if nodes is None:
  139.             return [ "Error", [] ]
  140.            
  141.         returnList = []
  142.        
  143.         if flat:
  144.             # Flat groupings
  145.             count = 0
  146.             # Cycle through nodes till we find the one specified
  147.             for node in nodes:
  148.                 count += 1
  149.                 if "condition" in node.attrib:
  150.                     if not xbmc.getCondVisibility( node.attrib.get( "condition" ) ):
  151.                         group += 1
  152.                         continue
  153.                 if "version" in node.attrib:
  154.                     if __xbmcversion__ != node.attrib.get( "version" ):
  155.                         group += 1
  156.                         continue
  157.                 if count == group:
  158.                     # We found it :)
  159.                     return( node.attrib.get( "label" ), self.buildNodeListing( node, True ) )
  160.                     for subnode in node:
  161.                         if subnode.tag == "content":
  162.                             returnList = returnList + self.retrieveContent( subnode.text )
  163.                         if subnode.tag == "shortcut":
  164.                             returnList.append( self._create( [subnode.text, subnode.attrib.get( "label" ), subnode.attrib.get( "type" ), {"icon": subnode.attrib.get( "icon" )}] ) )
  165.  
  166.                     return [node.attrib.get( "label" ), returnList]
  167.                    
  168.             return ["Error", []]
  169.            
  170.         else:
  171.             # Heirachical groupings
  172.             if group == "":
  173.                 # We're going to get the root nodes
  174.                 return [ __language__(32048), self.buildNodeListing( nodes, False ) ]
  175.             else:
  176.                 groups = group.split( "," )
  177.                
  178.                 nodes = [ "", nodes ]
  179.                 for groupNum in groups:
  180.                     nodes = self.getNode( nodes[1], int( groupNum ) )
  181.                    
  182.                 return [ nodes[0], self.buildNodeListing( nodes[1], False ) ]
  183.                        
  184.     def getNode( self, tree, number ):
  185.         count = 0
  186.         for subnode in tree:
  187.             count += 1
  188.             # If not visible, skip it
  189.             if "condition" in subnode.attrib:
  190.                 if not xbmc.getCondVisibility( subnode.attrib.get( "condition" ) ):
  191.                     number += 1
  192.                     continue
  193.             if "version" in subnode.attrib:
  194.                 if __xbmcversion__ != subnode.attrib.get( "version" ):
  195.                     number += 1
  196.                     continue
  197.  
  198.             if count == number:
  199.                 label = DATA.local( subnode.attrib.get( "label" ) )[2]
  200.                 return [ label, subnode ]
  201.                
  202.     def buildNodeListing( self, nodes, flat ):
  203.         returnList = []
  204.         count = 0
  205.         for node in nodes:
  206.             if "condition" in node.attrib:
  207.                 if not xbmc.getCondVisibility( node.attrib.get( "condition" ) ):
  208.                     continue
  209.             if "version" in node.attrib:
  210.                 if __xbmcversion__ != node.attrib.get( "version" ):
  211.                     continue
  212.             count += 1
  213.             if node.tag == "content":
  214.                 returnList = returnList + self.retrieveContent( node.text )
  215.             if node.tag == "shortcut":
  216.                 returnList.append( self._create( [node.text, node.attrib.get( "label" ), node.attrib.get( "type" ), {"icon": node.attrib.get( "icon" )}] ) )
  217.             if node.tag == "node" and flat == False:
  218.                 returnList.append( self._create( ["||NODE||" + str( count ), node.attrib.get( "label" ), "", {"icon": "DefaultFolder.png"}] ) )
  219.                
  220.         return returnList
  221.                
  222.     def retrieveContent( self, content ):
  223.         if content == "upnp-video":
  224.             items = [ self._create(["||UPNP||", "32070", "32069", {"icon": "DefaultFolder.png"}]) ]
  225.         elif content == "upnp-music":
  226.             items = [ self._create(["||UPNP||", "32070", "32073", {"icon": "DefaultFolder.png"}]) ]
  227.            
  228.         elif self.dictionaryGroupings[ content ] is None:
  229.             # The data hasn't been loaded yet
  230.             items = self.loadGrouping( content )
  231.         else:
  232.             items = self.dictionaryGroupings[ content ]
  233.  
  234.         if items is not None:
  235.             items = self.checkForFolder( items )
  236.         else:
  237.             items = []
  238.            
  239.         # Check for any icon overrides for these items
  240.         tree = DATA._get_overrides_skin()
  241.         if tree is None:
  242.             return items
  243.            
  244.         for item in items:
  245.             item = self._get_icon_overrides( tree, item, content )
  246.            
  247.         return items
  248.            
  249.     def checkForFolder( self, items ):
  250.         # This function will check for any folders in the listings that are being returned
  251.         # and, if found, move their sub-items into a property
  252.         returnItems = []
  253.         for item in items:
  254.             if isinstance( item, list ):
  255.                 self.foldersCount += 1
  256.                 self.folders[ str( self.foldersCount ) ] = item[1]
  257.                 newItem = item[0]
  258.                 newItem.setProperty( "folder", str( self.foldersCount ) )
  259.                 returnItems.append( newItem )
  260.             else:
  261.                 returnItems.append( item )
  262.            
  263.         return( returnItems )
  264.        
  265.     def loadGrouping( self, content ):
  266.         # We'll be called if the data for a wanted group hasn't been loaded yet
  267.         if content == "common":
  268.             self.common()
  269.         if content  == "commands":
  270.             self.more()            
  271.         if content == "movie" or content == "tvshow" or content == "musicvideo" or content == "customvideonode" or content == "movie-flat" or content == "tvshow-flat" or content == "musicvideo-flat" or content == "customvideonode-flat":
  272.             # These have been deprecated
  273.             return []
  274.         if content == "video":
  275.             self.videolibrary()
  276.         if content == "videosources" or content == "musicsources" or content == "picturesources":
  277.             self.librarysources()
  278.         if content == "music":
  279.             self.musiclibrary()
  280.         if content == "pvr" or content == "pvr-tv" or content == "pvr-radio":
  281.             self.pvrlibrary()
  282.         if content == "radio":
  283.             self.radiolibrary()
  284.         if content == "playlist-video" or content == "playlist-audio":
  285.             self.playlists()
  286.         if content == "addon-program" or content == "addon-video" or content == "addon-audio" or content == "addon-image":
  287.             self.addons()
  288.         if content == "favourite":
  289.             self.favourites()
  290.         if content == "settings":
  291.             self.settings()
  292.            
  293.         # The data has now been loaded, return it
  294.         return self.dictionaryGroupings[ content ]
  295.        
  296.     def flatGroupingsCount( self ):
  297.         # Return how many nodes there are in the the flat grouping
  298.         tree = DATA._get_overrides_script()
  299.         if tree is None:
  300.             return 1
  301.         groupings = tree.find( "flatgroupings" )
  302.         nodes = groupings.findall( "node" )
  303.         count = 0
  304.         for node in nodes:
  305.             if "condition" in node.attrib:
  306.                 if not xbmc.getCondVisibility( node.attrib.get( "condition" ) ):
  307.                     continue
  308.             if "version" in node.attrib:
  309.                 if __xbmcversion__ != node.attrib.get( "version" ):
  310.                     continue
  311.                    
  312.             count += 1
  313.                
  314.         return count
  315.        
  316.    
  317.     def addToDictionary( self, group, content ):
  318.         # This function adds content to the dictionaryGroupings - including
  319.         # adding any skin-provided shortcuts to the group
  320.         tree = DATA._get_overrides_skin()
  321.         if tree is None:
  322.             # There are no overrides to check for extra shortcuts
  323.             self.dictionaryGroupings[ group ] = content
  324.             return
  325.            
  326.         # Search for skin-provided shortcuts for this group
  327.         originalGroup = group
  328.         if group.endswith( "-flat" ):
  329.             group = group.replace( "-flat", "" )
  330.            
  331.         if group != "movie" and group != "tvshow" and group != "musicvideo":
  332.             for elem in tree.findall( "shortcut" ):
  333.                 if "grouping" in elem.attrib:
  334.                     if group == elem.attrib.get( "grouping" ):
  335.                         # We want to add this shortcut
  336.                         label = elem.attrib.get( "label" )
  337.                         type = elem.attrib.get( "type" )
  338.                         thumb = elem.attrib.get( "thumbnail" )
  339.                         icon = elem.attrib.get( "icon" )
  340.                        
  341.                         action = elem.text
  342.                        
  343.                         #if label.isdigit():
  344.                         #    label = "::LOCAL::" + label
  345.                            
  346.                         if type is None:
  347.                             type = "32024"
  348.                         #elif type.isdigit():
  349.                         #    type = "::LOCAL::" + type
  350.                        
  351.                         if icon is None:
  352.                             icon = ""
  353.                         if thumb is None:
  354.                             thumb = ""
  355.  
  356.                         listitem = self._create( [action, label, type, {"icon": icon, "thumb": thumb}] )
  357.                        
  358.                         if "condition" in elem.attrib:
  359.                             if xbmc.getCondVisibility( elem.attrib.get( "condition" ) ):
  360.                                 content.insert( 0, listitem )
  361.                         else:
  362.                             content.insert( 0, listitem )
  363.  
  364.                 elif group == "common":
  365.                     # We want to add this shortcut
  366.                     label = elem.attrib.get( "label" )
  367.                     type = elem.attrib.get( "type" )
  368.                     thumb = elem.attrib.get( "thumbnail" )
  369.                     icon = elem.attrib.get( "icon" )
  370.                    
  371.                     action = elem.text
  372.                    
  373.                     #if label.isdigit():
  374.                     #    label = "::LOCAL::" + label
  375.                        
  376.                     if type is None:
  377.                         type = "32024"
  378.                     #elif type.isdigit():
  379.                     #    type = "::LOCAL::" + type
  380.                        
  381.                     if type is None or type == "":
  382.                         type = "Skin Provided"
  383.                        
  384.                     if icon is None:
  385.                         icon = ""
  386.                        
  387.                     if thumb is None:
  388.                         thumb = ""
  389.  
  390.                     listitem = self._create( [action, label, type, {"icon": icon, "thumb":thumb}] )
  391.                    
  392.                     if "condition" in elem.attrib:
  393.                         if xbmc.getCondVisibility( elem.attrib.get( "condition" ) ):
  394.                             content.append( listitem )
  395.                     else:
  396.                         content.append( listitem )
  397.                    
  398.         self.dictionaryGroupings[ originalGroup ] = content
  399.        
  400.     # ================================
  401.     # === BUILD AVAILABLE SHORTCUT ===
  402.     # ================================
  403.    
  404.     def _create ( self, item, allowOverrideLabel = True ):
  405.         # Retrieve label
  406.         localLabel = DATA.local( item[1] )[0]
  407.        
  408.         # Create localised label2
  409.         displayLabel2 = DATA.local( item[2] )[2]
  410.         shortcutType = DATA.local( item[2] )[0]
  411.        
  412.         if allowOverrideLabel:
  413.             # Check for a replaced label
  414.             replacementLabel = DATA.checkShortcutLabelOverride( item[0] )
  415.             if replacementLabel is not None:
  416.                
  417.                 localLabel = DATA.local( replacementLabel[0] )[0]
  418.                    
  419.                 if len( replacementLabel ) == 2:
  420.                     # We're also overriding the type
  421.                     displayLabel2 = DATA.local( replacementLabel[1] )[2]
  422.                     shortcutType = DATA.local( replacementLabel[1] )[0]
  423.                    
  424.        
  425.         # Try localising it
  426.         displayLabel = DATA.local( localLabel )[2]
  427.         labelID = DATA.createNiceName( DATA.local( localLabel )[0] )
  428.        
  429.         if displayLabel.startswith( "$NUMBER[" ):
  430.             displayLabel = displayLabel[8:-1]
  431.        
  432.         # Create localised label2
  433.         displayLabel2 = DATA.local( displayLabel2 )[2]
  434.         shortcutType = DATA.local( shortcutType )[0]
  435.        
  436.         # If either displayLabel starts with a $, ask Kodi to parse it for us
  437.         if displayLabel.startswith( "$" ):
  438.             displayLabel = xbmc.getInfoLabel( displayLabel )
  439.         if displayLabel2.startswith( "$" ):
  440.             displayLabel2 = xbmc.getInfoLabel( displayLabel2 )
  441.            
  442.         # If this launches our explorer, append a notation to the displayLabel
  443.         if item[0].startswith( "||" ):
  444.             displayLabel = displayLabel + "  >"
  445.            
  446.         # Retrieve icon and thumbnail
  447.         if item[3]:
  448.             if "icon" in item[3].keys() and item[ 3 ][ "icon" ] is not None:
  449.                 icon = item[3]["icon"]
  450.             else:
  451.                 icon = "DefaultShortcut.png"
  452.             if "thumb" in item[3].keys():
  453.                 thumbnail = item[3]["thumb"]
  454.             else:
  455.                 thumbnail = None
  456.         else:
  457.             icon = "DefaultShortcut.png"
  458.             thumbnail = None
  459.                        
  460.         # Check if the option to use the thumb as the icon is enabled
  461.         if self.useDefaultThumbAsIcon is None:
  462.             # Retrieve the choice from the overrides.xml
  463.             tree = DATA._get_overrides_skin()
  464.             if tree is None:
  465.                 self.useDefaultThumbAsIcon = False
  466.             else:
  467.                 node = tree.getroot().find( "useDefaultThumbAsIcon" )
  468.                 if node is None:
  469.                     self.useDefaultThumbAsIcon = False
  470.                 else:
  471.                     if node.text.lower() == "true":
  472.                         self.useDefaultThumbAsIcon = True
  473.                     else:
  474.                         self.useDefaultThumbAsIcon = False
  475.            
  476.         usedDefaultThumbAsIcon = False
  477.         if self.useDefaultThumbAsIcon == True and thumbnail is not None:            
  478.             icon = thumbnail
  479.             thumbnail = None
  480.             usedDefaultThumbAsIcon = True
  481.            
  482.         oldicon = None
  483.        
  484.         # If the icon starts with a $, ask Kodi to parse it for us
  485.         displayIcon = icon
  486.         iconIsVar = False
  487.         if icon.startswith( "$" ):
  488.             displayIcon = xbmc.getInfoLabel( icon )
  489.             iconIsVar = True
  490.        
  491.         # Get a temporary labelID
  492.         DATA._clear_labelID()
  493.         labelID = DATA._get_labelID( labelID, item[0] )
  494.                        
  495.         # If the skin doesn't have the icon, replace it with DefaultShortcut.png
  496.         if ( not displayIcon or not xbmc.skinHasImage( displayIcon ) ) and not iconIsVar:
  497.             if not usedDefaultThumbAsIcon:
  498.                 displayIcon = "DefaultShortcut.png"
  499.                            
  500.         # Build listitem
  501.         if thumbnail is not None:
  502.             listitem = xbmcgui.ListItem(label=displayLabel, label2=displayLabel2, iconImage=displayIcon, thumbnailImage=thumbnail)
  503.             listitem.setProperty( "thumbnail", thumbnail)
  504.         else:
  505.             listitem = xbmcgui.ListItem(label=displayLabel, label2=displayLabel2, iconImage=thumbnail)
  506.         listitem.setProperty( "path", item[0] )
  507.         listitem.setProperty( "localizedString", localLabel )
  508.         listitem.setProperty( "shortcutType", shortcutType )
  509.         listitem.setProperty( "icon", displayIcon )
  510.         listitem.setProperty( "tempLabelID", labelID )
  511.         listitem.setProperty( "defaultLabel", labelID )
  512.        
  513.         if displayIcon != icon:
  514.             listitem.setProperty( "untranslatedIcon", icon )
  515.        
  516.         return( listitem )
  517.                
  518.     def _get_icon_overrides( self, tree, item, content, setToDefault = True ):
  519.         if tree is None:
  520.             return item
  521.            
  522.         oldicon = None
  523.         newicon = item.getProperty( "icon" )
  524.         for elem in tree.findall( "icon" ):
  525.             if oldicon is None:
  526.                 if ("labelID" in elem.attrib and elem.attrib.get( "labelID" ) == item.getProperty( "tempLabelID" )) or ("image" in elem.attrib and elem.attrib.get( "image" ) == item.getProperty( "icon" )):
  527.                     # LabelID matched
  528.                     if "grouping" in elem.attrib:
  529.                         if elem.attrib.get( "grouping" ) == content:
  530.                             # Group also matches - change icon
  531.                             oldicon = item.getProperty( "icon" )
  532.                             newicon = elem.text
  533.                     elif "group" not in elem.attrib:
  534.                         # No group - change icon
  535.                         oldicon = item.getProperty( "icon" )
  536.                         newicon = elem.text
  537.                        
  538.         # If the icon doesn't exist, set icon to default
  539.         setDefault = False
  540.         if not xbmc.skinHasImage( newicon ) and setToDefault == True:
  541.             oldicon = item.getProperty( "icon" )
  542.             icon = "DefaultShortcut.png"
  543.             setDefault == True
  544.  
  545.         if oldicon is not None:
  546.             # we found an icon override
  547.             item.setProperty( "icon", newicon )
  548.             item.setIconImage( newicon )
  549.            
  550.         if setDefault == True:
  551.             item = self._get_icon_overrides( tree, item, content, False )
  552.            
  553.         return item
  554.  
  555.     # ===================================
  556.     # === LOAD VIDEO LIBRARY HEIRACHY ===
  557.     # ===================================
  558.    
  559.     def videolibrary( self ):
  560.         if self.loadedVideoLibrary == True:
  561.             # The List has already been populated, return it
  562.             return self.loadedVideoLibrary
  563.         elif self.loadedVideoLibrary == "Loading":
  564.             # The list is currently being populated, wait and then return it
  565.             count = 0
  566.             while count < 20:
  567.                 xbmc.sleep( 100 )
  568.                 count += 1
  569.                 if self.loadedVideoLibrary == True:
  570.                     return self.loadedVideoLibrary
  571.         else:
  572.             # We're going to populate the list
  573.             self.loadedVideoLibrary = "Loading"
  574.            
  575.         # Try loading custom nodes first
  576.         try:
  577.             if self._parse_videolibrary( "custom" ) == False:
  578.                 log( "Failed to load custom video nodes" )
  579.                 self._parse_videolibrary( "default" )
  580.         except:
  581.             log( "Failed to load custom video nodes" )
  582.             print_exc()
  583.             try:
  584.                 # Try loading default nodes
  585.                 self._parse_videolibrary( "default" )
  586.             except:
  587.                 # Empty library
  588.                 log( "Failed to load default video nodes" )
  589.                 print_exc()
  590.                
  591.         self.loadedVideoLibrary = True
  592.         return self.loadedVideoLibrary
  593.        
  594.     def _parse_videolibrary( self, type ):
  595.         #items = {"video":[], "movies":[], "tvshows":[], "musicvideos":[], "custom":{}}
  596.         items = {}
  597.  
  598.         rootdir = os.path.join( xbmc.translatePath( "special://profile".decode('utf-8') ), "library", "video" )
  599.         if type == "custom":
  600.             log('Listing custom video nodes...')
  601.         else:
  602.             rootdir = os.path.join( xbmc.translatePath( "special://xbmc".decode('utf-8') ), "system", "library", "video" )
  603.             log( "Listing default video nodes..." )
  604.            
  605.         nodes = NODE.get_video_nodes( rootdir )
  606.         if nodes == False or len( nodes ) == 0:
  607.             return False
  608.        
  609.         videos = []
  610.        
  611.         for key in nodes:
  612.             # 0 = Label
  613.             # 1 = Icon
  614.             # 2 = Path
  615.             # 3 = Type
  616.             # 4 = Order
  617.             if nodes[ key ][ 3 ] == "folder":
  618.                 videos.append( self._create( [ "||FOLDER||%s" % ( nodes[ key ][ 2 ] ), nodes[ key ][ 0 ], nodes[ key ][ 3 ], { "icon": nodes[ key ][ 1 ] } ] ) )
  619.             elif nodes[ key ][ 3 ] == "grouped":
  620.                 videos.append( self._create( [ "||FOLDER||%s" % ( nodes[ key ][ 2 ] ), nodes[ key ][ 0 ], nodes[ key ][ 3 ], { "icon": nodes[ key ][ 1 ] } ] ) )
  621.             else:
  622.                 videos.append( self._create( [ "ActivateWindow(10025,%s,Return)" %( nodes[ key ][ 2 ] ) , nodes[ key ][ 0 ], nodes[ key ][ 3 ], { "icon": nodes[ key ][ 1 ] } ] ) )
  623.            
  624.         self.addToDictionary( "video", videos )
  625.    
  626.  
  627.     # ============================
  628.     # === LOAD OTHER LIBRARIES ===
  629.     # ============================
  630.                
  631.     def common( self ):
  632.         if self.loadedCommon == True:
  633.             return True
  634.         elif self.loadedCommon == "Loading":
  635.             # The list is currently being populated, wait and then return it
  636.             count = 0
  637.             while count < 20:
  638.                 xbmc.sleep( 100 )
  639.                 count += 1
  640.                 if self.loadedCommon == True:
  641.                     return True
  642.         else:
  643.             # We're going to populate the list
  644.             self.loadedCommon = "Loading"
  645.        
  646.         listitems = []
  647.         log('Listing xbmc common items...')
  648.        
  649.         # Videos, Movies, TV Shows, Live TV, Music, Music Videos, Pictures, Weather, Programs,
  650.         # Play dvd, eject tray
  651.         # Settings, File Manager, Profiles, System Info
  652.         try:
  653.             listitems.append( self._create(["ActivateWindow(Videos)", "10006", "32034", {"icon": "DefaultVideo.png"} ]) )
  654.             listitems.append( self._create(["ActivateWindow(Videos,videodb://movies/titles/,return)", "342", "32034", {"icon": "DefaultMovies.png"} ]) )
  655.             listitems.append( self._create(["ActivateWindow(Videos,videodb://tvshows/titles/,return)", "20343", "32034", {"icon": "DefaultTVShows.png"} ]) )
  656.  
  657.             if __xbmcversion__ == "13":
  658.                 listitems.append( self._create(["ActivateWindowAndFocus(MyPVR,34,0 ,13,0)", "32022", "32034", {"icon": "DefaultTVShows.png"} ]) )
  659.             else:
  660.                 listitems.append( self._create(["ActivateWindow(TVGuide)", "32022", "32034", {"icon": "DefaultTVShows.png"} ]) )
  661.                 listitems.append( self._create(["ActivateWindow(RadioGuide)", "32087", "32034", {"icon": "DefaultTVShows.png"} ]) )
  662.                
  663.             listitems.append( self._create(["ActivateWindow(Music)", "10005", "32034", {"icon": "DefaultMusicAlbums.png"} ]) )
  664.             listitems.append( self._create(["ActivateWindow(Videos,videodb://musicvideos/titles/,return)", "20389", "32034", {"icon": "DefaultMusicVideos.png"} ] ) )
  665.             listitems.append( self._create(["ActivateWindow(Pictures)", "10002", "32034", {"icon": "DefaultPicture.png"} ] ) )
  666.             listitems.append( self._create(["ActivateWindow(Weather)", "12600", "32034", {} ]) )
  667.             listitems.append( self._create(["ActivateWindow(Programs,Addons,return)", "10001", "32034", {"icon": "DefaultProgram.png"} ] ) )
  668.  
  669.             listitems.append( self._create(["XBMC.PlayDVD()", "32032", "32034", {"icon": "DefaultDVDFull.png"} ] ) )
  670.             listitems.append( self._create(["EjectTray()", "32033", "32034", {"icon": "DefaultDVDFull.png"} ] ) )
  671.                    
  672.             listitems.append( self._create(["ActivateWindow(Settings)", "10004", "32034", {} ]) )
  673.             listitems.append( self._create(["ActivateWindow(FileManager)", "7", "32034", {"icon": "DefaultFolder.png"} ] ) )
  674.             listitems.append( self._create(["ActivateWindow(Profiles)", "13200", "32034", {"icon": "UnknownUser.png"} ] ) )
  675.             listitems.append( self._create(["ActivateWindow(SystemInfo)", "10007", "32034", {} ]) )
  676.            
  677.             listitems.append( self._create(["ActivateWindow(Favourites)", "1036", "32034", {} ]) )
  678.         except:
  679.             log( "Failed to load common XBMC shortcuts" )
  680.             print_exc()
  681.             listitems = []
  682.            
  683.         self.addToDictionary( "common", listitems )
  684.        
  685.         self.loadedCommon = True
  686.        
  687.         return self.loadedCommon
  688.        
  689.     def more( self ):
  690.         if self.loadedMoreCommands == True:
  691.             # The List has already been populated, return it
  692.             return True
  693.         elif self.loadedMoreCommands == "Loading":
  694.             # The list is currently being populated, wait and then return it
  695.             count = 0
  696.             while count < 20:
  697.                 xbmc.sleep( 100 )
  698.                 count += 1
  699.                 if self.loadedMoreCommands == True:
  700.                     return True
  701.         else:
  702.             # We're going to populate the list
  703.             self.loadedMoreCommands = "Loading"
  704.  
  705.         try:
  706.             listitems = []
  707.             log( 'Listing more XBMC commands...' )
  708.            
  709.             listitems.append( self._create(["Reboot", "13013", "32054", {} ]) )
  710.             listitems.append( self._create(["ShutDown", "13005", "32054", {} ]) )
  711.             listitems.append( self._create(["PowerDown", "13016", "32054", {} ]) )
  712.             listitems.append( self._create(["Quit", "13009", "32054", {} ]) )
  713.             listitems.append( self._create(["Hibernate", "13010", "32054", {} ]) )
  714.             listitems.append( self._create(["Suspend", "13011", "32054", {} ]) )
  715.             listitems.append( self._create(["AlarmClock(shutdowntimer,XBMC.Shutdown())", "19026", "32054", {} ]) )
  716.             listitems.append( self._create(["CancelAlarm(shutdowntimer)", "20151", "32054", {} ]) )
  717.             if xbmc.getCondVisibility( "System.HasLoginScreen" ):
  718.                 listitems.append( self._create(["System.LogOff", "20126", "32054", {} ]) )
  719.             listitems.append( self._create(["ActivateScreensaver", "360", "32054", {} ]) )
  720.             listitems.append( self._create(["Minimize", "13014", "32054", {} ]) )
  721.  
  722.             listitems.append( self._create(["Mastermode", "20045", "32054", {} ]) )
  723.            
  724.             listitems.append( self._create(["RipCD", "600", "32054", {} ]) )
  725.            
  726.             if __xbmcversion__ == "13":
  727.                 listitems.append( self._create(["UpdateLibrary(video)", "32046", "32054", {} ]) )
  728.                 listitems.append( self._create(["UpdateLibrary(music)", "32047", "32054", {} ]) )
  729.             else:
  730.                 listitems.append( self._create(["UpdateLibrary(video,,true)", "32046", "32054", {} ]) )
  731.                 listitems.append( self._create(["UpdateLibrary(music,,true)", "32047", "32054", {} ]) )
  732.            
  733.             if __xbmcversion__ == "13":
  734.                 listitems.append( self._create(["CleanLibrary(video)", "32055", "32054", {} ]) )
  735.                 listitems.append( self._create(["CleanLibrary(music)", "32056", "32054", {} ]) )
  736.             else:
  737.                 listitems.append( self._create(["CleanLibrary(video,true)", "32055", "32054", {} ]) )
  738.                 listitems.append( self._create(["CleanLibrary(music,true)", "32056", "32054", {} ]) )
  739.            
  740.             self.addToDictionary( "commands", listitems )
  741.         except:
  742.             log( "Failed to load more XBMC commands" )
  743.             print_exc()
  744.            
  745.         self.loadedMoreCommands = True
  746.         return self.loadedMoreCommands
  747.        
  748.     def settings( self ):
  749.         if self.loadedSettings == True:
  750.             # The List has already been populated, return it
  751.             return True
  752.         elif self.loadedSettings == "Loading":
  753.             # The list is currently being populated, wait and then return it
  754.             count = 0
  755.             while count < 20:
  756.                 xbmc.sleep( 100 )
  757.                 count += 1
  758.                 if self.loadedSettings == True:
  759.                     return True
  760.         else:
  761.             # We're going to populate the list
  762.             self.loadedSettings = "Loading"
  763.  
  764.         try:
  765.             listitems = []
  766.             log( 'Listing XBMC settings...' )
  767.            
  768.             listitems.append( self._create(["ActivateWindow(Settings)", "10004", "10004", {} ]) )
  769.            
  770.             listitems.append( self._create(["ActivateWindow(AppearanceSettings)", "480", "10004", {} ]) )
  771.             listitems.append( self._create(["ActivateWindow(VideosSettings)", "3", "10004", {} ]) )
  772.             listitems.append( self._create(["ActivateWindow(PVRSettings)", "19020", "10004", {} ]) )
  773.             listitems.append( self._create(["ActivateWindow(MusicSettings)", "2", "10004", {} ]) )
  774.             listitems.append( self._create(["ActivateWindow(PicturesSettings)", "1", "10004", {} ]) )
  775.             listitems.append( self._create(["ActivateWindow(WeatherSettings)", "8", "10004", {} ]) )
  776.             listitems.append( self._create(["ActivateWindow(AddonBrowser)", "24001", "10004", {} ]) )
  777.             listitems.append( self._create(["ActivateWindow(ServiceSettings)", "14036", "10004", {} ]) )
  778.             listitems.append( self._create(["ActivateWindow(SystemSettings)", "13000", "10004", {} ]) )
  779.             listitems.append( self._create(["ActivateWindow(SkinSettings)", "20077", "10004", {} ]) )
  780.            
  781.             self.addToDictionary( "settings", listitems )
  782.         except:
  783.             log( "Failed to load more XBMC settings" )
  784.             print_exc()
  785.            
  786.         self.loadedSettings = True
  787.         return self.loadedSettings
  788.        
  789.    
  790.     def pvrlibrary( self ):
  791.         if self.loadedPVRLibrary == True:
  792.             # The List has already been populated, return it
  793.             return self.loadedPVRLibrary
  794.         elif self.loadedPVRLibrary == "Loading":
  795.             # The list is currently being populated, wait and then return it
  796.             count = 0
  797.             while count < 20:
  798.                 xbmc.sleep( 100 )
  799.                 count += 1
  800.                 if self.loadedPVRLibrary == True:
  801.                     return self.loadedPVRLibrary
  802.         else:
  803.             # We're going to populate the list
  804.             self.loadedPVRLibrary = "Loading"
  805.  
  806.         try:
  807.             listitems = []
  808.             log('Listing pvr library...')
  809.            
  810.             # PVR
  811.             if __xbmcversion__ == "13":
  812.                 listitems.append( self._create(["ActivateWindowAndFocus(MyPVR,32,0 ,11,0)", "19023", "32017", {"icon": "DefaultTVShows.png"} ] ) )
  813.                 listitems.append( self._create(["ActivateWindowAndFocus(MyPVR,33,0 ,12,0)", "19024", "32017", {"icon": "DefaultTVShows.png"} ] ) )
  814.                 listitems.append( self._create(["ActivateWindowAndFocus(MyPVR,31,0 ,10,0)", "19069", "32017", {"icon": "DefaultTVShows.png"} ] ) )
  815.                 listitems.append( self._create(["ActivateWindowAndFocus(MyPVR,34,0 ,13,0)", "19163", "32017", {"icon": "DefaultTVShows.png"} ] ) )
  816.                 listitems.append( self._create(["ActivateWindowAndFocus(MyPVR,35,0 ,14,0)", "32023", "32017", {"icon": "DefaultTVShows.png"} ] ) )
  817.  
  818.                 listitems.append( self._create(["PlayPvrTV", "32066", "32017", {"icon": "DefaultTVShows.png"} ] ) )
  819.                 listitems.append( self._create(["PlayPvrRadio", "32067", "32017", {"icon": "DefaultTVShows.png"} ] ) )
  820.                 listitems.append( self._create(["PlayPvr", "32068", "32017", {"icon": "DefaultTVShows.png"} ] ) )
  821.             else:
  822.                 listitems.append( self._create(["ActivateWindow(TVChannels)", "19019", "32017", {"icon": "DefaultTVShows.png"} ] ) )
  823.                 listitems.append( self._create(["ActivateWindow(TVGuide)", "22020", "32017", {"icon": "DefaultTVShows.png"} ] ) )
  824.                 listitems.append( self._create(["ActivateWindow(TVRecordings)", "19163", "32017", {"icon": "DefaultTVShows.png"} ] ) )
  825.                 listitems.append( self._create(["ActivateWindow(TVTimers)", "19040", "32017", {"icon": "DefaultTVShows.png"} ] ) )
  826.                 listitems.append( self._create(["ActivateWindow(TVSearch)", "137", "32017", {"icon": "DefaultTVShows.png"} ] ) )
  827.                
  828.                 listitems.append( self._create(["PlayPvrTV", "32066", "32017", {"icon": "DefaultTVShows.png"} ] ) )
  829.                 listitems.append( self._create(["PlayPvr", "32068", "32017", {"icon": "DefaultTVShows.png"} ] ) )
  830.  
  831.             self.addToDictionary( "pvr", listitems )            
  832.            
  833.             # Add tv channels
  834.             listitems = []
  835.             json_query = xbmc.executeJSONRPC('{ "jsonrpc": "2.0", "id": 0, "method": "PVR.GetChannels", "params": { "channelgroupid": "alltv", "properties": ["thumbnail", "channeltype", "hidden", "locked", "channel", "lastplayed"] } }')
  836.             json_query = unicode(json_query, 'utf-8', errors='ignore')
  837.             json_response = simplejson.loads(json_query)
  838.            
  839.             # Add all directories returned by the json query
  840.             if json_response.has_key('result') and json_response['result'].has_key('channels') and json_response['result']['channels'] is not None:
  841.                 for item in json_response['result']['channels']:
  842.                     listitems.append( self._create(["pvr-channel://" + str( item['channelid'] ), item['label'], "::SCRIPT::32076", {"icon": "DefaultTVShows.png", "thumb": item[ "thumbnail"]}]) )
  843.            
  844.             self.addToDictionary( "pvr-tv", listitems )
  845.            
  846.             # Add radio channels
  847.             listitems = []
  848.             json_query = xbmc.executeJSONRPC('{ "jsonrpc": "2.0", "id": 0, "method": "PVR.GetChannels", "params": { "channelgroupid": "allradio", "properties": ["thumbnail", "channeltype", "hidden", "locked", "channel", "lastplayed"] } }')
  849.             json_query = unicode(json_query, 'utf-8', errors='ignore')
  850.             json_response = simplejson.loads(json_query)
  851.            
  852.             # Add all directories returned by the json query
  853.             if json_response.has_key('result') and json_response['result'].has_key('channels') and json_response['result']['channels'] is not None:
  854.                 for item in json_response['result']['channels']:
  855.                     listitems.append( self._create(["pvr-channel://" + str( item['channelid'] ), item['label'], "::SCRIPT::32077", {"icon": "DefaultTVShows.png", "thumb": item[ "thumbnail"]}]) )
  856.            
  857.             log( "Found " + str( len( listitems ) ) + " radio channels" )
  858.             self.addToDictionary( "pvr-radio", listitems )
  859.  
  860.         except:
  861.             log( "Failed to load pvr library" )
  862.             print_exc()
  863.  
  864.         self.loadedPVRLibrary = True
  865.         return self.loadedPVRLibrary
  866.        
  867.     def radiolibrary( self ):
  868.         if self.loadedRadioLibrary == True:
  869.             # The List has already been populated, return it
  870.             return self.loadedRadioLibrary
  871.         elif self.loadedRadioLibrary == "Loading":
  872.             # The list is currently being populated, wait and then return it
  873.             count = 0
  874.             while count < 20:
  875.                 xbmc.sleep( 100 )
  876.                 count += 1
  877.                 if self.loadedRadioLibrary == True:
  878.                     return self.loadedRadioLibrary
  879.         else:
  880.             # We're going to populate the list
  881.             self.loadedRadioLibrary = "Loading"
  882.  
  883.         try:
  884.             listitems = []
  885.             log('Listing pvr-radio library...')
  886.            
  887.             # PVR
  888.             listitems.append( self._create(["ActivateWindow(RadioChannels)", "19019", "32087", {"icon": "DefaultAudio.png"} ] ) )
  889.             listitems.append( self._create(["ActivateWindow(RadioGuide)", "22020", "32087", {"icon": "DefaultAudio.png"} ] ) )
  890.             listitems.append( self._create(["ActivateWindow(RadioRecordings)", "19163", "32087", {"icon": "DefaultAudio.png"} ] ) )
  891.             listitems.append( self._create(["ActivateWindow(RadioTimers)", "19040", "32087", {"icon": "DefaultAudio.png"} ] ) )
  892.             listitems.append( self._create(["ActivateWindow(RadioSearch)", "137", "32087", {"icon": "DefaultAudio.png"} ] ) )
  893.            
  894.             listitems.append( self._create(["PlayPvrRadio", "32067", "32087", {"icon": "DefaultAudio.png"} ] ) )
  895.             listitems.append( self._create(["PlayPvr", "32068", "32087", {"icon": "DefaultAudio.png"} ] ) )
  896.  
  897.             self.addToDictionary( "radio", listitems )            
  898.  
  899.         except:
  900.             log( "Failed to load pvr-radio library" )
  901.             print_exc()
  902.  
  903.         self.loadedRadioLibrary = True
  904.         return self.loadedRadioLibrary
  905.        
  906.     def musiclibrary( self ):
  907.         if self.loadedMusicLibrary == True:
  908.             # The List has already been populated, return it
  909.             return self.loadedMusicLibrary
  910.         elif self.loadedMusicLibrary == "Loading":
  911.             # The list is currently being populated, wait and then return it
  912.             count = 0
  913.             while count < 20:
  914.                 xbmc.sleep( 100 )
  915.                 count += 1
  916.                 if loadedMusicLibrary == True:
  917.                     return self.loadedMusicLibrary
  918.         else:
  919.             # We're going to populate the list
  920.             self.loadedMusicLibrary = "Loading"
  921.  
  922.         try:
  923.             listitems = []
  924.             log('Listing music library...')
  925.                        
  926.             # Music
  927.             listitems.append( self._create(["ActivateWindow(MusicFiles)", "744", "32019", {"icon": "DefaultFolder.png"} ]) )
  928.             listitems.append( self._create(["ActivateWindow(MusicLibrary,MusicLibrary,return)", "15100", "32019", {"icon": "DefaultFolder.png"} ] ) )
  929.             listitems.append( self._create(["ActivateWindow(MusicLibrary,Genres,return)", "135", "32019", {"icon": "DefaultMusicGenres.png"} ] ) )
  930.             listitems.append( self._create(["ActivateWindow(MusicLibrary,Artists,return)", "133", "32019", {"icon": "DefaultMusicArtists.png"} ] ) )
  931.             listitems.append( self._create(["ActivateWindow(MusicLibrary,Albums,return)", "132", "32019", {"icon": "DefaultMusicAlbums.png"} ] ) )
  932.             listitems.append( self._create(["ActivateWindow(MusicLibrary,Songs,return)", "134", "32019", {"icon": "DefaultMusicSongs.png"} ] ) )
  933.             listitems.append( self._create(["ActivateWindow(MusicLibrary,Years,return)", "652", "32019", {"icon": "DefaultMusicYears.png"} ] ) )
  934.             listitems.append( self._create(["ActivateWindow(MusicLibrary,Top100,return)", "271", "32019", {"icon": "DefaultMusicTop100.png"} ] ) )
  935.             listitems.append( self._create(["ActivateWindow(MusicLibrary,Top100Songs,return)", "10504", "32019", {"icon": "DefaultMusicTop100Songs.png"} ] ) )
  936.             listitems.append( self._create(["ActivateWindow(MusicLibrary,Top100Albums,return)", "10505", "32019", {"icon": "DefaultMusicTop100Albums.png"} ] ) )
  937.             listitems.append( self._create(["ActivateWindow(MusicLibrary,RecentlyAddedAlbums,return)", "359", "32019", {"icon": "DefaultMusicRecentlyAdded.png"} ] ) )
  938.             listitems.append( self._create(["ActivateWindow(MusicLibrary,RecentlyPlayedAlbums,return)", "517", "32019", {"icon": "DefaultMusicRecentlyPlayed.png"} ] ) )
  939.             listitems.append( self._create(["ActivateWindow(MusicLibrary,Playlists,return)", "136", "32019", {"icon": "DefaultMusicPlaylists.png"} ] ) )
  940.            
  941.             # Do a JSON query for upnp sources (so that they'll show first time the user asks to see them)
  942.             if self.loadedUPNP == False:
  943.                 json_query = xbmc.executeJSONRPC('{ "jsonrpc": "2.0", "id": 0, "method": "Files.GetDirectory", "params": { "properties": ["title", "file", "thumbnail"], "directory": "upnp://", "media": "files" } }')
  944.                 self.loadedUPNP = True
  945.                
  946.             self.addToDictionary( "music", listitems )
  947.         except:
  948.             log( "Failed to load music library" )
  949.             print_exc()
  950.  
  951.         self.loadedMusicLibrary = True
  952.         return self.loadedMusicLibrary
  953.    
  954.     def librarysources( self ):
  955.         if self.loadedLibrarySources == True:
  956.             # The List has already been populated, return it
  957.             return self.loadedLibrarySources
  958.         elif self.loadedLibrarySources == "Loading":
  959.             # The list is currently being populated, wait and then return it
  960.             count = 0
  961.             while count < 20:
  962.                 xbmc.sleep( 100 )
  963.                 count += 1
  964.                 if self.loadedLibrarySources == True:
  965.                     return self.loadedLibrarySources
  966.         else:
  967.             # We're going to populate the list
  968.             self.loadedLibrarySources = "Loading"
  969.            
  970.         log('Listing library sources...')
  971.         # Add video sources
  972.         listitems = []
  973.         json_query = xbmc.executeJSONRPC('{ "jsonrpc": "2.0", "id": 0, "method": "Files.GetSources", "params": { "media": "video" } }')
  974.         json_query = unicode(json_query, 'utf-8', errors='ignore')
  975.         json_response = simplejson.loads(json_query)
  976.            
  977.         # Add all directories returned by the json query
  978.         if json_response.has_key('result') and json_response['result'].has_key('sources') and json_response['result']['sources'] is not None:
  979.             for item in json_response['result']['sources']:
  980.                 listitems.append( self._create(["||SOURCE||" + item['file'], item['label'], "32069", {"icon": "DefaultFolder.png"} ]) )
  981.         self.addToDictionary( "videosources", listitems )
  982.        
  983.         log( " - " + str( len( listitems ) ) + " video sources" )
  984.                
  985.         # Add audio sources
  986.         listitems = []
  987.         json_query = xbmc.executeJSONRPC('{ "jsonrpc": "2.0", "id": 0, "method": "Files.GetSources", "params": { "media": "music" } }')
  988.         json_query = unicode(json_query, 'utf-8', errors='ignore')
  989.         json_response = simplejson.loads(json_query)
  990.            
  991.         # Add all directories returned by the json query
  992.         if json_response.has_key('result') and json_response['result'].has_key('sources') and json_response['result']['sources'] is not None:
  993.             for item in json_response['result']['sources']:
  994.                 listitems.append( self._create(["||SOURCE||" + item['file'], item['label'], "32073", {"icon": "DefaultFolder.png"} ]) )
  995.         self.addToDictionary( "musicsources", listitems )
  996.        
  997.         log( " - " + str( len( listitems ) ) + " audio sources" )
  998.        
  999.         # Add picture sources
  1000.         listitems = []
  1001.         json_query = xbmc.executeJSONRPC('{ "jsonrpc": "2.0", "id": 0, "method": "Files.GetSources", "params": { "media": "pictures" } }')
  1002.         json_query = unicode(json_query, 'utf-8', errors='ignore')
  1003.         json_response = simplejson.loads(json_query)
  1004.            
  1005.         # Add all directories returned by the json query
  1006.         if json_response.has_key('result') and json_response['result'].has_key('sources') and json_response['result']['sources'] is not None:
  1007.             for item in json_response['result']['sources']:
  1008.                 listitems.append( self._create(["||SOURCE||" + item['file'], item['label'], "32089", {"icon": "DefaultFolder.png"} ]) )
  1009.         self.addToDictionary( "picturesources", listitems )
  1010.        
  1011.         log( " - " + str( len( listitems ) ) + " picture sources" )
  1012.        
  1013.         self.loadedLibrarySources = True
  1014.         return self.loadedLibrarySources
  1015.            
  1016.     def playlists( self ):
  1017.         if self.loadedPlaylists == True:
  1018.             # The List has already been populated, return it
  1019.             return self.loadedPlaylists
  1020.         elif self.loadedPlaylists == "Loading":
  1021.             # The list is currently being populated, wait and then return it
  1022.             count = 0
  1023.             while count < 20:
  1024.                 xbmc.sleep( 100 )
  1025.                 count += 1
  1026.                 if self.loadedPlaylists == True:
  1027.                     return self.loadedPlaylists
  1028.         else:
  1029.             # We're going to populate the list
  1030.             self.loadedPlaylists = "Loading"
  1031.            
  1032.         try:
  1033.             audiolist = []
  1034.             videolist = []
  1035.             log('Loading playlists...')
  1036.             paths = [['special://videoplaylists/','32004','VideoLibrary'], ['special://musicplaylists/','32005','MusicLibrary'], ["special://skin/playlists/",'32059',None], ["special://skin/extras/",'32059',None]]
  1037.             for path in paths:
  1038.                 count = 0
  1039.                 for root, subdirs, files in kodiwalk( path[0], stringForce = "special://skin/" ):
  1040.                     for file in files:
  1041.                         playlist = file['path']
  1042.                         label = file['label']
  1043.                         playlistfile = xbmc.translatePath( playlist ).decode('utf-8')
  1044.                         mediaLibrary = path[2]
  1045.                        
  1046.                         if playlist.endswith( '.xsp' ):
  1047.                             contents = xbmcvfs.File(playlistfile, 'r')
  1048.                             contents_data = contents.read().decode('utf-8')
  1049.                             xmldata = xmltree.fromstring(contents_data.encode('utf-8'))
  1050.                             for line in xmldata.getiterator():
  1051.                                 if line.tag == "smartplaylist":
  1052.                                     mediaType = line.attrib['type']
  1053.                                     if mediaType == "movies" or mediaType == "tvshows" or mediaType == "seasons" or mediaType == "episodes" or mediaType == "musicvideos" or mediaType == "sets":
  1054.                                         mediaLibrary = "VideoLibrary"
  1055.                                     elif mediaType == "albums" or mediaType == "artists" or mediaType == "songs":
  1056.                                         mediaLibrary = "MusicLibrary"                                
  1057.                                    
  1058.                                 if line.tag == "name" and mediaLibrary is not None:
  1059.                                     name = line.text
  1060.                                     if not name:
  1061.                                         name = label
  1062.                                     # Create a list item
  1063.                                     listitem = self._create(["::PLAYLIST::", name, path[1], {"icon": "DefaultPlaylist.png"} ])
  1064.                                     listitem.setProperty( "action-play", "PlayMedia(" + playlist + ")" )
  1065.                                     listitem.setProperty( "action-show", "ActivateWindow(" + mediaLibrary + "," + playlist + ",return)".encode( 'utf-8' ) )
  1066.                                    
  1067.                                     if mediaLibrary == "VideoLibrary":
  1068.                                         videolist.append( listitem )
  1069.                                     else:
  1070.                                         audiolist.append( listitem )
  1071.                                     # Save it for the widgets list
  1072.                                     self.widgetPlaylistsList.append( [playlist, "(" + __language__( int( path[1] ) ) + ") " + name, name] )
  1073.                                    
  1074.                                     count += 1
  1075.                                     break
  1076.                         elif playlist.endswith( '.m3u' ):
  1077.                             name = label
  1078.                             listitem = self._create( ["::PLAYLIST::", name, "32005", {"icon": "DefaultPlaylist.png"} ] )
  1079.                             listitem.setProperty( "action-play", "PlayMedia(" + playlist + ")" )
  1080.                             listitem.setProperty( "action-show", "ActivateWindow(MusicLibrary," + playlist + ",return)".encode( 'utf-8' ) )
  1081.                            
  1082.                             audiolist.append( listitem )
  1083.                            
  1084.                             count += 1
  1085.                            
  1086.                 log( " - [" + path[0] + "] " + str( count ) + " playlists found" )
  1087.            
  1088.             self.addToDictionary( "playlist-video", videolist )
  1089.             self.addToDictionary( "playlist-audio", audiolist )
  1090.            
  1091.         except:
  1092.             log( "Failed to load playlists" )
  1093.             print_exc()
  1094.            
  1095.         self.loadedPlaylists = True
  1096.         return self.loadedPlaylists
  1097.                
  1098.     def scriptPlaylists( self ):
  1099.         # Lazy loading of random source playlists auto-generated by the script
  1100.         # (loaded lazily as these can be created/deleted after gui has loaded)
  1101.         returnPlaylists = []
  1102.         try:
  1103.             log('Loading script generated playlists...')
  1104.             path = "special://profile/addon_data/" + __addonid__ + "/"
  1105.             count = 0
  1106.             for root, subdirs, files in kodiwalk( path ):
  1107.                 for file in files:
  1108.                     playlist = file['path']
  1109.                     label = file['label']
  1110.                     playlistfile = xbmc.translatePath( playlist ).decode('utf-8')
  1111.                    
  1112.                     if playlist.endswith( '-randomversion.xsp' ):
  1113.                         contents = xbmcvfs.File(playlistfile, 'r')
  1114.                         contents_data = contents.read().decode('utf-8')
  1115.                         xmldata = xmltree.fromstring(contents_data.encode('utf-8'))
  1116.                         for line in xmldata.getiterator():                              
  1117.                             if line.tag == "name":
  1118.                                    
  1119.                                 # Save it for the widgets list
  1120.                                 # TO-DO - Localize display name
  1121.                                 returnPlaylists.append( [playlist.encode( 'utf-8' ), "(Source) " + name, name] )
  1122.                                
  1123.                                 count += 1
  1124.                                 break
  1125.                        
  1126.             log( " - [" + path[0] + "] " + str( count ) + " playlists found" )
  1127.            
  1128.         except:
  1129.             log( "Failed to load script generated playlists" )
  1130.             print_exc()
  1131.            
  1132.         return returnPlaylists
  1133.                
  1134.     def favourites( self ):
  1135.         if self.loadedFavourites == True:
  1136.             # The List has already been populated, return it
  1137.             return self.loadedFavourites
  1138.         elif self.loadedFavourites == "Loading":
  1139.             # The list is currently being populated, wait and then return it
  1140.             count = 0
  1141.             while count < 20:
  1142.                 xbmc.sleep( 100 )
  1143.                 count += 1
  1144.                 if self.loadedFavourites == True:
  1145.                     return self.loadedFavourites
  1146.         else:
  1147.             # We're going to populate the list
  1148.             self.loadedFavourites = "Loading"
  1149.            
  1150.         try:
  1151.             log('Loading favourites...')
  1152.            
  1153.             listitems = []
  1154.             listing = None
  1155.            
  1156.             fav_file = xbmc.translatePath( 'special://profile/favourites.xml' ).decode("utf-8")
  1157.             if xbmcvfs.exists( fav_file ):
  1158.                 doc = parse( fav_file )
  1159.                 listing = doc.documentElement.getElementsByTagName( 'favourite' )
  1160.             else:
  1161.                 # No favourites file found
  1162.                 self.addToDictionary( "favourite", [] )
  1163.                 self.loadedFavourites = True
  1164.                 return True
  1165.                
  1166.             for count, favourite in enumerate(listing):
  1167.                 name = favourite.attributes[ 'name' ].nodeValue
  1168.                 path = favourite.childNodes [ 0 ].nodeValue
  1169.                 if ('RunScript' not in path) and ('StartAndroidActivity' not in path) and not (path.endswith(',return)') ):
  1170.                     path = path.rstrip(')')
  1171.                     path = path + ',return)'
  1172.  
  1173.                 try:
  1174.                     thumb = favourite.attributes[ 'thumb' ].nodeValue
  1175.                    
  1176.                 except:
  1177.                     thumb = None
  1178.                
  1179.                 listitems.append( self._create( [ path, name, "32006", { "icon": "DefaultFolder.png", "thumb": thumb} ] ) )
  1180.            
  1181.             log( " - " + str( len( listitems ) ) + " favourites found" )
  1182.            
  1183.             self.addToDictionary( "favourite", listitems )
  1184.            
  1185.         except:
  1186.             log( "Failed to load favourites" )
  1187.             print_exc()
  1188.            
  1189.         self.loadedFavourites = True            
  1190.         return self.loadedFavourites
  1191.        
  1192.     def addons( self ):
  1193.         if self.loadedAddOns == True:
  1194.             # The List has already been populated, return it
  1195.             return self.loadedAddOns
  1196.         elif self.loadedAddOns == "Loading":
  1197.             # The list is currently being populated, wait and then return it
  1198.             count = 0
  1199.             while count < 20:
  1200.                 xbmc.sleep( 100 )
  1201.                 count += 1
  1202.                 if self.loadedAddOns == True:
  1203.                     return self.loadedAddOns
  1204.         else:
  1205.             # We're going to populate the list
  1206.             self.loadedAddOns = "Loading"
  1207.            
  1208.         try:
  1209.             log( 'Loading add-ons' )
  1210.                        
  1211.             contenttypes = ["executable", "video", "audio", "image"]
  1212.             for contenttype in contenttypes:
  1213.                 listitems = {}
  1214.                 if contenttype == "executable":
  1215.                     contentlabel = __language__(32009)
  1216.                     shortcutType = "::SCRIPT::32009"
  1217.                 elif contenttype == "video":
  1218.                     contentlabel = __language__(32010)
  1219.                     shortcutType = "::SCRIPT::32010"
  1220.                 elif contenttype == "audio":
  1221.                     contentlabel = __language__(32011)
  1222.                     shortcutType = "::SCRIPT::32011"
  1223.                 elif contenttype == "image":
  1224.                     contentlabel = __language__(32012)
  1225.                     shortcutType = "::SCRIPT::32012"
  1226.                    
  1227.                 json_query = xbmc.executeJSONRPC('{ "jsonrpc": "2.0", "id": 0, "method": "Addons.Getaddons", "params": { "content": "%s", "properties": ["name", "path", "thumbnail", "enabled"] } }' % contenttype)
  1228.                 json_query = unicode(json_query, 'utf-8', errors='ignore')
  1229.                 json_response = simplejson.loads(json_query)
  1230.                
  1231.                 if json_response.has_key('result') and json_response['result'].has_key('addons') and json_response['result']['addons'] is not None:
  1232.                     for item in json_response['result']['addons']:
  1233.                         if item['enabled'] == True:                            
  1234.                             path = "RunAddOn(" + item['addonid'].encode('utf-8') + ")"
  1235.                             action = None
  1236.                            
  1237.                             # If this is a plugin, mark that we can browse it
  1238.                             if item['addonid'].startswith( "plugin." ):
  1239.                                 path = "||BROWSE||" + item['addonid'].encode('utf-8')
  1240.                                 action = "RunAddOn(" + item['addonid'].encode('utf-8') + ")"
  1241.  
  1242.                             thumb = "DefaultAddon.png"
  1243.                             if item['thumbnail'] != "":
  1244.                                 thumb = item[ 'thumbnail' ]
  1245.                             else:  
  1246.                                 thumb = None
  1247.                                
  1248.                             listitem = self._create([path, item['name'], shortcutType, {"icon": "DefaultAddon.png", "thumb": thumb} ])
  1249.                             if action is not None:
  1250.                                 listitem.setProperty( "path", path )
  1251.                                 listitem.setProperty( "action", action )
  1252.  
  1253.                             listitems[ item[ "name" ] ] = listitem
  1254.                             #listitems.append(listitem)
  1255.                            
  1256.                 if contenttype == "executable":
  1257.                     self.addToDictionary( "addon-program", self.sortDictionary( listitems ) )
  1258.                     log( " - " + str( len( listitems ) ) + " programs found" )
  1259.                 elif contenttype == "video":
  1260.                     self.addToDictionary( "addon-video", self.sortDictionary( listitems ) )
  1261.                     log( " - " + str( len( listitems ) ) + " video add-ons found" )
  1262.                 elif contenttype == "audio":
  1263.                     self.addToDictionary( "addon-audio", self.sortDictionary( listitems ) )
  1264.                     log( " - " + str( len( listitems ) ) + " audio add-ons found" )
  1265.                 elif contenttype == "image":
  1266.                     self.addToDictionary( "addon-image", self.sortDictionary( listitems ) )
  1267.                     log( " - " + str( len( listitems ) ) + " image add-ons found" )
  1268.            
  1269.         except:
  1270.             log( "Failed to load addons" )
  1271.             print_exc()
  1272.        
  1273.         self.loadedAddOns = True
  1274.         return self.loadedAddOns
  1275.        
  1276.     def sortDictionary( self, dictionary ):
  1277.         listitems = []
  1278.         for key in sorted( dictionary.keys() ): #, reverse = True):
  1279.             listitems.append( dictionary[ key ] )
  1280.         return listitems
  1281.            
  1282.     # =============================
  1283.     # === ADDON/SOURCE EXPLORER ===
  1284.     # =============================
  1285.    
  1286.     def explorer( self, history, location, label, thumbnail, itemType ):
  1287.         dialogLabel = label[0].replace( "  >", "" )
  1288.  
  1289.         # Default action - create shortcut
  1290.         listings = []
  1291.        
  1292.         tree = DATA._get_overrides_skin()
  1293.        
  1294.         listings.append( self._get_icon_overrides( tree, self._create( ["::CREATE::", "32058", "", {}] ), "" ) )
  1295.                
  1296.         # If this isn't the root, create a link to go up the heirachy
  1297.         if len( label ) is not 1:
  1298.         #    listitem = xbmcgui.ListItem( label=".." )
  1299.         #    listitem.setProperty( "path", "||BACK||" )
  1300.         #    listings.append( listitem )
  1301.         #    
  1302.             dialogLabel = label[0].replace( "  >", "" ) + " - " + label[ len( label ) - 1 ].replace( "  >", "" )
  1303.            
  1304.         # Show a waiting dialog, then get the listings for the directory
  1305.         dialog = xbmcgui.DialogProgress()
  1306.         dialog.create( dialogLabel, __language__( 32063) )
  1307.    
  1308.         json_query = xbmc.executeJSONRPC('{ "jsonrpc": "2.0", "id": 0, "method": "Files.GetDirectory", "params": { "properties": ["title", "file", "thumbnail"], "directory": "' + location + '", "media": "files" } }')
  1309.         json_query = unicode(json_query, 'utf-8', errors='ignore')
  1310.         json_response = simplejson.loads(json_query)
  1311.        
  1312.         dialog.close()
  1313.            
  1314.         # Add all directories returned by the json query
  1315.         if json_response.has_key('result') and json_response['result'].has_key('files') and json_response['result']['files'] is not None:
  1316.             for item in json_response['result']['files']:
  1317.                 # Handle numeric labels
  1318.                 altLabel = item[ "label" ]
  1319.                 if item[ "label" ].isnumeric():
  1320.                     altLabel = "$NUMBER[" + item[ "label" ] + "]"
  1321.                 if location.startswith( "library://" ):
  1322.                     # Process this as a video node
  1323.                     if item[ "filetype" ] == "directory":
  1324.                         thumb = None
  1325.                         if item[ "thumbnail" ] is not "":
  1326.                             thumb = item[ "thumbnail" ]
  1327.                            
  1328.                         listitem = self._create( [ "ActivateWindow(10025,%s,Return)" %( item[ "file" ] ), altLabel, "", {"icon": "DefaultFolder.png", "thumb": thumb} ] )
  1329.  
  1330.                         if item[ "file" ].endswith( ".xml/" ) and NODE.isGrouped( item[ "file" ] ):
  1331.                             listitem = self._create( [ item[ "file" ], "%s  >" %( item[ "label" ] ), "", {"icon": "DefaultFolder.png", "thumb": thumb} ] )
  1332.                        
  1333.                         listings.append( self._get_icon_overrides( tree, listitem, "" ) )
  1334.                 else:
  1335.                     # Process this as a plugin
  1336.                     if item["filetype"] == "directory":
  1337.                         thumb = None
  1338.                         if item[ "thumbnail" ] is not "":
  1339.                             thumb = item[ "thumbnail" ]
  1340.                         listitem = self._create( [item[ "file" ], item[ "label" ] + "  >", "", {"icon": "DefaultFolder.png", "thumb": thumb} ] )
  1341.                         listings.append( self._get_icon_overrides( tree, listitem, "" ) )
  1342.            
  1343.         # Show dialog
  1344.         w = ShowDialog( "DialogSelect.xml", __cwd__, listing=listings, windowtitle=dialogLabel )
  1345.         w.doModal()
  1346.         selectedItem = w.result
  1347.         del w
  1348.        
  1349.         if selectedItem != -1:
  1350.             selectedAction = listings[ selectedItem ].getProperty( "path" )
  1351.             if selectedAction == "::CREATE::":
  1352.                 # User has chosen the shortcut they want
  1353.  
  1354.                 # Localize strings
  1355.                 localItemType = DATA.local( itemType )[2]
  1356.                
  1357.                 # Create a listitem
  1358.                 listitem = xbmcgui.ListItem(label=label[ len( label ) - 1 ].replace( "  >", "" ), label2=localItemType, iconImage="DefaultShortcut.png", thumbnailImage=thumbnail[ len( thumbnail ) - 1 ])
  1359.                
  1360.                 # Build the action
  1361.                 if itemType == "32010" or itemType == "32014" or itemType == "32069":
  1362.                     action = 'ActivateWindow(10025,"' + location + '",return)'
  1363.                     listitem.setProperty( "windowID", "10025" )
  1364.                 elif itemType == "32011" or itemType == "32019" or itemType == "32073":
  1365.                     action = 'ActivateWindow(10501,"' + location + '",return)'
  1366.                     listitem.setProperty( "windowID", "10501" )
  1367.                 elif itemType == "32012" or itemType == "32089":
  1368.                     action = 'ActivateWindow(10002,"' + location + '",return)'
  1369.                     listitem.setProperty( "windowID", "10002" )
  1370.                 elif itemType == "32009":
  1371.                     action = 'ActivateWindow(10001,"' + location + '",return)'
  1372.                 else:
  1373.                     action = "RunAddon(" + location + ")"
  1374.  
  1375.                 listitem.setProperty( "path", action )
  1376.                 listitem.setProperty( "displayPath", action )
  1377.                 listitem.setProperty( "shortcutType", itemType )
  1378.                 listitem.setProperty( "icon", "DefaultShortcut.png" )
  1379.                 if thumbnail[ len( thumbnail ) -1 ] == "":
  1380.                     listitem.setProperty( "thumbnail", thumbnail[ 0 ] )
  1381.                 else:
  1382.                     listitem.setProperty( "thumbnail", thumbnail[ len( thumbnail ) - 1 ] )
  1383.                 listitem.setProperty( "location", location )
  1384.                
  1385.                 return listitem
  1386.                
  1387.             elif selectedAction == "||BACK||":
  1388.                 # User is going up the heirarchy, remove current level and re-call this function
  1389.                 history.pop()
  1390.                 label.pop()
  1391.                 thumbnail.pop()
  1392.                 return self.explorer( history, history[ len( history ) -1 ], label, thumbnail, itemType )
  1393.                
  1394.             elif selectedAction.startswith( "ActivateWindow(" ):
  1395.                 # The user wants to create a shortcut to a specific shortcut listed
  1396.                 return listings[ selectedItem ]
  1397.                
  1398.             else:
  1399.                 # User has chosen a sub-level to display, add details and re-call this function
  1400.                 history.append( selectedAction )
  1401.                 label.append( listings[ selectedItem ].getLabel() )
  1402.                 thumbnail.append( listings[ selectedItem ].getProperty( "thumbnail" ) )
  1403.                 return self.explorer( history, selectedAction, label, thumbnail, itemType )
  1404.    
  1405.     # ======================
  1406.     # === AUTO-PLAYLISTS ===
  1407.     # ======================
  1408.    
  1409.     def _sourcelink_choice( self, selectedShortcut ):
  1410.         # The user has selected a source. We're going to give them the choice of displaying it
  1411.         # in the files view, or view library content from the source
  1412.         dialog = xbmcgui.Dialog()
  1413.        
  1414.         mediaType = None
  1415.         windowID = selectedShortcut.getProperty( "windowID" )
  1416.         # Check if we're going to display this in the files view, or the library view
  1417.         if windowID == "10025":
  1418.             # Video library                                    Files view           Movies                TV Shows             Music videos         !Movies               !TV Shows            !Music Videos
  1419.             userChoice = dialog.select( __language__(32078), [__language__(32079), __language__(32015), __language__(32016), __language__(32018), __language__(32081), __language__(32082), __language__(32083) ] )            
  1420.             if userChoice == -1:
  1421.                 return None
  1422.             elif userChoice == 0:
  1423.                 # Escape any backslashes (Windows fix)
  1424.                 newAction = selectedShortcut.getProperty( "Path" )
  1425.                 newAction = newAction.replace( "\\", "\\\\" )
  1426.                 selectedShortcut.setProperty( "Path", newAction )
  1427.                 selectedShortcut.setProperty( "displayPath", newAction )
  1428.                 return selectedShortcut
  1429.             elif userChoice == 1:
  1430.                 mediaType = "movies"
  1431.                 negative = False
  1432.             elif userChoice == 2:
  1433.                 mediaType = "tvshows"
  1434.                 negative = False
  1435.             elif userChoice == 3:
  1436.                 mediaType = "musicvideo"
  1437.                 negative = False
  1438.             elif userChoice == 4:
  1439.                 mediaType = "movies"
  1440.                 negative = True
  1441.             elif userChoice == 5:
  1442.                 mediaType = "tvshows"
  1443.                 negative = True
  1444.             elif userChoice == 6:
  1445.                 mediaType = "musicvideo"
  1446.                 negative = True
  1447.         elif windowID == "10501":
  1448.             # Music library                                    Files view           Songs                          Albums                         Mixed                           !Songs               !Albums               !Mixed
  1449.             userChoice = dialog.select( __language__(32078), [__language__(32079), xbmc.getLocalizedString(134), xbmc.getLocalizedString(132), xbmc.getLocalizedString(20395), __language__(32084), __language__(32085), __language__(32086) ] )            
  1450.             if userChoice == -1:
  1451.                 return None
  1452.             elif userChoice == 0:
  1453.                 # Escape any backslashes (Windows fix)
  1454.                 newAction = selectedShortcut.getProperty( "Path" )
  1455.                 newAction = newAction.replace( "\\", "\\\\" )
  1456.                 selectedShortcut.setProperty( "Path", newAction )
  1457.                 selectedShortcut.setProperty( "displayPath", newAction )
  1458.                 return selectedShortcut
  1459.             elif userChoice == 1:
  1460.                 mediaType = "songs"
  1461.                 windowID = "10502"
  1462.                 negative = False
  1463.             elif userChoice == 2:
  1464.                 mediaType = "albums"
  1465.                 windowID = "10502"
  1466.                 negative = False
  1467.             elif userChoice == 3:
  1468.                 mediaType = "mixed"
  1469.                 windowID = "10502"
  1470.                 negative = False
  1471.             elif userChoice == 4:
  1472.                 mediaType = "songs"
  1473.                 windowID = "10502"
  1474.                 negative = True
  1475.             elif userChoice == 5:
  1476.                 mediaType = "albums"
  1477.                 windowID = "10502"
  1478.                 negative = True
  1479.             elif userChoice == 6:
  1480.                 mediaType = "mixed"
  1481.                 windowID = "10502"
  1482.                 negative = True
  1483.         else:
  1484.             # Pictures                                         Files view            Slideshow
  1485.             userChoice = dialog.select( __language__(32078), [__language__(32079), xbmc.getLocalizedString(108)])
  1486.             if userChoice == -1:
  1487.                 return None
  1488.             elif userChoice == 0:
  1489.                 # Escape any backslashes (Windows fix)
  1490.                 newAction = selectedShortcut.getProperty( "Path" )
  1491.                 newAction = newAction.replace( "\\", "\\\\" )
  1492.                 selectedShortcut.setProperty( "Path", newAction )
  1493.                 selectedShortcut.setProperty( "displayPath", newAction )
  1494.                 return selectedShortcut
  1495.             elif userChoice == 1:
  1496.                 newAction = "SlideShow(" + selectedShortcut.getProperty( "location" ) + ")"
  1497.                 selectedShortcut.setProperty( "path", newAction )
  1498.                 selectedShortcut.setProperty( "displayPath", newAction )
  1499.                 return selectedShortcut
  1500.            
  1501.         # We're going to display it in the library
  1502.         filename = self._build_playlist( selectedShortcut.getProperty( "location" ), mediaType, selectedShortcut.getLabel(), negative )
  1503.         newAction = "ActivateWindow(" + windowID + "," +"special://profile/addon_data/" + __addonid__ + "/" + filename + ",return)"
  1504.         selectedShortcut.setProperty( "Path", newAction )
  1505.         selectedShortcut.setProperty( "displayPath", newAction )
  1506.         return selectedShortcut
  1507.    
  1508.     def _build_playlist( self, target, mediatype, name, negative ):
  1509.         # This function will build a playlist that displays the contents of a source in the library view
  1510.         # (that is to say, "path" "contains")
  1511.         tree = xmltree.ElementTree( xmltree.Element( "smartplaylist" ) )
  1512.         root = tree.getroot()
  1513.         root.set( "type", mediatype )
  1514.        
  1515.         if target.startswith ( "multipath://" ):
  1516.             temp_path = target.replace( "multipath://", "" ).split( "%2f/" )
  1517.             target = []
  1518.             for item in temp_path:
  1519.                 if item is not "":
  1520.                     target.append( urllib.url2pathname( item ) )
  1521.         else:
  1522.             target = [target]
  1523.        
  1524.         xmltree.SubElement( root, "name").text = name
  1525.         if negative == False:
  1526.             xmltree.SubElement( root, "match").text = "one"
  1527.         else:
  1528.             xmltree.SubElement( root, "match").text = "all"
  1529.        
  1530.         for item in target:
  1531.             if negative == False:
  1532.                 rule = xmltree.SubElement( root, "rule")
  1533.                 rule.set( "field", "path" )
  1534.                 rule.set( "operator", "startswith" )
  1535.                 xmltree.SubElement( rule, "value" ).text = item
  1536.             else:
  1537.                 rule = xmltree.SubElement( root, "rule")
  1538.                 rule.set( "field", "path" )
  1539.                 rule.set( "operator", "doesnotcontain" )
  1540.                 xmltree.SubElement( rule, "value" ).text = item
  1541.        
  1542.         id = 1
  1543.         while xbmcvfs.exists( os.path.join( __datapath__, str( id ) + ".xsp" ) ) :
  1544.             id += 1
  1545.                
  1546.         # Write playlist we'll link to the menu item
  1547.         DATA.indent( tree.getroot() )
  1548.         tree.write( os.path.join( __datapath__, str( id ) + ".xsp" ), encoding="utf-8" )
  1549.        
  1550.         # Add a random property, and save this for use in playlists/backgrounds
  1551.         order = xmltree.SubElement( root, "order" )
  1552.         order.text = "random"
  1553.         DATA.indent( tree.getroot() )
  1554.         tree.write( os.path.join( __datapath__, str( id ) + "-randomversion.xsp" ), encoding="utf-8" )
  1555.        
  1556.         return str( id ) + ".xsp"
  1557.        
  1558.     def _delete_playlist( self, target ):
  1559.         # This function will check if the target links to an auto-generated playlist and, if so, delete it
  1560.         target = target
  1561.         if target.startswith( "ActivateWindow(" ):
  1562.             try:
  1563.                 elements = target.split( "," )
  1564.                 if len( elements ) > 1:
  1565.                     if elements[1].startswith( "special://profile/addon_data/" + __addonid__ + "/" ) and elements[1].endswith( ".xsp" ):
  1566.                         xbmcvfs.delete( xbmc.translatePath( elements[1] ) )
  1567.                         xbmcvfs.delete( xbmc.translatePath( elements[1].replace( ".xsp", "-randomversion.xsp" ) ) )                        
  1568.             except:
  1569.                 return
  1570.  
  1571.     def _rename_playlist( self, target, newLabel ):
  1572.         # This function changes the label tag of an auto-generated playlist
  1573.        
  1574.         # First we will check that this is a playlist
  1575.         target = target
  1576.         if target.startswith( "ActivateWindow(" ):
  1577.             try:
  1578.                 elements = target.split( "," )
  1579.             except:
  1580.                 return
  1581.                    
  1582.             try:
  1583.                 if elements[1].startswith( "special://profile/addon_data/" + __addonid__ + "/" ) and elements[1].endswith( ".xsp" ):
  1584.                     filename =  xbmc.translatePath( elements[1] )
  1585.                 else:
  1586.                     return
  1587.             except:
  1588.                 return
  1589.                    
  1590.             # Load the tree and change the name
  1591.             tree = xmltree.parse( filename )
  1592.             name = tree.getroot().find( "name" )
  1593.             name.text = newLabel
  1594.            
  1595.             # Write the tree
  1596.             DATA.indent( tree.getroot() )
  1597.             tree.write( filename, encoding="utf-8" )
  1598.                    
  1599.             # Load the random tree and change the name
  1600.             tree = xmltree.parse( filename.replace( ".xsp", "-randomversion.xsp" ) )
  1601.             name = tree.getroot().find( "name" )
  1602.             name.text = newLabel
  1603.            
  1604.             # Write the random tree
  1605.             DATA.indent( tree.getroot() )
  1606.             tree.write( filename.replace( ".xsp", "-randomversion.xsp" ), encoding="utf-8" )
  1607.  
  1608. # =====================================
  1609. # === COMMON SELECT SHORTCUT METHOD ===
  1610. # =====================================
  1611.  
  1612.     def selectShortcut( self, group = "", custom = False, availableShortcuts = None, windowTitle = None, showNone = False ):
  1613.         # This function allows the user to select a shortcut
  1614.        
  1615.         # If group is empty, start background loading of shortcuts
  1616.         if group == "":
  1617.             thread.start_new_thread( self.loadLibrary, () )
  1618.        
  1619.         if availableShortcuts is None:
  1620.             nodes = self.retrieveGroup( group, False )
  1621.             availableShortcuts = nodes[1]
  1622.             windowTitle = nodes[0]
  1623.         else:
  1624.             availableShortcuts = self.checkForFolder( availableShortcuts )
  1625.            
  1626.         if showNone is not False:
  1627.             availableShortcuts.insert( 0, self._create(["::NONE::", "None", "", {"icon":"DefaultAddonNone.png"}] ) )
  1628.            
  1629.         if custom is not False:
  1630.             availableShortcuts.append( self._create(["||CUSTOM||", "Custom shortcut", "", {}] ) )
  1631.        
  1632.         # Check a shortcut is available
  1633.         if len( availableShortcuts ) == 0:
  1634.             log( "No available shortcuts found" )
  1635.             xbmcgui.Dialog().ok( __language__(32064), __language__(32065) )
  1636.             return
  1637.                                
  1638.         w = ShowDialog( "DialogSelect.xml", __cwd__, listing=availableShortcuts, windowtitle=windowTitle )
  1639.         w.doModal()
  1640.         number = w.result
  1641.         del w
  1642.        
  1643.         if number != -1:
  1644.             selectedShortcut = availableShortcuts[ number ]
  1645.             path = selectedShortcut.getProperty( "Path" )
  1646.             if path.startswith( "||NODE||" ):
  1647.                 if group == "":
  1648.                     group = path.replace( "||NODE||", "" )
  1649.                 else:
  1650.                     group = group + "," + path.replace( "||NODE||", "" )
  1651.                 return self.selectShortcut( group = group )
  1652.             elif path.startswith( "||BROWSE||" ):
  1653.                 selectedShortcut = self.explorer( ["plugin://" + path.replace( "||BROWSE||", "" )], "plugin://" + path.replace( "||BROWSE||", "" ), [selectedShortcut.getLabel()], [selectedShortcut.getProperty("thumbnail")], selectedShortcut.getProperty("shortcutType") )
  1654.                 # Convert backslashes to double-backslashes (windows fix)
  1655.                 if selectedShortcut is not None:
  1656.                     newAction = selectedShortcut.getProperty( "Path" )
  1657.                     newAction = newAction.replace( "\\", "\\\\" )
  1658.                     selectedShortcut.setProperty( "Path", newAction )
  1659.                     selectedShortcut.setProperty( "displayPath", newAction )
  1660.             elif path.startswith( "||FOLDER||" ):
  1661.                 selectedShortcut = self.explorer( [ path.replace( "||FOLDER||", "" )], path.replace( "||FOLDER||", "" ), [selectedShortcut.getLabel()], [selectedShortcut.getProperty("thumbnail")], "32014" )
  1662.                 # Convert backslashes to double-backslashes (windows fix)
  1663.                 if selectedShortcut is not None:
  1664.                     newAction = selectedShortcut.getProperty( "Path" )
  1665.                     newAction = newAction.replace( "\\", "\\\\" )
  1666.                     selectedShortcut.setProperty( "Path", newAction )
  1667.                     selectedShortcut.setProperty( "displayPath", newAction )
  1668.  
  1669.                 # The next set of shortcuts are within the listitem property folder-contents
  1670.                 #shortcuts = self.folders[ selectedShortcut.getProperty( "folder" ) ]
  1671.                 #return self.selectShortcut( group=group, availableShortcuts=shortcuts, windowTitle = selectedShortcut.getLabel() )
  1672.             elif path == "||UPNP||":
  1673.                 selectedShortcut = self.explorer( ["upnp://"], "upnp://", [selectedShortcut.getLabel()], [selectedShortcut.getProperty("thumbnail")], selectedShortcut.getProperty("shortcutType")  )
  1674.                 path = selectedShortcut.getProperty( "Path" )
  1675.             elif path.startswith( "||SOURCE||" ):
  1676.                 selectedShortcut = self.explorer( [path.replace( "||SOURCE||", "" )], path.replace( "||SOURCE||", "" ), [selectedShortcut.getLabel()], [selectedShortcut.getProperty("thumbnail")], selectedShortcut.getProperty("shortcutType")  )
  1677.                 if selectedShortcut is None or "upnp://" in selectedShortcut.getProperty( "Path" ):
  1678.                     return selectedShortcut
  1679.                 selectedShortcut = self._sourcelink_choice( selectedShortcut )
  1680.                 #path = urllib.unquote( selectedShortcut.getProperty( "Path" ) )
  1681.             elif path == "::PLAYLIST::" :
  1682.                 # Give the user the choice of playing or displaying the playlist
  1683.                 dialog = xbmcgui.Dialog()
  1684.                 userchoice = dialog.yesno( __language__( 32040 ), __language__( 32060 ), "", "", __language__( 32061 ), __language__( 32062 ) )
  1685.                 # False: Display
  1686.                 # True: Play
  1687.                 if userchoice == False:
  1688.                     selectedShortcut.setProperty( "chosenPath", selectedShortcut.getProperty( "action-show" ) )
  1689.                 else:
  1690.                     selectedShortcut.setProperty( "chosenPath", selectedShortcut.getProperty( "action-play" ) )
  1691.                    
  1692.             elif path == "||CUSTOM||":
  1693.                 # Let the user type a command
  1694.                 keyboard = xbmc.Keyboard( "", __language__(32027), False )
  1695.                 keyboard.doModal()
  1696.                
  1697.                 if ( keyboard.isConfirmed() ):
  1698.                     action = keyboard.getText()
  1699.                     if action != "":
  1700.                         # Create a really simple listitem to return
  1701.                         selectedShortcut = xbmcgui.ListItem( None, __language__(32024) )
  1702.                         selectedShortcut.setProperty( "Path", action )
  1703.                     else:
  1704.                         selectedShortcut = None
  1705.                            
  1706.                 else:
  1707.                     selectedShortcut = None
  1708.                    
  1709.             elif path == "::NONE::":
  1710.                 # Create a really simple listitem to return
  1711.                 selectedShortcut = xbmcgui.ListItem( "::NONE::" )
  1712.  
  1713.             return selectedShortcut
  1714.         else:
  1715.             return None
  1716.  
  1717. # ============================
  1718. # === PRETTY SELECT DIALOG ===
  1719. # ============================
  1720.            
  1721. class ShowDialog( xbmcgui.WindowXMLDialog ):
  1722.     def __init__( self, *args, **kwargs ):
  1723.         xbmcgui.WindowXMLDialog.__init__( self )
  1724.         self.listing = kwargs.get( "listing" )
  1725.         self.windowtitle = kwargs.get( "windowtitle" )
  1726.         self.result = -1
  1727.  
  1728.     def onInit(self):
  1729.         try:
  1730.             self.fav_list = self.getControl(6)
  1731.             self.getControl(3).setVisible(False)
  1732.         except:
  1733.             print_exc()
  1734.             self.fav_list = self.getControl(3)
  1735.  
  1736.         self.getControl(5).setVisible(False)
  1737.         self.getControl(1).setLabel(self.windowtitle)
  1738.  
  1739.         for item in self.listing :
  1740.             listitem = xbmcgui.ListItem(label=item.getLabel(), label2=item.getLabel2(), iconImage=item.getProperty( "icon" ), thumbnailImage=item.getProperty( "thumbnail" ))
  1741.             listitem.setProperty( "Addon.Summary", item.getLabel2() )
  1742.             self.fav_list.addItem( listitem )
  1743.  
  1744.         self.setFocus(self.fav_list)
  1745.  
  1746.     def onAction(self, action):
  1747.         if action.getId() in ( 9, 10, 92, 216, 247, 257, 275, 61467, 61448, ):
  1748.             self.result = -1
  1749.             self.close()
  1750.  
  1751.     def onClick(self, controlID):
  1752.         if controlID == 6 or controlID == 3:
  1753.             num = self.fav_list.getSelectedPosition()
  1754.             self.result = num
  1755.         else:
  1756.             self.result = -1
  1757.  
  1758.         self.close()
  1759.  
  1760.     def onFocus(self, controlID):
  1761.         pass
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement