async def test_process_play_media_url_for_addon(hass, mock_sign_path): """Test it uses the hostname for an addon if available.""" await async_process_ha_core_config( hass, { "internal_url": "http://example.local:8123", "external_url": "https://example.com", }, ) # Not hassio or hassio not loaded yet, don't use supervisor network url hass.config.api = Mock(use_ssl=False, port=8123, local_ip="192.168.123.123") assert (async_process_play_media_url( hass, "/path", for_supervisor_network=True) != "http://homeassistant:8123/path?authSig=bla") # Is hassio and not SSL, use an supervisor network url mock_component(hass, "hassio") assert (async_process_play_media_url(hass, "/path", for_supervisor_network=True) == "http://homeassistant:8123/path?authSig=bla") # Hassio loaded but using SSL, don't use an supervisor network url hass.config.api = Mock(use_ssl=True, port=8123, local_ip="192.168.123.123") assert (async_process_play_media_url( hass, "/path", for_supervisor_network=True) != "https://homeassistant:8123/path?authSig=bla")
async def async_play_media( self, media_type: str, media_id: str, enqueue: MediaPlayerEnqueue | None = None, announce: bool | None = None, **kwargs: Any, ) -> None: """Send the play_media command to the media player.""" # Handle media_source if media_source.is_media_source_id(media_id): sourced_media = await media_source.async_resolve_media( self.hass, media_id, self.entity_id) media_id = sourced_media.url media_id = async_process_play_media_url(self.hass, media_id) queue_opt = QUEUE_OPTION_MAP.get(enqueue, QueueOption.PLAY) if announce is None: announce = "/api/tts_proxy" in media_id if announce: announce_sound = "/api/tts_proxy" in media_id self.hass.create_task( self.player.active_queue.play_alert(media_id, announce_sound)) else: await self.player.active_queue.play_media(media_id, queue_opt)
async def async_play_media(self, media_type: str, media_id: str, **kwargs) -> None: """Play media.""" if media_source.is_media_source_id(media_id): play_item = await media_source.async_resolve_media( self.hass, media_id, self.entity_id ) media_id = play_item.url if self.state == STATE_OFF: await self.async_turn_on() if media_id: parts = media_id.split(":") if parts[0] == "list": if (index := parts[3]) == "-1": index = "0" await self.coordinator.musiccast.play_list_media(index, self._zone_id) return if parts[0] == "presets": index = parts[1] await self.coordinator.musiccast.recall_netusb_preset( self._zone_id, index ) return if parts[0] in ("http", "https") or media_id.startswith("/"): media_id = async_process_play_media_url(self.hass, media_id) await self.coordinator.musiccast.play_url_media( self._zone_id, media_id, "HomeAssistant" ) return
async def async_play_media(self, media_type: str, media_id: str, **kwargs: Any) -> None: """Send the play_media command to the media player.""" # If input (file) has a file format supported by pyatv, then stream it with # RAOP. Otherwise try to play it with regular AirPlay. if media_type == MEDIA_TYPE_APP: await self.atv.apps.launch_app(media_id) return if media_source.is_media_source_id(media_id): play_item = await media_source.async_resolve_media( self.hass, media_id, self.entity_id) media_id = async_process_play_media_url(self.hass, play_item.url) media_type = MEDIA_TYPE_MUSIC if self._is_feature_available( FeatureName.StreamFile) and (media_type == MEDIA_TYPE_MUSIC or await is_streamable(media_id)): _LOGGER.debug("Streaming %s via RAOP", media_id) await self.atv.stream.stream_file(media_id) elif self._is_feature_available(FeatureName.PlayUrl): _LOGGER.debug("Playing %s via AirPlay", media_id) await self.atv.stream.play_url(media_id) else: _LOGGER.error( "Media streaming is not possible with current configuration")
async def async_play_media(self, media_type: str, media_id: str, **kwargs: Any) -> None: """Play a piece of media.""" if media_source.is_media_source_id(media_id): media_type = MEDIA_TYPE_MUSIC play_item = await media_source.async_resolve_media( self.hass, media_id) media_id = async_process_play_media_url(self.hass, play_item.url) if media_type != MEDIA_TYPE_MUSIC: raise HomeAssistantError("Only music media type is supported") _LOGGER.debug("Playing Media %s for %s Speaker", media_id, self.device.name) await self.async_media_stop() try: await self.device.play_audio(media_id, blocking=False) except StreamError as err: raise HomeAssistantError(err) from err else: # update state after starting player self._async_updated_event() # wait until player finishes to update state again await self.device.wait_until_audio_completes() self._async_updated_event()
async def async_play_media(self, media_type, media_id, **kwargs): """Send the media player the command for playing a playlist.""" if media_source.is_media_source_id(media_id): media_type = MEDIA_TYPE_MUSIC play_item = await media_source.async_resolve_media( self.hass, media_id) media_id = play_item.url if media_type == MEDIA_TYPE_PLAYLIST: _LOGGER.debug("Playing playlist: %s", media_id) if media_id in self._playlists: self._currentplaylist = media_id else: self._currentplaylist = None _LOGGER.warning("Unknown playlist name %s", media_id) await self._client.clear() await self._client.load(media_id) await self._client.play() else: media_id = async_process_play_media_url(self.hass, media_id) await self._client.clear() self._currentplaylist = None await self._client.add(media_id) await self._client.play()
async def async_play_media(self, media_type: str, media_id: str, **kwargs: Any) -> None: """Play a piece of media.""" if media_source.is_media_source_id(media_id): media_type = MEDIA_TYPE_URL play_item = await media_source.async_resolve_media( self.hass, media_id, self.entity_id) media_id = play_item.url if media_type in (MEDIA_TYPE_URL, MEDIA_TYPE_MUSIC): media_id = async_process_play_media_url(self.hass, media_id) await self._player.play_url(media_id) return if media_type == "quick_select": # media_id may be an int or a str selects = await self._player.get_quick_selects() try: index: int | None = int(media_id) except ValueError: # Try finding index by name index = next( (index for index, select in selects.items() if select == media_id), None, ) if index is None: raise ValueError(f"Invalid quick select '{media_id}'") await self._player.play_quick_select(index) return if media_type == MEDIA_TYPE_PLAYLIST: playlists = await self._player.heos.get_playlists() playlist = next((p for p in playlists if p.name == media_id), None) if not playlist: raise ValueError(f"Invalid playlist '{media_id}'") add_queue_option = HA_HEOS_ENQUEUE_MAP.get( kwargs.get(ATTR_MEDIA_ENQUEUE)) await self._player.add_to_queue(playlist, add_queue_option) return if media_type == "favorite": # media_id may be an int or str try: index = int(media_id) except ValueError: # Try finding index by name index = next( (index for index, favorite in self._source_manager. favorites.items() if favorite.name == media_id), None, ) if index is None: raise ValueError(f"Invalid favorite '{media_id}'") await self._player.play_favorite(index) return raise ValueError(f"Unsupported media type '{media_type}'")
async def async_play_media(self, media_type, media_id, **kwargs): if media_source.is_media_source_id(media_id): media_type = MEDIA_TYPE_URL play_item = await media_source.async_resolve_media( self.hass, media_id) media_id = play_item.url if media_type in (MEDIA_TYPE_URL, MEDIA_TYPE_MUSIC): media_id = async_process_play_media_url(self.hass, media_id) self.connection.send("play", media_content_id=media_id)
async def async_play_media(self, media_type, media_id, **kwargs): """Play a piece of media.""" if media_source.is_media_source_id(media_id): play_item = await media_source.async_resolve_media( self.hass, media_id) media_id = async_process_play_media_url(self.hass, play_item.url) await self.hass.async_add_executor_job( partial(self.play_media, media_type, media_id, **kwargs))
async def async_play_media(self, media_type, media_id, **kwargs): """Send the play_media command to the media player.""" index = None enqueue: MediaPlayerEnqueue | None = kwargs.get(ATTR_MEDIA_ENQUEUE) if enqueue == MediaPlayerEnqueue.ADD: cmd = "add" elif enqueue == MediaPlayerEnqueue.NEXT: cmd = "insert" elif enqueue == MediaPlayerEnqueue.PLAY: cmd = "play_now" else: cmd = "play" if media_source.is_media_source_id(media_id): media_type = MEDIA_TYPE_MUSIC play_item = await media_source.async_resolve_media( self.hass, media_id, self.entity_id ) media_id = play_item.url if media_type in MEDIA_TYPE_MUSIC: if not media_id.startswith(SQUEEZEBOX_SOURCE_STRINGS): # do not process special squeezebox "source" media ids media_id = async_process_play_media_url(self.hass, media_id) await self._player.async_load_url(media_id, cmd) return if media_type == MEDIA_TYPE_PLAYLIST: try: # a saved playlist by number payload = { "search_id": int(media_id), "search_type": MEDIA_TYPE_PLAYLIST, } playlist = await generate_playlist(self._player, payload) except ValueError: # a list of urls content = json.loads(media_id) playlist = content["urls"] index = content["index"] else: payload = { "search_id": media_id, "search_type": media_type, } playlist = await generate_playlist(self._player, payload) _LOGGER.debug("Generated playlist: %s", playlist) await self._player.async_load_playlist(playlist, cmd) if index is not None: await self._player.async_index(index)
async def test_process_play_media_url(hass, mock_sign_path): """Test it prefixes and signs urls.""" await async_process_ha_core_config( hass, {"internal_url": "http://example.local:8123"}, ) hass.config.api = Mock(use_ssl=False, port=8123, local_ip="192.168.123.123") # Not changing a url that is not a hass url assert (async_process_play_media_url( hass, "https://not-hass.com/path") == "https://not-hass.com/path") # Testing signing hass URLs assert (async_process_play_media_url( hass, "/path") == "http://example.local:8123/path?authSig=bla") assert (async_process_play_media_url(hass, "http://example.local:8123/path") == "http://example.local:8123/path?authSig=bla") assert (async_process_play_media_url(hass, "http://192.168.123.123:8123/path") == "http://192.168.123.123:8123/path?authSig=bla") # Test skip signing URLs that have a query param assert (async_process_play_media_url( hass, "/path?hello=world") == "http://example.local:8123/path?hello=world") assert (async_process_play_media_url( hass, "http://192.168.123.123:8123/path?hello=world") == "http://192.168.123.123:8123/path?hello=world")
async def test_process_play_media_url(hass, mock_sign_path): """Test it prefixes and signs urls.""" await async_process_ha_core_config( hass, {"internal_url": "http://example.local:8123"}, ) hass.config.api = Mock(use_ssl=False, port=8123, local_ip="192.168.123.123") # Not changing a url that is not a hass url assert (async_process_play_media_url( hass, "https://not-hass.com/path") == "https://not-hass.com/path") # Testing signing hass URLs assert (async_process_play_media_url( hass, "/path") == "http://example.local:8123/path?authSig=bla") assert (async_process_play_media_url(hass, "http://example.local:8123/path") == "http://example.local:8123/path?authSig=bla") assert (async_process_play_media_url(hass, "http://192.168.123.123:8123/path") == "http://192.168.123.123:8123/path?authSig=bla") with pytest.raises(HomeAssistantError), patch( "homeassistant.components.media_player.browse_media.get_url", side_effect=NoURLAvailableError, ): async_process_play_media_url(hass, "/path") # Test skip signing URLs that have a query param assert (async_process_play_media_url( hass, "/path?hello=world") == "http://example.local:8123/path?hello=world") assert (async_process_play_media_url( hass, "http://192.168.123.123:8123/path?hello=world") == "http://192.168.123.123:8123/path?hello=world") with pytest.raises(ValueError): async_process_play_media_url(hass, "hello")
async def async_play_media( self, media_type: str, media_id: str, **kwargs: Any ) -> None: """Play a piece of media.""" if media_source.is_media_source_id(media_id): play_item = await media_source.async_resolve_media( self.hass, media_id, self.entity_id ) media_id = async_process_play_media_url(self.hass, play_item.url) await self.coordinator.fully.playSound(media_id, AUDIOMANAGER_STREAM_MUSIC) self._attr_state = STATE_PLAYING self.async_write_ha_state()
async def async_play_media(self, media_type, media_id, **kwargs): """Play media.""" # Handle media_source if media_source.is_media_source_id(media_id): sourced_media = await media_source.async_resolve_media(self.hass, media_id) media_id = sourced_media.url elif media_type != MEDIA_TYPE_MUSIC: _LOGGER.error("Invalid media type") return media_id = async_process_play_media_url(self.hass, media_id) await self.hass.async_add_executor_job(self._player.queue, media_id)
async def async_play_media( self, media_type: str, media_id: str, **kwargs: Any ) -> None: """Send the play command with media url to the media player.""" if media_source.is_media_source_id(media_id): sourced_media = await media_source.async_resolve_media(self.hass, media_id) media_id = sourced_media.url media_id = async_process_play_media_url(self.hass, media_id) await self._client.media_player_command( self._static_info.key, media_url=media_id, )
async def async_play_media(self, media_type: str, media_id: str, **kwargs) -> None: """Send the play_media command to the media player.""" to_send_media_type: str | None = media_type # Handle media_source if media_source.is_media_source_id(media_id): sourced_media = await media_source.async_resolve_media(self.hass, media_id) media_id = sourced_media.url to_send_media_type = sourced_media.mime_type if to_send_media_type and not to_send_media_type.startswith("audio/"): to_send_media_type = None media_id = async_process_play_media_url(self.hass, media_id) await self.player.play_url(media_id, mime_type=to_send_media_type)
async def async_play_media(self, media_type, media_id, **kwargs): """Play media.""" if media_source.is_media_source_id(media_id): media_type = MEDIA_TYPE_URL play_item = await media_source.async_resolve_media( self.hass, media_id) media_id = play_item.url if media_type != MEDIA_TYPE_URL: _LOGGER.warning("Unsupported media_type: %s", media_type) return media_id = async_process_play_media_url(self.hass, media_id) await self._remote.async_play_media(media_type, media_id)
async def async_play_media(self, media_type, media_id, **kwargs): """Send the play_media command to the media player.""" if self.is_grouped and not self.is_master: return if media_source.is_media_source_id(media_id): play_item = await media_source.async_resolve_media( self.hass, media_id, self.entity_id) media_id = play_item.url media_id = async_process_play_media_url(self.hass, media_id) url = f"Play?url={media_id}" return await self.send_bluesound_command(url)
async def async_play_media(self, media_type, media_id, **kwargs): """ Send the play_media command to the media player. If ATTR_MEDIA_ENQUEUE is True, add `media_id` to the current playlist. """ cmd = "play" index = None if kwargs.get(ATTR_MEDIA_ENQUEUE): cmd = "add" if media_source.is_media_source_id(media_id): media_type = MEDIA_TYPE_MUSIC play_item = await media_source.async_resolve_media(self.hass, media_id) media_id = play_item.url if media_type in MEDIA_TYPE_MUSIC: media_id = async_process_play_media_url(self.hass, media_id) await self._player.async_load_url(media_id, cmd) return if media_type == MEDIA_TYPE_PLAYLIST: try: # a saved playlist by number payload = { "search_id": int(media_id), "search_type": MEDIA_TYPE_PLAYLIST, } playlist = await generate_playlist(self._player, payload) except ValueError: # a list of urls content = json.loads(media_id) playlist = content["urls"] index = content["index"] else: payload = { "search_id": media_id, "search_type": media_type, } playlist = await generate_playlist(self._player, payload) _LOGGER.debug("Generated playlist: %s", playlist) await self._player.async_load_playlist(playlist, cmd) if index is not None: await self._player.async_index(index)
async def websocket_resolve_media( hass: HomeAssistant, connection: ActiveConnection, msg: dict ) -> None: """Resolve media.""" try: media = await async_resolve_media(hass, msg["media_content_id"]) except Unresolvable as err: connection.send_error(msg["id"], "resolve_media_failed", str(err)) return connection.send_result( msg["id"], { "url": async_process_play_media_url( hass, media.url, allow_relative_url=True ), "mime_type": media.mime_type, }, )
async def async_play_media(self, media_type, media_id, **kwargs): """Send the play_media command to the media player.""" if media_source.is_media_source_id(media_id): media_type = MEDIA_TYPE_MUSIC play_item = await media_source.async_resolve_media( self.hass, media_id) media_id = play_item.url if media_type != MEDIA_TYPE_MUSIC: _LOGGER.error( "Invalid media type %s. Only %s is supported", media_type, MEDIA_TYPE_MUSIC, ) return media_id = async_process_play_media_url(self.hass, media_id) track_details = {"title": "Home Assistant", "uri": media_id} await self._device.play_media(track_details)
async def async_play_media( self, media_type: str, media_id: str, **kwargs: Any ) -> None: """Send the play_media command to the media player.""" if media_source.is_media_source_id(media_id): media_type = MEDIA_TYPE_URL play_item = await media_source.async_resolve_media(self.hass, media_id) media_id = play_item.url media_type_lower = media_type.lower() if media_type_lower == MEDIA_TYPE_CHANNEL: await self._kodi.play_channel(int(media_id)) elif media_type_lower == MEDIA_TYPE_PLAYLIST: await self._kodi.play_playlist(int(media_id)) elif media_type_lower == "file": await self._kodi.play_file(media_id) elif media_type_lower == "directory": await self._kodi.play_directory(media_id) elif media_type_lower in [ MEDIA_TYPE_ARTIST, MEDIA_TYPE_ALBUM, MEDIA_TYPE_TRACK, ]: await self.async_clear_playlist() await self.async_add_to_playlist(media_type_lower, media_id) await self._kodi.play_playlist(0) elif media_type_lower in [ MEDIA_TYPE_MOVIE, MEDIA_TYPE_EPISODE, MEDIA_TYPE_SEASON, MEDIA_TYPE_TVSHOW, ]: await self._kodi.play_item( {MAP_KODI_MEDIA_TYPES[media_type_lower]: int(media_id)} ) else: media_id = async_process_play_media_url(self.hass, media_id) await self._kodi.play_file(media_id)
async def async_play_media(self, media_type, media_id, **kwargs): """ Send the play_media command to the media player. If ATTR_MEDIA_ENQUEUE is True, add `media_id` to the queue. """ if self.is_grouped and not self.is_master: return if media_source.is_media_source_id(media_id): play_item = await media_source.async_resolve_media( self.hass, media_id) media_id = play_item.url media_id = async_process_play_media_url(self.hass, media_id) url = f"Play?url={media_id}" if kwargs.get(ATTR_MEDIA_ENQUEUE): return await self.send_bluesound_command(url) return await self.send_bluesound_command(url)
async def async_play_media(self, media_type, media_id, **kwargs): """Play media from a URL or file.""" # Handle media_source if media_source.is_media_source_id(media_id): sourced_media = await media_source.async_resolve_media(self.hass, media_id) media_id = sourced_media.url elif media_type != MEDIA_TYPE_MUSIC: _LOGGER.error( "Invalid media type %s. Only %s is supported", media_type, MEDIA_TYPE_MUSIC, ) return media_id = async_process_play_media_url(self.hass, media_id) def play(): self._vlc.set_media(self._instance.media_new(media_id)) self._vlc.play() await self.hass.async_add_executor_job(play) self._state = STATE_PLAYING
async def async_play_media(self, media_type, media_id, **kwargs): """Play a URI.""" if media_source.is_media_source_id(media_id): media_type = MEDIA_TYPE_MUSIC play_item = await media_source.async_resolve_media( self.hass, media_id, self.entity_id) media_id = play_item.url if media_type == MEDIA_TYPE_MUSIC: media_id = async_process_play_media_url(self.hass, media_id) saved_state = self.state # save play state saved_mute = self.is_volume_muted sleep_future = asyncio.create_task( asyncio.sleep(self._tts_pause_time) ) # start timing now, but not exact because of fd buffer + tts latency await self._pause_and_wait_for_callback() await self._save_and_set_tts_volumes() # save position saved_song_position = self._player["item_progress_ms"] saved_queue = (self._queue if self._queue["count"] > 0 else None ) # stash queue if saved_queue: saved_queue_position = next( i for i, item in enumerate(saved_queue["items"]) if item["id"] == self._player["item_id"]) self._tts_requested = True await sleep_future await self._api.add_to_queue(uris=media_id, playback="start", clear=True) try: await asyncio.wait_for(self._tts_playing_event.wait(), timeout=TTS_TIMEOUT) # we have started TTS, now wait for completion await asyncio.sleep( self._queue["items"][0]["length_ms"] / 1000 # player may not have updated yet so grab length from queue + self._tts_pause_time) except asyncio.TimeoutError: self._tts_requested = False _LOGGER.warning("TTS request timed out") self._tts_playing_event.clear() # TTS done, return to normal await self.async_turn_on() # restore outputs and volumes if saved_mute: # mute if we were muted await self.async_mute_volume(True) if self._use_pipe_control(): # resume pipe await self._api.add_to_queue( uris=self._sources_uris[self._source], clear=True) if saved_state == STATE_PLAYING: await self.async_media_play() else: # restore stashed queue if saved_queue: uris = "" for item in saved_queue["items"]: uris += item["uri"] + "," await self._api.add_to_queue( uris=uris, playback="start", playback_from_position=saved_queue_position, clear=True, ) await self._api.seek(position_ms=saved_song_position) if saved_state == STATE_PAUSED: await self.async_media_pause() elif saved_state != STATE_PLAYING: await self.async_media_stop() else: _LOGGER.debug("Media type '%s' not supported", media_type)