From 95f56097a32d5899375961e1b8d321f3e8ec44d4 Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Wed, 5 Apr 2023 01:37:17 +0200 Subject: [PATCH] fix addming to db from file providers --- music_assistant/common/models/media_items.py | 4 + .../server/controllers/media/base.py | 29 +++--- .../server/controllers/media/tracks.py | 90 +++++++++++-------- 3 files changed, 70 insertions(+), 53 deletions(-) diff --git a/music_assistant/common/models/media_items.py b/music_assistant/common/models/media_items.py index 9321cbd9..32f75f30 100755 --- a/music_assistant/common/models/media_items.py +++ b/music_assistant/common/models/media_items.py @@ -335,6 +335,10 @@ class TrackAlbumMapping(ItemMapping): disc_number: int | None = None track_number: int | None = None + def __hash__(self): + """Return custom hash.""" + return hash((self.media_type, self.provider, self.item_id)) + @dataclass class Track(MediaItem): diff --git a/music_assistant/server/controllers/media/base.py b/music_assistant/server/controllers/media/base.py index a2aea7b9..12d16cda 100644 --- a/music_assistant/server/controllers/media/base.py +++ b/music_assistant/server/controllers/media/base.py @@ -11,7 +11,7 @@ from typing import TYPE_CHECKING, Generic, TypeVar from music_assistant.common.helpers.json import serialize_to_json from music_assistant.common.models.enums import EventType, MediaType, ProviderFeature -from music_assistant.common.models.errors import MediaNotFoundError +from music_assistant.common.models.errors import InvalidDataError, MediaNotFoundError from music_assistant.common.models.media_items import ( ItemMapping, MediaItemType, @@ -546,14 +546,13 @@ class MediaControllerBase(Generic[ItemCls], metaclass=ABCMeta): overwrite: bool = False, ) -> list[ItemMapping]: """Extract (database) album/track artist(s) as ItemMapping.""" - if not update_item or isinstance(update_item, ItemMapping): - return org_item.artists - if overwrite and update_item.provider_mappings: - return update_item.artists - item_artists: set[ItemMapping] = set() - for item in (org_item, update_item): - for artist in item.artists: - item_artists.add(await self._get_artist_mapping(artist)) + if update_item is None or isinstance(update_item, ItemMapping): + source_artists = org_item.artists + elif overwrite and update_item.artists: + source_artists = update_item.artists + else: + source_artists = org_item.artists + update_item.artists + item_artists = {await self._get_artist_mapping(artist) for artist in source_artists} # use intermediate set to prevent duplicates # filter various artists if multiple artists if len(item_artists) > 1: @@ -573,12 +572,14 @@ class MediaControllerBase(Generic[ItemCls], metaclass=ABCMeta): return ItemMapping.from_item(db_artist) # try to request the full item - artist = await self.mass.music.artists.get_provider_item( + with suppress(MediaNotFoundError, AssertionError, InvalidDataError): + db_artist = await self.mass.music.artists.add(artist, skip_metadata_lookup=True) + return ItemMapping.from_item(db_artist) + # fallback to just the provider item + album = await self.mass.music.albums.get_provider_item( artist.item_id, artist.provider, fallback=artist ) - if isinstance(artist, ItemMapping): + if isinstance(album, ItemMapping): # this can happen for unavailable items return artist - - db_artist = await self.mass.music.artists.add(artist, skip_metadata_lookup=True) - return ItemMapping.from_item(db_artist) + return ItemMapping.from_item(album) diff --git a/music_assistant/server/controllers/media/tracks.py b/music_assistant/server/controllers/media/tracks.py index f0f9236e..15ea8d34 100644 --- a/music_assistant/server/controllers/media/tracks.py +++ b/music_assistant/server/controllers/media/tracks.py @@ -2,6 +2,7 @@ from __future__ import annotations import asyncio +from contextlib import suppress from music_assistant.common.helpers.datetime import utc_timestamp from music_assistant.common.helpers.json import serialize_to_json @@ -335,8 +336,8 @@ class TracksController(MediaControllerBase[Track]): provider_mappings = self._get_provider_mappings(cur_item, item, overwrite) if getattr(item, "isrc", None): cur_item.isrc.update(item.isrc) - track_artists = await self._get_artist_mappings(cur_item, item) - track_albums = await self._get_track_albums(cur_item, item) + track_artists = await self._get_artist_mappings(cur_item, item, overwrite=overwrite) + track_albums = await self._get_track_albums(cur_item, item, overwrite=overwrite) async with self._db_add_lock: await self.mass.music.database.update( self.db_table, @@ -361,42 +362,51 @@ class TracksController(MediaControllerBase[Track]): async def _get_track_albums( self, - base_track: Track, - upd_track: Track | ItemMapping | None = None, + org_item: DbTrack, + update_item: Track | ItemMapping | None = None, + overwrite: bool = False, ) -> list[TrackAlbumMapping]: """Extract all (unique) albums of track as TrackAlbumMapping.""" - track_albums: list[TrackAlbumMapping] = [] - # existing TrackAlbumMappings are starting point - if base_track.albums: - track_albums = base_track.albums - elif upd_track and getattr(upd_track, "albums", None): - track_albums = upd_track.albums - # append update item album if needed - if upd_track and getattr(upd_track, "album", None): - mapping = await self._get_album_mapping(upd_track.album) - mapping = TrackAlbumMapping.from_dict( - { - **mapping.to_dict(), - "disc_number": upd_track.disc_number, - "track_number": upd_track.track_number, - } - ) - if mapping not in track_albums: - track_albums.append(mapping) - # append base item album if needed - elif base_track and base_track.album: - mapping = await self._get_album_mapping(base_track.album) - mapping = TrackAlbumMapping.from_dict( - { - **mapping.to_dict(), - "disc_number": base_track.disc_number, - "track_number": base_track.track_number, - } - ) - if mapping not in track_albums: - track_albums.append(mapping) - - return track_albums + if update_item is None or isinstance(update_item, ItemMapping) and org_item.albums: + # already TrackAlbumMappings + return org_item.albums + track_albums: set[TrackAlbumMapping] = set() + # add base albums (only if not overwriting) + if ( + not overwrite + or update_item is None + or isinstance(update_item, ItemMapping) + or not (update_item.album or update_item.albums) + ): + track_albums.update(org_item.albums) + if org_item.album: + mapping = await self._get_album_mapping(update_item.album) + track_albums.add( + TrackAlbumMapping.from_dict( + { + **mapping.to_dict(), + "disc_number": org_item.disc_number, + "track_number": org_item.track_number, + } + ) + ) + # album(s) from update item + if update_item and not isinstance(update_item, ItemMapping): + if update_item.albums: + track_albums.update(update_item.albums) + if update_item.album: + mapping = await self._get_album_mapping(update_item.album) + track_albums.add( + TrackAlbumMapping.from_dict( + { + **mapping.to_dict(), + "disc_number": update_item.disc_number, + "track_number": update_item.track_number, + } + ) + ) + # use intermediate set to prevent duplicates + return list(track_albums) async def _get_album_mapping( self, @@ -414,12 +424,14 @@ class TracksController(MediaControllerBase[Track]): return ItemMapping.from_item(db_album) # try to request the full item + with suppress(MediaNotFoundError, AssertionError, InvalidDataError): + db_album = await self.mass.music.albums.add(album, skip_metadata_lookup=True) + return ItemMapping.from_item(db_album) + # fallback to just the provider item album = await self.mass.music.albums.get_provider_item( album.item_id, album.provider, fallback=album ) if isinstance(album, ItemMapping): # this can happen for unavailable items return album - - db_album = await self.mass.music.albums.add(album, skip_metadata_lookup=True) - return ItemMapping.from_item(db_album) + return ItemMapping.from_item(album) -- 2.34.1