From bf63b0993d2f7fc8abefb5f4816f4cdaa75bc1e9 Mon Sep 17 00:00:00 2001 From: Jason Parker Date: Thu, 24 Oct 2024 13:51:19 -0400 Subject: [PATCH] Reduce the number of API calls in Twitch integration (#128996) --- .../components/twitch/coordinator.py | 31 ++++++++++++------- tests/components/twitch/conftest.py | 4 +-- .../fixtures/get_followed_channels.json | 2 ++ ...streams.json => get_followed_streams.json} | 1 + tests/components/twitch/test_sensor.py | 4 +-- 5 files changed, 27 insertions(+), 15 deletions(-) rename tests/components/twitch/fixtures/{get_streams.json => get_followed_streams.json} (89%) diff --git a/homeassistant/components/twitch/coordinator.py b/homeassistant/components/twitch/coordinator.py index 00e36781ee7..c34eeaa5325 100644 --- a/homeassistant/components/twitch/coordinator.py +++ b/homeassistant/components/twitch/coordinator.py @@ -4,7 +4,7 @@ from dataclasses import dataclass from datetime import datetime, timedelta from twitchAPI.helper import first -from twitchAPI.object.api import FollowedChannelsResult, TwitchUser, UserSubscription +from twitchAPI.object.api import FollowedChannel, Stream, TwitchUser, UserSubscription from twitchAPI.twitch import Twitch from twitchAPI.type import TwitchAPIException, TwitchResourceNotFound @@ -81,12 +81,24 @@ class TwitchCoordinator(DataUpdateCoordinator[dict[str, TwitchUpdate]]): self.session.token["refresh_token"], False, ) - data = {} + data: dict[str, TwitchUpdate] = {} + streams: dict[str, Stream] = { + s.user_id: s + async for s in self.twitch.get_followed_streams( + user_id=self.current_user.id, first=100 + ) + } + follows: dict[str, FollowedChannel] = { + f.broadcaster_id: f + async for f in await self.twitch.get_followed_channels( + user_id=self.current_user.id, first=100 + ) + } for channel in self.users: followers = await self.twitch.get_channel_followers(channel.id) - stream = await first(self.twitch.get_streams(user_id=[channel.id], first=1)) + stream = streams.get(channel.id) + follow = follows.get(channel.id) sub: UserSubscription | None = None - follows: FollowedChannelsResult | None = None try: sub = await self.twitch.check_user_subscription( user_id=self.current_user.id, broadcaster_id=channel.id @@ -95,10 +107,7 @@ class TwitchCoordinator(DataUpdateCoordinator[dict[str, TwitchUpdate]]): LOGGER.debug("User is not subscribed to %s", channel.display_name) except TwitchAPIException as exc: LOGGER.error("Error response on check_user_subscription: %s", exc) - else: - follows = await self.twitch.get_followed_channels( - self.current_user.id, broadcaster_id=channel.id - ) + data[channel.id] = TwitchUpdate( channel.display_name, followers.total, @@ -108,11 +117,11 @@ class TwitchCoordinator(DataUpdateCoordinator[dict[str, TwitchUpdate]]): stream.started_at if stream else None, stream.thumbnail_url if stream else None, channel.profile_image_url, - sub is not None if sub else None, + bool(sub), sub.is_gift if sub else None, {"1000": 1, "2000": 2, "3000": 3}.get(sub.tier) if sub else None, - follows is not None and follows.total > 0, - follows.data[0].followed_at if follows and follows.total else None, + bool(follow), + follow.followed_at if follow else None, stream.viewer_count if stream else None, ) return data diff --git a/tests/components/twitch/conftest.py b/tests/components/twitch/conftest.py index 25e443c2778..07732de1b0c 100644 --- a/tests/components/twitch/conftest.py +++ b/tests/components/twitch/conftest.py @@ -111,8 +111,8 @@ def twitch_mock() -> Generator[AsyncMock]: mock_client.return_value.get_followed_channels.return_value = TwitchIterObject( "get_followed_channels.json", FollowedChannel ) - mock_client.return_value.get_streams.return_value = get_generator( - "get_streams.json", Stream + mock_client.return_value.get_followed_streams.return_value = get_generator( + "get_followed_streams.json", Stream ) mock_client.return_value.check_user_subscription.return_value = ( UserSubscription( diff --git a/tests/components/twitch/fixtures/get_followed_channels.json b/tests/components/twitch/fixtures/get_followed_channels.json index 4add7cc0a98..990fac390e9 100644 --- a/tests/components/twitch/fixtures/get_followed_channels.json +++ b/tests/components/twitch/fixtures/get_followed_channels.json @@ -1,9 +1,11 @@ [ { + "broadcaster_id": 123, "broadcaster_login": "internetofthings", "followed_at": "2023-08-01" }, { + "broadcaster_id": 456, "broadcaster_login": "homeassistant", "followed_at": "2023-08-01" } diff --git a/tests/components/twitch/fixtures/get_streams.json b/tests/components/twitch/fixtures/get_followed_streams.json similarity index 89% rename from tests/components/twitch/fixtures/get_streams.json rename to tests/components/twitch/fixtures/get_followed_streams.json index 73f6dc1b42a..e02c594c4cc 100644 --- a/tests/components/twitch/fixtures/get_streams.json +++ b/tests/components/twitch/fixtures/get_followed_streams.json @@ -1,5 +1,6 @@ [ { + "user_id": 123, "game_name": "Good game", "title": "Title", "thumbnail_url": "stream-medium.png", diff --git a/tests/components/twitch/test_sensor.py b/tests/components/twitch/test_sensor.py index 0f7ea0c33eb..613c0919c49 100644 --- a/tests/components/twitch/test_sensor.py +++ b/tests/components/twitch/test_sensor.py @@ -21,8 +21,8 @@ async def test_offline( hass: HomeAssistant, twitch_mock: AsyncMock, config_entry: MockConfigEntry ) -> None: """Test offline state.""" - twitch_mock.return_value.get_streams.return_value = get_generator_from_data( - [], Stream + twitch_mock.return_value.get_followed_streams.return_value = ( + get_generator_from_data([], Stream) ) await setup_integration(hass, config_entry)