From 7f4f477c323e7e0ce7e7a1d9ed033aca52703d73 Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Thu, 4 Aug 2011 11:16:12 -0700 Subject: [PATCH] remove old items/albums from database when re-importing items --- beets/importer.py | 12 ++++++++ test/_common.py | 62 ++++++++++++++++++++------------------- test/test_importer.py | 67 +++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 109 insertions(+), 32 deletions(-) diff --git a/beets/importer.py b/beets/importer.py index 3ecda5dbd..f8a3723f6 100644 --- a/beets/importer.py +++ b/beets/importer.py @@ -516,6 +516,7 @@ def apply_choices(config): # Add items to library. We consolidate this at the end to avoid # locking while we do the copying and tag updates. try: + # Add new items. if task.is_album: # Add an album. album = lib.add_album(task.items) @@ -524,6 +525,17 @@ def apply_choices(config): # Add tracks. for item in items: lib.add(item) + + # Remove old entries if we're re-importing old items. Old + # album structures are automatically cleaned up when the + # last item is removed. + for item in items: + dup_items = list(lib.items( + library.MatchQuery('path', item.path) + )) + for dup_item in dup_items: + if dup_item.id != item.id: + lib.remove(dup_item) finally: lib.save() diff --git a/test/_common.py b/test/_common.py index f2af539ad..cffdbc4d0 100644 --- a/test/_common.py +++ b/test/_common.py @@ -31,35 +31,39 @@ log.setLevel(logging.CRITICAL) RSRC = os.path.join(os.path.dirname(__file__), 'rsrc') # Dummy item creation. -def item(): return beets.library.Item({ - 'title': u'the title', - 'artist': u'the artist', - 'albumartist': u'the album artist', - 'album': u'the album', - 'genre': u'the genre', - 'composer': u'the composer', - 'grouping': u'the grouping', - 'year': 1, - 'month': 2, - 'day': 3, - 'track': 4, - 'tracktotal': 5, - 'disc': 6, - 'disctotal': 7, - 'lyrics': u'the lyrics', - 'comments': u'the comments', - 'bpm': 8, - 'comp': True, - 'path': 'somepath', - 'length': 60.0, - 'bitrate': 128000, - 'format': 'FLAC', - 'mb_trackid': 'someID-1', - 'mb_albumid': 'someID-2', - 'mb_artistid': 'someID-3', - 'mb_albumartistid': 'someID-4', - 'album_id': None, -}) +_item_ident = 0 +def item(): + global _item_ident + _item_ident += 1 + return beets.library.Item({ + 'title': u'the title', + 'artist': u'the artist', + 'albumartist': u'the album artist', + 'album': u'the album', + 'genre': u'the genre', + 'composer': u'the composer', + 'grouping': u'the grouping', + 'year': 1, + 'month': 2, + 'day': 3, + 'track': 4, + 'tracktotal': 5, + 'disc': 6, + 'disctotal': 7, + 'lyrics': u'the lyrics', + 'comments': u'the comments', + 'bpm': 8, + 'comp': True, + 'path': 'somepath' + str(_item_ident), + 'length': 60.0, + 'bitrate': 128000, + 'format': 'FLAC', + 'mb_trackid': 'someID-1', + 'mb_albumid': 'someID-2', + 'mb_artistid': 'someID-3', + 'mb_albumartistid': 'someID-4', + 'album_id': None, + }) # Dummy import stuff. def iconfig(lib, **kwargs): diff --git a/test/test_importer.py b/test/test_importer.py index 8d9786fd6..93a7932c4 100644 --- a/test/test_importer.py +++ b/test/test_importer.py @@ -147,9 +147,11 @@ def _call_apply(coros, items, info): coros = [coros] for coro in coros: task = coro.send(task) -def _call_apply_choice(coro, items, choice): +def _call_apply_choice(coro, items, choice, album=True): task = importer.ImportTask(None, None, items) - task.is_album = True + task.is_album = album + if not album: + task.item = items[0] task.set_choice(choice) coro.send(task) @@ -283,6 +285,64 @@ class AsIsApplyTest(unittest.TestCase): self.assertFalse(alb.comp) self.assertEqual(alb.albumartist, self.items[2].artist) +class ApplyExistingItemsTest(unittest.TestCase): + def setUp(self): + self.libdir = os.path.join(_common.RSRC, 'testlibdir') + os.mkdir(self.libdir) + + self.dbpath = os.path.join(_common.RSRC, 'templib.blb') + self.lib = library.Library(self.dbpath, self.libdir) + self.lib.path_formats = { + 'default': '$artist/$title', + } + self.config = _common.iconfig(self.lib, write=False, copy=False) + + self.srcpath = os.path.join(self.libdir, 'srcfile.mp3') + shutil.copy(os.path.join(_common.RSRC, 'full.mp3'), self.srcpath) + self.i = library.Item.from_path(self.srcpath) + self.i.comp = False + + def tearDown(self): + os.remove(self.dbpath) + shutil.rmtree(self.libdir) + + def _apply_asis(self, items, album=True): + """Run the "apply" coroutine.""" + coro = importer.apply_choices(self.config) + coro.next() + _call_apply_choice(coro, items, importer.action.ASIS, album) + + def test_apply_existing_album_does_not_duplicate_item(self): + # First, import an item to add it to the library. + self._apply_asis([self.i]) + + # Get the item's path and import it again. + item = self.lib.items().next() + new_item = library.Item.from_path(item.path) + self._apply_asis([new_item]) + + # Should not be duplicated. + self.assertEqual(len(list(self.lib.items())), 1) + + def test_apply_existing_album_does_not_duplicate_album(self): + # As above. + self._apply_asis([self.i]) + item = self.lib.items().next() + new_item = library.Item.from_path(item.path) + self._apply_asis([new_item]) + + # Should not be duplicated. + self.assertEqual(len(list(self.lib.albums())), 1) + + def test_apply_existing_singleton_does_not_duplicate_album(self): + self._apply_asis([self.i]) + item = self.lib.items().next() + new_item = library.Item.from_path(item.path) + self._apply_asis([new_item], False) + + # Should not be duplicated. + self.assertEqual(len(list(self.lib.items())), 1) + class InferAlbumDataTest(unittest.TestCase): def setUp(self): i1 = _common.item() @@ -345,7 +405,8 @@ class InferAlbumDataTest(unittest.TestCase): self._infer() self.assertEqual(self.items[0].albumartist, self.items[0].artist) - self.assertEqual(self.items[0].mb_albumartistid, self.items[0].mb_artistid) + self.assertEqual(self.items[0].mb_albumartistid, + self.items[0].mb_artistid) def test_apply_lets_album_values_override(self): for item in self.items: