diff --git a/beets/library.py b/beets/library.py index 1b991cbe1..819645493 100644 --- a/beets/library.py +++ b/beets/library.py @@ -681,12 +681,11 @@ class BaseLibrary(object): # granularity. AlbumInfo proxy objects are used to access fields; # they invoke _album_get and _album_set. - def albuminfo(self, artist, album): + def albuminfo(self, item): """Given an artist and album name, return an AlbumInfo proxy - object whose attributes correspond to information about the - album. + object for the given item's album. """ - return AlbumInfo(self, artist, album) + return AlbumInfo(self, item.artist, item.album) def _album_get(self, artist, album, key): """For the album specified, returns the value associated with @@ -903,4 +902,26 @@ class Library(BaseLibrary): " ORDER BY artist, album, disc, track" c = self.conn.execute(sql, subvals) return ResultIterator(c, self) + + + # Album information. + def albuminfo(self, item): + # Lazily create a row in the albums table if one doesn't + # exist. + sql = 'SELECT id FROM albums WHERE artist=? AND album=?' + c = self.conn.execute(sql, (item.artist, item.album)) + row = c.fetchone() + if not row: + sql = 'INSERT INTO albums (artist, album) VALUES (?, ?)' + self.conn.execute(sql, (item.artist, item.album)) + return super(Library, self).albuminfo(item) + + def _album_get(self, artist, album, key): + sql = 'SELECT %s FROM albums WHERE artist=? AND album=?' % key + c = self.conn.execute(sql, (artist, album)) + return c.fetchone()[0] + + def _album_set(self, artist, album, key, value): + sql = 'UPDATE albums SET %s=? WHERE artist=? AND album=?' % key + self.conn.execute(sql, (value, artist, album)) diff --git a/test/test_db.py b/test/test_db.py index 48a29ef05..3cd386827 100644 --- a/test/test_db.py +++ b/test/test_db.py @@ -328,15 +328,71 @@ class AlbumInfoTest(unittest.TestCase): self.lib.add(self.i) def test_albuminfo_reflects_metadata(self): - ai = self.lib.albuminfo(self.i.artist, self.i.album) + ai = self.lib.albuminfo(self.i) self.assertEqual(ai.artist, self.i.artist) self.assertEqual(ai.album, self.i.album) def test_albuminfo_stores_art(self): - ai = self.lib.albuminfo(self.i.artist, self.i.album) + ai = self.lib.albuminfo(self.i) ai.artpath = '/my/great/art' - new_ai = self.lib.albuminfo(self.i.artist, self.i.album) + new_ai = self.lib.albuminfo(self.i) self.assertEqual(new_ai.artpath, '/my/great/art') + + def test_albuminfo_removed_when_last_item_removed(self): + self.lib.albuminfo(self.i) + c = self.lib.conn.cursor() + c.execute('select * from albums where album=?', (self.i.album,)) + self.assertNotEqual(c.fetchone(), None) + + self.lib.remove(self.i) + + c = self.lib.conn.cursor() + c.execute('select * from albums where album=?', (self.i.album,)) + self.assertEqual(c.fetchone(), None) + + def test_albuminfo_changes_when_item_field_changes(self): + self.lib.albuminfo(self.i) + self.i.album = 'anotherAlbum' + self.lib.store(self.i) + + ai = self.lib.albuminfo(self.i) + self.assertEqual(ai.album, 'anotherAlbum') + + def test_old_albuminfo_removed_when_last_item_changes(self): + oldalbum = self.i.album + self.lib.albuminfo(self.i) + self.i.album = 'anotherAlbum' + self.lib.store(self.i) + + c = self.lib.conn.cursor() + c.execute('select * from albums where album=?', (oldalbum,)) + self.assertEqual(c.fetchone(), None) + + def test_splitting_album_leaves_albuminfo_for_both(self): + i2 = item() + self.lib.add(i2) + self.lib.albuminfo(self.i) + self.lib.albuminfo(i2) + + i2.artist = 'anotherArtist' + self.lib.store(i2) + + ai = self.lib.albuminfo(self.i) + self.assertEqual(ai.artist, self.i.artist) + ai = self.lib.albuminfo(i2) + self.assertEqual(ai.artist, 'anotherArtist') + + def test_albuminfo_for_two_items_doesnt_duplicate_row(self): + i2 = item() + self.lib.add(i2) + self.lib.albuminfo(self.i) + self.lib.albuminfo(i2) + + c = self.lib.conn.cursor() + c.execute('select * from albums where album=?', (self.i.album,)) + # Cursor should only return one row. + self.assertNotEqual(c.fetchone(), None) + self.assertEqual(c.fetchone(), None) def suite(): return unittest.TestLoader().loadTestsFromName(__name__)