Updated kodi settings on Lenovo

This commit is contained in:
2026-03-22 22:28:43 +01:00
parent 725dfa7157
commit 32b5a81da6
10925 changed files with 575678 additions and 5511 deletions

View File

@@ -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')

View File

@@ -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)

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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)

File diff suppressed because it is too large Load Diff

View File

@@ -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