diff --git a/mopidy/backends/spotify/session_manager.py b/mopidy/backends/spotify/session_manager.py index 8f520896..125b6ada 100644 --- a/mopidy/backends/spotify/session_manager.py +++ b/mopidy/backends/spotify/session_manager.py @@ -33,9 +33,17 @@ class SpotifySessionManager(process.BaseThread, PyspotifySessionManager): self.cache_location = config['spotify']['cache_dir'] self.settings_location = config['spotify']['cache_dir'] + full_proxy = '' + if config['proxy']['hostname']: + full_proxy = config['proxy']['hostname'] + if config['proxy']['port']: + full_proxy += ':' + str(config['proxy']['port']) + if config['proxy']['scheme']: + full_proxy = config['proxy']['scheme'] + "://" + full_proxy + PyspotifySessionManager.__init__( self, config['spotify']['username'], config['spotify']['password'], - proxy=config['proxy']['hostname'], + proxy=full_proxy, proxy_username=config['proxy']['username'], proxy_password=config['proxy']['password']) diff --git a/mopidy/config/__init__.py b/mopidy/config/__init__.py index e9ae7d86..dfdae6a7 100644 --- a/mopidy/config/__init__.py +++ b/mopidy/config/__init__.py @@ -26,7 +26,10 @@ _audio_schema['mixer_track'] = String(optional=True) _audio_schema['output'] = String() _proxy_schema = ConfigSchema('proxy') +_proxy_schema['scheme'] = String(optional=True, + choices=['http', 'https', 'socks4', 'socks5']) _proxy_schema['hostname'] = Hostname(optional=True) +_proxy_schema['port'] = Port(optional=True) _proxy_schema['username'] = String(optional=True) _proxy_schema['password'] = Secret(optional=True) diff --git a/mopidy/config/convert.py b/mopidy/config/convert.py index 6cb20fcd..3c3edb85 100644 --- a/mopidy/config/convert.py +++ b/mopidy/config/convert.py @@ -39,6 +39,7 @@ def convert(settings): helper('audio/output', 'OUTPUT') helper('proxy/hostname', 'SPOTIFY_PROXY_HOST') + helper('proxy/port', 'SPOTIFY_PROXY_PORT') helper('proxy/username', 'SPOTIFY_PROXY_USERNAME') helper('proxy/password', 'SPOTIFY_PROXY_PASSWORD') diff --git a/mopidy/config/default.conf b/mopidy/config/default.conf index b525ef47..3899c2c5 100644 --- a/mopidy/config/default.conf +++ b/mopidy/config/default.conf @@ -13,6 +13,8 @@ mixer_track = output = autoaudiosink [proxy] +scheme = hostname = +port = username = password = diff --git a/mopidy/config/types.py b/mopidy/config/types.py index ec0be1de..29651940 100644 --- a/mopidy/config/types.py +++ b/mopidy/config/types.py @@ -71,9 +71,9 @@ class String(ConfigValue): def deserialize(self, value): value = decode(value).strip() validators.validate_required(value, self._required) - validators.validate_choice(value, self._choices) if not value: return None + validators.validate_choice(value, self._choices) return value def serialize(self, value, display=False): @@ -111,12 +111,16 @@ class Secret(ConfigValue): class Integer(ConfigValue): """Integer value.""" - def __init__(self, minimum=None, maximum=None, choices=None): + def __init__(self, minimum=None, maximum=None, choices=None, optional=False): + self._required = not optional self._minimum = minimum self._maximum = maximum self._choices = choices def deserialize(self, value): + validators.validate_required(value, self._required) + if not value: + return None value = int(value) validators.validate_choice(value, self._choices) validators.validate_minimum(value, self._minimum) @@ -222,9 +226,9 @@ class Port(Integer): allocate a port for us. """ # TODO: consider probing if port is free or not? - def __init__(self, choices=None): + def __init__(self, choices=None, optional=False): super(Port, self).__init__( - minimum=0, maximum=2 ** 16 - 1, choices=choices) + minimum=0, maximum=2 ** 16 - 1, choices=choices, optional=optional) class Path(ConfigValue): diff --git a/tests/config/types_test.py b/tests/config/types_test.py index 88c8f067..74e9672d 100644 --- a/tests/config/types_test.py +++ b/tests/config/types_test.py @@ -98,6 +98,11 @@ class StringTest(unittest.TestCase): self.assertIsInstance(result, bytes) self.assertEqual(b'', result) + def test_deserialize_enforces_choices_optional(self): + value = types.String(optional=True, choices=['foo', 'bar', 'baz']) + self.assertEqual(None, value.deserialize(b'')) + self.assertRaises(ValueError, value.deserialize, b'foobar') + class SecretTest(unittest.TestCase): def test_deserialize_passes_through(self): @@ -163,6 +168,10 @@ class IntegerTest(unittest.TestCase): self.assertEqual(5, value.deserialize('5')) self.assertRaises(ValueError, value.deserialize, '15') + def test_deserialize_respects_optional(self): + value = types.Integer(optional=True) + self.assertEqual(None, value.deserialize('')) + class BooleanTest(unittest.TestCase): def test_deserialize_conversion_success(self):