Updated kodi settings on Lenovo
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,836 @@
|
||||
# coding=utf-8
|
||||
import os, sys, shutil, unicodedata, re, types
|
||||
|
||||
from resources.lib.common import *
|
||||
|
||||
from html.entities import name2codepoint
|
||||
from urllib.parse import parse_qs
|
||||
from urllib.parse import quote, unquote
|
||||
|
||||
import xbmc, xbmcplugin, xbmcgui, xbmcvfs
|
||||
import xml.etree.ElementTree as xmltree
|
||||
import urllib
|
||||
from unidecode import unidecode
|
||||
|
||||
from traceback import print_exc
|
||||
import json
|
||||
|
||||
from resources.lib import rules, pathrules, viewattrib, orderby, moveNodes
|
||||
|
||||
# character entity reference
|
||||
CHAR_ENTITY_REXP = re.compile('&(%s);' % '|'.join(name2codepoint))
|
||||
|
||||
# decimal character reference
|
||||
DECIMAL_REXP = re.compile('&#(\d+);')
|
||||
|
||||
# hexadecimal character reference
|
||||
HEX_REXP = re.compile('&#x([\da-fA-F]+);')
|
||||
|
||||
REPLACE1_REXP = re.compile(r'[\']+')
|
||||
REPLACE2_REXP = re.compile(r'[^-a-z0-9]+')
|
||||
REMOVE_REXP = re.compile('-{2,}')
|
||||
|
||||
|
||||
|
||||
class Main:
|
||||
# MAIN ENTRY POINT
|
||||
def __init__(self, params, ltype, rule, attrib, pathrule, orderby):
|
||||
|
||||
self._parse_argv()
|
||||
self.ltype = ltype
|
||||
self.PARAMS = params
|
||||
self.RULE = rule
|
||||
self.ATTRIB = attrib
|
||||
self.PATHRULE = pathrule
|
||||
self.ORDERBY = orderby
|
||||
# If there are no custom library nodes in the profile directory, copy them from the Kodi install
|
||||
targetDir = os.path.join( xbmcvfs.translatePath( "special://profile" ), "library", ltype )
|
||||
if True:
|
||||
if not os.path.exists( targetDir ):
|
||||
xbmcvfs.mkdirs( targetDir )
|
||||
originDir = os.path.join( xbmcvfs.translatePath( "special://xbmc" ), "system", "library", ltype )
|
||||
dirs, files = xbmcvfs.listdir( originDir )
|
||||
self.copyNode( dirs, files, targetDir, originDir )
|
||||
else:
|
||||
xbmcgui.Dialog().ok(ADDONNAME, LANGUAGE( 30400 ) )
|
||||
print_exc
|
||||
xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))
|
||||
return
|
||||
# Create data if not exists
|
||||
if not os.path.exists(DATAPATH):
|
||||
xbmcvfs.mkdir(DATAPATH)
|
||||
if "type" in self.PARAMS:
|
||||
# We're performing a specific action
|
||||
if self.PARAMS[ "type" ] == "delete":
|
||||
message = LANGUAGE( 30401 )
|
||||
actionpath = unquote(self.PARAMS["actionPath"])
|
||||
if self.PARAMS[ "actionPath" ] == targetDir:
|
||||
# Ask the user is they want to reset all nodes
|
||||
message = LANGUAGE( 30402 )
|
||||
result = xbmcgui.Dialog().yesno(ADDONNAME, message )
|
||||
if result:
|
||||
if actionpath.endswith( ".xml" ):
|
||||
# Delete single file
|
||||
xbmcvfs.delete(actionpath)
|
||||
else:
|
||||
# Delete folder
|
||||
self.RULE.deleteAllNodeRules(actionpath)
|
||||
shutil.rmtree(actionpath)
|
||||
else:
|
||||
return
|
||||
elif self.PARAMS[ "type" ] == "deletenode":
|
||||
result = xbmcgui.Dialog().yesno(ADDONNAME, LANGUAGE( 30403 ) )
|
||||
if result:
|
||||
self.changeViewElement( self.PARAMS[ "actionPath" ], self.PARAMS[ "node" ], "" )
|
||||
elif self.PARAMS[ "type" ] == "editlabel":
|
||||
if self.PARAMS[ "label" ].isdigit():
|
||||
label = xbmc.getLocalizedString( int( self.PARAMS[ "label" ] ) )
|
||||
else:
|
||||
label = self.PARAMS[ "label" ]
|
||||
# Get new label from keyboard dialog
|
||||
keyboard = xbmc.Keyboard( label, LANGUAGE( 30300 ), False )
|
||||
keyboard.doModal()
|
||||
if ( keyboard.isConfirmed() ):
|
||||
newlabel = keyboard.getText()
|
||||
if newlabel != "" and newlabel != label:
|
||||
# We've got a new label, update the xml file
|
||||
self.changeViewElement( self.PARAMS[ "actionPath" ], "label", newlabel )
|
||||
else:
|
||||
return
|
||||
elif self.PARAMS[ "type" ] == "editvisibility":
|
||||
currentVisibility = self.getRootAttrib( self.PARAMS[ "actionPath" ], "visible" )
|
||||
# Get new visibility from keyboard dialog
|
||||
keyboard = xbmc.Keyboard( currentVisibility, LANGUAGE( 30301 ), False )
|
||||
keyboard.doModal()
|
||||
if ( keyboard.isConfirmed() ):
|
||||
newVisibility = keyboard.getText()
|
||||
if newVisibility != currentVisibility:
|
||||
# We've got a new label, update the xml file
|
||||
self.changeRootAttrib( self.PARAMS[ "actionPath" ], "visible", newVisibility )
|
||||
else:
|
||||
return
|
||||
elif self.PARAMS[ "type" ] == "moveNode":
|
||||
self.indexCounter = -1
|
||||
|
||||
# Get existing nodes
|
||||
nodes = {}
|
||||
self.listNodes( self.PARAMS[ "actionPath" ], nodes )
|
||||
|
||||
# Get updated order
|
||||
newOrder = moveNodes.getNewOrder( nodes, int( self.PARAMS[ "actionItem" ] ) )
|
||||
|
||||
if newOrder is not None:
|
||||
# Update the orders
|
||||
for i, node in enumerate( newOrder, 1 ):
|
||||
path = unquote( node[ 2 ] )
|
||||
if node[ 3 ] == "folder":
|
||||
path = os.path.join( unquote( node[ 2 ] ), "index.xml" )
|
||||
self.changeRootAttrib( path, "order", str( i * 10 ) )
|
||||
|
||||
elif self.PARAMS[ "type" ] == "newView":
|
||||
# Get new view name from keyboard dialog
|
||||
keyboard = xbmc.Keyboard( "", LANGUAGE( 30316 ), False )
|
||||
keyboard.doModal()
|
||||
if ( keyboard.isConfirmed() ):
|
||||
newView = keyboard.getText()
|
||||
if newView != "":
|
||||
# Ensure filename is unique
|
||||
filename = self.slugify( newView.lower().replace( " ", "" ) )
|
||||
if os.path.exists( os.path.join( self.PARAMS[ "actionPath" ], filename + ".xml" ) ):
|
||||
count = 0
|
||||
while os.path.exists( os.path.join( self.PARAMS[ "actionPath" ], filename + "-" + str( count ) + ".xml" ) ):
|
||||
count += 1
|
||||
filename = filename + "-" + str( count )
|
||||
# Create a new xml file
|
||||
tree = xmltree.ElementTree( xmltree.Element( "node" ) )
|
||||
root = tree.getroot()
|
||||
subtree = xmltree.SubElement( root, "label" ).text = newView
|
||||
# Add any node rules
|
||||
self.RULE.addAllNodeRules( self.PARAMS[ "actionPath" ], root )
|
||||
# Write the xml file
|
||||
self.indent( root )
|
||||
xmlfile = unquote(os.path.join( self.PARAMS[ "actionPath" ], filename + ".xml" ))
|
||||
if not os.path.exists(xmlfile):
|
||||
with open(xmlfile, 'a'):
|
||||
os.utime(xmlfile, None)
|
||||
tree.write( xmlfile, encoding="UTF-8" )
|
||||
else:
|
||||
return
|
||||
elif self.PARAMS[ "type" ] == "newNode":
|
||||
# Get new node name from the keyboard dialog
|
||||
keyboard = xbmc.Keyboard( "", LANGUAGE( 30303 ), False )
|
||||
keyboard.doModal()
|
||||
if ( keyboard.isConfirmed() ):
|
||||
newNode = keyboard.getText()
|
||||
if newNode == "":
|
||||
return
|
||||
# Ensure foldername is unique
|
||||
foldername = self.slugify( newNode.lower().replace( " ", "" ) )
|
||||
if os.path.exists( os.path.join( self.PARAMS[ "actionPath" ], foldername + os.pathsep ) ):
|
||||
count = 0
|
||||
while os.path.exists( os.path.join( self.PARAMS[ "actionPath" ], foldername + "-" + str( count ) + os.pathsep ) ):
|
||||
count += 1
|
||||
foldername = foldername + "-" + str( count )
|
||||
foldername = unquote(os.path.join( self.PARAMS[ "actionPath" ], foldername ))
|
||||
# Create new node folder
|
||||
xbmcvfs.mkdir( foldername )
|
||||
# Create a new xml file
|
||||
tree = xmltree.ElementTree( xmltree.Element( "node" ) )
|
||||
root = tree.getroot()
|
||||
subtree = xmltree.SubElement( root, "label" ).text = newNode
|
||||
# Ask user if they want to import defaults
|
||||
if self.ltype.startswith( "video" ):
|
||||
defaultNames = [ xbmc.getLocalizedString( 231 ), xbmc.getLocalizedString( 342 ), xbmc.getLocalizedString( 20343 ), xbmc.getLocalizedString( 20389 ) ]
|
||||
defaultValues = [ "", "movies", "tvshows", "musicvideos" ]
|
||||
selected = xbmcgui.Dialog().select( LANGUAGE( 30304 ), defaultNames )
|
||||
else:
|
||||
selected = 0
|
||||
# If the user selected some defaults...
|
||||
if selected != -1 and selected != 0:
|
||||
try:
|
||||
# Copy those defaults across
|
||||
originDir = os.path.join( xbmcvfs.translatePath( "special://xbmc" ), "system", "library", self.ltype, defaultValues[ selected ] )
|
||||
dirs, files = xbmcvfs.listdir( originDir )
|
||||
for file in files:
|
||||
if file != "index.xml":
|
||||
xbmcvfs.copy( os.path.join( originDir, file), os.path.join( foldername, file ) )
|
||||
# Open index.xml and copy values across
|
||||
index = xmltree.parse( os.path.join( originDir, "index.xml" ) ).getroot()
|
||||
if "visible" in index.attrib:
|
||||
root.set( "visible", index.attrib.get( "visible" ) )
|
||||
icon = index.find( "icon" )
|
||||
if icon is not None:
|
||||
xmltree.SubElement( root, "icon" ).text = icon.text
|
||||
except:
|
||||
print_exc()
|
||||
# Write the xml file
|
||||
self.indent( root )
|
||||
tree.write( unquote(os.path.join( foldername, "index.xml" )), encoding="UTF-8" )
|
||||
else:
|
||||
return
|
||||
elif self.PARAMS[ "type" ] == "rule":
|
||||
# Display list of all elements of a rule
|
||||
self.RULE.displayRule( self.PARAMS[ "actionPath" ], self.PATH, self.PARAMS[ "rule" ] )
|
||||
return
|
||||
elif self.PARAMS[ "type" ] == "editMatch":
|
||||
# Editing the field the rule is matched against
|
||||
self.RULE.editMatch( self.PARAMS[ "actionPath" ], self.PARAMS[ "rule" ], self.PARAMS[ "content"], self.PARAMS[ "default" ] )
|
||||
elif self.PARAMS[ "type" ] == "editOperator":
|
||||
# Editing the operator of a rule
|
||||
self.RULE.editOperator( self.PARAMS[ "actionPath" ], self.PARAMS[ "rule" ], self.PARAMS[ "group" ], self.PARAMS[ "default" ] )
|
||||
elif self.PARAMS[ "type" ] == "editValue":
|
||||
# Editing the value of a rule
|
||||
self.RULE.editValue(self.PARAMS["actionPath"], self.PARAMS[ "rule" ] )
|
||||
elif self.PARAMS[ "type" ] == "browseValue":
|
||||
# Browse for the new value of a rule
|
||||
self.RULE.browse( self.PARAMS[ "actionPath" ], self.PARAMS[ "rule" ], self.PARAMS[ "match" ], self.PARAMS[ "content" ] )
|
||||
elif self.PARAMS[ "type" ] == "deleteRule":
|
||||
# Delete a rule
|
||||
self.RULE.deleteRule( self.PARAMS[ "actionPath" ], self.PARAMS[ "rule" ] )
|
||||
elif self.PARAMS[ "type" ] == "editRulesMatch":
|
||||
# Editing whether any or all rules must match
|
||||
self.ATTRIB.editMatch( self.PARAMS[ "actionPath" ] )
|
||||
# --- Edit order-by ---
|
||||
elif self.PARAMS[ "type" ] == "orderby":
|
||||
# Display all elements of order by
|
||||
self.ORDERBY.displayOrderBy( self.PARAMS[ "actionPath" ])
|
||||
return
|
||||
elif self.PARAMS[ "type" ] == "editOrderBy":
|
||||
self.ORDERBY.editOrderBy( self.PARAMS[ "actionPath" ], self.PARAMS[ "content" ], self.PARAMS[ "default" ] )
|
||||
elif self.PARAMS[ "type" ] == "editOrderByDirection":
|
||||
self.ORDERBY.editDirection( self.PARAMS[ "actionPath" ], self.PARAMS[ "default" ] )
|
||||
# --- Edit paths ---
|
||||
elif self.PARAMS[ "type" ] == "addPath":
|
||||
self.ATTRIB.addPath( self.PARAMS[ "actionPath" ] )
|
||||
elif self.PARAMS[ "type" ] == "editPath":
|
||||
self.ATTRIB.editPath( self.PARAMS[ "actionPath" ], self.PARAMS[ "value" ] )
|
||||
elif self.PARAMS[ "type" ] == "pathRule":
|
||||
self.PATHRULE.displayRule( self.PARAMS[ "actionPath" ], int( self.PARAMS[ "rule" ] ) )
|
||||
return
|
||||
elif self.PARAMS[ "type" ] == "deletePathRule":
|
||||
self.ATTRIB.deletePathRule( self.PARAMS[ "actionPath" ], int( self.PARAMS[ "rule" ] ) )
|
||||
elif self.PARAMS[ "type" ] == "editPathMatch":
|
||||
# Editing the field the rule is matched against
|
||||
self.PATHRULE.editMatch( self.PARAMS[ "actionPath" ], int( self.PARAMS[ "rule" ] ) )
|
||||
elif self.PARAMS[ "type" ] == "editPathValue":
|
||||
# Editing the value of a rule
|
||||
self.PATHRULE.editValue( self.PARAMS[ "actionPath" ], int( self.PARAMS[ "rule" ] ) )
|
||||
elif self.PARAMS[ "type" ] == "browsePathValue":
|
||||
# Browse for the new value of a rule
|
||||
self.PATHRULE.browse( self.PARAMS[ "actionPath" ], int( self.PARAMS[ "rule" ] ) )
|
||||
# --- Edit other attribute of view ---
|
||||
# > Content
|
||||
elif self.PARAMS[ "type" ] == "editContent":
|
||||
self.ATTRIB.editContent( self.PARAMS[ "actionPath" ], "" ) # No default to pass, yet!
|
||||
# > Grouping
|
||||
elif self.PARAMS[ "type" ] == "editGroup":
|
||||
self.ATTRIB.editGroup( self.PARAMS[ "actionPath" ], self.PARAMS[ "content" ], "" )
|
||||
# > Limit
|
||||
elif self.PARAMS[ "type" ] == "editLimit":
|
||||
self.ATTRIB.editLimit( self.PARAMS[ "actionPath" ], self.PARAMS[ "value" ] )
|
||||
# > Icon (also for node)
|
||||
elif self.PARAMS[ "type" ] == "editIcon":
|
||||
self.ATTRIB.editIcon( self.PARAMS[ "actionPath" ], self.PARAMS[ "value" ] )
|
||||
elif self.PARAMS[ "type" ] == "browseIcon":
|
||||
self.ATTRIB.browseIcon( self.PARAMS[ "actionPath" ] )
|
||||
# Refresh the listings and exit
|
||||
xbmc.executebuiltin("Container.Refresh")
|
||||
return
|
||||
if self.PATH.endswith( ".xml" ):
|
||||
self.RulesList()
|
||||
else:
|
||||
self.NodesList(targetDir)
|
||||
|
||||
def NodesList( self, targetDir ):
|
||||
# List nodes and views
|
||||
nodes = {}
|
||||
self.indexCounter = -1
|
||||
if self.PATH != "":
|
||||
self.listNodes( self.PATH, nodes )
|
||||
else:
|
||||
self.listNodes( targetDir, nodes )
|
||||
self.PATH = quote( self.PATH )
|
||||
for i, key in enumerate( sorted( nodes ) ):
|
||||
# 0 = Label
|
||||
# 1 = Icon
|
||||
# 2 = Path
|
||||
# 3 = Type
|
||||
# 4 = Order
|
||||
# Localize the label
|
||||
if nodes[ key ][ 0 ].isdigit():
|
||||
label = xbmc.getLocalizedString( int( nodes[ key ][ 0 ] ) )
|
||||
else:
|
||||
label = nodes[ key ][ 0 ]
|
||||
# Create the listitem
|
||||
if nodes[ key ][ 3 ] == "folder":
|
||||
listitem = xbmcgui.ListItem( label="%s >" % ( label ), label2=nodes[ key ][ 4 ] )
|
||||
listitem.setArt({"icon": nodes[ key ][ 1 ]})
|
||||
else:
|
||||
listitem = xbmcgui.ListItem( label=label, label2=nodes[ key ][ 4 ] )
|
||||
listitem.setArt({"icon": nodes[ key ][ 1 ]})
|
||||
# Add context menu items
|
||||
commands = []
|
||||
commandsNode = []
|
||||
commandsView = []
|
||||
commandsNode.append( ( LANGUAGE(30101), "RunPlugin(plugin://plugin.library.node.editor?ltype=%s&type=editlabel&actionPath=" % self.ltype + os.path.join( nodes[ key ][ 2 ], "index.xml" ) + "&label=" + nodes[ key ][ 0 ] + ")" ) )
|
||||
commandsNode.append( ( LANGUAGE(30102), "RunPlugin(plugin://plugin.library.node.editor?ltype=%s&type=editIcon&actionPath=" % self.ltype + os.path.join( nodes[ key ][ 2 ], "index.xml" ) + "&value=" + nodes[ key ][ 1 ] + ")" ) )
|
||||
commandsNode.append( ( LANGUAGE(30103), "RunPlugin(plugin://plugin.library.node.editor?ltype=%s&type=browseIcon&actionPath=" % self.ltype + os.path.join( nodes [ key ][ 2 ], "index.xml" ) + ")" ) )
|
||||
if self.PATH == "":
|
||||
commandsNode.append( ( LANGUAGE(30104), "RunPlugin(plugin://plugin.library.node.editor?ltype=%s&type=moveNode&actionPath=" % self.ltype + targetDir + "&actionItem=" + str( i ) + ")" ) )
|
||||
else:
|
||||
commandsNode.append( ( LANGUAGE(30104), "RunPlugin(plugin://plugin.library.node.editor?ltype=%s&type=moveNode&actionPath=" % self.ltype + self.PATH + "&actionItem=" + str( i ) + ")" ) )
|
||||
commandsNode.append( ( LANGUAGE(30105), "RunPlugin(plugin://plugin.library.node.editor?ltype=%s&type=editvisibility&actionPath=" % self.ltype + os.path.join( nodes[ key ][ 2 ], "index.xml" ) + ")" ) )
|
||||
commandsNode.append( ( LANGUAGE(30100), "RunPlugin(plugin://plugin.library.node.editor?ltype=%s&type=delete&actionPath=" % self.ltype + nodes[ key ][ 2 ] + ")" ) )
|
||||
|
||||
commandsView.append( ( LANGUAGE(30101), "RunPlugin(plugin://plugin.library.node.editor?ltype=%s&type=editlabel&actionPath=" % self.ltype + nodes[ key ][ 2 ] + "&label=" + nodes[ key ][ 0 ] + ")" ) )
|
||||
commandsView.append( ( LANGUAGE(30102), "RunPlugin(plugin://plugin.library.node.editor?ltype=%s&type=editIcon&actionPath=" % self.ltype + nodes[ key ][ 2 ] + "&value=" + nodes[ key ][ 1 ] + ")" ) )
|
||||
commandsView.append( ( LANGUAGE(30103), "RunPlugin(plugin://plugin.library.node.editor?ltype=%s&type=browseIcon&actionPath=" % self.ltype + nodes[ key ][ 2 ] + ")" ) )
|
||||
if self.PATH == "":
|
||||
commandsView.append( ( LANGUAGE(30104), "RunPlugin(plugin://plugin.library.node.editor?ltype=%s&type=moveNode&actionPath=" % self.ltype + targetDir + "&actionItem=" + str( i ) + ")" ) )
|
||||
else:
|
||||
commandsView.append( ( LANGUAGE(30104), "RunPlugin(plugin://plugin.library.node.editor?ltype=%s&type=moveNode&actionPath=" % self.ltype + self.PATH + "&actionItem=" + str( i ) + ")" ) )
|
||||
commandsView.append( ( LANGUAGE(30105), "RunPlugin(plugin://plugin.library.node.editor?ltype=%s&type=editvisibility&actionPath=" % self.ltype + nodes[ key ][ 2 ] + ")" ) )
|
||||
commandsView.append( ( LANGUAGE(30100), "RunPlugin(plugin://plugin.library.node.editor?ltype=%s&type=delete&actionPath=" % self.ltype + nodes[ key ][ 2 ] + ")" ) )
|
||||
if nodes[ key ][ 3 ] == "folder":
|
||||
listitem.addContextMenuItems( commandsNode )
|
||||
xbmcplugin.addDirectoryItem( int(sys.argv[ 1 ]), "plugin://plugin.library.node.editor?ltype=%s&path=" % self.ltype + nodes[ key ][ 2 ], listitem, isFolder=True )
|
||||
else:
|
||||
listitem.addContextMenuItems( commandsView )
|
||||
xbmcplugin.addDirectoryItem( int(sys.argv[ 1 ]), "plugin://plugin.library.node.editor?ltype=%s&path=" % self.ltype + nodes[ key ][ 2 ], listitem, isFolder=True )
|
||||
if self.PATH != "":
|
||||
# Get any rules from the index.xml
|
||||
rules, nextRule = self.getRules( os.path.join( unquote( self.PATH ), "index.xml" ), True )
|
||||
rulecount = 0
|
||||
if rules is not None:
|
||||
for rule in rules:
|
||||
commands = []
|
||||
if rule[ 0 ] == "rule":
|
||||
# 1 = field
|
||||
# 2 = operator
|
||||
# 3 = value (optional)
|
||||
if len(rule) == 3:
|
||||
translated = self.RULE.translateRule( [ rule[ 1 ], rule[ 2 ] ] )
|
||||
else:
|
||||
translated = self.RULE.translateRule( [ rule[ 1 ], rule[ 2 ], rule[ 3 ] ] )
|
||||
if len(translated) == 2:
|
||||
listitem = xbmcgui.ListItem( label="%s: %s %s" % ( LANGUAGE(30205), translated[ 0 ][ 0 ], translated[ 1 ][ 0 ] ) )
|
||||
else:
|
||||
listitem = xbmcgui.ListItem( label="%s: %s %s %s" % ( LANGUAGE(30205), translated[ 0 ][ 0 ], translated[ 1 ][ 0 ], translated[ 2 ][ 1 ] ) )
|
||||
commands.append( ( LANGUAGE( 30100 ), "RunPlugin(plugin://plugin.library.node.editor?ltype=%s&type=deleteRule&actionPath=" % self.ltype + os.path.join( self.PATH, "index.xml" ) + "&rule=" + str( rulecount ) + ")" ) )
|
||||
action = "plugin://plugin.library.node.editor?ltype=%s&type=rule&actionPath=" % self.ltype + os.path.join( self.PATH, "index.xml" ) + "&rule=" + str( rulecount )
|
||||
rulecount += 1
|
||||
listitem.addContextMenuItems( commands, replaceItems = True )
|
||||
if rule[ 0 ] == "rule" or rule[ 0 ] == "order":
|
||||
xbmcplugin.addDirectoryItem( int(sys.argv[ 1 ]), action, listitem, isFolder=True )
|
||||
else:
|
||||
xbmcplugin.addDirectoryItem( int(sys.argv[ 1 ]), action, listitem, isFolder=False )
|
||||
# New rule
|
||||
xbmcplugin.addDirectoryItem( int( sys.argv[ 1 ] ), "plugin://plugin.library.node.editor?ltype=%s&type=rule&actionPath=" % self.ltype + os.path.join( self.PATH, "index.xml" ) + "&rule=" + str( nextRule), xbmcgui.ListItem( label="* %s" %( LANGUAGE(30005) ) ), isFolder=True )
|
||||
showReset = False
|
||||
if self.PATH == "":
|
||||
self.PATH = quote( targetDir )
|
||||
showReset = True
|
||||
# New view and node
|
||||
xbmcplugin.addDirectoryItem( int( sys.argv[ 1 ] ), "plugin://plugin.library.node.editor?ltype=%s&type=newView&actionPath=" % self.ltype + self.PATH, xbmcgui.ListItem( label="* %s" %( LANGUAGE(30006) ) ), isFolder=False )
|
||||
xbmcplugin.addDirectoryItem( int( sys.argv[ 1 ] ), "plugin://plugin.library.node.editor?ltype=%s&type=newNode&actionPath=" % self.ltype + self.PATH, xbmcgui.ListItem( label="* %s" %( LANGUAGE(30007) ) ), isFolder=False )
|
||||
if showReset:
|
||||
xbmcplugin.addDirectoryItem( int(sys.argv[ 1 ]), "plugin://plugin.library.node.editor?ltype=%s&type=delete&actionPath=" % self.ltype + targetDir, xbmcgui.ListItem( label="* %s" %( LANGUAGE(30008) ) ), isFolder=False )
|
||||
xbmcplugin.setContent(int(sys.argv[1]), 'files')
|
||||
xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))
|
||||
|
||||
def RulesList( self ):
|
||||
# List rules for specific view
|
||||
rules, nextRule = self.getRules( self.PATH )
|
||||
hasContent = False
|
||||
content = ""
|
||||
hasOrder = False
|
||||
hasGroup = False
|
||||
hasLimit = False
|
||||
hasPath = False
|
||||
splitPath = None
|
||||
rulecount = 0
|
||||
if rules is not None:
|
||||
for rule in rules:
|
||||
commands = []
|
||||
if rule[ 0 ] == "content":
|
||||
# 1 = Content
|
||||
listitem = xbmcgui.ListItem( label="%s: %s" % ( LANGUAGE(30200), self.ATTRIB.translateContent( rule[ 1 ] ) ) )
|
||||
commands.append( ( LANGUAGE(30100), "RunPlugin(plugin://plugin.library.node.editor?ltype=%s&type=deletenode&actionPath=" % self.ltype + self.PATH + "&node=content)" ) )
|
||||
action = "plugin://plugin.library.node.editor?ltype=%s&type=editContent&actionPath=" % self.ltype + self.PATH
|
||||
hasContent = True
|
||||
content = rule[ 1 ]
|
||||
elif rule[ 0 ] == "order":
|
||||
# 1 = orderby
|
||||
# 2 = direction (optional?)
|
||||
if len( rule ) == 3:
|
||||
translate = self.ORDERBY.translateOrderBy( [ rule[ 1 ], rule[ 2 ] ] )
|
||||
listitem = xbmcgui.ListItem( label="%s: %s (%s)" % ( LANGUAGE(30201), translate[ 0 ][ 0 ], translate[ 1 ][ 0 ] ) )
|
||||
else:
|
||||
translate = self.ORDERBY.translateOrderBy( [ rule[ 1 ], "" ] )
|
||||
listitem = xbmcgui.ListItem( label="%s: %s" % ( LANGUAGE(30201), translate[ 0 ][ 0 ] ) )
|
||||
commands.append( ( LANGUAGE(30100), "RunPlugin(plugin://plugin.library.node.editor?ltype=%s&type=deletenode&actionPath=" % self.ltype + self.PATH + "&node=order)" ) )
|
||||
action = "plugin://plugin.library.node.editor?ltype=%s&type=orderby&actionPath=" % self.ltype + self.PATH
|
||||
hasOrder = True
|
||||
elif rule[ 0 ] == "group":
|
||||
# 1 = group
|
||||
listitem = xbmcgui.ListItem( label="%s: %s" % ( LANGUAGE(30202), self.ATTRIB.translateGroup( rule[ 1 ] ) ) )
|
||||
commands.append( ( LANGUAGE(30100), "RunPlugin(plugin://plugin.library.node.editor?ltype=%s&type=deletenode&actionPath=" % self.ltype + self.PATH + "&node=group)" ) )
|
||||
action = "plugin://plugin.library.node.editor?ltype=%s&type=editGroup&actionPath=" % self.ltype + self.PATH + "&value=" + rule[ 1 ] + "&content=" + content
|
||||
hasGroup = True
|
||||
elif rule[ 0 ] == "limit":
|
||||
# 1 = limit
|
||||
listitem = xbmcgui.ListItem( label="%s: %s" % ( LANGUAGE(30203), rule[ 1 ] ) )
|
||||
commands.append( ( LANGUAGE(30100), "RunPlugin(plugin://plugin.library.node.editor?ltype=%s&type=deletenode&actionPath=" % self.ltype + self.PATH + "&node=limit)" ) )
|
||||
action = "plugin://plugin.library.node.editor?ltype=%s&type=editLimit&actionPath=" % self.ltype + self.PATH + "&value=" + rule[ 1 ]
|
||||
hasLimit = True
|
||||
elif rule[ 0 ] == "path":
|
||||
# 1 = path
|
||||
# Split the path into components
|
||||
splitPath = self.ATTRIB.splitPath( rule[ 1 ] )
|
||||
|
||||
# Add each element of the path to the list
|
||||
for x, component in enumerate( splitPath ):
|
||||
if x == 0:
|
||||
# library://path/
|
||||
listitem = xbmcgui.ListItem( label="%s: %s" % ( LANGUAGE(30204), self.ATTRIB.translatePath( component ) ) )
|
||||
commands.append( ( LANGUAGE(30100), "RunPlugin(plugin://plugin.library.node.editor?ltype=%s&type=deletenode&actionPath=" % self.ltype + self.PATH + "&node=path)" ) )
|
||||
action = "plugin://plugin.library.node.editor?ltype=%s&type=addPath&actionPath=" % self.ltype + self.PATH
|
||||
|
||||
# Get the rules
|
||||
rules = self.PATHRULE.getRulesForPath( splitPath[ 0 ] )
|
||||
if x != 0:
|
||||
# Specific component
|
||||
|
||||
# Add the listitem generated from the last component we processed
|
||||
listitem.addContextMenuItems( commands, replaceItems = True )
|
||||
xbmcplugin.addDirectoryItem( int(sys.argv[ 1 ]), action, listitem, isFolder=True )
|
||||
commands = []
|
||||
|
||||
# Get the rule for this component
|
||||
componentRule = self.PATHRULE.getMatchingRule( component, rules )
|
||||
translatedComponent = self.PATHRULE.translateComponent( componentRule, splitPath[ x ] )
|
||||
translatedValue = self.PATHRULE.translateValue( componentRule, splitPath, x )
|
||||
|
||||
listitem = xbmcgui.ListItem( label="%s: %s" % ( translatedComponent, translatedValue ) )
|
||||
commands.append( ( LANGUAGE(30100), "RunPlugin(plugin://plugin.library.node.editor?ltype=%s&type=deletePathRule&actionPath=%s&rule=%d)" %( self.ltype, self.PATH, x ) ) )
|
||||
action = "plugin://plugin.library.node.editor?ltype=%s&type=pathRule&actionPath=%s&rule=%d" % ( self.ltype, self.PATH, x )
|
||||
hasPath = True
|
||||
elif rule[ 0 ] == "rule":
|
||||
# 1 = field
|
||||
# 2 = operator
|
||||
# 3 = value (optional)
|
||||
# 4 = ruleNum
|
||||
if len(rule) == 3:
|
||||
translated = self.RULE.translateRule( [ rule[ 1 ], rule[ 2 ] ] )
|
||||
else:
|
||||
translated = self.RULE.translateRule( [ rule[ 1 ], rule[ 2 ], rule[ 3 ] ] )
|
||||
if translated[ 2 ][ 0 ] == "|NONE|":
|
||||
listitem = xbmcgui.ListItem( label="%s: %s %s" % ( LANGUAGE(30205), translated[ 0 ][ 0 ], translated[ 1 ][ 0 ] ) )
|
||||
else:
|
||||
listitem = xbmcgui.ListItem( label="%s: %s %s %s" % ( LANGUAGE(30205), translated[ 0 ][ 0 ], translated[ 1 ][ 0 ], translated[ 2 ][ 1 ] ) )
|
||||
commands.append( ( LANGUAGE(30100), "RunPlugin(plugin://plugin.library.node.editor?ltype=%s&type=deleteRule&actionPath=" % self.ltype + self.PATH + "&rule=" + str( rule[ 4 ] ) + ")" ) )
|
||||
action = "plugin://plugin.library.node.editor?ltype=%s&type=rule&actionPath=" % self.ltype + self.PATH + "&rule=" + str( rule[ 4 ] )
|
||||
rulecount += 1
|
||||
elif rule[ 0 ] == "match":
|
||||
# 1 = value
|
||||
listitem = xbmcgui.ListItem( label="%s: %s" % ( LANGUAGE(30206), self.ATTRIB.translateMatch( rule[ 1 ] ) ) )
|
||||
action = "plugin://plugin.library.node.editor?ltype=%s&type=editRulesMatch&actionPath=%s" %( self.ltype, self.PATH )
|
||||
hasGroup = True
|
||||
listitem.addContextMenuItems( commands, replaceItems = True )
|
||||
if rule[ 0 ] == "rule" or rule[ 0 ] == "order" or rule[ 0 ] == "path":
|
||||
xbmcplugin.addDirectoryItem( int(sys.argv[ 1 ]), action, listitem, isFolder=True )
|
||||
else:
|
||||
xbmcplugin.addDirectoryItem( int(sys.argv[ 1 ]), action, listitem, isFolder=False )
|
||||
if not hasContent and not hasPath:
|
||||
# Add content
|
||||
xbmcplugin.addDirectoryItem( int( sys.argv[ 1 ] ), "plugin://plugin.library.node.editor?ltype=%s&type=editContent&actionPath=" % self.ltype + self.PATH, xbmcgui.ListItem( label="* %s" %( LANGUAGE(30000) ) ) )
|
||||
if not hasOrder and hasContent:
|
||||
# Add order
|
||||
xbmcplugin.addDirectoryItem( int( sys.argv[ 1 ] ), "plugin://plugin.library.node.editor?ltype=%s&type=orderby&actionPath=" % self.ltype + self.PATH, xbmcgui.ListItem( label="* %s" %( LANGUAGE(30002) ) ), isFolder=True )
|
||||
if not hasGroup and hasContent:
|
||||
# Add group
|
||||
xbmcplugin.addDirectoryItem( int( sys.argv[ 1 ] ), "plugin://plugin.library.node.editor?ltype=%s&type=editGroup&actionPath=" % self.ltype + self.PATH + "&content=" + content, xbmcgui.ListItem( label="* %s" %( LANGUAGE(30004) ) ) )
|
||||
if not hasLimit and hasContent:
|
||||
# Add limit
|
||||
xbmcplugin.addDirectoryItem( int( sys.argv[ 1 ] ), "plugin://plugin.library.node.editor?ltype=%s&type=editLimit&actionPath=" % self.ltype + self.PATH + "&value=25", xbmcgui.ListItem( label="* %s" %( LANGUAGE(30003) ) ) )
|
||||
if not hasPath and not hasContent:
|
||||
# Add path
|
||||
xbmcplugin.addDirectoryItem( int( sys.argv[ 1 ] ), "plugin://plugin.library.node.editor?ltype=%s&type=addPath&actionPath=" % self.ltype + self.PATH, xbmcgui.ListItem( label="* %s" %( LANGUAGE(30001) ) ) )
|
||||
if hasContent:
|
||||
# Add rule
|
||||
xbmcplugin.addDirectoryItem( int( sys.argv[ 1 ] ), "plugin://plugin.library.node.editor?ltype=%s&type=rule&actionPath=" % self.ltype + self.PATH + "&rule=" + str( nextRule ), xbmcgui.ListItem( label="* %s" %( LANGUAGE(30005) ) ), isFolder = True )
|
||||
if hasPath:
|
||||
if "plugin://" not in splitPath[0][0]:
|
||||
# Add component
|
||||
xbmcplugin.addDirectoryItem( int( sys.argv[ 1 ] ), "plugin://plugin.library.node.editor?ltype=%s&type=pathRule&actionPath=%s&rule=%d" % ( self.ltype, self.PATH, x + 1 ), xbmcgui.ListItem( label="* %s" %( LANGUAGE(30009) ) ), isFolder = True )
|
||||
# Manually edit path
|
||||
xbmcplugin.addDirectoryItem( int( sys.argv[ 1 ] ), "plugin://plugin.library.node.editor?ltype=%s&type=editPath&actionPath=" % self.ltype + self.PATH + "&value=" + quote( rule[ 1 ] ), xbmcgui.ListItem( label="* %s" %( LANGUAGE(30010) ) ), isFolder = True )
|
||||
xbmcplugin.setContent(int(sys.argv[1]), 'files')
|
||||
xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))
|
||||
|
||||
def _parse_argv( self ):
|
||||
try:
|
||||
p = parse_qs(sys.argv[2][1:])
|
||||
for i in p.keys():
|
||||
p[i] = p[i][0]
|
||||
self.PARAMS = p
|
||||
except:
|
||||
p = parse_qs(sys.argv[1])
|
||||
for i in p.keys():
|
||||
p[i] = p[i][0]
|
||||
self.PARAMS = p
|
||||
if "path" in self.PARAMS:
|
||||
self.PATH = self.PARAMS[ "path" ]
|
||||
else:
|
||||
self.PATH = ""
|
||||
|
||||
def getRules( self, actionPath, justRules = False ):
|
||||
returnVal = []
|
||||
try:
|
||||
# Load the xml file
|
||||
tree = xmltree.parse( actionPath )
|
||||
root = tree.getroot()
|
||||
if justRules == False:
|
||||
# Look for a 'content'
|
||||
content = root.find( "content" )
|
||||
if content is not None:
|
||||
returnVal.append( ( "content", content.text ) )
|
||||
# Look for an 'order'
|
||||
order = root.find( "order" )
|
||||
if order is not None:
|
||||
if "direction" in order.attrib:
|
||||
returnVal.append( ( "order", order.text, order.attrib.get( "direction" ) ) )
|
||||
else:
|
||||
returnVal.append( ( "order", order.text ) )
|
||||
# Look for a 'group'
|
||||
group = root.find( "group" )
|
||||
if group is not None:
|
||||
returnVal.append( ( "group", group.text ) )
|
||||
# Look for a 'limit'
|
||||
limit = root.find( "limit" )
|
||||
if limit is not None:
|
||||
returnVal.append( ( "limit", limit.text ) )
|
||||
# Look for a 'path'
|
||||
path = root.find( "path" )
|
||||
if path is not None:
|
||||
returnVal.append( ( "path", path.text ) )
|
||||
# Save the current length of the returnVal - we'll insert Match here if there are two or more rules
|
||||
currentLen = len( returnVal )
|
||||
# Look for any rules
|
||||
ruleNum = 0
|
||||
if actionPath.endswith( "index.xml" ):
|
||||
# Load the rules from RULE module
|
||||
rules = self.RULE.getNodeRules( actionPath )
|
||||
if rules is not None:
|
||||
for rule in rules:
|
||||
returnVal.append( ( "rule", rule[ 0 ], rule[ 1 ], rule[ 2 ], ruleNum ) )
|
||||
ruleNum += 1
|
||||
return returnVal, len( rules )
|
||||
else:
|
||||
return returnVal, 0
|
||||
else:
|
||||
rules = root.findall( "rule" )
|
||||
# Process the rules
|
||||
if rules is not None:
|
||||
for rule in rules:
|
||||
value = rule.find( "value" )
|
||||
if value is not None and value.text is not None:
|
||||
translated = self.RULE.translateRule( [ rule.attrib.get( "field" ), rule.attrib.get( "operator" ), value.text ] )
|
||||
if not self.RULE.isNodeRule( translated, actionPath ):
|
||||
returnVal.append( ( "rule", rule.attrib.get( "field" ), rule.attrib.get( "operator" ), value.text, ruleNum ) )
|
||||
else:
|
||||
translated = self.RULE.translateRule( [ rule.attrib.get( "field" ), rule.attrib.get( "operator" ), "" ] )
|
||||
if not self.RULE.isNodeRule( translated, actionPath ):
|
||||
returnVal.append( ( "rule", rule.attrib.get( "field" ), rule.attrib.get( "operator" ), "", ruleNum ) )
|
||||
ruleNum += 1
|
||||
# Get any current match value if there are more than two rules
|
||||
# (if there's only one, the match value doesn't matter)
|
||||
if ruleNum >= 2:
|
||||
matchRules = "all"
|
||||
match = root.find( "match" )
|
||||
if match is not None:
|
||||
matchRules = match.text
|
||||
returnVal.insert( currentLen, ( "match", matchRules ) )
|
||||
return returnVal, len( rules )
|
||||
return returnVal, 0
|
||||
except:
|
||||
print_exc()
|
||||
|
||||
def listNodes( self, targetDir, nodes ):
|
||||
dirs, files = xbmcvfs.listdir( targetDir )
|
||||
for dir in dirs:
|
||||
self.parseNode( os.path.join( targetDir, dir ), nodes )
|
||||
for file in files:
|
||||
self.parseItem( os.path.join( targetDir, file ), nodes )
|
||||
|
||||
def parseNode( self, node, nodes ):
|
||||
# If the folder we've been passed contains an index.xml, send that file to be processed
|
||||
if os.path.exists( os.path.join( node, "index.xml" ) ):
|
||||
# BETA2 ONLY CODE
|
||||
self.RULE.moveNodeRuleToAppdata( node, os.path.join( node, "index.xml" ) )
|
||||
# /BETA2 ONLY CODE
|
||||
self.parseItem( os.path.join( node, "index.xml" ), nodes, True, node )
|
||||
|
||||
def parseItem( self, file, nodes, isFolder = False, origFolder = None ):
|
||||
if not isFolder and file.endswith( "index.xml" ):
|
||||
return
|
||||
try:
|
||||
# Load the xml file
|
||||
tree = xmltree.parse( file )
|
||||
root = tree.getroot()
|
||||
# Get the item index
|
||||
if "order" in tree.getroot().attrib:
|
||||
index = tree.getroot().attrib.get( "order" )
|
||||
origIndex = index
|
||||
while int( index ) in nodes:
|
||||
index = int( index )
|
||||
index += 1
|
||||
index = str( index )
|
||||
else:
|
||||
self.indexCounter -= 1
|
||||
index = str( self.indexCounter )
|
||||
origIndex = "-"
|
||||
# Get label and icon
|
||||
label = root.find( "label" ).text
|
||||
icon = root.find( "icon" )
|
||||
if icon is not None:
|
||||
icon = icon.text
|
||||
else:
|
||||
icon = ""
|
||||
# Add it to our list of nodes
|
||||
if isFolder:
|
||||
nodes[ int( index ) ] = [ label, icon, quote( origFolder ), "folder", origIndex ]
|
||||
else:
|
||||
nodes[ int( index ) ] = [ label, icon, file, "item", origIndex ]
|
||||
except:
|
||||
print_exc()
|
||||
|
||||
def getViewElement( self, file, element, newvalue ):
|
||||
try:
|
||||
# Load the file
|
||||
tree = xmltree.parse( file )
|
||||
root = tree.getroot()
|
||||
# Change the element
|
||||
node = root.find( element )
|
||||
if node is not None:
|
||||
return node.text
|
||||
else:
|
||||
return ""
|
||||
except:
|
||||
print_exc()
|
||||
|
||||
def changeViewElement( self, file, element, newvalue ):
|
||||
try:
|
||||
# Load the file
|
||||
tree = xmltree.parse( file )
|
||||
root = tree.getroot()
|
||||
# If the element is content, we can only delete this if there are no
|
||||
# rules, limits, orders
|
||||
if element == "content":
|
||||
rule = root.find( "rule" )
|
||||
order = root.find( "order" )
|
||||
limit = root.find( "limit" )
|
||||
if rule is not None or order is not None or limit is not None:
|
||||
xbmcgui.Dialog().ok( ADDONNAME, LANGUAGE( 30404 ) )
|
||||
return
|
||||
# Find the element
|
||||
node = root.find( element )
|
||||
if node is not None:
|
||||
# If we've been passed an empty value, delete the node
|
||||
if newvalue == "":
|
||||
root.remove( node )
|
||||
else:
|
||||
node.text = newvalue
|
||||
else:
|
||||
# Add a new node
|
||||
if newvalue != "":
|
||||
xmltree.SubElement( root, element ).text = newvalue
|
||||
# Pretty print and save
|
||||
self.indent( root )
|
||||
tree.write( file, encoding="UTF-8" )
|
||||
except:
|
||||
print_exc()
|
||||
|
||||
def getRootAttrib( self, file, attrib ):
|
||||
try:
|
||||
# Load the file
|
||||
tree = xmltree.parse( file )
|
||||
root = tree.getroot()
|
||||
# Find the element
|
||||
if attrib in root.attrib:
|
||||
return root.attrib.get( attrib )
|
||||
else:
|
||||
return ""
|
||||
except:
|
||||
print_exc()
|
||||
|
||||
def changeRootAttrib( self, file, attrib, newvalue ):
|
||||
try:
|
||||
# Load the file
|
||||
tree = xmltree.parse( file )
|
||||
root = tree.getroot()
|
||||
# If empty newvalue, delete the attribute
|
||||
if newvalue == "":
|
||||
if attrib in root.attrib:
|
||||
root.attrib.pop( attrib )
|
||||
else:
|
||||
# Change or add the attribute
|
||||
root.set( attrib, newvalue )
|
||||
# Pretty print and save
|
||||
self.indent( root )
|
||||
tree.write( file, encoding="UTF-8" )
|
||||
except:
|
||||
print_exc()
|
||||
|
||||
def copyNode(self, dirs, files, target, origin):
|
||||
for file in files:
|
||||
success = xbmcvfs.copy( os.path.join( origin, file ), os.path.join( target, file ) )
|
||||
for dir in dirs:
|
||||
nextDirs, nextFiles = xbmcvfs.listdir( os.path.join( origin, dir ) )
|
||||
self.copyNode( nextDirs, nextFiles, os.path.join( target, dir ), os.path.join( origin, dir ) )
|
||||
|
||||
# in-place prettyprint formatter
|
||||
def indent( self, elem, level=0 ):
|
||||
i = "\n" + level*"\t"
|
||||
if len(elem):
|
||||
if not elem.text or not elem.text.strip():
|
||||
elem.text = i + "\t"
|
||||
if not elem.tail or not elem.tail.strip():
|
||||
elem.tail = i
|
||||
for elem in elem:
|
||||
self.indent(elem, level+1)
|
||||
if not elem.tail or not elem.tail.strip():
|
||||
elem.tail = i
|
||||
else:
|
||||
if level and (not elem.tail or not elem.tail.strip()):
|
||||
elem.tail = i
|
||||
|
||||
# Slugify functions
|
||||
def smart_truncate(string, max_length=0, word_boundaries=False, separator=' '):
|
||||
string = string.strip(separator)
|
||||
if not max_length:
|
||||
return string
|
||||
if len(string) < max_length:
|
||||
return string
|
||||
if not word_boundaries:
|
||||
return string[:max_length].strip(separator)
|
||||
if separator not in string:
|
||||
return string[:max_length]
|
||||
truncated = ''
|
||||
for word in string.split(separator):
|
||||
if word:
|
||||
next_len = len(truncated) + len(word) + len(separator)
|
||||
if next_len <= max_length:
|
||||
truncated += '{0}{1}'.format(word, separator)
|
||||
if not truncated:
|
||||
truncated = string[:max_length]
|
||||
return truncated.strip(separator)
|
||||
|
||||
def slugify(self, text, entities=True, decimal=True, hexadecimal=True, max_length=0, word_boundary=False, separator='-', convertInteger=False):
|
||||
# Handle integers
|
||||
if convertInteger and text.isdigit():
|
||||
text = "NUM-" + text
|
||||
# decode unicode ( ??? = Ying Shi Ma)
|
||||
text = unidecode(text)
|
||||
# character entity reference
|
||||
if entities:
|
||||
text = CHAR_ENTITY_REXP.sub(lambda m: unichr(name2codepoint[m.group(1)]), text)
|
||||
# decimal character reference
|
||||
if decimal:
|
||||
try:
|
||||
text = DECIMAL_REXP.sub(lambda m: unichr(int(m.group(1))), text)
|
||||
except:
|
||||
pass
|
||||
# hexadecimal character reference
|
||||
if hexadecimal:
|
||||
try:
|
||||
text = HEX_REXP.sub(lambda m: unichr(int(m.group(1), 16)), text)
|
||||
except:
|
||||
pass
|
||||
# translate
|
||||
text = unicodedata.normalize('NFKD', text)
|
||||
if sys.version_info < (3,):
|
||||
text = text.encode('ascii', 'ignore')
|
||||
# replace unwanted characters
|
||||
text = REPLACE1_REXP.sub('', text.lower()) # replace ' with nothing instead with -
|
||||
text = REPLACE2_REXP.sub('-', text.lower())
|
||||
# remove redundant -
|
||||
text = REMOVE_REXP.sub('-', text).strip('-')
|
||||
# smart truncate if requested
|
||||
if max_length > 0:
|
||||
text = smart_truncate(text, max_length, word_boundary, '-')
|
||||
if separator != '-':
|
||||
text = text.replace('-', separator)
|
||||
return text
|
||||
|
||||
def getVideoLibraryLType():
|
||||
json_query = xbmc.executeJSONRPC('{ "jsonrpc": "2.0", "id": 0, "method": "Settings.GetSettingValue", "params": {"setting": "myvideos.flatten"}}')
|
||||
json_response = json.loads(json_query)
|
||||
|
||||
if json_response.get('result') and json_response['result'].get('value'):
|
||||
if json_response['result']['value']:
|
||||
return "video_flat"
|
||||
|
||||
return "video"
|
||||
|
||||
def run(args):
|
||||
log('script version %s started' % ADDONVERSION)
|
||||
ltype = ''
|
||||
if args[2] == '':
|
||||
videoltype = getVideoLibraryLType()
|
||||
xbmcplugin.addDirectoryItem( int( args[ 1 ] ), "plugin://plugin.library.node.editor?ltype=%s" %( videoltype ), xbmcgui.ListItem( label=LANGUAGE(30091) ), isFolder=True )
|
||||
xbmcplugin.addDirectoryItem( int( args[ 1 ] ), "plugin://plugin.library.node.editor?ltype=music", xbmcgui.ListItem( label=LANGUAGE(30092) ), isFolder=True )
|
||||
xbmcplugin.setContent(int(args[1]), 'files')
|
||||
xbmcplugin.endOfDirectory(handle=int(args[1]))
|
||||
else:
|
||||
params = dict( arg.split( "=" ) for arg in args[ 2 ][1:].split( "&" ) )
|
||||
ltype = params['ltype']
|
||||
if ltype != '':
|
||||
RULE = rules.RuleFunctions(ltype)
|
||||
ATTRIB = viewattrib.ViewAttribFunctions(ltype)
|
||||
PATHRULE = pathrules.PathRuleFunctions(ltype)
|
||||
PATHRULE.ATTRIB = ATTRIB
|
||||
ORDERBY = orderby.OrderByFunctions(ltype)
|
||||
Main(params, ltype, RULE, ATTRIB, PATHRULE, ORDERBY)
|
||||
|
||||
log('script stopped')
|
||||
@@ -0,0 +1,17 @@
|
||||
|
||||
import sys
|
||||
import os
|
||||
import xbmc, xbmcvfs, xbmcaddon
|
||||
|
||||
ADDON = xbmcaddon.Addon()
|
||||
ADDONID = ADDON.getAddonInfo('id')
|
||||
ADDONVERSION = ADDON.getAddonInfo('version')
|
||||
LANGUAGE = ADDON.getLocalizedString
|
||||
CWD = ADDON.getAddonInfo('path')
|
||||
ADDONNAME = ADDON.getAddonInfo('name')
|
||||
DATAPATH = xbmcvfs.translatePath(xbmcaddon.Addon().getAddonInfo('profile'))
|
||||
DEFAULTPATH = os.path.join( CWD, 'resources' )
|
||||
|
||||
def log(txt):
|
||||
message = u'%s: %s' % (ADDONID, txt)
|
||||
xbmc.log(msg=message, level=xbmc.LOGDEBUG)
|
||||
@@ -0,0 +1,98 @@
|
||||
# coding=utf-8
|
||||
import sys
|
||||
import xbmc, xbmcaddon, xbmcgui
|
||||
|
||||
from resources.lib.common import *
|
||||
|
||||
|
||||
def getNewOrder( currentPositions, indexToMove ):
|
||||
# Show select dialog
|
||||
w = ShowDialog( "DialogSelect.xml", CWD, order=currentPositions, focus=indexToMove, windowtitle=LANGUAGE(30104) )
|
||||
w.doModal()
|
||||
newOrder = w.newOrder
|
||||
del w
|
||||
|
||||
return newOrder
|
||||
|
||||
class ShowDialog( xbmcgui.WindowXMLDialog ):
|
||||
def __init__( self, *args, **kwargs ):
|
||||
xbmcgui.WindowXMLDialog.__init__( self )
|
||||
self.order = kwargs.get( "order" )
|
||||
self.windowtitle = kwargs.get( "windowtitle" )
|
||||
self.selectedItem = kwargs.get( "focus" )
|
||||
self.newOrder = []
|
||||
|
||||
def onInit(self):
|
||||
self.list = self.getControl(3)
|
||||
|
||||
# Set visibility
|
||||
self.getControl(3).setVisible(True)
|
||||
self.getControl(3).setEnabled(True)
|
||||
self.getControl(5).setVisible(False)
|
||||
self.getControl(6).setVisible(False)
|
||||
self.getControl(1).setLabel(self.windowtitle)
|
||||
|
||||
# Set Cancel label
|
||||
self.getControl(7).setLabel(xbmc.getLocalizedString(222))
|
||||
|
||||
# Add all the items to the list
|
||||
for i, key in enumerate( sorted( self.order ) ):
|
||||
# Get the label and localise if necessary
|
||||
label = self.order[ key ][ 0 ]
|
||||
if label.isdigit():
|
||||
label = xbmc.getLocalizedString( int( label ) )
|
||||
if label == "":
|
||||
label = self.order[ key ][ 0 ]
|
||||
if self.order[ key ][ 3 ] == "folder":
|
||||
label = "%s >" %( label )
|
||||
|
||||
# Create the listitem and add it
|
||||
listitem = xbmcgui.ListItem( label=label )
|
||||
self.list.addItem( listitem )
|
||||
|
||||
# And add it to the list that we'll eventually return
|
||||
self.newOrder.append( self.order[ key ] )
|
||||
|
||||
# If it's the item we're moving, save it separately
|
||||
if i == self.selectedItem:
|
||||
self.itemMoving = self.order[ key ]
|
||||
|
||||
# Set focus
|
||||
self.list.selectItem(self.selectedItem)
|
||||
self.setFocus(self.list)
|
||||
|
||||
def onAction(self, action):
|
||||
# Check if the selected item has changed
|
||||
if self.list.getSelectedPosition() != self.selectedItem:
|
||||
# Remove the item we're moving from the list of items
|
||||
self.newOrder.pop( self.selectedItem )
|
||||
|
||||
# Add the item we're moving at its new location
|
||||
self.newOrder.insert( self.list.getSelectedPosition(), self.itemMoving )
|
||||
|
||||
# Update its current current position
|
||||
self.selectedItem = self.list.getSelectedPosition()
|
||||
|
||||
# Update the labels of all list items
|
||||
for i in range( len( self.newOrder ) ):
|
||||
item = self.list.getListItem( i )
|
||||
label = self.newOrder[ i ][ 0 ]
|
||||
if label.isdigit():
|
||||
label = xbmc.getLocalizedString( int( label ) )
|
||||
if label == "":
|
||||
label = self.newOrder[ i ][ 0 ]
|
||||
if self.newOrder[ i ][ 3 ] == "folder":
|
||||
label = "%s >" %( label )
|
||||
if item.getLabel() != label:
|
||||
item.setLabel( label )
|
||||
|
||||
if action.getId() in ( 9, 10, 92, 216, 247, 257, 275, 61467, 61448, ):
|
||||
self.close()
|
||||
return
|
||||
|
||||
def onClick(self, controlID):
|
||||
if controlID == 7:
|
||||
# Cancel button
|
||||
self.newOrder = None
|
||||
|
||||
self.close()
|
||||
@@ -0,0 +1,195 @@
|
||||
# coding=utf-8
|
||||
import os, sys
|
||||
import xbmc, xbmcaddon, xbmcplugin, xbmcgui, xbmcvfs
|
||||
import xml.etree.ElementTree as xmltree
|
||||
from traceback import print_exc
|
||||
from urllib.parse import unquote
|
||||
|
||||
from resources.lib.common import *
|
||||
|
||||
class OrderByFunctions():
|
||||
def __init__(self, ltype):
|
||||
self.ltype = ltype
|
||||
|
||||
def _load_rules( self ):
|
||||
if self.ltype.startswith('video'):
|
||||
overridepath = os.path.join( DEFAULTPATH , "videorules.xml" )
|
||||
else:
|
||||
overridepath = os.path.join( DEFAULTPATH , "musicrules.xml" )
|
||||
try:
|
||||
tree = xmltree.parse( overridepath )
|
||||
return tree
|
||||
except:
|
||||
return None
|
||||
|
||||
def translateOrderBy( self, rule ):
|
||||
# Load the rules
|
||||
tree = self._load_rules()
|
||||
hasValue = True
|
||||
if rule[ 0 ] == "sorttitle":
|
||||
rule[ 0 ] = "title"
|
||||
if rule[ 0 ] != "random":
|
||||
# Get the field we're ordering by
|
||||
elems = tree.getroot().find( "matches" ).findall( "match" )
|
||||
for elem in elems:
|
||||
if elem.attrib.get( "name" ) == rule[ 0 ]:
|
||||
match = xbmc.getLocalizedString( int( elem.find( "label" ).text ) )
|
||||
else:
|
||||
# We'll manually set for random
|
||||
match = xbmc.getLocalizedString( 590 )
|
||||
# Get localization of direction
|
||||
direction = None
|
||||
elems = tree.getroot().find( "orderby" ).findall( "type" )
|
||||
for elem in elems:
|
||||
if elem.text == rule[ 1 ]:
|
||||
direction = xbmc.getLocalizedString( int( elem.attrib.get( "label" ) ) )
|
||||
directionVal = rule[ 1 ]
|
||||
if direction is None:
|
||||
direction = xbmc.getLocalizedString( int( tree.getroot().find( "orderby" ).find( "type" ).attrib.get( "label" ) ) )
|
||||
directionVal = tree.getroot().find( "orderby" ).find( "type" ).text
|
||||
return [ [ match, rule[ 0 ] ], [ direction, directionVal ] ]
|
||||
|
||||
def displayOrderBy( self, actionPath):
|
||||
try:
|
||||
# Load the xml file
|
||||
tree = xmltree.parse( unquote(actionPath) )
|
||||
root = tree.getroot()
|
||||
# Get the content type
|
||||
content = root.find( "content" ).text
|
||||
# Get the order node
|
||||
orderby = root.find( "order" )
|
||||
if orderby is None:
|
||||
# There is no orderby element, so add one
|
||||
self.newOrderBy( tree, actionPath )
|
||||
orderby = root.find( "order" )
|
||||
match = orderby.text
|
||||
if "direction" in orderby.attrib:
|
||||
direction = orderby.attrib.get( "direction" )
|
||||
else:
|
||||
direction = ""
|
||||
translated = self.translateOrderBy( [match, direction ] )
|
||||
listitem = xbmcgui.ListItem( label="%s" % ( translated[ 0 ][ 0 ] ) )
|
||||
action = "plugin://plugin.library.node.editor?ltype=%s&type=editOrderBy&actionPath=" % self.ltype + actionPath + "&content=" + content + "&default=" + translated[0][1]
|
||||
xbmcplugin.addDirectoryItem( int(sys.argv[ 1 ]), action, listitem, isFolder=False )
|
||||
listitem = xbmcgui.ListItem( label="%s" % ( translated[ 1 ][ 0 ] ) )
|
||||
action = "plugin://plugin.library.node.editor?ltype=%s&type=editOrderByDirection&actionPath=" % self.ltype + actionPath + "&default=" + translated[1][1]
|
||||
xbmcplugin.addDirectoryItem( int(sys.argv[ 1 ]), action, listitem, isFolder=False )
|
||||
xbmcplugin.setContent(int(sys.argv[1]), 'files')
|
||||
xbmcplugin.endOfDirectory(int(sys.argv[1]))
|
||||
except:
|
||||
print_exc()
|
||||
|
||||
def editOrderBy( self, actionPath, content, default ):
|
||||
# Load all operator groups
|
||||
tree = self._load_rules().getroot()
|
||||
elems = tree.find( "matches" ).findall( "match" )
|
||||
selectName = []
|
||||
selectValue = []
|
||||
# Find the matches for the content we've been passed
|
||||
for elem in elems:
|
||||
contentMatch = elem.find( content )
|
||||
if contentMatch is not None:
|
||||
selectName.append( xbmc.getLocalizedString( int( elem.find( "label" ).text ) ) )
|
||||
selectValue.append( elem.attrib.get( "name" ) )
|
||||
# Add a random element
|
||||
selectName.append( xbmc.getLocalizedString( 590 ) )
|
||||
selectValue.append( "random" )
|
||||
# Let the user select an operator
|
||||
selectedOperator = xbmcgui.Dialog().select( LANGUAGE( 30314 ), selectName )
|
||||
# If the user selected no operator...
|
||||
if selectedOperator == -1:
|
||||
return
|
||||
returnVal = selectValue[ selectedOperator ]
|
||||
if returnVal == "title":
|
||||
returnVal = "sorttitle"
|
||||
self.writeUpdatedOrderBy( actionPath, field = returnVal )
|
||||
|
||||
def editDirection( self, actionPath, direction ):
|
||||
# Load all directions
|
||||
tree = self._load_rules().getroot()
|
||||
elems = tree.find( "orderby" ).findall( "type" )
|
||||
selectName = []
|
||||
selectValue = []
|
||||
# Find the group we've been passed and load its operators
|
||||
for elem in elems:
|
||||
selectName.append( xbmc.getLocalizedString( int( elem.attrib.get( "label" ) ) ) )
|
||||
selectValue.append( elem.text )
|
||||
# Let the user select an operator
|
||||
selectedOperator = xbmcgui.Dialog().select( LANGUAGE( 30315 ), selectName )
|
||||
# If the user selected no operator...
|
||||
if selectedOperator == -1:
|
||||
return
|
||||
self.writeUpdatedOrderBy( actionPath, direction = selectValue[ selectedOperator ] )
|
||||
|
||||
def writeUpdatedOrderBy( self, actionPath, field = None, direction = None ):
|
||||
# This function writes an updated orderby rule
|
||||
try:
|
||||
# Load the xml file
|
||||
tree = xmltree.parse( unquote(unquote(actionPath)) )
|
||||
root = tree.getroot()
|
||||
# Get all the rules
|
||||
orderby = root.find( "order" )
|
||||
if field is not None:
|
||||
orderby.text = field
|
||||
if direction is not None:
|
||||
orderby.set( "direction", direction )
|
||||
# Save the file
|
||||
self.indent( root )
|
||||
tree.write( unquote(actionPath), encoding="UTF-8" )
|
||||
except:
|
||||
print_exc()
|
||||
|
||||
def newOrderBy( self, tree, actionPath ):
|
||||
# This function adds a new OrderBy, with default match and direction
|
||||
try:
|
||||
# Load the xml file
|
||||
#tree = xmltree.parse( actionPath )
|
||||
root = tree.getroot()
|
||||
# Get the content type
|
||||
content = root.find( "content" )
|
||||
if content is None:
|
||||
xbmcgui.Dialog().ok( ADDONNAME, LANGUAGE( 30406 ) )
|
||||
return
|
||||
else:
|
||||
content = content.text
|
||||
# Find the default match for this content type
|
||||
ruleTree = self._load_rules().getroot()
|
||||
elems = ruleTree.find( "matches" ).findall( "match" )
|
||||
match = "title"
|
||||
for elem in elems:
|
||||
contentCheck = elem.find( content )
|
||||
if contentCheck is not None:
|
||||
# We've found the first match for this type
|
||||
match = elem.attrib.get( "name" )
|
||||
break
|
||||
if match == "title":
|
||||
match = "sorttitle"
|
||||
# Find the default direction
|
||||
elem = ruleTree.find( "orderby" ).find( "type" )
|
||||
direction = elem.text
|
||||
# Write the new rule
|
||||
newRule = xmltree.SubElement( root, "order" )
|
||||
newRule.text = match
|
||||
newRule.set( "direction", direction )
|
||||
# Save the file
|
||||
self.indent( root )
|
||||
tree.write( unquote( actionPath ), encoding="UTF-8" )
|
||||
except:
|
||||
print_exc()
|
||||
|
||||
# in-place prettyprint formatter
|
||||
def indent( self, elem, level=0 ):
|
||||
i = "\n" + level*"\t"
|
||||
if len(elem):
|
||||
if not elem.text or not elem.text.strip():
|
||||
elem.text = i + "\t"
|
||||
if not elem.tail or not elem.tail.strip():
|
||||
elem.tail = i
|
||||
for elem in elem:
|
||||
self.indent(elem, level+1)
|
||||
if not elem.tail or not elem.tail.strip():
|
||||
elem.tail = i
|
||||
else:
|
||||
if level and (not elem.tail or not elem.tail.strip()):
|
||||
elem.tail = i
|
||||
|
||||
@@ -0,0 +1,377 @@
|
||||
# coding=utf-8
|
||||
import os, sys, shutil
|
||||
import xbmc, xbmcaddon, xbmcplugin, xbmcgui, xbmcvfs
|
||||
import xml.etree.ElementTree as xmltree
|
||||
import json
|
||||
from traceback import print_exc
|
||||
from urllib.parse import quote, unquote
|
||||
|
||||
from resources.lib.common import *
|
||||
|
||||
class PathRuleFunctions():
|
||||
def __init__(self, ltype):
|
||||
self.nodeRules = None
|
||||
self.ATTRIB = None
|
||||
self.ltype = ltype
|
||||
|
||||
def _load_rules( self ):
|
||||
if self.ltype.startswith('video'):
|
||||
overridepath = os.path.join( DEFAULTPATH , "videorules.xml" )
|
||||
else:
|
||||
overridepath = os.path.join( DEFAULTPATH , "musicrules.xml" )
|
||||
try:
|
||||
tree = xmltree.parse( overridepath )
|
||||
return tree
|
||||
except:
|
||||
return None
|
||||
|
||||
def translateComponent( self, component, splitPath ):
|
||||
if component[ 0 ] is None:
|
||||
return splitPath[ 0 ]
|
||||
if component[0].isdigit():
|
||||
string = LANGUAGE( int( component[ 0 ] ) )
|
||||
if string != "": return string
|
||||
return xbmc.getLocalizedString( int( component[ 0 ] ) )
|
||||
else:
|
||||
return component[ 0 ]
|
||||
|
||||
def translateValue( self, rule, splitPath, ruleNum ):
|
||||
if splitPath[ ruleNum ][ 1 ] == "":
|
||||
return "<No value>"
|
||||
|
||||
if rule[ 1 ] == "year" or rule[ 2 ] != "integer" or rule[ 3 ] is None:
|
||||
return splitPath[ ruleNum ][ 1 ]
|
||||
|
||||
try:
|
||||
value = int( splitPath[ ruleNum ][ 1 ] )
|
||||
except:
|
||||
return splitPath[ ruleNum ][ 1 ]
|
||||
|
||||
json_query = xbmc.executeJSONRPC('{ "jsonrpc": "2.0", "id": 0, "method": "Files.GetDirectory", "params": { "properties": ["title"], "directory": "%s", "media": "files" } }' % rule[ 3 ] )
|
||||
json_response = json.loads(json_query)
|
||||
listings = []
|
||||
values = []
|
||||
# Add all directories returned by the json query
|
||||
if json_response.get('result') and json_response['result'].get('files') and json_response['result']['files'] is not None:
|
||||
for item in json_response['result']['files']:
|
||||
if "id" in item and item[ "id" ] == value:
|
||||
return "(%d) %s" %( value, item[ "label" ] )
|
||||
|
||||
# Didn't find a match
|
||||
return "%s (%s)" %( splitPath[ ruleNum ][ 1 ], xbmc.getLocalizedString(13205) )
|
||||
|
||||
def displayRule( self, actionPath, ruleNum ):
|
||||
try:
|
||||
# Load the xml file
|
||||
tree = xmltree.parse( unquote(actionPath) )
|
||||
root = tree.getroot()
|
||||
# Get the path
|
||||
path = root.find( "path" ).text
|
||||
# Split the path element
|
||||
splitPath = self.ATTRIB.splitPath( path )
|
||||
# Get the rules
|
||||
rules = self.getRulesForPath( splitPath[ 0 ] )
|
||||
if len( splitPath ) == int( ruleNum ):
|
||||
# This rule doesn't exist - create it
|
||||
self.ATTRIB.writeUpdatedPath( actionPath, ( ruleNum, rules[ 0 ][ 1 ], "" ) )
|
||||
rule = rules[ 0 ]
|
||||
translatedValue = "<No value>"
|
||||
else:
|
||||
# Find the matching rule
|
||||
rule = self.getMatchingRule( splitPath[ ruleNum ], rules )
|
||||
translatedValue = self.translateValue( rule, splitPath, ruleNum )
|
||||
|
||||
#Component
|
||||
listitem = xbmcgui.ListItem( label="%s" % ( self.translateComponent( rule, splitPath[ruleNum] ) ) )
|
||||
action = "plugin://plugin.library.node.editor?ltype=%s&type=editPathMatch&actionPath=%s&rule=%d" %( self.ltype, actionPath, ruleNum )
|
||||
xbmcplugin.addDirectoryItem( int(sys.argv[ 1 ]), action, listitem, isFolder=False )
|
||||
|
||||
# Value
|
||||
listitem = xbmcgui.ListItem( label="%s" % ( translatedValue ) )
|
||||
action = "plugin://plugin.library.node.editor?ltype=%s&type=editPathValue&actionPath=%s&rule=%s" %( self.ltype, actionPath, ruleNum )
|
||||
xbmcplugin.addDirectoryItem( int(sys.argv[ 1 ]), action, listitem, isFolder=False )
|
||||
# Browse
|
||||
if rule[ 3 ] is not None:
|
||||
listitem = xbmcgui.ListItem( label=LANGUAGE(30107) )
|
||||
action = "plugin://plugin.library.node.editor?ltype=%s&type=browsePathValue&actionPath=%s&rule=%s" %( self.ltype, actionPath, ruleNum )
|
||||
xbmcplugin.addDirectoryItem( int(sys.argv[ 1 ]), action, listitem, isFolder=False )
|
||||
|
||||
xbmcplugin.setContent(int(sys.argv[1]), 'files')
|
||||
xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))
|
||||
return
|
||||
except:
|
||||
log( "Failed" )
|
||||
print_exc()
|
||||
|
||||
def getRulesForPath( self, path ):
|
||||
# This function gets all valid rules for a given path
|
||||
# Load the rules
|
||||
tree = self._load_rules()
|
||||
subSearch = None
|
||||
content = []
|
||||
elems = tree.getroot().find( "paths" ).findall( "type" )
|
||||
for elem in elems:
|
||||
if elem.attrib.get( "name" ) == path[ 0 ]:
|
||||
for contentType in elem.findall( "content" ):
|
||||
content.append( contentType.text )
|
||||
subSearch = elem
|
||||
break
|
||||
|
||||
if path[ 1 ] and subSearch is not None:
|
||||
for elem in subSearch.findall( "type" ):
|
||||
if elem.attrib.get( "name" ) == path[ 1 ]:
|
||||
if elem.find( "content" ):
|
||||
content = []
|
||||
for contentType in elem.findall( "content" ):
|
||||
content.append( contentType.text )
|
||||
break
|
||||
|
||||
rules = []
|
||||
for rule in tree.getroot().find( "pathRules" ).findall( "rule" ):
|
||||
# Can it be browsed
|
||||
if rule.find( "browse" ) is not None:
|
||||
browse = rule.find( "browse" ).text
|
||||
else:
|
||||
browse = None
|
||||
if len( content ) == 0:
|
||||
rules.append( ( rule.attrib.get( "label" ), rule.attrib.get( "name" ), rule.find( "value" ).text, browse ) )
|
||||
else:
|
||||
for contentType in rule.findall( "content" ):
|
||||
if contentType.text in content:
|
||||
# If the root of the browse is changed dependant on what we're looking at, replace
|
||||
# it now with the correct content
|
||||
if browse is not None and "::root::" in browse:
|
||||
browse = browse.replace( "::root::", content[ 0 ] )
|
||||
rules.append( ( rule.attrib.get( "label" ), rule.attrib.get( "name" ), rule.find( "value" ).text, browse ) )
|
||||
|
||||
if len( rules ) == 0:
|
||||
return [ ( None, "property", "string", None ) ]
|
||||
return rules
|
||||
|
||||
def getMatchingRule( self, component, rules ):
|
||||
# This function matches a component to its rule
|
||||
for rule in rules:
|
||||
if rule[ 1 ] == component[ 0 ]:
|
||||
return rule
|
||||
|
||||
# No rule matched, return an empty one
|
||||
return ( None, "property", "string", None )
|
||||
|
||||
def editMatch( self, actionPath, ruleNum ):
|
||||
# Change the match element of a path component
|
||||
|
||||
# Load the xml file
|
||||
tree = xmltree.parse( unquote(actionPath) )
|
||||
root = tree.getroot()
|
||||
# Get the path
|
||||
path = root.find( "path" ).text
|
||||
# Split the path element
|
||||
splitPath = self.ATTRIB.splitPath( path )
|
||||
# Get the rules and current value
|
||||
rules = self.getRulesForPath( splitPath[ 0 ] )
|
||||
currentValue = splitPath[ ruleNum ][ 1 ]
|
||||
|
||||
if rules[ 0 ][ 0 ] is None:
|
||||
# There are no available choices
|
||||
self.manuallyEditMatch( actionPath, ruleNum, splitPath[ ruleNum ][ 0 ], currentValue )
|
||||
return
|
||||
|
||||
# Build list of rules to choose from
|
||||
selectName = []
|
||||
for rule in rules:
|
||||
selectName.append( self.translateComponent( rule, None ) )
|
||||
|
||||
# Add a manual option
|
||||
selectName.append( LANGUAGE( 30408 ) )
|
||||
|
||||
# Let the user select an operator
|
||||
selectedOperator = xbmcgui.Dialog().select( LANGUAGE( 30305 ), selectName )
|
||||
# If the user selected no operator...
|
||||
if selectedOperator == -1:
|
||||
return
|
||||
elif selectedOperator == len( rules ):
|
||||
# User choose custom property
|
||||
self.manuallyEditMatch( actionPath, ruleNum, splitPath[ ruleNum ][ 0 ], currentValue )
|
||||
return
|
||||
else:
|
||||
self.ATTRIB.writeUpdatedPath( actionPath, ( ruleNum, rules[ selectedOperator ][ 1 ], currentValue ) )
|
||||
|
||||
def manuallyEditMatch( self, actionPath, ruleNum, currentName, currentValue ):
|
||||
type = xbmcgui.INPUT_ALPHANUM
|
||||
returnVal = xbmcgui.Dialog().input( LANGUAGE( 30318 ), currentName, type=type )
|
||||
if returnVal != "":
|
||||
self.ATTRIB.writeUpdatedPath( actionPath, ( ruleNum, returnVal.decode( "utf-8" ), currentValue ) )
|
||||
|
||||
def editValue( self, actionPath, ruleNum ):
|
||||
# Let the user edit the value of a path component
|
||||
|
||||
# Load the xml file
|
||||
tree = xmltree.parse( unquote(actionPath) )
|
||||
root = tree.getroot()
|
||||
# Get the path
|
||||
path = root.find( "path" ).text
|
||||
# Split the path element
|
||||
splitPath = self.ATTRIB.splitPath( path )
|
||||
# Get the rules and current value
|
||||
rules = self.getRulesForPath( splitPath[ 0 ] )
|
||||
rule = self.getMatchingRule( splitPath[ ruleNum ], rules )
|
||||
|
||||
if rule[ 2 ] == "boolean":
|
||||
# Let the user select a boolean
|
||||
selectedBool = xbmcgui.Dialog().select( LANGUAGE( 30307 ), [ xbmc.getLocalizedString(20122), xbmc.getLocalizedString(20424) ] )
|
||||
# If the user selected nothing...
|
||||
if selectedBool == -1:
|
||||
return
|
||||
value = "true"
|
||||
if selectedBool == 1:
|
||||
value = "false"
|
||||
self.ATTRIB.writeUpdatedPath( actionPath, ( ruleNum, splitPath[ ruleNum][ 0 ], value ) )
|
||||
else:
|
||||
type = xbmcgui.INPUT_ALPHANUM
|
||||
if rule[ 2 ] == "integer":
|
||||
type = xbmcgui.INPUT_NUMERIC
|
||||
returnVal = xbmcgui.Dialog().input( LANGUAGE( 30307 ), splitPath[ ruleNum ][ 1 ], type=type )
|
||||
if returnVal != "":
|
||||
self.ATTRIB.writeUpdatedPath( actionPath, ( ruleNum, splitPath[ ruleNum ][ 0 ], returnVal ) )
|
||||
|
||||
|
||||
def browse( self, actionPath, ruleNum ):
|
||||
# This function launches the browser for the given property type
|
||||
|
||||
pDialog = xbmcgui.DialogProgress()
|
||||
pDialog.create( ADDONNAME, LANGUAGE( 30317 ) )
|
||||
|
||||
# Load the xml file
|
||||
tree = xmltree.parse( unquote(actionPath) )
|
||||
root = tree.getroot()
|
||||
# Get the path
|
||||
path = root.find( "path" ).text
|
||||
# Split the path element
|
||||
splitPath = self.ATTRIB.splitPath( path )
|
||||
# Get the rules and current value
|
||||
rules = self.getRulesForPath( splitPath[ 0 ] )
|
||||
rule = self.getMatchingRule( splitPath[ ruleNum ], rules )
|
||||
title = self.translateComponent( rule, splitPath[ ruleNum ] )
|
||||
|
||||
# Get the path we'll be browsing
|
||||
browsePath = self.getBrowsePath( splitPath, rule[ 3 ], ruleNum )
|
||||
|
||||
json_query = xbmc.executeJSONRPC('{ "jsonrpc": "2.0", "id": 0, "method": "Files.GetDirectory", "params": { "properties": ["title", "file", "thumbnail"], "directory": "%s", "media": "files" } }' % browsePath )
|
||||
json_response = json.loads(json_query)
|
||||
listings = []
|
||||
values = []
|
||||
# Add all directories returned by the json query
|
||||
if json_response.get('result') and json_response['result'].get('files') and json_response['result']['files'] is not None:
|
||||
total = len( json_response[ 'result' ][ 'files' ] )
|
||||
for item in json_response['result']['files']:
|
||||
if item[ "label" ] == "..":
|
||||
continue
|
||||
thumb = None
|
||||
if item[ "thumbnail" ] is not "":
|
||||
thumb = item[ "thumbnail" ]
|
||||
listitem = xbmcgui.ListItem(label=item[ "label" ] )
|
||||
listitem.setArt({'icon': thumb})
|
||||
listitem.setProperty( "thumbnail", thumb )
|
||||
listings.append( listitem )
|
||||
if rule[ 2 ] == "integer" and "id" in item:
|
||||
values.append( str( item[ "id" ] ) )
|
||||
else:
|
||||
values.append( item[ "label" ] )
|
||||
|
||||
pDialog.close()
|
||||
|
||||
if len( listings ) == 0:
|
||||
# No browsable options found
|
||||
xbmcgui.Dialog().ok( ADDONNAME, LANGUAGE( 30409 ) )
|
||||
return
|
||||
|
||||
# Show dialog
|
||||
w = ShowDialog( "DialogSelect.xml", CWD, listing=listings, windowtitle=title )
|
||||
w.doModal()
|
||||
selectedItem = w.result
|
||||
del w
|
||||
if selectedItem == "" or selectedItem == -1:
|
||||
return None
|
||||
|
||||
self.ATTRIB.writeUpdatedPath( actionPath, ( ruleNum, splitPath[ ruleNum ][ 0 ], values[ selectedItem ] ) )
|
||||
|
||||
def getBrowsePath( self, splitPath, newBase, rule ):
|
||||
# This function replaces the base path with the browse path, and removes the current
|
||||
# component
|
||||
|
||||
returnText = ""
|
||||
|
||||
if "::root::" in newBase:
|
||||
newBase = newBase.replace( "::root::", splitPath[ 0 ][ 0 ] )
|
||||
|
||||
# Enumarate through everything in the existing path
|
||||
addedQ = False
|
||||
for x, component in enumerate( splitPath ):
|
||||
if x != rule:
|
||||
# Transfer this component to the new path
|
||||
if x == 0:
|
||||
returnText = newBase
|
||||
elif not addedQ:
|
||||
returnText += "?%s=%s" %( component[ 0 ], quote(component[1]) )
|
||||
addedQ = True
|
||||
else:
|
||||
returnText += "&%s=%s" %( component[ 0 ], quote(component[1]) )
|
||||
return returnText
|
||||
|
||||
# in-place prettyprint formatter
|
||||
def indent( self, elem, level=0 ):
|
||||
i = "\n" + level*"\t"
|
||||
if len(elem):
|
||||
if not elem.text or not elem.text.strip():
|
||||
elem.text = i + "\t"
|
||||
if not elem.tail or not elem.tail.strip():
|
||||
elem.tail = i
|
||||
for elem in elem:
|
||||
self.indent(elem, level+1)
|
||||
if not elem.tail or not elem.tail.strip():
|
||||
elem.tail = i
|
||||
else:
|
||||
if level and (not elem.tail or not elem.tail.strip()):
|
||||
elem.tail = i
|
||||
|
||||
# ============================
|
||||
# === PRETTY SELECT DIALOG ===
|
||||
# ============================
|
||||
class ShowDialog( xbmcgui.WindowXMLDialog ):
|
||||
def __init__( self, *args, **kwargs ):
|
||||
xbmcgui.WindowXMLDialog.__init__( self )
|
||||
self.listing = kwargs.get( "listing" )
|
||||
self.windowtitle = kwargs.get( "windowtitle" )
|
||||
self.result = -1
|
||||
|
||||
def onInit(self):
|
||||
try:
|
||||
self.fav_list = self.getControl(6)
|
||||
self.getControl(3).setVisible(False)
|
||||
except:
|
||||
print_exc()
|
||||
self.fav_list = self.getControl(3)
|
||||
self.getControl(5).setVisible(False)
|
||||
self.getControl(1).setLabel(self.windowtitle)
|
||||
for item in self.listing :
|
||||
listitem = xbmcgui.ListItem(label=item.getLabel(), label2=item.getLabel2())
|
||||
listitem.setArt({'icon': item.getProperty( "icon" ), 'thumb': item.getProperty( "thumbnail" )})
|
||||
listitem.setProperty( "Addon.Summary", item.getLabel2() )
|
||||
self.fav_list.addItem( listitem )
|
||||
self.setFocus(self.fav_list)
|
||||
|
||||
def onAction(self, action):
|
||||
if action.getId() in ( 9, 10, 92, 216, 247, 257, 275, 61467, 61448, ):
|
||||
self.result = -1
|
||||
self.close()
|
||||
|
||||
def onClick(self, controlID):
|
||||
if controlID == 6 or controlID == 3:
|
||||
num = self.fav_list.getSelectedPosition()
|
||||
self.result = num
|
||||
else:
|
||||
self.result = -1
|
||||
self.close()
|
||||
|
||||
def onFocus(self, controlID):
|
||||
pass
|
||||
@@ -0,0 +1,54 @@
|
||||
# coding=utf-8
|
||||
import sys
|
||||
import xbmc, xbmcaddon, xbmcgui
|
||||
import json
|
||||
|
||||
from resources.lib.common import *
|
||||
|
||||
def getPluginPath( ltype, location = None ):
|
||||
listings = []
|
||||
listingsLabels = []
|
||||
|
||||
if location is not None:
|
||||
# If location given, add 'create' item
|
||||
listings.append( "::CREATE::" )
|
||||
listingsLabels.append( LANGUAGE( 30411 ) )
|
||||
else:
|
||||
# If no location, build default
|
||||
if location is None:
|
||||
if ltype.startswith( "video" ):
|
||||
location = "addons://sources/video"
|
||||
else:
|
||||
location = "addons://sources/audio"
|
||||
|
||||
# Show a waiting dialog, then get the listings for the directory
|
||||
dialog = xbmcgui.DialogProgress()
|
||||
dialog.create( ADDONNAME, LANGUAGE( 30410 ) )
|
||||
|
||||
json_query = xbmc.executeJSONRPC('{ "jsonrpc": "2.0", "id": 0, "method": "Files.GetDirectory", "params": { "properties": ["title", "file", "thumbnail", "episode", "showtitle", "season", "album", "artist", "imdbnumber", "firstaired", "mpaa", "trailer", "studio", "art"], "directory": "' + location + '", "media": "files" } }')
|
||||
json_response = json.loads(json_query)
|
||||
|
||||
# Add all directories returned by the json query
|
||||
if json_response.get('result') and json_response['result'].get('files') and json_response['result']['files']:
|
||||
json_result = json_response['result']['files']
|
||||
|
||||
for item in json_result:
|
||||
if item[ "file" ].startswith( "plugin://" ):
|
||||
listings.append( item[ "file" ] )
|
||||
listingsLabels.append( "%s >" %( item[ "label" ] ) )
|
||||
|
||||
# Close progress dialog
|
||||
dialog.close()
|
||||
|
||||
selectedItem = xbmcgui.Dialog().select( LANGUAGE( 30309 ), listingsLabels )
|
||||
|
||||
if selectedItem == -1:
|
||||
# User cancelled
|
||||
return None
|
||||
|
||||
selectedAction = listings[ selectedItem ]
|
||||
if selectedAction == "::CREATE::":
|
||||
return location
|
||||
else:
|
||||
# User has chosen a sub-level to display, add details and re-call this function
|
||||
return getPluginPath(ltype, selectedAction)
|
||||
1128
Kodi/Lenovo/addons/plugin.library.node.editor/resources/lib/rules.py
Normal file
1128
Kodi/Lenovo/addons/plugin.library.node.editor/resources/lib/rules.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,353 @@
|
||||
# coding=utf-8
|
||||
import os, sys
|
||||
import xbmc, xbmcaddon, xbmcplugin, xbmcgui, xbmcvfs
|
||||
import xml.etree.ElementTree as xmltree
|
||||
import json
|
||||
from traceback import print_exc
|
||||
from urllib.parse import quote, unquote
|
||||
|
||||
from resources.lib.common import *
|
||||
from resources.lib import pluginBrowser
|
||||
|
||||
class ViewAttribFunctions():
|
||||
def __init__(self, ltype):
|
||||
self.ltype = ltype
|
||||
|
||||
def _load_rules( self ):
|
||||
if self.ltype.startswith('video'):
|
||||
overridepath = os.path.join( DEFAULTPATH , "videorules.xml" )
|
||||
else:
|
||||
overridepath = os.path.join( DEFAULTPATH , "musicrules.xml" )
|
||||
try:
|
||||
tree = xmltree.parse( overridepath )
|
||||
return tree
|
||||
except:
|
||||
return None
|
||||
|
||||
def translateContent( self, content ):
|
||||
# Load the rules
|
||||
tree = self._load_rules()
|
||||
hasValue = True
|
||||
elems = tree.getroot().find( "content" ).findall( "type" )
|
||||
for elem in elems:
|
||||
if elem.text == content:
|
||||
return xbmc.getLocalizedString( int( elem.attrib.get( "label" ) ) )
|
||||
return None
|
||||
|
||||
def editContent( self, actionPath, default ):
|
||||
# Load all the rules
|
||||
tree = self._load_rules().getroot()
|
||||
elems = tree.find( "content" ).findall( "type" )
|
||||
selectName = []
|
||||
selectValue = []
|
||||
# Find all the content types
|
||||
for elem in elems:
|
||||
selectName.append( xbmc.getLocalizedString( int( elem.attrib.get( "label" ) ) ) )
|
||||
selectValue.append( elem.text )
|
||||
# Let the user select a content type
|
||||
selectedContent = xbmcgui.Dialog().select( LANGUAGE( 30309 ), selectName )
|
||||
# If the user selected no operator...
|
||||
if selectedContent == -1:
|
||||
return
|
||||
self.writeUpdatedRule( actionPath, "content", selectValue[ selectedContent ], addFilter = True )
|
||||
|
||||
def translateGroup( self, grouping ):
|
||||
# Load the rules
|
||||
tree = self._load_rules()
|
||||
hasValue = True
|
||||
elems = tree.getroot().find( "groupings" ).findall( "grouping" )
|
||||
for elem in elems:
|
||||
if elem.attrib.get( "name" ) == grouping:
|
||||
return xbmc.getLocalizedString( int( elem.find( "label" ).text ) )
|
||||
return None
|
||||
|
||||
def editGroup( self, actionPath, content, default ):
|
||||
# Load all the rules
|
||||
tree = self._load_rules().getroot()
|
||||
elems = tree.find( "groupings" ).findall( "grouping" )
|
||||
selectName = []
|
||||
selectValue = []
|
||||
# Find all the content types
|
||||
for elem in elems:
|
||||
checkContent = elem.find( content )
|
||||
if checkContent is not None:
|
||||
selectName.append( xbmc.getLocalizedString( int( elem.find( "label" ).text ) ) )
|
||||
selectValue.append( elem.attrib.get( "name" ) )
|
||||
# Let the user select a content type
|
||||
selectedGrouping = xbmcgui.Dialog().select( LANGUAGE( 30310 ), selectName )
|
||||
# If the user selected no operator...
|
||||
if selectedGrouping == -1:
|
||||
return
|
||||
self.writeUpdatedRule( actionPath, "group", selectValue[ selectedGrouping ] )
|
||||
|
||||
def addLimit( self, actionPath ):
|
||||
# Load all the rules
|
||||
try:
|
||||
tree = xmltree.parse( unquote(actionPath) )
|
||||
root = tree.getroot()
|
||||
# Add a new content tag
|
||||
newContent = xmltree.SubElement( root, "limit" )
|
||||
newContent.text = "25"
|
||||
# Save the file
|
||||
self.indent( root )
|
||||
tree.write( unquote(actionPath), encoding="UTF-8" )
|
||||
except:
|
||||
print_exc()
|
||||
|
||||
def editLimit( self, actionPath, curValue ):
|
||||
returnVal = xbmcgui.Dialog().input( LANGUAGE( 30311 ), curValue, type=xbmcgui.INPUT_NUMERIC )
|
||||
if returnVal != "":
|
||||
self.writeUpdatedRule( actionPath, "limit", returnVal )
|
||||
|
||||
def addPath( self, actionPath ):
|
||||
# Load all the rules
|
||||
tree = self._load_rules().getroot()
|
||||
elems = tree.find( "paths" ).findall( "type" )
|
||||
selectName = []
|
||||
selectValue = []
|
||||
# Find all the path types
|
||||
for elem in elems:
|
||||
selectName.append( xbmc.getLocalizedString( int( elem.attrib.get( "label" ) ) ) )
|
||||
selectValue.append( elem.attrib.get( "name" ) )
|
||||
# Find any sub-path types
|
||||
for subElem in elem.findall( "type" ):
|
||||
selectName.append( " - %s" %( xbmc.getLocalizedString( int( subElem.attrib.get( "label" ) ) ) ) )
|
||||
selectValue.append( "%s/%s" %( elem.attrib.get( "name" ), subElem.attrib.get( "name" ) ) )
|
||||
|
||||
# Add option to select a plugin
|
||||
selectName.append( LANGUAGE( 30514 ) )
|
||||
selectValue.append( "::PLUGIN::" )
|
||||
|
||||
# Let the user select a path
|
||||
selectedContent = xbmcgui.Dialog().select( LANGUAGE( 30309 ), selectName )
|
||||
# If the user selected no operator...
|
||||
if selectedContent == -1:
|
||||
return
|
||||
if selectValue[ selectedContent ] == "::PLUGIN::":
|
||||
# The user has asked to browse for a plugin
|
||||
path = pluginBrowser.getPluginPath(self.ltype)
|
||||
if path is not None:
|
||||
# User has selected a plugin
|
||||
self.writeUpdatedPath( actionPath, (0, path), addFolder = True)
|
||||
else:
|
||||
# The user has chosen a specific path
|
||||
self.writeUpdatedPath( actionPath, (0, selectValue[ selectedContent ] ), addFolder = True )
|
||||
|
||||
def editPath( self, actionPath, curValue ):
|
||||
returnVal = xbmcgui.Dialog().input( LANGUAGE( 30312 ), curValue, type=xbmcgui.INPUT_ALPHANUM )
|
||||
if returnVal != "":
|
||||
self.writeUpdatedRule( actionPath, "path", returnVal )
|
||||
|
||||
def editIcon( self, actionPath, curValue ):
|
||||
returnVal = xbmcgui.Dialog().input( LANGUAGE( 30313 ), curValue, type=xbmcgui.INPUT_ALPHANUM )
|
||||
if returnVal != "":
|
||||
self.writeUpdatedRule( actionPath, "icon", returnVal )
|
||||
|
||||
def browseIcon( self, actionPath ):
|
||||
returnVal = xbmcgui.Dialog().browse( 2, LANGUAGE( 30313 ), "files", useThumbs = True )
|
||||
if returnVal:
|
||||
self.writeUpdatedRule( actionPath, "icon", returnVal )
|
||||
|
||||
def writeUpdatedRule( self, actionPath, attrib, value, addFilter = False ):
|
||||
# This function writes an updated match, operator or value to a rule
|
||||
try:
|
||||
# Load the xml file
|
||||
tree = xmltree.parse( actionPath )
|
||||
root = tree.getroot()
|
||||
# Add type="filter" if requested
|
||||
if addFilter:
|
||||
root.set( "type", "filter" )
|
||||
# Find the attribute and update it
|
||||
elem = root.find( attrib )
|
||||
if elem is None:
|
||||
# There's no existing attribute with this name, so create one
|
||||
elem = xmltree.SubElement( root, attrib )
|
||||
elem.text = value
|
||||
# Save the file
|
||||
self.indent( root )
|
||||
tree.write( actionPath, encoding="UTF-8" )
|
||||
except:
|
||||
print_exc()
|
||||
|
||||
def writeUpdatedPath( self, actionPath, newComponent, addFolder = False ):
|
||||
# This functions writes an updated path
|
||||
try:
|
||||
# Load the xml file
|
||||
tree = xmltree.parse( unquote(actionPath) )
|
||||
root = tree.getroot()
|
||||
# Add type="folder" if requested
|
||||
if addFolder:
|
||||
root.set( "type", "folder" )
|
||||
# Find the current path element
|
||||
elem = root.find( "path" )
|
||||
if elem is None:
|
||||
# There's no existing path element, so create one
|
||||
elem = xmltree.SubElement( root, "path" )
|
||||
# Get the split version of the path
|
||||
splitPath = self.splitPath( elem.text )
|
||||
elem.text = ""
|
||||
if len( splitPath ) == 0:
|
||||
# If the splitPath is empty, add our new component straight away
|
||||
elem.text = "%s/" %( newComponent[ 1 ] )
|
||||
elif newComponent[ 0 ] == 0 and newComponent[ 1 ].startswith( "plugin://"):
|
||||
# We've been passed a plugin, so only want to write that plugin
|
||||
elem.text = newComponent[ 1 ]
|
||||
else:
|
||||
# Enumarate through everything in the existing path
|
||||
for x, component in enumerate( splitPath ):
|
||||
if x != newComponent[ 0 ]:
|
||||
# Transfer this component to the new path
|
||||
if x == 0:
|
||||
elem.text = self.joinPath( component )
|
||||
elif x == 1:
|
||||
elem.text += "?%s=%s" %( component[ 0 ], quote(component[1]) )
|
||||
else:
|
||||
elem.text += "&%s=%s" %( component[ 0 ], quote(component[1]) )
|
||||
else:
|
||||
# Add our new component
|
||||
if x == 0:
|
||||
elem.text = "%s/" %( newComponent[ 1 ] )
|
||||
elif x == 1:
|
||||
elem.text += "?%s=%s" %( newComponent[ 1 ], quote(newComponent[2]) )
|
||||
else:
|
||||
elem.text += "&%s=%s" %( newComponent[ 1 ], quote(newComponent[2]) )
|
||||
# Check that we added it
|
||||
if x < newComponent[ 0 ]:
|
||||
if newComponent[ 0 ] == 1:
|
||||
elem.text += "?%s=%s" %( newComponent[ 1 ], quote(newComponent[2]) )
|
||||
else:
|
||||
elem.text += "&%s=%s" %( newComponent[ 1 ], quote(newComponent[2]) )
|
||||
# Save the file
|
||||
self.indent( root )
|
||||
tree.write( unquote(actionPath), encoding="UTF-8" )
|
||||
except:
|
||||
print_exc()
|
||||
|
||||
def deletePathRule( self, actionPath, rule ):
|
||||
# This function deletes a rule from a path component
|
||||
result = xbmcgui.Dialog().yesno(ADDONNAME, LANGUAGE( 30407 ) )
|
||||
if not result:
|
||||
return
|
||||
|
||||
try:
|
||||
# Load the xml file
|
||||
tree = xmltree.parse( actionPath )
|
||||
root = tree.getroot()
|
||||
# Find the current path element
|
||||
elem = root.find( "path" )
|
||||
# Get the split version of the path
|
||||
splitPath = self.splitPath( elem.text )
|
||||
elem.text = ""
|
||||
|
||||
# Enumarate through everything in the existing path
|
||||
addedQ = False
|
||||
for x, component in enumerate( splitPath ):
|
||||
if x != rule:
|
||||
if x == 0:
|
||||
elem.text = self.joinPath( component )
|
||||
elif not addedQ:
|
||||
elem.text += "?%s=%s" %( component[ 0 ], quote(component[1]) )
|
||||
addedQ = True
|
||||
else:
|
||||
elem.text += "&%s=%s" %( component[ 0 ], quote(component[1]) )
|
||||
# Save the file
|
||||
self.indent( root )
|
||||
tree.write( actionPath, encoding="UTF-8" )
|
||||
except:
|
||||
print_exc()
|
||||
|
||||
def splitPath( self, completePath ):
|
||||
# This function returns an array of the different components of a path
|
||||
# [library]://[primary path]/[secondary path]/?attribute1=value1&attribute2=value2...
|
||||
# [( , )] [( , )] [( , )]...
|
||||
splitPath = []
|
||||
|
||||
# If completePath is empty, return an empty list
|
||||
if completePath is None:
|
||||
return []
|
||||
|
||||
# If it's a plugin, then we don't want to split it as its unlikely the user will want to edit individual components
|
||||
if completePath.startswith( "plugin://" ):
|
||||
return [ ( completePath, None ) ]
|
||||
|
||||
# Split, get the library://primarypath/[secondarypath]
|
||||
split = completePath.rsplit( "/", 1 )
|
||||
if split[ 0 ].count( "/" ) == 3:
|
||||
# There's a secondary path
|
||||
paths = split[ 0 ].rsplit( "/", 1 )
|
||||
splitPath.append( ( paths[0], paths[1] ) )
|
||||
else:
|
||||
splitPath.append( ( split[ 0 ], None ) )
|
||||
|
||||
|
||||
# Now split the components
|
||||
if len( split ) != 1 and split[ 1 ].startswith( "?" ):
|
||||
for component in split[ 1 ][ 1: ].split( "&" ):
|
||||
componentSplit = component.split( "=" )
|
||||
splitPath.append( ( componentSplit[ 0 ], unquote( componentSplit[1]) ) )
|
||||
|
||||
return splitPath
|
||||
|
||||
def joinPath( self, components ):
|
||||
# This function rejoins the library://path/subpath components of a path
|
||||
returnPath = "%s/" %( components[ 0 ] )
|
||||
if components[ 1 ] is not None:
|
||||
returnPath += "%s/" %( components[ 1 ] )
|
||||
return returnPath
|
||||
|
||||
|
||||
def translatePath( self, path ):
|
||||
# Load the rules
|
||||
tree = self._load_rules()
|
||||
subSearch = None
|
||||
translated = [ path[ 0 ], path[ 1 ] ]
|
||||
elems = tree.getroot().find( "paths" ).findall( "type" )
|
||||
for elem in elems:
|
||||
if elem.attrib.get( "name" ) == path[ 0 ]:
|
||||
translated[ 0 ] = xbmc.getLocalizedString( int( elem.attrib.get( "label" ) ) )
|
||||
subSearch = elem
|
||||
break
|
||||
|
||||
if path[ 1 ] and subSearch is not None:
|
||||
for elem in subSearch.findall( "type" ):
|
||||
if elem.attrib.get( "name" ) == path[ 1 ]:
|
||||
translated[ 1 ] = xbmc.getLocalizedString( int( elem.attrib.get( "label" ) ) )
|
||||
break
|
||||
|
||||
returnString = translated[ 0 ]
|
||||
if translated[ 1 ]:
|
||||
returnString += " - %s" %( translated[ 1 ] )
|
||||
|
||||
return returnString
|
||||
|
||||
def translateMatch( self, value ):
|
||||
if value == "any":
|
||||
return xbmc.getLocalizedString(21426).capitalize()
|
||||
else:
|
||||
return xbmc.getLocalizedString(21425).capitalize()
|
||||
|
||||
def editMatch( self, actionPath ):
|
||||
selectName = [ xbmc.getLocalizedString(21425).capitalize(), xbmc.getLocalizedString(21426).capitalize() ]
|
||||
selectValue = [ "all", "any" ]
|
||||
# Let the user select wether any or all rules need match
|
||||
selectedMatch = xbmcgui.Dialog().select( LANGUAGE( 30310 ), selectName )
|
||||
# If the user made no selection...
|
||||
if selectedMatch == -1:
|
||||
return
|
||||
self.writeUpdatedRule( actionPath, "match", selectValue[ selectedMatch ] )
|
||||
|
||||
# in-place prettyprint formatter
|
||||
def indent( self, elem, level=0 ):
|
||||
i = "\n" + level*"\t"
|
||||
if len(elem):
|
||||
if not elem.text or not elem.text.strip():
|
||||
elem.text = i + "\t"
|
||||
if not elem.tail or not elem.tail.strip():
|
||||
elem.tail = i
|
||||
for elem in elem:
|
||||
self.indent(elem, level+1)
|
||||
if not elem.tail or not elem.tail.strip():
|
||||
elem.tail = i
|
||||
else:
|
||||
if level and (not elem.tail or not elem.tail.strip()):
|
||||
elem.tail = i
|
||||
Reference in New Issue
Block a user