fix db lock issue
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Mon, 3 Apr 2023 15:53:29 +0000 (17:53 +0200)
committerMarcel van der Veldt <m.vanderveldt@outlook.com>
Mon, 3 Apr 2023 15:53:29 +0000 (17:53 +0200)
music_assistant/server/controllers/media/albums.py
music_assistant/server/controllers/media/artists.py
music_assistant/server/controllers/media/playlists.py
music_assistant/server/controllers/media/radio.py
music_assistant/server/controllers/media/tracks.py

index 0633778ae46a45f1172057a4fbc681d415ebc9e2..de863b7519120efb6a1e0a7e246d7f73873dcbb8 100644 (file)
@@ -190,34 +190,34 @@ class AlbumsController(MediaControllerBase[Album]):
         """Add a new record to the database."""
         assert item.provider_mappings, "Item is missing provider mapping(s)"
         assert item.artists, f"Album {item.name} is missing artists"
-        async with self._db_add_lock:
-            cur_item = None
-            # always try to grab existing item by musicbrainz_id
-            if item.musicbrainz_id:
-                match = {"musicbrainz_id": item.musicbrainz_id}
-                cur_item = await self.mass.music.database.get_row(self.db_table, match)
-            # try barcode/upc
-            if not cur_item and item.barcode:
-                for barcode in item.barcode:
-                    if search_result := await self.mass.music.database.search(
-                        self.db_table, barcode, "barcode"
-                    ):
-                        cur_item = Album.from_db_row(search_result[0])
-                        break
-            if not cur_item:
-                # fallback to search and match
-                for row in await self.mass.music.database.search(self.db_table, item.name):
-                    row_album = Album.from_db_row(row)
-                    if compare_album(row_album, item):
-                        cur_item = row_album
-                        break
-            if cur_item:
-                # update existing
-                return await self._update_db_item(cur_item.item_id, item)
+        cur_item = None
+        # always try to grab existing item by musicbrainz_id
+        if item.musicbrainz_id:
+            match = {"musicbrainz_id": item.musicbrainz_id}
+            cur_item = await self.mass.music.database.get_row(self.db_table, match)
+        # try barcode/upc
+        if not cur_item and item.barcode:
+            for barcode in item.barcode:
+                if search_result := await self.mass.music.database.search(
+                    self.db_table, barcode, "barcode"
+                ):
+                    cur_item = Album.from_db_row(search_result[0])
+                    break
+        if not cur_item:
+            # fallback to search and match
+            for row in await self.mass.music.database.search(self.db_table, item.name):
+                row_album = Album.from_db_row(row)
+                if compare_album(row_album, item):
+                    cur_item = row_album
+                    break
+        if cur_item:
+            # update existing
+            return await self._update_db_item(cur_item.item_id, item)
 
-            # insert new item
-            album_artists = await self._get_artist_mappings(item, cur_item)
-            sort_artist = album_artists[0].sort_name if album_artists else ""
+        # insert new item
+        album_artists = await self._get_artist_mappings(item, cur_item)
+        sort_artist = album_artists[0].sort_name if album_artists else ""
+        async with self._db_add_lock:
             new_item = await self.mass.music.database.insert(
                 self.db_table,
                 {
@@ -229,11 +229,11 @@ class AlbumsController(MediaControllerBase[Album]):
                 },
             )
             item_id = new_item["item_id"]
