fix bug in (recursive) item retrieval
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Mon, 15 Apr 2024 22:46:09 +0000 (00:46 +0200)
committerMarcel van der Veldt <m.vanderveldt@outlook.com>
Mon, 15 Apr 2024 22:46:09 +0000 (00:46 +0200)
music_assistant/server/controllers/media/albums.py
music_assistant/server/controllers/media/base.py
music_assistant/server/controllers/media/tracks.py
music_assistant/server/controllers/music.py
music_assistant/server/providers/builtin/__init__.py

index aa1e5a8f50386e88193951859dbf4a597f73f9dd..deb83705deed5a7b68ce9561f843d98937050477 100644 (file)
@@ -13,6 +13,7 @@ from music_assistant.common.models.enums import EventType, ProviderFeature
 from music_assistant.common.models.errors import (
     InvalidDataError,
     MediaNotFoundError,
+    MusicAssistantError,
     UnsupportedFeaturedException,
 )
 from music_assistant.common.models.media_items import (
@@ -76,17 +77,25 @@ class AlbumsController(MediaControllerBase[Album]):
             details=details,
             add_to_library=add_to_library,
         )
-        # append full artist details to full album item
-        album.artists = [
-            await self.mass.music.artists.get(
-                item.item_id,
-                item.provider,
-                lazy=lazy,
-                details=item,
-                add_to_library=add_to_library,
-            )
-            for item in album.artists
-        ]
+        # append artist details to full track item (resolve ItemMappings)
+        album_artists = []
+        for artist in album.artists:
+            if not isinstance(artist, ItemMapping):
+                album_artists.append(artist)
+                continue
+            try:
+                album_artists.append(
+                    await self.mass.music.artists.get(
+                        artist.item_id,
+                        artist.provider,
+                        lazy=lazy,
+                        add_to_library=False,  # TODO: make this configurable
+                    )
+                )
+            except MusicAssistantError as err:
+                # edge case where playlist track has invalid artistdetails
+                self.logger.warning("Unable to fetch artist details %s - %s", artist.uri, str(err))
+        album.artists = album_artists
         return album
 
     async def add_item_to_library(
index ff61dbdc0c59128a120a776f50da111073a3f1d3..977eeb130abe174811ea22a86376a659f98e79a5 100644 (file)
@@ -276,7 +276,11 @@ class MediaControllerBase(Generic[ItemCls], metaclass=ABCMeta):
                 # returns the first provider that is available
                 if not prov_mapping.available:
                     continue
-                if provider := self.mass.get_provider(prov_mapping.provider_instance):
+                if provider := self.mass.get_provider(
+                    prov_mapping.provider_instance
+                    if prefer_unique
+                    else prov_mapping.provider_domain
+                ):
                     if prefer_unique and provider.is_streaming_provider:
                         continue
                     return (prov_mapping.provider_instance, prov_mapping.item_id)
index 70ef210ebe20d500923b71774bc8657d15988937..517a5f4811f46e9b50d031902173d11794791b93 100644 (file)
@@ -12,6 +12,7 @@ from music_assistant.common.models.enums import AlbumType, EventType, MediaType,
 from music_assistant.common.models.errors import (
     InvalidDataError,
     MediaNotFoundError,
+    MusicAssistantError,
     UnsupportedFeaturedException,
 )
 from music_assistant.common.models.media_items import Album, ItemMapping, Track
@@ -75,18 +76,10 @@ class TracksController(MediaControllerBase[Track]):
             details=details,
             add_to_library=add_to_library,
         )
-        # append full album details to full track item
+        # append full album details to full track item (resolve ItemMappings)
         try:
             if album_uri and (album := await self.mass.music.get_item_by_uri(album_uri)):
                 track.album = album
