Fix artist not marked as 'in library' when exists on multiple providers (#337)
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Sun, 22 May 2022 20:41:55 +0000 (22:41 +0200)
committerGitHub <noreply@github.com>
Sun, 22 May 2022 20:41:55 +0000 (22:41 +0200)
Fix filesystem library race condition with streaming provider

music_assistant/controllers/music/providers/filesystem.py
music_assistant/models/config.py
music_assistant/models/provider.py

index b7328fda184df92b33b956cebec280984fcd1366..97bd135eff2b9730cd1f8aa457cc0cb7fdc0dc34 100644 (file)
@@ -153,14 +153,30 @@ class FileSystemProvider(MusicProvider):
                     if track := await self._parse_track(entry.path, checksum):
                         # process album
                         if track.album:
-                            await self.mass.music.albums.add_db_item(track.album, db=db)
+                            db_album = await self.mass.music.albums.add_db_item(
+                                track.album, db=db
+                            )
+                            if not db_album.in_library:
+                                await self.mass.music.albums.set_db_library(
+                                    db_album.item_id, True, db=db
+                                )
                             # process (album)artist
                             if track.album.artist:
-                                await self.mass.music.artists.add_db_item(
+                                db_artist = await self.mass.music.artists.add_db_item(
                                     track.album.artist, db=db
                                 )
+                                if not db_artist.in_library:
+                                    await self.mass.music.artists.set_db_library(
+                                        db_artist.item_id, True, db=db
+                                    )
                         # add/update track to db
-                        await self.mass.music.tracks.add_db_item(track, db=db)
+                        db_track = await self.mass.music.tracks.add_db_item(
+                            track, db=db
+                        )
+                        if not db_track.in_library:
+                            await self.mass.music.tracks.set_db_library(
+                                db_track.item_id, True, db=db
+                            )
                     elif playlist := await self._parse_playlist(entry.path, checksum):
                         # add/update] playlist to db
                         await self.mass.music.playlists.add_db_item(playlist, db=db)
@@ -321,7 +337,7 @@ class FileSystemProvider(MusicProvider):
             raise MediaNotFoundError(f"Artist not found: {prov_artist_id}")
         # TODO: adjust to json query instead of text search
         query = f"SELECT * FROM albums WHERE artist LIKE '%\"{db_artist.item_id}\"%'"
-        query += f" AND provider_ids like  '%\"{self.type.value}\"%'"
+        query += f" AND provider_ids LIKE '%\"{self.type.value}\"%'"
         return await self.mass.music.albums.get_db_items(query)
 
     async def get_artist_toptracks(self, prov_artist_id: str) -> List[Track]:
@@ -334,7 +350,7 @@ class FileSystemProvider(MusicProvider):
             raise MediaNotFoundError(f"Artist not found: {prov_artist_id}")
         # TODO: adjust to json query instead of text search
         query = f"SELECT * FROM tracks WHERE artists LIKE '%\"{db_artist.item_id}\"%'"
-        query += f" AND provider_ids like  '%\"{self.type.value}\"%'"
+        query += f" AND provider_ids LIKE '%\"{self.type.value}\"%'"
         return await self.mass.music.tracks.get_db_items(query)
 
     async def library_add(self, *args, **kwargs) -> bool:
@@ -430,7 +446,6 @@ class FileSystemProvider(MusicProvider):
         if tags.title:
             track_title = tags.title
         else:
-
             ext = track_path.split(".")[-1]
             track_title = track_path.split(os.sep)[-1]
             track_title = track_title.replace(f".{ext}", "").replace("_", " ")
index b2a8d8b2d4b53df6b993a9e5b4d1fb18e1b59344..a5680d290b0569a4295d668b3498075f58b529aa 100644 (file)
@@ -24,7 +24,7 @@ class MusicProviderConfig:
     def __post_init__(self):
         """Call after init."""
         # create a default (hopefully unique enough) id from type + username/path
-        if not self.id:
+        if not self.id and (self.path or self.username):
             prov_id = f"{self.type.value}_"
             base_str = (self.path or self.username).lower()
             prov_id += (
index 588505bd1a6052e30d4247a95dc620c76d510207..ffded981eeb60d6a5e40b2b661e1331076a63a78 100644 (file)
@@ -191,7 +191,7 @@ class MusicProvider:
         """Run library sync for this provider."""
         # this reference implementation can be overridden with provider specific approach
         # this logic is aimed at streaming/online providers,
-        #  which all have more or less the same structure.
+        # which all have more or less the same structure.
         # filesystem implementation(s) just override this.
         async with self.mass.database.get_db() as db:
             for media_type in self.supported_mediatypes:
@@ -199,8 +199,17 @@ class MusicProvider:
                 controller = self.mass.music.get_controller(media_type)
 
                 # create a set of all previous and current db id's
+                # note we only store the items in the prev_ids list that are
+                # unique to this provider to avoid getting into a mess where
+                # for example an item still exists on disk (in case of file provider)
+                # and no longer favorite on streaming provider.
+                # Bottomline this means that we don't do a full 2 way sync if multiple
+                # providers are attached to the same media item.
                 prev_ids = set()
                 for db_item in await controller.library():
+                    prov_types = {x.prov_type for x in db_item.provider_ids}
+                    if len(prov_types) > 1:
+                        continue
                     for prov_id in db_item.provider_ids:
                         if prov_id.prov_id == self.id:
                             prev_ids.add(db_item.item_id)