Skip to content

Commit

Permalink
Merge pull request #198 from flavio/sonos_playlist_write_operations
Browse files Browse the repository at this point in the history
Sonos playlist write operations
  • Loading branch information
Kenneth Nielsen committed Sep 3, 2014
2 parents d333022 + 336adb9 commit dcb45ed
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 1 deletion.
60 changes: 60 additions & 0 deletions soco/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,9 @@ class SoCo(_SocoSingletonBase):
get_favorite_radio_shows -- Get favorite radio shows from Sonos'
Radio app.
get_favorite_radio_stations -- Get favorite radio stations.
create_sonos_playlist -- Creates a new Sonos' playlist
add_item_to_sonos_playlist -- Adds a queueable item to a Sonos'
playlist
Properties::
Expand Down Expand Up @@ -1541,6 +1544,63 @@ def _update_album_art_to_full_uri(self, item):
item.album_art_uri = 'http://' + self.ip_address + ':1400' +\
item.album_art_uri

def create_sonos_playlist(self, title):
""" Create a new Sonos' playlist .
:params title: Name of the playlist
:returns: An instance of
:py:class:`~.soco.data_structures.MLSonosPlaylist`
"""
response = self.avTransport.CreateSavedQueue([
('InstanceID', 0),
('Title', title),
('EnqueuedURI', ''),
('EnqueuedURIMetaData', ''),
])

obj_id = response['AssignedObjectID'].split(':', 2)[1]
uri = "file:///jffs/settings/savedqueues.rsq#{0}".format(obj_id)

return MLSonosPlaylist(uri, title, 'SQ:')

def add_item_to_sonos_playlist(self, queueable_item, sonos_playlist):
""" Adds a queueable item to a Sonos' playlist
:param queueable_item: the item to add to the Sonos' playlist
:param sonos_playlist: the Sonos' playlist to which the item should
be added
"""
# Check if the required attributes are there
for attribute in ['didl_metadata', 'uri']:
if not hasattr(queueable_item, attribute):
message = 'queueable_item has no attribute {0}'.\
format(attribute)
raise AttributeError(message)
# Get the metadata
try:
metadata = XML.tostring(queueable_item.didl_metadata)
except CannotCreateDIDLMetadata as exception:
message = ('The queueable item could not be enqueued, because it '
'raised a CannotCreateDIDLMetadata exception with the '
'following message:\n{0}').format(str(exception))
raise ValueError(message)
if isinstance(metadata, str):
metadata = metadata.encode('utf-8')

response, _ = self._music_lib_search(sonos_playlist.item_id, 0, 1)
update_id = response['UpdateID']
self.avTransport.AddURIToSavedQueue([
('InstanceID', 0),
('UpdateID', update_id),
('ObjectID', sonos_playlist.item_id),
('EnqueuedURI', queueable_item.uri),
('EnqueuedURIMetaData', metadata),
('AddAtIndex', 4294967295) # this field has always this value, we
# do not known the meaning of this
# "magic" number.
])

# definition section

Expand Down
6 changes: 6 additions & 0 deletions soco/data_structures.py
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,12 @@ class MLSonosPlaylist(MusicLibraryItem):

item_class = 'object.container.playlistContainer'

@property
def item_id(self): # pylint: disable=C0103
"""Returns the id."""
out = 'SQ:' + super(MLSonosPlaylist, self).item_id
return out


class MLShare(MusicLibraryItem):
"""Class that represents a music library share.
Expand Down
44 changes: 44 additions & 0 deletions unittest/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from soco import SoCo
from soco.groups import ZoneGroup
from soco.xml import XML

IP_ADDR = '192.168.1.101'

Expand Down Expand Up @@ -231,6 +232,49 @@ def test_switch_to_tv(self, moco_zgs):
('CurrentURIMetaData', '')]
)

def test_create_sonos_playlist(self, moco):
playlist_name = "cool music"
playlist_id = 1
moco.avTransport.CreateSavedQueue.return_value = {
'AssignedObjectID': 'SQ:{0}'.format(playlist_id)
}
playlist = moco.create_sonos_playlist(playlist_name)
moco.avTransport.CreateSavedQueue.assert_called_once_with(
[('InstanceID', 0),
('Title', playlist_name),
('EnqueuedURI', ''),
('EnqueuedURIMetaData', '')]
)
assert playlist.title == playlist_name
expected_uri = "file:///jffs/settings/savedqueues.rsq#{0}".format(
playlist_id)
assert playlist.uri == expected_uri
assert playlist.parent_id == "SQ:"

def test_add_item_to_sonos_playlist(self, moco):
playlist = mock.Mock()
playlist.item_id = 7

track = mock.Mock()
track.uri = 'fake_uri'
track.didl_metadata = XML.Element('a')

update_id = 100
moco._music_lib_search = mock.Mock(return_value=(
{'UpdateID': update_id},
None))

moco.add_item_to_sonos_playlist(track, playlist)
moco._music_lib_search.assert_called_once_with(playlist.item_id, 0, 1)
moco.avTransport.AddURIToSavedQueue.assert_called_once_with(
[('InstanceID', 0),
('UpdateID', update_id),
('ObjectID', playlist.item_id),
('EnqueuedURI', track.uri),
('EnqueuedURIMetaData', XML.tostring(track.didl_metadata)),
('AddAtIndex', 4294967295)]
)


class TestRenderingControl:

Expand Down
2 changes: 1 addition & 1 deletion unittest/test_data_structures.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ def test_mlsonosplaylist():

# Run tests on inherited methods and attributes
content = {'uri': uri, 'title': TITLE, 'parent_id': 'SQ:'}
common_tests('object.container.playlistContainer', '13 title: Koop',
common_tests('object.container.playlistContainer', 'SQ:13 title: Koop',
playlist, content, SONOS_PLAYLIST_XML, SONOS_PLAYLIST_DICT)


Expand Down

0 comments on commit dcb45ed

Please sign in to comment.