The branch, frodo has been updated
       via  50a117274492d2b7e4dc4fa9156a0b482c6df508 (commit)
      from  8dacbefee34295973c204f1369dca7ba6231e32b (commit)

- Log -----------------------------------------------------------------
http://xbmc.git.sourceforge.net/git/gitweb.cgi?p=xbmc/scripts;a=commit;h=50a117274492d2b7e4dc4fa9156a0b482c6df508

commit 50a117274492d2b7e4dc4fa9156a0b482c6df508
Author: Martijn Kaijser <[email protected]>
Date:   Sun Aug 24 17:36:26 2014 +0200

    [script.sonos] 1.0.10

diff --git a/script.sonos/addon.xml b/script.sonos/addon.xml
index e9e4479..544ee44 100644
--- a/script.sonos/addon.xml
+++ b/script.sonos/addon.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<addon id="script.sonos" name="Sonos" version="1.0.9" 
provider-name="robwebset">
+<addon id="script.sonos" name="Sonos" version="1.0.10" 
provider-name="robwebset">
        <requires>
                <import addon="xbmc.python" version="2.1.0"/>
                <import addon="script.module.requests" version="1.1.0"/>
@@ -15,7 +15,6 @@
        <extension point="xbmc.python.pluginsource" library="plugin.py">
                <provides>audio</provides>
        </extension>
-       <extension point="xbmc.python.module" library="lib" />
        <extension point="xbmc.addon.metadata">
        
                <summary lang="de">Sonos Controller</summary>
diff --git a/script.sonos/changelog.txt b/script.sonos/changelog.txt
index 6322651..feab768 100644
--- a/script.sonos/changelog.txt
+++ b/script.sonos/changelog.txt
@@ -1,3 +1,6 @@
+v1.0.10
+- Highlight which speakers are group coordinators
+
 v1.0.9
 - Add option to Switch Sonos To Line-In On Media Start
 - Add check that Artist Slideshow has transparent image enabled
diff --git a/script.sonos/discovery.py b/script.sonos/discovery.py
index bf38ec6..ffc6ec8 100644
--- a/script.sonos/discovery.py
+++ b/script.sonos/discovery.py
@@ -73,7 +73,17 @@ if __name__ == '__main__':
                 displayName = "%s     [%s]" % (ip, zone_name)
             else:
                 log("SonosDiscovery: No zone for IP address %s" % ip)
-            speakers[displayName] = (ip, zone_name)
+            # Record if this is the group coordinator, as when there are 
several
+            # speakers in the group, we need to send messages to the group
+            # coordinator for things to work correctly
+            isCoordinator = device.is_coordinator
+            if isCoordinator:
+                log("SonosDiscovery: %s is the group coordinator" % ip)
+                displayName = "%s - %s" % (displayName, 
__addon__.getLocalizedString(32031))
+            else:
+                log("SonosDiscovery: %s is not the group coordinator" % ip)
+
+            speakers[displayName] = (ip, zone_name, isCoordinator)
 
     # Remove the busy dialog
     xbmc.executebuiltin("Dialog.Close(busydialog)")
@@ -90,6 +100,14 @@ if __name__ == '__main__':
             log("SonosDiscovery: Entry chosen = %s" % selectedDisplayName)
             chosenIPAddress = speakers.get(selectedDisplayName)[0]
             chosenZoneName = speakers.get(selectedDisplayName)[1]
+            chosenIsCoordinator = speakers.get(selectedDisplayName)[2]
+
+            # Warn the user if they have selected something that is not the 
zone coordinator
+            if not chosenIsCoordinator:
+                xbmcgui.Dialog().ok(__addon__.getLocalizedString(32001),
+                                    "%s %s:" % (chosenIPAddress, 
__addon__.getLocalizedString(32032)),
+                                    "          \"%s\"" % chosenZoneName,
+                                    __addon__.getLocalizedString(32033))
             # Set the selected item into the settings
             Settings.setIPAddress(chosenIPAddress)
             Settings.setZoneName(chosenZoneName)
diff --git a/script.sonos/resources/language/English/strings.po 
b/script.sonos/resources/language/English/strings.po
index d827079..345063c 100644
--- a/script.sonos/resources/language/English/strings.po
+++ b/script.sonos/resources/language/English/strings.po
@@ -144,7 +144,19 @@ msgctxt "#32030"
 msgid "Switch Sonos To Line-In On Media Start"
 msgstr ""
 
-#empty strings from id 32031 to 32059
+msgctxt "#32031"
+msgid "Coordinator"
+msgstr ""
+
+msgctxt "#32032"
+msgid "is not the group coordinator for zone"
+msgstr ""
+
+msgctxt "#32033"
+msgid "Some functions may not work correctly if the speaker is not a 
coordinator"
+msgstr ""
+
+#empty strings from id 32034 to 32059
 
 msgctxt "#32060"
 msgid "The following setting in ArtistSlideshow must be enabled"
diff --git a/script.sonos/resources/lib/mocksonos.py 
b/script.sonos/resources/lib/mocksonos.py
index d5b1f8d..018dd46 100644
--- a/script.sonos/resources/lib/mocksonos.py
+++ b/script.sonos/resources/lib/mocksonos.py
@@ -1,103 +1,103 @@
-# -*- coding: utf-8 -*-
-import xbmc
-
-
-#########################################################################
-# Mock Sonos class to support testing when there is no live Sonos system
-#########################################################################
-class TestMockSonos():
-    def __init__(self):
-        self.currentPlayState = 'PLAYING'
-        self.trackNumber = 100
-        self.isMuted = False
-        self.currentVolume = 50
-        self.duration = "00:05:47"
-        self.position = "00:02:25"
-
-    def get_current_track_info(self, forcedTrackNum=None):
-        displayTrackNum = self.trackNumber
-        if forcedTrackNum is not None:
-            displayTrackNum = forcedTrackNum
-
-        # Test code to test the dialog without a Sonos Speaker connected
-        track = {'title': "Money Money Money %d" % displayTrackNum,
-                 'artist': "ABBA %d" % displayTrackNum,
-                 'album': "Gold %d" % displayTrackNum,
-                 'album_art': '',
-                 'position': self.position,
-                 'duration': self.duration,
-                 'uri': "%d" % self.trackNumber,
-                 'playlist_position': "%d" % self.trackNumber}
-        return track
-
-    def get_current_transport_info(self):
-        playStatus = {'current_transport_state': self.currentPlayState}
-        return playStatus
-
-    def play(self):
-        self.currentPlayState = 'PLAYING'
-        self._displayOperation("Play")
-
-    def pause(self):
-        self.currentPlayState = 'PAUSED_PLAYBACK'
-        self._displayOperation("Paused")
-
-    def stop(self):
-        self.currentPlayState = 'STOPPED'
-        self._displayOperation("Stopped")
-
-    def next(self):
-        self.trackNumber = self.trackNumber + 1
-        self._displayOperation("Next Track")
-
-    def previous(self):
-        self.trackNumber = self.trackNumber - 1
-        self._displayOperation("Previous Track")
-
-    @property
-    def mute(self):
-        return self.isMuted
-
-    @mute.setter
-    def mute(self, mute):
-        if mute is True:
-            self.isMuted = True
-            self._displayOperation("Volume Muted")
-        else:
-            self.isMuted = False
-            self._displayOperation("Volume Unmuted")
-        return True
-
-    def get_queue(self, start=0, max_items=100):
-        queue = []
-        for num in range(start, start + max_items):
-            queue.append(self.get_current_track_info(num))
-        return queue
-
-    def get_tracks(self, start=0, max_items=100):
-        # TODO: THis is not currently returning what the sonos system does
-        out = {'item_list': []}
-        for num in range(start, start + max_items):
-            
out['item_list'].append(self.get_current_track_info(self.trackNumber + num + 1))
-        return out
-
-    @property
-    def volume(self):
-        return self.currentVolume
-
-    @volume.setter
-    def volume(self, newVolume):
-        if newVolume is not None:
-            self.currentVolume = int(newVolume)
-            self._displayOperation("Volume set to %d" % newVolume)
-        return self.currentVolume
-
-    def seek(self, timestamp):
-        self.position = timestamp
-        self._displayOperation("Seek Time to %s" % timestamp)
-
-    def _displayOperation(self, textStr):
-        xbmc.executebuiltin('Notification("Test Mock Sonos", %s, %d)' % 
(textStr, 3))
-
-    def switch_to_line_in(self):
-        xbmc.executebuiltin('Notification("Test Mock Sonos", "Switched to 
Line-In", %d)' % 3)
+# -*- coding: utf-8 -*-

+import xbmc

+

+

+#########################################################################

+# Mock Sonos class to support testing when there is no live Sonos system

+#########################################################################

+class TestMockSonos():

+    def __init__(self):

+        self.currentPlayState = 'PLAYING'

+        self.trackNumber = 100

+        self.isMuted = False

+        self.currentVolume = 50

+        self.duration = "00:05:47"

+        self.position = "00:02:25"

+

+    def get_current_track_info(self, forcedTrackNum=None):

+        displayTrackNum = self.trackNumber

+        if forcedTrackNum is not None:

+            displayTrackNum = forcedTrackNum

+

+        # Test code to test the dialog without a Sonos Speaker connected

+        track = {'title': "Money Money Money %d" % displayTrackNum,

+                 'artist': "ABBA %d" % displayTrackNum,

+                 'album': "Gold %d" % displayTrackNum,

+                 'album_art': '',

+                 'position': self.position,

+                 'duration': self.duration,

+                 'uri': "%d" % self.trackNumber,

+                 'playlist_position': "%d" % self.trackNumber}

+        return track

+

+    def get_current_transport_info(self):

+        playStatus = {'current_transport_state': self.currentPlayState}

+        return playStatus

+

+    def play(self):

+        self.currentPlayState = 'PLAYING'

+        self._displayOperation("Play")

+

+    def pause(self):

+        self.currentPlayState = 'PAUSED_PLAYBACK'

+        self._displayOperation("Paused")

+

+    def stop(self):

+        self.currentPlayState = 'STOPPED'

+        self._displayOperation("Stopped")

+

+    def next(self):

+        self.trackNumber = self.trackNumber + 1

+        self._displayOperation("Next Track")

+

+    def previous(self):

+        self.trackNumber = self.trackNumber - 1

+        self._displayOperation("Previous Track")

+

+    @property

+    def mute(self):

+        return self.isMuted

+

+    @mute.setter

+    def mute(self, mute):

+        if mute is True:

+            self.isMuted = True

+            self._displayOperation("Volume Muted")

+        else:

+            self.isMuted = False

+            self._displayOperation("Volume Unmuted")

+        return True

+

+    def get_queue(self, start=0, max_items=100):

+        queue = []

+        for num in range(start, start + max_items):

+            queue.append(self.get_current_track_info(num))

+        return queue

+

+    def get_tracks(self, start=0, max_items=100):

+        # TODO: THis is not currently returning what the sonos system does

+        out = {'item_list': []}

+        for num in range(start, start + max_items):

+            
out['item_list'].append(self.get_current_track_info(self.trackNumber + num + 1))

+        return out

+

+    @property

+    def volume(self):

+        return self.currentVolume

+

+    @volume.setter

+    def volume(self, newVolume):

+        if newVolume is not None:

+            self.currentVolume = int(newVolume)

+            self._displayOperation("Volume set to %d" % newVolume)

+        return self.currentVolume

+

+    def seek(self, timestamp):

+        self.position = timestamp

+        self._displayOperation("Seek Time to %s" % timestamp)

+

+    def _displayOperation(self, textStr):

+        xbmc.executebuiltin('Notification("Test Mock Sonos", %s, %d)' % 
(textStr, 3))

+

+    def switch_to_line_in(self):

+        xbmc.executebuiltin('Notification("Test Mock Sonos", "Switched to 
Line-In", %d)' % 3)

diff --git a/script.sonos/resources/lib/soco/event_structures.py 
b/script.sonos/resources/lib/soco/event_structures.py
index 6a4f8d4..345895e 100644
--- a/script.sonos/resources/lib/soco/event_structures.py
+++ b/script.sonos/resources/lib/soco/event_structures.py
@@ -1,315 +1,315 @@
-# -*- coding: utf-8 -*-
-
-""" This module contains all the event structures for the events that
-can be returned
-
-"""
-from __future__ import unicode_literals
-
-import logging
-from .xml import XML
-from .utils import really_utf8
-
-log = logging.getLogger(__name__)  # pylint: disable=C0103
-
-
-# pylint: disable=too-many-public-methods,too-many-statements
-class LastChangeEvent(object):
-    """
-    Class to handle the Last Change event XML
-    """
-
-    def __init__(self, contents):
-        """ Initialize the class with the contents of the event
-
-        :param contents: Dictionary of the data
-        """
-        self.content = contents
-
-    # pylint: disable=bare-except,broad-except,too-many-locals
-    @classmethod
-    def from_xml(cls, xmlstr):
-        """Return an instance of this class, created from xml.
-
-        :param xmlStr: The xml in string form to create the class from
-        """
-        try:
-            # Need to handle character encoding
-            xmlstr = really_utf8(xmlstr)
-            # Read in the XML
-            last_change_xml = XML.fromstring(xmlstr)
-        except Exception as exc:
-            # Not valid XML
-            log.exception(xmlstr)
-            log.exception(str(exc))
-            return None
-
-        # All the namespaces used in the event XML
-        avtns = '{urn:schemas-upnp-org:metadata-1-0/AVT/}'
-        didlns = '{urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/}'
-        rns = '{urn:schemas-rinconnetworks-com:metadata-1-0/}'
-        upnpns = '{urn:schemas-upnp-org:metadata-1-0/upnp/}'
-        nsdc = '{http://purl.org/dc/elements/1.1/}'
-
-        instanceid = last_change_xml.find('{0}InstanceID'.format(avtns))
-        if instanceid is None:
-            log.debug("No InstanceID found %s", xmlstr)
-            return None
-
-        result = {}
-        result['transportState'] = LastChangeEvent._get_val_data(
-            avtns, instanceid, 'TransportState')
-        result['currentPlayMode'] = LastChangeEvent._get_val_data(
-            avtns, instanceid, 'CurrentPlayMode')
-        result['currentCrossfadeMode'] = LastChangeEvent._get_val_data(
-            avtns, instanceid, 'CurrentCrossfadeMode')
-        result['numberOfTracks'] = LastChangeEvent._get_val_data(
-            avtns, instanceid, 'NumberOfTracks')
-        result['currentTrack'] = LastChangeEvent._get_val_data(
-            avtns, instanceid, 'CurrentTrack')
-        result['currentSection'] = LastChangeEvent._get_val_data(
-            avtns, instanceid, 'CurrentSection')
-        result['currentTrackURI'] = LastChangeEvent._get_val_data(
-            avtns, instanceid, 'CurrentTrackURI')
-        result['currentTrackDuration'] = LastChangeEvent._get_val_data(
-            avtns, instanceid, 'CurrentTrackDuration')
-
-        # The current track meta data is embedded XML
-        current_track_md = LastChangeEvent._get_val_data(
-            avtns, instanceid, 'CurrentTrackMetaData')
-        if (current_track_md is not None) and (current_track_md != ""):
-            log.debug("CurrentTrackMetaData: %s", current_track_md)
-            try:
-                current_track_md = current_track_md.encode('utf-8')
-                ctrack_metadata_xml = XML.fromstring(current_track_md)
-            except Exception as exc:
-                # Not valid XML
-                log.exception(current_track_md)
-                log.exception(str(exc))
-                return None
-
-            item = ctrack_metadata_xml.find('{0}item'.format(didlns))
-
-            if item is not None:
-                result['title'] = LastChangeEvent._get_element_data(
-                    nsdc, item, 'title')
-                result['creator'] = LastChangeEvent._get_element_data(
-                    nsdc, item, 'creator')
-                result['album'] = LastChangeEvent._get_element_data(
-                    upnpns, item, 'album')
-                result['originalTrackNumber'] =\
-                    LastChangeEvent._get_element_data(
-                        upnpns, item, 'originalTrackNumber')
-                result['albumArtist'] = LastChangeEvent._get_element_data(
-                    rns, item, 'albumArtist')
-                result['albumArtURI'] = LastChangeEvent._get_element_data(
-                    upnpns, item, 'albumArtURI')
-                result['radioShowMd'] = LastChangeEvent._get_element_data(
-                    rns, item, 'radioShowMd')
-
-        result['nextTrackURI'] = LastChangeEvent._get_val_data(
-            rns, instanceid, 'NextTrackURI')
-
-        # The next track meta data is embedded XML
-        next_track_metadata = LastChangeEvent._get_val_data(
-            rns, instanceid, 'NextTrackMetaData')
-        if (next_track_metadata is not None) and (next_track_metadata != ""):
-            log.debug("NextTrackMetaData: %s", next_track_metadata)
-            try:
-                next_track_metadata = next_track_metadata.encode('utf-8')
-                next_track_metadata_xml = XML.fromstring(next_track_metadata)
-            except Exception as exc:
-                # Not valid XML
-                log.exception(next_track_metadata)
-                log.exception(str(exc))
-                return None
-
-            item = next_track_metadata_xml.find('{0}item'.format(didlns))
-
-            if item is not None:
-                result['nextTitle'] = LastChangeEvent._get_element_data(
-                    nsdc, item, 'title')
-                result['nextCreator'] = LastChangeEvent._get_element_data(
-                    nsdc, item, 'creator')
-                result['nextAlbum'] = LastChangeEvent._get_element_data(
-                    upnpns, item, 'album')
-                result['nextOriginalTrackNumber'] =\
-                    LastChangeEvent._get_element_data(
-                        upnpns, item, 'originalTrackNumber')
-                result['nextAlbumArtist'] = LastChangeEvent._get_element_data(
-                    rns, item, 'albumArtist')
-                result['nextAlbumArtURI'] = LastChangeEvent._get_element_data(
-                    upnpns, item, 'albumArtURI')
-
-        # The transport meta data is embedded XML
-        transportmetadata = LastChangeEvent._get_val_data(
-            rns, instanceid, 'EnqueuedTransportURIMetaData')
-        if (transportmetadata is not None) and (transportmetadata != ""):
-            log.debug("EnqueuedTransportURIMetaData: %s", transportmetadata)
-            try:
-                transportmetadata = transportmetadata.encode('utf-8')
-                transport_xml = XML.fromstring(transportmetadata)
-            except Exception as exc:
-                # Not valid XML
-                log.exception(transportmetadata)
-                log.exception(str(exc))
-                return None
-
-            item = transport_xml.find('{0}item'.format(didlns))
-
-            if item is not None:
-                result['transportTitle'] = LastChangeEvent._get_element_data(
-                    nsdc, item, 'title')
-
-        return cls(result)
-
-    @staticmethod
-    def _get_val_data(namesp, container, elem_name):
-        """Returns the string from the val attribute
-
-        :param ns: Namespace the element is in
-        :param container: Parent object the element is in
-        :param elem_name: Name of the to get the attribute of
-        """
-        value = None
-        if container is not None:
-            element = container.find('{0}{1}'.format(namesp, elem_name))
-            if element is not None:
-                value = element.get('val')
-        return value
-
-    @staticmethod
-    def _get_element_data(namesp, container, elem_name):
-        """Returns the string from the element
-
-        :param ns: Namespace the element is in
-        :param container: Parent object the element is in
-        :param elem_name: Name of the to get the value of
-        """
-        value = None
-        if container is not None:
-            element = container.find('{0}{1}'.format(namesp, elem_name))
-            if element is not None:
-                value = element.text
-        return value
-
-    def _get_integer_content(self, keyval):
-        """Gets the content value as an integer"""
-        int_val = self.content.get(keyval, None)
-        if int_val is not None:
-            try:
-                # Try and convert to an integer
-                int_val = int(int_val)
-            except ValueError:
-                pass
-        return int_val
-
-    @property
-    def transport_state(self):
-        """Get the transport state"""
-        return self.content.get('transportState', None)
-
-    @property
-    def current_play_mode(self):
-        """Get the current play mode"""
-        return self.content.get('currentPlayMode', None)
-
-    @property
-    def current_crossfade_mode(self):
-        """Get the current cross fade mode"""
-        return self.content.get('currentCrossfadeMode', None)
-
-    @property
-    def number_of_tracks(self):
-        """Get the number of tracks"""
-        return self._get_integer_content('numberOfTracks')
-
-    @property
-    def current_track(self):
-        """Get the current track number"""
-        return self._get_integer_content('currentTrack')
-
-    @property
-    def current_track_uri(self):
-        """Get the track URI"""
-        return self.content.get('currentTrackURI', None)
-
-    @property
-    def current_track_duration(self):
-        """Get the current track duration"""
-        return self.content.get('currentTrackDuration', None)
-
-    @property
-    def title(self):
-        """Get the title"""
-        return self.content.get('title', None)
-
-    @property
-    def creator(self):
-        """Get the creator"""
-        return self.content.get('creator', None)
-
-    @property
-    def album(self):
-        """Get the album"""
-        return self.content.get('album', None)
-
-    @property
-    def original_track_number(self):
-        """Get the original track number"""
-        return self._get_integer_content('originalTrackNumber')
-
-    @property
-    def album_artist(self):
-        """Get the album artist"""
-        return self.content.get('albumArtist', None)
-
-    @property
-    def album_art_uri(self):
-        """Get the albumArtURI"""
-        return self.content.get('albumArtURI', None)
-
-    @property
-    def radio_show_md(self):
-        """Get the radio show name"""
-        return self.content.get('radioShowMd', None)
-
-    @property
-    def next_track_uri(self):
-        """Get the next track URI"""
-        return self.content.get('nextTrackURI', None)
-
-    @property
-    def next_title(self):
-        """Get the next title"""
-        return self.content.get('nextTitle', None)
-
-    @property
-    def next_creator(self):
-        """Get the next creator"""
-        return self.content.get('nextCreator', None)
-
-    @property
-    def next_album(self):
-        """Get the next album name"""
-        return self.content.get('nextAlbum', None)
-
-    @property
-    def next_original_track_number(self):
-        """Get the next original track number"""
-        return self._get_integer_content('nextOriginalTrackNumber')
-
-    @property
-    def next_album_artist(self):
-        """Get the next album artist"""
-        return self.content.get('nextAlbumArtist', None)
-
-    @property
-    def next_album_art_uri(self):
-        """Get the next albumArtURI"""
-        return self.content.get('nextAlbumArtURI', None)
-
-    @property
-    def transport_title(self):
-        """Get the transport title"""
-        return self.content.get('transportTitle', None)
+# -*- coding: utf-8 -*-

+

+""" This module contains all the event structures for the events that

+can be returned

+

+"""

+from __future__ import unicode_literals

+

+import logging

+from .xml import XML

+from .utils import really_utf8

+

+log = logging.getLogger(__name__)  # pylint: disable=C0103

+

+

+# pylint: disable=too-many-public-methods,too-many-statements

+class LastChangeEvent(object):

+    """

+    Class to handle the Last Change event XML

+    """

+

+    def __init__(self, contents):

+        """ Initialize the class with the contents of the event

+

+        :param contents: Dictionary of the data

+        """

+        self.content = contents

+

+    # pylint: disable=bare-except,broad-except,too-many-locals

+    @classmethod

+    def from_xml(cls, xmlstr):

+        """Return an instance of this class, created from xml.

+

+        :param xmlStr: The xml in string form to create the class from

+        """

+        try:

+            # Need to handle character encoding

+            xmlstr = really_utf8(xmlstr)

+            # Read in the XML

+            last_change_xml = XML.fromstring(xmlstr)

+        except Exception as exc:

+            # Not valid XML

+            log.exception(xmlstr)

+            log.exception(str(exc))

+            return None

+

+        # All the namespaces used in the event XML

+        avtns = '{urn:schemas-upnp-org:metadata-1-0/AVT/}'

+        didlns = '{urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/}'

+        rns = '{urn:schemas-rinconnetworks-com:metadata-1-0/}'

+        upnpns = '{urn:schemas-upnp-org:metadata-1-0/upnp/}'

+        nsdc = '{http://purl.org/dc/elements/1.1/}'

+

+        instanceid = last_change_xml.find('{0}InstanceID'.format(avtns))

+        if instanceid is None:

+            log.debug("No InstanceID found %s", xmlstr)

+            return None

+

+        result = {}

+        result['transportState'] = LastChangeEvent._get_val_data(

+            avtns, instanceid, 'TransportState')

+        result['currentPlayMode'] = LastChangeEvent._get_val_data(

+            avtns, instanceid, 'CurrentPlayMode')

+        result['currentCrossfadeMode'] = LastChangeEvent._get_val_data(

+            avtns, instanceid, 'CurrentCrossfadeMode')

+        result['numberOfTracks'] = LastChangeEvent._get_val_data(

+            avtns, instanceid, 'NumberOfTracks')

+        result['currentTrack'] = LastChangeEvent._get_val_data(

+            avtns, instanceid, 'CurrentTrack')

+        result['currentSection'] = LastChangeEvent._get_val_data(

+            avtns, instanceid, 'CurrentSection')

+        result['currentTrackURI'] = LastChangeEvent._get_val_data(

+            avtns, instanceid, 'CurrentTrackURI')

+        result['currentTrackDuration'] = LastChangeEvent._get_val_data(

+            avtns, instanceid, 'CurrentTrackDuration')

+

+        # The current track meta data is embedded XML

+        current_track_md = LastChangeEvent._get_val_data(

+            avtns, instanceid, 'CurrentTrackMetaData')

+        if (current_track_md is not None) and (current_track_md != ""):

+            log.debug("CurrentTrackMetaData: %s", current_track_md)

+            try:

+                current_track_md = current_track_md.encode('utf-8')

+                ctrack_metadata_xml = XML.fromstring(current_track_md)

+            except Exception as exc:

+                # Not valid XML

+                log.exception(current_track_md)

+                log.exception(str(exc))

+                return None

+

+            item = ctrack_metadata_xml.find('{0}item'.format(didlns))

+

+            if item is not None:

+                result['title'] = LastChangeEvent._get_element_data(

+                    nsdc, item, 'title')

+                result['creator'] = LastChangeEvent._get_element_data(

+                    nsdc, item, 'creator')

+                result['album'] = LastChangeEvent._get_element_data(

+                    upnpns, item, 'album')

+                result['originalTrackNumber'] =\

+                    LastChangeEvent._get_element_data(

+                        upnpns, item, 'originalTrackNumber')

+                result['albumArtist'] = LastChangeEvent._get_element_data(

+                    rns, item, 'albumArtist')

+                result['albumArtURI'] = LastChangeEvent._get_element_data(

+                    upnpns, item, 'albumArtURI')

+                result['radioShowMd'] = LastChangeEvent._get_element_data(

+                    rns, item, 'radioShowMd')

+

+        result['nextTrackURI'] = LastChangeEvent._get_val_data(

+            rns, instanceid, 'NextTrackURI')

+

+        # The next track meta data is embedded XML

+        next_track_metadata = LastChangeEvent._get_val_data(

+            rns, instanceid, 'NextTrackMetaData')

+        if (next_track_metadata is not None) and (next_track_metadata != ""):

+            log.debug("NextTrackMetaData: %s", next_track_metadata)

+            try:

+                next_track_metadata = next_track_metadata.encode('utf-8')

+                next_track_metadata_xml = XML.fromstring(next_track_metadata)

+            except Exception as exc:

+                # Not valid XML

+                log.exception(next_track_metadata)

+                log.exception(str(exc))

+                return None

+

+            item = next_track_metadata_xml.find('{0}item'.format(didlns))

+

+            if item is not None:

+                result['nextTitle'] = LastChangeEvent._get_element_data(

+                    nsdc, item, 'title')

+                result['nextCreator'] = LastChangeEvent._get_element_data(

+                    nsdc, item, 'creator')

+                result['nextAlbum'] = LastChangeEvent._get_element_data(

+                    upnpns, item, 'album')

+                result['nextOriginalTrackNumber'] =\

+                    LastChangeEvent._get_element_data(

+                        upnpns, item, 'originalTrackNumber')

+                result['nextAlbumArtist'] = LastChangeEvent._get_element_data(

+                    rns, item, 'albumArtist')

+                result['nextAlbumArtURI'] = LastChangeEvent._get_element_data(

+                    upnpns, item, 'albumArtURI')

+

+        # The transport meta data is embedded XML

+        transportmetadata = LastChangeEvent._get_val_data(

+            rns, instanceid, 'EnqueuedTransportURIMetaData')

+        if (transportmetadata is not None) and (transportmetadata != ""):

+            log.debug("EnqueuedTransportURIMetaData: %s", transportmetadata)

+            try:

+                transportmetadata = transportmetadata.encode('utf-8')

+                transport_xml = XML.fromstring(transportmetadata)

+            except Exception as exc:

+                # Not valid XML

+                log.exception(transportmetadata)

+                log.exception(str(exc))

+                return None

+

+            item = transport_xml.find('{0}item'.format(didlns))

+

+            if item is not None:

+                result['transportTitle'] = LastChangeEvent._get_element_data(

+                    nsdc, item, 'title')

+

+        return cls(result)

+

+    @staticmethod

+    def _get_val_data(namesp, container, elem_name):

+        """Returns the string from the val attribute

+

+        :param ns: Namespace the element is in

+        :param container: Parent object the element is in

+        :param elem_name: Name of the to get the attribute of

+        """

+        value = None

+        if container is not None:

+            element = container.find('{0}{1}'.format(namesp, elem_name))

+            if element is not None:

+                value = element.get('val')

+        return value

+

+    @staticmethod

+    def _get_element_data(namesp, container, elem_name):

+        """Returns the string from the element

+

+        :param ns: Namespace the element is in

+        :param container: Parent object the element is in

+        :param elem_name: Name of the to get the value of

+        """

+        value = None

+        if container is not None:

+            element = container.find('{0}{1}'.format(namesp, elem_name))

+            if element is not None:

+                value = element.text

+        return value

+

+    def _get_integer_content(self, keyval):

+        """Gets the content value as an integer"""

+        int_val = self.content.get(keyval, None)

+        if int_val is not None:

+            try:

+                # Try and convert to an integer

+                int_val = int(int_val)

+            except ValueError:

+                pass

+        return int_val

+

+    @property

+    def transport_state(self):

+        """Get the transport state"""

+        return self.content.get('transportState', None)

+

+    @property

+    def current_play_mode(self):

+        """Get the current play mode"""

+        return self.content.get('currentPlayMode', None)

+

+    @property

+    def current_crossfade_mode(self):

+        """Get the current cross fade mode"""

+        return self.content.get('currentCrossfadeMode', None)

+

+    @property

+    def number_of_tracks(self):

+        """Get the number of tracks"""

+        return self._get_integer_content('numberOfTracks')

+

+    @property

+    def current_track(self):

+        """Get the current track number"""

+        return self._get_integer_content('currentTrack')

+

+    @property

+    def current_track_uri(self):

+        """Get the track URI"""

+        return self.content.get('currentTrackURI', None)

+

+    @property

+    def current_track_duration(self):

+        """Get the current track duration"""

+        return self.content.get('currentTrackDuration', None)

+

+    @property

+    def title(self):

+        """Get the title"""

+        return self.content.get('title', None)

+

+    @property

+    def creator(self):

+        """Get the creator"""

+        return self.content.get('creator', None)

+

+    @property

+    def album(self):

+        """Get the album"""

+        return self.content.get('album', None)

+

+    @property

+    def original_track_number(self):

+        """Get the original track number"""

+        return self._get_integer_content('originalTrackNumber')

+

+    @property

+    def album_artist(self):

+        """Get the album artist"""

+        return self.content.get('albumArtist', None)

+

+    @property

+    def album_art_uri(self):

+        """Get the albumArtURI"""

+        return self.content.get('albumArtURI', None)

+

+    @property

+    def radio_show_md(self):

+        """Get the radio show name"""

+        return self.content.get('radioShowMd', None)

+

+    @property

+    def next_track_uri(self):

+        """Get the next track URI"""

+        return self.content.get('nextTrackURI', None)

+

+    @property

+    def next_title(self):

+        """Get the next title"""

+        return self.content.get('nextTitle', None)

+

+    @property

+    def next_creator(self):

+        """Get the next creator"""

+        return self.content.get('nextCreator', None)

+

+    @property

+    def next_album(self):

+        """Get the next album name"""

+        return self.content.get('nextAlbum', None)

+

+    @property

+    def next_original_track_number(self):

+        """Get the next original track number"""

+        return self._get_integer_content('nextOriginalTrackNumber')

+

+    @property

+    def next_album_artist(self):

+        """Get the next album artist"""

+        return self.content.get('nextAlbumArtist', None)

+

+    @property

+    def next_album_art_uri(self):

+        """Get the next albumArtURI"""

+        return self.content.get('nextAlbumArtURI', None)

+

+    @property

+    def transport_title(self):

+        """Get the transport title"""

+        return self.content.get('transportTitle', None)

diff --git a/script.sonos/resources/lib/sonos.py 
b/script.sonos/resources/lib/sonos.py
index 2f4cd5a..876a21c 100644
--- a/script.sonos/resources/lib/sonos.py
+++ b/script.sonos/resources/lib/sonos.py
@@ -1,201 +1,201 @@
-# -*- coding: utf-8 -*-
-import cgi
-import traceback
-import logging
-
-# Load the Soco classes
-from soco import SoCo
-from soco.event_structures import LastChangeEvent
-
-# Use the SoCo logger
-LOGGER = logging.getLogger('soco')
-
-
-#########################################################################
-# Sonos class to add extra support on top of SoCo
-#########################################################################
-class Sonos(SoCo):
-    # Format of the meta data (borrowed from sample code)
-    meta_template = '<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/"; 
xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" 
xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" 
xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><item id="R:0/0/0" 
parentID="R:0/0" 
restricted="true"><dc:title>{title}</dc:title><upnp:class>object.item.audioItem.audioBroadcast</upnp:class><desc
 id="cdudn" 
nameSpace="urn:schemas-rinconnetworks-com:metadata-1-0/">{service}</desc></item></DIDL-Lite>'
-    tunein_service = 'SA_RINCON65031_'
-
-    # Converts non complete URIs to complete URIs with IP address
-    def _updateAlbumArtToFullUri(self, musicInfo):
-        if hasattr(musicInfo, 'album_art_uri'):
-            # Add on the full album art link, as the URI version does not 
include the ipaddress
-            if (musicInfo.album_art_uri is not None) and 
(musicInfo.album_art_uri != ""):
-                if not musicInfo.album_art_uri.startswith(('http:', 'https:')):
-                    musicInfo.album_art_uri = 'http://' + self.ip_address + 
':1400' + musicInfo.album_art_uri
-
-    # Override method so that the album art http reference can be added
-    def get_music_library_information(self, search_type, start=0, 
max_items=100, sub_category=''):
-        # Make sure the sub category is valid for the message, escape invalid 
characters
-        sub_category = cgi.escape(sub_category)
-
-        # Call the base version
-        musicInfo = SoCo.get_music_library_information(self, search_type, 
start, max_items, sub_category)
-
-        if musicInfo is not None:
-            for anItem in musicInfo['item_list']:
-                # Make sure the album art URI is the full path
-                self._updateAlbumArtToFullUri(anItem)
-
-        return musicInfo
-
-    # Override method so that the album art http reference can be added
-    def get_queue(self, start=0, max_items=100):
-        list = SoCo.get_queue(self, start=start, max_items=max_items)
-
-        if list is not None:
-            for anItem in list:
-                # Make sure the album art URI is the full path
-                self._updateAlbumArtToFullUri(anItem)
-
-        return list
-
-    # For radio playing a title is required
-    def play_uri(self, uri='', title=None, metadata=''):
-        # Radio stations need to have at least a title to play
-        if (metadata == '') and (title is not None):
-            title_esc = cgi.escape(title)
-            metadata = Sonos.meta_template.format(title=title_esc, 
service=Sonos.tunein_service)
-
-        # Need to replace any special characters in the URI
-        uri = cgi.escape(uri)
-        # Now play the track
-        SoCo.play_uri(self, uri, metadata)
-
-    # Reads the current Random and repeat status
-    def getPlayMode(self):
-        isRandom = False
-        isLoop = False
-        # Check what the play mode is
-        playMode = self.play_mode
-        if playMode.upper() == "REPEAT_ALL":
-            isLoop = True
-        elif playMode.upper() == "SHUFFLE":
-            isLoop = True
-            isRandom = True
-        elif playMode.upper() == "SHUFFLE_NOREPEAT":
-            isRandom = True
-
-        return isRandom, isLoop
-
-    # Sets the current Random and repeat status
-    def setPlayMode(self, isRandom, isLoop):
-        playMode = "NORMAL"
-
-        # Convert the booleans into a playmode
-        if isRandom and isLoop:
-            playMode = "SHUFFLE"
-        elif isRandom and (not isLoop):
-            playMode = "SHUFFLE_NOREPEAT"
-        elif (not isRandom) and isLoop:
-            playMode = "REPEAT_ALL"
-
-        # Now set the playmode on the Sonos speaker
-        self.play_mode = playMode
-
-    def hasTrackChanged(self, track1, track2):
-        if track2 is None:
-            return False
-        if track1 is None:
-            return True
-        if track1['uri'] != track2['uri']:
-            return True
-        # Don't update if the URI is the same but the new version does
-        # not have event info
-        if (track2['lastEventDetails'] is None) and 
(track1['lastEventDetails'] is not None):
-            return False
-        if track1['title'] != track2['title']:
-            return True
-        if track1['album_art'] != track2['album_art']:
-            return True
-        if track1['artist'] != track2['artist']:
-            return True
-        if track1['album'] != track2['album']:
-            return True
-
-        return False
-
-    # Gets the most recent event from the event queue
-    def getLastEventDetails(self, sub):
-        lastChangeDetails = None
-        try:
-            queueItem = None
-            # Get the most recent event received
-            while not sub.events.empty():
-                try:
-                    # Get the next event - but do not block or wait for an 
event
-                    # if there is not already one there
-                    queueItem = sub.events.get(False)
-                except:
-                    LOGGER.debug("Sonos: Queue get failed: %s" % 
traceback.format_exc())
-
-            # Now get the details of an event if there is one there
-            lastChangeDetails = None
-            if queueItem is not None:
-                lastChangeXmlStr = queueItem.variables['LastChange']
-                if lastChangeXmlStr is not None:
-                    LOGGER.debug("Event details: %s" % lastChangeXmlStr)
-                    # Convert the XML into an object
-                    lastChangeDetails = 
LastChangeEvent.from_xml(lastChangeXmlStr)
-        except:
-            LOGGER.debug("Sonos: Failed to get latest event details: %s" % 
traceback.format_exc())
-
-        return lastChangeDetails
-
-    # When given a track info structure and an event, will merge the data
-    # together so that it is complete and accurate
-    def mergeTrackInfoAndEvent(self, track, eventDetails, previousTrack=None):
-        # If there is no event data, then just return the track unchanged
-        if eventDetails is None:
-            # Check to see if the track has changed, if it has not, then we can
-            # safely use the previous event we stored
-            if (previousTrack is not None) and (track['uri'] == 
previousTrack['uri']) and (previousTrack['lastEventDetails'] is not None):
-                LOGGER.debug("Sonos: Using previous Event details for merge")
-                track['lastEventDetails'] = previousTrack['lastEventDetails']
-                eventDetails = previousTrack['lastEventDetails']
-            else:
-                LOGGER.debug("Sonos: Event details not set for merge")
-                track['lastEventDetails'] = None
-                return track
-        else:
-            LOGGER.debug("Sonos: Event details set for merge")
-            track['lastEventDetails'] = eventDetails
-
-        # If the track has no album art, use the event one (if it exists)
-        if (track['album_art'] is None) or (track['album_art'] == ""):
-            if (eventDetails.album_art_uri is not None) and 
(eventDetails.album_art_uri != ""):
-                track['album_art'] = eventDetails.album_art_uri
-                # Make sure the Album art is fully qualified
-                if not track['album_art'].startswith(('http:', 'https:')):
-                    track['album_art'] = 'http://' + self.ip_address + ':1400' 
+ track['album_art']
-
-        if (track['artist'] is None) or (track['artist'] == ""):
-            if (eventDetails.album_artist is not None) and 
(eventDetails.album_artist != ""):
-                track['artist'] = eventDetails.album_artist
-
-        # Check if this is radio stream, in which case use that as the title
-        if (eventDetails.transport_title is not None) and 
(eventDetails.transport_title != ""):
-            if (track['title'] is None) or (track['title'] == ""):
-                track['title'] = eventDetails.transport_title
-        # Otherwise treat as a normal title
-        elif (track['title'] is None) or (track['title'] == ""):
-            if (eventDetails.title is not None) and (eventDetails.title != ""):
-                track['title'] = eventDetails.title
-
-        # Check if this is radio stream, in which case use that as the album 
title
-        if (eventDetails.radio_show_md is not None) and 
(eventDetails.radio_show_md != ""):
-            if (track['album'] is None) or (track['album'] == ""):
-                track['album'] = eventDetails.radio_show_md
-                # This may be something like: Drivetime,p239255 so need to 
remove the last section
-                trimmed = track['album'].rpartition(',p')[0]
-                if (trimmed is not None) and (trimmed != ""):
-                    track['album'] = trimmed
-        # Otherwise treat as a album title
-        elif (track['album'] is None) or (track['album'] == ""):
-            if (eventDetails.album is not None) and (eventDetails.album != ""):
-                track['album'] = eventDetails.album
-
-        return track
+# -*- coding: utf-8 -*-

+import cgi

+import traceback

+import logging

+

+# Load the Soco classes

+from soco import SoCo

+from soco.event_structures import LastChangeEvent

+

+# Use the SoCo logger

+LOGGER = logging.getLogger('soco')

+

+

+#########################################################################

+# Sonos class to add extra support on top of SoCo

+#########################################################################

+class Sonos(SoCo):

+    # Format of the meta data (borrowed from sample code)

+    meta_template = '<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/"; 
xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" 
xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" 
xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><item id="R:0/0/0" 
parentID="R:0/0" 
restricted="true"><dc:title>{title}</dc:title><upnp:class>object.item.audioItem.audioBroadcast</upnp:class><desc
 id="cdudn" 
nameSpace="urn:schemas-rinconnetworks-com:metadata-1-0/">{service}</desc></item></DIDL-Lite>'

+    tunein_service = 'SA_RINCON65031_'

+

+    # Converts non complete URIs to complete URIs with IP address

+    def _updateAlbumArtToFullUri(self, musicInfo):

+        if hasattr(musicInfo, 'album_art_uri'):

+            # Add on the full album art link, as the URI version does not 
include the ipaddress

+            if (musicInfo.album_art_uri is not None) and 
(musicInfo.album_art_uri != ""):

+                if not musicInfo.album_art_uri.startswith(('http:', 'https:')):

+                    musicInfo.album_art_uri = 'http://' + self.ip_address + 
':1400' + musicInfo.album_art_uri

+

+    # Override method so that the album art http reference can be added

+    def get_music_library_information(self, search_type, start=0, 
max_items=100, sub_category=''):

+        # Make sure the sub category is valid for the message, escape invalid 
characters

+        sub_category = cgi.escape(sub_category)

+

+        # Call the base version

+        musicInfo = SoCo.get_music_library_information(self, search_type, 
start, max_items, sub_category)

+

+        if musicInfo is not None:

+            for anItem in musicInfo['item_list']:

+                # Make sure the album art URI is the full path

+                self._updateAlbumArtToFullUri(anItem)

+

+        return musicInfo

+

+    # Override method so that the album art http reference can be added

+    def get_queue(self, start=0, max_items=100):

+        list = SoCo.get_queue(self, start=start, max_items=max_items)

+

+        if list is not None:

+            for anItem in list:

+                # Make sure the album art URI is the full path

+                self._updateAlbumArtToFullUri(anItem)

+

+        return list

+

+    # For radio playing a title is required

+    def play_uri(self, uri='', title=None, metadata=''):

+        # Radio stations need to have at least a title to play

+        if (metadata == '') and (title is not None):

+            title_esc = cgi.escape(title)

+            metadata = Sonos.meta_template.format(title=title_esc, 
service=Sonos.tunein_service)

+

+        # Need to replace any special characters in the URI

+        uri = cgi.escape(uri)

+        # Now play the track

+        SoCo.play_uri(self, uri, metadata)

+

+    # Reads the current Random and repeat status

+    def getPlayMode(self):

+        isRandom = False

+        isLoop = False

+        # Check what the play mode is

+        playMode = self.play_mode

+        if playMode.upper() == "REPEAT_ALL":

+            isLoop = True

+        elif playMode.upper() == "SHUFFLE":

+            isLoop = True

+            isRandom = True

+        elif playMode.upper() == "SHUFFLE_NOREPEAT":

+            isRandom = True

+

+        return isRandom, isLoop

+

+    # Sets the current Random and repeat status

+    def setPlayMode(self, isRandom, isLoop):

+        playMode = "NORMAL"

+

+        # Convert the booleans into a playmode

+        if isRandom and isLoop:

+            playMode = "SHUFFLE"

+        elif isRandom and (not isLoop):

+            playMode = "SHUFFLE_NOREPEAT"

+        elif (not isRandom) and isLoop:

+            playMode = "REPEAT_ALL"

+

+        # Now set the playmode on the Sonos speaker

+        self.play_mode = playMode

+

+    def hasTrackChanged(self, track1, track2):

+        if track2 is None:

+            return False

+        if track1 is None:

+            return True

+        if track1['uri'] != track2['uri']:

+            return True

+        # Don't update if the URI is the same but the new version does

+        # not have event info

+        if (track2['lastEventDetails'] is None) and 
(track1['lastEventDetails'] is not None):

+            return False

+        if track1['title'] != track2['title']:

+            return True

+        if track1['album_art'] != track2['album_art']:

+            return True

+        if track1['artist'] != track2['artist']:

+            return True

+        if track1['album'] != track2['album']:

+            return True

+

+        return False

+

+    # Gets the most recent event from the event queue

+    def getLastEventDetails(self, sub):

+        lastChangeDetails = None

+        try:

+            queueItem = None

+            # Get the most recent event received

+            while not sub.events.empty():

+                try:

+                    # Get the next event - but do not block or wait for an 
event

+                    # if there is not already one there

+                    queueItem = sub.events.get(False)

+                except:

+                    LOGGER.debug("Sonos: Queue get failed: %s" % 
traceback.format_exc())

+

+            # Now get the details of an event if there is one there

+            lastChangeDetails = None

+            if queueItem is not None:

+                lastChangeXmlStr = queueItem.variables['LastChange']

+                if lastChangeXmlStr is not None:

+                    LOGGER.debug("Event details: %s" % lastChangeXmlStr)

+                    # Convert the XML into an object

+                    lastChangeDetails = 
LastChangeEvent.from_xml(lastChangeXmlStr)

+        except:

+            LOGGER.debug("Sonos: Failed to get latest event details: %s" % 
traceback.format_exc())

+

+        return lastChangeDetails

+

+    # When given a track info structure and an event, will merge the data

+    # together so that it is complete and accurate

+    def mergeTrackInfoAndEvent(self, track, eventDetails, previousTrack=None):

+        # If there is no event data, then just return the track unchanged

+        if eventDetails is None:

+            # Check to see if the track has changed, if it has not, then we can

+            # safely use the previous event we stored

+            if (previousTrack is not None) and (track['uri'] == 
previousTrack['uri']) and (previousTrack['lastEventDetails'] is not None):

+                LOGGER.debug("Sonos: Using previous Event details for merge")

+                track['lastEventDetails'] = previousTrack['lastEventDetails']

+                eventDetails = previousTrack['lastEventDetails']

+            else:

+                LOGGER.debug("Sonos: Event details not set for merge")

+                track['lastEventDetails'] = None

+                return track

+        else:

+            LOGGER.debug("Sonos: Event details set for merge")

+            track['lastEventDetails'] = eventDetails

+

+        # If the track has no album art, use the event one (if it exists)

+        if (track['album_art'] is None) or (track['album_art'] == ""):

+            if (eventDetails.album_art_uri is not None) and 
(eventDetails.album_art_uri != ""):

+                track['album_art'] = eventDetails.album_art_uri

+                # Make sure the Album art is fully qualified

+                if not track['album_art'].startswith(('http:', 'https:')):

+                    track['album_art'] = 'http://' + self.ip_address + ':1400' 
+ track['album_art']

+

+        if (track['artist'] is None) or (track['artist'] == ""):

+            if (eventDetails.album_artist is not None) and 
(eventDetails.album_artist != ""):

+                track['artist'] = eventDetails.album_artist

+

+        # Check if this is radio stream, in which case use that as the title

+        if (eventDetails.transport_title is not None) and 
(eventDetails.transport_title != ""):

+            if (track['title'] is None) or (track['title'] == ""):

+                track['title'] = eventDetails.transport_title

+        # Otherwise treat as a normal title

+        elif (track['title'] is None) or (track['title'] == ""):

+            if (eventDetails.title is not None) and (eventDetails.title != ""):

+                track['title'] = eventDetails.title

+

+        # Check if this is radio stream, in which case use that as the album 
title

+        if (eventDetails.radio_show_md is not None) and 
(eventDetails.radio_show_md != ""):

+            if (track['album'] is None) or (track['album'] == ""):

+                track['album'] = eventDetails.radio_show_md

+                # This may be something like: Drivetime,p239255 so need to 
remove the last section

+                trimmed = track['album'].rpartition(',p')[0]

+                if (trimmed is not None) and (trimmed != ""):

+                    track['album'] = trimmed

+        # Otherwise treat as a album title

+        elif (track['album'] is None) or (track['album'] == ""):

+            if (eventDetails.album is not None) and (eventDetails.album != ""):

+                track['album'] = eventDetails.album

+

+        return track

diff --git a/script.sonos/resources/settings.xml 
b/script.sonos/resources/settings.xml
index 3d77f90..0181787 100644
--- a/script.sonos/resources/settings.xml
+++ b/script.sonos/resources/settings.xml
@@ -1,36 +1,36 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<settings>
-       <category label="32010">
-               <setting label="32013" type="action" 
action="RunScript($CWD/discovery.py)"/>
-               <setting id="ipAddress" type="ipaddress" label="32002" 
default="0.0.0.0"/>
-               <setting id="zoneName" type="text" label="32028" default=""/>
-               <setting id="autoIPUpdate" subsetting="true" enable="!eq(-1,)" 
type="bool" label="32029" default="false"/>
-       <setting label="32012" type="lsep"/>
-               <setting id="logEnabled" type="bool" label="32009" 
default="false"/>
-               <setting id="useTestData" type="bool" label="32017" 
default="false"/>
-       </category>
-       <category label="32015">
-               <setting id="refreshInterval" label="32016" type="slider" 
default="2" range="0.5,0.5,5" option="float"/>
-               <setting id="displayArtistInfo" type="bool" label="32021" 
default="false"/>
-               <setting id="avoidDuplicateCommands" label="32022" 
type="slider" default="1.5" range="0.5,0.5,5" option="float"/>
-               <setting id="volumeChangeIncrements" label="32025" 
type="slider" default="3" range="1,1,10" option="int"/>
-
-               <setting id="linkAudioWithSonos" type="bool" label="32023" 
default="false"/>
-               <setting id="switchSonosToLineIn" subsetting="true" 
enable="eq(-1,true)" type="bool" label="32024" default="false"/>
-               <setting id="switchSonosToLineInOnMediaStart" subsetting="true" 
enable="eq(-2,true)" type="bool" label="32030" default="false"/>
-               
-               <setting id="autoPauseSonos" enable="eq(-2,false)" type="bool" 
label="32026" default="false"/>
-               <setting id="autoResumeSonos" subsetting="true" 
enable="eq(-1,true)" label="32027" type="slider" default="0" range="0,3,60" 
option="int"/>
-       </category>
-       <category label="32011">
-               <setting id="notifEnabled" type="bool" label="32003" 
default="true"/>
-               <setting id="notifDisplayDuration" enable="eq(-1,true)" 
label="32004" type="slider" default="3" range="0,1,60" option="int"/>
-               <setting id="notifCheckFrequency" enable="eq(-2,true)" 
label="32005" type="slider" default="10" range="0,1,60" option="int"/>
-               <setting id="notifNotIfVideoPlaying" enable="eq(-3,true)" 
type="bool" label="32006" default="true"/>
-               <setting id="xbmcNotifDialog" enable="eq(-4,true)" type="bool" 
label="32007" default="false"/>
-       </category>
-       <category label="32018">
-               <setting id="batchSize" label="32019" type="slider" 
default="100" range="10,10,1000" option="int"/>
-               <setting id="maxListEntries" label="32020" type="slider" 
default="1000" range="0,100,3000" option="int"/>
-       </category>
-</settings>
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>

+<settings>

+       <category label="32010">

+               <setting label="32013" type="action" 
action="RunScript($CWD/discovery.py)"/>

+               <setting id="ipAddress" type="ipaddress" label="32002" 
default="0.0.0.0"/>

+               <setting id="zoneName" type="text" label="32028" default=""/>

+               <setting id="autoIPUpdate" subsetting="true" enable="!eq(-1,)" 
type="bool" label="32029" default="false"/>

+       <setting label="32012" type="lsep"/>

+               <setting id="logEnabled" type="bool" label="32009" 
default="false"/>

+               <setting id="useTestData" type="bool" label="32017" 
default="false"/>

+       </category>

+       <category label="32015">

+               <setting id="refreshInterval" label="32016" type="slider" 
default="2" range="0.5,0.5,5" option="float"/>

+               <setting id="displayArtistInfo" type="bool" label="32021" 
default="false"/>

+               <setting id="avoidDuplicateCommands" label="32022" 
type="slider" default="1.5" range="0.5,0.5,5" option="float"/>

+               <setting id="volumeChangeIncrements" label="32025" 
type="slider" default="3" range="1,1,10" option="int"/>

+

+               <setting id="linkAudioWithSonos" type="bool" label="32023" 
default="false"/>

+               <setting id="switchSonosToLineIn" subsetting="true" 
enable="eq(-1,true)" type="bool" label="32024" default="false"/>

+               <setting id="switchSonosToLineInOnMediaStart" subsetting="true" 
enable="eq(-2,true)" type="bool" label="32030" default="false"/>

+               

+               <setting id="autoPauseSonos" enable="eq(-2,false)" type="bool" 
label="32026" default="false"/>

+               <setting id="autoResumeSonos" subsetting="true" 
enable="eq(-1,true)" label="32027" type="slider" default="0" range="0,3,60" 
option="int"/>

+       </category>

+       <category label="32011">

+               <setting id="notifEnabled" type="bool" label="32003" 
default="true"/>

+               <setting id="notifDisplayDuration" enable="eq(-1,true)" 
label="32004" type="slider" default="3" range="0,1,60" option="int"/>

+               <setting id="notifCheckFrequency" enable="eq(-2,true)" 
label="32005" type="slider" default="10" range="0,1,60" option="int"/>

+               <setting id="notifNotIfVideoPlaying" enable="eq(-3,true)" 
type="bool" label="32006" default="true"/>

+               <setting id="xbmcNotifDialog" enable="eq(-4,true)" type="bool" 
label="32007" default="false"/>

+       </category>

+       <category label="32018">

+               <setting id="batchSize" label="32019" type="slider" 
default="100" range="10,10,1000" option="int"/>

+               <setting id="maxListEntries" label="32020" type="slider" 
default="1000" range="0,100,3000" option="int"/>

+       </category>

+</settings>

diff --git 
a/script.sonos/resources/skins/Default/720p/script-sonos-controller.xml 
b/script.sonos/resources/skins/Default/720p/script-sonos-controller.xml
index 940d9c5..a42cbce 100644
--- a/script.sonos/resources/skins/Default/720p/script-sonos-controller.xml
+++ b/script.sonos/resources/skins/Default/720p/script-sonos-controller.xml
@@ -1,393 +1,393 @@
-<?xml version="1.0" encoding="utf-8"?>
-<window type="dialog" id="3002">
-       <defaultcontrol always="true">100</defaultcontrol>
-       <animation effect="fade" time="250">WindowOpen</animation>
-       <animation effect="fade" time="250">WindowClose</animation>
-       <coordinates>
-               <system>1</system>
-               <posx>390</posx>
-               <posy>250</posy>
-       </coordinates>
-       <controls>
-               <control type="image">
-                       <description>Main Dialog Background</description>
-                       <posx>0</posx>
-                       <posy>0</posy>
-                       <width>500</width>
-                       <height>225</height>
-                       <colordiffuse>CCFFFFFF</colordiffuse>
-                       <texture border="20">[email protected]</texture>
-               </control>
-               <control type="image">
-                       <description>gradient</description>
-                       <posx>5</posx>
-                       <posy>5</posy>
-                       <width>490</width>
-                       <height>215</height>
-                       <texture border="20">msNowPlayingBg_ipad.png</texture>
-               </control>
-
-               <!-- Music Info -->
-               <control type="group" id="300">
-                       <control type="image" id="801">
-                               <description>Cover image</description>
-                               <posx>20</posx>
-                               <posy>17</posy>
-                               <width>130</width>
-                               <height>130</height>
-                       </control>
-                       <control type="fadelabel" id="802">
-                               <description>Artist label</description>
-                               <posx>160</posx>
-                               <posy>20</posy>
-                               <height>30</height>
-                               <width>325</width>
-                               <align>left</align>
-                               <aligny>center</aligny>
-                               <font>font12_title</font>
-                               <textcolor>FF999999</textcolor>         <!-- 
grey2 -->
-                               <shadowcolor>FF000000</shadowcolor> <!-- black 
-->
-                               <scrollout>false</scrollout>
-                               <pauseatend>2000</pauseatend>
-                       </control>
-                       <control type="fadelabel" id="803">
-                               <description>Title label</description>
-                               <posx>160</posx>
-                               <posy>43</posy>
-                               <height>30</height>
-                               <width>325</width>
-                               <align>left</align>
-                               <aligny>center</aligny>
-                               <font>font13_title</font>
-                               <textcolor>ffffffff</textcolor>         <!-- 
white -->
-                               <shadowcolor>FF000000</shadowcolor> <!-- black 
-->
-                               <scrollout>false</scrollout>
-                               <pauseatend>2000</pauseatend>
-                       </control>
-                       <control type="fadelabel" id="804">
-                               <description>Album Label</description>
-                               <posx>160</posx>
-                               <posy>70</posy>
-                               <height>30</height>
-                               <width>325</width>
-                               <align>left</align>
-                               <aligny>center</aligny>
-                               <font>font12</font>
-                               <textcolor>ffffffff</textcolor>         <!-- 
white -->
-                               <shadowcolor>FF000000</shadowcolor> <!-- black 
-->
-                               <scrollout>false</scrollout>
-                               <pauseatend>2000</pauseatend>
-                       </control>
-                       <control type="fadelabel" id="805">
-                               <description>Next Label</description>
-                               <posx>160</posx>
-                               <posy>100</posy>
-                               <height>30</height>
-                               <width>325</width>
-                               <align>right</align>
-                               <aligny>center</aligny>
-                               <font>font12</font>
-                               <textcolor>7fffffff</textcolor>         <!-- 
grey -->
-                               <shadowcolor>FF000000</shadowcolor> <!-- black 
-->
-                               <scrollout>false</scrollout>
-                               <pauseatend>2000</pauseatend>
-                       </control>
-                       <control type="label" id="810">
-                               <description>Current Time Position 
Label</description>
-                               <posx>160</posx>
-                               <posy>130</posy>
-                               <height>15</height>
-                               <width>60</width>
-                               <label>-</label>
-                               <align>center</align>
-                               <aligny>center</aligny>
-                               <font>font10</font>
-                               <textcolor>ffffffff</textcolor>         <!-- 
white -->
-                               <shadowcolor>FF000000</shadowcolor> <!-- black 
-->
-                               <!-- Only display the start time if there is a 
duration set -->
-                               <visible>Control.IsVisible(812)</visible>
-                       </control>
-                       <control type="slider" id="811">
-                               <description>Seek Slider</description>
-                               <posx>220</posx>
-                               <posy>135</posy>
-                               <width>215</width>
-                               <height>5</height>
-                               <texturefocus>-</texturefocus>
-                               <texturenofocus>-</texturenofocus>
-                               
<texturesliderbar>pmProgressTrackBar.png</texturesliderbar>
-                               
<textureslidernib>pmProgressTrackBarFillStretch.png</textureslidernib>
-                               
<textureslidernibfocus>pmProgressTrackBarFillStretch.png</textureslidernibfocus>
-                               <onup>100</onup>
-                               <ondown>900</ondown>
-                               <!-- Only display the slider if there is a 
duration set -->
-                               <visible>Control.IsVisible(812)</visible>
-                       </control>
-                       <control type="label" id="812">
-                               <description>Duration Label</description>
-                               <posx>435</posx>
-                               <posy>130</posy>
-                               <height>15</height>
-                               <width>60</width>
-                               <label>-</label>
-                               <align>center</align>
-                               <aligny>center</aligny>
-                               <font>font10</font>
-                               <textcolor>ffffffff</textcolor>         <!-- 
white -->
-                               <shadowcolor>FF000000</shadowcolor> <!-- black 
-->
-                               <visible>True</visible>
-                       </control>
-               </control>
-
-               <control type="image">
-                       <description>Background For the controls</description>
-                       <posx>10</posx>
-                       <posy>165</posy>
-                       <width>480</width>
-                       <height>53</height>
-                       <colordiffuse>DDFFFFFF</colordiffuse>
-                       <texture flipy="false" 
border="20,20,20,2">[email protected]</texture>
-               </control>
-
-               <control type="group" id="100">
-                       <posx>25</posx>
-                       <posy>172</posy>
-                       <defaultcontrol always="true">603</defaultcontrol>
-                       <control type="button" id="600">
-                               <description>Previous Button</description>
-                               <posx>0</posx>
-                               <posy>0</posy>
-                               <width>40</width>
-                               <height>40</height>
-                               <label>-</label>
-                               
<texturefocus>control_buttons/tbTransportBack_sel.png</texturefocus>
-                               
<texturenofocus>control_buttons/tbTransportBack.png</texturenofocus>
-                               <onleft>102</onleft>
-                               <onright>101</onright>
-                               <onup>900</onup>
-                               <ondown>622</ondown>
-                       </control>
-                       <!-- Group for the Play Pause Button to allow keyboard 
navigation -->
-                       <control type="group" id="101">
-                               <control type="button" id="601">
-                                       <description>Play Button</description>
-                                       <posx>40</posx>
-                                       <posy>0</posy>
-                                       <width>40</width>
-                                       <height>40</height>
-                                       <label>-</label>
-                                       
<texturefocus>control_buttons/tbPlay_sel.png</texturefocus>
-                                       
<texturenofocus>control_buttons/tbPlay.png</texturenofocus>
-                                       <onleft>600</onleft>
-                                       <onright>603</onright>
-                                       <onup>900</onup>
-                                       <ondown>622</ondown>
-                                       <visible>True</visible>
-                               </control>
-                               <control type="button" id="602">
-                                       <description>Pause Button</description>
-                                       <posx>40</posx>
-                                       <posy>0</posy>
-                                       <width>40</width>
-                                       <height>40</height>
-                                       <label>-</label>
-                                       
<texturefocus>control_buttons/tbPause_sel.png</texturefocus>
-                                       
<texturenofocus>control_buttons/tbPause.png</texturenofocus>
-                                       <onleft>600</onleft>
-                                       <onright>603</onright>
-                                       <onup>900</onup>
-                                       <ondown>622</ondown>
-                                       
<visible>!Control.IsVisible(601)</visible>
-                               </control>
-                       </control>
-                       <control type="button" id="603">
-                               <description>Stop Button</description>
-                               <posx>80</posx>
-                               <posy>0</posy>
-                               <width>40</width>
-                               <height>40</height>
-                               <label>-</label>
-                               
<texturefocus>control_buttons/tbStop_sel.png</texturefocus>
-                               
<texturenofocus>control_buttons/tbStop.png</texturenofocus>
-                               <onleft>101</onleft>
-                               <onright>604</onright>
-                               <onup>900</onup>
-                               <ondown>622</ondown>
-                       </control>
-                       <control type="button" id="604">
-                               <description>Next Button</description>
-                               <posx>120</posx>
-                               <posy>0</posy>
-                               <width>40</width>
-                               <height>40</height>
-                               <label>-</label>
-                               
<texturefocus>control_buttons/tbTransportForward_sel.png</texturefocus>
-                               
<texturenofocus>control_buttons/tbTransportForward.png</texturenofocus>
-                               <onleft>603</onleft>
-                               <onright>104</onright>
-                               <onup>900</onup>
-                               <ondown>622</ondown>
-                       </control>
-                       <!-- Group the Repeat Buttons -->
-                       <control type="group" id="104">
-                               <control type="button" id="605">
-                                       <description>Repeat Button</description>
-                                       <posx>190</posx>
-                                       <posy>10</posy>
-                                       <width>20</width>
-                                       <height>20</height>
-                                       <label>-</label>
-                                       
<texturefocus>control_buttons/[email protected]</texturefocus>
-                                       
<texturenofocus>control_buttons/[email protected]</texturenofocus>
-                                       <onleft>604</onleft>
-                                       <onright>103</onright>
-                                       <onup>900</onup>
-                                       <ondown>622</ondown>
-                               </control>
-                               <control type="button" id="606">
-                                       <description>Repeat Button 
Enabled</description>
-                                       <posx>190</posx>
-                                       <posy>10</posy>
-                                       <width>20</width>
-                                       <height>20</height>
-                                       <label>-</label>
-                                       
<texturefocus>control_buttons/[email protected]</texturefocus>
-                                       
<texturenofocus>control_buttons/[email protected]</texturenofocus>
-                                       <onleft>604</onleft>
-                                       <onright>103</onright>
-                                       <onup>900</onup>
-                                       
<visible>!Control.IsVisible(605)</visible>
-                                       <ondown>622</ondown>
-                               </control>
-                       </control>
-                       <!-- Group the Random Buttons -->
-                       <control type="group" id="103">
-                               <control type="button" id="607">
-                                       <description>Random Button</description>
-                                       <posx>220</posx>
-                                       <posy>10</posy>
-                                       <width>20</width>
-                                       <height>20</height>
-                                       <label>-</label>
-                                       
<texturefocus>control_buttons/[email protected]</texturefocus>
-                                       
<texturenofocus>control_buttons/[email protected]</texturenofocus>
-                                       <onleft>104</onleft>
-                                       <onright>105</onright>
-                                       <onup>900</onup>
-                                       <ondown>622</ondown>
-                               </control>
-                               <control type="button" id="608">
-                                       <description>Random Button 
Enabled</description>
-                                       <posx>220</posx>
-                                       <posy>10</posy>
-                                       <width>20</width>
-                                       <height>20</height>
-                                       <label>-</label>
-                                       
<texturefocus>control_buttons/[email protected]</texturefocus>
-                                       
<texturenofocus>control_buttons/[email protected]</texturenofocus>
-                                       <onleft>104</onleft>
-                                       <onright>105</onright>
-                                       <onup>900</onup>
-                                       <ondown>622</ondown>
-                                       
<visible>!Control.IsVisible(607)</visible>
-                               </control>
-                       </control>
-                       <!-- Group the Crossfade Buttons -->
-                       <control type="group" id="105">
-                               <control type="button" id="609">
-                                       <description>Crossfade 
Button</description>
-                                       <posx>250</posx>
-                                       <posy>10</posy>
-                                       <width>20</width>
-                                       <height>20</height>
-                                       <label>-</label>
-                                       
<texturefocus>control_buttons/[email protected]</texturefocus>
-                                       
<texturenofocus>control_buttons/[email protected]</texturenofocus>
-                                       <onleft>103</onleft>
-                                       <onright>102</onright>
-                                       <onup>900</onup>
-                                       <ondown>622</ondown>
-                               </control>
-                               <control type="button" id="610">
-                                       <description>Crossfade Button 
Enabled</description>
-                                       <posx>250</posx>
-                                       <posy>10</posy>
-                                       <width>20</width>
-                                       <height>20</height>
-                                       <label>-</label>
-                                       
<texturefocus>control_buttons/[email protected]</texturefocus>
-                                       
<texturenofocus>control_buttons/[email protected]</texturenofocus>
-                                       <onleft>103</onleft>
-                                       <onright>102</onright>
-                                       <onup>900</onup>
-                                       <ondown>622</ondown>
-                                       
<visible>!Control.IsVisible(609)</visible>
-                               </control>
-                       </control>
-                       <!-- Group for the Sound and mute Button to allow 
keyboard navigation -->
-                       <control type="group" id="102">
-                               <control type="button" id="620">
-                                       <description>Sound Volume 
Button</description>
-                                       <posx>290</posx>
-                                       <posy>0</posy>
-                                       <width>40</width>
-                                       <height>40</height>
-                                       <label>-</label>
-                                       
<texturefocus>control_buttons/tbUnMute_sel.png</texturefocus>
-                                       
<texturenofocus>control_buttons/tbMute.png</texturenofocus>
-                                       <onleft>105</onleft>
-                                       <onright>600</onright>
-                                       <onup>900</onup>
-                                       <ondown>622</ondown>
-                                       <visible>True</visible>
-                               </control>
-                               <control type="button" id="621">
-                                       <description>Sound Mute 
Button</description>
-                                       <posx>290</posx>
-                                       <posy>0</posy>
-                                       <width>40</width>
-                                       <height>40</height>
-                                       <label>-</label>
-                                       
<texturefocus>control_buttons/tbMute_sel.png</texturefocus>
-                                       
<texturenofocus>control_buttons/tbMute_on.png</texturenofocus>
-                                       <onleft>105</onleft>
-                                       <onright>600</onright>
-                                       <onup>900</onup>
-                                       <ondown>622</ondown>
-                                       
<visible>!Control.IsVisible(620)</visible>
-                               </control>
-                       </control>
-                       <control type="slider" id="622">
-                               <description>Volume Slider</description>
-                               <posx>330</posx>
-                               <posy>10</posy>
-                               <width>120</width>
-                               <height>20</height>
-                               <texturefocus>-</texturefocus>
-                               <texturenofocus>-</texturenofocus>
-                               
<texturesliderbar>control_buttons/snAutoBtn.png</texturesliderbar>
-                               
<textureslidernib>control_buttons/tbVolumeScrubber_dis.png</textureslidernib>
-                               
<textureslidernibfocus>control_buttons/tbVolumeScrubber.png</textureslidernibfocus>
-                               <onup>100</onup>
-                               <ondown>900</ondown>
-                       </control>
-               </control>
-               <control type="button" id="900">
-                       <description>Close Window button</description>
-                       <posx>430</posx>
-                       <posy>5</posy>
-                       <width>52</width>
-                       <height>26</height>
-                       <label>-</label>
-                       <font>-</font>
-                       <onclick>back</onclick>
-                       <texturefocus>DialogCloseButton-focus.png</texturefocus>
-                       <texturenofocus>DialogCloseButton.png</texturenofocus>
-                       <onleft>100</onleft>
-                       <onright>100</onright>
-                       <onup>622</onup>
-                       <ondown>100</ondown>
-                       <visible>system.getbool(input.enablemouse)</visible>
-               </control>
-       </controls>
-</window>
+<?xml version="1.0" encoding="utf-8"?>

+<window type="dialog" id="3002">

+       <defaultcontrol always="true">100</defaultcontrol>

+       <animation effect="fade" time="250">WindowOpen</animation>

+       <animation effect="fade" time="250">WindowClose</animation>

+       <coordinates>

+               <system>1</system>

+               <posx>390</posx>

+               <posy>250</posy>

+       </coordinates>

+       <controls>

+               <control type="image">

+                       <description>Main Dialog Background</description>

+                       <posx>0</posx>

+                       <posy>0</posy>

+                       <width>500</width>

+                       <height>225</height>

+                       <colordiffuse>CCFFFFFF</colordiffuse>

+                       <texture border="20">[email protected]</texture>

+               </control>

+               <control type="image">

+                       <description>gradient</description>

+                       <posx>5</posx>

+                       <posy>5</posy>

+                       <width>490</width>

+                       <height>215</height>

+                       <texture border="20">msNowPlayingBg_ipad.png</texture>

+               </control>

+

+               <!-- Music Info -->

+               <control type="group" id="300">

+                       <control type="image" id="801">

+                               <description>Cover image</description>

+                               <posx>20</posx>

+                               <posy>17</posy>

+                               <width>130</width>

+                               <height>130</height>

+                       </control>

+                       <control type="fadelabel" id="802">

+                               <description>Artist label</description>

+                               <posx>160</posx>

+                               <posy>20</posy>

+                               <height>30</height>

+                               <width>325</width>

+                               <align>left</align>

+                               <aligny>center</aligny>

+                               <font>font12_title</font>

+                               <textcolor>FF999999</textcolor>         <!-- 
grey2 -->

+                               <shadowcolor>FF000000</shadowcolor> <!-- black 
-->

+                               <scrollout>false</scrollout>

+                               <pauseatend>2000</pauseatend>

+                       </control>

+                       <control type="fadelabel" id="803">

+                               <description>Title label</description>

+                               <posx>160</posx>

+                               <posy>43</posy>

+                               <height>30</height>

+                               <width>325</width>

+                               <align>left</align>

+                               <aligny>center</aligny>

+                               <font>font13_title</font>

+                               <textcolor>ffffffff</textcolor>         <!-- 
white -->

+                               <shadowcolor>FF000000</shadowcolor> <!-- black 
-->

+                               <scrollout>false</scrollout>

+                               <pauseatend>2000</pauseatend>

+                       </control>

+                       <control type="fadelabel" id="804">

+                               <description>Album Label</description>

+                               <posx>160</posx>

+                               <posy>70</posy>

+                               <height>30</height>

+                               <width>325</width>

+                               <align>left</align>

+                               <aligny>center</aligny>

+                               <font>font12</font>

+                               <textcolor>ffffffff</textcolor>         <!-- 
white -->

+                               <shadowcolor>FF000000</shadowcolor> <!-- black 
-->

+                               <scrollout>false</scrollout>

+                               <pauseatend>2000</pauseatend>

+                       </control>

+                       <control type="fadelabel" id="805">

+                               <description>Next Label</description>

+                               <posx>160</posx>

+                               <posy>100</posy>

+                               <height>30</height>

+                               <width>325</width>

+                               <align>right</align>

+                               <aligny>center</aligny>

+                               <font>font12</font>

+                               <textcolor>7fffffff</textcolor>         <!-- 
grey -->

+                               <shadowcolor>FF000000</shadowcolor> <!-- black 
-->

+                               <scrollout>false</scrollout>

+                               <pauseatend>2000</pauseatend>

+                       </control>

+                       <control type="label" id="810">

+                               <description>Current Time Position 
Label</description>

+                               <posx>160</posx>

+                               <posy>130</posy>

+                               <height>15</height>

+                               <width>60</width>

+                               <label>-</label>

+                               <align>center</align>

+                               <aligny>center</aligny>

+                               <font>font10</font>

+                               <textcolor>ffffffff</textcolor>         <!-- 
white -->

+                               <shadowcolor>FF000000</shadowcolor> <!-- black 
-->

+                               <!-- Only display the start time if there is a 
duration set -->

+                               <visible>Control.IsVisible(812)</visible>

+                       </control>

+                       <control type="slider" id="811">

+                               <description>Seek Slider</description>

+                               <posx>220</posx>

+                               <posy>135</posy>

+                               <width>215</width>

+                               <height>5</height>

+                               <texturefocus>-</texturefocus>

+                               <texturenofocus>-</texturenofocus>

+                               
<texturesliderbar>pmProgressTrackBar.png</texturesliderbar>

+                               
<textureslidernib>pmProgressTrackBarFillStretch.png</textureslidernib>

+                               
<textureslidernibfocus>pmProgressTrackBarFillStretch.png</textureslidernibfocus>

+                               <onup>100</onup>

+                               <ondown>900</ondown>

+                               <!-- Only display the slider if there is a 
duration set -->

+                               <visible>Control.IsVisible(812)</visible>

+                       </control>

+                       <control type="label" id="812">

+                               <description>Duration Label</description>

+                               <posx>435</posx>

+                               <posy>130</posy>

+                               <height>15</height>

+                               <width>60</width>

+                               <label>-</label>

+                               <align>center</align>

+                               <aligny>center</aligny>

+                               <font>font10</font>

+                               <textcolor>ffffffff</textcolor>         <!-- 
white -->

+                               <shadowcolor>FF000000</shadowcolor> <!-- black 
-->

+                               <visible>True</visible>

+                       </control>

+               </control>

+

+               <control type="image">

+                       <description>Background For the controls</description>

+                       <posx>10</posx>

+                       <posy>165</posy>

+                       <width>480</width>

+                       <height>53</height>

+                       <colordiffuse>DDFFFFFF</colordiffuse>

+                       <texture flipy="false" 
border="20,20,20,2">[email protected]</texture>

+               </control>

+

+               <control type="group" id="100">

+                       <posx>25</posx>

+                       <posy>172</posy>

+                       <defaultcontrol always="true">603</defaultcontrol>

+                       <control type="button" id="600">

+                               <description>Previous Button</description>

+                               <posx>0</posx>

+                               <posy>0</posy>

+                               <width>40</width>

+                               <height>40</height>

+                               <label>-</label>

+                               
<texturefocus>control_buttons/tbTransportBack_sel.png</texturefocus>

+                               
<texturenofocus>control_buttons/tbTransportBack.png</texturenofocus>

+                               <onleft>102</onleft>

+                               <onright>101</onright>

+                               <onup>900</onup>

+                               <ondown>622</ondown>

+                       </control>

+                       <!-- Group for the Play Pause Button to allow keyboard 
navigation -->

+                       <control type="group" id="101">

+                               <control type="button" id="601">

+                                       <description>Play Button</description>

+                                       <posx>40</posx>

+                                       <posy>0</posy>

+                                       <width>40</width>

+                                       <height>40</height>

+                                       <label>-</label>

+                                       
<texturefocus>control_buttons/tbPlay_sel.png</texturefocus>

+                                       
<texturenofocus>control_buttons/tbPlay.png</texturenofocus>

+                                       <onleft>600</onleft>

+                                       <onright>603</onright>

+                                       <onup>900</onup>

+                                       <ondown>622</ondown>

+                                       <visible>True</visible>

+                               </control>

+                               <control type="button" id="602">

+                                       <description>Pause Button</description>

+                                       <posx>40</posx>

+                                       <posy>0</posy>

+                                       <width>40</width>

+                                       <height>40</height>

+                                       <label>-</label>

+                                       
<texturefocus>control_buttons/tbPause_sel.png</texturefocus>

+                                       
<texturenofocus>control_buttons/tbPause.png</texturenofocus>

+                                       <onleft>600</onleft>

+                                       <onright>603</onright>

+                                       <onup>900</onup>

+                                       <ondown>622</ondown>

+                                       
<visible>!Control.IsVisible(601)</visible>

+                               </control>

+                       </control>

+                       <control type="button" id="603">

+                               <description>Stop Button</description>

+                               <posx>80</posx>

+                               <posy>0</posy>

+                               <width>40</width>

+                               <height>40</height>

+                               <label>-</label>

+                               
<texturefocus>control_buttons/tbStop_sel.png</texturefocus>

+                               
<texturenofocus>control_buttons/tbStop.png</texturenofocus>

+                               <onleft>101</onleft>

+                               <onright>604</onright>

+                               <onup>900</onup>

+                               <ondown>622</ondown>

+                       </control>

+                       <control type="button" id="604">

+                               <description>Next Button</description>

+                               <posx>120</posx>

+                               <posy>0</posy>

+                               <width>40</width>

+                               <height>40</height>

+                               <label>-</label>

+                               
<texturefocus>control_buttons/tbTransportForward_sel.png</texturefocus>

+                               
<texturenofocus>control_buttons/tbTransportForward.png</texturenofocus>

+                               <onleft>603</onleft>

+                               <onright>104</onright>

+                               <onup>900</onup>

+                               <ondown>622</ondown>

+                       </control>

+                       <!-- Group the Repeat Buttons -->

+                       <control type="group" id="104">

+                               <control type="button" id="605">

+                                       <description>Repeat Button</description>

+                                       <posx>190</posx>

+                                       <posy>10</posy>

+                                       <width>20</width>

+                                       <height>20</height>

+                                       <label>-</label>

+                                       
<texturefocus>control_buttons/[email protected]</texturefocus>

+                                       
<texturenofocus>control_buttons/[email protected]</texturenofocus>

+                                       <onleft>604</onleft>

+                                       <onright>103</onright>

+                                       <onup>900</onup>

+                                       <ondown>622</ondown>

+                               </control>

+                               <control type="button" id="606">

+                                       <description>Repeat Button 
Enabled</description>

+                                       <posx>190</posx>

+                                       <posy>10</posy>

+                                       <width>20</width>

+                                       <height>20</height>

+                                       <label>-</label>

+                                       
<texturefocus>control_buttons/[email protected]</texturefocus>

+                                       
<texturenofocus>control_buttons/[email protected]</texturenofocus>

+                                       <onleft>604</onleft>

+                                       <onright>103</onright>

+                                       <onup>900</onup>

+                                       
<visible>!Control.IsVisible(605)</visible>

+                                       <ondown>622</ondown>

+                               </control>

+                       </control>

+                       <!-- Group the Random Buttons -->

+                       <control type="group" id="103">

+                               <control type="button" id="607">

+                                       <description>Random Button</description>

+                                       <posx>220</posx>

+                                       <posy>10</posy>

+                                       <width>20</width>

+                                       <height>20</height>

+                                       <label>-</label>

+                                       
<texturefocus>control_buttons/[email protected]</texturefocus>

+                                       
<texturenofocus>control_buttons/[email protected]</texturenofocus>

+                                       <onleft>104</onleft>

+                                       <onright>105</onright>

+                                       <onup>900</onup>

+                                       <ondown>622</ondown>

+                               </control>

+                               <control type="button" id="608">

+                                       <description>Random Button 
Enabled</description>

+                                       <posx>220</posx>

+                                       <posy>10</posy>

+                                       <width>20</width>

+                                       <height>20</height>

+                                       <label>-</label>

+                                       
<texturefocus>control_buttons/[email protected]</texturefocus>

+                                       
<texturenofocus>control_buttons/[email protected]</texturenofocus>

+                                       <onleft>104</onleft>

+                                       <onright>105</onright>

+                                       <onup>900</onup>

+                                       <ondown>622</ondown>

+                                       
<visible>!Control.IsVisible(607)</visible>

+                               </control>

+                       </control>

+                       <!-- Group the Crossfade Buttons -->

+                       <control type="group" id="105">

+                               <control type="button" id="609">

+                                       <description>Crossfade 
Button</description>

+                                       <posx>250</posx>

+                                       <posy>10</posy>

+                                       <width>20</width>

+                                       <height>20</height>

+                                       <label>-</label>

+                                       
<texturefocus>control_buttons/[email protected]</texturefocus>

+                                       
<texturenofocus>control_buttons/[email protected]</texturenofocus>

+                                       <onleft>103</onleft>

+                                       <onright>102</onright>

+                                       <onup>900</onup>

+                                       <ondown>622</ondown>

+                               </control>

+                               <control type="button" id="610">

+                                       <description>Crossfade Button 
Enabled</description>

+                                       <posx>250</posx>

+                                       <posy>10</posy>

+                                       <width>20</width>

+                                       <height>20</height>

+                                       <label>-</label>

+                                       
<texturefocus>control_buttons/[email protected]</texturefocus>

+                                       
<texturenofocus>control_buttons/[email protected]</texturenofocus>

+                                       <onleft>103</onleft>

+                                       <onright>102</onright>

+                                       <onup>900</onup>

+                                       <ondown>622</ondown>

+                                       
<visible>!Control.IsVisible(609)</visible>

+                               </control>

+                       </control>

+                       <!-- Group for the Sound and mute Button to allow 
keyboard navigation -->

+                       <control type="group" id="102">

+                               <control type="button" id="620">

+                                       <description>Sound Volume 
Button</description>

+                                       <posx>290</posx>

+                                       <posy>0</posy>

+                                       <width>40</width>

+                                       <height>40</height>

+                                       <label>-</label>

+                                       
<texturefocus>control_buttons/tbUnMute_sel.png</texturefocus>

+                                       
<texturenofocus>control_buttons/tbMute.png</texturenofocus>

+                                       <onleft>105</onleft>

+                                       <onright>600</onright>

+                                       <onup>900</onup>

+                                       <ondown>622</ondown>

+                                       <visible>True</visible>

+                               </control>

+                               <control type="button" id="621">

+                                       <description>Sound Mute 
Button</description>

+                                       <posx>290</posx>

+                                       <posy>0</posy>

+                                       <width>40</width>

+                                       <height>40</height>

+                                       <label>-</label>

+                                       
<texturefocus>control_buttons/tbMute_sel.png</texturefocus>

+                                       
<texturenofocus>control_buttons/tbMute_on.png</texturenofocus>

+                                       <onleft>105</onleft>

+                                       <onright>600</onright>

+                                       <onup>900</onup>

+                                       <ondown>622</ondown>

+                                       
<visible>!Control.IsVisible(620)</visible>

+                               </control>

+                       </control>

+                       <control type="slider" id="622">

+                               <description>Volume Slider</description>

+                               <posx>330</posx>

+                               <posy>10</posy>

+                               <width>120</width>

+                               <height>20</height>

+                               <texturefocus>-</texturefocus>

+                               <texturenofocus>-</texturenofocus>

+                               
<texturesliderbar>control_buttons/snAutoBtn.png</texturesliderbar>

+                               
<textureslidernib>control_buttons/tbVolumeScrubber_dis.png</textureslidernib>

+                               
<textureslidernibfocus>control_buttons/tbVolumeScrubber.png</textureslidernibfocus>

+                               <onup>100</onup>

+                               <ondown>900</ondown>

+                       </control>

+               </control>

+               <control type="button" id="900">

+                       <description>Close Window button</description>

+                       <posx>430</posx>

+                       <posy>5</posy>

+                       <width>52</width>

+                       <height>26</height>

+                       <label>-</label>

+                       <font>-</font>

+                       <onclick>back</onclick>

+                       <texturefocus>DialogCloseButton-focus.png</texturefocus>

+                       <texturenofocus>DialogCloseButton.png</texturenofocus>

+                       <onleft>100</onleft>

+                       <onright>100</onright>

+                       <onup>622</onup>

+                       <ondown>100</ondown>

+                       <visible>system.getbool(input.enablemouse)</visible>

+               </control>

+       </controls>

+</window>

diff --git 
a/script.sonos/resources/skins/Default/720p/script-sonos-notif-popup.xml 
b/script.sonos/resources/skins/Default/720p/script-sonos-notif-popup.xml
index 2d68569..51d47a4 100644
--- a/script.sonos/resources/skins/Default/720p/script-sonos-notif-popup.xml
+++ b/script.sonos/resources/skins/Default/720p/script-sonos-notif-popup.xml
@@ -1,77 +1,77 @@
-<?xml version="1.0" encoding="utf-8"?>
-<window id="3001">
-       <animation effect="fade" start="0" end="100" 
time="200">WindowOpen</animation>
-       <animation effect="fade" start="100" end="0" 
time="200">WindowClose</animation>
-       <coordinates>
-               <posx>840</posx>
-               <posy>620</posy>
-       </coordinates>
-       <controls>
-               <control type="group">
-                       <animation effect="slide" start="0,0" end="-190,0" 
time="200" condition="Window.IsVisible(BusyDialog)">conditional</animation>
-                       <control type="image">
-                               <posx>0</posx>
-                               <posy>0</posy>
-                               <width>420</width>
-                               <height>90</height>
-                               <colordiffuse>CCFFFFFF</colordiffuse>
-                               <texture 
border="12">[email protected]</texture>
-                       </control>
-                       <control type="image">
-                               <posx>5</posx>
-                               <posy>5</posy>
-                               <width>410</width>
-                               <height>80</height>
-                               <texture 
border="20">msNowPlayingBg_ipad.png</texture>
-                       </control>
-                       <control type="image" id="400">
-                               <description>avatar</description>
-                               <posx>20</posx>
-                               <posy>10</posy>
-                               <width>70</width>
-                               <height>70</height>
-                               <aspectratio>keep</aspectratio>
-                               <texture>DefaultFile.png</texture>
-                       </control>
-                       <control type="fadelabel" id="401">
-                               <description>Line 1 Label</description>
-                               <posx>95</posx>
-                               <posy>15</posy>
-                               <width>310</width>
-                               <height>18</height>
-                               <font>font12_title</font>
-                               <textcolor>ffffffff</textcolor>         <!-- 
white -->
-                               <align>left</align>
-                               <aligny>center</aligny>
-                               <scrollout>false</scrollout>
-                               <pauseatend>2000</pauseatend>
-                       </control>
-                       <control type="fadelabel" id="402">
-                               <description>Line 2 Label</description>
-                               <posx>95</posx>
-                               <posy>35</posy>
-                               <width>310</width>
-                               <height>20</height>
-                               <font>font12_title</font>
-                               <textcolor>7fffffff</textcolor>         <!-- 
grey -->
-                               <align>left</align>
-                               <aligny>center</aligny>
-                               <scrollout>false</scrollout>
-                               <pauseatend>2000</pauseatend>
-                       </control>
-                       <control type="fadelabel" id="403">
-                               <description>Line 3 Label</description>
-                               <posx>95</posx>
-                               <posy>55</posy>
-                               <width>310</width>
-                               <height>20</height>
-                               <font>font12_title</font>
-                               <textcolor>7fffffff</textcolor>         <!-- 
grey -->
-                               <align>left</align>
-                               <aligny>center</aligny>
-                               <scrollout>false</scrollout>
-                               <pauseatend>2000</pauseatend>
-                       </control>
-               </control>
-       </controls>
-</window>
+<?xml version="1.0" encoding="utf-8"?>

+<window id="3001">

+       <animation effect="fade" start="0" end="100" 
time="200">WindowOpen</animation>

+       <animation effect="fade" start="100" end="0" 
time="200">WindowClose</animation>

+       <coordinates>

+               <posx>840</posx>

+               <posy>620</posy>

+       </coordinates>

+       <controls>

+               <control type="group">

+                       <animation effect="slide" start="0,0" end="-190,0" 
time="200" condition="Window.IsVisible(BusyDialog)">conditional</animation>

+                       <control type="image">

+                               <posx>0</posx>

+                               <posy>0</posy>

+                               <width>420</width>

+                               <height>90</height>

+                               <colordiffuse>CCFFFFFF</colordiffuse>

+                               <texture 
border="12">[email protected]</texture>

+                       </control>

+                       <control type="image">

+                               <posx>5</posx>

+                               <posy>5</posy>

+                               <width>410</width>

+                               <height>80</height>

+                               <texture 
border="20">msNowPlayingBg_ipad.png</texture>

+                       </control>

+                       <control type="image" id="400">

+                               <description>avatar</description>

+                               <posx>20</posx>

+                               <posy>10</posy>

+                               <width>70</width>

+                               <height>70</height>

+                               <aspectratio>keep</aspectratio>

+                               <texture>DefaultFile.png</texture>

+                       </control>

+                       <control type="fadelabel" id="401">

+                               <description>Line 1 Label</description>

+                               <posx>95</posx>

+                               <posy>15</posy>

+                               <width>310</width>

+                               <height>18</height>

+                               <font>font12_title</font>

+                               <textcolor>ffffffff</textcolor>         <!-- 
white -->

+                               <align>left</align>

+                               <aligny>center</aligny>

+                               <scrollout>false</scrollout>

+                               <pauseatend>2000</pauseatend>

+                       </control>

+                       <control type="fadelabel" id="402">

+                               <description>Line 2 Label</description>

+                               <posx>95</posx>

+                               <posy>35</posy>

+                               <width>310</width>

+                               <height>20</height>

+                               <font>font12_title</font>

+                               <textcolor>7fffffff</textcolor>         <!-- 
grey -->

+                               <align>left</align>

+                               <aligny>center</aligny>

+                               <scrollout>false</scrollout>

+                               <pauseatend>2000</pauseatend>

+                       </control>

+                       <control type="fadelabel" id="403">

+                               <description>Line 3 Label</description>

+                               <posx>95</posx>

+                               <posy>55</posy>

+                               <width>310</width>

+                               <height>20</height>

+                               <font>font12_title</font>

+                               <textcolor>7fffffff</textcolor>         <!-- 
grey -->

+                               <align>left</align>

+                               <aligny>center</aligny>

+                               <scrollout>false</scrollout>

+                               <pauseatend>2000</pauseatend>

+                       </control>

+               </control>

+       </controls>

+</window>

diff --git a/script.sonos/service.py b/script.sonos/service.py
index 02f9498..41bdfec 100644
--- a/script.sonos/service.py
+++ b/script.sonos/service.py
@@ -175,27 +175,34 @@ class SonosAutoPause():
     # Check if the Sonos system should be paused or resumed
     def check(self):
         if Settings.autoPauseSonos() and not Settings.linkAudioWithSonos():
-            # Check to see if something has started playing
-            if xbmc.Player().isPlaying():
-                # If this is a change in play state since the last time we 
checked
-                if self.xbmcPlayState is False:
-                    log("SonosAutoPause: Automatically pausing Sonos")
-                    self.xbmcPlayState = True
-                    # Pause the sonos if it is playing
-                    if self._isSonosPlaying():
-                        self.sonosDevice.pause()
-                        self.autoStopped = True
-                        self.resumeCountdown = Settings.autoResumeSonos()
-            else:
-                self.xbmcPlayState = False
-                if Settings.autoResumeSonos() > 0 and self.autoStopped:
-                    if self.resumeCountdown > 0:
-                        self.resumeCountdown = self.resumeCountdown - 1
-                    else:
-                        log("SonosAutoPause: Automatically resuming Sonos")
-                        self.sonosDevice.play()
-                        self.autoStopped = False
-                        self.resumeCountdown = Settings.autoResumeSonos()
+            try:
+                # Check to see if something has started playing
+                if xbmc.Player().isPlaying():
+                    # If this is a change in play state since the last time we 
checked
+                    if self.xbmcPlayState is False:
+                        log("SonosAutoPause: Automatically pausing Sonos")
+                        self.xbmcPlayState = True
+                        # Pause the sonos if it is playing
+                        if self._isSonosPlaying():
+                            self.sonosDevice.pause()
+                            self.autoStopped = True
+                            self.resumeCountdown = Settings.autoResumeSonos()
+                else:
+                    self.xbmcPlayState = False
+                    if Settings.autoResumeSonos() > 0 and self.autoStopped:
+                        if self.resumeCountdown > 0:
+                            self.resumeCountdown = self.resumeCountdown - 1
+                        else:
+                            log("SonosAutoPause: Automatically resuming Sonos")
+                            self.sonosDevice.play()
+                            self.autoStopped = False
+                            self.resumeCountdown = Settings.autoResumeSonos()
+            except:
+                # If we fail to stop the speaker playing, it may be because
+                # there is a network problem or the speaker is powered down
+                # So we just continue after logging the error
+                log("SonosAutoPause: Error from speaker %s" % 
Settings.getIPAddress())
+                log("SonosAutoPause: %s" % traceback.format_exc())
 
     # Works out if the Sonos system is playing
     def _isSonosPlaying(self):

-----------------------------------------------------------------------

Summary of changes:
 script.sonos/addon.xml                             |    3 +-
 script.sonos/changelog.txt                         |    3 +
 script.sonos/discovery.py                          |   20 +-
 script.sonos/resources/language/English/strings.po |   14 +-
 script.sonos/resources/lib/mocksonos.py            |  206 +++---
 .../resources/lib/soco/event_structures.py         |  630 ++++++++--------
 script.sonos/resources/lib/sonos.py                |  402 +++++-----
 script.sonos/resources/settings.xml                |   72 +-
 .../skins/Default/720p/script-sonos-controller.xml |  786 ++++++++++----------
 .../Default/720p/script-sonos-notif-popup.xml      |  154 ++--
 script.sonos/service.py                            |   49 +-
 11 files changed, 1189 insertions(+), 1150 deletions(-)


hooks/post-receive
-- 
Scripts

------------------------------------------------------------------------------
Slashdot TV.  
Video for Nerds.  Stuff that matters.
http://tv.slashdot.org/
_______________________________________________
Xbmc-addons mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/xbmc-addons

Reply via email to