diff --git a/default.py b/default.py
index 7572a409..5504b553 100644
--- a/default.py
+++ b/default.py
@@ -106,7 +106,10 @@ def __init__(self):
# We're just going to choose a shortcut, and save its details to the given
# skin labels
- selectedShortcut = LIBRARY.selectShortcut( "", custom = self.CUSTOM, showNone = self.NONE )
+ if self.GROUPING is not None:
+ selectedShortcut = LIBRARY.selectShortcut( "", grouping = self.GROUPING, custom = self.CUSTOM, showNone = self.NONE )
+ else:
+ selectedShortcut = LIBRARY.selectShortcut( "", custom = self.CUSTOM, showNone = self.NONE )
# Now set the skin strings
if selectedShortcut is not None and selectedShortcut.getProperty( "Path" ):
diff --git a/resources/Advanced Usage.txt b/resources/Advanced Usage.txt
index 36086756..08e721a3 100644
--- a/resources/Advanced Usage.txt
+++ b/resources/Advanced Usage.txt
@@ -37,31 +37,23 @@ Custom backgrounds are saved on a skin-by-skin basis.
Managing Widgets
----------------
-When using Skin Shortcuts to provide the whole main menu, you may wish to provide a series of widgets - such as PVR information, weather conditions, recently added movies, and so forth - that the user can choose from for each main menu item.
+When using Skin Shortcuts to provide the whole main menu, you can additionally use it to let the user select a widget for a menu item. The script provides a method to select any library node, playlist, source or plugin as a widget automatically, and you can additionally define custom widgets - such as PVR information, weather conditions and so forth.
-To let the user select from these widgets, include a button with the id 309 in your script-skinshortcuts.xml file.
-
-Then use the following in the visibility condition for each widget:
+To let the user select from these widgets, include a button with the id 312 in your script-skinshortcuts.xml file.
- StringCompare(Container(9000).ListItem.Property(widget),[WidgetID])
-
-You can define your widgets - along with their WidgetID, an optional widgetType parameter and default labelID's they should appear against - in an overrides.xml file. See "overrides.xml" sections 3 and 4 for more details.
+For most shortcuts, the script will set a widgetPath property to the main menu item, which you can use to fill the contents of a list. You can use the properties widget and widgetType to decide which list to display. In the case of custom widgets, you can define these properties yourself so you can display your custom widget.
-You can also user overrides.xml to let the user select a playlist to use as a widget, which can then be shown by using it as the tag in a list. To check for a playlist widget:
-
- StringCompare(Container(9000).ListItem.Property(widget),Playlist)
-
-And then set the content of the list as so:
+For example, for a movie playlist, you could use something like the following to show the correct list
- $INFO[Container(9000).ListItem.Property(widgetPlaylist)]
+ StringCompare(Container(9000).ListItem.Property(widget),Playlist) + StringCompare(Container(9000).ListItem.Property(widgetType),movies)
-In all cases, remember to replace 9000 with the ID of the list containing the menu. Widgets are saved on a skin-by-skin basis.
+And then fill the list as so:
-It may be advantageous for your submenu items to have the same background property as your main menu items. In which case, include &options=clonewidgets in your buildxml command (multiple options can be separated with a pipe - | - symbol.
+ $INFO[Container(9000).ListItem.Property(widgetPath)]
-If you wish to run code based on whether a particular widget is attached to a menu item, the script will set the skin property skinshortcuts-widget-[widgetID] for each widget currently selected for a menu item. For a widget with the ID complexWidget for example, you could use the following in a condition:
+You can define your custom widgets - along with their relevant properties - in an overrides.xml file. See "overrides.xml" section 4 for more information, including the default values that will be returned for widget and widgetType for various widgets the user can select.
- condition="Skin.HasSetting(skinshortcuts-widget-complexWidget)"
+It may be advantageous for your submenu items to have the same widget properties as your main menu items. In which case, include &options=clonewidgets in your buildxml command (multiple options can be separated with a pipe - | - symbol.)
Widgets are saved on a skin-by-skin basis.
@@ -100,10 +92,11 @@ When using this method, you specify which skin strings you want the script to se
To use the script in this way, put the following in an onclick:
-RunScript(script.skinshortcuts,type=shortcuts&custom=[True/False]&showNone=[True/False]&skinLabel=[skinLabel]&skinAction=[skinAction]&skinList=[skinList]&skinType=[skinType]&skinThumbnail=[skinThumbnail])
+RunScript(script.skinshortcuts,type=shortcuts&custom=[True/False]&showNone=[True/False]&grouping=[grouping]&skinLabel=[skinLabel]&skinAction=[skinAction]&skinList=[skinList]&skinType=[skinType]&skinThumbnail=[skinThumbnail])
custom=[True/False] - Whether you wish to allow the user to type their own custom command. If ommitted this will default to False. [OPTIONAL]
showNone=[True/False] - Whether you wish to show a 'None' option to reset skin labels. If ommitted this will default to False. [OPTIONAL]
+[grouping] - The custom shortcut grouping to display - see Overrides.xml below, section 15
[skinLabel] - The skin string the script will write the label of the selected shortcut to. [OPTIONAL]
[skinAction] - the skin string the script will write the action of the selected shortcut to. [OPTIONAL]
[skinList] - the skin string the script will write the action of the selected shortcut, without any 'ActivateWindow' elements, to. [OPTIONAL]
@@ -234,20 +227,42 @@ You can then choose which background to display based on the 'background' proper
4. Widgets
-If you are using Skin Shortcuts to manage widgets, provide a list of the widgets your skin supports and defaults for labelID's in the overrides.xml.
+If you are using Skin Shortcuts to manage widgets, include button 312 to provide a list of library nodes, library sources and add-ons the user can select for a shortcut.
-[WidgetID]
-[widgetID]
+Note, if moving from the previous method of managing shortcuts via 309, there is only an upgrade path for playlists. Other widgets will not provide that additional information that can now be set and will require the user to re-select the widget.
+
+You can provide a list of widgets your skin supports and defaults for labelID's in the overrides.xml.
+
+[widgetID]
[label] - The display name of the widget, to be shown when choosing widgets (can be a localised string)
+[icon] - [Optional] An image provided by your skin to be used when selecting widgets
[Condition] - [Optional] Boolean condition that must be true for the background to show in the management dialogs Background Select (evaluated when management dialog is loaded)[type] - [Optional] A string you use to identy the type of widget (returned as property widgetType)
+[type] - The string that will be assigned to
+[path] - [Optional] The string that will be assigned to
+[target] - [Optional] The string that will be assigned to
+
+To set a default to one of your custom widgets:
+
+[widgetID]
+
+[labelID] - The labelID you are setting the default for. (Replace with defaultID="[defaultID]" to set default based on this property instead)
[GroupName] - [Optional] The group that the labelID must be in, for example "movies". If omitted, the property will apply to items in the main menu.
[widgetID] - A string you use to identify this widget
-[labelID] - The labelID you are setting the default for. (Replace with defaultID="[defaultID]" to set default based on this property instead)
-If you want the user to be able to select a playlist as a widget, also include:
+To set a default to a library node, source, add-on or playlist, you need to set all widget parameters directly:
+
+[widgetID]
+
+[labelID] - The labelID you are setting the default for. (Replace with defaultID="[defaultID]" to set default based on this property instead)
+[GroupName] - [Optional] The group that the labelID must be in, for example "movies". If omitted, the property will apply to items in the main menu.
+[label] - The display name of the widget (can be a localised string)
+[type] - The string that will be assigned to
+[path] - [Optional] The string that will be assigned to
+[target] - [Optional] The string that will be assigned to
+[widgetID] - A string you use to identify this widget
-True
+Be careful when setting defaults to library nodes - if the user has customised their nodes, the node may not exist on their system. Only skin-provided playlists should be set as defaults.
So, for example:
@@ -260,10 +275,47 @@ So, for example:
True
-You can then choose which widget to display based on the 'widget' property of the selected listitem.
-
You can optionally use choose to set the shortcut name to the widget name when selecting a widget. To do so, set the window property "useWidgetNameAsLabel" to "true". This property will be reset after the widget is set.
+The script will set the widgetTarget property to the menu item you have assigned the shortcut to for library/addons/playlists - this is the value that should be used in the target element of the content tag of the list you are using to display the widget.
+
+Additionally, the script will set four properties to the menu item you have assigned a shortcut to, as follows:
+
+4.1 Library nodes
+
+widget - Library
+widgetName - The name of the library node
+widgetType - Either 'video' or 'audio', or the content type specified in the nodes xml file
+widgetPath - The path to the selected node
+
+4.2 Library sources
+
+widget - Source
+widgetName - The name of the library source
+widgetType - Either 'video', 'audio' or 'picture'
+widgetPath - The path to the selected source
+
+4.3 Playlists
+
+widget - Playlist
+widgetName - The name of the playlist
+widgetType - The content type specified by the playlist
+widgetPath - The path to the playlist
+
+4.4 Add-ons
+
+widget - Addon
+widgetName - The name of the add-on + the selected node (if available)
+widgetType - Either 'video', 'audio' or 'picture', or a best-guess at the type of content provided
+widgetPath - The path to the selected node
+
+4.5 Skin widgets
+
+widget - The [widgetID]
+widgetName - The [label]
+widgetType - The [type]
+widgetPath - The [path]
+
5. Skin-Recommended Shortcuts
@@ -286,10 +338,6 @@ Shortcut groupings within the management dialog are made up of several smaller g
- commands
- video
- - movie
- - tvshow
- - musicvideo
- - customvideonode
- videosources
- music
@@ -498,6 +546,25 @@ As result all items in mainmenu will have property "exampleProperty" filled with
...
+15. Customised groupings
+
+Groupings are used to decide which shortcuts are displayed where when the user selects a shortcut. Skin Shortcuts has three groupings in its overrides.xml
+
+ flatgroupings - used with list 111
+ groupings - used with buttons 401 and 307, and the Just Select method
+ widget-groupings - used with button 312
+
+You can include any of these groupings in your own overrides.xml to change how and where the available shortcuts are displayed.
+
+You can also create additional groups, naming them [name]-groupings, to provide additional layouts for specific cases.
+
+For button 401 or 307, first set the window property "custom-grouping" to the [name] of the grouping you wish to use, then send a click to the relevant button. The window property will be cleared after the shortcut dialog is closed.
+
+For Just Select, see the documentation on the Just Select Shortcut method, above.
+
+Note, widget-groupings is handled differently from all other groupings in that the user won't ever get a prompt about what they want to do with the shortcut they selected (no prompt about whether to play or display a playlist, for example) - it will always default to displaying the shortcut in the library.
+
+
Localisation
------------
diff --git a/resources/Management Dialog.txt b/resources/Management Dialog.txt
index fd604d58..e4329b39 100644
--- a/resources/Management Dialog.txt
+++ b/resources/Management Dialog.txt
@@ -22,9 +22,9 @@ ID Type Label Description
307 Button 32027 Change shortcut action (if control 403 is not included)
308 Button 32028 Reset shortcuts
-309 Button 32044 Change widget (See "Advanced Usage")
310 Button 32045 Change background (See "Advanced Usage")
311 Button Select skin-provided thumbnail (See "Advanced Usage")
+312 Button 32044 Change widget (See "Advanced Usage")
401 Button 32048 Alternative method to set a shortcut
404 Button Set a custom property
diff --git a/resources/Templates.txt b/resources/Templates.txt
index 090f656c..125c7bff 100755
--- a/resources/Templates.txt
+++ b/resources/Templates.txt
@@ -72,6 +72,7 @@ The element must be included. Put your gui xml within.
+
@@ -87,22 +88,25 @@ The element must be included. Put your gui xml within.
[include] - [Optional] The name of the include this will be added to, appended to skinshortcuts-templates-. If ommitted, it will be added to skinshortcuts-template
- [Optional] See 3.1
- - [Optional] See 3.2
+ - [Optional] See 3.2
+ - [Optional] See 3.3
visibility - This will be replaced with a visibility condition based on the focus of the main menu
%skinshortcuts[propertyName] - This will be replaced by the value of a matched element
The element must be included. Put your gui xml within.
+One Other template will be built for each main menu item per [include]. Use different [include]'s to build multiple Other templates.
+
3.1 Condition elements
-Condition elements are used to decide whether an Other template should be built, dependant on the items in the main menu. You may use multiple Condition elements - the template will only be built if they all match.
+Condition elements are used to decide whether an Other template should be built, dependant on the items in the main menu. You may use multiple Condition elements - unless you specify a element, the template will only be built if they all match.
[textValue]
[tag] - The main menu must have an element with this tag
[attributeName]|[attributeValue] - [Optional] The element must have an attribute with the name [attributeName] and the value [attributeValue]
-[textValue] - The element must have this value
+[textValue] - [Optional] The element must have this value
For example, to match against the following element in a main menu item:
@@ -112,11 +116,25 @@ The would be
myWidgetGrouping
-3.2 Property elements
+3.2 Match element
+
+If you are using multiple condition elements, you can include a match element to speicify whether any or all of the conditions must be met for the template to be built
+
+[any/all]
+
+[any/all] - set as 'any' for the template to be built if any of the conditions match
+ - set as 'all' for the temlate to be built if all of the conditions match (default behaviour)
+
+e.g.
+
+any
+
+
+3.3 Property elements
Property elements let you include a custom property in your template, dependant on the items in the main menu. You may include mutliple elements which set the same property - the first one matched will be used.
-[propertyValue]
+[propertyValue]
[name] - The name of the property you are setting
[tag] - [Optional] The main menu must have an element with this tag
@@ -124,17 +142,40 @@ Property elements let you include a custom property in your template, dependant
[textValue] - [Optional] The elment must have this value
[propertyValue] - [Optional] What you are setting the property to. If ommitted (e.g. a element), the propertyName will be set to the current text value of the matching property.
+You can also set a property directly to the value of a element within a main menu item:
+
+
+
+[name] - The name of the property you are setting
+[tag] - The main menu must have an element with this tag
+[attributeName]|[attributeValue] The element must have an attribute with the name [attributeName] and the value [attributeValue]
+
+If none of the elements match, the property will be an empty string. You can set an alternative fallback value by including the following as the final element in a list of 's
+
+[propertyValue]
+
+[name] - The name of the property you are setting
+[propertyValue] - What you are setting the property to
+
To access the property, include $SKINSHORTCUTS[propertyName] in either an attribute or as the value of an element.
For example, the content of a widget list could be set as follows
plugin://service.library.data.provider?type=recentmovies&reload=$INFO[Window.Property(recentmovies)]
plugin://service.library.data.provider?type=recentalbums&reload=$INFO[Window.Property(recentalbums)]
+
+My fallback value
And then the content of a list within your Other template could be filled as
$SKINSHORTCUTS[widgetContent]
+Alternatively, the property can be set to $INCLUDE[includeName], in which case the above example will be written as:
+
+
+ [includeName]
+
+
A Simple Example
----------------
@@ -194,10 +235,21 @@ There is then an additional widget include for playlists, though it uses the sam
+
media
+ movies
+ tvshows
+ any
+
+
plugin://service.library.data.provider?type=recentmovies&reload=$INFO[Window.Property(recentmovies)]
plugin://service.library.data.provider?type=recommendedepisodes&reload=$INFO[Window.Property(recommendedepisodes)]
plugin://service.library.data.provider?type=randomalbums&reload=$INFO[Window.Property(randomalbums)]
+
+
+
+
+
visibility
@@ -230,4 +282,4 @@ There is then an additional widget include for playlists, though it uses the sam
-
\ No newline at end of file
+
diff --git a/resources/language/English/strings.po b/resources/language/English/strings.po
index 7c17af8e..396ce295 100644
--- a/resources/language/English/strings.po
+++ b/resources/language/English/strings.po
@@ -417,3 +417,11 @@ msgstr ""
msgctxt "#32098"
msgid "Writing menu..."
msgstr ""
+
+msgctxt "#32099"
+msgid "Widget"
+msgstr ""
+
+msgctxt "#32100"
+msgid "Use as widget"
+msgstr ""
diff --git a/resources/lib/datafunctions.py b/resources/lib/datafunctions.py
index 495bda97..447e941f 100644
--- a/resources/lib/datafunctions.py
+++ b/resources/lib/datafunctions.py
@@ -474,48 +474,84 @@ def _get_additionalproperties( self, profileDir ):
# Load skin defaults (in case we need them...)
tree = self._get_overrides_skin()
if tree is not None:
- for elemSearch in [["widget", tree.findall( "widgetdefault" )], ["background", tree.findall( "backgrounddefault" )], ["custom", tree.findall( "propertydefault" )] ]:
+ for elemSearch in [["widget", tree.findall( "widgetdefault" )], ["widget:node", tree.findall( "widgetdefaultnode" )], ["background", tree.findall( "backgrounddefault" )], ["custom", tree.findall( "propertydefault" )] ]:
for elem in elemSearch[1]:
+ # Get labelID and defaultID
+ labelID = elem.attrib.get( "labelID" )
+ defaultID = labelID
+ if "defaultID" in elem.attrib:
+ defaultID = elem.attrib.get( "defaultID" )
+
if elemSearch[0] == "custom":
# Custom property
if "group" not in elem.attrib:
- defaultProperties.append( ["mainmenu", elem.attrib.get( 'labelID' ), elem.attrib.get( 'property' ), elem.text, elem.attrib.get( 'defaultID' ) ] )
+ defaultProperties.append( ["mainmenu", labelID, elem.attrib.get( 'property' ), elem.text, defaultID ] )
else:
- defaultProperties.append( [elem.attrib.get( "group" ), elem.attrib.get( 'labelID' ), elem.attrib.get( 'property' ), elem.text, elem.attrib.get( 'defaultID' ) ] )
+ defaultProperties.append( [elem.attrib.get( "group" ), labelID, elem.attrib.get( 'property' ), elem.text, defaultID ] )
else:
# Widget or background
if "group" not in elem.attrib:
- defaultProperties.append( [ "mainmenu", elem.attrib.get( 'labelID' ), elemSearch[0], elem.text, elem.attrib.get( 'defaultID' ) ] )
+ defaultProperties.append( [ "mainmenu", labelID, elemSearch[ 0 ].split( ":" )[ 0 ], elem.text, defaultID ] )
if elemSearch[ 0 ] == "background":
# Get and set the background name
backgroundName = self._getBackgroundName( elem.text )
if backgroundName is not None:
- defaultProperties.append( [ "mainmenu", elem.attrib.get( "labelID" ), "backgroundName", backgroundName, elem.attrib.get( 'defaultID' ) ] )
+ defaultProperties.append( [ "mainmenu", labelID, "backgroundName", backgroundName, defaultID ] )
if elemSearch[0] == "widget":
# Get and set widget type and name
widgetDetails = self._getWidgetNameAndType( elem.text )
if widgetDetails is not None:
- defaultProperties.append( [ "mainmenu", elem.attrib.get( "labelID" ), "widgetName", widgetDetails[0], elem.attrib.get( 'defaultID' ) ] )
- if widgetDetails[1] is not None:
- defaultProperties.append( [ "mainmenu", elem.attrib.get( "labelID" ), "widgetType", widgetDetails[1], elem.attrib.get( 'defaultID' ) ] )
+ defaultProperties.append( [ "mainmenu", labelID, "widgetName", widgetDetails[ "name" ], defaultID ] )
+ if "type" in widgetDetails:
+ defaultProperties.append( [ "mainmenu", labelID, "widgetType", widgetDetails[ "type" ], defaultID ] )
+ if "path" in widgetDetails:
+ defaultProperties.append( [ "mainmenu", labelID, "widgetPath", widgetDetails[ "path" ], defaultID ] )
+ if "target" in widgetDetails:
+ defaultProperties.append( [ "mainmenu", labelID, "widgetTarget", widgetDetails[ "target" ], defaultID ] )
+
+ if elemSearch[0] == "widget:node":
+ # Set all widget properties from the default
+ if "label" in elem.attrib:
+ defaultProperties.append( [ "mainmenu", labelID, "widgetName", elem.attrib.get( "label" ), defaultID ] )
+ if "type" in elem.attrib:
+ defaultProperties.append( [ "mainmenu", labelID, "widgetType", elem.attrib.get( "type" ), defaultID ] )
+ if "path" in elem.attrib:
+ defaultProperties.append( [ "mainmenu", labelID, "widgetPath", elem.attrib.get( "path" ), defaultID ] )
+ if "target" in elem.attrib:
+ defaultProperties.append( [ "mainmenu", labelID, "widgetTarget", elem.attrib.get( "target" ), defaultID ] )
else:
- defaultProperties.append( [ elem.attrib.get( "group" ), elem.attrib.get( 'labelID' ), elemSearch[0], elem.text, elem.attrib.get( 'defaultID' ) ] )
+ defaultProperties.append( [ elem.attrib.get( "group" ), labelID, elemSearch[ 0 ].split( ":" )[ 0 ], elem.text, defaultID ] )
if elemSearch[ 0 ] == "background":
# Get and set the background name
backgroundName = self._getBackgroundName( elem.text )
if backgroundName is not None:
- defaultProperties.append( [ "mainmenu", elem.attrib.get( "labelID" ), "backgroundName", backgroundName, elem.attrib.get( 'defaultID' ) ] )
+ defaultProperties.append( [ "mainmenu", labelID, "backgroundName", backgroundName, defaultID ] )
if elemSearch[0] == "widget":
# Get and set widget type and name
widgetDetails = self._getWidgetNameAndType( elem.text )
if widgetDetails is not None:
- defaultProperties.append( [ elem.attrib.get( "group" ), elem.attrib.get( "labelID" ), "widgetName", widgetDetails[0], elem.attrib.get( 'defaultID' ) ] )
- if widgetDetails[1] is not None:
- defaultProperties.append( [ elem.attrib.get( "group" ), elem.attrib.get( "labelID" ), "widgetType", widgetDetails[1], elem.attrib.get( 'defaultID' ) ] )
+ defaultProperties.append( [ elem.attrib.get( "group" ), labelID, "widgetName", widgetDetails[ "name" ], defaultID ] )
+ if "type" in widgetDetails:
+ defaultProperties.append( [ elem.attrib.get( "group" ), labelID, "widgetType", widgetDetails[ "type" ], defaultID ] )
+ if "path" in widgetDetails:
+ defaultProperties.append( [ elem.attrib.get( "group" ), labelID, "widgetPath", widgetDetails[ "path" ], defaultID ] )
+ if "target" in widgetDetails:
+ defaultProperties.append( [ elem.attrib.get( "group" ), labelID, "widgetTarget", widgetDetails[ "target" ], defaultID ] )
+
+ if elemSearch[ 0 ] == "widget:node":
+ # Set all widget properties from the default
+ if "label" in elem.attrib:
+ defaultProperties.append( [ elem.attrib.get( "group" ), labelID, "widgetName", elem.attrib.get( "label" ), defaultID ] )
+ if "type" in elem.attrib:
+ defaultProperties.append( [ elem.attrib.get( "group" ), labelID, "widgetType", elem.attrib.get( "type" ), defaultID ] )
+ if "path" in elem.attrib:
+ defaultProperties.append( [ elem.attrib.get( "group" ), labelID, "widgetPath", elem.attrib.get( "path" ), defaultID ] )
+ if "target" in elem.attrib:
+ defaultProperties.append( [ elem.attrib.get( "group" ), labelID, "widgetTarget", elem.attrib.get( "target" ), defaultID ] )
returnVal = [currentProperties, defaultProperties]
return returnVal
@@ -528,12 +564,15 @@ def _getWidgetNameAndType( self, widgetID ):
if tree is not None:
for elem in tree.findall( "widget" ):
if elem.text == widgetID:
+ widgetInfo = { "name": elem.attrib.get( "label" ) }
if "type" in elem.attrib:
- returnList = [elem.attrib.get( "label" ), elem.attrib.get( "type" )]
- else:
- returnList = [ elem.attrib.get( "label" ), None ]
- self.widgetNameAndType[ widgetID ] = returnList
- return returnList
+ widgetInfo[ "type" ] = elem.attrib.get( "type" )
+ if "path" in elem.attrib:
+ widgetInfo[ "path" ] = elem.attrib.get( "path" )
+ if "target" in elem.attrib:
+ widgetInfo[ "target" ] = elem.attrib.get( "target" )
+ self.widgetNameAndType[ widgetID ] = widgetInfo
+ return widgetInfo
self.widgetNameAndType[ widgetID ] = None
return None
diff --git a/resources/lib/gui.py b/resources/lib/gui.py
index fcda8a5b..68edc19c 100644
--- a/resources/lib/gui.py
+++ b/resources/lib/gui.py
@@ -77,6 +77,7 @@ def onInit( self ):
self._close()
else:
self.window_id = xbmcgui.getCurrentWindowDialogId()
+ self.currentWindow = xbmcgui.Window( xbmcgui.getCurrentWindowDialogId() )
xbmcgui.Window(self.window_id).setProperty('groupname', self.group)
if self.groupname is not None:
xbmcgui.Window( self.window_id ).setProperty( 'groupDisplayName', self.groupname )
@@ -168,13 +169,19 @@ def onInit( self ):
try:
if self.getControl( 309 ).getLabel() == "":
self.getControl( 309 ).setLabel( __language__(32044) )
+ log( "Warning: Deprecated widget button (id 309)" )
except:
- log( "No widget button on GUI (id 309)" )
+ pass
try:
if self.getControl( 310 ).getLabel() == "":
self.getControl( 310 ).setLabel( __language__(32045) )
except:
log( "No background button on GUI (id 310)" )
+ try:
+ if self.getControl( 312 ).getLabel() == "":
+ self.getControl( 312 ).setLabel( __language__(32044) )
+ except:
+ log( "No widget button on GUI (id 309)" )
try:
if self.getControl( 401 ).getLabel() == "":
@@ -683,8 +690,6 @@ def _save_properties( self, properties, labelIDChanges ):
def _load_widgetsbackgrounds( self ):
# Pre-load widget, background and thumbnail options provided by skin
- self.widgets = []
- self.widgetsPretty = {}
self.backgrounds = []
self.backgroundsPretty = {}
self.thumbnails = []
@@ -692,26 +697,13 @@ def _load_widgetsbackgrounds( self ):
# Load skin overrides
tree = DATA._get_overrides_skin()
-
- # Get widgets
- if tree is not None:
- elems = tree.getroot().findall('widget')
- for elem in elems:
- widgetType = None
- if "type" in elem.attrib:
- widgetType = elem.attrib.get( "type" )
- if "condition" in elem.attrib:
- if not xbmc.getCondVisibility( elem.attrib.get( "condition" ) ):
- continue
- self.widgets.append( [elem.text, DATA.local( elem.attrib.get( 'label' ) )[2], widgetType ] )
- self.widgetsPretty[elem.text] = DATA.local( elem.attrib.get( 'label' ) )[2]
- # Should we allow the user to select a playlist as a widget...
- elem = tree.find('widgetPlaylists')
- if elem is not None and elem.text == "True":
- self.widgetPlaylists = True
- if "type" in elem.attrib:
- self.widgetPlaylistsType = elem.attrib.get( "type" )
+ # Should we allow the user to select a playlist as a widget...
+ elem = tree.find('widgetPlaylists')
+ if elem is not None and elem.text == "True":
+ self.widgetPlaylists = True
+ if "type" in elem.attrib:
+ self.widgetPlaylistsType = elem.attrib.get( "type" )
# Get backgrounds
if tree is not None:
@@ -1003,7 +995,12 @@ def onClick(self, controlID):
if action == "noop":
action = ""
- selectedShortcut = LIBRARY.selectShortcut(custom = True, currentAction = listitem.getProperty("path"))
+ if self.currentWindow.getProperty( "custom-grouping" ):
+ selectedShortcut = LIBRARY.selectShortcut(custom = True, currentAction = listitem.getProperty("path"), grouping = self.currentWindow.getProperty( "custom-grouping" ))
+ self.currentWindow.clearProperty( "custom-grouping" )
+ else:
+ selectedShortcut = LIBRARY.selectShortcut(custom = True, currentAction = listitem.getProperty("path"))
+
if selectedShortcut:
if selectedShortcut.getProperty( "path" ):
action = try_decode(selectedShortcut.getProperty( "path" ))
@@ -1039,16 +1036,18 @@ def onClick(self, controlID):
if controlID == 309:
# Choose widget
- log( "Choose widget (309)" )
- currentWindow = xbmcgui.Window( xbmcgui.getCurrentWindowDialogId() )
+ log( "Warning: Deprecated control 309 (Choose widget) selected")
listControl = self.getControl( 211 )
listitem = listControl.getSelectedItem()
+
+ # Check that widgets have been loaded
+ LIBRARY.widgets()
# If we're setting for an additional widget, get it's number
widgetID = ""
- if currentWindow.getProperty( "widgetID" ):
- widgetID += "." + currentWindow.getProperty( "widgetID" )
- currentWindow.clearProperty( "widgetID" )
+ if self.currentWindow.getProperty( "widgetID" ):
+ widgetID += "." + self.currentWindow.getProperty( "widgetID" )
+ self.currentWindow.clearProperty( "widgetID" )
# Get the default widget for this item
defaultWidget = self.find_default( "widget", listitem.getProperty( "labelID" ), listitem.getProperty( "defaultID" ) )
@@ -1058,7 +1057,7 @@ def onClick(self, controlID):
widgetLabel = [__language__(32053)]
widgetName = [""]
widgetType = [ None ]
- for key in self.widgets:
+ for key in LIBRARY.dictionaryGroupings[ "widgets-classic" ]:
widget.append( key[0] )
widgetName.append( "" )
widgetType.append( key[2] )
@@ -1103,16 +1102,16 @@ def onClick(self, controlID):
self._add_additionalproperty( listitem, "widget" + widgetID, "Playlist" )
self._add_additionalproperty( listitem, "widgetName" + widgetID, widgetName[selectedWidget] )
self._add_additionalproperty( listitem, "widgetPlaylist" + widgetID, widget[selectedWidget].strip( "::PLAYLIST::" ) )
- if currentWindow.getProperty( "useWidgetNameAsLabel" ) == "true" and widgetID == "":
+ if self.currentWindow.getProperty( "useWidgetNameAsLabel" ) == "true" and widgetID == "":
self._set_label( listitem, widgetName[selectedWidget] )
- currentWindow.clearProperty( "useWidgetNameAsLabel" )
+ self.currentWindow.clearProperty( "useWidgetNameAsLabel" )
else:
self._add_additionalproperty( listitem, "widgetName" + widgetID, widgetLabel[selectedWidget].replace( " (%s)" %( __language__(32050) ), "" ) )
self._add_additionalproperty( listitem, "widget" + widgetID, widget[selectedWidget] )
self._remove_additionalproperty( listitem, "widgetPlaylist" + widgetID )
- if currentWindow.getProperty( "useWidgetNameAsLabel" ) == "true" and widgetID == "":
+ if self.currentWindow.getProperty( "useWidgetNameAsLabel" ) == "true" and widgetID == "":
self._set_label( listitem, widgetLabel[selectedWidget].replace( " (%s)" %( __language__(32050) ), "" ) )
- currentWindow.clearProperty( "useWidgetNameAsLabel" )
+ self.currentWindow.clearProperty( "useWidgetNameAsLabel" )
if widgetType[ selectedWidget] is not None:
self._add_additionalproperty( listitem, "widgetType" + widgetID, widgetType[ selectedWidget] )
@@ -1120,6 +1119,50 @@ def onClick(self, controlID):
self._remove_additionalproperty( listitem, "widgetType" + widgetID )
self.changeMade = True
+
+ if controlID == 312:
+ # Alternative widget select
+ log( "Choose widget (312)" )
+ listControl = self.getControl( 211 )
+ listitem = listControl.getSelectedItem()
+
+ # If we're setting for an additional widget, get its number
+ widgetID = ""
+ if self.currentWindow.getProperty( "widgetID" ):
+ widgetID = "." + self.currentWindow.getProperty( "widgetID" )
+ self.currentWindow.clearProperty( "widgetID" )
+
+ # Get the default widget for this item
+ defaultWidget = self.find_default( "widget", listitem.getProperty( "labelID" ), listitem.getProperty( "defaultID" ) )
+
+ # Ensure widgets are loaded
+ LIBRARY.widgets()
+
+ # Let user choose widget
+ selectedShortcut = LIBRARY.selectShortcut( grouping = "widget", showNone = True )
+
+ if selectedShortcut is not None and selectedShortcut.getProperty( "Path" ):
+ self._add_additionalproperty( listitem, "widget" + widgetID, selectedShortcut.getProperty( "widget" ) )
+ self._add_additionalproperty( listitem, "widgetName" + widgetID, selectedShortcut.getProperty( "widgetName" ) )
+ self._add_additionalproperty( listitem, "widgetType" + widgetID, selectedShortcut.getProperty( "widgetType" ) )
+ self._add_additionalproperty( listitem, "widgetTarget" + widgetID, selectedShortcut.getProperty( "widgetTarget" ) )
+ self._add_additionalproperty( listitem, "widgetPath" + widgetID, selectedShortcut.getProperty( "widgetPath" ) )
+ if self.currentWindow.getProperty( "useWidgetNameAsLabel" ) == "true" and widgetID == "":
+ self._set_label( listitem, selectedShortcut.getProperty( ( "widgetName" ) ) )
+ self.currentWindow.clearProperty( "useWidgetNameAsLabel" )
+ self.changeMade = True
+
+ elif selectedShortcut is not None:
+ self._remove_additionalproperty( listitem, "widget" + widgetID )
+ self._remove_additionalproperty( listitem, "widgetName" + widgetID )
+ self._remove_additionalproperty( listitem, "widgetType" + widgetID )
+ self._remove_additionalproperty( listitem, "widgetTarget" + widgetID )
+ self._remove_additionalproperty( listitem, "widgetPath" + widgetID )
+ if self.currentWindow.getProperty( "useWidgetNameAsLabel" ) == "true" and widgetID == "":
+ self._set_label( listitem, selectedShortcut.getProperty( ( "widgetName" ) ) )
+ self.currentWindow.clearProperty( "useWidgetNameAsLabel" )
+ self.changeMade = True
+
if controlID == 310:
# Choose background
@@ -1276,14 +1319,18 @@ def onClick(self, controlID):
if controlID == 401:
# Select shortcut
log( "Select shortcut (401)" )
-
num = self.getControl( 211 ).getSelectedPosition()
orderIndex = int( self.getControl( 211 ).getListItem( num ).getProperty( "skinshortcuts-orderindex" ) )
if self.warnonremoval( self.getControl( 211 ).getListItem( num ) ) == False:
return
-
- selectedShortcut = LIBRARY.selectShortcut()
+
+ if self.currentWindow.getProperty( "custom-grouping" ):
+ selectedShortcut = LIBRARY.selectShortcut( grouping = self.currentWindow.getProperty( "custom-grouping" ) )
+ self.currentWindow.clearProperty( "custom-grouping" )
+ else:
+ selectedShortcut = LIBRARY.selectShortcut()
+
if selectedShortcut is not None:
listitemCopy = self._duplicate_listitem( selectedShortcut, self.getControl( 211 ).getListItem( num ) )
if selectedShortcut.getProperty( "chosenPath" ):
@@ -1302,19 +1349,18 @@ def onClick(self, controlID):
listControl = self.getControl( 211 )
listitem = listControl.getSelectedItem()
- currentWindow = xbmcgui.Window(xbmcgui.getCurrentWindowDialogId())
propertyName = ""
propertyValue = ""
# Retrieve the custom property
- if currentWindow.getProperty( "customProperty" ):
- propertyName = currentWindow.getProperty( "customProperty" )
- currentWindow.clearProperty( "customProperty" )
+ if self.currentWindow.getProperty( "customProperty" ):
+ propertyName = self.currentWindow.getProperty( "customProperty" )
+ self.currentWindow.clearProperty( "customProperty" )
# Retrieve the custom value
- if currentWindow.getProperty( "customValue" ):
- propertyValue = currentWindow.getProperty( "customValue" )
- currentWindow.clearProperty( "customValue" )
+ if self.currentWindow.getProperty( "customValue" ):
+ propertyValue = self.currentWindow.getProperty( "customValue" )
+ self.currentWindow.clearProperty( "customValue" )
if propertyValue == "":
# No value set, so remove it from additionalListItemProperties
@@ -1325,8 +1371,8 @@ def onClick(self, controlID):
self._add_additionalproperty( listitem, propertyName, propertyValue )
self.changeMade = True
- elif currentWindow.getProperty( "chooseProperty" ):
- propertyName = currentWindow.getProperty( "chooseProperty" )
+ elif self.currentWindow.getProperty( "chooseProperty" ):
+ propertyName = self.currentWindow.getProperty( "chooseProperty" )
# Create lists for the select dialog
property = [""]
propertyLabel = ["None"]
@@ -1355,14 +1401,12 @@ def onClick(self, controlID):
else:
# The customProperty value needs to be set, so return
- currentWindow.clearProperty( "customValue" )
+ self.currentWindow.clearProperty( "customValue" )
return
if controlID == 405 or controlID == 406 or controlID == 407 or controlID == 408 or controlID == 409 or controlID == 410:
# Launch management dialog for submenu
log( "Launching management dialog for submenu/additional menu (" + str( controlID ) + ")" )
-
- currentWindow = xbmcgui.Window(xbmcgui.getCurrentWindowDialogId())
# Get the group we're about to edit
launchGroup = self.getControl( 211 ).getSelectedItem().getProperty( "labelID" )
@@ -1403,22 +1447,22 @@ def onClick(self, controlID):
elif controlID == 410:
launchGroup = launchGroup + ".5"
# Check if 'level' property has been set
- elif currentWindow.getProperty("level"):
- launchGroup = launchGroup + "." + currentWindow.getProperty("level")
- currentWindow.clearProperty("level")
+ elif self.currentWindow.getProperty("level"):
+ launchGroup = launchGroup + "." + self.currentWindow.getProperty("level")
+ self.currentWindow.clearProperty("level")
# Check if 'groupname' property has been set
- if currentWindow.getProperty( "overrideName" ):
- groupName = currentWindow.getProperty( "overrideName" )
- currentWindow.clearProperty( "overrideName" )
+ if self.currentWindow.getProperty( "overrideName" ):
+ groupName = self.currentWindow.getProperty( "overrideName" )
+ self.currentWindow.clearProperty( "overrideName" )
# Execute the script
- currentWindow.setProperty( "additionalDialog", "True" )
+ self.currentWindow.setProperty( "additionalDialog", "True" )
import gui
ui= gui.GUI( "script-skinshortcuts.xml", __cwd__, "default", group=launchGroup, defaultGroup=launchDefaultGroup, nolabels=self.nolabels, groupname=groupName )
ui.doModal()
del ui
- currentWindow.clearProperty( "additionalDialog" )
+ self.currentWindow.clearProperty( "additionalDialog" )
diff --git a/resources/lib/library.py b/resources/lib/library.py
index 2cef6dfd..fb63ae41 100755
--- a/resources/lib/library.py
+++ b/resources/lib/library.py
@@ -88,11 +88,39 @@ def __init__( self, *args, **kwargs ):
self.loadedFavourites = False
self.loadedUPNP = False
self.loadedSettings = False
+ self.loadedWidgets = False
self.widgetPlaylistsList = []
# Empty dictionary for different shortcut types
- self.dictionaryGroupings = {"common":None, "commands":None, "video":None, "movie":None, "movie-flat":None, "tvshow":None, "tvshow-flat":None, "musicvideo":None, "musicvideo-flat":None, "customvideonode":None, "customvideonode-flat":None, "videosources":None, "pvr":None, "radio":None, "pvr-tv":None, "pvr-radio":None, "music":None, "musicsources":None, "picturesources":None, "playlist-video":None, "playlist-audio":None, "addon-program":None, "addon-video":None, "addon-audio":None, "addon-image":None, "favourite":None, "settings":None }
+ self.dictionaryGroupings = {"common":None,
+ "commands":None,
+ "video":None, "movie":None,
+ "movie-flat":None,
+ "tvshow":None,
+ "tvshow-flat":None,
+ "musicvideo":None,
+ "musicvideo-flat":None,
+ "customvideonode":None,
+ "customvideonode-flat":None,
+ "videosources":None,
+ "pvr":None,
+ "radio":None,
+ "pvr-tv":None,
+ "pvr-radio":None,
+ "music":None,
+ "musicsources":None,
+ "picturesources":None,
+ "playlist-video":None,
+ "playlist-audio":None,
+ "addon-program":None,
+ "addon-video":None,
+ "addon-audio":None,
+ "addon-image":None,
+ "favourite":None,
+ "settings":None,
+ "widgets":None,
+ "widgets-classic":[] }
self.folders = {}
self.foldersCount = 0
@@ -111,6 +139,7 @@ def loadLibrary( self ):
self.addons()
self.favourites()
self.settings()
+ self.widgets()
# Do a JSON query for upnp sources (so that they'll show first time the user asks to see them)
if self.loadedUPNP == False:
@@ -121,7 +150,7 @@ def loadLibrary( self ):
# === BUILD/DISPLAY AVAILABLE SHORTCUT NODES ===
# ==============================================
- def retrieveGroup( self, group, flat = True ):
+ def retrieveGroup( self, group, flat = True, grouping = None ):
trees = [DATA._get_overrides_skin(), DATA._get_overrides_script()]
nodes = None
for tree in trees:
@@ -130,8 +159,10 @@ def retrieveGroup( self, group, flat = True ):
nodes = tree.find( "flatgroupings" )
if nodes is not None:
nodes = nodes.findall( "node" )
- else:
+ elif grouping is None:
nodes = tree.find( "groupings" )
+ else:
+ nodes = tree.find( "%s-groupings" %( grouping ) )
if nodes is not None:
break
@@ -172,7 +203,10 @@ def retrieveGroup( self, group, flat = True ):
# Heirachical groupings
if group == "":
# We're going to get the root nodes
- return [ __language__(32048), self.buildNodeListing( nodes, False ) ]
+ windowTitle = __language__(32048)
+ if grouping == "widget":
+ windowTitle = __language__(32044)
+ return [ windowTitle, self.buildNodeListing( nodes, False ) ]
else:
groups = group.split( "," )
@@ -216,7 +250,25 @@ def buildNodeListing( self, nodes, flat ):
if node.tag == "content":
returnList = returnList + self.retrieveContent( node.text )
if node.tag == "shortcut":
- returnList.append( self._create( [node.text, node.attrib.get( "label" ), node.attrib.get( "type" ), {"icon": node.attrib.get( "icon" )}] ) )
+ shortcutItem = self._create( [node.text, node.attrib.get( "label" ), node.attrib.get( "type" ), {"icon": node.attrib.get( "icon" )}] )
+ if "widget" in node.attrib:
+ # This is a widget shortcut, so add the relevant widget information
+ shortcutItem.setProperty( "widget", node.attrib.get( "widget" ) )
+ if "widgetType" in node.attrib:
+ shortcutItem.setProperty( "widgetType", node.attrib.get( "widgetType" ) )
+ else:
+ shortcutItem.setProperty( "widgetType", "" )
+ if "widgetTarget" in node.attrib:
+ shortcutItem.setProperty( "widgetTarget", node.attrib.get( "widgetTarget" ) )
+ else:
+ shortcutItem.setProperty( "widgetTarget", "" )
+ if "widgetName" in node.attrib:
+ shortcutItem.setProperty( "widgetName", node.attrib.get( "label" ) )
+ else:
+ shortcutItem.setProperty( "widgetName", "" )
+ shortcutItem.setProperty( "widgetPath", node.text )
+ returnList.append( shortcutItem )
+ #returnList.append( self._create( [node.text, node.attrib.get( "label" ), node.attrib.get( "type" ), {"icon": node.attrib.get( "icon" )}] ) )
if node.tag == "node" and flat == False:
returnList.append( self._create( ["||NODE||" + str( count ), node.attrib.get( "label" ), "", {"icon": "DefaultFolder.png"}] ) )
@@ -238,6 +290,27 @@ def retrieveContent( self, content ):
items = self.checkForFolder( items )
else:
items = []
+
+ # Add widget information for video/audio nodes
+ if content in [ "video", "music" ]:
+ # Video nodes - add widget information
+ for listitem in items:
+ path = listitem.getProperty( "path" )
+ if path.lower().startswith( "activatewindow" ):
+ path = DATA.getListProperty( path )
+ listitem.setProperty( "widget", "Library" )
+ if content == "video":
+ listitem.setProperty( "widgetType", "video" )
+ listitem.setProperty( "widgetTarget", "video" )
+ else:
+ listitem.setProperty( "widgetType", "audio" )
+ listitem.setProperty( "widgetTarget", "music" )
+ listitem.setProperty( "widgetName", listitem.getLabel() )
+ listitem.setProperty( "widgetPath", path )
+
+ widgetType = NODE.get_mediaType( path )
+ if widgetType != "unknown":
+ listitem.setProperty( "widgetType", widgetType )
# Check for any icon overrides for these items
tree = DATA._get_overrides_skin()
@@ -292,6 +365,8 @@ def loadGrouping( self, content ):
self.favourites()
if content == "settings":
self.settings()
+ if content == "widgets":
+ self.widgets()
# The data has now been loaded, return it
return self.dictionaryGroupings[ content ]
@@ -331,7 +406,7 @@ def addToDictionary( self, group, content ):
if group.endswith( "-flat" ):
group = group.replace( "-flat", "" )
- if group != "movie" and group != "tvshow" and group != "musicvideo":
+ if group not in [ "movie", "tvshow", "musicvideo" ]:
for elem in tree.findall( "shortcut" ):
if "grouping" in elem.attrib:
if group == elem.attrib.get( "grouping" ):
@@ -560,16 +635,14 @@ def _get_icon_overrides( self, tree, item, content, setToDefault = True ):
# ===================================
def videolibrary( self ):
- if self.loadedVideoLibrary == True:
+ if self.loadedVideoLibrary is True:
# The List has already been populated, return it
return self.loadedVideoLibrary
elif self.loadedVideoLibrary == "Loading":
# The list is currently being populated, wait and then return it
- count = 0
- while count < 20:
+ for i in ( 0, 20 ):
xbmc.sleep( 100 )
- count += 1
- if self.loadedVideoLibrary == True:
+ if self.loadedVideoLibrary is True:
return self.loadedVideoLibrary
else:
# We're going to populate the list
@@ -601,9 +674,11 @@ def _parse_libraryNodes( self, library, type ):
if library == "video":
windowID = "10025"
prefix = "library://video"
+ action = "||VIDEO||"
elif library == "music":
windowID = "10502"
prefix = "library://music"
+ action = "||AUDIO||"
rootdir = os.path.join( xbmc.translatePath( "special://profile".decode('utf-8') ), "library", library )
if type == "custom":
@@ -624,12 +699,17 @@ def _parse_libraryNodes( self, library, type ):
# 2 = Path
# 3 = Type
# 4 = Order
+ # 5 = Media type (not folders...?)
if nodes[ key ][ 3 ] == "folder":
- items.append( self._create( [ "||FOLDER||%s" % ( nodes[ key ][ 2 ] ), nodes[ key ][ 0 ], nodes[ key ][ 3 ], { "icon": nodes[ key ][ 1 ] } ] ) )
+ item = self._create( [ "%s%s" % ( action, nodes[ key ][ 2 ] ), nodes[ key ][ 0 ], nodes[ key ][ 3 ], { "icon": nodes[ key ][ 1 ] } ] )
elif nodes[ key ][ 3 ] == "grouped":
- items.append( self._create( [ "||FOLDER||%s" % ( nodes[ key ][ 2 ] ), nodes[ key ][ 0 ], nodes[ key ][ 3 ], { "icon": nodes[ key ][ 1 ] } ] ) )
+ item = self._create( [ "%s%s" % ( action, nodes[ key ][ 2 ] ), nodes[ key ][ 0 ], nodes[ key ][ 3 ], { "icon": nodes[ key ][ 1 ] } ] )
else:
- items.append( self._create( [ "ActivateWindow(%s,%s,return)" %( windowID, nodes[ key ][ 2 ] ), nodes[ key ][ 0 ], nodes[ key ][ 3 ], { "icon": nodes[ key ][ 1 ] } ] ) )
+ item = self._create( [ "ActivateWindow(%s,%s,return)" %( windowID, nodes[ key ][ 2 ] ), nodes[ key ][ 0 ], nodes[ key ][ 3 ], { "icon": nodes[ key ][ 1 ] } ] )
+ if nodes[ key ][ 5 ] is not None:
+ item.setProperty( "widgetType", nodes[ key ][ 5 ] )
+ item.setProperty( "widgetTarget", library )
+ items.append( item )
self.addToDictionary( library, items )
@@ -639,15 +719,13 @@ def _parse_libraryNodes( self, library, type ):
# ============================
def common( self ):
- if self.loadedCommon == True:
+ if self.loadedCommon is True:
return True
elif self.loadedCommon == "Loading":
# The list is currently being populated, wait and then return it
- count = 0
- while count < 20:
+ for i in ( 0, 20 ):
xbmc.sleep( 100 )
- count += 1
- if self.loadedCommon == True:
+ if self.loadedCommon is True:
return True
else:
# We're going to populate the list
@@ -659,36 +737,31 @@ def common( self ):
# Videos, Movies, TV Shows, Live TV, Music, Music Videos, Pictures, Weather, Programs,
# Play dvd, eject tray
# Settings, File Manager, Profiles, System Info
- try:
- listitems.append( self._create(["ActivateWindow(Videos)", "10006", "32034", {"icon": "DefaultVideo.png"} ]) )
- listitems.append( self._create(["ActivateWindow(Videos,videodb://movies/titles/,return)", "342", "32034", {"icon": "DefaultMovies.png"} ]) )
- listitems.append( self._create(["ActivateWindow(Videos,videodb://tvshows/titles/,return)", "20343", "32034", {"icon": "DefaultTVShows.png"} ]) )
+ listitems.append( self._create(["ActivateWindow(Videos)", "10006", "32034", {"icon": "DefaultVideo.png"} ]) )
+ listitems.append( self._create(["ActivateWindow(Videos,videodb://movies/titles/,return)", "342", "32034", {"icon": "DefaultMovies.png"} ]) )
+ listitems.append( self._create(["ActivateWindow(Videos,videodb://tvshows/titles/,return)", "20343", "32034", {"icon": "DefaultTVShows.png"} ]) )
- if __xbmcversion__ == "13":
- listitems.append( self._create(["ActivateWindowAndFocus(MyPVR,34,0 ,13,0)", "32022", "32034", {"icon": "DefaultTVShows.png"} ]) )
- else:
- listitems.append( self._create(["ActivateWindow(TVGuide)", "32022", "32034", {"icon": "DefaultTVShows.png"} ]) )
- listitems.append( self._create(["ActivateWindow(RadioGuide)", "32087", "32034", {"icon": "DefaultTVShows.png"} ]) )
+ if __xbmcversion__ == "13":
+ listitems.append( self._create(["ActivateWindowAndFocus(MyPVR,34,0 ,13,0)", "32022", "32034", {"icon": "DefaultTVShows.png"} ]) )
+ else:
+ listitems.append( self._create(["ActivateWindow(TVGuide)", "32022", "32034", {"icon": "DefaultTVShows.png"} ]) )
+ listitems.append( self._create(["ActivateWindow(RadioGuide)", "32087", "32034", {"icon": "DefaultTVShows.png"} ]) )
+
+ listitems.append( self._create(["ActivateWindow(Music)", "10005", "32034", {"icon": "DefaultMusicAlbums.png"} ]) )
+ listitems.append( self._create(["ActivateWindow(Videos,videodb://musicvideos/titles/,return)", "20389", "32034", {"icon": "DefaultMusicVideos.png"} ] ) )
+ listitems.append( self._create(["ActivateWindow(Pictures)", "10002", "32034", {"icon": "DefaultPicture.png"} ] ) )
+ listitems.append( self._create(["ActivateWindow(Weather)", "12600", "32034", {} ]) )
+ listitems.append( self._create(["ActivateWindow(Programs,Addons,return)", "10001", "32034", {"icon": "DefaultProgram.png"} ] ) )
+
+ listitems.append( self._create(["XBMC.PlayDVD()", "32032", "32034", {"icon": "DefaultDVDFull.png"} ] ) )
+ listitems.append( self._create(["EjectTray()", "32033", "32034", {"icon": "DefaultDVDFull.png"} ] ) )
- listitems.append( self._create(["ActivateWindow(Music)", "10005", "32034", {"icon": "DefaultMusicAlbums.png"} ]) )
- listitems.append( self._create(["ActivateWindow(Videos,videodb://musicvideos/titles/,return)", "20389", "32034", {"icon": "DefaultMusicVideos.png"} ] ) )
- listitems.append( self._create(["ActivateWindow(Pictures)", "10002", "32034", {"icon": "DefaultPicture.png"} ] ) )
- listitems.append( self._create(["ActivateWindow(Weather)", "12600", "32034", {} ]) )
- listitems.append( self._create(["ActivateWindow(Programs,Addons,return)", "10001", "32034", {"icon": "DefaultProgram.png"} ] ) )
-
- listitems.append( self._create(["XBMC.PlayDVD()", "32032", "32034", {"icon": "DefaultDVDFull.png"} ] ) )
- listitems.append( self._create(["EjectTray()", "32033", "32034", {"icon": "DefaultDVDFull.png"} ] ) )
-
- listitems.append( self._create(["ActivateWindow(Settings)", "10004", "32034", {} ]) )
- listitems.append( self._create(["ActivateWindow(FileManager)", "7", "32034", {"icon": "DefaultFolder.png"} ] ) )
- listitems.append( self._create(["ActivateWindow(Profiles)", "13200", "32034", {"icon": "UnknownUser.png"} ] ) )
- listitems.append( self._create(["ActivateWindow(SystemInfo)", "10007", "32034", {} ]) )
-
- listitems.append( self._create(["ActivateWindow(Favourites)", "1036", "32034", {} ]) )
- except:
- log( "Failed to load common XBMC shortcuts" )
- print_exc()
- listitems = []
+ listitems.append( self._create(["ActivateWindow(Settings)", "10004", "32034", {} ]) )
+ listitems.append( self._create(["ActivateWindow(FileManager)", "7", "32034", {"icon": "DefaultFolder.png"} ] ) )
+ listitems.append( self._create(["ActivateWindow(Profiles)", "13200", "32034", {"icon": "UnknownUser.png"} ] ) )
+ listitems.append( self._create(["ActivateWindow(SystemInfo)", "10007", "32034", {} ]) )
+
+ listitems.append( self._create(["ActivateWindow(Favourites)", "1036", "32034", {} ]) )
self.addToDictionary( "common", listitems )
@@ -697,117 +770,103 @@ def common( self ):
return self.loadedCommon
def more( self ):
- if self.loadedMoreCommands == True:
+ if self.loadedMoreCommands is True:
# The List has already been populated, return it
return True
elif self.loadedMoreCommands == "Loading":
# The list is currently being populated, wait and then return it
- count = 0
- while count < 20:
+ for i in ( 0, 20 ):
xbmc.sleep( 100 )
- count += 1
- if self.loadedMoreCommands == True:
+ if self.loadedMoreCommands is True:
return True
else:
# We're going to populate the list
self.loadedMoreCommands = "Loading"
- try:
- listitems = []
- log( 'Listing more XBMC commands...' )
-
- listitems.append( self._create(["Reboot", "13013", "32054", {} ]) )
- listitems.append( self._create(["ShutDown", "13005", "32054", {} ]) )
- listitems.append( self._create(["PowerDown", "13016", "32054", {} ]) )
- listitems.append( self._create(["Quit", "13009", "32054", {} ]) )
- listitems.append( self._create(["Hibernate", "13010", "32054", {} ]) )
- listitems.append( self._create(["Suspend", "13011", "32054", {} ]) )
- listitems.append( self._create(["AlarmClock(shutdowntimer,XBMC.Shutdown())", "19026", "32054", {} ]) )
- listitems.append( self._create(["CancelAlarm(shutdowntimer)", "20151", "32054", {} ]) )
- if xbmc.getCondVisibility( "System.HasLoginScreen" ):
- listitems.append( self._create(["System.LogOff", "20126", "32054", {} ]) )
- listitems.append( self._create(["ActivateScreensaver", "360", "32054", {} ]) )
- listitems.append( self._create(["Minimize", "13014", "32054", {} ]) )
+ listitems = []
+ log( 'Listing more XBMC commands...' )
+
+ listitems.append( self._create(["Reboot", "13013", "32054", {} ]) )
+ listitems.append( self._create(["ShutDown", "13005", "32054", {} ]) )
+ listitems.append( self._create(["PowerDown", "13016", "32054", {} ]) )
+ listitems.append( self._create(["Quit", "13009", "32054", {} ]) )
+ listitems.append( self._create(["Hibernate", "13010", "32054", {} ]) )
+ listitems.append( self._create(["Suspend", "13011", "32054", {} ]) )
+ listitems.append( self._create(["AlarmClock(shutdowntimer,XBMC.Shutdown())", "19026", "32054", {} ]) )
+ listitems.append( self._create(["CancelAlarm(shutdowntimer)", "20151", "32054", {} ]) )
+ if xbmc.getCondVisibility( "System.HasLoginScreen" ):
+ listitems.append( self._create(["System.LogOff", "20126", "32054", {} ]) )
+ listitems.append( self._create(["ActivateScreensaver", "360", "32054", {} ]) )
+ listitems.append( self._create(["Minimize", "13014", "32054", {} ]) )
- listitems.append( self._create(["Mastermode", "20045", "32054", {} ]) )
-
- listitems.append( self._create(["RipCD", "600", "32054", {} ]) )
-
- if __xbmcversion__ == "13":
- listitems.append( self._create(["UpdateLibrary(video)", "32046", "32054", {} ]) )
- listitems.append( self._create(["UpdateLibrary(music)", "32047", "32054", {} ]) )
- else:
- listitems.append( self._create(["UpdateLibrary(video,,true)", "32046", "32054", {} ]) )
- listitems.append( self._create(["UpdateLibrary(music,,true)", "32047", "32054", {} ]) )
-
- if __xbmcversion__ == "13":
- listitems.append( self._create(["CleanLibrary(video)", "32055", "32054", {} ]) )
- listitems.append( self._create(["CleanLibrary(music)", "32056", "32054", {} ]) )
- else:
- listitems.append( self._create(["CleanLibrary(video,true)", "32055", "32054", {} ]) )
- listitems.append( self._create(["CleanLibrary(music,true)", "32056", "32054", {} ]) )
-
- self.addToDictionary( "commands", listitems )
- except:
- log( "Failed to load more XBMC commands" )
- print_exc()
+ listitems.append( self._create(["Mastermode", "20045", "32054", {} ]) )
+
+ listitems.append( self._create(["RipCD", "600", "32054", {} ]) )
+
+ if __xbmcversion__ == "13":
+ listitems.append( self._create(["UpdateLibrary(video)", "32046", "32054", {} ]) )
+ listitems.append( self._create(["UpdateLibrary(music)", "32047", "32054", {} ]) )
+ else:
+ listitems.append( self._create(["UpdateLibrary(video,,true)", "32046", "32054", {} ]) )
+ listitems.append( self._create(["UpdateLibrary(music,,true)", "32047", "32054", {} ]) )
+
+ if __xbmcversion__ == "13":
+ listitems.append( self._create(["CleanLibrary(video)", "32055", "32054", {} ]) )
+ listitems.append( self._create(["CleanLibrary(music)", "32056", "32054", {} ]) )
+ else:
+ listitems.append( self._create(["CleanLibrary(video,true)", "32055", "32054", {} ]) )
+ listitems.append( self._create(["CleanLibrary(music,true)", "32056", "32054", {} ]) )
+
+ self.addToDictionary( "commands", listitems )
self.loadedMoreCommands = True
return self.loadedMoreCommands
def settings( self ):
- if self.loadedSettings == True:
+ if self.loadedSettings is True:
# The List has already been populated, return it
return True
elif self.loadedSettings == "Loading":
# The list is currently being populated, wait and then return it
- count = 0
- while count < 20:
+ for i in ( 0, 20 ):
xbmc.sleep( 100 )
- count += 1
- if self.loadedSettings == True:
+ if self.loadedSettings is True:
return True
else:
# We're going to populate the list
self.loadedSettings = "Loading"
- try:
- listitems = []
- log( 'Listing XBMC settings...' )
-
- listitems.append( self._create(["ActivateWindow(Settings)", "10004", "10004", {} ]) )
-
- listitems.append( self._create(["ActivateWindow(AppearanceSettings)", "480", "10004", {} ]) )
- listitems.append( self._create(["ActivateWindow(VideosSettings)", "3", "10004", {} ]) )
- listitems.append( self._create(["ActivateWindow(PVRSettings)", "19020", "10004", {} ]) )
- listitems.append( self._create(["ActivateWindow(MusicSettings)", "2", "10004", {} ]) )
- listitems.append( self._create(["ActivateWindow(PicturesSettings)", "1", "10004", {} ]) )
- listitems.append( self._create(["ActivateWindow(WeatherSettings)", "8", "10004", {} ]) )
- listitems.append( self._create(["ActivateWindow(AddonBrowser)", "24001", "10004", {} ]) )
- listitems.append( self._create(["ActivateWindow(ServiceSettings)", "14036", "10004", {} ]) )
- listitems.append( self._create(["ActivateWindow(SystemSettings)", "13000", "10004", {} ]) )
- listitems.append( self._create(["ActivateWindow(SkinSettings)", "20077", "10004", {} ]) )
-
- self.addToDictionary( "settings", listitems )
- except:
- log( "Failed to load more XBMC settings" )
- print_exc()
+ listitems = []
+ log( 'Listing XBMC settings...' )
+
+ listitems.append( self._create(["ActivateWindow(Settings)", "10004", "10004", {} ]) )
+
+ listitems.append( self._create(["ActivateWindow(AppearanceSettings)", "480", "10004", {} ]) )
+ listitems.append( self._create(["ActivateWindow(VideosSettings)", "3", "10004", {} ]) )
+ listitems.append( self._create(["ActivateWindow(PVRSettings)", "19020", "10004", {} ]) )
+ listitems.append( self._create(["ActivateWindow(MusicSettings)", "2", "10004", {} ]) )
+ listitems.append( self._create(["ActivateWindow(PicturesSettings)", "1", "10004", {} ]) )
+ listitems.append( self._create(["ActivateWindow(WeatherSettings)", "8", "10004", {} ]) )
+ listitems.append( self._create(["ActivateWindow(AddonBrowser)", "24001", "10004", {} ]) )
+ listitems.append( self._create(["ActivateWindow(ServiceSettings)", "14036", "10004", {} ]) )
+ listitems.append( self._create(["ActivateWindow(SystemSettings)", "13000", "10004", {} ]) )
+ listitems.append( self._create(["ActivateWindow(SkinSettings)", "20077", "10004", {} ]) )
+
+ self.addToDictionary( "settings", listitems )
self.loadedSettings = True
return self.loadedSettings
def pvrlibrary( self ):
- if self.loadedPVRLibrary == True:
+ if self.loadedPVRLibrary is True:
# The List has already been populated, return it
return self.loadedPVRLibrary
elif self.loadedPVRLibrary == "Loading":
# The list is currently being populated, wait and then return it
- count = 0
- while count < 20:
+ for i in ( 0, 20 ):
xbmc.sleep( 100 )
- count += 1
- if self.loadedPVRLibrary == True:
+ if self.loadedPVRLibrary is True:
return self.loadedPVRLibrary
else:
# We're going to populate the list
@@ -875,55 +934,46 @@ def pvrlibrary( self ):
return self.loadedPVRLibrary
def radiolibrary( self ):
- if self.loadedRadioLibrary == True:
+ if self.loadedRadioLibrary is True:
# The List has already been populated, return it
return self.loadedRadioLibrary
elif self.loadedRadioLibrary == "Loading":
# The list is currently being populated, wait and then return it
- count = 0
- while count < 20:
+ for i in ( 0, 20 ):
xbmc.sleep( 100 )
- count += 1
- if self.loadedRadioLibrary == True:
+ if self.loadedRadioLibrary is True:
return self.loadedRadioLibrary
else:
# We're going to populate the list
self.loadedRadioLibrary = "Loading"
- try:
- listitems = []
- log('Listing pvr-radio library...')
-
- # PVR
- listitems.append( self._create(["ActivateWindow(RadioChannels)", "19019", "32087", {"icon": "DefaultAudio.png"} ] ) )
- listitems.append( self._create(["ActivateWindow(RadioGuide)", "22020", "32087", {"icon": "DefaultAudio.png"} ] ) )
- listitems.append( self._create(["ActivateWindow(RadioRecordings)", "19163", "32087", {"icon": "DefaultAudio.png"} ] ) )
- listitems.append( self._create(["ActivateWindow(RadioTimers)", "19040", "32087", {"icon": "DefaultAudio.png"} ] ) )
- listitems.append( self._create(["ActivateWindow(RadioSearch)", "137", "32087", {"icon": "DefaultAudio.png"} ] ) )
-
- listitems.append( self._create(["PlayPvrRadio", "32067", "32087", {"icon": "DefaultAudio.png"} ] ) )
- listitems.append( self._create(["PlayPvr", "32068", "32087", {"icon": "DefaultAudio.png"} ] ) )
-
- self.addToDictionary( "radio", listitems )
+ listitems = []
+ log('Listing pvr-radio library...')
+
+ # PVR
+ listitems.append( self._create(["ActivateWindow(RadioChannels)", "19019", "32087", {"icon": "DefaultAudio.png"} ] ) )
+ listitems.append( self._create(["ActivateWindow(RadioGuide)", "22020", "32087", {"icon": "DefaultAudio.png"} ] ) )
+ listitems.append( self._create(["ActivateWindow(RadioRecordings)", "19163", "32087", {"icon": "DefaultAudio.png"} ] ) )
+ listitems.append( self._create(["ActivateWindow(RadioTimers)", "19040", "32087", {"icon": "DefaultAudio.png"} ] ) )
+ listitems.append( self._create(["ActivateWindow(RadioSearch)", "137", "32087", {"icon": "DefaultAudio.png"} ] ) )
+
+ listitems.append( self._create(["PlayPvrRadio", "32067", "32087", {"icon": "DefaultAudio.png"} ] ) )
+ listitems.append( self._create(["PlayPvr", "32068", "32087", {"icon": "DefaultAudio.png"} ] ) )
- except:
- log( "Failed to load pvr-radio library" )
- print_exc()
+ self.addToDictionary( "radio", listitems )
self.loadedRadioLibrary = True
return self.loadedRadioLibrary
def musiclibrary( self ):
- if self.loadedMusicLibrary == True:
+ if self.loadedMusicLibrary is True:
# The List has already been populated, return it
return self.loadedMusicLibrary
elif self.loadedMusicLibrary == "Loading":
# The list is currently being populated, wait and then return it
- count = 0
- while count < 20:
+ for i in ( 0, 20 ):
xbmc.sleep( 100 )
- count += 1
- if loadedMusicLibrary == True:
+ if loadedMusicLibrary is True:
return self.loadedMusicLibrary
else:
# We're going to populate the list
@@ -985,16 +1035,14 @@ def musiclibrary( self ):
return self.loadedMusicLibrary
def librarysources( self ):
- if self.loadedLibrarySources == True:
+ if self.loadedLibrarySources is True:
# The List has already been populated, return it
return self.loadedLibrarySources
elif self.loadedLibrarySources == "Loading":
# The list is currently being populated, wait and then return it
- count = 0
- while count < 20:
+ for i in ( 0, 20 ):
xbmc.sleep( 100 )
- count += 1
- if self.loadedLibrarySources == True:
+ if self.loadedLibrarySources is True:
return self.loadedLibrarySources
else:
# We're going to populate the list
@@ -1047,16 +1095,14 @@ def librarysources( self ):
return self.loadedLibrarySources
def playlists( self ):
- if self.loadedPlaylists == True:
+ if self.loadedPlaylists is True:
# The List has already been populated, return it
return self.loadedPlaylists
elif self.loadedPlaylists == "Loading":
# The list is currently being populated, wait and then return it
- count = 0
- while count < 20:
+ for i in ( 0, 20 ):
xbmc.sleep( 100 )
- count += 1
- if self.loadedPlaylists == True:
+ if self.loadedPlaylists is True:
return self.loadedPlaylists
else:
# We're going to populate the list
@@ -1080,13 +1126,16 @@ def playlists( self ):
contents = xbmcvfs.File(playlistfile, 'r')
contents_data = contents.read().decode('utf-8')
xmldata = xmltree.fromstring(contents_data.encode('utf-8'))
+ mediaType = "unknown"
for line in xmldata.getiterator():
if line.tag == "smartplaylist":
mediaType = line.attrib['type']
if mediaType == "movies" or mediaType == "tvshows" or mediaType == "seasons" or mediaType == "episodes" or mediaType == "musicvideos" or mediaType == "sets":
mediaLibrary = "VideoLibrary"
+ mediaContent = "video"
elif mediaType == "albums" or mediaType == "artists" or mediaType == "songs":
- mediaLibrary = "MusicLibrary"
+ mediaLibrary = "MusicLibrary"
+ mediaContent = "music"
if line.tag == "name" and mediaLibrary is not None:
name = line.text
@@ -1096,6 +1145,13 @@ def playlists( self ):
listitem = self._create(["::PLAYLIST::", name, path[1], {"icon": "DefaultPlaylist.png"} ])
listitem.setProperty( "action-play", "PlayMedia(" + playlist + ")" )
listitem.setProperty( "action-show", "ActivateWindow(" + mediaLibrary + "," + playlist + ",return)".encode( 'utf-8' ) )
+
+ # Add widget information
+ listitem.setProperty( "widget", "Playlist" )
+ listitem.setProperty( "widgetType", mediaType )
+ listitem.setProperty( "widgetTarget", mediaContent )
+ listitem.setProperty( "widgetName", name )
+ listitem.setProperty( "widgetPath", playlist )
if mediaLibrary == "VideoLibrary":
videolist.append( listitem )
@@ -1106,6 +1162,7 @@ def playlists( self ):
count += 1
break
+
elif playlist.endswith( '.m3u' ):
name = label
listitem = self._create( ["::PLAYLIST::", name, "32005", {"icon": "DefaultPlaylist.png"} ] )
@@ -1165,16 +1222,14 @@ def scriptPlaylists( self ):
return returnPlaylists
def favourites( self ):
- if self.loadedFavourites == True:
+ if self.loadedFavourites is True:
# The List has already been populated, return it
return self.loadedFavourites
elif self.loadedFavourites == "Loading":
# The list is currently being populated, wait and then return it
- count = 0
- while count < 20:
+ for i in ( 0, 20 ):
xbmc.sleep( 100 )
- count += 1
- if self.loadedFavourites == True:
+ if self.loadedFavourites is True:
return self.loadedFavourites
else:
# We're going to populate the list
@@ -1223,16 +1278,14 @@ def favourites( self ):
return self.loadedFavourites
def addons( self ):
- if self.loadedAddOns == True:
+ if self.loadedAddOns is True:
# The List has already been populated, return it
return self.loadedAddOns
elif self.loadedAddOns == "Loading":
# The list is currently being populated, wait and then return it
- count = 0
- while count < 20:
+ for i in ( 0, 20 ):
xbmc.sleep( 100 )
- count += 1
- if self.loadedAddOns == True:
+ if self.loadedAddOns is True:
return self.loadedAddOns
else:
# We're going to populate the list
@@ -1240,10 +1293,15 @@ def addons( self ):
try:
log( 'Loading add-ons' )
+
+ executableItems = {}
+ videoItems = {}
+ audioItems = {}
+ imageItems = {}
- contenttypes = ["executable", "video", "audio", "image"]
- for contenttype in contenttypes:
- listitems = {}
+ contenttypes = [ ( "executable", executableItems ), ( "video", videoItems ), ( "audio", audioItems ), ( "image", imageItems ) ]
+ for contenttype, listitems in contenttypes:
+ #listitems = {}
if contenttype == "executable":
contentlabel = __language__(32009)
shortcutType = "::SCRIPT::32009"
@@ -1266,11 +1324,6 @@ def addons( self ):
if item['enabled'] == True:
path = "RunAddOn(" + item['addonid'].encode('utf-8') + ")"
action = None
-
- # If this is a plugin, mark that we can browse it
- if item['addonid'].startswith( "plugin." ):
- path = "||BROWSE||" + item['addonid'].encode('utf-8')
- action = "RunAddOn(" + item['addonid'].encode('utf-8') + ")"
thumb = "DefaultAddon.png"
if item['thumbnail'] != "":
@@ -1279,12 +1332,32 @@ def addons( self ):
thumb = None
listitem = self._create([path, item['name'], shortcutType, {"icon": "DefaultAddon.png", "thumb": thumb} ])
+
+ # If this is a plugin, mark that we can browse it
+ if item[ "type" ] == "xbmc.python.pluginsource":
+ path = "||BROWSE||" + item['addonid'].encode('utf-8')
+ action = "RunAddOn(" + item['addonid'].encode('utf-8') + ")"
+ elif contenttype == "executable":
+ # Check if it's a program that can be run as an exectuble
+ provides = self.hasPluginEntryPoint( item[ "path" ] )
+ if provides is None:
+ continue
+
+ for content in provides:
+ # For each content that it provides, add it to the add-ons for that type
+ contentData = { "video": [ "::SCRIPT::32010", videoItems ], "audio": [ "::SCRIPT::32011", audioItems ], "image": [ "::SCRIPT::32012", imageItems ], "executable": [ "::SCRIPT::32009", executableItems ] }
+ if content in contentData:
+ otherItem = self._create([path, item['name'] + " >", contentData[ content ][ 0 ], {"icon": "DefaultAddon.png", "thumb": thumb} ])
+ otherItem.setProperty( "path", "||BROWSE||" + item['addonid'].encode('utf-8') )
+ otherItem.setProperty( "action", "RunAddOn(" + item['addonid'].encode('utf-8') + ")" )
+ contentData[ content ][ 1 ][ item[ "name" ] ] = otherItem
+
if action is not None:
listitem.setProperty( "path", path )
listitem.setProperty( "action", action )
+ listitem.setLabel( listitem.getLabel() + " >" )
listitems[ item[ "name" ] ] = listitem
- #listitems.append(listitem)
if contenttype == "executable":
self.addToDictionary( "addon-program", self.sortDictionary( listitems ) )
@@ -1305,6 +1378,117 @@ def addons( self ):
self.loadedAddOns = True
return self.loadedAddOns
+
+ def hasPluginEntryPoint( self, path ):
+ # Check if an addon has a plugin entry point by parsing its addon.xml file
+ try:
+ tree = xmltree.parse( os.path.join( path, "addon.xml" ) ).getroot()
+ for extension in tree.findall( "extension" ):
+ if "point" in extension.attrib and extension.attrib.get( "point" ) == "xbmc.python.pluginsource":
+ # Find out what content type it provides
+ provides = extension.find( "provides" )
+ if provides is None:
+ return None
+ return provides.text.split( " " )
+
+
+ except:
+ return None
+ return None
+
+ def detectPluginContent(self, item):
+ #based on the properties in the listitem we try to detect the content
+
+ if not item.has_key("showtitle") and not item.has_key("artist"):
+ #these properties are only returned in the json response if we're looking at actual file content...
+ # if it's missing it means this is a main directory listing and no need to scan the underlying listitems.
+ return None
+
+ if not item.has_key("showtitle") and item.has_key("artist"):
+ ##### AUDIO ITEMS ####
+ if item["artist"][0] == item["title"]:
+ return "artists"
+ elif item["album"] == item["title"]:
+ return "albums"
+ elif (item["type"] == "song" or (item["artist"] and item["album"])):
+ return "songs"
+ else:
+ ##### VIDEO ITEMS ####
+ if (item["showtitle"] and not item["artist"]):
+ #this is a tvshow, episode or season...
+ if (item["season"] > -1 and item["episode"] == -1):
+ return "seasons"
+ elif item["season"] > -1 and item["episode"] > -1:
+ return "episodes"
+ else:
+ return "tvshows"
+ elif (item["artist"]):
+ #this is a musicvideo!
+ return "musicvideos"
+ elif (item["imdbnumber"] or item["mpaa"] or item["trailer"] or item["studio"]):
+ return "movies"
+
+ return None
+
+ def widgets( self ):
+ if self.loadedWidgets is True:
+ # The list has already been populated
+ return self.loadedWidgets
+ elif self.loadedWidgets == "Loading":
+ # The list is currently being populated, wait and then return it
+ for i in range( 0, 20 ):
+ xbmc.sleep( 100 )
+ count += 1
+ if self.loadedWidgets is True:
+ return self.loadedWidgets
+ else:
+ # We're going to populat the list
+ self.loadedWidgets = "Loading"
+
+ # Load skin overrides
+ tree = DATA._get_overrides_skin()
+
+ # Get widgets
+ listitems = []
+ log( "Loading skin widgets" )
+ if tree is not None:
+ elems = tree.getroot().findall( "widget" )
+ for elem in elems:
+ widgetType = None
+ widgetPath = None
+ widgetTarget = None
+ widgetIcon = ""
+ if "type" in elem.attrib:
+ widgetType = elem.attrib.get( "type" )
+ if "condition" in elem.attrib:
+ if not xbmc.getCondVisibility( elem.attrib.get( "condition" ) ):
+ continue
+ if "path" in elem.attrib:
+ widgetPath = elem.attrib.get( "path" )
+ if "target" in elem.attrib:
+ widgetTarget = elem.attrib.get( "target" )
+ if "icon" in elem.attrib:
+ widgetIcon = elem.attrib.get( "icon" )
+
+ # Save widget for button 309
+ self.dictionaryGroupings[ "widgets-classic" ].append( [elem.text, DATA.local( elem.attrib.get( 'label' ) )[2], widgetType, widgetPath, widgetIcon, widgetTarget ] )
+
+ # Save widgets for button 312
+ listitem = self._create( [ elem.text, DATA.local( elem.attrib.get( 'label' ) )[2], "::SCRIPT::32099", {"icon": widgetIcon } ] )
+ listitem.setProperty( "widget", elem.text )
+ listitem.setProperty( "widgetName", DATA.local( elem.attrib.get( 'label' ) )[2] )
+ if widgetType is not None:
+ listitem.setProperty( "widgetType", widgetType )
+ if widgetPath is not None:
+ listitem.setProperty( "widgetPath", widgetPath )
+ if widgetTarget is not None:
+ listitem.setProperty( "widgetTarget", widgetTarget )
+ listitems.append( listitem )
+
+ self.addToDictionary( "widgets", listitems )
+
+ self.loadedWidgets = True
+ return self.loadedWidgets
def sortDictionary( self, dictionary ):
listitems = []
@@ -1316,52 +1500,91 @@ def sortDictionary( self, dictionary ):
# === ADDON/SOURCE EXPLORER ===
# =============================
- def explorer( self, history, location, label, thumbnail, itemType ):
+ def explorer( self, history, location, label, thumbnail, itemType, isWidget = False ):
+ isLibrary = False
+ widgetType = None
+ addonType = None
+
dialogLabel = label[0].replace( " >", "" )
+ if len( label ) != 1:
+ dialogLabel = label[0].replace( " >", "" ) + " - " + label[ len( label ) - 1 ].replace( " >", "" )
- # Default action - create shortcut
listings = []
tree = DATA._get_overrides_skin()
+
+ # Shortcut to go 'up'
+ if len( label ) == 1:
+ # This is the root, create a link to go back to selectShortcut
+ listitem = self._create( [ "::UP::", "..", "", {} ] )
+ else:
+ # This isn't the root, create a link to go up the heirachy
+ listitem = self._create( [ "::BACK::", "..", "", {} ] )
+ listings.append( listitem )
+
- listings.append( self._get_icon_overrides( tree, self._create( ["::CREATE::", "32058", "", {}] ), "" ) )
+ # Default action - create shortcut
+ createLabel = "32058"
+ if isWidget:
+ createLabel = "32100"
+ listings.append( self._get_icon_overrides( tree, self._create( ["::CREATE::", createLabel, "", {}] ), "" ) )
- # If this isn't the root, create a link to go up the heirachy
- if len( label ) is not 1:
- # listitem = xbmcgui.ListItem( label=".." )
- # listitem.setProperty( "path", "||BACK||" )
- # listings.append( listitem )
- #
- dialogLabel = label[0].replace( " >", "" ) + " - " + label[ len( label ) - 1 ].replace( " >", "" )
+ log( "Getting %s - %s" %( dialogLabel, location ) )
# Show a waiting dialog, then get the listings for the directory
dialog = xbmcgui.DialogProgress()
- dialog.create( dialogLabel, __language__( 32063) )
-
- json_query = xbmc.executeJSONRPC('{ "jsonrpc": "2.0", "id": 0, "method": "Files.GetDirectory", "params": { "properties": ["title", "file", "thumbnail"], "directory": "' + location + '", "media": "files" } }')
+ dialog.create( dialogLabel, __language__( 32063 ) )
+
+ #we retrieve a whole bunch of properties, needed to guess the content type properly
+ 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"], "directory": "' + location + '", "media": "files" } }')
json_query = unicode(json_query, 'utf-8', errors='ignore')
json_response = simplejson.loads(json_query)
-
- dialog.close()
# Add all directories returned by the json query
- if json_response.has_key('result') and json_response['result'].has_key('files') and json_response['result']['files'] is not None:
- for item in json_response['result']['files']:
+ if json_response.has_key('result') and json_response['result'].has_key('files') and json_response['result']['files']:
+ json_result = json_response['result']['files']
+
+ for item in json_result:
# Handle numeric labels
altLabel = item[ "label" ]
if item[ "label" ].isnumeric():
altLabel = "$NUMBER[" + item[ "label" ] + "]"
if location.startswith( "library://" ):
- # Process this as a video node
+ # Process this as a library node
+ isLibrary = True
+ if widgetType is None:
+ widgetType = NODE.get_mediaType( location )
+
+ if itemType == "32014":
+ # Video node
+ windowID = "10025"
+ if widgetType == "unknown":
+ widgetType = "video"
+ widgetTarget = "video"
+ else:
+ # Audio node
+ windowID = "10502"
+ if widgetType == "unknown":
+ widgetType = "audio"
+ widgetTarget = "music"
+
if item[ "filetype" ] == "directory":
thumb = None
if item[ "thumbnail" ] is not "":
thumb = item[ "thumbnail" ]
- listitem = self._create( [ "ActivateWindow(10025,%s,return)" %( item[ "file" ] ), altLabel, "", {"icon": "DefaultFolder.png", "thumb": thumb} ] )
+ listitem = self._create( [ "ActivateWindow(%s,%s,return)" %( windowID, item[ "file" ] ), altLabel, "", {"icon": "DefaultFolder.png", "thumb": thumb} ] )
if item[ "file" ].endswith( ".xml/" ) and NODE.isGrouped( item[ "file" ] ):
listitem = self._create( [ item[ "file" ], "%s >" %( item[ "label" ] ), "", {"icon": "DefaultFolder.png", "thumb": thumb} ] )
+
+ # Add widget properties
+ widgetName = label[0].replace( " >", "" ) + " - " + item[ "label" ]
+ listitem.setProperty( "widget", "Library" )
+ listitem.setProperty( "widgetName", widgetName )
+ listitem.setProperty( "widgetType", widgetType )
+ listitem.setProperty( "widgetTarget", widgetTarget )
+ listitem.setProperty( "widgetPath", item[ "file" ] )
listings.append( self._get_icon_overrides( tree, listitem, "" ) )
else:
@@ -1372,8 +1595,19 @@ def explorer( self, history, location, label, thumbnail, itemType ):
thumb = item[ "thumbnail" ]
listitem = self._create( [item[ "file" ], item[ "label" ] + " >", "", {"icon": "DefaultFolder.png", "thumb": thumb} ] )
listings.append( self._get_icon_overrides( tree, listitem, "" ) )
-
- # Show dialog
+ else:
+ contentType = self.detectPluginContent( item )
+ if contentType is not None:
+ if addonType is not None:
+ addonType = contentType
+ else:
+ if addonType != contentType and addonType != "mixed":
+ addonType = "mixed"
+
+ # Close progress dialog
+ dialog.close()
+
+ # Show select dialog
w = ShowDialog( "DialogSelect.xml", __cwd__, listing=listings, windowtitle=dialogLabel )
w.doModal()
selectedItem = w.result
@@ -1381,6 +1615,12 @@ def explorer( self, history, location, label, thumbnail, itemType ):
if selectedItem != -1:
selectedAction = listings[ selectedItem ].getProperty( "path" )
+ if selectedAction == "::UP::":
+ # User wants to go out of explorer, back to selectShortcut
+ listitem = xbmcgui.ListItem( label="back" )
+ listitem.setProperty( "path", "::UP::" )
+
+ return listitem
if selectedAction == "::CREATE::":
# User has chosen the shortcut they want
@@ -1394,12 +1634,54 @@ def explorer( self, history, location, label, thumbnail, itemType ):
if itemType == "32010" or itemType == "32014" or itemType == "32069":
action = 'ActivateWindow(10025,"' + location + '",return)'
listitem.setProperty( "windowID", "10025" )
+ listitem.setProperty( "widgetType", "video" )
+
+ # Add widget details
+ if isLibrary:
+ listitem.setProperty( "widget", "Library" )
+ widgetType = NODE.get_mediaType( location )
+ if widgetType != "unknown":
+ listitem.setProperty( "widgetType", widgetType )
+ else:
+ listitem.setProperty( "widget", "Addon" )
+
+ if addonType is not None:
+ listitem.setProperty( "widgetType", addonType)
+
+ listitem.setProperty( "widgetTarget", "video" )
+ listitem.setProperty( "widgetName", dialogLabel )
+ listitem.setProperty( "widgetPath", location )
+
elif itemType == "32011" or itemType == "32019" or itemType == "32073":
action = 'ActivateWindow(10501,"' + location + '",return)'
listitem.setProperty( "windowID", "10501" )
+
+ # Add widget details
+ listitem.setProperty( "widgetType", "audio" )
+ if isLibrary:
+ listitem.setProperty( "widget", "Library" )
+ widgetType = NODE.get_mediaType( location )
+ if widgetType != "unknown":
+ listitem.setProperty( "widgetType", widgetType )
+ else:
+ listitem.setProperty( "widget", "Addon" )
+ if addonType is not None:
+ listitem.setProperty( "widgetType", addonType)
+
+ listitem.setProperty( "widgetTarget", "music" )
+ listitem.setProperty( "widgetName", dialogLabel )
+ listitem.setProperty( "widgetPath", location )
+
elif itemType == "32012" or itemType == "32089":
action = 'ActivateWindow(10002,"' + location + '",return)'
listitem.setProperty( "windowID", "10002" )
+
+ # Add widget details
+ listitem.setProperty( "widget", "Addon" )
+ listitem.setProperty( "widgetType", "picture" )
+ listitem.setProperty( "widgetTarget", "pictures" )
+ listitem.setProperty( "widgetName", dialogLabel )
+ listitem.setProperty( "widgetPath", location )
elif itemType == "32009":
action = 'ActivateWindow(10001,"' + location + '",return)'
else:
@@ -1417,23 +1699,31 @@ def explorer( self, history, location, label, thumbnail, itemType ):
return listitem
- elif selectedAction == "||BACK||":
+ elif selectedAction == "::BACK::":
# User is going up the heirarchy, remove current level and re-call this function
history.pop()
label.pop()
thumbnail.pop()
- return self.explorer( history, history[ len( history ) -1 ], label, thumbnail, itemType )
+ return self.explorer( history, history[ len( history ) -1 ], label, thumbnail, itemType, isWidget = isWidget )
elif selectedAction.startswith( "ActivateWindow(" ):
# The user wants to create a shortcut to a specific shortcut listed
- return listings[ selectedItem ]
+ listitem = listings[ selectedItem ]
+
+ # Add widget details
+ if isLibrary:
+ widgetType = NODE.get_mediaType( listitem.getProperty( "widgetPath" ) )
+ if widgetType != "unknown":
+ listitem.setProperty( "widgetType", widgetType )
+
+ return listitem
else:
# User has chosen a sub-level to display, add details and re-call this function
history.append( selectedAction )
label.append( listings[ selectedItem ].getLabel() )
thumbnail.append( listings[ selectedItem ].getProperty( "thumbnail" ) )
- return self.explorer( history, selectedAction, label, thumbnail, itemType )
+ return self.explorer( history, selectedAction, label, thumbnail, itemType, isWidget = isWidget )
# ======================
# === AUTO-PLAYLISTS ===
@@ -1649,32 +1939,35 @@ def _rename_playlist( self, target, newLabel ):
# === COMMON SELECT SHORTCUT METHOD ===
# =====================================
- def selectShortcut( self, group = "", custom = False, availableShortcuts = None, windowTitle = None, showNone = False, currentAction = "" ):
+ def selectShortcut( self, group = "", custom = False, availableShortcuts = None, windowTitle = None, showNone = False, currentAction = "", grouping = None ):
# This function allows the user to select a shortcut
# If group is empty, start background loading of shortcuts
if group == "":
thread.start_new_thread( self.loadLibrary, () )
+
+ isWidget = False
+ if grouping == "widget":
+ isWidget = True
if availableShortcuts is None:
- nodes = self.retrieveGroup( group, False )
+ nodes = self.retrieveGroup( group, flat = False, grouping = grouping )
availableShortcuts = nodes[1]
windowTitle = nodes[0]
else:
availableShortcuts = self.checkForFolder( availableShortcuts )
- if showNone is not False:
+ if showNone is not False and group == "":
availableShortcuts.insert( 0, self._create(["::NONE::", __language__(32053), "", {"icon":"DefaultAddonNone.png"}] ) )
- if custom is not False:
+ if custom is not False and group == "":
availableShortcuts.append( self._create(["||CUSTOM||", __language__(32024), "", {}] ) )
-
- # Check a shortcut is available
- if len( availableShortcuts ) == 0:
- log( "No available shortcuts found" )
- xbmcgui.Dialog().ok( __language__(32064), __language__(32065) )
- return
-
+
+ if group != "":
+ # Add a link to go 'up'
+ availableShortcuts.insert( 0, self._create( ["::BACK::", "..", "", {}] ) )
+
+ # Show select dialog
w = ShowDialog( "DialogSelect.xml", __cwd__, listing=availableShortcuts, windowtitle=windowTitle )
w.doModal()
number = w.result
@@ -1683,51 +1976,75 @@ def selectShortcut( self, group = "", custom = False, availableShortcuts = None,
if number != -1:
selectedShortcut = availableShortcuts[ number ]
path = selectedShortcut.getProperty( "Path" )
+ if path.startswith( "::BACK::" ):
+ # Go back up
+ if "," in group:
+ # Remove last level from group
+ newGroup = group.rsplit( ",", 1 )[ 0 ]
+ else:
+ # We're only one level in, so we'll just clear the group
+ newGroup = ""
+ # Recall this function
+ return self.selectShortcut( group = newGroup, grouping = grouping, custom = custom, showNone = showNone, currentAction = currentAction )
if path.startswith( "||NODE||" ):
if group == "":
group = path.replace( "||NODE||", "" )
else:
group = group + "," + path.replace( "||NODE||", "" )
- return self.selectShortcut( group = group )
+ return self.selectShortcut( group = group, grouping = grouping, custom = custom, showNone = showNone, currentAction = currentAction )
elif path.startswith( "||BROWSE||" ):
- selectedShortcut = self.explorer( ["plugin://" + path.replace( "||BROWSE||", "" )], "plugin://" + path.replace( "||BROWSE||", "" ), [selectedShortcut.getLabel()], [selectedShortcut.getProperty("thumbnail")], selectedShortcut.getProperty("shortcutType") )
+ selectedShortcut = self.explorer( ["plugin://" + path.replace( "||BROWSE||", "" )], "plugin://" + path.replace( "||BROWSE||", "" ), [selectedShortcut.getLabel()], [selectedShortcut.getProperty("thumbnail")], selectedShortcut.getProperty("shortcutType"), isWidget = isWidget )
# Convert backslashes to double-backslashes (windows fix)
if selectedShortcut is not None:
newAction = selectedShortcut.getProperty( "Path" )
newAction = newAction.replace( "\\", "\\\\" )
selectedShortcut.setProperty( "Path", newAction )
selectedShortcut.setProperty( "displayPath", newAction )
- elif path.startswith( "||FOLDER||" ):
- selectedShortcut = self.explorer( [ path.replace( "||FOLDER||", "" )], path.replace( "||FOLDER||", "" ), [selectedShortcut.getLabel()], [selectedShortcut.getProperty("thumbnail")], "32014" )
+ elif path.startswith( "||VIDEO||" ):
+ # Video node
+ selectedShortcut = self.explorer( [ path.replace( "||VIDEO||", "" )], path.replace( "||VIDEO||", "" ), [selectedShortcut.getLabel()], [selectedShortcut.getProperty("thumbnail")], "32014", isWidget = isWidget )
+ # Convert backslashes to double-backslashes (windows fix)
+ if selectedShortcut is not None:
+ newAction = selectedShortcut.getProperty( "Path" )
+ newAction = newAction.replace( "\\", "\\\\" )
+ selectedShortcut.setProperty( "Path", newAction )
+ selectedShortcut.setProperty( "displayPath", newAction )
+ elif path.startswith( "||AUDIO||" ):
+ # Audio node
+ selectedShortcut = self.explorer( [ path.replace( "||AUDIO||", "" )], path.replace( "||AUDIO||", "" ), [selectedShortcut.getLabel()], [selectedShortcut.getProperty("thumbnail")], "32019", isWidget = isWidget )
# Convert backslashes to double-backslashes (windows fix)
if selectedShortcut is not None:
newAction = selectedShortcut.getProperty( "Path" )
newAction = newAction.replace( "\\", "\\\\" )
selectedShortcut.setProperty( "Path", newAction )
selectedShortcut.setProperty( "displayPath", newAction )
-
- # The next set of shortcuts are within the listitem property folder-contents
- #shortcuts = self.folders[ selectedShortcut.getProperty( "folder" ) ]
- #return self.selectShortcut( group=group, availableShortcuts=shortcuts, windowTitle = selectedShortcut.getLabel() )
elif path == "||UPNP||":
- selectedShortcut = self.explorer( ["upnp://"], "upnp://", [selectedShortcut.getLabel()], [selectedShortcut.getProperty("thumbnail")], selectedShortcut.getProperty("shortcutType") )
+ selectedShortcut = self.explorer( ["upnp://"], "upnp://", [selectedShortcut.getLabel()], [selectedShortcut.getProperty("thumbnail")], selectedShortcut.getProperty("shortcutType"), isWidget = isWidget )
path = selectedShortcut.getProperty( "Path" )
elif path.startswith( "||SOURCE||" ):
- selectedShortcut = self.explorer( [path.replace( "||SOURCE||", "" )], path.replace( "||SOURCE||", "" ), [selectedShortcut.getLabel()], [selectedShortcut.getProperty("thumbnail")], selectedShortcut.getProperty("shortcutType") )
+ selectedShortcut = self.explorer( [path.replace( "||SOURCE||", "" )], path.replace( "||SOURCE||", "" ), [selectedShortcut.getLabel()], [selectedShortcut.getProperty("thumbnail")], selectedShortcut.getProperty("shortcutType"), isWidget = isWidget )
if selectedShortcut is None or "upnp://" in selectedShortcut.getProperty( "Path" ):
return selectedShortcut
- selectedShortcut = self._sourcelink_choice( selectedShortcut )
- #path = urllib.unquote( selectedShortcut.getProperty( "Path" ) )
+ if isWidget:
+ # Set widget to 'source'
+ selectedShortcut.setProperty( "widget", "source" )
+ else:
+ # Find out what the user wants to do with the source
+ selectedShortcut = self._sourcelink_choice( selectedShortcut )
elif path == "::PLAYLIST::" :
- # Give the user the choice of playing or displaying the playlist
- dialog = xbmcgui.Dialog()
- userchoice = dialog.yesno( __language__( 32040 ), __language__( 32060 ), "", "", __language__( 32061 ), __language__( 32062 ) )
- # False: Display
- # True: Play
- if userchoice == False:
+ if isWidget:
+ # Return actionShow as chosenPath
selectedShortcut.setProperty( "chosenPath", selectedShortcut.getProperty( "action-show" ) )
else:
- selectedShortcut.setProperty( "chosenPath", selectedShortcut.getProperty( "action-play" ) )
+ # Give the user the choice of playing or displaying the playlist
+ dialog = xbmcgui.Dialog()
+ userchoice = dialog.yesno( __language__( 32040 ), __language__( 32060 ), "", "", __language__( 32061 ), __language__( 32062 ) )
+ # False: Display
+ # True: Play
+ if userchoice is False:
+ selectedShortcut.setProperty( "chosenPath", selectedShortcut.getProperty( "action-show" ) )
+ else:
+ selectedShortcut.setProperty( "chosenPath", selectedShortcut.getProperty( "action-play" ) )
elif path == "||CUSTOM||":
# Let the user type a command
@@ -1750,6 +2067,10 @@ def selectShortcut( self, group = "", custom = False, availableShortcuts = None,
# Create a really simple listitem to return
selectedShortcut = xbmcgui.ListItem( "::NONE::" )
+ # Check that explorer hasn't sent us back here
+ if selectedShortcut is not None and selectedShortcut.getProperty( "path" ) == "::UP::":
+ return self.selectShortcut( group = group, custom = custom, availableShortcuts = None, windowTitle = windowTitle, showNone = showNone, grouping = grouping, currentAction = currentAction )
+
return selectedShortcut
else:
return None
diff --git a/resources/lib/nodefunctions.py b/resources/lib/nodefunctions.py
index b2efdf42..af26767d 100755
--- a/resources/lib/nodefunctions.py
+++ b/resources/lib/nodefunctions.py
@@ -87,8 +87,8 @@ def parse_view( self, file, nodes, isFolder = False, origFolder = None, origPath
root = tree.getroot()
# Get the item index
- if "order" in tree.getroot().attrib:
- index = tree.getroot().attrib.get( "order" )
+ if "order" in root.attrib:
+ index = root.attrib.get( "order" )
origIndex = index
while int( index ) in nodes:
index = int( index )
@@ -98,7 +98,22 @@ def parse_view( self, file, nodes, isFolder = False, origFolder = None, origPath
self.indexCounter -= 1
index = str( self.indexCounter )
origIndex = "-"
-
+
+ # Try to get media type from visibility condition
+ mediaType = None
+ if "visible" in root.attrib:
+ visibleAttrib = root.attrib.get( "visible" )
+ if not xbmc.getCondVisibility( visibleAttrib ):
+ # The node isn't visible
+ return
+ if "Library.HasContent(" in visibleAttrib and "+" not in visibleAttrib and "|" not in visibleAttrib:
+ mediaType = visibleAttrib.split( "(" )[ 1 ].split( ")" )[ 0 ].lower()
+
+ # Try to get media type from content node
+ contentNode = root.find( "content" )
+ if contentNode is not None:
+ mediaType = contentNode.text
+
# Get label and icon
label = root.find( "label" ).text
@@ -107,10 +122,10 @@ def parse_view( self, file, nodes, isFolder = False, origFolder = None, origPath
icon = icon.text
else:
icon = ""
-
+
if isFolder:
# Add it to our list of nodes
- nodes[ int( index ) ] = [ label, icon, origFolder.decode( "utf-8" ), "folder", origIndex ]
+ nodes[ int( index ) ] = [ label, icon, origFolder.decode( "utf-8" ), "folder", origIndex, mediaType ]
else:
# Check for a path
path = root.find( "path" )
@@ -122,10 +137,10 @@ def parse_view( self, file, nodes, isFolder = False, origFolder = None, origPath
group = root.find( "group" )
if group is None:
# Add it as an item
- nodes[ int( index ) ] = [ label, icon, origPath, "item", origIndex ]
+ nodes[ int( index ) ] = [ label, icon, origPath, "item", origIndex, mediaType ]
else:
# Add it as grouped
- nodes[ int( index ) ] = [ label, icon, origPath, "grouped", origIndex ]
+ nodes[ int( index ) ] = [ label, icon, origPath, "grouped", origIndex, mediaType ]
except:
print_exc()
@@ -167,16 +182,25 @@ def isGrouped( self, path ):
def get_visibility( self, path ):
path = path.replace( "videodb://", "library://video/" )
path = path.replace( "musicdb://", "library://music/" )
-
- customPath = path.replace( "library://video", os.path.join( xbmc.translatePath( "special://profile".decode('utf-8') ), "library", "video" ) ) + "index.xml"
- customPath = path.replace( "library://music", os.path.join( xbmc.translatePath( "special://profile".decode('utf-8') ), "library", "music" ) ) + "index.xml"
- customFile = path.replace( "library://video", os.path.join( xbmc.translatePath( "special://profile".decode('utf-8') ), "library", "video" ) )[:-1] + ".xml"
- customFile = path.replace( "library://music", os.path.join( xbmc.translatePath( "special://profile".decode('utf-8') ), "library", "music" ) )[:-1] + ".xml"
- defaultPath = path.replace( "library://video", os.path.join( xbmc.translatePath( "special://xbmc".decode('utf-8') ), "system", "library", "video" ) ) + "index.xml"
- defaultPath = path.replace( "library://music", os.path.join( xbmc.translatePath( "special://xbmc".decode('utf-8') ), "system", "library", "music" ) ) + "index.xml"
- defaultFile = path.replace( "library://video", os.path.join( xbmc.translatePath( "special://xbmc".decode('utf-8') ), "system", "library", "video" ) )[:-1] + ".xml"
- defaultFile = path.replace( "library://music", os.path.join( xbmc.translatePath( "special://xbmc".decode('utf-8') ), "system", "library", "music" ) )[:-1] + ".xml"
-
+ if path.endswith( ".xml" ):
+ path = path[ :-3 ]
+ if path.endswith( ".xml/" ):
+ path = path[ :-4 ]
+
+ if "library://video" in path:
+ pathStart = "library://video"
+ pathEnd = "video"
+ elif "library://music" in path:
+ pathStart = "library://music"
+ pathEnd = "music"
+ else:
+ return ""
+
+ customPath = path.replace( pathStart, os.path.join( xbmc.translatePath( "special://profile".decode('utf-8') ), "library", pathEnd ) ) + "index.xml"
+ customFile = path.replace( pathStart, os.path.join( xbmc.translatePath( "special://profile".decode('utf-8') ), "library", pathEnd ) )[:-1] + ".xml"
+ defaultPath = path.replace( pathStart, os.path.join( xbmc.translatePath( "special://xbmc".decode('utf-8') ), "system", "library", pathEnd ) ) + "index.xml"
+ defaultFile = path.replace( pathStart, os.path.join( xbmc.translatePath( "special://xbmc".decode('utf-8') ), "system", "library", pathEnd ) )[:-1] + ".xml"
+
# Check whether the node exists - either as a parent node (with an index.xml) or a view node (append .xml)
# in first custom video nodes, then default video nodes
if xbmcvfs.exists( customPath ):
@@ -202,6 +226,62 @@ def get_visibility( self, path ):
return ""
except:
return False
+
+ def get_mediaType( self, path ):
+ path = path.replace( "videodb://", "library://video/" )
+ path = path.replace( "musicdb://", "library://music/" )
+ if path.endswith( ".xml" ):
+ path = path[ :-3 ]
+ if path.endswith( ".xml/" ):
+ path = path[ :-4 ]
+
+ if "library://video" in path:
+ pathStart = "library://video"
+ pathEnd = "video"
+ elif "library://music" in path:
+ pathStart = "library://music"
+ pathEnd = "music"
+ else:
+ return "unknown"
+
+ customPath = path.replace( pathStart, os.path.join( xbmc.translatePath( "special://profile".decode('utf-8') ), "library", pathEnd ) ) + "index.xml"
+ customFile = path.replace( pathStart, os.path.join( xbmc.translatePath( "special://profile".decode('utf-8') ), "library", pathEnd ) )[:-1] + ".xml"
+ defaultPath = path.replace( pathStart, os.path.join( xbmc.translatePath( "special://xbmc".decode('utf-8') ), "system", "library", pathEnd ) ) + "index.xml"
+ defaultFile = path.replace( pathStart, os.path.join( xbmc.translatePath( "special://xbmc".decode('utf-8') ), "system", "library", pathEnd ) )[:-1] + ".xml"
+
+ # Check whether the node exists - either as a parent node (with an index.xml) or a view node (append .xml)
+ # in first custom video nodes, then default video nodes
+ if xbmcvfs.exists( customPath ):
+ path = customPath
+ elif xbmcvfs.exists( customFile ):
+ path = customFile
+ elif xbmcvfs.exists( defaultPath ):
+ path = defaultPath
+ elif xbmcvfs.exists( defaultFile ):
+ path = defaultFile
+ else:
+ return "unknown"
+
+ # Open the file
+ try:
+ # Load the xml file
+ tree = xmltree.parse( path )
+ root = tree.getroot()
+
+ mediaType = "unknown"
+ if "visible" in root.attrib:
+ visibleAttrib = root.attrib.get( "visible" )
+ if "Library.HasContent(" in visibleAttrib and "+" not in visibleAttrib and "|" not in visibleAttrib:
+ mediaType = visibleAttrib.split( "(" )[ 1 ].split( ")" )[ 0 ].lower()
+
+ contentNode = root.find( "content" )
+ if contentNode is not None:
+ mediaType = contentNode.text
+
+ return mediaType
+
+ except:
+ return "unknown"
############################################
# Functions used to add a node to the menu #
diff --git a/resources/lib/template.py b/resources/lib/template.py
index 6070dcb8..bcb591cd 100755
--- a/resources/lib/template.py
+++ b/resources/lib/template.py
@@ -27,6 +27,8 @@ def __init__( self ):
templatepath = os.path.join( __skinpath__ , "template.xml" )
try:
self.tree = xmltree.parse( templatepath )
+
+ log( "Loaded template.xml file")
# Add the template.xml to the hash file
self._save_hash( templatepath, xbmcvfs.File( templatepath ).read() )
@@ -62,27 +64,28 @@ def parseItems( self, menuType, level, items, profile, profileVisibility, visibi
else:
template = self.findSubmenu( menuName, level )
- if template is None:
- # There is no template for this
- return
-
- log( " - Template found" )
-
- # We need to check that the relevant includes existing
- # First, the overarching include
- includeName = "skinshortcuts-template"
- if "include" in template.attrib:
- includeName += "-%s" %( template.attrib.get( "include" ) )
-
- treeRoot = self.getInclude( self.includes, includeName, profileVisibility, profile )
- includeTree = self.getInclude( self.includes, includeName + "-%s" %( profile ), None, None )
-
- # Now replace all elements with correct data
- self.replaceElements( template, visibilityCondition, profileVisibility, items )
+ if template is not None:
+ # Found a template - let's build it
+ if menuType == "mainmenu":
+ log( "Main menu template found" )
+ else:
+ log( " - Submenu template found" )
- # Add the template to the includes
- for child in template.find( "controls" ):
- includeTree.append( child )
+ # We need to check that the relevant includes existing
+ # First, the overarching include
+ includeName = "skinshortcuts-template"
+ if "include" in template.attrib:
+ includeName += "-%s" %( template.attrib.get( "include" ) )
+
+ treeRoot = self.getInclude( self.includes, includeName, profileVisibility, profile )
+ includeTree = self.getInclude( self.includes, includeName + "-%s" %( profile ), None, None )
+
+ # Now replace all elements with correct data
+ self.replaceElements( template, visibilityCondition, profileVisibility, items )
+
+ # Add the template to the includes
+ for child in template.find( "controls" ):
+ includeTree.append( child )
# Now we want to see if any of the main menu items match a template
if menuType != "mainmenu":
@@ -100,7 +103,7 @@ def parseItems( self, menuType, level, items, profile, profileVisibility, visibi
# Now find a matching template - if one matches, it will be saved to be processed
# at the end (when we have all visibility conditions)
- template = self.findOther( item, profile, profileVisibility, visibilityCondition )
+ self.findOther( item, profile, profileVisibility, visibilityCondition )
def writeOthers( self ):
# This will write any 'other' elements we have into the includes file
@@ -203,20 +206,47 @@ def findSubmenu( self, name, level ):
def findOther( self, item, profile, profileVisibility, visibilityCondition ):
# Find a template matching the item we have been passed
+ foundTemplateIncludes = []
for elem in self.tree.findall( "other" ):
+ # Check that we don't already have a template for this include
+ includeName = None
+ if "include" in elem.attrib:
+ includeName = elem.attrib.get( "include" )
+ if includeName in foundTemplateIncludes:
+ continue
+
template = copy.deepcopy( elem )
- match = True
+ matched = True
+
+ # Check whether the skinner has set the match type (whether all conditions need to match, or any)
+ matchType = "all"
+ matchElem = template.find( "match" )
+ if matchElem is not None:
+ matchType = matchElem.text.lower()
+ if matchType not in [ "any", "all" ]:
+ log( "Invalid element in template" )
+ matchType = "all"
+ elif matchType == "any":
+ matched = False
# Check the conditions
for condition in template.findall( "condition" ):
- if match == False:
- break
- if self.checkCondition( condition, item ) == False:
- match = False
- break
+ if matchType == "all":
+ if matched == False:
+ break
+ if self.checkCondition( condition, item ) == False:
+ matched = False
+ break
+ else:
+ if matched == True:
+ break
+ if self.checkCondition( condition, item ) == True:
+ matched = True
+ break
+
# If the conditions didn't match, we're done here
- if match == False:
+ if matched == False:
continue
# All the rules matched, so next we'll get any properties
@@ -229,7 +259,7 @@ def findOther( self, item, profile, profileVisibility, visibilityCondition ):
# Now we need to check if we've already got a template identical to this
textVersion = None
- match = False
+ foundInPrevious = False
for previous in self.finalize:
# If we haven't already, convert our new template to a string
if textVersion is None:
@@ -262,20 +292,25 @@ def findOther( self, item, profile, profileVisibility, visibilityCondition ):
xmltree.SubElement( newElement, "visible" ).text = visibilityCondition
# And we're done
- return previous
-
- # We don't have this template saved, so add our profile details to it
- newElement = xmltree.SubElement( template, "skinshortcuts-profile" )
- newElement.set( "profile", profile )
- newElement.set( "visible", profileVisibility )
-
- # Save the visibility condition
- xmltree.SubElement( newElement, "visible" ).text = visibilityCondition
-
- # Add it to our finalize list
- self.finalize.append( template )
-
- return template
+ foundTemplateIncludes.append( includeName )
+ foundInPrevious = True
+
+ if foundInPrevious == False:
+ # We don't have this template saved, so add our profile details to it
+ newElement = xmltree.SubElement( template, "skinshortcuts-profile" )
+ newElement.set( "profile", profile )
+ newElement.set( "visible", profileVisibility )
+
+ # Save the visibility condition
+ xmltree.SubElement( newElement, "visible" ).text = visibilityCondition
+
+ # Add it to our finalize list
+ self.finalize.append( template )
+
+ # Add that we've found a template for this include
+ foundTemplateIncludes.append( includeName )
+
+ log( " - Other template found" )
def checkCondition( self, condition, items ):
# Check if a particular condition is matched for an 'other' template
@@ -300,7 +335,7 @@ def checkCondition( self, condition, items ):
# This property doesn't match
continue
- if item.text != condition.text:
+ if condition.text is not None and item.text != condition.text:
# This property doesn't match
continue
@@ -314,22 +349,27 @@ def getProperties( self, elem, items ):
properties = {}
for property in elem.findall( "property" ):
value = None
- if "name" not in property.attrib or "tag" not in property.attrib or property.attrib.get( "name" ) in properties:
- # Name and tag both required, so pass on this
- # (or we've already got a property with this name)
+ if "name" not in property.attrib or property.attrib.get( "name" ) in properties:
+ # Name attrib required, or we've already got a property with this name
continue
- else:
- name = property.attrib.get( "name" )
+ name = property.attrib.get( "name" )
+ if "tag" in property.attrib:
tag = property.attrib.get( "tag" )
- if "attribute" in property.attrib and "value" not in property.attrib:
- # Attribute requires a value, so pass on this
+ else:
+ # No tag property, so this will always match (so let's just use it!)
+ if property.text:
+ properties[ name ] = property.text
+ else:
+ properties[ name ] = ""
continue
+
+ if "attribute" in property.attrib and "value" not in property.attrib:
+ attrib = property.attrib.get( "attribute" ).split( "|" )
else:
attrib = property.attrib.get( "attribute" ).split( "|" )
value = property.attrib.get( "value" )
# Let's get looking for any items that match
- matched = False
for item in items.findall( tag ):
if attrib is not None:
if attrib[ 0 ] not in item.attrib:
@@ -338,6 +378,10 @@ def getProperties( self, elem, items ):
if attrib[ 1 ] != item.attrib.get( attrib[ 0 ] ):
# The attributes value doesn't match
continue
+
+ if not item.text:
+ # The item doesn't have a value to match
+ continue
if value is not None and item.text != value:
# The value doesn't match
@@ -347,7 +391,7 @@ def getProperties( self, elem, items ):
if property.text:
properties[ name ] = property.text
else:
- properties[ name ] = elem.text
+ properties[ name ] = item.text
break
return properties
@@ -391,15 +435,29 @@ def replaceElements( self, tree, visibilityCondition, profileVisibility, items,
tree.insert( index, newElement )
# $skinshortcuts[var] -> [value]
- if elem.text is not None and elem.text.startswith( "$SKINSHORTCUTS[" ) and elem.text[ 15:-1 ] in properties:
- # Replace the text with the property value
- elem.text = properties[ elem.text[ 15:-1 ] ]
+ # $skinshortcuts[var] -> [includeName] (property = $INCLUDE[includeName])
+ if elem.text is not None and elem.text.startswith( "$SKINSHORTCUTS[" ):
+ newValue = ""
+ if elem.text[ 15:-1 ] in properties:
+ newValue = properties[ elem.text[ 15:-1 ] ]
+ if newValue.startswith( "$INCLUDE[" ):
+ # Remove text property
+ elem.text = ""
+ # Add include element
+ includeElement = xmltree.SubElement( elem, "include" )
+ includeElement.text = newValue[ 9:-1 ]
+ else:
+ # Replace the text with the property value
+ elem.text = newValue
# ->
for attrib in elem.attrib:
value = elem.attrib.get( attrib )
if value.startswith( "$SKINSHORTCUTS[" ) and value[ 15:-1 ] in properties:
- elem.set( attrib, properties[ value[ 15:-1 ] ] )
+ newValue = ""
+ if value[ 15:-1 ] in properties:
+ newValue = properties[ value[ 15:-1 ] ]
+ elem.set( attrib, newValue )
# visible -> [condition]
# items -> ...
diff --git a/resources/lib/xmlfunctions.py b/resources/lib/xmlfunctions.py
index 9311a7d7..9f6edc7f 100644
--- a/resources/lib/xmlfunctions.py
+++ b/resources/lib/xmlfunctions.py
@@ -4,6 +4,7 @@
import xml.etree.ElementTree as xmltree
from xml.sax.saxutils import escape as escapeXML
import copy
+import ast
from traceback import print_exc
from unicodeutils import try_decode
@@ -187,7 +188,7 @@ def shouldwerun( self, profilelist ):
try:
- hashes = eval( xbmcvfs.File( os.path.join( __masterpath__ , xbmc.getSkinDir() + ".hash" ) ).read() )
+ hashes = ast.literal_eval( xbmcvfs.File( os.path.join( __masterpath__ , xbmc.getSkinDir() + ".hash" ) ).read() )
except:
# There is no hash list, return True
log( "No hash list" )
@@ -236,12 +237,16 @@ def shouldwerun( self, profilelist ):
else:
xbmc.executebuiltin( "Skin.Reset(%s)" %( hash[ 1 ][ 1 ] ) )
else:
- hasher = hashlib.md5()
- hasher.update( xbmcvfs.File( hash[0] ).read() )
- if hasher.hexdigest() != hash[1]:
- log( "Hash does not match on file " + hash[0] )
- log( "(" + hash[1] + " > " + hasher.hexdigest() + ")" )
- return True
+ try:
+ hasher = hashlib.md5()
+ hasher.update( xbmcvfs.File( hash[0] ).read() )
+ if hasher.hexdigest() != hash[1]:
+ log( "Hash does not match on file " + hash[0] )
+ log( "(" + hash[1] + " > " + hasher.hexdigest() + ")" )
+ return True
+ except:
+ log( "Unable to generate hash for %s" %( hash[ 0 ] ) )
+ log( "(%s > ?)" %( hash[ 1 ] ) )
else:
if xbmcvfs.exists( hash[0] ):
log( "File now exists " + hash[0] )
@@ -310,6 +315,7 @@ def writexml( self, profilelist, mainmenuID, groups, numLevels, buildMode, progr
submenuNodes = {}
for profile in profilelist:
+ log( "Building menu for profile %s" %( profile[ 2 ] ) )
# Load profile details
profileDir = profile[0]
profileVis = profile[1]
@@ -732,7 +738,16 @@ def buildElement( self, item, groupName, visibilityCondition, profileVisibility,
if "clonebackgrounds" in options:
if property[0] == "background" or property[0] == "backgroundName" or property[0] == "backgroundPlaylist" or property[0] == "backgroundPlaylistName":
self.MAINBACKGROUND[ property[0] ] = property[1]
-
+
+ # For backwards compatibility, save widgetPlaylist as widgetPath too
+ if property[ 0 ] == "widgetPlaylist":
+ additionalproperty = xmltree.SubElement( newelement, "property" )
+ additionalproperty.set( "name", "widgetPath" )
+ try:
+ additionalproperty.text = DATA.local( property[1].decode( "utf-8" ) )[1]
+ except:
+ additionalproperty.text = DATA.local( property[1] )[1]
+
# If this isn't the main menu, and we're cloning widgets or backgrounds...
if groupName != "mainmenu":
if "clonewidgets" in options and len( self.MAINWIDGET ) is not 0:
diff --git a/resources/shortcuts/overrides.xml b/resources/shortcuts/overrides.xml
index c55f03f0..863fdaca 100755
--- a/resources/shortcuts/overrides.xml
+++ b/resources/shortcuts/overrides.xml
@@ -176,4 +176,50 @@
commands
-
\ No newline at end of file
+
+
+ widgets
+
+ video
+
+ playlist-video
+
+
+ addon-video
+
+
+ videosources
+
+
+
+ music
+
+ playlist-audio
+
+
+ addon-audio
+
+
+ musicsources
+
+
+
+ picturesources
+
+
+
+ addon-video
+
+
+ addon-audio
+
+
+ addon-image
+
+ addons://sources/executable
+ addons://sources/video
+ addons://sources/audio
+ addons://sources/image
+
+
+