Skip to content

Commit 448e90a

Browse files
committed
Release v2.1.0
2 parents ba773a0 + 353edea commit 448e90a

16 files changed

+235
-147
lines changed

.coveragerc

-5
This file was deleted.

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
*.egg-info
22
*.pyc
33
*.swp
4+
.cache/
45
.coverage
56
.tox/
67
MANIFEST

.travis.yml

+12-7
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,25 @@
1+
sudo: false
2+
13
language: python
24

35
python:
46
- "2.7_with_system_site_packages"
57

8+
addons:
9+
apt:
10+
sources:
11+
- mopidy-stable
12+
packages:
13+
- libffi-dev
14+
- libspotify-dev
15+
- mopidy
16+
- python-all-dev
17+
618
env:
719
- TOX_ENV=py27
820
- TOX_ENV=flake8
921

1022
install:
11-
- "wget -O - http://apt.mopidy.com/mopidy.gpg | sudo apt-key add -"
12-
- "sudo wget -O /etc/apt/sources.list.d/mopidy.list http://apt.mopidy.com/mopidy.list"
13-
- "sudo apt-get update || true"
14-
- "sudo apt-get install mopidy-spotify"
15-
# Note: The next line can be removed when pyspotify2 has been packaged as a
16-
# Debian package.
17-
- "sudo apt-get install python-all-dev libffi-dev libspotify-dev"
1823
- "pip install tox"
1924

2025
script:

MANIFEST.in

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
include *.txt
2-
include .coveragerc
32
include .travis.yml
43
include LICENSE
54
include MANIFEST.in

README.rst

+14-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Mopidy-Spotify
1515
:alt: Travis CI build status
1616

1717
.. image:: https://img.shields.io/coveralls/mopidy/mopidy-spotify/develop.svg?style=flat
18-
:target: https://coveralls.io/r/mopidy/mopidy-spotify?branch=develop
18+
:target: https://coveralls.io/r/mopidy/mopidy-spotify
1919
:alt: Test coverage
2020

2121
`Mopidy <http://www.mopidy.com/>`_ extension for playing music from
@@ -139,6 +139,19 @@ Project resources
139139
Changelog
140140
=========
141141

142+
v2.1.0 (2015-09-22)
143+
-------------------
144+
145+
Feature release.
146+
147+
- Require Requests >= 2.0.
148+
149+
- Support using a proxy when connecting to Spotify. This was forgotten in the
150+
2.0 reimplementation. (Fixes: #68)
151+
152+
- Support using a proxy when accessing Spotify's web API to get cover and
153+
artist imagery.
154+
142155
v2.0.1 (2015-08-23)
143156
-------------------
144157

dev-requirements.txt

+1
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ pytest
22
pytest-cov
33
pytest-capturelog
44
mock
5+
responses

mopidy_spotify/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from mopidy import config, ext
66

77

8-
__version__ = '2.0.1'
8+
__version__ = '2.1.0'
99

1010

1111
class Extension(ext.Extension):

mopidy_spotify/backend.py

+13-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import os
55
import threading
66

7-
from mopidy import backend
7+
from mopidy import backend, httpclient
88

99
import pykka
1010

@@ -89,13 +89,25 @@ def _get_session(self, config):
8989
def _get_spotify_config(self, config):
9090
ext = Extension()
9191
spotify_config = spotify.Config()
92+
9293
spotify_config.load_application_key_file(
9394
os.path.join(os.path.dirname(__file__), 'spotify_appkey.key'))
95+
9496
if config['spotify']['allow_cache']:
9597
spotify_config.cache_location = ext.get_cache_dir(config)
9698
else:
9799
spotify_config.cache_location = None
100+
98101
spotify_config.settings_location = ext.get_data_dir(config)
102+
103+
proxy_uri = httpclient.format_proxy(config['proxy'], auth=False)
104+
if proxy_uri is not None:
105+
logger.debug('Connecting to Spotify through proxy: %s', proxy_uri)
106+
107+
spotify_config.proxy = proxy_uri
108+
spotify_config.proxy_username = config['proxy'].get('username')
109+
spotify_config.proxy_password = config['proxy'].get('password')
110+
99111
return spotify_config
100112

101113
def on_logged_in(self):

mopidy_spotify/images.py

+18-13
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
from __future__ import unicode_literals
22

33
import itertools
4-
import json
54
import logging
65
import operator
7-
import urllib2
86
import urlparse
97

108
from mopidy import models
119

12-
from mopidy_spotify import utils
10+
import requests
1311

1412

1513
# NOTE: This module is independent of libspotify and built using the Spotify
@@ -25,7 +23,7 @@
2523
logger = logging.getLogger(__name__)
2624

2725

28-
def get_images(uris):
26+
def get_images(requests_session, uris):
2927
result = {}
3028
uri_type_getter = operator.itemgetter('type')
3129
uris = sorted((_parse_uri(u) for u in uris), key=uri_type_getter)
@@ -37,9 +35,10 @@ def get_images(uris):
3735
else:
3836
batch.append(uri)
3937
if len(batch) >= _API_MAX_IDS_PER_REQUEST:
40-
result.update(_process_uris(uri_type, batch))
38+
result.update(
39+
_process_uris(requests_session, uri_type, batch))
4140
batch = []
42-
result.update(_process_uris(uri_type, batch))
41+
result.update(_process_uris(requests_session, uri_type, batch))
4342
return result
4443

4544

@@ -60,20 +59,26 @@ def _parse_uri(uri):
6059
raise ValueError('Could not parse %r as a Spotify URI' % uri)
6160

6261

63-
def _process_uris(uri_type, uris):
62+
def _process_uris(requests_session, uri_type, uris):
6463
result = {}
64+
ids = [u['id'] for u in uris]
6565
ids_to_uris = {u['id']: u for u in uris}
6666

6767
if not uris:
6868
return result
6969

70+
lookup_uri = _API_BASE_URI % (uri_type, ','.join(ids))
71+
72+
try:
73+
response = requests_session.get(lookup_uri)
74+
except requests.RequestException as exc:
75+
logger.debug('Fetching %s failed: %s', lookup_uri, exc)
76+
return result
77+
7078
try:
71-
lookup_uri = _API_BASE_URI % (
72-
uri_type, ','.join(sorted(ids_to_uris.keys())))
73-
data = json.load(urllib2.urlopen(lookup_uri))
74-
except (ValueError, IOError) as e:
75-
error_msg = utils.locale_decode(e)
76-
logger.debug('Fetching %s failed: %s', lookup_uri, error_msg)
79+
data = response.json()
80+
except ValueError as exc:
81+
logger.debug('JSON decoding failed for %s: %s', lookup_uri, exc)
7782
return result
7883

7984
for item in data.get(uri_type + 's', []):

mopidy_spotify/library.py

+9-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
from mopidy import backend
66

7-
from mopidy_spotify import browse, distinct, images, lookup, search
7+
import mopidy_spotify
8+
from mopidy_spotify import browse, distinct, images, lookup, search, utils
89

910

1011
logger = logging.getLogger(__name__)
@@ -15,7 +16,12 @@ class SpotifyLibraryProvider(backend.LibraryProvider):
1516

1617
def __init__(self, backend):
1718
self._backend = backend
18-
self._config = self._backend._config['spotify']
19+
self._config = backend._config['spotify']
20+
self._requests_session = utils.get_requests_session(
21+
proxy_config=backend._config['proxy'],
22+
user_agent='%s/%s' % (
23+
mopidy_spotify.Extension.dist_name,
24+
mopidy_spotify.__version__))
1925

2026
def browse(self, uri):
2127
return browse.browse(self._config, self._backend._session, uri)
@@ -25,7 +31,7 @@ def get_distinct(self, field, query=None):
2531
self._config, self._backend._session, field, query)
2632

