From 8c311cbaa05dc88250ed56479a5e3786646415a5 Mon Sep 17 00:00:00 2001 From: jjlawren Date: Wed, 28 Apr 2021 12:58:05 -0500 Subject: [PATCH] Handle special Plex library sections (#49525) --- homeassistant/components/plex/models.py | 25 ++++++-- tests/components/plex/conftest.py | 37 +++++++++++- tests/components/plex/test_init.py | 73 ++++++++++++++++++++++- tests/fixtures/plex/livetv_sessions.xml | 17 ++++++ tests/fixtures/plex/session_live_tv.xml | 14 +++++ tests/fixtures/plex/session_transient.xml | 13 ++++ tests/fixtures/plex/session_unknown.xml | 13 ++++ 7 files changed, 186 insertions(+), 6 deletions(-) create mode 100644 tests/fixtures/plex/livetv_sessions.xml create mode 100644 tests/fixtures/plex/session_live_tv.xml create mode 100644 tests/fixtures/plex/session_transient.xml create mode 100644 tests/fixtures/plex/session_unknown.xml diff --git a/homeassistant/components/plex/models.py b/homeassistant/components/plex/models.py index af1343095f0..2dc7b83b439 100644 --- a/homeassistant/components/plex/models.py +++ b/homeassistant/components/plex/models.py @@ -1,4 +1,6 @@ """Models to represent various Plex objects used in the integration.""" +import logging + from homeassistant.components.media_player.const import ( MEDIA_TYPE_MOVIE, MEDIA_TYPE_MUSIC, @@ -7,7 +9,15 @@ from homeassistant.components.media_player.const import ( ) from homeassistant.util import dt as dt_util -LIVE_TV_SECTION = -4 +LIVE_TV_SECTION = "Live TV" +TRANSIENT_SECTION = "Preroll" +UNKNOWN_SECTION = "Unknown" +SPECIAL_SECTIONS = { + -2: TRANSIENT_SECTION, + -4: LIVE_TV_SECTION, +} + +_LOGGER = logging.getLogger(__name__) class PlexSession: @@ -66,8 +76,15 @@ class PlexSession: if media.duration: self.media_duration = int(media.duration / 1000) - if media.librarySectionID == LIVE_TV_SECTION: - self.media_library_title = "Live TV" + if media.librarySectionID in SPECIAL_SECTIONS: + self.media_library_title = SPECIAL_SECTIONS[media.librarySectionID] + elif media.librarySectionID < 1: + self.media_library_title = UNKNOWN_SECTION + _LOGGER.warning( + "Unknown library section ID (%s) for title '%s', please create an issue", + media.librarySectionID, + media.title, + ) else: self.media_library_title = ( media.section().title if media.librarySectionID is not None else "" @@ -115,7 +132,7 @@ class PlexSession: """Get the image URL from a media object.""" thumb_url = media.thumbUrl if media.type == "episode" and not self.plex_server.option_use_episode_art: - if media.librarySectionID == LIVE_TV_SECTION: + if SPECIAL_SECTIONS.get(media.librarySectionID) == LIVE_TV_SECTION: thumb_url = media.grandparentThumb else: thumb_url = media.url(media.grandparentThumb) diff --git a/tests/components/plex/conftest.py b/tests/components/plex/conftest.py index 358ab293bf0..bd2fc6a7fa8 100644 --- a/tests/components/plex/conftest.py +++ b/tests/components/plex/conftest.py @@ -278,6 +278,30 @@ def session_plexweb_fixture(): return load_fixture("plex/session_plexweb.xml") +@pytest.fixture(name="session_transient", scope="session") +def session_transient_fixture(): + """Load a transient session payload and return it.""" + return load_fixture("plex/session_transient.xml") + + +@pytest.fixture(name="session_unknown", scope="session") +def session_unknown_fixture(): + """Load a hypothetical unknown session payload and return it.""" + return load_fixture("plex/session_unknown.xml") + + +@pytest.fixture(name="session_live_tv", scope="session") +def session_live_tv_fixture(): + """Load a Live TV session payload and return it.""" + return load_fixture("plex/session_live_tv.xml") + + +@pytest.fixture(name="livetv_sessions", scope="session") +def livetv_sessions_fixture(): + """Load livetv/sessions payload and return it.""" + return load_fixture("plex/livetv_sessions.xml") + + @pytest.fixture(name="security_token", scope="session") def security_token_fixture(): """Load a security token payload and return it.""" @@ -393,18 +417,23 @@ def mock_plex_calls( def setup_plex_server( hass, entry, + livetv_sessions, mock_websocket, mock_plex_calls, requests_mock, empty_payload, session_default, + session_live_tv, session_photo, session_plexweb, + session_transient, + session_unknown, ): """Set up and return a mocked Plex server instance.""" async def _wrapper(**kwargs): """Wrap the fixture to allow passing arguments to the setup method.""" + url = plex_server_url(entry) config_entry = kwargs.get("config_entry", entry) disable_clients = kwargs.pop("disable_clients", False) disable_gdm = kwargs.pop("disable_gdm", True) @@ -415,10 +444,16 @@ def setup_plex_server( session = session_plexweb elif session_type == "photo": session = session_photo + elif session_type == "live_tv": + session = session_live_tv + requests_mock.get(f"{url}/livetv/sessions/live_tv_1", text=livetv_sessions) + elif session_type == "transient": + session = session_transient + elif session_type == "unknown": + session = session_unknown else: session = session_default - url = plex_server_url(entry) requests_mock.get(f"{url}/status/sessions", text=session) if disable_clients: diff --git a/tests/components/plex/test_init.py b/tests/components/plex/test_init.py index 2e5a30ce11a..dc46c2ca771 100644 --- a/tests/components/plex/test_init.py +++ b/tests/components/plex/test_init.py @@ -8,13 +8,24 @@ import plexapi import requests import homeassistant.components.plex.const as const +from homeassistant.components.plex.models import ( + LIVE_TV_SECTION, + TRANSIENT_SECTION, + UNKNOWN_SECTION, +) from homeassistant.config_entries import ( ENTRY_STATE_LOADED, ENTRY_STATE_NOT_LOADED, ENTRY_STATE_SETUP_ERROR, ENTRY_STATE_SETUP_RETRY, ) -from homeassistant.const import CONF_TOKEN, CONF_URL, CONF_VERIFY_SSL, STATE_IDLE +from homeassistant.const import ( + CONF_TOKEN, + CONF_URL, + CONF_VERIFY_SSL, + STATE_IDLE, + STATE_PLAYING, +) from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util @@ -108,6 +119,66 @@ async def test_setup_with_photo_session(hass, entry, setup_plex_server): assert sensor.state == "0" +async def test_setup_with_live_tv_session(hass, entry, setup_plex_server): + """Test setup component with a Live TV session.""" + await setup_plex_server(session_type="live_tv") + + assert len(hass.config_entries.async_entries(const.DOMAIN)) == 1 + assert entry.state == ENTRY_STATE_LOADED + await hass.async_block_till_done() + + media_player = hass.states.get( + "media_player.plex_plex_for_android_tv_shield_android_tv" + ) + assert media_player.state == STATE_PLAYING + assert media_player.attributes["media_library_title"] == LIVE_TV_SECTION + + await wait_for_debouncer(hass) + + sensor = hass.states.get("sensor.plex_plex_server_1") + assert sensor.state == "1" + + +async def test_setup_with_transient_session(hass, entry, setup_plex_server): + """Test setup component with a transient session.""" + await setup_plex_server(session_type="transient") + + assert len(hass.config_entries.async_entries(const.DOMAIN)) == 1 + assert entry.state == ENTRY_STATE_LOADED + await hass.async_block_till_done() + + media_player = hass.states.get( + "media_player.plex_plex_for_android_tv_shield_android_tv" + ) + assert media_player.state == STATE_PLAYING + assert media_player.attributes["media_library_title"] == TRANSIENT_SECTION + + await wait_for_debouncer(hass) + + sensor = hass.states.get("sensor.plex_plex_server_1") + assert sensor.state == "1" + + +async def test_setup_with_unknown_session(hass, entry, setup_plex_server): + """Test setup component with an unknown session.""" + await setup_plex_server(session_type="unknown") + + assert len(hass.config_entries.async_entries(const.DOMAIN)) == 1 + assert entry.state == ENTRY_STATE_LOADED + await hass.async_block_till_done() + + media_player = hass.states.get( + "media_player.plex_plex_for_android_tv_shield_android_tv" + ) + assert media_player.state == STATE_PLAYING + assert media_player.attributes["media_library_title"] == UNKNOWN_SECTION + + await wait_for_debouncer(hass) + + sensor = hass.states.get("sensor.plex_plex_server_1") + assert sensor.state == "1" + + async def test_setup_when_certificate_changed( hass, requests_mock, diff --git a/tests/fixtures/plex/livetv_sessions.xml b/tests/fixtures/plex/livetv_sessions.xml new file mode 100644 index 00000000000..faec345db2c --- /dev/null +++ b/tests/fixtures/plex/livetv_sessions.xml @@ -0,0 +1,17 @@ + + + diff --git a/tests/fixtures/plex/session_live_tv.xml b/tests/fixtures/plex/session_live_tv.xml new file mode 100644 index 00000000000..d71ef146f38 --- /dev/null +++ b/tests/fixtures/plex/session_live_tv.xml @@ -0,0 +1,14 @@ + + + diff --git a/tests/fixtures/plex/session_transient.xml b/tests/fixtures/plex/session_transient.xml new file mode 100644 index 00000000000..0bb007e195c --- /dev/null +++ b/tests/fixtures/plex/session_transient.xml @@ -0,0 +1,13 @@ + + + diff --git a/tests/fixtures/plex/session_unknown.xml b/tests/fixtures/plex/session_unknown.xml new file mode 100644 index 00000000000..6bbe87d8b27 --- /dev/null +++ b/tests/fixtures/plex/session_unknown.xml @@ -0,0 +1,13 @@ + + +