Example #1
0
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
Example #2
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)
Example #3
0
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)