From: marcelveldt Date: Wed, 13 Nov 2019 20:50:33 +0000 (+0100) Subject: some failsafe fixes X-Git-Url: https://git.kitaultman.com/?a=commitdiff_plain;h=7ca3ff93a34c6418b8850e8d9b957b7a09794653;p=music-assistant-server.git some failsafe fixes --- diff --git a/music_assistant/homeassistant.py b/music_assistant/homeassistant.py index 3ec5f741..5c246652 100644 --- a/music_assistant/homeassistant.py +++ b/music_assistant/homeassistant.py @@ -139,6 +139,8 @@ class HomeAssistant(): # call is for one of our players so handle it player_id = self._published_players[entity_id] player = await self.mass.players.get_player(player_id) + if not player: + return if service == 'turn_on': await player.power_on() elif service == 'turn_off': diff --git a/music_assistant/http_streamer.py b/music_assistant/http_streamer.py index ebc7e9f2..73bdd199 100755 --- a/music_assistant/http_streamer.py +++ b/music_assistant/http_streamer.py @@ -444,6 +444,9 @@ class HTTPStreamer(): elif streamdetails['type'] == 'executable': audio_data = subprocess.check_output(streamdetails["path"], shell=True) + elif streamdetails['type'] == 'file': + with open(streamdetails['path'], 'rb') as f: + audio_data = f.read() # calculate BS.1770 R128 integrated loudness with io.BytesIO(audio_data) as tmpfile: data, rate = soundfile.read(tmpfile) diff --git a/music_assistant/music_manager.py b/music_assistant/music_manager.py index f8b7d43f..d5e66244 100755 --- a/music_assistant/music_manager.py +++ b/music_assistant/music_manager.py @@ -57,16 +57,17 @@ class MusicManager(): # schedule sync task self.mass.event_loop.create_task(self.__sync_music_providers()) - async def load_modules(self): + async def load_modules(self, reload_module=None): """Dynamically (un)load musicprovider modules.""" - prev_ids = list(self.providers.keys()) + if reload_module and reload_module in self.providers: + # unload existing module + for player in self.providers[reload_module].players: + await self.mass.players.remove_player(player.player_id) + self.providers.pop(reload_module, None) + LOGGER.info('Unloaded %s module', reload_module) + # load all modules (that are not already loaded) await load_provider_modules(self.mass, self.providers, CONF_KEY_MUSICPROVIDERS) - # schedule sync for any newly added providers - for prov_id in self.providers: - if prov_id not in prev_ids: - self.mass.event_loop.create_task( - self.sync_music_provider(prov_id)) async def item(self, item_id, diff --git a/music_assistant/musicproviders/file.py b/music_assistant/musicproviders/file.py index c5770079..cc01fdc1 100644 --- a/music_assistant/musicproviders/file.py +++ b/music_assistant/musicproviders/file.py @@ -32,15 +32,18 @@ class FileProvider(MusicProvider): Supports having URI's from streaming providers within m3u playlist Should be compatible with LMS ''' + _music_dir = None + _playlists_dir = None async def setup(self, conf): """ setup the provider, return True if succesfull""" - self._music_dir = conf["music_dir"] - self._playlists_dir = conf["playlists_dir"] if not os.path.isdir(conf["music_dir"]): raise FileNotFoundError(f"Directory {conf['music_dir']} does not exist") - if not os.path.isdir(conf["playlists_dir"]): - raise FileNotFoundError(f"Directory {conf['playlists_dir']} does not exist") + self._music_dir = conf["music_dir"] + if os.path.isdir(conf["playlists_dir"]): + self._playlists_dir = conf["playlists_dir"] + else: + self._playlists_dir = None async def search(self, searchstring, media_types=List[MediaType], limit=5): ''' perform search on the provider ''' @@ -67,14 +70,14 @@ class FileProvider(MusicProvider): async def get_library_albums(self) -> List[Album]: ''' get album folders recursively ''' - async for artist in await self.get_library_artists(): + async for artist in self.get_library_artists(): async for album in self.get_artist_albums(artist.item_id): yield album async def get_library_tracks(self) -> List[Track]: ''' get all tracks recursively ''' #TODO: support disk subfolders - async for album in await self.get_library_albums(): + async for album in self.get_library_albums(): async for track in self.get_album_tracks(album.item_id): yield track @@ -230,7 +233,7 @@ class FileProvider(MusicProvider): async def get_artist_toptracks(self, prov_artist_id) -> List[Track]: ''' get a list of random tracks as we have no clue about preference ''' - async for album in await self.get_artist_albums(prov_artist_id): + async for album in self.get_artist_albums(prov_artist_id): async for track in self.get_album_tracks(album.item_id): yield track diff --git a/music_assistant/musicproviders/qobuz.py b/music_assistant/musicproviders/qobuz.py index 1c488a66..80b86d29 100644 --- a/music_assistant/musicproviders/qobuz.py +++ b/music_assistant/musicproviders/qobuz.py @@ -329,7 +329,7 @@ class QobuzProvider(MusicProvider): async def __parse_artist(self, artist_obj): ''' parse qobuz artist object to generic layout ''' artist = Artist() - if not artist_obj.get('id'): + if not artist_obj or not artist_obj.get('id'): return None artist.item_id = artist_obj['id'] artist.provider = self.prov_id @@ -355,7 +355,7 @@ class QobuzProvider(MusicProvider): async def __parse_album(self, album_obj): ''' parse qobuz album object to generic layout ''' album = Album() - if not album_obj.get('id') or not album_obj[ + if not album_obj or not album_obj.get('id') or not album_obj[ "streamable"] or not album_obj["displayable"]: # do not return unavailable items return None @@ -426,7 +426,7 @@ class QobuzProvider(MusicProvider): async def __parse_track(self, track_obj): ''' parse qobuz track object to generic layout ''' track = Track() - if not track_obj.get('id') or not track_obj[ + if not track_obj or not track_obj.get('id') or not track_obj[ "streamable"] or not track_obj["displayable"]: # do not return unavailable items return None @@ -505,7 +505,7 @@ class QobuzProvider(MusicProvider): async def __parse_playlist(self, playlist_obj): ''' parse qobuz playlist object to generic layout ''' playlist = Playlist() - if not playlist_obj.get('id'): + if not playlist_obj or not playlist_obj.get('id'): return None playlist.item_id = playlist_obj['id'] playlist.provider = self.prov_id diff --git a/music_assistant/musicproviders/spotify.py b/music_assistant/musicproviders/spotify.py index 85597e17..f094ad67 100644 --- a/music_assistant/musicproviders/spotify.py +++ b/music_assistant/musicproviders/spotify.py @@ -253,6 +253,8 @@ class SpotifyProvider(MusicProvider): async def __parse_artist(self, artist_obj): ''' parse spotify artist object to generic layout ''' + if not artist_obj: + return None artist = Artist() artist.item_id = artist_obj['id'] artist.provider = self.prov_id @@ -276,6 +278,8 @@ class SpotifyProvider(MusicProvider): async def __parse_album(self, album_obj): ''' parse spotify album object to generic layout ''' + if not album_obj: + return None if 'album' in album_obj: album_obj = album_obj['album'] if not album_obj['id'] or not album_obj.get('is_playable', True): @@ -321,6 +325,8 @@ class SpotifyProvider(MusicProvider): async def __parse_track(self, track_obj): ''' parse spotify track object to generic layout ''' + if not track_obj: + return None if 'track' in track_obj: track_obj = track_obj['track'] if track_obj['is_local'] or not track_obj['id'] or not track_obj[ diff --git a/music_assistant/player_manager.py b/music_assistant/player_manager.py index 9b61a98b..fb26d564 100755 --- a/music_assistant/player_manager.py +++ b/music_assistant/player_manager.py @@ -39,6 +39,13 @@ class PlayerManager(): async def load_modules(self): """Dynamically (un)load musicprovider modules.""" + if reload_module and reload_module in self.providers: + # unload existing module + if hasattr(self.providers[reload_module], 'http_session'): + await self.providers[reload_module].http_session.close() + self.providers.pop(reload_module, None) + LOGGER.info('Unloaded %s module', reload_module) + # load all modules (that are not already loaded) await load_provider_modules(self.mass, self.providers, CONF_KEY_PLAYERPROVIDERS) diff --git a/music_assistant/utils.py b/music_assistant/utils.py index 7b420417..594b3855 100755 --- a/music_assistant/utils.py +++ b/music_assistant/utils.py @@ -216,19 +216,6 @@ async def load_provider_modules(mass, provider_modules, prov_type=CONF_KEY_MUSIC prov_mod = await load_provider_module(mass, module_name, prov_type) if prov_mod: provider_modules[module_name] = prov_mod - # unload modules (if needed) - removed_modules = [] - for prov_id, prov in provider_modules.items(): - if not mass.config[prov_type][prov_id][CONF_ENABLED]: - removed_modules.append(prov_id) - if hasattr(prov, 'http_session'): - await prov.http_session.close() - if prov_type == CONF_KEY_PLAYERPROVIDERS: - for player in prov.players: - await mass.players.remove_player(player.player_id) - for prov_id in removed_modules: - provider_modules.pop(prov_id, None) - LOGGER.info('Unloaded %s module', prov_id) async def load_provider_module(mass, module_name, prov_type): ''' dynamically load music/player provider ''' @@ -253,4 +240,5 @@ async def load_provider_module(mass, module_name, prov_type): else: return None except Exception as exc: - LOGGER.exception("Error loading module %s: %s", module_name, exc) + LOGGER.error("Error loading module %s: %s", module_name, exc) + LOGGER.debug(exc_info=exc) diff --git a/music_assistant/web.py b/music_assistant/web.py index 2a50babe..605507ca 100755 --- a/music_assistant/web.py +++ b/music_assistant/web.py @@ -496,13 +496,13 @@ class Web(): self.mass.event_loop.create_task( self.mass.players.trigger_update(conf_subkey)) elif conf_key == CONF_KEY_MUSICPROVIDERS: - # (re)load music provider modules + # (re)load music provider module self.mass.event_loop.create_task( - self.mass.music.load_modules()) + self.mass.music.load_modules(conf_subkey)) elif conf_key == CONF_KEY_PLAYERPROVIDERS: - # (re)load player provider modules + # (re)load player provider module self.mass.event_loop.create_task( - self.mass.players.load_modules()) + self.mass.players.load_modules(conf_subkey)) else: # other settings need restart result["restart_required"] = True