-            # update/set provider_mappings table
-            await self._set_provider_mappings(item_id, item.provider_mappings)
-            self.logger.debug("added %s to database", item.name)
-            # return created object
-            return await self.get_db_item(item_id)
+        # update/set provider_mappings table
+        await self._set_provider_mappings(item_id, item.provider_mappings)
+        self.logger.debug("added %s to database", item.name)
+        # return created object
+        return await self.get_db_item(item_id)
 
     async def _update_db_item(
         self, item_id: int, item: Album | ItemMapping, overwrite: bool = False
index b4eb0be810996361b426006a0f53e41dc6416f76..5d98e8033ed0ef67bb3252a4a598f704a513bb6b 100644 (file)
@@ -283,41 +283,41 @@ class ArtistsController(MediaControllerBase[Artist]):
             if item.musicbrainz_id == VARIOUS_ARTISTS_ID:
                 item.name = VARIOUS_ARTISTS
 
-        async with self._db_add_lock:
-            # always try to grab existing item by musicbrainz_id
-            cur_item = None
-            if musicbrainz_id := getattr(item, "musicbrainz_id", None):
-                match = {"musicbrainz_id": musicbrainz_id}
-                cur_item = await self.mass.music.database.get_row(self.db_table, match)
-            if not cur_item:
-                # fallback to exact name match
-                # NOTE: we match an artist by name which could theoretically lead to collisions
-                # but the chance is so small it is not worth the additional overhead of grabbing
-                # the musicbrainz id upfront
-                match = {"sort_name": item.sort_name}
-                for row in await self.mass.music.database.get_rows(self.db_table, match):
-                    row_artist = Artist.from_db_row(row)
-                    if row_artist.sort_name == item.sort_name:
-                        cur_item = row_artist
-                        break
-            if cur_item:
-                # update existing
-                return await self._update_db_item(cur_item.item_id, item)
+        # always try to grab existing item by musicbrainz_id
+        cur_item = None
+        if musicbrainz_id := getattr(item, "musicbrainz_id", None):
+            match = {"musicbrainz_id": musicbrainz_id}
+            cur_item = await self.mass.music.database.get_row(self.db_table, match)
+        if not cur_item:
+            # fallback to exact name match
+            # NOTE: we match an artist by name which could theoretically lead to collisions
+            # but the chance is so small it is not worth the additional overhead of grabbing
+            # the musicbrainz id upfront
+            match = {"sort_name": item.sort_name}
+            for row in await self.mass.music.database.get_rows(self.db_table, match):
+                row_artist = Artist.from_db_row(row)
+                if row_artist.sort_name == item.sort_name:
+                    cur_item = row_artist
+                    break
+        if cur_item:
+            # update existing
+            return await self._update_db_item(cur_item.item_id, item)
 
-            # insert item
-            item.timestamp_added = int(utc_timestamp())
-            item.timestamp_modified = int(utc_timestamp())
-            # edge case: item is an ItemMapping,
-            # try to construct (a half baken) Artist object from it
-            if isinstance(item, ItemMapping):
-                item = Artist.from_dict(item.to_dict())
+        # insert item
+        item.timestamp_added = int(utc_timestamp())
+        item.timestamp_modified = int(utc_timestamp())
+        # edge case: item is an ItemMapping,
+        # try to construct (a half baken) Artist object from it
+        if isinstance(item, ItemMapping):
+            item = Artist.from_dict(item.to_dict())
+        async with self._db_add_lock:
             new_item = await self.mass.music.database.insert(self.db_table, item.to_db_row())
             item_id = new_item["item_id"]
-            # update/set provider_mappings table
-            await self._set_provider_mappings(item_id, item.provider_mappings)
-            self.logger.debug("added %s to database", item.name)
-            # return created object
-            return await self.get_db_item(item_id)
+        # update/set provider_mappings table
+        await self._set_provider_mappings(item_id, item.provider_mappings)
+        self.logger.debug("added %s to database", item.name)
+        # return created object
+        return await self.get_db_item(item_id)
 
     async def _update_db_item(
         self, item_id: int, item: Artist | ItemMapping, overwrite: bool = False
index 789aec25ec90af7a567323a73dc3a20a2f662eb6..8cb38f6f6a127f4b6f76a02d6453649bf67931eb 100644 (file)
@@ -193,21 +193,21 @@ class PlaylistController(MediaControllerBase[Playlist]):
     async def _add_db_item(self, item: Playlist) -> Playlist:
         """Add a new record to the database."""
         assert item.provider_mappings, "Item is missing provider mapping(s)"
+        match = {"name": item.name, "owner": item.owner}
+        if cur_item := await self.mass.music.database.get_row(self.db_table, match):
+            # update existing
+            return await self._update_db_item(cur_item["item_id"], item)
+        # insert new item
+        item.timestamp_added = int(utc_timestamp())
+        item.timestamp_modified = int(utc_timestamp())
         async with self._db_add_lock:
-            match = {"name": item.name, "owner": item.owner}
-            if cur_item := await self.mass.music.database.get_row(self.db_table, match):
-                # update existing
-                return await self._update_db_item(cur_item["item_id"], item)
-            # insert new item
-            item.timestamp_added = int(utc_timestamp())
-            item.timestamp_modified = int(utc_timestamp())
             new_item = await self.mass.music.database.insert(self.db_table, item.to_db_row())
             item_id = new_item["item_id"]
-            # update/set provider_mappings table
-            await self._set_provider_mappings(item_id, item.provider_mappings)
-            self.logger.debug("added %s to database", item.name)
-            # return created object
-            return await self.get_db_item(item_id)
+        # update/set provider_mappings table
+        await self._set_provider_mappings(item_id, item.provider_mappings)
+        self.logger.debug("added %s to database", item.name)
+        # return created object
+        return await self.get_db_item(item_id)
 
     async def _update_db_item(
         self, item_id: int, item: Playlist, overwrite: bool = True
index 8a27bdba70dae995a22e4bbac5baefeab89f1228..2dbb946dd83f3cdbad52d0cf642f547af860c7da 100644 (file)
@@ -79,21 +79,21 @@ class RadioController(MediaControllerBase[Radio]):
     async def _add_db_item(self, item: Radio) -> Radio:
         """Add a new item record to the database."""
         assert item.provider_mappings, "Item is missing provider mapping(s)"
+        match = {"name": item.name}
+        if cur_item := await self.mass.music.database.get_row(self.db_table, match):
+            # update existing
+            return await self._update_db_item(cur_item["item_id"], item)
+        # insert new item
+        item.timestamp_added = int(utc_timestamp())
+        item.timestamp_modified = int(utc_timestamp())
         async with self._db_add_lock:
-            match = {"name": item.name}
-            if cur_item := await self.mass.music.database.get_row(self.db_table, match):
-                # update existing
-                return await self._update_db_item(cur_item["item_id"], item)
-            # insert new item
-            item.timestamp_added = int(utc_timestamp())
-            item.timestamp_modified = int(utc_timestamp())
             new_item = await self.mass.music.database.insert(self.db_table, item.to_db_row())
             item_id = new_item["item_id"]
-            # update/set provider_mappings table
-            await self._set_provider_mappings(item_id, item.provider_mappings)
-            self.logger.debug("added %s to database", item.name)
-            # return created object
-            return await self.get_db_item(item_id)
+        # update/set provider_mappings table
+        await self._set_provider_mappings(item_id, item.provider_mappings)
+        self.logger.debug("added %s to database", item.name)
+        # return created object
+        return await self.get_db_item(item_id)
 
     async def _update_db_item(self, item_id: int, item: Radio, overwrite: bool = True) -> Radio:
         """Update Radio record in the database."""
index 0889d5126001da7b39e1b1d9a6267ff6f9e41378..3f4e6364e44c9623e08561e438624003214cb2c1 100644 (file)
@@ -271,36 +271,34 @@ class TracksController(MediaControllerBase[Track]):
         assert isinstance(item, Track), "Not a full Track object"
         assert item.artists, "Track is missing artist(s)"
         assert item.provider_mappings, "Track is missing provider mapping(s)"
-        async with self._db_add_lock:
-            cur_item = None
+        cur_item = None
 
-            # always try to grab existing item by external_id
-            if item.musicbrainz_id:
-                match = {"musicbrainz_id": item.musicbrainz_id}
-                cur_item = await self.mass.music.database.get_row(self.db_table, match)
-            for isrc in item.isrc:
-                if search_result := await self.mass.music.database.search(
-                    self.db_table, isrc, "isrc"
-                ):
-                    cur_item = Track.from_db_row(search_result[0])
+        # always try to grab existing item by external_id
+        if item.musicbrainz_id:
+            match = {"musicbrainz_id": item.musicbrainz_id}
+            cur_item = await self.mass.music.database.get_row(self.db_table, match)
+        for isrc in item.isrc:
+            if search_result := await self.mass.music.database.search(self.db_table, isrc, "isrc"):
+                cur_item = Track.from_db_row(search_result[0])
+                break
+        if not cur_item:
+            # fallback to matching
+            match = {"sort_name": item.sort_name}
+            for row in await self.mass.music.database.get_rows(self.db_table, match):
+                row_track = Track.from_db_row(row)
+                if compare_track(row_track, item):
+                    cur_item = row_track
                     break
-            if not cur_item:
-                # fallback to matching
-                match = {"sort_name": item.sort_name}
-                for row in await self.mass.music.database.get_rows(self.db_table, match):
-                    row_track = Track.from_db_row(row)
-                    if compare_track(row_track, item):
-                        cur_item = row_track
-                        break
-            if cur_item:
-                # update existing
-                return await self._update_db_item(cur_item.item_id, item)
+        if cur_item:
+            # update existing
+            return await self._update_db_item(cur_item.item_id, item)
 
-            # no existing match found: insert new item
-            track_artists = await self._get_artist_mappings(item)
-            track_albums = await self._get_track_albums(item)
-            sort_artist = track_artists[0].sort_name if track_artists else ""
-            sort_album = track_albums[0].sort_name if track_albums else ""
+        # no existing match found: insert new item
+        track_artists = await self._get_artist_mappings(item)
+        track_albums = await self._get_track_albums(item)
+        sort_artist = track_artists[0].sort_name if track_artists else ""
+        sort_album = track_albums[0].sort_name if track_albums else ""
+        async with self._db_add_lock:
             new_item = await self.mass.music.database.insert(
                 self.db_table,
                 {
@@ -314,11 +312,11 @@ class TracksController(MediaControllerBase[Track]):
                 },
             )
             item_id = new_item["item_id"]
-            # update/set provider_mappings table
-            await self._set_provider_mappings(item_id, item.provider_mappings)
-            # return created object
-            self.logger.debug("added %s to database: %s", item.name, item_id)
-            return await self.get_db_item(item_id)
+        # update/set provider_mappings table
+        await self._set_provider_mappings(item_id, item.provider_mappings)
+        # return created object
+        self.logger.debug("added %s to database: %s", item.name, item_id)
+        return await self.get_db_item(item_id)
 
     async def _update_db_item(
         self, item_id: int, item: Track | ItemMapping, overwrite: bool = False