diff --git a/emmental/db/libraries.py b/emmental/db/libraries.py index fae61b6..f4aec01 100644 --- a/emmental/db/libraries.py +++ b/emmental/db/libraries.py @@ -28,6 +28,13 @@ class Library(playlist.Playlist): super().__init__(queue=idle.Queue(), **kwargs) self.scan() + def __check_trackid(self, trackid: int) -> bool: + track = self.table.sql.tracks.rows.get(trackid) + if track is not None and not track.path.exists(): + tagger.untag_track(self.table.sql, track) + track.delete() + return True + def __queue_delete(self) -> bool: self.table.delete(self) return True @@ -55,6 +62,9 @@ class Library(playlist.Playlist): self.readdir = path.readdir_async(self.path) if self.readdir is not None: self.online = True + self.load_tracks() + self.queue.push_many(self.__check_trackid, + [(tid,) for tid in self.tracks.trackids]) self.queue.push(self.__queue_tracks) self.tagger = tagger.Thread() return True diff --git a/tests/db/test_libraries.py b/tests/db/test_libraries.py index 851c555..0de54cd 100644 --- a/tests/db/test_libraries.py +++ b/tests/db/test_libraries.py @@ -75,6 +75,8 @@ class TestLibraryObject(tests.util.TestCase): """Test scanning an online Library path.""" self.assertEqual(self.library.queue[0], (self.library._Library__scan_library,)) + self.library.tracks.trackids = {1, 2, 3} + self.library.load_tracks = unittest.mock.Mock() with unittest.mock.patch.object(self.library.queue, "push") as mock_push: @@ -83,6 +85,7 @@ class TestLibraryObject(tests.util.TestCase): mock_is_dir.return_value = True self.assertTrue(self.library.queue.run_task()) + self.library.load_tracks.assert_called() mock_is_dir.assert_called() self.assertTrue(self.library.online) @@ -91,8 +94,11 @@ class TestLibraryObject(tests.util.TestCase): emmental.path.ReaddirThread) self.assertIsInstance(self.library.tagger, emmental.db.tagger.Thread) - self.assertEqual(self.library.queue[0], - (self.library._Library__queue_tracks,)) + self.assertEqual(self.library.queue._tasks, + [(self.library._Library__check_trackid, 1), + (self.library._Library__check_trackid, 2), + (self.library._Library__check_trackid, 3), + (self.library._Library__queue_tracks,)]) def test_scan_offline(self): """Test scanning an offline Library path.""" @@ -166,6 +172,27 @@ class TestLibraryObject(tests.util.TestCase): tagger.tag_file.assert_not_called() tagger.get_result.assert_called_with(self.sql, self.library) + @unittest.mock.patch("emmental.db.tagger.untag_track") + def test_scan_check_trackid(self, mock_untag: unittest.mock.Mock()): + """Test that deleted Tracks are removed during scanning.""" + track = unittest.mock.Mock() + track.path = pathlib.Path("/a/b/c/1.ogg") + + self.library._Library__check_trackid(1) + mock_untag.assert_not_called() + + self.sql.tracks.rows[1] = track + with unittest.mock.patch.object(type(track.path), + "exists") as mock_exists: + mock_exists.return_value = True + self.library._Library__check_trackid(1) + mock_untag.assert_not_called() + track.delete.assert_not_called() + + self.library._Library__check_trackid(1) + mock_untag.assert_called_with(self.sql, track) + track.delete.assert_called() + def test_stop(self): """Test stopping a Library's background work.""" readdir = unittest.mock.Mock()