From edb2f060c4b56f9f455ed6c63679e90081fbd221 Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Mon, 1 Nov 2010 23:42:33 +0100 Subject: [PATCH 01/15] Add musicbrainz to models --- mopidy/models.py | 15 ++++++++ tests/models_test.py | 86 +++++++++++++++++++++++++++++++++++++------- 2 files changed, 89 insertions(+), 12 deletions(-) diff --git a/mopidy/models.py b/mopidy/models.py index 7dd75660..c45d6769 100644 --- a/mopidy/models.py +++ b/mopidy/models.py @@ -56,6 +56,8 @@ class Artist(ImmutableObject): :type uri: string :param name: artist name :type name: string + :param musicbrainz_id: musicbrainz id + :type musicbrainz_id: string """ #: The artist URI. Read-only. @@ -64,6 +66,9 @@ class Artist(ImmutableObject): #: The artist name. Read-only. name = None + #: The musicbrainz id of the artist. Read-only. + musicbrainz_id = None + class Album(ImmutableObject): """ @@ -75,6 +80,8 @@ class Album(ImmutableObject): :type artists: list of :class:`Artist` :param num_tracks: number of tracks in album :type num_tracks: integer + :param musicbrainz_id: musicbrainz id + :type musicbrainz_id: string """ #: The album URI. Read-only. @@ -86,6 +93,9 @@ class Album(ImmutableObject): #: The number of tracks in the album. Read-only. num_tracks = 0 + #: The musicbrainz id of the album. Read-only. + musicbrainz_id = None + def __init__(self, *args, **kwargs): self._artists = frozenset(kwargs.pop('artists', [])) super(Album, self).__init__(*args, **kwargs) @@ -114,6 +124,8 @@ class Track(ImmutableObject): :type length: integer :param bitrate: bitrate in kbit/s :type bitrate: integer + :param musicbrainz_id: musicbrainz id + :type musicbrainz_id: string """ #: The track URI. Read-only. @@ -137,6 +149,9 @@ class Track(ImmutableObject): #: The track's bitrate in kbit/s. Read-only. bitrate = None + #: The musicbrainz id of the track. Read-only. + musicbrainz_id = None + def __init__(self, *args, **kwargs): self._artists = frozenset(kwargs.pop('artists', [])) super(Track, self).__init__(*args, **kwargs) diff --git a/tests/models_test.py b/tests/models_test.py index 2c1dfec7..0b44f337 100644 --- a/tests/models_test.py +++ b/tests/models_test.py @@ -62,6 +62,13 @@ class ArtistTest(unittest.TestCase): self.assertEqual(artist.name, name) self.assertRaises(AttributeError, setattr, artist, 'name', None) + def test_musicbrainz_id(self): + mb_id = u'mb-id' + artist = Artist(musicbrainz_id=mb_id) + self.assertEqual(artist.musicbrainz_id, mb_id) + self.assertRaises(AttributeError, setattr, artist, + 'musicbrainz_id', None) + def test_invalid_kwarg(self): test = lambda: Artist(foo='baz') self.assertRaises(TypeError, test) @@ -78,9 +85,15 @@ class ArtistTest(unittest.TestCase): self.assertEqual(artist1, artist2) self.assertEqual(hash(artist1), hash(artist2)) + def test_eq_musibrainz_id(self): + artist1 = Artist(musicbrainz_id=u'id') + artist2 = Artist(musicbrainz_id=u'id') + self.assertEqual(artist1, artist2) + self.assertEqual(hash(artist1), hash(artist2)) + def test_eq(self): - artist1 = Artist(uri=u'uri', name=u'name') - artist2 = Artist(uri=u'uri', name=u'name') + artist1 = Artist(uri=u'uri', name=u'name', musicbrainz_id='id') + artist2 = Artist(uri=u'uri', name=u'name', musicbrainz_id='id') self.assertEqual(artist1, artist2) self.assertEqual(hash(artist1), hash(artist2)) @@ -102,9 +115,15 @@ class ArtistTest(unittest.TestCase): self.assertNotEqual(artist1, artist2) self.assertNotEqual(hash(artist1), hash(artist2)) + def test_ne_musicbrainz_id(self): + artist1 = Artist(musicbrainz_id=u'id1') + artist2 = Artist(musicbrainz_id=u'id2') + self.assertNotEqual(artist1, artist2) + self.assertNotEqual(hash(artist1), hash(artist2)) + def test_ne(self): - artist1 = Artist(uri=u'uri1', name=u'name1') - artist2 = Artist(uri=u'uri2', name=u'name2') + artist1 = Artist(uri=u'uri1', name=u'name1', musicbrainz_id='id1') + artist2 = Artist(uri=u'uri2', name=u'name2', musicbrainz_id='id2') self.assertNotEqual(artist1, artist2) self.assertNotEqual(hash(artist1), hash(artist2)) @@ -134,6 +153,13 @@ class AlbumTest(unittest.TestCase): self.assertEqual(album.num_tracks, num_tracks) self.assertRaises(AttributeError, setattr, album, 'num_tracks', None) + def test_musicbrainz_id(self): + mb_id = u'mb-id' + album = Album(musicbrainz_id=mb_id) + self.assertEqual(album.musicbrainz_id, mb_id) + self.assertRaises(AttributeError, setattr, album, + 'musicbrainz_id', None) + def test_invalid_kwarg(self): test = lambda: Album(foo='baz') self.assertRaises(TypeError, test) @@ -171,10 +197,16 @@ class AlbumTest(unittest.TestCase): self.assertEqual(album1, album2) self.assertEqual(hash(album1), hash(album2)) + def test_eq_musibrainz_id(self): + album1 = Album(musicbrainz_id=u'id') + album2 = Album(musicbrainz_id=u'id') + self.assertEqual(album1, album2) + self.assertEqual(hash(album1), hash(album2)) + def test_eq(self): artists = [Artist()] - album1 = Album(name=u'name', uri=u'uri', artists=artists, num_tracks=2) - album2 = Album(name=u'name', uri=u'uri', artists=artists, num_tracks=2) + album1 = Album(name=u'name', uri=u'uri', artists=artists, num_tracks=2, musicbrainz_id='id') + album2 = Album(name=u'name', uri=u'uri', artists=artists, num_tracks=2, musicbrainz_id='id') self.assertEqual(album1, album2) self.assertEqual(hash(album1), hash(album2)) @@ -208,11 +240,19 @@ class AlbumTest(unittest.TestCase): self.assertNotEqual(album1, album2) self.assertNotEqual(hash(album1), hash(album2)) + def test_ne_musicbrainz_id(self): + album1 = Album(musicbrainz_id=u'id1') + album2 = Album(musicbrainz_id=u'id2') + self.assertNotEqual(album1, album2) + self.assertNotEqual(hash(album1), hash(album2)) + def test_ne(self): album1 = Album(name=u'name1', uri=u'uri1', - artists=[Artist(name=u'name1')], num_tracks=1) + artists=[Artist(name=u'name1')], num_tracks=1, + musicbrainz_id='id1') album2 = Album(name=u'name2', uri=u'uri2', - artists=[Artist(name=u'name2')], num_tracks=2) + artists=[Artist(name=u'name2')], num_tracks=2, + musicbrainz_id='id2') self.assertNotEqual(album1, album2) self.assertNotEqual(hash(album1), hash(album2)) @@ -266,6 +306,13 @@ class TrackTest(unittest.TestCase): self.assertEqual(track.bitrate, bitrate) self.assertRaises(AttributeError, setattr, track, 'bitrate', None) + def test_musicbrainz_id(self): + mb_id = u'mb-id' + track = Track(musicbrainz_id=mb_id) + self.assertEqual(track.musicbrainz_id, mb_id) + self.assertRaises(AttributeError, setattr, track, + 'musicbrainz_id', None) + def test_invalid_kwarg(self): test = lambda: Track(foo='baz') self.assertRaises(TypeError, test) @@ -329,14 +376,22 @@ class TrackTest(unittest.TestCase): self.assertEqual(track1, track2) self.assertEqual(hash(track1), hash(track2)) + def test_eq_musibrainz_id(self): + track1 = Track(musicbrainz_id=u'id') + track2 = Track(musicbrainz_id=u'id') + self.assertEqual(track1, track2) + self.assertEqual(hash(track1), hash(track2)) + def test_eq(self): date = dt.date.today() artists = [Artist()] album = Album() track1 = Track(uri=u'uri', name=u'name', artists=artists, album=album, - track_no=1, date=date, length=100, bitrate=100) + track_no=1, date=date, length=100, bitrate=100, + musicbrainz_id='id') track2 = Track(uri=u'uri', name=u'name', artists=artists, album=album, - track_no=1, date=date, length=100, bitrate=100) + track_no=1, date=date, length=100, bitrate=100, + musicbrainz_id='id') self.assertEqual(track1, track2) self.assertEqual(hash(track1), hash(track2)) @@ -394,14 +449,21 @@ class TrackTest(unittest.TestCase): self.assertNotEqual(track1, track2) self.assertNotEqual(hash(track1), hash(track2)) + def test_ne_musicbrainz_id(self): + track1 = Track(musicbrainz_id=u'id1') + track2 = Track(musicbrainz_id=u'id2') + self.assertNotEqual(track1, track2) + self.assertNotEqual(hash(track1), hash(track2)) + def test_ne(self): track1 = Track(uri=u'uri1', name=u'name1', artists=[Artist(name=u'name1')], album=Album(name=u'name1'), - track_no=1, date=dt.date.today(), length=100, bitrate=100) + track_no=1, date=dt.date.today(), length=100, bitrate=100, + musicbrainz_id='id1') track2 = Track(uri=u'uri2', name=u'name2', artists=[Artist(name=u'name2')], album=Album(name=u'name2'), track_no=2, date=dt.date.today()-dt.timedelta(days=1), - length=200, bitrate=200) + length=200, bitrate=200, musicbrainz_id='id2') self.assertNotEqual(track1, track2) self.assertNotEqual(hash(track1), hash(track2)) From 3b7904b8265b9ca3ffbd450bdb3e080f7c8d6ee0 Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Tue, 2 Nov 2010 00:12:21 +0100 Subject: [PATCH 02/15] Add FIXME to data translator for scanner --- mopidy/scanner.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mopidy/scanner.py b/mopidy/scanner.py index 4ccccbdb..7ae40423 100644 --- a/mopidy/scanner.py +++ b/mopidy/scanner.py @@ -18,6 +18,8 @@ def translator(data): artist_kwargs = {} track_kwargs = {} + # FIXME replace with data.get('foo', None) ? + if 'album' in data: album_kwargs['name'] = data['album'] From 5974d696f1d91d2323a703795aa7e30d36d8ae77 Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Tue, 2 Nov 2010 00:12:55 +0100 Subject: [PATCH 03/15] Refactor mpd formater test --- tests/frontends/mpd/serializer_test.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/tests/frontends/mpd/serializer_test.py b/tests/frontends/mpd/serializer_test.py index 77a25e15..4313cfda 100644 --- a/tests/frontends/mpd/serializer_test.py +++ b/tests/frontends/mpd/serializer_test.py @@ -10,6 +10,17 @@ from mopidy.models import Album, Artist, Playlist, Track from tests import data_folder, SkipTest class TrackMpdFormatTest(unittest.TestCase): + track = Track( + uri=u'a uri', + artists=[Artist(name=u'an artist')], + name=u'a name', + album=Album(name=u'an album', num_tracks=13, + artists=[Artist(name=u'an other artist')]), + track_no=7, + date=dt.date(1977, 1, 1), + length=137000, + ) + def setUp(self): settings.LOCAL_MUSIC_PATH = '/dir/subdir' mtime.set_fake_time(1234567) @@ -43,17 +54,7 @@ class TrackMpdFormatTest(unittest.TestCase): self.assert_(('Id', 2) in result) def test_track_to_mpd_format_for_nonempty_track(self): - track = Track( - uri=u'a uri', - artists=[Artist(name=u'an artist')], - name=u'a name', - album=Album(name=u'an album', num_tracks=13, - artists=[Artist(name=u'an other artist')]), - track_no=7, - date=dt.date(1977, 1, 1), - length=137000, - ) - result = translator.track_to_mpd_format(track, position=9, cpid=122) + result = translator.track_to_mpd_format(self.track, position=9, cpid=122) self.assert_(('file', 'a uri') in result) self.assert_(('Time', 137) in result) self.assert_(('Artist', 'an artist') in result) From 988ff66c5e7ef480aa143169daee1398bdf21310 Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Tue, 2 Nov 2010 00:20:59 +0100 Subject: [PATCH 04/15] Add musicbrainz support to scanner translator --- mopidy/scanner.py | 9 +++++++++ tests/scanner_test.py | 21 +++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/mopidy/scanner.py b/mopidy/scanner.py index 7ae40423..e1899c4b 100644 --- a/mopidy/scanner.py +++ b/mopidy/scanner.py @@ -46,6 +46,15 @@ def translator(data): if albumartist_kwargs: album_kwargs['artists'] = [Artist(**albumartist_kwargs)] + if 'musicbrainz-trackid' in data: + track_kwargs['musicbrainz_id'] = data['musicbrainz-trackid'] + + if 'musicbrainz-artistid' in data: + artist_kwargs['musicbrainz_id'] = data['musicbrainz-artistid'] + + if 'musicbrainz-albumid' in data: + album_kwargs['musicbrainz_id'] = data['musicbrainz-albumid'] + track_kwargs['uri'] = data['uri'] track_kwargs['length'] = data['duration'] track_kwargs['album'] = Album(**album_kwargs) diff --git a/tests/scanner_test.py b/tests/scanner_test.py index 141f2ceb..a18b64a1 100644 --- a/tests/scanner_test.py +++ b/tests/scanner_test.py @@ -25,15 +25,20 @@ class TranslatorTest(unittest.TestCase): 'date': FakeGstDate(2006, 1, 1,), 'container-format': u'ID3 tag', 'duration': 4531, + 'musicbrainz-trackid': 'mbtrackid', + 'musicbrainz-albumid': 'mbalbumid', + 'musicbrainz-artistid': 'mbartistid', } self.album = { 'name': 'albumname', 'num_tracks': 2, + 'musicbrainz_id': 'mbalbumid', } self.artist = { 'name': 'name', + 'musicbrainz_id': 'mbartistid', } self.albumartist = { @@ -46,6 +51,7 @@ class TranslatorTest(unittest.TestCase): 'date': date(2006, 1, 1), 'track_no': 1, 'length': 4531, + 'musicbrainz_id': 'mbtrackid', } def build_track(self): @@ -78,16 +84,31 @@ class TranslatorTest(unittest.TestCase): del self.track['name'] self.check() + def test_missing_track_musicbrainz_id(self): + del self.data['musicbrainz-trackid'] + del self.track['musicbrainz_id'] + self.check() + def test_missing_album_name(self): del self.data['album'] del self.album['name'] self.check() + def test_missing_album_musicbrainz_id(self): + del self.data['musicbrainz-albumid'] + del self.album['musicbrainz_id'] + self.check() + def test_missing_artist_name(self): del self.data['artist'] del self.artist['name'] self.check() + def test_missing_artist_musicbrainz_id(self): + del self.data['musicbrainz-artistid'] + del self.artist['musicbrainz_id'] + self.check() + def test_missing_album_artist(self): del self.data['album-artist'] del self.albumartist['name'] From e7a7d342b8d811d9ec8519e38b740b5b5da1f0e7 Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Tue, 2 Nov 2010 00:21:32 +0100 Subject: [PATCH 05/15] Add tests for musicbrainz in mpd formated tracks --- tests/frontends/mpd/serializer_test.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/frontends/mpd/serializer_test.py b/tests/frontends/mpd/serializer_test.py index 4313cfda..164e384f 100644 --- a/tests/frontends/mpd/serializer_test.py +++ b/tests/frontends/mpd/serializer_test.py @@ -67,6 +67,23 @@ class TrackMpdFormatTest(unittest.TestCase): self.assert_(('Id', 122) in result) self.assertEqual(len(result), 10) + def test_track_to_mpd_format_musicbrainz_trackid(self): + track = self.track.copy(musicbrainz_id='foo') + result = translator.track_to_mpd_format(self.track) + self.assert_(('MUSICBRAINZ_TRACKID', 'foo') in result) + + def test_track_to_mpd_format_musicbrainz_albumid(self): + album = self.track.album.copy(musicbrainz_id='foo') + track = self.track.copy(album=album) + result = translator.track_to_mpd_format(self.track) + self.assert_(('MUSICBRAINZ_ALBUMID', 'foo') in result) + + def test_track_to_mpd_format_musicbrainz_artistid(self): + artist = list(self.track.artists)[0].copy(musicbrainz_id='foo') + track = self.track.copy(artists=[artist]) + result = translator.track_to_mpd_format(self.track) + self.assert_(('MUSICBRAINZ_ARTISTID', 'foo') in result) + def test_artists_to_mpd_format(self): artists = [Artist(name=u'ABBA'), Artist(name=u'Beatles')] translated = translator.artists_to_mpd_format(artists) From 31933c2c2fa2c6aac0130070041ee497f50bae49 Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Tue, 2 Nov 2010 00:43:03 +0100 Subject: [PATCH 06/15] Fix musicbrainz support in mpd format track --- mopidy/frontends/mpd/translator.py | 9 +++++++++ tests/frontends/mpd/serializer_test.py | 6 +++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/mopidy/frontends/mpd/translator.py b/mopidy/frontends/mpd/translator.py index e15e1ba5..48c8fe30 100644 --- a/mopidy/frontends/mpd/translator.py +++ b/mopidy/frontends/mpd/translator.py @@ -41,6 +41,15 @@ def track_to_mpd_format(track, position=None, cpid=None): if position is not None and cpid is not None: result.append(('Pos', position)) result.append(('Id', cpid)) + if track.album is not None and track.album.musicbrainz_id is not None: + result.append(('MUSICBRAINZ_ALBUMID', track.album.musicbrainz_id)) + if track.artists: + artists = filter(lambda a: a.musicbrainz_id is not None, track.artists) + if artists: + # FIXME don't use first and best artist? + result.append(('MUSICBRAINZ_ARTISTID', artists[0].musicbrainz_id)) + if track.musicbrainz_id is not None: + result.append(('MUSICBRAINZ_TRACKID', track.musicbrainz_id)) return result MPD_KEY_ORDER = ''' diff --git a/tests/frontends/mpd/serializer_test.py b/tests/frontends/mpd/serializer_test.py index 164e384f..bc01d820 100644 --- a/tests/frontends/mpd/serializer_test.py +++ b/tests/frontends/mpd/serializer_test.py @@ -69,19 +69,19 @@ class TrackMpdFormatTest(unittest.TestCase): def test_track_to_mpd_format_musicbrainz_trackid(self): track = self.track.copy(musicbrainz_id='foo') - result = translator.track_to_mpd_format(self.track) + result = translator.track_to_mpd_format(track) self.assert_(('MUSICBRAINZ_TRACKID', 'foo') in result) def test_track_to_mpd_format_musicbrainz_albumid(self): album = self.track.album.copy(musicbrainz_id='foo') track = self.track.copy(album=album) - result = translator.track_to_mpd_format(self.track) + result = translator.track_to_mpd_format(track) self.assert_(('MUSICBRAINZ_ALBUMID', 'foo') in result) def test_track_to_mpd_format_musicbrainz_artistid(self): artist = list(self.track.artists)[0].copy(musicbrainz_id='foo') track = self.track.copy(artists=[artist]) - result = translator.track_to_mpd_format(self.track) + result = translator.track_to_mpd_format(track) self.assert_(('MUSICBRAINZ_ARTISTID', 'foo') in result) def test_artists_to_mpd_format(self): From 90fbf69b85ae10fa4495294456e68c4c53d810dc Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Tue, 2 Nov 2010 01:00:00 +0100 Subject: [PATCH 07/15] Add albumartistid to scanner --- mopidy/scanner.py | 9 ++++++--- tests/scanner_test.py | 7 +++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/mopidy/scanner.py b/mopidy/scanner.py index e1899c4b..a7d2e664 100644 --- a/mopidy/scanner.py +++ b/mopidy/scanner.py @@ -43,9 +43,6 @@ def translator(data): if 'album-artist' in data: albumartist_kwargs['name'] = data['album-artist'] - if albumartist_kwargs: - album_kwargs['artists'] = [Artist(**albumartist_kwargs)] - if 'musicbrainz-trackid' in data: track_kwargs['musicbrainz_id'] = data['musicbrainz-trackid'] @@ -55,6 +52,12 @@ def translator(data): if 'musicbrainz-albumid' in data: album_kwargs['musicbrainz_id'] = data['musicbrainz-albumid'] + if 'musicbrainz-albumartistid' in data: + albumartist_kwargs['musicbrainz_id'] = data['musicbrainz-albumartistid'] + + if albumartist_kwargs: + album_kwargs['artists'] = [Artist(**albumartist_kwargs)] + track_kwargs['uri'] = data['uri'] track_kwargs['length'] = data['duration'] track_kwargs['album'] = Album(**album_kwargs) diff --git a/tests/scanner_test.py b/tests/scanner_test.py index a18b64a1..a1b53bcf 100644 --- a/tests/scanner_test.py +++ b/tests/scanner_test.py @@ -28,6 +28,7 @@ class TranslatorTest(unittest.TestCase): 'musicbrainz-trackid': 'mbtrackid', 'musicbrainz-albumid': 'mbalbumid', 'musicbrainz-artistid': 'mbartistid', + 'musicbrainz-albumartistid': 'mbalbumartistid', } self.album = { @@ -43,6 +44,7 @@ class TranslatorTest(unittest.TestCase): self.albumartist = { 'name': 'albumartistname', + 'musicbrainz_id': 'mbalbumartistid', } self.track = { @@ -114,6 +116,11 @@ class TranslatorTest(unittest.TestCase): del self.albumartist['name'] self.check() + def test_missing_album_artist_musicbrainz_id(self): + del self.data['musicbrainz-albumartistid'] + del self.albumartist['musicbrainz_id'] + self.check() + def test_missing_date(self): del self.data['date'] del self.track['date'] From 37222d66f5b8e11bf70296aebe6885574d1d482a Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Tue, 2 Nov 2010 01:00:11 +0100 Subject: [PATCH 08/15] Add albumartistid to frontent --- mopidy/frontends/mpd/translator.py | 7 ++++++- tests/frontends/mpd/serializer_test.py | 7 +++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/mopidy/frontends/mpd/translator.py b/mopidy/frontends/mpd/translator.py index 48c8fe30..0073c113 100644 --- a/mopidy/frontends/mpd/translator.py +++ b/mopidy/frontends/mpd/translator.py @@ -43,10 +43,15 @@ def track_to_mpd_format(track, position=None, cpid=None): result.append(('Id', cpid)) if track.album is not None and track.album.musicbrainz_id is not None: result.append(('MUSICBRAINZ_ALBUMID', track.album.musicbrainz_id)) + # FIXME don't use first and best artist? + # FIXME don't duplicate following code? + if track.album is not None and track.album.artists: + artists = filter(lambda a: a.musicbrainz_id is not None, track.album.artists) + if artists: + result.append(('MUSICBRAINZ_ALBUMARTISTID', artists[0].musicbrainz_id)) if track.artists: artists = filter(lambda a: a.musicbrainz_id is not None, track.artists) if artists: - # FIXME don't use first and best artist? result.append(('MUSICBRAINZ_ARTISTID', artists[0].musicbrainz_id)) if track.musicbrainz_id is not None: result.append(('MUSICBRAINZ_TRACKID', track.musicbrainz_id)) diff --git a/tests/frontends/mpd/serializer_test.py b/tests/frontends/mpd/serializer_test.py index bc01d820..7e4500ea 100644 --- a/tests/frontends/mpd/serializer_test.py +++ b/tests/frontends/mpd/serializer_test.py @@ -78,6 +78,13 @@ class TrackMpdFormatTest(unittest.TestCase): result = translator.track_to_mpd_format(track) self.assert_(('MUSICBRAINZ_ALBUMID', 'foo') in result) + def test_track_to_mpd_format_musicbrainz_albumid(self): + artist = list(self.track.artists)[0].copy(musicbrainz_id='foo') + album = self.track.album.copy(artists=[artist]) + track = self.track.copy(album=album) + result = translator.track_to_mpd_format(track) + self.assert_(('MUSICBRAINZ_ALBUMARTISTID', 'foo') in result) + def test_track_to_mpd_format_musicbrainz_artistid(self): artist = list(self.track.artists)[0].copy(musicbrainz_id='foo') track = self.track.copy(artists=[artist]) From 8d30b745d260f00026b0ca787a643273140f7b6f Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Tue, 2 Nov 2010 01:02:41 +0100 Subject: [PATCH 09/15] Add test_musicbrainz_tagcache as reminder that tag cache parser does not know about musicbrainz yet --- tests/backends/local/translator_test.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/backends/local/translator_test.py b/tests/backends/local/translator_test.py index 2f97e45c..3f2a1c12 100644 --- a/tests/backends/local/translator_test.py +++ b/tests/backends/local/translator_test.py @@ -136,3 +136,6 @@ class MPDTagCacheToTracksTest(unittest.TestCase): data_folder('')) uri = path_to_uri(data_folder('song1.mp3')) self.assertEqual(set([Track(uri=uri, length=4000)]), tracks) + + def test_musicbrainz_tagcache(self): + raise SkipTest From 326ade05cc5fbc3511a95ff9f6dda55fa79ec9a0 Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Tue, 2 Nov 2010 17:24:42 +0100 Subject: [PATCH 10/15] Add musicbrainz support to tag_cache parsing --- mopidy/backends/local/translator.py | 25 +++++++++++++++++++------ tests/backends/local/translator_test.py | 11 +++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/mopidy/backends/local/translator.py b/mopidy/backends/local/translator.py index 26c2ad6b..0af8f126 100644 --- a/mopidy/backends/local/translator.py +++ b/mopidy/backends/local/translator.py @@ -96,25 +96,31 @@ def _convert_mpd_data(data, tracks, music_dir): track_kwargs = {} album_kwargs = {} + artist_kwargs = {} if 'track' in data: album_kwargs['num_tracks'] = int(data['track'].split('/')[1]) track_kwargs['track_no'] = int(data['track'].split('/')[0]) if 'artist' in data: - artist = Artist(name=data['artist']) - track_kwargs['artists'] = [artist] - album_kwargs['artists'] = [artist] + artist_kwargs['name'] = data['artist'] # FIXME Newer mpd tag caches support albumartist names if 'album' in data: album_kwargs['name'] = data['album'] - album = Album(**album_kwargs) - track_kwargs['album'] = album if 'title' in data: track_kwargs['name'] = data['title'] + if 'musicbrainz_trackid' in data: + track_kwargs['musicbrainz_id'] = data['musicbrainz_trackid'] + + if 'musicbrainz_albumid' in data: + album_kwargs['musicbrainz_id'] = data['musicbrainz_albumid'] + + if 'musicbrainz_artistid' in data: + artist_kwargs['musicbrainz_id'] = data['musicbrainz_artistid'] + # FIXME what if file is uri - generated tag cache needs to allways make # LOCAL_MUSIC_PATH relative paths or this code must handle uris if data['file'][0] == '/': @@ -122,7 +128,14 @@ def _convert_mpd_data(data, tracks, music_dir): else: path = data['file'] - # FIXME newer mpd tag caches provide musicbrainz ids + if artist_kwargs: + artist = Artist(**artist_kwargs) + album_kwargs['artists'] = [artist] + track_kwargs['artists'] = [artist] + + if album_kwargs: + album = Album(**album_kwargs) + track_kwargs['album'] = album track_kwargs['uri'] = path_to_uri(music_dir, path) track_kwargs['length'] = int(data.get('time', 0)) * 1000 diff --git a/tests/backends/local/translator_test.py b/tests/backends/local/translator_test.py index 3f2a1c12..9347db59 100644 --- a/tests/backends/local/translator_test.py +++ b/tests/backends/local/translator_test.py @@ -138,4 +138,15 @@ class MPDTagCacheToTracksTest(unittest.TestCase): self.assertEqual(set([Track(uri=uri, length=4000)]), tracks) def test_musicbrainz_tagcache(self): + tracks = parse_mpd_tag_cache(data_folder('musicbrainz_tag_cache'), + data_folder('')) + artist = list(expected_tracks[0].artists)[0].copy( + musicbrainz_id='7364dea6-ca9a-48e3-be01-b44ad0d19897') + album = expected_tracks[0].album.copy(artists=[artist], + musicbrainz_id='cb5f1603-d314-4c9c-91e5-e295cfb125d2') + track = expected_tracks[0].copy(artists=[artist], album=album, + musicbrainz_id='90488461-8c1f-4a4e-826b-4c6dc70801f0') + self.assertEqual(track, list(tracks)[0]) + + def test_albumartist_tag_cache(self): raise SkipTest From f58d47fda2ea7ac7d7355fb379de5a8b045e8afa Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Tue, 2 Nov 2010 17:41:21 +0100 Subject: [PATCH 11/15] Add fixmes --- mopidy/backends/local/translator.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/mopidy/backends/local/translator.py b/mopidy/backends/local/translator.py index 0af8f126..8855c1d6 100644 --- a/mopidy/backends/local/translator.py +++ b/mopidy/backends/local/translator.py @@ -105,7 +105,6 @@ def _convert_mpd_data(data, tracks, music_dir): if 'artist' in data: artist_kwargs['name'] = data['artist'] - # FIXME Newer mpd tag caches support albumartist names if 'album' in data: album_kwargs['name'] = data['album'] @@ -121,8 +120,6 @@ def _convert_mpd_data(data, tracks, music_dir): if 'musicbrainz_artistid' in data: artist_kwargs['musicbrainz_id'] = data['musicbrainz_artistid'] - # FIXME what if file is uri - generated tag cache needs to allways make - # LOCAL_MUSIC_PATH relative paths or this code must handle uris if data['file'][0] == '/': path = data['file'][1:] else: From 89b46af983ad99499005a202dc2abefa5c276401 Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Tue, 2 Nov 2010 17:43:32 +0100 Subject: [PATCH 12/15] Add musicbrainz_tag_cache that was missing --- tests/data/musicbrainz_tag_cache | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tests/data/musicbrainz_tag_cache diff --git a/tests/data/musicbrainz_tag_cache b/tests/data/musicbrainz_tag_cache new file mode 100644 index 00000000..010242f4 --- /dev/null +++ b/tests/data/musicbrainz_tag_cache @@ -0,0 +1,19 @@ +info_begin +mpd_version: 0.16.0 +fs_charset: UTF-8 +info_end +songList begin +key: song1.mp3 +file: /song1.mp3 +Time: 4 +Artist: name +Title: trackname +Album: albumname +Track: 1/2 +Date: 2006 +MUSICBRAINZ_ALBUMID: cb5f1603-d314-4c9c-91e5-e295cfb125d2 +MUSICBRAINZ_ALBUMARTISTID: 7364dea6-ca9a-48e3-be01-b44ad0d19897 +MUSICBRAINZ_ARTISTID: 7364dea6-ca9a-48e3-be01-b44ad0d19897 +MUSICBRAINZ_TRACKID: 90488461-8c1f-4a4e-826b-4c6dc70801f0 +mtime: 1272319626 +songList end From 6cba4bc5649bdd570c4558c9ea4ba3c9ad9556f7 Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Tue, 2 Nov 2010 20:19:29 +0100 Subject: [PATCH 13/15] Add albumartist support --- mopidy/backends/local/translator.py | 13 ++++++++++++- tests/backends/local/translator_test.py | 9 ++++++++- tests/data/albumartist_tag_cache | 16 ++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 tests/data/albumartist_tag_cache diff --git a/mopidy/backends/local/translator.py b/mopidy/backends/local/translator.py index 8855c1d6..51522ead 100644 --- a/mopidy/backends/local/translator.py +++ b/mopidy/backends/local/translator.py @@ -97,6 +97,7 @@ def _convert_mpd_data(data, tracks, music_dir): track_kwargs = {} album_kwargs = {} artist_kwargs = {} + albumartist_kwargs = {} if 'track' in data: album_kwargs['num_tracks'] = int(data['track'].split('/')[1]) @@ -104,6 +105,10 @@ def _convert_mpd_data(data, tracks, music_dir): if 'artist' in data: artist_kwargs['name'] = data['artist'] + albumartist_kwargs['name'] = data['artist'] + + if 'albumartist' in data: + albumartist_kwargs['name'] = data['albumartist'] if 'album' in data: album_kwargs['name'] = data['album'] @@ -120,6 +125,9 @@ def _convert_mpd_data(data, tracks, music_dir): if 'musicbrainz_artistid' in data: artist_kwargs['musicbrainz_id'] = data['musicbrainz_artistid'] + if 'musicbrainz_albumartistid' in data: + albumartist_kwargs['musicbrainz_id'] = data['musicbrainz_albumartistid'] + if data['file'][0] == '/': path = data['file'][1:] else: @@ -127,8 +135,11 @@ def _convert_mpd_data(data, tracks, music_dir): if artist_kwargs: artist = Artist(**artist_kwargs) - album_kwargs['artists'] = [artist] track_kwargs['artists'] = [artist] + + if albumartist_kwargs: + albumartist = Artist(**albumartist_kwargs) + album_kwargs['artists'] = [albumartist] if album_kwargs: album = Album(**album_kwargs) diff --git a/tests/backends/local/translator_test.py b/tests/backends/local/translator_test.py index 9347db59..1a3a36b0 100644 --- a/tests/backends/local/translator_test.py +++ b/tests/backends/local/translator_test.py @@ -149,4 +149,11 @@ class MPDTagCacheToTracksTest(unittest.TestCase): self.assertEqual(track, list(tracks)[0]) def test_albumartist_tag_cache(self): - raise SkipTest + tracks = parse_mpd_tag_cache(data_folder('albumartist_tag_cache'), + data_folder('')) + uri = path_to_uri(data_folder('song1.mp3')) + artist = Artist(name='albumartistname') + album = expected_albums[0].copy(artists=[artist]) + track = Track(name='trackname', artists=expected_artists, track_no=1, + album=album, length=4000, uri=uri) + self.assertEqual(track, list(tracks)[0]) diff --git a/tests/data/albumartist_tag_cache b/tests/data/albumartist_tag_cache new file mode 100644 index 00000000..29942a75 --- /dev/null +++ b/tests/data/albumartist_tag_cache @@ -0,0 +1,16 @@ +info_begin +mpd_version: 0.14.2 +fs_charset: UTF-8 +info_end +songList begin +key: song1.mp3 +file: /song1.mp3 +Time: 4 +Artist: name +Title: trackname +Album: albumname +AlbumArtist: albumartistname +Track: 1/2 +Date: 2006 +mtime: 1272319626 +songList end From 9cc053cfd26c752eb65a0a1050992b7bf5ec97e3 Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Tue, 2 Nov 2010 20:20:01 +0100 Subject: [PATCH 14/15] Add albumartist to musicbrainz test --- tests/backends/local/translator_test.py | 6 +++++- tests/data/musicbrainz_tag_cache | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/backends/local/translator_test.py b/tests/backends/local/translator_test.py index 1a3a36b0..b7fd212c 100644 --- a/tests/backends/local/translator_test.py +++ b/tests/backends/local/translator_test.py @@ -142,10 +142,14 @@ class MPDTagCacheToTracksTest(unittest.TestCase): data_folder('')) artist = list(expected_tracks[0].artists)[0].copy( musicbrainz_id='7364dea6-ca9a-48e3-be01-b44ad0d19897') - album = expected_tracks[0].album.copy(artists=[artist], + albumartist = list(expected_tracks[0].artists)[0].copy( + name='albumartistname', + musicbrainz_id='7364dea6-ca9a-48e3-be01-b44ad0d19897') + album = expected_tracks[0].album.copy(artists=[albumartist], musicbrainz_id='cb5f1603-d314-4c9c-91e5-e295cfb125d2') track = expected_tracks[0].copy(artists=[artist], album=album, musicbrainz_id='90488461-8c1f-4a4e-826b-4c6dc70801f0') + self.assertEqual(track, list(tracks)[0]) def test_albumartist_tag_cache(self): diff --git a/tests/data/musicbrainz_tag_cache b/tests/data/musicbrainz_tag_cache index 010242f4..0e9dca46 100644 --- a/tests/data/musicbrainz_tag_cache +++ b/tests/data/musicbrainz_tag_cache @@ -9,6 +9,7 @@ Time: 4 Artist: name Title: trackname Album: albumname +AlbumArtist: albumartistname Track: 1/2 Date: 2006 MUSICBRAINZ_ALBUMID: cb5f1603-d314-4c9c-91e5-e295cfb125d2 From 3fe3ed27b9fba656d99facf4b6c8172a55acbb16 Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Wed, 3 Nov 2010 00:01:32 +0100 Subject: [PATCH 15/15] Document copy method and update musicbrainz field doc --- mopidy/models.py | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/mopidy/models.py b/mopidy/models.py index c45d6769..60569004 100644 --- a/mopidy/models.py +++ b/mopidy/models.py @@ -38,15 +38,29 @@ class ImmutableObject(object): def __ne__(self, other): return not self.__eq__(other) - def copy(self, **kwargs): + def copy(self, **values): + """ + Copy the model with ``field`` updated to new value. + + Examples:: + + # Returns a track with a new name + Track(name='foo').copy(name='bar') + # Return an album with a new number of tracks + Album(num_tracks=2).copy(num_tracks=5) + + :param values: the model field to modify + :type values: dict + :rtype: new instance of the model being copied + """ data = {} for key in self.__dict__.keys(): public_key = key.lstrip('_') - data[public_key] = kwargs.pop(public_key, self.__dict__[key]) - for key in kwargs.keys(): + data[public_key] = values.pop(public_key, self.__dict__[key]) + for key in values.keys(): if hasattr(self, key): - data[key] = kwargs.pop(key) - if kwargs: + data[key] = values.pop(key) + if values: raise TypeError("copy() got an unexpected keyword argument '%s'" % key) return self.__class__(**data) @@ -56,7 +70,7 @@ class Artist(ImmutableObject): :type uri: string :param name: artist name :type name: string - :param musicbrainz_id: musicbrainz id + :param musicbrainz_id: MusicBrainz ID :type musicbrainz_id: string """ @@ -66,7 +80,7 @@ class Artist(ImmutableObject): #: The artist name. Read-only. name = None - #: The musicbrainz id of the artist. Read-only. + #: The MusicBrainz ID of the artist. Read-only. musicbrainz_id = None @@ -80,7 +94,7 @@ class Album(ImmutableObject): :type artists: list of :class:`Artist` :param num_tracks: number of tracks in album :type num_tracks: integer - :param musicbrainz_id: musicbrainz id + :param musicbrainz_id: MusicBrainz ID :type musicbrainz_id: string """ @@ -93,7 +107,7 @@ class Album(ImmutableObject): #: The number of tracks in the album. Read-only. num_tracks = 0 - #: The musicbrainz id of the album. Read-only. + #: The MusicBrainz ID of the album. Read-only. musicbrainz_id = None def __init__(self, *args, **kwargs): @@ -124,7 +138,7 @@ class Track(ImmutableObject): :type length: integer :param bitrate: bitrate in kbit/s :type bitrate: integer - :param musicbrainz_id: musicbrainz id + :param musicbrainz_id: MusicBrainz ID :type musicbrainz_id: string """ @@ -149,7 +163,7 @@ class Track(ImmutableObject): #: The track's bitrate in kbit/s. Read-only. bitrate = None - #: The musicbrainz id of the track. Read-only. + #: The MusicBrainz ID of the track. Read-only. musicbrainz_id = None def __init__(self, *args, **kwargs):