Merge branch 'develop' into feature/mpd-tokenized-requests
Conflicts: mopidy/mpd/protocol/music_db.py tests/mpd/protocol/test_music_db.py
This commit is contained in:
commit
3cec929bd7
2
AUTHORS
2
AUTHORS
@ -35,3 +35,5 @@
|
||||
- Luke Giuliani <luke@giuliani.com.au>
|
||||
- Colin Montgomerie <kiteflyingmonkey@gmail.com>
|
||||
- Simon de Bakker <simon@simbits.nl>
|
||||
- Arnaud Barisain-Monrose <abarisain@gmail.com>
|
||||
- nathanharper <nathan.sam.harper@gmail.com>
|
||||
|
||||
@ -4,7 +4,34 @@ Changelog
|
||||
|
||||
This changelog is used to track all major changes to Mopidy.
|
||||
|
||||
v0.18.0 (UNRELEASED)
|
||||
|
||||
v0.19.0 (unreleased)
|
||||
====================
|
||||
|
||||
**MPD**
|
||||
|
||||
- Minor refactor of context such that it stores password instead of config.
|
||||
(Fixes: :issue:`646`)
|
||||
|
||||
**Windows**
|
||||
|
||||
- Network and signal handling has been updated to play nice on windows systems.
|
||||
|
||||
|
||||
v0.18.1 (2014-01-23)
|
||||
====================
|
||||
|
||||
Bug fix release.
|
||||
|
||||
- Disable extension instead of crashing if a dependency has the wrong
|
||||
version. (Fixes: :issue:`657`)
|
||||
|
||||
- Make logging work to both console, debug log file, and any custom logging
|
||||
setup from :confval:`logging/config_file` at the same time. (Fixes:
|
||||
:issue:`661`)
|
||||
|
||||
|
||||
v0.18.0 (2014-01-19)
|
||||
====================
|
||||
|
||||
The focus of 0.18 have been on two fronts: the local library and browsing.
|
||||
@ -21,8 +48,8 @@ the local and Spotify backends. It is also used by the new Mopidy-Dirble
|
||||
extension to provide you with a directory of Internet radio stations from all
|
||||
over the world.
|
||||
|
||||
Since the release of 0.17, we've closed or merged 47 issues and pull requests
|
||||
through about 270 commits by :ref:`11 people <authors>`, including six new
|
||||
Since the release of 0.17, we've closed or merged 49 issues and pull requests
|
||||
through about 285 commits by :ref:`11 people <authors>`, including six new
|
||||
guys. Thanks to everyone that has contributed!
|
||||
|
||||
**Core API**
|
||||
@ -1831,9 +1858,8 @@ to this problem.
|
||||
- MPD frontend:
|
||||
|
||||
- Add support for password authentication. See
|
||||
:attr:`mopidy.settings.MPD_SERVER_PASSWORD` and
|
||||
:ref:`use-mpd-on-a-network` for details on how to use it. (Fixes:
|
||||
:issue:`41`)
|
||||
:attr:`mopidy.settings.MPD_SERVER_PASSWORD` for details on how to use it.
|
||||
(Fixes: :issue:`41`)
|
||||
|
||||
- Support ``setvol 50`` without quotes around the argument. Fixes volume
|
||||
control in Droid MPD.
|
||||
|
||||
@ -21,4 +21,4 @@ if (isinstance(pykka.__version__, basestring)
|
||||
warnings.filterwarnings('ignore', 'could not open display')
|
||||
|
||||
|
||||
__version__ = '0.18.0'
|
||||
__version__ = '0.18.1'
|
||||
|
||||
@ -37,7 +37,9 @@ def main():
|
||||
logger.info('Starting Mopidy %s', versioning.get_version())
|
||||
|
||||
signal.signal(signal.SIGTERM, process.exit_handler)
|
||||
signal.signal(signal.SIGUSR1, pykka.debug.log_thread_tracebacks)
|
||||
# Windows does not have signal.SIGUSR1
|
||||
if hasattr(signal, 'SIGUSR1'):
|
||||
signal.signal(signal.SIGUSR1, pykka.debug.log_thread_tracebacks)
|
||||
|
||||
try:
|
||||
registry = ext.Registry()
|
||||
@ -70,8 +72,7 @@ def main():
|
||||
if args.verbosity_level:
|
||||
verbosity_level += args.verbosity_level
|
||||
|
||||
log.setup_logging(
|
||||
config, installed_extensions, verbosity_level, args.save_debug_log)
|
||||
log.setup_logging(config, verbosity_level, args.save_debug_log)
|
||||
|
||||
enabled_extensions = []
|
||||
for extension in installed_extensions:
|
||||
|
||||
@ -222,6 +222,12 @@ def validate_extension(extension):
|
||||
'Disabled extension %s: Dependency %s not found',
|
||||
extension.ext_name, ex)
|
||||
return False
|
||||
except pkg_resources.VersionConflict as ex:
|
||||
found, required = ex.args
|
||||
logger.info(
|
||||
'Disabled extension %s: %s required, but found %s at %s',
|
||||
extension.ext_name, required, found, found.location)
|
||||
return False
|
||||
|
||||
try:
|
||||
extension.validate_environment()
|
||||
|
||||
@ -66,12 +66,15 @@ class _BrowseCache(object):
|
||||
for i in reversed(range(len(parts))):
|
||||
directory = '/'.join(parts[:i+1])
|
||||
uri = translator.path_to_local_directory_uri(directory)
|
||||
|
||||
# First dir we process is our parent
|
||||
if not parent_uri:
|
||||
parent_uri = uri
|
||||
|
||||
# We found ourselves and we exist, done.
|
||||
if uri in self._cache:
|
||||
if child:
|
||||
self._cache[uri][child.uri] = child
|
||||
break
|
||||
|
||||
# Initialize ourselves, store child if present, and add
|
||||
|
||||
@ -218,8 +218,8 @@ class MpdContext(object):
|
||||
#: The current :class:`mopidy.mpd.MpdSession`.
|
||||
session = None
|
||||
|
||||
#: The Mopidy configuration.
|
||||
config = None
|
||||
#: The MPD password
|
||||
password = None
|
||||
|
||||
#: The Mopidy core API. An instance of :class:`mopidy.core.Core`.
|
||||
core = None
|
||||
@ -235,7 +235,8 @@ class MpdContext(object):
|
||||
def __init__(self, dispatcher, session=None, config=None, core=None):
|
||||
self.dispatcher = dispatcher
|
||||
self.session = session
|
||||
self.config = config
|
||||
if config is not None:
|
||||
self.password = config['mpd']['password']
|
||||
self.core = core
|
||||
self.events = set()
|
||||
self.subscriptions = set()
|
||||
|
||||
@ -37,7 +37,7 @@ def password(context, password):
|
||||
This is used for authentication with the server. ``PASSWORD`` is
|
||||
simply the plaintext password.
|
||||
"""
|
||||
if password == context.config['mpd']['password']:
|
||||
if password == context.password:
|
||||
context.dispatcher.authenticated = True
|
||||
else:
|
||||
raise exceptions.MpdPasswordError('incorrect password')
|
||||
|
||||
@ -27,23 +27,23 @@ _delayed_handler = DelayedHandler()
|
||||
|
||||
def bootstrap_delayed_logging():
|
||||
root = logging.getLogger('')
|
||||
root.setLevel(logging.DEBUG)
|
||||
root.setLevel(logging.NOTSET)
|
||||
root.addHandler(_delayed_handler)
|
||||
|
||||
|
||||
def setup_logging(config, extensions, verbosity_level, save_debug_log):
|
||||
setup_log_levels(config)
|
||||
|
||||
setup_console_logging(config, extensions, verbosity_level)
|
||||
|
||||
if save_debug_log:
|
||||
setup_debug_logging_to_file(config, extensions)
|
||||
|
||||
def setup_logging(config, verbosity_level, save_debug_log):
|
||||
logging.captureWarnings(True)
|
||||
|
||||
if config['logging']['config_file']:
|
||||
# Logging config from file must be read before other handlers are
|
||||
# added. If not, the other handlers will have no effect.
|
||||
logging.config.fileConfig(config['logging']['config_file'])
|
||||
|
||||
setup_log_levels(config)
|
||||
setup_console_logging(config, verbosity_level)
|
||||
if save_debug_log:
|
||||
setup_debug_logging_to_file(config)
|
||||
|
||||
_delayed_handler.release()
|
||||
|
||||
|
||||
@ -61,46 +61,43 @@ LOG_LEVELS = {
|
||||
}
|
||||
|
||||
|
||||
def setup_console_logging(config, extensions, verbosity_level):
|
||||
class VerbosityFilter(logging.Filter):
|
||||
def __init__(self, verbosity_level):
|
||||
self.verbosity_level = verbosity_level
|
||||
|
||||
def filter(self, record):
|
||||
if record.name.startswith('mopidy'):
|
||||
required_log_level = LOG_LEVELS[self.verbosity_level]['mopidy']
|
||||
else:
|
||||
required_log_level = LOG_LEVELS[self.verbosity_level]['root']
|
||||
return record.levelno >= required_log_level
|
||||
|
||||
|
||||
def setup_console_logging(config, verbosity_level):
|
||||
if verbosity_level < min(LOG_LEVELS.keys()):
|
||||
verbosity_level = min(LOG_LEVELS.keys())
|
||||
if verbosity_level > max(LOG_LEVELS.keys()):
|
||||
verbosity_level = max(LOG_LEVELS.keys())
|
||||
|
||||
verbosity_filter = VerbosityFilter(verbosity_level)
|
||||
|
||||
if verbosity_level < 1:
|
||||
log_format = config['logging']['console_format']
|
||||
else:
|
||||
log_format = config['logging']['debug_format']
|
||||
formatter = logging.Formatter(log_format)
|
||||
|
||||
root_handler = logging.StreamHandler()
|
||||
root_handler.setFormatter(formatter)
|
||||
root_handler.setLevel(LOG_LEVELS[verbosity_level]['root'])
|
||||
logging.getLogger('').addHandler(root_handler)
|
||||
handler = logging.StreamHandler()
|
||||
handler.addFilter(verbosity_filter)
|
||||
handler.setFormatter(formatter)
|
||||
|
||||
mopidy_handler = logging.StreamHandler()
|
||||
mopidy_handler.setFormatter(formatter)
|
||||
mopidy_handler.setLevel(LOG_LEVELS[verbosity_level]['mopidy'])
|
||||
add_mopidy_handler(extensions, mopidy_handler)
|
||||
logging.getLogger('').addHandler(handler)
|
||||
|
||||
|
||||
def setup_debug_logging_to_file(config, extensions):
|
||||
def setup_debug_logging_to_file(config):
|
||||
formatter = logging.Formatter(config['logging']['debug_format'])
|
||||
handler = logging.handlers.RotatingFileHandler(
|
||||
config['logging']['debug_file'], maxBytes=10485760, backupCount=3)
|
||||
handler.setFormatter(formatter)
|
||||
|
||||
logging.getLogger('').addHandler(handler)
|
||||
|
||||
# We must add our handler explicitly, since the mopidy* handlers don't
|
||||
# propagate to the root handler.
|
||||
add_mopidy_handler(extensions, handler)
|
||||
|
||||
|
||||
def add_mopidy_handler(extensions, handler):
|
||||
names = ['mopidy_%s' % ext.ext_name for ext in extensions]
|
||||
names.append('mopidy')
|
||||
for name in names:
|
||||
logger = logging.getLogger(name)
|
||||
logger.propagate = False
|
||||
logger.addHandler(handler)
|
||||
|
||||
@ -5,6 +5,7 @@ import gobject
|
||||
import logging
|
||||
import re
|
||||
import socket
|
||||
import sys
|
||||
import threading
|
||||
|
||||
import pykka
|
||||
@ -43,7 +44,12 @@ def create_socket():
|
||||
if has_ipv6:
|
||||
sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
|
||||
# Explicitly configure socket to work for both IPv4 and IPv6
|
||||
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
|
||||
if hasattr(socket, 'IPPROTO_IPV6'):
|
||||
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
|
||||
elif sys.platform == 'win32': # also match 64bit windows.
|
||||
# Python 2.7 on windows does not have the IPPROTO_IPV6 constant
|
||||
# Use values extracted from Windows Vista/7/8's header
|
||||
sock.setsockopt(41, 27, 0)
|
||||
else:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
|
||||
@ -7,20 +7,27 @@ from mopidy.models import Ref
|
||||
|
||||
|
||||
class BrowseCacheTest(unittest.TestCase):
|
||||
maxDiff = None
|
||||
|
||||
def setUp(self):
|
||||
self.uris = [b'local:track:foo/bar/song1',
|
||||
b'local:track:foo/bar/song2',
|
||||
b'local:track:foo/song3']
|
||||
self.uris = ['local:track:foo/bar/song1',
|
||||
'local:track:foo/bar/song2',
|
||||
'local:track:foo/baz/song3',
|
||||
'local:track:foo/song4',
|
||||
'local:track:song5']
|
||||
self.cache = json._BrowseCache(self.uris)
|
||||
|
||||
def test_lookup_root(self):
|
||||
expected = [Ref.directory(uri='local:directory:foo', name='foo')]
|
||||
expected = [Ref.directory(uri='local:directory:foo', name='foo'),
|
||||
Ref.track(uri='local:track:song5', name='song5')]
|
||||
self.assertEqual(expected, self.cache.lookup('local:directory'))
|
||||
|
||||
def test_lookup_foo(self):
|
||||
expected = [Ref.directory(uri='local:directory:foo/bar', name='bar'),
|
||||
Ref.track(uri=self.uris[2], name='song3')]
|
||||
self.assertEqual(expected, self.cache.lookup('local:directory:foo'))
|
||||
Ref.directory(uri='local:directory:foo/baz', name='baz'),
|
||||
Ref.track(uri=self.uris[3], name='song4')]
|
||||
result = self.cache.lookup('local:directory:foo')
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
def test_lookup_foo_bar(self):
|
||||
expected = [Ref.track(uri=self.uris[0], name='song1'),
|
||||
@ -29,4 +36,5 @@ class BrowseCacheTest(unittest.TestCase):
|
||||
expected, self.cache.lookup('local:directory:foo/bar'))
|
||||
|
||||
def test_lookup_foo_baz(self):
|
||||
self.assertEqual([], self.cache.lookup('local:directory:foo/baz'))
|
||||
result = self.cache.lookup('local:directory:foo/unknown')
|
||||
self.assertEqual([], result)
|
||||
|
||||
@ -42,5 +42,6 @@ class VersionTest(unittest.TestCase):
|
||||
self.assertLess(SV('0.14.2'), SV('0.15.0'))
|
||||
self.assertLess(SV('0.15.0'), SV('0.16.0'))
|
||||
self.assertLess(SV('0.16.0'), SV('0.17.0'))
|
||||
self.assertLess(SV('0.17.0'), SV(__version__))
|
||||
self.assertLess(SV(__version__), SV('0.18.1'))
|
||||
self.assertLess(SV('0.17.0'), SV('0.18.0'))
|
||||
self.assertLess(SV('0.18.0'), SV(__version__))
|
||||
self.assertLess(SV(__version__), SV('0.18.2'))
|
||||
|
||||
Loading…
Reference in New Issue
Block a user