Files
DevOps/Kodi/Lenovo/addons/plugin.library.node.editor/resources/lib/pathrules.py

378 lines
15 KiB
Python

# 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