From 59fba7c45dc74ea9880974e3c2f0b0526f420864 Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Sun, 22 May 2022 01:06:20 +0200 Subject: [PATCH] Fixed some small glitches (#335) * fix exception when audio analyze fails * cleanup event enum * allow provider object to be serialized to dict * emit events on all media library changes --- music_assistant/controllers/music/albums.py | 19 +++++++++++---- music_assistant/controllers/music/artists.py | 19 +++++++++++---- .../controllers/music/playlists.py | 24 ++++++++++++------- music_assistant/controllers/music/radio.py | 22 +++++++++++------ music_assistant/controllers/music/tracks.py | 22 +++++++++++------ music_assistant/helpers/audio.py | 7 +++--- music_assistant/models/enums.py | 24 ++++++++----------- music_assistant/models/media_controller.py | 15 +++++++++++- music_assistant/models/provider.py | 11 ++++++++- 9 files changed, 110 insertions(+), 53 deletions(-) diff --git a/music_assistant/controllers/music/albums.py b/music_assistant/controllers/music/albums.py index 72d97fa5..32a59c4d 100644 --- a/music_assistant/controllers/music/albums.py +++ b/music_assistant/controllers/music/albums.py @@ -94,9 +94,6 @@ class AlbumsController(MediaControllerBase[Album]): # also fetch same album on all providers await self._match(db_item) db_item = await self.get_db_item(db_item.item_id) - self.mass.signal_event( - MassEvent(EventType.ALBUM_ADDED, object_id=db_item.uri, data=db_item) - ) return db_item async def get_provider_album_tracks( @@ -150,7 +147,13 @@ class AlbumsController(MediaControllerBase[Album]): item_id = new_item["item_id"] self.logger.debug("added %s to database", album.name) # return created object - return await self.get_db_item(item_id, db=db) + db_item = await self.get_db_item(item_id, db=db) + self.mass.signal_event( + MassEvent( + EventType.MEDIA_ITEM_ADDED, object_id=db_item.uri, data=db_item + ) + ) + return db_item async def update_db_item( self, @@ -192,7 +195,13 @@ class AlbumsController(MediaControllerBase[Album]): db=db, ) self.logger.debug("updated %s in database: %s", album.name, item_id) - return await self.get_db_item(item_id, db=db) + db_item = await self.get_db_item(item_id, db=db) + self.mass.signal_event( + MassEvent( + EventType.MEDIA_ITEM_UPDATED, object_id=db_item.uri, data=db_item + ) + ) + return db_item async def _match(self, db_album: Album) -> None: """ diff --git a/music_assistant/controllers/music/artists.py b/music_assistant/controllers/music/artists.py index 346871c1..fa4a1612 100644 --- a/music_assistant/controllers/music/artists.py +++ b/music_assistant/controllers/music/artists.py @@ -87,9 +87,6 @@ class ArtistsController(MediaControllerBase[Artist]): # also fetch same artist on all providers await self.match_artist(db_item) db_item = await self.get_db_item(db_item.item_id) - self.mass.signal_event( - MassEvent(EventType.ARTIST_ADDED, object_id=db_item.uri, data=db_item) - ) return db_item async def match_artist(self, db_artist: Artist): @@ -168,7 +165,13 @@ class ArtistsController(MediaControllerBase[Artist]): item_id = new_item["item_id"] self.logger.debug("added %s to database", artist.name) # return created object - return await self.get_db_item(item_id, db=db) + db_item = await self.get_db_item(item_id, db=db) + self.mass.signal_event( + MassEvent( + EventType.MEDIA_ITEM_ADDED, object_id=db_item.uri, data=db_item + ) + ) + return db_item async def update_db_item( self, @@ -200,7 +203,13 @@ class ArtistsController(MediaControllerBase[Artist]): db=db, ) self.logger.debug("updated %s in database: %s", artist.name, item_id) - return await self.get_db_item(item_id, db=db) + db_item = await self.get_db_item(item_id, db=db) + self.mass.signal_event( + MassEvent( + EventType.MEDIA_ITEM_UPDATED, object_id=db_item.uri, data=db_item + ) + ) + return db_item async def _match(self, db_artist: Artist, provider: MusicProvider) -> bool: """Try to find matching artists on given provider for the provided (database) artist.""" diff --git a/music_assistant/controllers/music/playlists.py b/music_assistant/controllers/music/playlists.py index 3f740d44..c7392ae1 100644 --- a/music_assistant/controllers/music/playlists.py +++ b/music_assistant/controllers/music/playlists.py @@ -50,11 +50,7 @@ class PlaylistController(MediaControllerBase[Playlist]): """Add playlist to local db and return the new database item.""" item.metadata.last_refresh = int(time()) await self.mass.metadata.get_playlist_metadata(item) - db_item = await self.add_db_item(item) - self.mass.signal_event( - MassEvent(EventType.PLAYLIST_ADDED, object_id=db_item.uri, data=db_item) - ) - return db_item + return await self.add_db_item(item) async def add_playlist_tracks(self, db_playlist_id: str, uris: List[str]) -> None: """Add multiple tracks to playlist. Creates background tasks to process the action.""" @@ -132,7 +128,9 @@ class PlaylistController(MediaControllerBase[Playlist]): # update local db entry self.mass.signal_event( MassEvent( - type=EventType.PLAYLIST_UPDATED, object_id=db_playlist_id, data=playlist + type=EventType.MEDIA_ITEM_UPDATED, + object_id=db_playlist_id, + data=playlist, ) ) @@ -158,7 +156,9 @@ class PlaylistController(MediaControllerBase[Playlist]): await provider.remove_playlist_tracks(prov.item_id, track_ids_to_remove) self.mass.signal_event( MassEvent( - type=EventType.PLAYLIST_UPDATED, object_id=db_playlist_id, data=playlist + type=EventType.MEDIA_ITEM_UPDATED, + object_id=db_playlist_id, + data=playlist, ) ) @@ -181,7 +181,13 @@ class PlaylistController(MediaControllerBase[Playlist]): item_id = new_item["item_id"] self.logger.debug("added %s to database", playlist.name) # return created object - return await self.get_db_item(item_id, db=db) + db_item = await self.get_db_item(item_id, db=db) + self.mass.signal_event( + MassEvent( + EventType.MEDIA_ITEM_ADDED, object_id=db_item.uri, data=db_item + ) + ) + return db_item async def update_db_item( self, @@ -218,7 +224,7 @@ class PlaylistController(MediaControllerBase[Playlist]): db_item = await self.get_db_item(item_id, db=db) self.mass.signal_event( MassEvent( - type=EventType.PLAYLIST_UPDATED, object_id=item_id, data=playlist + EventType.MEDIA_ITEM_UPDATED, object_id=db_item.uri, data=db_item ) ) return db_item diff --git a/music_assistant/controllers/music/radio.py b/music_assistant/controllers/music/radio.py index ae65a6c1..06eb3755 100644 --- a/music_assistant/controllers/music/radio.py +++ b/music_assistant/controllers/music/radio.py @@ -29,11 +29,7 @@ class RadioController(MediaControllerBase[Radio]): """Add radio to local db and return the new database item.""" item.metadata.last_refresh = int(time()) await self.mass.metadata.get_radio_metadata(item) - db_item = await self.add_db_item(item) - self.mass.signal_event( - MassEvent(EventType.RADIO_ADDED, object_id=db_item.uri, data=db_item) - ) - return db_item + return await self.add_db_item(item) async def add_db_item(self, radio: Radio, db: Optional[Db] = None) -> Radio: """Add a new radio record to the database.""" @@ -53,7 +49,13 @@ class RadioController(MediaControllerBase[Radio]): item_id = new_item["item_id"] self.logger.debug("added %s to database", radio.name) # return created object - return await self.get_db_item(item_id, db=db) + db_item = await self.get_db_item(item_id, db=db) + self.mass.signal_event( + MassEvent( + EventType.MEDIA_ITEM_ADDED, object_id=db_item.uri, data=db_item + ) + ) + return db_item async def update_db_item( self, @@ -85,4 +87,10 @@ class RadioController(MediaControllerBase[Radio]): db=db, ) self.logger.debug("updated %s in database: %s", radio.name, item_id) - return await self.get_db_item(item_id, db=db) + db_item = await self.get_db_item(item_id, db=db) + self.mass.signal_event( + MassEvent( + EventType.MEDIA_ITEM_UPDATED, object_id=db_item.uri, data=db_item + ) + ) + return db_item diff --git a/music_assistant/controllers/music/tracks.py b/music_assistant/controllers/music/tracks.py index 8448aa3a..9a22c620 100644 --- a/music_assistant/controllers/music/tracks.py +++ b/music_assistant/controllers/music/tracks.py @@ -54,11 +54,7 @@ class TracksController(MediaControllerBase[Track]): db_item = await self.add_db_item(item) # also fetch same track on all providers (will also get other quality versions) await self._match(db_item) - db_item = await self.get_db_item(db_item.item_id) - self.mass.signal_event( - MassEvent(EventType.TRACK_ADDED, object_id=db_item.uri, data=db_item) - ) - return db_item + return await self.get_db_item(db_item.item_id) async def versions( self, @@ -162,7 +158,13 @@ class TracksController(MediaControllerBase[Track]): item_id = new_item["item_id"] # return created object self.logger.debug("added %s to database: %s", track.name, item_id) - return await self.get_db_item(item_id, db=db) + db_item = await self.get_db_item(item_id, db=db) + self.mass.signal_event( + MassEvent( + EventType.MEDIA_ITEM_ADDED, object_id=db_item.uri, data=db_item + ) + ) + return db_item async def update_db_item( self, @@ -200,7 +202,13 @@ class TracksController(MediaControllerBase[Track]): db=db, ) self.logger.debug("updated %s in database: %s", track.name, item_id) - return await self.get_db_item(item_id, db=db) + db_item = await self.get_db_item(item_id, db=db) + self.mass.signal_event( + MassEvent( + EventType.MEDIA_ITEM_UPDATED, object_id=db_item.uri, data=db_item + ) + ) + return db_item async def _get_track_artists( self, diff --git a/music_assistant/helpers/audio.py b/music_assistant/helpers/audio.py index 78e06178..727cc4be 100644 --- a/music_assistant/helpers/audio.py +++ b/music_assistant/helpers/audio.py @@ -165,16 +165,15 @@ async def analyze_audio(mass: MusicAssistant, streamdetails: StreamDetails) -> N stdout=asyncio.subprocess.PIPE, stdin=asyncio.subprocess.PIPE if audio_data else None, ) - stdout, stderr = await proc.communicate(audio_data or None) + stdout, _ = await proc.communicate(audio_data or None) try: loudness = float(stdout.decode().strip()) except (ValueError, AttributeError): LOGGER.warning( - "Could not determine integrated loudness of %s/%s - %s %s", + "Could not determine integrated loudness of %s/%s - %s", streamdetails.provider.value, streamdetails.item_id, - stdout.decode(), - stderr.decode(), + stdout.decode() or "received empty value", ) else: await mass.music.set_track_loudness( diff --git a/music_assistant/models/enums.py b/music_assistant/models/enums.py index 022eb6fc..b0046a04 100644 --- a/music_assistant/models/enums.py +++ b/music_assistant/models/enums.py @@ -185,21 +185,17 @@ class PlayerState(Enum): class EventType(Enum): """Enum with possible Events.""" - PLAYER_ADDED = "player added" - PLAYER_UPDATED = "player updated" - STREAM_STARTED = "streaming started" - STREAM_ENDED = "streaming ended" + PLAYER_ADDED = "player_added" + PLAYER_UPDATED = "player_updated" + STREAM_STARTED = "streaming_started" + STREAM_ENDED = "streaming_ended" QUEUE_ADDED = "queue_added" - QUEUE_UPDATED = "queue updated" - QUEUE_ITEMS_UPDATED = "queue items updated" - QUEUE_TIME_UPDATED = "queue time updated" - SHUTDOWN = "application shutdown" - ARTIST_ADDED = "artist added" - ALBUM_ADDED = "album added" - TRACK_ADDED = "track added" - PLAYLIST_ADDED = "playlist added" - PLAYLIST_UPDATED = "playlist updated" - RADIO_ADDED = "radio added" + QUEUE_UPDATED = "queue_updated" + QUEUE_ITEMS_UPDATED = "queue_items_updated" + QUEUE_TIME_UPDATED = "queue_time_updated" + SHUTDOWN = "application_shutdown" + MEDIA_ITEM_ADDED = "media_item_added" + MEDIA_ITEM_UPDATED = "media_item_updated" BACKGROUND_JOB_UPDATED = "background_job_updated" diff --git a/music_assistant/models/media_controller.py b/music_assistant/models/media_controller.py index d14a9790..bf71a31e 100644 --- a/music_assistant/models/media_controller.py +++ b/music_assistant/models/media_controller.py @@ -8,8 +8,9 @@ from typing import TYPE_CHECKING, Generic, List, Optional, Tuple, TypeVar from databases import Database as Db from music_assistant.models.errors import MediaNotFoundError, ProviderUnavailableError +from music_assistant.models.event import MassEvent -from .enums import MediaType, ProviderType +from .enums import EventType, MediaType, ProviderType from .media_items import MediaItemType if TYPE_CHECKING: @@ -139,7 +140,13 @@ class MediaControllerBase(Generic[ItemCls], metaclass=ABCMeta): await prov.library_add(prov_id.item_id, self.media_type) # mark as library item in internal db if not db_item.in_library: + db_item.in_library = True await self.set_db_library(db_item.item_id, True) + self.mass.signal_event( + MassEvent( + EventType.MEDIA_ITEM_UPDATED, object_id=db_item.uri, data=db_item + ) + ) async def remove_from_library( self, @@ -158,7 +165,13 @@ class MediaControllerBase(Generic[ItemCls], metaclass=ABCMeta): await prov.library_remove(prov_id.item_id, self.media_type) # unmark as library item in internal db if db_item.in_library: + db_item.in_library = False await self.set_db_library(db_item.item_id, False) + self.mass.signal_event( + MassEvent( + EventType.MEDIA_ITEM_UPDATED, object_id=db_item.uri, data=db_item + ) + ) async def get_provider_id( self, item: ItemCls, db: Optional[Db] = None diff --git a/music_assistant/models/provider.py b/music_assistant/models/provider.py index 7088b19c..588505bd 100644 --- a/music_assistant/models/provider.py +++ b/music_assistant/models/provider.py @@ -2,7 +2,7 @@ from __future__ import annotations from abc import abstractmethod -from typing import TYPE_CHECKING, AsyncGenerator, List, Optional +from typing import TYPE_CHECKING, Any, AsyncGenerator, Dict, List, Optional from music_assistant.models.config import MusicProviderConfig from music_assistant.models.enums import MediaType, ProviderType @@ -240,6 +240,15 @@ class MusicProvider: """Return unique provider id to distinguish multiple instances of the same provider.""" return self.config.id + def to_dict(self) -> Dict[str, Any]: + """Return (serializable) dict representation of MusicProvider.""" + return { + "type": self.type.value, + "name": self.name, + "id": self.id, + "supported_mediatypes": [x.value for x in self.supported_mediatypes], + } + def _get_library_gen(self, media_type: MediaType) -> AsyncGenerator[MediaItemType]: """Return library generator for given media_type.""" if media_type == MediaType.ARTIST: -- 2.34.1