-            elif track.album:
-                track.album = await self.mass.music.albums.get(
-                    track.album.item_id,
-                    track.album.provider,
-                    lazy=lazy,
-                    details=None if isinstance(track.album, ItemMapping) else track.album,
-                    add_to_library=add_to_library,
-                )
             elif provider_instance_id_or_domain == "library":
                 # grab the first album this track is attached to
                 for album_track_row in await self.mass.music.database.get_rows(
@@ -95,9 +88,17 @@ class TracksController(MediaControllerBase[Track]):
                     track.album = await self.mass.music.albums.get_library_item(
                         album_track_row["album_id"]
                     )
-        except MediaNotFoundError:
+            elif isinstance(track.album, ItemMapping) or track.album and not track.album.image:
+                track.album = await self.mass.music.albums.get(
+                    track.album.item_id,
+                    track.album.provider,
+                    lazy=lazy,
+                    details=None if isinstance(track.album, ItemMapping) else track.album,
+                    add_to_library=False,  # TODO: make this configurable
+                )
+        except MusicAssistantError as err:
             # edge case where playlist track has invalid albumdetails
-            self.logger.warning("Unable to fetch album details %s", track.album.uri)
+            self.logger.warning("Unable to fetch album details %s - %s", track.album.uri, str(err))
         # prefer album image if album explicitly given or track has no image on its own
         if (
             (album_uri or not track.metadata.images)
@@ -105,19 +106,25 @@ class TracksController(MediaControllerBase[Track]):
             and track.album.image
         ):
             track.metadata.images = [track.album.image]
-        # append full artist details to full track item
-        full_artists = []
+        # append artist details to full track item (resolve ItemMappings)
+        track_artists = []
         for artist in track.artists:
-            full_artists.append(
-                await self.mass.music.artists.get(
-                    artist.item_id,
-                    artist.provider,
-                    lazy=lazy,
-                    details=None if isinstance(artist, ItemMapping) else artist,
-                    add_to_library=add_to_library,
+            if not isinstance(artist, ItemMapping):
+                track_artists.append(artist)
+                continue
+            try:
+                track_artists.append(
+                    await self.mass.music.artists.get(
+                        artist.item_id,
+                        artist.provider,
+                        lazy=lazy,
+                        add_to_library=False,  # TODO: make this configurable
+                    )
                 )
-            )
-        track.artists = full_artists
+            except MusicAssistantError as err:
+                # edge case where playlist track has invalid artistdetails
+                self.logger.warning("Unable to fetch artist details %s - %s", artist.uri, str(err))
+        track.artists = track_artists
         return track
 
     async def add_item_to_library(self, item: Track, metadata_lookup: bool = True) -> Track:
@@ -236,7 +243,7 @@ class TracksController(MediaControllerBase[Track]):
     ) -> list[Track]:
         """Return all versions of a track we can find on all providers."""
         track = await self.get(item_id, provider_instance_id_or_domain, add_to_library=False)
-        search_query = f"{track.artists[0].name} - {track.name}"
+        search_query = f"{track.artist_str} - {track.name}"
         result: list[Track] = []
         for provider_id in self.mass.music.get_unique_providers():
             provider = self.mass.get_provider(provider_id)
index 2d699d9280523f0eb6436c46994ce8576962d332..02d3da99b772f481353b22bf4f387ef611b86778 100644 (file)
@@ -25,6 +25,7 @@ from music_assistant.common.models.errors import (
     InvalidProviderURI,
     MediaNotFoundError,
     MusicAssistantError,
+    ProviderUnavailableError,
 )
 from music_assistant.common.models.media_items import BrowseFolder, MediaItemType, SearchResults
 from music_assistant.common.models.provider import SyncTask
@@ -342,7 +343,7 @@ class MusicController(CoreController):
         db_rows = await self.mass.music.database.get_rows_from_query(query, limit=limit)
         result: list[MediaItemType] = []
         for db_row in db_rows:
-            with suppress(MediaNotFoundError):
+            with suppress(MediaNotFoundError, ProviderUnavailableError):
                 media_type = MediaType(db_row["media_type"])
                 item = await self.get_item(media_type, db_row["item_id"], db_row["provider"])
                 result.append(item)
index 863440357a233393eaed0ec1e05cac60994c6937..320c22bbc245ae044c0e038fda772c96cbe4f0c8 100644 (file)
@@ -68,6 +68,7 @@ ALL_FAVORITE_TRACKS = "all_favorite_tracks"
 RANDOM_ARTIST = "random_artist"
 RANDOM_ALBUM = "random_album"
 RANDOM_TRACKS = "random_tracks"
+RECENTLY_PLAYED = "recently_played"
 
 BUILTIN_PLAYLISTS = {
     ALL_LIBRARY_TRACKS: "All library tracks",
@@ -75,6 +76,7 @@ BUILTIN_PLAYLISTS = {
     RANDOM_ARTIST: "Random Artist (from library)",
     RANDOM_ALBUM: "Random Album (from library)",
     RANDOM_TRACKS: "100 Random tracks (from library)",
+    RECENTLY_PLAYED: "Recently played tracks",
 }
 
 COLLAGE_IMAGE_PLAYLISTS = (ALL_FAVORITE_TRACKS, ALL_LIBRARY_TRACKS, RANDOM_TRACKS)
@@ -537,3 +539,8 @@ class BuiltinProvider(MusicProvider):
                     count += 1
                     yield PlaylistTrack.from_dict({**artist_track.to_dict(), "position": count})
                 return
+        if builtin_playlist_id == RECENTLY_PLAYED:
+            for track in await self.mass.music.recently_played(250, [MediaType.TRACK]):
+                count += 1
+                yield PlaylistTrack.from_dict({**track.to_dict(), "position": count})
+            return