diff --git a/docs/changelog.rst b/docs/changelog.rst index 5be97bd9..47bbe0b3 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -46,6 +46,9 @@ v0.20.0 (UNRELEASED) - Enable browsing of artist references, in addition to albums and playlists. (PR: :issue:`884`) + +- Share a single mapping between names and URIs across all MPD sessions. (Fixes: + :issue:`934`, PR: :issue:`968`) **Audio** diff --git a/mopidy/mpd/actor.py b/mopidy/mpd/actor.py index cd9b9145..c9ffff02 100644 --- a/mopidy/mpd/actor.py +++ b/mopidy/mpd/actor.py @@ -6,7 +6,7 @@ import pykka from mopidy import exceptions, zeroconf from mopidy.core import CoreListener -from mopidy.mpd import dispatcher, session +from mopidy.mpd import session, uri_mapper from mopidy.utils import encoding, network, process logger = logging.getLogger(__name__) @@ -18,7 +18,7 @@ class MpdFrontend(pykka.ThreadingActor, CoreListener): self.hostname = network.format_hostname(config['mpd']['hostname']) self.port = config['mpd']['port'] - self.uri_map = dispatcher.MpdUriMapper(core) + self.uri_map = uri_mapper.MpdUriMapper(core) self.zeroconf_name = config['mpd']['zeroconf'] self.zeroconf_service = None diff --git a/mopidy/mpd/dispatcher.py b/mopidy/mpd/dispatcher.py index a0950625..b1b2db77 100644 --- a/mopidy/mpd/dispatcher.py +++ b/mopidy/mpd/dispatcher.py @@ -305,79 +305,3 @@ class MpdContext(object): if recursive: path_and_futures.append( (path, self.core.library.browse(ref.uri))) - - -class MpdUriMapper(object): - """ - Maintains the mappings between uniquified MPD names and URIs. - """ - - #: The Mopidy core API. An instance of :class:`mopidy.core.Core`. - core = None - - _invalid_browse_chars = re.compile(r'[\n\r]') - _invalid_playlist_chars = re.compile(r'[/]') - - def __init__(self, core=None): - self.core = core - self._uri_from_name = {} - self._name_from_uri = {} - - def _create_unique_name(self, name, uri): - stripped_name = self._invalid_browse_chars.sub(' ', name) - name = stripped_name - i = 2 - while name in self._uri_from_name: - if self._uri_from_name[name] == uri: - return name - name = '%s [%d]' % (stripped_name, i) - i += 1 - return name - - def insert(self, name, uri): - """ - Create a unique and MPD compatible name that maps to the given uri. - """ - name = self._create_unique_name(name, uri) - self._uri_from_name[name] = uri - self._name_from_uri[uri] = name - return name - - def uri_from_name(self, name): - """ - Return the uri for the given MPD name. - """ - if name in self._uri_from_name: - return self._uri_from_name[name] - - def refresh_playlists_mapping(self): - """ - Maintain map between playlists and unique playlist names to be used by - MPD - """ - if self.core is not None: - for playlist in self.core.playlists.playlists.get(): - if not playlist.name: - continue - # TODO: add scheme to name perhaps 'foo (spotify)' etc. - name = self._invalid_playlist_chars.sub('|', playlist.name) - self.insert(name, playlist.uri) - - def playlist_from_name(self, name): - """ - Helper function to retrieve a playlist from its unique MPD name. - """ - if not self._uri_from_name: - self.refresh_playlists_mapping() - if name not in self._uri_from_name: - return None - uri = self._uri_from_name[name] - return self.core.playlists.lookup(uri).get() - - def playlist_name_from_uri(self, uri): - """ - Helper function to retrieve the unique MPD playlist name from its uri. - """ - if uri not in self._name_from_uri: - self.refresh_playlists_mapping() - return self._name_from_uri[uri] diff --git a/mopidy/mpd/uri_mapper.py b/mopidy/mpd/uri_mapper.py new file mode 100644 index 00000000..99413493 --- /dev/null +++ b/mopidy/mpd/uri_mapper.py @@ -0,0 +1,79 @@ +from __future__ import absolute_import, unicode_literals + +import re + + +class MpdUriMapper(object): + """ + Maintains the mappings between uniquified MPD names and URIs. + """ + + #: The Mopidy core API. An instance of :class:`mopidy.core.Core`. + core = None + + _invalid_browse_chars = re.compile(r'[\n\r]') + _invalid_playlist_chars = re.compile(r'[/]') + + def __init__(self, core=None): + self.core = core + self._uri_from_name = {} + self._name_from_uri = {} + + def _create_unique_name(self, name, uri): + stripped_name = self._invalid_browse_chars.sub(' ', name) + name = stripped_name + i = 2 + while name in self._uri_from_name: + if self._uri_from_name[name] == uri: + return name + name = '%s [%d]' % (stripped_name, i) + i += 1 + return name + + def insert(self, name, uri): + """ + Create a unique and MPD compatible name that maps to the given uri. + """ + name = self._create_unique_name(name, uri) + self._uri_from_name[name] = uri + self._name_from_uri[uri] = name + return name + + def uri_from_name(self, name): + """ + Return the uri for the given MPD name. + """ + if name in self._uri_from_name: + return self._uri_from_name[name] + + def refresh_playlists_mapping(self): + """ + Maintain map between playlists and unique playlist names to be used by + MPD + """ + if self.core is not None: + for playlist in self.core.playlists.playlists.get(): + if not playlist.name: + continue + # TODO: add scheme to name perhaps 'foo (spotify)' etc. + name = self._invalid_playlist_chars.sub('|', playlist.name) + self.insert(name, playlist.uri) + + def playlist_from_name(self, name): + """ + Helper function to retrieve a playlist from its unique MPD name. + """ + if not self._uri_from_name: + self.refresh_playlists_mapping() + if name not in self._uri_from_name: + return None + uri = self._uri_from_name[name] + return self.core.playlists.lookup(uri).get() + + def playlist_name_from_uri(self, uri): + """ + Helper function to retrieve the unique MPD playlist name from its uri. + """ + if uri not in self._name_from_uri: + self.refresh_playlists_mapping() + return self._name_from_uri[uri] diff --git a/tests/mpd/protocol/__init__.py b/tests/mpd/protocol/__init__.py index f895316b..8c7b60f1 100644 --- a/tests/mpd/protocol/__init__.py +++ b/tests/mpd/protocol/__init__.py @@ -8,7 +8,7 @@ import pykka from mopidy import core from mopidy.backend import dummy -from mopidy.mpd import dispatcher, session +from mopidy.mpd import session, uri_mapper class MockConnection(mock.Mock): @@ -35,7 +35,7 @@ class BaseTestCase(unittest.TestCase): self.backend = dummy.create_dummy_backend_proxy() self.core = core.Core.start(backends=[self.backend]).proxy() - self.uri_map = dispatcher.MpdUriMapper(self.core) + self.uri_map = uri_mapper.MpdUriMapper(self.core) self.connection = MockConnection() self.session = session.MpdSession( self.connection, config=self.get_config(), core=self.core,