From cd67f3043627259f35ae6dac151a5905ca9f8671 Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Fri, 29 Apr 2022 21:31:03 +0200 Subject: [PATCH] Fix some small glitches (#275) --- music_assistant/constants.py | 1 + music_assistant/controllers/music/tracks.py | 20 +++++++------- music_assistant/helpers/compare.py | 23 ++++++++++------ music_assistant/helpers/database.py | 27 ++++++++++++------- music_assistant/mass.py | 3 ++- music_assistant/models/media_items.py | 4 +-- music_assistant/providers/filesystem.py | 2 +- music_assistant/providers/qobuz.py | 6 +++-- music_assistant/providers/spotify/__init__.py | 2 +- 9 files changed, 55 insertions(+), 33 deletions(-) diff --git a/music_assistant/constants.py b/music_assistant/constants.py index c2389a4f..25506a42 100755 --- a/music_assistant/constants.py +++ b/music_assistant/constants.py @@ -66,6 +66,7 @@ class BackgroundJob: "id": self.id, "name": self.name, "timestamp": self.status.value, + "status": self.status.value, } diff --git a/music_assistant/controllers/music/tracks.py b/music_assistant/controllers/music/tracks.py index 5cf65cdf..493a51c8 100644 --- a/music_assistant/controllers/music/tracks.py +++ b/music_assistant/controllers/music/tracks.py @@ -136,7 +136,7 @@ class TracksController(MediaControllerBase[Track]): track.sort_name = create_sort_name(track.name) cur_item = None async with self.mass.database.get_db() as _db: - if track.album: + if track.album and not isinstance(track.album, ItemMapping): track.album = ItemMapping.from_item( await self.get_db_item_by_prov_id( track.album.provider, track.album.item_id, db=_db @@ -194,23 +194,24 @@ class TracksController(MediaControllerBase[Track]): async with self.mass.database.get_db() as _db: cur_item = await self.get_db_item(item_id, db=_db) if overwrite: - metadata = track.metadata provider_ids = track.provider_ids track_artists = track.artists + track_album = track.album or cur_item.album else: - metadata = cur_item.metadata.update(track.metadata) provider_ids = {*cur_item.provider_ids, *track.provider_ids} - track_artists = await self._get_track_artists(track, cur_item.artists) - if track.album and not isinstance(track.album, ItemMapping): - track.album = ItemMapping.from_item( + track_artists = cur_item.artists + track.artists + track_album = cur_item.album or track.album + metadata = cur_item.metadata.update(track.metadata, overwrite) + if track_album and not isinstance(track_album, ItemMapping): + track_album = ItemMapping.from_item( await self.get_db_item_by_prov_id( - track.album.provider, track.album.item_id, db=_db + track_album.provider, track_album.item_id, db=_db ) - or await self.mass.music.albums.add_db_item(track.album) + or await self.mass.music.albums.add_db_item(track_album) ) # we store a mapping to artists on the track for easier access/listings - track_artists = await self._get_track_artists(track, cur_item.artists) + track_artists = await self._get_track_artists(track, track_artists) await self.mass.database.update( self.db_table, {"item_id": item_id}, @@ -220,6 +221,7 @@ class TracksController(MediaControllerBase[Track]): "version": track.version if overwrite else cur_item.version, "duration": track.duration if overwrite else cur_item.duration, "artists": json_serializer(track_artists), + "album": json_serializer(track_album), "metadata": json_serializer(metadata), "provider_ids": json_serializer(provider_ids), "isrc": track.isrc or cur_item.isrc, diff --git a/music_assistant/helpers/compare.py b/music_assistant/helpers/compare.py index 56c5cebe..5f6aabc0 100644 --- a/music_assistant/helpers/compare.py +++ b/music_assistant/helpers/compare.py @@ -6,6 +6,8 @@ from typing import TYPE_CHECKING, List import unidecode +from music_assistant.models.media_items import ItemMapping + if TYPE_CHECKING: from music_assistant.models.media_items import ( Album, @@ -81,14 +83,19 @@ def compare_album(left_album: "Album", right_album: "Album"): and left_album.item_id == right_album.item_id ): return True - if left_album.upc and right_album.upc: - if (left_album.upc in right_album.upc) or (right_album.upc in left_album.upc): - # UPC is always 100% accurate match - return True - if left_album.musicbrainz_id and right_album.musicbrainz_id: - if left_album.musicbrainz_id == right_album.musicbrainz_id: - # musicbrainz_id is always 100% accurate match - return True + if not ( + isinstance(left_album, ItemMapping) or isinstance(right_album, ItemMapping) + ): + if left_album.upc and right_album.upc: + if (left_album.upc in right_album.upc) or ( + right_album.upc in left_album.upc + ): + # UPC is always 100% accurate match + return True + if left_album.musicbrainz_id and right_album.musicbrainz_id: + if left_album.musicbrainz_id == right_album.musicbrainz_id: + # musicbrainz_id is always 100% accurate match + return True if not compare_strings(left_album.name, right_album.name): return False if not compare_version(left_album.version, right_album.version): diff --git a/music_assistant/helpers/database.py b/music_assistant/helpers/database.py index 8de2359b..0f652745 100755 --- a/music_assistant/helpers/database.py +++ b/music_assistant/helpers/database.py @@ -11,7 +11,7 @@ from music_assistant.helpers.typing import MusicAssistant # pylint: disable=invalid-name -SCHEMA_VERSION = 3 +SCHEMA_VERSION = 4 TABLE_PROV_MAPPINGS = "provider_mappings" TABLE_TRACK_LOUDNESS = "track_loudness" @@ -21,6 +21,7 @@ TABLE_ALBUMS = "albums" TABLE_TRACKS = "tracks" TABLE_PLAYLISTS = "playlists" TABLE_RADIOS = "radios" +TABLE_CACHE = "cache" class Database: @@ -164,15 +165,22 @@ class Database: if prev_version < 3: # schema version 3: too many breaking changes, rebuild db async with self.get_db() as _db: - await _db.execute("DROP TABLE IF EXISTS artists") - await _db.execute("DROP TABLE IF EXISTS albums") + await _db.execute(f"DROP TABLE IF EXISTS {TABLE_ARTISTS}") + await _db.execute(f"DROP TABLE IF EXISTS {TABLE_ALBUMS}") + await _db.execute(f"DROP TABLE IF EXISTS {TABLE_TRACKS}") + await _db.execute(f"DROP TABLE IF EXISTS {TABLE_PLAYLISTS}") + await _db.execute(f"DROP TABLE IF EXISTS {TABLE_RADIOS}") + await _db.execute(f"DROP TABLE IF EXISTS {TABLE_PROV_MAPPINGS}") + await _db.execute(f"DROP TABLE IF EXISTS {TABLE_CACHE}") + + if prev_version < 4: + # schema version 4: add album to tracks table + async with self.get_db() as _db: await _db.execute("DROP TABLE IF EXISTS tracks") - await _db.execute("DROP TABLE IF EXISTS playlists") - await _db.execute("DROP TABLE IF EXISTS radios") - await _db.execute("DROP TABLE IF EXISTS playlist_tracks") - await _db.execute("DROP TABLE IF EXISTS album_tracks") - await _db.execute("DROP TABLE IF EXISTS provider_mappings") - await _db.execute("DROP TABLE IF EXISTS cache") + if await self.exists(TABLE_PROV_MAPPINGS, _db): + await self.delete( + TABLE_PROV_MAPPINGS, {"media_type": "track"}, db=_db + ) # create db tables await self.__create_database_tables() @@ -248,6 +256,7 @@ class Database: isrc TEXT, musicbrainz_id TEXT, artists json, + album json, metadata json, provider_ids json );""" diff --git a/music_assistant/mass.py b/music_assistant/mass.py index 96eab68e..b5dc5311 100644 --- a/music_assistant/mass.py +++ b/music_assistant/mass.py @@ -233,6 +233,8 @@ class MusicAssistant: def __job_done_cb(self, task: asyncio.Task, job: BackgroundJob): """Call when background job finishes.""" + execution_time = round(time() - job.timestamp, 2) + job.timestamp = execution_time if task.cancelled(): job.status = JobStatus.CANCELLED self.logger.debug("Job [%s] is cancelled.", job.name) @@ -246,7 +248,6 @@ class MusicAssistant: ) else: job.status = JobStatus.FINISHED - execution_time = round(time() - job.timestamp, 2) self.logger.info( "Finished job [%s] in %s seconds.", job.name, execution_time ) diff --git a/music_assistant/models/media_items.py b/music_assistant/models/media_items.py index 765c2933..2fe9b9d8 100755 --- a/music_assistant/models/media_items.py +++ b/music_assistant/models/media_items.py @@ -12,7 +12,7 @@ from music_assistant.helpers.util import create_sort_name MetadataTypes = Union[int, bool, str, List[str]] -JSON_KEYS = ("artists", "artist", "metadata", "provider_ids") +JSON_KEYS = ("artists", "artist", "album", "metadata", "provider_ids") class MediaType(Enum): @@ -182,7 +182,7 @@ class MediaItem(DataClassDictMixin): db_row = dict(db_row) db_row["provider"] = "database" for key in JSON_KEYS: - if key in db_row: + if key in db_row and db_row[key] is not None: db_row[key] = json.loads(db_row[key]) if "in_library" in db_row: db_row["in_library"] = bool(db_row["in_library"]) diff --git a/music_assistant/providers/filesystem.py b/music_assistant/providers/filesystem.py index 39ae574f..e87334ce 100644 --- a/music_assistant/providers/filesystem.py +++ b/music_assistant/providers/filesystem.py @@ -390,7 +390,7 @@ class FileSystemProvider(MusicProvider): MediaItemProviderId(provider=self.id, item_id=prov_item_id, url=filename) ) playlist.owner = self._attr_name - playlist.checksum = os.path.getmtime(filename) + playlist.checksum = str(os.path.getmtime(filename)) return playlist async def _parse_track_from_uri(self, uri): diff --git a/music_assistant/providers/qobuz.py b/music_assistant/providers/qobuz.py index 254c8039..caeedf46 100644 --- a/music_assistant/providers/qobuz.py +++ b/music_assistant/providers/qobuz.py @@ -561,7 +561,9 @@ class QobuzProvider(MusicProvider): if track_obj.get("isrc"): track.isrc = track_obj["isrc"] if track_obj.get("performers"): - track.metadata.performers = track_obj["performers"] + track.metadata.performers = { + x.strip() for x in track_obj["performers"].split("-") + } if track_obj.get("copyright"): track.metadata.copyright = track_obj["copyright"] if track_obj.get("audio_info"): @@ -620,7 +622,7 @@ class QobuzProvider(MusicProvider): ) if img := self.__get_image(playlist_obj): playlist.metadata.images = {MediaItemImage(ImageType.THUMB, img)} - playlist.checksum = playlist_obj["updated_at"] + playlist.checksum = str(playlist_obj["updated_at"]) return playlist async def _auth_token(self): diff --git a/music_assistant/providers/spotify/__init__.py b/music_assistant/providers/spotify/__init__.py index dbe84b01..ec6f8f38 100644 --- a/music_assistant/providers/spotify/__init__.py +++ b/music_assistant/providers/spotify/__init__.py @@ -425,7 +425,7 @@ class SpotifyProvider(MusicProvider): playlist.metadata.images = { MediaItemImage(ImageType.THUMB, playlist_obj["images"][0]["url"]) } - playlist.checksum = playlist_obj["snapshot_id"] + playlist.checksum = str(playlist_obj["snapshot_id"]) return playlist async def get_token(self): -- 2.34.1