# 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':
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)
# 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,
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 '''
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
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
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
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
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
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
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
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):
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[
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)
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 '''
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)
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