2733
def get_images(self, uris):
28-
return images.get_images(uris)
34+
return images.get_images(self._requests_session, uris)
2935

3036
def lookup(self, uri):
3137
return lookup.lookup(self._config, self._backend._session, uri)

mopidy_spotify/utils.py

+13-6
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
11
from __future__ import unicode_literals
22

33
import contextlib
4-
import locale
54
import logging
65
import time
76

7+
from mopidy import httpclient
8+
9+
import requests
10+
811

912
logger = logging.getLogger(__name__)
1013
TRACE = logging.getLevelName('TRACE')
1114

1215

13-
def locale_decode(bytestr):
14-
try:
15-
return unicode(bytestr)
16-
except UnicodeError:
17-
return bytes(bytestr).decode(locale.getpreferredencoding())
16+
def get_requests_session(proxy_config, user_agent):
17+
proxy = httpclient.format_proxy(proxy_config)
18+
full_user_agent = httpclient.format_user_agent(user_agent)
19+
20+
session = requests.Session()
21+
session.proxies.update({'http': proxy, 'https': proxy})
22+
session.headers.update({'user-agent': full_user_agent})
23+
24+
return session
1825

1926

2027
@contextlib.contextmanager

setup.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@ def get_version(filename):
2424
zip_safe=False,
2525
include_package_data=True,
2626
install_requires=[
27-
'setuptools',
2827
'Mopidy >= 1.1',
2928
'Pykka >= 1.1',
30-
'pyspotify >= 2.0.2',
29+
'pyspotify >= 2.0.5',
30+
'requests >= 2.0',
31+
'setuptools',
3132
],
3233
entry_points={
3334
'mopidy.ext': [

tests/conftest.py

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ def config(tmpdir):
1818
'cache_dir': '%s' % tmpdir.join('cache'),
1919
'data_dir': '%s' % tmpdir.join('data'),
2020
},
21+
'proxy': {
22+
},
2123
'spotify': {
2224
'username': 'alice',
2325
'password': 'password',

tests/test_backend.py

+17
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,23 @@ def test_on_start_configures_volume_normalization(spotify_mock, config):
101101
volume_normalization_mock.assert_called_once_with(False)
102102

103103

104+
def test_on_start_configures_proxy(spotify_mock, config):
105+
config['proxy'] = {
106+
'scheme': 'https',
107+
'hostname': 'my-proxy.example.com',
108+
'port': 8080,
109+
'username': 'alice',
110+
'password': 's3cret',
111+
}
112+
spotify_config = spotify_mock.Config.return_value
113+
114+
get_backend(config).on_start()
115+
116+
assert spotify_config.proxy == 'https://my-proxy.example.com:8080'
117+
assert spotify_config.proxy_username == 'alice'
118+
assert spotify_config.proxy_password == 's3cret'
119+
120+
104121
def test_on_start_adds_connection_state_changed_handler_to_session(
105122
spotify_mock, config):
106123
session = spotify_mock.Session.return_value

0 commit comments

Comments
 (0)