async def test_lookup_media_with_urls(hass, mock_plex_server): """Test media lookup for media_player.play_media calls from cast/sonos.""" CONTENT_ID_URL = f"{PLEX_URI_SCHEME}{DEFAULT_DATA[CONF_SERVER_IDENTIFIER]}/100" # Test URL format result = process_plex_payload(hass, MEDIA_TYPE_MUSIC, CONTENT_ID_URL, supports_playqueues=False) assert isinstance(result.media, plexapi.audio.Track) assert result.shuffle is False # Test URL format with shuffle CONTENT_ID_URL_WITH_SHUFFLE = CONTENT_ID_URL + "?shuffle=1" result = process_plex_payload(hass, MEDIA_TYPE_MUSIC, CONTENT_ID_URL_WITH_SHUFFLE, supports_playqueues=False) assert isinstance(result.media, plexapi.audio.Track) assert result.shuffle is True assert result.offset == 0
def play_media( # noqa: C901 self, media_type: str, media_id: str, **kwargs: Any) -> None: """ Send the play_media command to the media player. If media_id is a Plex payload, attempt Plex->Sonos playback. If media_id is an Apple Music, Deezer, Sonos, or Tidal share link, attempt playback using the respective service. If media_type is "playlist", media_id should be a Sonos Playlist name. Otherwise, media_id should be a URI. """ # Use 'replace' as the default enqueue option enqueue = kwargs.get(ATTR_MEDIA_ENQUEUE, MediaPlayerEnqueue.REPLACE) if spotify.is_spotify_media_type(media_type): media_type = spotify.resolve_spotify_media_type(media_type) media_id = spotify.spotify_uri_from_media_browser_url(media_id) is_radio = False if media_source.is_media_source_id(media_id): is_radio = media_id.startswith("media-source://radio_browser/") media_type = MEDIA_TYPE_MUSIC media_id = (run_coroutine_threadsafe( media_source.async_resolve_media(self.hass, media_id, self.entity_id), self.hass.loop, ).result().url) if media_type == "favorite_item_id": favorite = self.speaker.favorites.lookup_by_item_id(media_id) if favorite is None: raise ValueError(f"Missing favorite for media_id: {media_id}") self._play_favorite(favorite) return soco = self.coordinator.soco if media_id and media_id.startswith(PLEX_URI_SCHEME): plex_plugin = self.speaker.plex_plugin result = process_plex_payload(self.hass, media_type, media_id, supports_playqueues=False) if result.shuffle: self.set_shuffle(True) if enqueue == MediaPlayerEnqueue.ADD: plex_plugin.add_to_queue(result.media) elif enqueue in ( MediaPlayerEnqueue.NEXT, MediaPlayerEnqueue.PLAY, ): pos = (self.media.queue_position or 0) + 1 new_pos = plex_plugin.add_to_queue(result.media, position=pos) if enqueue == MediaPlayerEnqueue.PLAY: soco.play_from_queue(new_pos - 1) elif enqueue == MediaPlayerEnqueue.REPLACE: soco.clear_queue() plex_plugin.add_to_queue(result.media) soco.play_from_queue(0) return share_link = self.coordinator.share_link if share_link.is_share_link(media_id): if enqueue == MediaPlayerEnqueue.ADD: share_link.add_share_link_to_queue(media_id) elif enqueue in ( MediaPlayerEnqueue.NEXT, MediaPlayerEnqueue.PLAY, ): pos = (self.media.queue_position or 0) + 1 new_pos = share_link.add_share_link_to_queue(media_id, position=pos) if enqueue == MediaPlayerEnqueue.PLAY: soco.play_from_queue(new_pos - 1) elif enqueue == MediaPlayerEnqueue.REPLACE: soco.clear_queue() share_link.add_share_link_to_queue(media_id) soco.play_from_queue(0) elif media_type in (MEDIA_TYPE_MUSIC, MEDIA_TYPE_TRACK): # If media ID is a relative URL, we serve it from HA. media_id = async_process_play_media_url(self.hass, media_id) if enqueue == MediaPlayerEnqueue.ADD: soco.add_uri_to_queue(media_id) elif enqueue in ( MediaPlayerEnqueue.NEXT, MediaPlayerEnqueue.PLAY, ): pos = (self.media.queue_position or 0) + 1 new_pos = soco.add_uri_to_queue(media_id, position=pos) if enqueue == MediaPlayerEnqueue.PLAY: soco.play_from_queue(new_pos - 1) elif enqueue == MediaPlayerEnqueue.REPLACE: soco.play_uri(media_id, force_radio=is_radio) elif media_type == MEDIA_TYPE_PLAYLIST: if media_id.startswith("S:"): item = media_browser.get_media( self.media.library, media_id, media_type) # type: ignore[no-untyped-call] soco.play_uri(item.get_uri()) return try: playlists = soco.get_sonos_playlists() playlist = next(p for p in playlists if p.title == media_id) except StopIteration: _LOGGER.error('Could not find a Sonos playlist named "%s"', media_id) else: soco.clear_queue() soco.add_to_queue(playlist) soco.play_from_queue(0) elif media_type in PLAYABLE_MEDIA_TYPES: item = media_browser.get_media( self.media.library, media_id, media_type) # type: ignore[no-untyped-call] if not item: _LOGGER.error('Could not find "%s" in the library', media_id) return soco.play_uri(item.get_uri()) else: _LOGGER.error('Sonos does not support a media type of "%s"', media_type)
async def test_lookup_media_for_other_integrations( hass, entry, setup_plex_server, requests_mock, playqueue_1234, playqueue_created, ): """Test media lookup for media_player.play_media calls from cast/sonos.""" CONTENT_ID = PLEX_URI_SCHEME + '{"library_name": "Music", "artist_name": "Artist"}' CONTENT_ID_KEY = PLEX_URI_SCHEME + "100" CONTENT_ID_BAD_MEDIA = ( PLEX_URI_SCHEME + '{"library_name": "Music", "artist_name": "Not an Artist"}') CONTENT_ID_PLAYQUEUE = PLEX_URI_SCHEME + '{"playqueue_id": 1234}' CONTENT_ID_BAD_PLAYQUEUE = PLEX_URI_SCHEME + '{"playqueue_id": 1235}' CONTENT_ID_SERVER = ( PLEX_URI_SCHEME + '{"plex_server": "Plex Server 1", "library_name": "Music", "artist_name": "Artist"}' ) CONTENT_ID_SHUFFLE = ( PLEX_URI_SCHEME + '{"library_name": "Music", "artist_name": "Artist", "shuffle": 1}') # Test with no Plex integration available with pytest.raises(HomeAssistantError) as excinfo: process_plex_payload(hass, MEDIA_TYPE_MUSIC, CONTENT_ID) assert "Plex integration not configured" in str(excinfo.value) with patch("homeassistant.components.plex.PlexServer.connect", side_effect=NotFound): # Initialize Plex integration without setting up a server with pytest.raises(AssertionError): await setup_plex_server() # Test with no Plex servers available with pytest.raises(HomeAssistantError) as excinfo: process_plex_payload(hass, MEDIA_TYPE_MUSIC, CONTENT_ID) assert "No Plex servers available" in str(excinfo.value) # Complete setup of a Plex server await hass.config_entries.async_unload(entry.entry_id) await setup_plex_server() # Test lookup success without playqueue result = process_plex_payload(hass, MEDIA_TYPE_MUSIC, CONTENT_ID, supports_playqueues=False) assert isinstance(result.media, plexapi.audio.Artist) assert not result.shuffle # Test media key payload without playqueue result = process_plex_payload(hass, MEDIA_TYPE_MUSIC, CONTENT_ID_KEY, supports_playqueues=False) assert isinstance(result.media, plexapi.audio.Track) assert not result.shuffle # Test with specified server without playqueue result = process_plex_payload(hass, MEDIA_TYPE_MUSIC, CONTENT_ID_SERVER, supports_playqueues=False) assert isinstance(result.media, plexapi.audio.Artist) assert not result.shuffle # Test shuffle without playqueue result = process_plex_payload(hass, MEDIA_TYPE_MUSIC, CONTENT_ID_SHUFFLE, supports_playqueues=False) assert isinstance(result.media, plexapi.audio.Artist) assert result.shuffle # Test with media not found with patch("plexapi.library.LibrarySection.search", return_value=None): with pytest.raises(HomeAssistantError) as excinfo: process_plex_payload(hass, MEDIA_TYPE_MUSIC, CONTENT_ID_BAD_MEDIA) assert f"No {MEDIA_TYPE_MUSIC} results in 'Music' for" in str( excinfo.value) # Test with playqueue requests_mock.get("https://1.2.3.4:32400/playQueues/1234", text=playqueue_1234) result = process_plex_payload(hass, MEDIA_TYPE_MUSIC, CONTENT_ID_PLAYQUEUE) assert isinstance(result.media, plexapi.playqueue.PlayQueue) # Test with invalid playqueue requests_mock.get("https://1.2.3.4:32400/playQueues/1235", status_code=HTTPStatus.NOT_FOUND) with pytest.raises(HomeAssistantError) as excinfo: process_plex_payload(hass, MEDIA_TYPE_MUSIC, CONTENT_ID_BAD_PLAYQUEUE) assert "PlayQueue '1235' could not be found" in str(excinfo.value) # Test playqueue is created with shuffle requests_mock.post("/playqueues", text=playqueue_created) result = process_plex_payload(hass, MEDIA_TYPE_MUSIC, CONTENT_ID_SHUFFLE) assert isinstance(result.media, plexapi.playqueue.PlayQueue)