Handle not found playlists in Spotify (#132033)

* Handle not found playlists

* Handle not found playlists

* Handle not found playlists

* Handle not found playlists

* Handle not found playlists

* Update homeassistant/components/spotify/coordinator.py

---------

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
pull/132040/head
Joost Lekkerkerker 2024-12-02 03:17:07 +01:00 committed by GitHub
parent b6458ff9b8
commit 782fff198c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 114 additions and 4 deletions

View File

@ -11,6 +11,7 @@ from spotifyaio import (
Playlist,
SpotifyClient,
SpotifyConnectionError,
SpotifyNotFoundError,
UserProfile,
)
@ -62,6 +63,7 @@ class SpotifyCoordinator(DataUpdateCoordinator[SpotifyCoordinatorData]):
)
self.client = client
self._playlist: Playlist | None = None
self._checked_playlist_id: str | None = None
async def _async_setup(self) -> None:
"""Set up the coordinator."""
@ -87,15 +89,29 @@ class SpotifyCoordinator(DataUpdateCoordinator[SpotifyCoordinatorData]):
dj_playlist = False
if (context := current.context) is not None:
if self._playlist is None or self._playlist.uri != context.uri:
dj_playlist = context.uri == SPOTIFY_DJ_PLAYLIST_URI
if not (
context.uri
in (
self._checked_playlist_id,
SPOTIFY_DJ_PLAYLIST_URI,
)
or (self._playlist is None and context.uri == self._checked_playlist_id)
):
self._checked_playlist_id = context.uri
self._playlist = None
if context.uri == SPOTIFY_DJ_PLAYLIST_URI:
dj_playlist = True
elif context.context_type == ContextType.PLAYLIST:
if context.context_type == ContextType.PLAYLIST:
# Make sure any playlist lookups don't break the current
# playback state update
try:
self._playlist = await self.client.get_playlist(context.uri)
except SpotifyNotFoundError:
_LOGGER.debug(
"Spotify playlist '%s' not found. "
"Most likely a Spotify-created playlist",
context.uri,
)
self._playlist = None
except SpotifyConnectionError:
_LOGGER.debug(
"Unable to load spotify playlist '%s'. "
@ -103,6 +119,7 @@ class SpotifyCoordinator(DataUpdateCoordinator[SpotifyCoordinatorData]):
context.uri,
)
self._playlist = None
self._checked_playlist_id = None
return SpotifyCoordinatorData(
current_playback=current,
position_updated_at=position_updated_at,

View File

@ -10,6 +10,7 @@ from spotifyaio import (
ProductType,
RepeatMode as SpotifyRepeatMode,
SpotifyConnectionError,
SpotifyNotFoundError,
)
from syrupy import SnapshotAssertion
@ -142,6 +143,7 @@ async def test_spotify_dj_list(
hass: HomeAssistant,
mock_spotify: MagicMock,
mock_config_entry: MockConfigEntry,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test the Spotify entities with a Spotify DJ playlist."""
mock_spotify.return_value.get_playback.return_value.context.uri = (
@ -152,12 +154,67 @@ async def test_spotify_dj_list(
assert state
assert state.attributes["media_playlist"] == "DJ"
mock_spotify.return_value.get_playlist.assert_not_called()
freezer.tick(timedelta(seconds=30))
async_fire_time_changed(hass)
await hass.async_block_till_done()
state = hass.states.get("media_player.spotify_spotify_1")
assert state
assert state.attributes["media_playlist"] == "DJ"
mock_spotify.return_value.get_playlist.assert_not_called()
@pytest.mark.usefixtures("setup_credentials")
async def test_normal_playlist(
hass: HomeAssistant,
mock_spotify: MagicMock,
freezer: FrozenDateTimeFactory,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test normal playlist switching."""
await setup_integration(hass, mock_config_entry)
state = hass.states.get("media_player.spotify_spotify_1")
assert state
assert state.attributes["media_playlist"] == "Spotify Web API Testing playlist"
mock_spotify.return_value.get_playlist.assert_called_once_with(
"spotify:user:rushofficial:playlist:2r35vbe6hHl6yDSMfjKgmm"
)
freezer.tick(timedelta(seconds=30))
async_fire_time_changed(hass)
await hass.async_block_till_done()
state = hass.states.get("media_player.spotify_spotify_1")
assert state
assert state.attributes["media_playlist"] == "Spotify Web API Testing playlist"
mock_spotify.return_value.get_playlist.assert_called_once_with(
"spotify:user:rushofficial:playlist:2r35vbe6hHl6yDSMfjKgmm"
)
mock_spotify.return_value.get_playback.return_value.context.uri = (
"spotify:playlist:123123123123123"
)
freezer.tick(timedelta(seconds=30))
async_fire_time_changed(hass)
await hass.async_block_till_done()
mock_spotify.return_value.get_playlist.assert_called_with(
"spotify:playlist:123123123123123"
)
@pytest.mark.usefixtures("setup_credentials")
async def test_fetching_playlist_does_not_fail(
hass: HomeAssistant,
mock_spotify: MagicMock,
mock_config_entry: MockConfigEntry,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test failing fetching playlist does not fail update."""
mock_spotify.return_value.get_playlist.side_effect = SpotifyConnectionError
@ -166,6 +223,42 @@ async def test_fetching_playlist_does_not_fail(
assert state
assert "media_playlist" not in state.attributes
mock_spotify.return_value.get_playlist.assert_called_once()
freezer.tick(timedelta(seconds=30))
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert mock_spotify.return_value.get_playlist.call_count == 2
@pytest.mark.usefixtures("setup_credentials")
async def test_fetching_playlist_once(
hass: HomeAssistant,
mock_spotify: MagicMock,
mock_config_entry: MockConfigEntry,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test that not being able to find a playlist doesn't retry."""
mock_spotify.return_value.get_playlist.side_effect = SpotifyNotFoundError
await setup_integration(hass, mock_config_entry)
state = hass.states.get("media_player.spotify_spotify_1")
assert state
assert "media_playlist" not in state.attributes
mock_spotify.return_value.get_playlist.assert_called_once()
freezer.tick(timedelta(seconds=30))
async_fire_time_changed(hass)
await hass.async_block_till_done()
state = hass.states.get("media_player.spotify_spotify_1")
assert state
assert "media_playlist" not in state.attributes
mock_spotify.return_value.get_playlist.assert_called_once()
@pytest.mark.usefixtures("setup_credentials")
async def test_idle(