Skip to content

Commit

Permalink
Release v0.4.0
Browse files Browse the repository at this point in the history
  • Loading branch information
jcass77 committed Sep 20, 2017
2 parents df9935b + 390d1de commit dd290c0
Show file tree
Hide file tree
Showing 11 changed files with 79 additions and 19 deletions.
9 changes: 8 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
Changelog
=========

v0.4.0 (Sep 20, 2017)
---------------------

- Update documentation to refer to new 'Pandora Plus' subscription model instead of the old 'Pandora One'.
- Update troubleshooting guide with more workarounds for cross-signed certificates using OpenSSL < 1.0.2.
- Allow station URI's to be added to playlists and the Mopidy tracklist. (Addresses: `#58 <https://github.com/rectalogic/mopidy-pandora/issues/58>`_).

v0.3.0 (Jul 8, 2016)
--------------------

Expand Down Expand Up @@ -47,7 +54,7 @@ v0.2.0 (Jan 26, 2016)
- Station lists are now cached which speeds up startup and browsing of the list of stations dramatically. Configuration
parameter ``cache_time_to_live`` can be used to specify when cache items should expire and be refreshed (in seconds).
- Force Mopidy to stop when skip limit is exceeded (workaround for `#1221 <https://github.com/mopidy/mopidy/issues/1221>`_).
- Now plays advertisements which should prevent non-Pandora One accounts from being locked after extended use.
- Now plays advertisements which should prevent non-Pandora Plus accounts from being locked after extended use.
- Tracks are now played in ``consume`` instead of ``repeat`` mode. This is more in line with how Pandora deals with
track playback. It also avoids infinite loops on unplayable tracks, which is still an issue in Mopidy 1.1.2.
- Station sort order now defaults to alphabetical. This makes it easier to find stations if the user profile contains
Expand Down
10 changes: 3 additions & 7 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ Mopidy-Pandora
:target: https://pypi.python.org/pypi/Mopidy-Pandora/
:alt: Latest PyPI version

.. image:: https://img.shields.io/pypi/dm/Mopidy-Pandora.svg?style=flat
:target: https://pypi.python.org/pypi/Mopidy-Pandora/
:alt: Number of PyPI downloads

.. image:: https://img.shields.io/travis/rectalogic/mopidy-pandora/develop.svg?style=flat
:target: https://travis-ci.org/rectalogic/mopidy-pandora
:alt: Travis CI build status
Expand All @@ -24,7 +20,7 @@ Mopidy-Pandora
Features
========

- Support for both Pandora One and ad-supported free accounts.
- Support for both Pandora Plus and ad-supported free accounts.
- Add ratings to tracks (thumbs up, thumbs down, sleep, etc.).
- Bookmark songs or artists.
- Browse and add genre stations.
Expand Down Expand Up @@ -53,7 +49,7 @@ idea. And not recommended.
Dependencies
============

- Requires a Pandora user account. Users with a Pandora One subscription will have access to the higher quality 192 Kbps
- Requires a Pandora user account. Users with a Pandora Plus subscription will have access to the higher quality 192 Kbps
audio stream. Free accounts will play advertisements.

- ``pydora`` >= 1.7.3. The Python Pandora API Client. The package is available as ``pydora`` on PyPI.
Expand Down Expand Up @@ -96,7 +92,7 @@ The following configuration values are available:
- ``pandora/enabled``: If the Pandora extension should be enabled or not. Defaults to ``true``.

- ``pandora/api_host``: Which of the JSON API `endpoints <http://6xq.net/pandora-apidoc/json/>`_ to use. Note that
the endpoints are different for Pandora One and free accounts (details in the link provided).
the endpoints are different for Pandora Plus and free accounts (details in the link provided).

