Fix cleanup of orphaned artists and albums on local file provider (#1323)
authornldroid <13297091+nldroid@users.noreply.github.com>
Sat, 1 Jun 2024 14:27:41 +0000 (16:27 +0200)
committerGitHub <noreply@github.com>
Sat, 1 Jun 2024 14:27:41 +0000 (16:27 +0200)
music_assistant/server/providers/filesystem_local/base.py

index 09532d21b3b56515ed294e09b123825400595b45..e0b498945fe96e094780b0882f02cdbdc30976ee 100644 (file)
@@ -39,7 +39,16 @@ from music_assistant.common.models.media_items import (
     Track,
 )
 from music_assistant.common.models.streamdetails import StreamDetails
-from music_assistant.constants import DB_TABLE_PROVIDER_MAPPINGS, VARIOUS_ARTISTS_NAME
+from music_assistant.constants import (
+    DB_TABLE_ALBUM_ARTISTS,
+    DB_TABLE_ALBUM_TRACKS,
+    DB_TABLE_ALBUMS,
+    DB_TABLE_ARTISTS,
+    DB_TABLE_PLAYLOG,
+    DB_TABLE_PROVIDER_MAPPINGS,
+    DB_TABLE_TRACK_ARTISTS,
+    VARIOUS_ARTISTS_NAME,
+)
 from music_assistant.server.controllers.cache import use_cache
 from music_assistant.server.controllers.music import DB_SCHEMA_VERSION
 from music_assistant.server.helpers.compare import compare_strings
@@ -388,6 +397,69 @@ class FileSystemProviderBase(MusicProvider):
         deleted_files = prev_filenames - cur_filenames
         await self._process_deletions(deleted_files)
 
+        # process orphaned albums and artists
+        await self._process_orphaned_albums_and_artists()
+
+    async def _process_orphaned_albums_and_artists(self) -> None:
+        """Process deletion of orphaned albums and artists."""
+        # process orphaned albums and artists
+
+        # Remove albums without any tracks
+        query = (
+            f"SELECT item_id FROM {DB_TABLE_ALBUMS} "
+            f"WHERE item_id not in (select album_id from {DB_TABLE_ALBUM_TRACKS})"
+        )
+        for db_row in await self.mass.music.database.get_rows_from_query(
+            query,
+            limit=100000,
+        ):
+            await self.mass.music.albums.remove_item_from_library(db_row["item_id"])
+
+        # Remove artists without any tracks or albums
+        query = (
+            f"SELECT item_id FROM {DB_TABLE_ARTISTS} "
+            f"WHERE item_id not in ("
+            f"select artist_id from {DB_TABLE_TRACK_ARTISTS} "
+            f"UNION "
+            f"select artist_id from {DB_TABLE_ALBUM_ARTISTS}"
+            ")"
+        )
+        for db_row in await self.mass.music.database.get_rows_from_query(
+            query,
+            limit=100000,
+        ):
+            await self.mass.music.artists.remove_item_from_library(db_row["item_id"])
+
+        # Provider mappings where the album is removed
+        query = (
+            f"SELECT item_id FROM {DB_TABLE_PROVIDER_MAPPINGS} "
+            f"WHERE media_type = 'album' "
+            f"and item_id not in (select item_id from {DB_TABLE_ALBUMS})"
+        )
+        for db_row in await self.mass.music.database.get_rows_from_query(query, limit=100000):
+            await self.mass.music.albums.remove_provider_mappings(
+                db_row["item_id"], self.instance_id
+            )
+
+        # Provider mappings where the artist is removed
+        query = (
+            f"SELECT item_id FROM {DB_TABLE_PROVIDER_MAPPINGS} "
+            f"WHERE media_type = 'artist' "
+            f"and item_id not in (select item_id from {DB_TABLE_ARTISTS})"
+        )
+        for db_row in await self.mass.music.database.get_rows_from_query(query, limit=100000):
+            await self.mass.music.artists.remove_provider_mappings(
+                db_row["item_id"], self.instance_id
+            )
+
+        # Remove albums that are removed from the playlog
+        where_clause = (
+            f"media_type = 'album' "
+            f"and provider = '{self.instance_id}' "
+            f"and item_id not in (select item_id from albums)"
+        )
+        await self.mass.music.database.delete_where_query(DB_TABLE_PLAYLOG, where_clause)
+
     async def _process_deletions(self, deleted_files: set[str]) -> None:
         """Process all deletions."""
         # process deleted tracks/playlists