From fdd809fd36def07d01b770e7ce04855a1cfb8f77 Mon Sep 17 00:00:00 2001 From: Carl Suster Date: Thu, 25 Apr 2019 13:08:26 +1000 Subject: [PATCH] bpd: support more tagtypes --- beetsplug/bpd/__init__.py | 29 ++++++++-------- docs/plugins/bpd.rst | 3 +- test/test_player.py | 70 +++++++++++++++++++++++++++++++-------- 3 files changed, 72 insertions(+), 30 deletions(-) diff --git a/beetsplug/bpd/__init__.py b/beetsplug/bpd/__init__.py index dca3310d8..95598ff18 100644 --- a/beetsplug/bpd/__init__.py +++ b/beetsplug/bpd/__init__.py @@ -1117,19 +1117,10 @@ class Server(BaseServer): info_lines = [ u'file: ' + item.destination(fragment=True), u'Time: ' + six.text_type(int(item.length)), - u'Title: ' + item.title, - u'Artist: ' + item.artist, - u'Album: ' + item.album, - u'Genre: ' + item.genre, + u'duration: ' + u'{:.3f}'.format(item.length), + u'Id: ' + six.text_type(item.id), ] - track = six.text_type(item.track) - if item.tracktotal: - track += u'/' + six.text_type(item.tracktotal) - info_lines.append(u'Track: ' + track) - - info_lines.append(u'Date: ' + six.text_type(item.year)) - try: pos = self._id_to_index(item.id) info_lines.append(u'Pos: ' + six.text_type(pos)) @@ -1137,7 +1128,9 @@ class Server(BaseServer): # Don't include position if not in playlist. pass - info_lines.append(u'Id: ' + six.text_type(item.id)) + for tagtype, field in self.tagtype_map.items(): + info_lines.append(u'{}: {}'.format( + tagtype, six.text_type(getattr(item, field)))) return info_lines @@ -1341,18 +1334,24 @@ class Server(BaseServer): tagtype_map = { u'Artist': u'artist', + u'ArtistSort': u'artist_sort', u'Album': u'album', u'Title': u'title', u'Track': u'track', u'AlbumArtist': u'albumartist', u'AlbumArtistSort': u'albumartist_sort', - # Name? + u'Label': u'label', u'Genre': u'genre', u'Date': u'year', + u'OriginalDate': u'original_year', u'Composer': u'composer', - # Performer? u'Disc': u'disc', - u'filename': u'path', # Suspect. + u'Comment': u'comments', + u'MUSICBRAINZ_TRACKID': u'mb_trackid', + u'MUSICBRAINZ_ALBUMID': u'mb_albumid', + u'MUSICBRAINZ_ARTISTID': u'mb_artistid', + u'MUSICBRAINZ_ALBUMARTISTID': u'mb_albumartistid', + u'MUSICBRAINZ_RELEASETRACKID': u'mb_releasetrackid', } def cmd_tagtypes(self, conn): diff --git a/docs/plugins/bpd.rst b/docs/plugins/bpd.rst index 87c931793..8bfe456ad 100644 --- a/docs/plugins/bpd.rst +++ b/docs/plugins/bpd.rst @@ -125,8 +125,7 @@ These are some of the known differences between BPD and MPD: * Advanced playback features like cross-fade, ReplayGain and MixRamp are not supported due to BPD's simple audio player backend. * Advanced query syntax is not currently supported. -* Not all tags (fields) are currently exposed to BPD. Clients also can't use - the ``tagtypes`` mask to hide fields. +* Clients can't use the ``tagtypes`` mask to hide fields. * BPD's ``random`` mode is not deterministic and doesn't support priorities. * Mounts and streams are not supported. BPD can only play files from disk. * Stickers are not supported (although this is basically a flexattr in beets diff --git a/test/test_player.py b/test/test_player.py index f22c19261..874a2db52 100644 --- a/test/test_player.py +++ b/test/test_player.py @@ -392,8 +392,31 @@ class BPDTest(BPDTestHelper): class BPDQueryTest(BPDTestHelper): test_implements_query = implements({ - 'clearerror', 'currentsong', - }) + 'clearerror', + }) + + def test_cmd_currentsong(self): + with self.run_bpd() as client: + self._bpd_add(client, self.item1) + responses = client.send_commands( + ('play',), + ('currentsong',), + ('stop',), + ('currentsong',)) + self._assert_ok(*responses) + self.assertEqual('1', responses[1].data['Id']) + self.assertNotIn('Id', responses[3].data) + + def test_cmd_currentsong_tagtypes(self): + with self.run_bpd() as client: + self._bpd_add(client, self.item1) + responses = client.send_commands( + ('play',), + ('currentsong',)) + self._assert_ok(*responses) + self.assertEqual( + BPDConnectionTest.TAGTYPES.union(BPDQueueTest.METADATA), + set(responses[1].data.keys())) def test_cmd_status(self): with self.run_bpd() as client: @@ -749,6 +772,8 @@ class BPDQueueTest(BPDTestHelper): 'swap', 'swapid', 'addtagid', 'cleartagid', }, expectedFailure=True) + METADATA = {'Pos', 'Time', 'Id', 'file', 'duration'} + def test_cmd_add(self): with self.run_bpd() as client: self._bpd_add(client, self.item1) @@ -762,6 +787,15 @@ class BPDQueueTest(BPDTestHelper): ('playlistinfo', '200')) self._assert_failed(responses, bpd.ERROR_ARG, pos=2) + def test_cmd_playlistinfo_tagtypes(self): + with self.run_bpd() as client: + self._bpd_add(client, self.item1) + response = client.send_command('playlistinfo', '0') + self._assert_ok(response) + self.assertEqual( + BPDConnectionTest.TAGTYPES.union(BPDQueueTest.METADATA), + set(response.data.keys())) + def test_cmd_playlistid(self): with self.run_bpd() as client: self._bpd_add(client, self.item1, self.item2) @@ -900,8 +934,24 @@ class BPDStickerTest(BPDTestHelper): class BPDConnectionTest(BPDTestHelper): test_implements_connection = implements({ - 'close', 'kill', 'tagtypes', - }) + 'close', 'kill', + }) + + ALL_MPD_TAGTYPES = { + 'Artist', 'ArtistSort', 'Album', 'AlbumSort', 'AlbumArtist', + 'AlbumArtistSort', 'Title', 'Track', 'Name', 'Genre', 'Date', + 'Composer', 'Performer', 'Comment', 'Disc', 'Label', + 'OriginalDate', 'MUSICBRAINZ_ARTISTID', 'MUSICBRAINZ_ALBUMID', + 'MUSICBRAINZ_ALBUMARTISTID', 'MUSICBRAINZ_TRACKID', + 'MUSICBRAINZ_RELEASETRACKID', 'MUSICBRAINZ_WORKID', + } + UNSUPPORTED_TAGTYPES = { + 'MUSICBRAINZ_WORKID', # not tracked by beets + 'Performer', # not tracked by beets + 'AlbumSort', # not tracked by beets + 'Name', # junk field for internet radio + } + TAGTYPES = ALL_MPD_TAGTYPES.difference(UNSUPPORTED_TAGTYPES) def test_cmd_password(self): with self.run_bpd(password='abc123') as client: @@ -921,19 +971,13 @@ class BPDConnectionTest(BPDTestHelper): response = client.send_command('ping') self._assert_ok(response) - @unittest.skip def test_cmd_tagtypes(self): with self.run_bpd() as client: response = client.send_command('tagtypes') self._assert_ok(response) - self.assertEqual({ - 'Artist', 'ArtistSort', 'Album', 'AlbumSort', 'AlbumArtist', - 'AlbumArtistSort', 'Title', 'Track', 'Name', 'Genre', 'Date', - 'Composer', 'Performer', 'Comment', 'Disc', 'Label', - 'OriginalDate', 'MUSICBRAINZ_ARTISTID', 'MUSICBRAINZ_ALBUMID', - 'MUSICBRAINZ_ALBUMARTISTID', 'MUSICBRAINZ_TRACKID', - 'MUSICBRAINZ_RELEASETRACKID', 'MUSICBRAINZ_WORKID', - }, set(response.data['tag'])) + self.assertEqual( + self.TAGTYPES, + set(response.data['tagtype'])) @unittest.skip def test_tagtypes_mask(self):