- ``pandora/partner_*`` related values: The `credentials <http://6xq.net/playground/pandora-apidoc/json/partners/#partners>`_
to use for the Pandora API entry point. You *must* provide these values based on your device preferences.
Expand Down
13 changes: 11 additions & 2 deletions docs/troubleshooting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,17 @@ If you are behind a proxy, you may have to configure some of Mopidy's
There is a `known problem <https://lukasa.co.uk/2015/04/Certifi_State_Of_Union/>`_
with cross-signed certificates and versions of OpenSSL prior to 1.0.2. If you
are running Mopidy on a Raspberry Pi it is likely that you still have an older
version of OpenSSL installed. You could try upgrading OpenSSL, or as a
workaround, revert to an older version of certifi with ``pip install certifi==2015.4.28``.
version of OpenSSL installed. `certifi/python-certifi#26 <https://github.com/certifi/python-certifi/issues/26>`_ lists
several workarounds. In order of preference, you could try to:

- Upgrade OpenSSL >= 1.0.2

- Run ``python -c 'import certifi; print certifi.old_where()'``, and assign the output of this command to
the ``REQUESTS_CA_BUNDLE`` environment variable. If running Mopidy as a service, you might have to edit
``/etc/init.d/mopidy`` so that ``start-stop-daemon`` calls a custom script that sets the variable and wraps
``/usr/local/bin/mopidy``.

- Revert to an older version of certifi with ``pip install certifi==2015.4.28``.


5. Run pydora directly
Expand Down
2 changes: 1 addition & 1 deletion mopidy_pandora/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from mopidy import config, ext

__version__ = '0.3.0'
__version__ = '0.4.0'


class Extension(ext.Extension):
Expand Down
1 change: 1 addition & 0 deletions mopidy_pandora/frontend.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ def __eq__(self, other):
def __lt__(self, other):
return self.ratio < other.ratio


EventMarker = namedtuple('EventMarker', 'event, uri, time')


Expand Down
12 changes: 10 additions & 2 deletions mopidy_pandora/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ def browse(self, uri):

def lookup(self, uri):
pandora_uri = PandoraUri.factory(uri)
logger.info('Looking up Pandora {} {}...'.format(pandora_uri.uri_type, pandora_uri.uri))
if isinstance(pandora_uri, SearchUri):
# Create the station first so that it can be browsed.
station_uri = self._create_station_for_token(pandora_uri.token)
Expand All @@ -62,15 +63,16 @@ def lookup(self, uri):
# Recursive call to look up first track in station that was searched for.
return self.lookup(track.uri)

track_kwargs = {'uri': uri}
(album_kwargs, artist_kwargs) = {}, {}

if isinstance(pandora_uri, TrackUri):
try:
track = self.lookup_pandora_track(uri)
except KeyError:
logger.exception("Failed to lookup Pandora URI '{}'.".format(uri))
return []
else:
track_kwargs = {'uri': uri}
(album_kwargs, artist_kwargs) = {}, {}
# TODO: Album.images has been deprecated in Mopidy 1.2. Remove this code when all frontends have been
# updated to make use of the newer LibraryController.get_images()
images = self.get_images([uri])[uri]
Expand All @@ -97,6 +99,12 @@ def lookup(self, uri):
pass
artist_kwargs['name'] = track.artist_name
album_kwargs['name'] = track.album_name
elif isinstance(pandora_uri, StationUri):
station = self.backend.api.get_station(pandora_uri.station_id)
album_kwargs = {'images': [station.art_url]}
track_kwargs['name'] = station.name
artist_kwargs['name'] = 'Pandora Station'
album_kwargs['name'] = ', '.join(station.genre)
else:
raise ValueError('Unexpected type to perform Pandora track lookup: {}.'.format(pandora_uri.uri_type))

Expand Down
10 changes: 9 additions & 1 deletion mopidy_pandora/playback.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import requests

from mopidy_pandora import listener

from mopidy_pandora.uri import PandoraUri, StationUri

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -57,6 +57,14 @@ def change_track(self, track):
if track.uri is None:
logger.warning("No URI for Pandora track '{}'. Track cannot be played.".format(track))
return False

pandora_uri = PandoraUri.factory(track.uri)
if isinstance(pandora_uri, StationUri):
# Change to first track in station playlist.
logger.warning('Cannot play Pandora stations directly. Retrieving tracks for station with ID: {}...'
.format(pandora_uri.station_id))
self.backend.end_of_tracklist_reached(station_id=pandora_uri.station_id, auto_play=True)
return False
try:
self._trigger_track_changing(track)
self.check_skip_limit()
Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def get_version(filename):


