From d698653d83b0be1cd3272ebd0e433e5cf8e40002 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Wed, 15 Jan 2014 01:08:17 +0100 Subject: [PATCH] mpd: Implement "listallinfo" command (fixes #145) --- mopidy/mpd/protocol/music_db.py | 32 ++++++++++++++++++++--- tests/mpd/protocol/test_music_db.py | 39 ++++++++++++++++++++++++++--- 2 files changed, 65 insertions(+), 6 deletions(-) diff --git a/mopidy/mpd/protocol/music_db.py b/mopidy/mpd/protocol/music_db.py index 302d37eb..7ef11111 100644 --- a/mopidy/mpd/protocol/music_db.py +++ b/mopidy/mpd/protocol/music_db.py @@ -6,8 +6,7 @@ import re from mopidy.models import Ref, Track from mopidy.mpd import translator -from mopidy.mpd.exceptions import ( - MpdArgError, MpdNoExistError, MpdNotImplemented) +from mopidy.mpd.exceptions import MpdArgError, MpdNoExistError from mopidy.mpd.protocol import handle_request, stored_playlists @@ -450,7 +449,34 @@ def listallinfo(context, uri=None): Same as ``listall``, except it also returns metadata info in the same format as ``lsinfo``. """ - raise MpdNotImplemented # TODO + if uri is None: + uri = '/' + if not uri.startswith('/'): + uri = '/%s' % uri + + dirs_and_futures = [] + browse_futures = [context.core.library.browse(uri)] + while browse_futures: + for ref in browse_futures.pop().get(): + if ref.type == Ref.DIRECTORY: + dirs_and_futures.append(('directory', ref.uri)) + browse_futures.append(context.core.library.browse(ref.uri)) + elif ref.type == Ref.TRACK: + # TODO Lookup tracks in batch for better performance + dirs_and_futures.append(context.core.library.lookup(ref.uri)) + + result = [] + for obj in dirs_and_futures: + if hasattr(obj, 'get'): + for track in obj.get(): + result.extend(translator.track_to_mpd_format(track)) + else: + result.append(obj) + + if not result: + raise MpdNoExistError('Not found', command='listallinfo') + + return [('directory', uri)] + result @handle_request(r'lsinfo$') diff --git a/tests/mpd/protocol/test_music_db.py b/tests/mpd/protocol/test_music_db.py index 78a94a78..d2ecd66c 100644 --- a/tests/mpd/protocol/test_music_db.py +++ b/tests/mpd/protocol/test_music_db.py @@ -160,12 +160,45 @@ class MusicDatabaseHandlerTest(protocol.BaseTestCase): self.assertEqualResponse('ACK [50@0] {listall} Not found') def test_listallinfo_without_uri(self): + tracks = [Track(uri='dummy:/a', name='a'), + Track(uri='dummy:/b', name='b')] + self.backend.library.dummy_library = tracks + self.backend.library.dummy_browse_result = { + '/': [Ref.track(uri='dummy:/a', name='a'), + Ref.directory(uri='/foo')], + '/foo': [Ref.track(uri='dummy:/b', name='b')]} + self.sendRequest('listallinfo') - self.assertEqualResponse('ACK [0@0] {} Not implemented') + + self.assertInResponse('file: dummy:/a') + self.assertInResponse('Title: a') + self.assertInResponse('directory: /dummy/foo') + self.assertInResponse('file: dummy:/b') + self.assertInResponse('Title: b') + self.assertInResponse('OK') def test_listallinfo_with_uri(self): - self.sendRequest('listallinfo "file:///dev/urandom"') - self.assertEqualResponse('ACK [0@0] {} Not implemented') + tracks = [Track(uri='dummy:/a', name='a'), + Track(uri='dummy:/b', name='b')] + self.backend.library.dummy_library = tracks + self.backend.library.dummy_browse_result = { + '/': [Ref.track(uri='dummy:/a', name='a'), + Ref.directory(uri='/foo')], + '/foo': [Ref.track(uri='dummy:/b', name='b')]} + + self.sendRequest('listallinfo "/dummy/foo"') + + self.assertNotInResponse('file: dummy:/a') + self.assertNotInResponse('Title: a') + self.assertInResponse('directory: /dummy/foo') + self.assertInResponse('file: dummy:/b') + self.assertInResponse('Title: b') + self.assertInResponse('OK') + + def test_listallinfo_with_unknown_uri(self): + self.sendRequest('listallinfo "/unknown"') + + self.assertEqualResponse('ACK [50@0] {listallinfo} Not found') def test_lsinfo_without_path_returns_same_as_for_root(self): last_modified = datetime.datetime(2001, 3, 17, 13, 41, 17, 12345)