Skip to content

Commit

Permalink
Merge pull request #491 from DPH/force_stream
Browse files Browse the repository at this point in the history
allow choice of how to play streams in play_uri
  • Loading branch information
Kenneth Nielsen committed Apr 30, 2017
2 parents c7fa6d6 + f916fa7 commit 06a5e75
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 21 deletions.
69 changes: 52 additions & 17 deletions soco/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -487,27 +487,61 @@ def play(self):
])

@only_on_master
def play_uri(self, uri='', meta='', title='', start=True):
"""Play a given stream. Pauses the queue. If there is no metadata
passed in and there is a title set then a metadata object will be
created. This is often the case if you have a custom stream, it will
need at least the title in the metadata in order to play.
# pylint: disable=too-many-arguments
def play_uri(self, uri='', meta='', title='', start=True,
force_radio=False):
"""Play a URI
.. note:: A change in Sonos® (as of at least version 6.4.2) means that
the devices no longer accepts ordinary "http://" and "https://"
URIs. This method automatically replaces these prefixes with the
one that Sonos® expects: "x-rincon-mp3radio://".
Playing a URI will replace what was playing with the stream given by
the URI. For some streams at least a title is required as metadata.
This can be provided using the `meta` argument or the `title` argument.
If the `title` argument is provided minimal metadata will be generated.
If `meta` argument is provided the `title` argument is ignored.
Args:
uri (str): URI of the stream to be played.
meta (str): The track metadata to show in the player, DIDL format.
title (str): The track title to show in the player
start (bool): If the URI that has been set should start playing
convert_internet_uris (bool): FIXME
meta (str): The metadata to show in the player, DIDL format.
title (str): The title to show in the player (if no meta).
start (bool): If the URI that has been set should start playing.
force_radio (bool): forces a uri to play as a radio stream.
Raises:
SoCoException: (or a subclass) upon errors.
On a Sonos controller music is shown with one of the following display
formats and controls:
* Radio format: Shows the name of the radio station and other available
data. No seek, next, previous, or voting capability.
Examples: TuneIn, radioPup
* Smart Radio: Shows track name, artist, and album. Limited seek, next
and sometimes voting capability depending on the Music Service.
Examples: Amazon Prime Stations, Pandora Radio Stations.
* Track format: Shows track name, artist, and album the same as when
playing from a queue. Full seek, next and previous capabilities.
Examples: Spotify, Napster, Rhapsody.
How it is displayed is determined by the URI prefix:
`x-sonosapi-stream:`, `x-sonosapi-radio:`, `x-rincon-mp3radio:`,
`hls-radio:` default to radio or smart radio format depending on the
stream. Others default to track format: `x-file-cifs:`, `aac:`,
`http:`, `https:`, `x-sonos-spotify:` (used by Spotify),
`x-sonosapi-hls-static:` (Amazon Prime),
`x-sonos-http:` (Google Play & Napster).
Some URIs that default to track format could be radio streams,
typically `http:`, `https:` or `aac:`.
To force display and controls to Radio format set `force_radio=True`
.. note:: Other URI prefixes exist but are less common.
If you have information on these please add to this doc string.
.. note:: A change in Sonos® (as of at least version 6.4.2) means that
the devices no longer accepts ordinary `http:` and `https:` URIs for
radio stations. This method has the option to replaces these
prefixes with the one that Sonos® expects: `x-rincon-mp3radio:` by
using the "force_radio=True" parameter.
A few streams may fail if not forced to to Radio format.
"""
if meta == '' and title != '':
meta_template = '<DIDL-Lite xmlns:dc="http://purl.org/dc/elements'\
Expand All @@ -523,10 +557,11 @@ def play_uri(self, uri='', meta='', title='', start=True):
# Radio stations need to have at least a title to play
meta = meta_template.format(title=title, service=tunein_service)

for prefix in ('http://', 'https://'):
if uri.startswith(prefix):
# Replace only the first instance
uri = uri.replace(prefix, 'x-rincon-mp3radio://', 1)
# change uri prefix to force radio style display and commands
if force_radio:
colon = uri.find(':')
if colon > 0:
uri = 'x-rincon-mp3radio{0}'.format(uri[colon:])

self.avTransport.SetAVTransportURI([
('InstanceID', 0),
Expand Down
29 changes: 25 additions & 4 deletions tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,18 +350,39 @@ def test_soco_play(self, moco):
[('InstanceID', 0), ('Speed', 1)]
)

# Test that in internet uris (that starts with http:// or https:// the
# prefic is replaced with x-rincon-mp3radion://
# Test that uris are forced to Radio style display and controls when
# force_radio is True prefix is replaced with "x-rincon-mp3radion://"
# first set of test with no forcing, second set with force_radio=True

# No forcing
@pytest.mark.parametrize("uri_in, uri_passed", [
('x-file-cifs://server/MyNiceRing.mp3',
'x-file-cifs://server/MyNiceRing.mp3'),
('http://archive.org/download/TenD2005-07-16t_64kb.mp3',
'http://archive.org/download/TenD2005-07-16t_64kb.mp3'),
('x-sonosapi-radio:station%3a%3aps.147077612?sid=203&flags=76&sn=2',
'x-sonosapi-radio:station%3a%3aps.147077612?sid=203&flags=76&sn=2'),
])
def test_soco_play_uri(self, moco, uri_in, uri_passed):
moco.play_uri(uri_in)
moco.avTransport.SetAVTransportURI.assert_called_once_with([
('InstanceID', 0),
('CurrentURI', uri_passed),
('CurrentURIMetaData', '')
])
moco.avTransport.reset_mock()

# with force_radio=True
@pytest.mark.parametrize("uri_in, uri_passed", [
('http://archive.org/download/TenD2005-07-16t_64kb.mp3',
'x-rincon-mp3radio://archive.org/download/TenD2005-07-16t_64kb.mp3'),
('https://archive.org/download/TenD2005-07-16t_64kb.mp3',
'x-rincon-mp3radio://archive.org/download/TenD2005-07-16t_64kb.mp3'),
('aac://icy-e-bz-04-cr.sharp-stream.com/magic1054.aac?amsparams=playerid:BMUK_tunein;skey:1483570441&awparams=loggedin:false',
'x-rincon-mp3radio://icy-e-bz-04-cr.sharp-stream.com/magic1054.aac?amsparams=playerid:BMUK_tunein;skey:1483570441&awparams=loggedin:false')
])
def test_soco_play_uri(self, moco, uri_in, uri_passed):
moco.play_uri(uri_in)
def test_soco_play_uri_force_radio(self, moco, uri_in, uri_passed):
moco.play_uri(uri_in, force_radio=True)
moco.avTransport.SetAVTransportURI.assert_called_once_with([
('InstanceID', 0),
('CurrentURI', uri_passed),
Expand Down

0 comments on commit 06a5e75

Please sign in to comment.