class Tox(test):
user_options = [(b'tox-args=', b'a', "Arguments to pass to tox")]
user_options = [(b'tox-args=', b'a', 'Arguments to pass to tox')]

def initialize_options(self):
test.initialize_options(self)
Expand All @@ -35,6 +35,7 @@ def run_tests(self):
errno = tox.cmdline(args=args)
sys.exit(errno)


setup(
name='Mopidy-Pandora',
version=get_version('mopidy_pandora/__init__.py'),
Expand Down
4 changes: 3 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
MOCK_STATION_TOKEN = '0000000000000000001'
MOCK_STATION_DETAIL_URL = 'http://mockup.com/station/detail_url?...'
MOCK_STATION_ART_URL = 'http://mockup.com/station/art_url?...'
MOCK_STATION_GENRE = 'Genre Mock'

MOCK_STATION_LIST_CHECKSUM = 'aa00aa00aa00aa00aa00aa00aa00aa00'

Expand Down Expand Up @@ -105,7 +106,8 @@ def station_result_mock():
'stationDetailUrl': MOCK_STATION_DETAIL_URL,
'artUrl': MOCK_STATION_ART_URL,
'stationToken': MOCK_STATION_TOKEN,
'stationName': MOCK_STATION_NAME},
'stationName': MOCK_STATION_NAME,
'genre': [MOCK_STATION_GENRE]},
}

return mock_result
Expand Down
23 changes: 20 additions & 3 deletions tests/test_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,8 @@ def test_lookup_of_invalid_uri_type(config, caplog):
with pytest.raises(ValueError):
backend = conftest.get_backend(config)

backend.library.lookup('pandora:station:mock_id:mock_token')
assert 'Unexpected type to perform Pandora track lookup: station.' in caplog.text()
backend.library.lookup('pandora:genre:mock_name')
assert 'Unexpected type to perform Pandora track lookup: genre.' in caplog.text()


def test_lookup_of_ad_uri(config, ad_item_mock):
Expand Down Expand Up @@ -182,13 +182,30 @@ def test_lookup_of_search_uri(config, playlist_item_mock):
backend.library.pandora_track_cache[track_uri.uri] = TrackCacheItem(mock.Mock(spec=models.Ref.track),
playlist_item_mock)

results = backend.library.lookup("pandora:search:S1234567")
results = backend.library.lookup('pandora:search:S1234567')
# Make sure a station is created for the search URI first
assert create_station_mock.called
# Check that the first track to be played is returned correctly.
assert results[0].uri == track_uri.uri


def test_lookup_of_station_uri(config):
with mock.patch.object(MopidyAPIClient, 'get_station', conftest.get_station_mock):
with mock.patch.object(APIClient, 'get_station_list', conftest.get_station_list_mock):
backend = conftest.get_backend(config)

station_uri = PandoraUri.factory(conftest.station_mock())
results = backend.library.lookup(station_uri.uri)
assert len(results) == 1

track = results[0]
assert track.uri == station_uri.uri
assert next(iter(track.album.images)) == conftest.MOCK_STATION_ART_URL
assert track.name == conftest.MOCK_STATION_NAME
assert next(iter(track.artists)).name == 'Pandora Station'
assert track.album.name == conftest.MOCK_STATION_GENRE


def test_lookup_of_track_uri(config, playlist_item_mock):
backend = conftest.get_backend(config)

Expand Down
11 changes: 11 additions & 0 deletions tests/test_playback.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,17 @@ def test_change_track_fetches_next_track_if_unplayable(provider, playlist_item_m
assert 'Error changing Pandora track' in caplog.text()


def test_change_track_fetches_next_track_if_station_uri(provider, caplog):
station = PandoraUri.factory(conftest.station_mock())

provider.backend._trigger_next_track_available = mock.PropertyMock()

assert provider.change_track(station) is False
assert 'Cannot play Pandora stations directly. Retrieving tracks for station with ID: {}...'.format(
station.station_id) in caplog.text()
assert provider.backend._trigger_next_track_available.called


def test_change_track_skips_if_no_track_uri(provider):
track = models.Track(uri=None)

Expand Down

0 comments on commit dd290c0

Please sign in to comment.