diff --git a/homeassistant/components/youtube/config_flow.py b/homeassistant/components/youtube/config_flow.py index 92695f80a2e..fa3bc6c8237 100644 --- a/homeassistant/components/youtube/config_flow.py +++ b/homeassistant/components/youtube/config_flow.py @@ -1,7 +1,7 @@ """Config flow for YouTube integration.""" from __future__ import annotations -from collections.abc import Mapping +from collections.abc import AsyncGenerator, Mapping import logging from typing import Any @@ -31,6 +31,24 @@ from .const import ( ) +async def _get_subscriptions(hass: HomeAssistant, resource: Resource) -> AsyncGenerator: + amount_of_subscriptions = 50 + received_amount_of_subscriptions = 0 + next_page_token = None + while received_amount_of_subscriptions < amount_of_subscriptions: + # pylint: disable=no-member + subscription_request: HttpRequest = resource.subscriptions().list( + part="snippet", mine=True, maxResults=50, pageToken=next_page_token + ) + res = await hass.async_add_executor_job(subscription_request.execute) + amount_of_subscriptions = res["pageInfo"]["totalResults"] + if "nextPageToken" in res: + next_page_token = res["nextPageToken"] + for item in res["items"]: + received_amount_of_subscriptions += 1 + yield item + + async def get_resource(hass: HomeAssistant, token: str) -> Resource: """Get Youtube resource async.""" @@ -152,17 +170,12 @@ class OAuth2FlowHandler( service = await get_resource( self.hass, self._data[CONF_TOKEN][CONF_ACCESS_TOKEN] ) - # pylint: disable=no-member - subscription_request: HttpRequest = service.subscriptions().list( - part="snippet", mine=True, maxResults=50 - ) - response = await self.hass.async_add_executor_job(subscription_request.execute) selectable_channels = [ SelectOptionDict( value=subscription["snippet"]["resourceId"]["channelId"], label=subscription["snippet"]["title"], ) - for subscription in response["items"] + async for subscription in _get_subscriptions(self.hass, service) ] return self.async_show_form( step_id="channels", @@ -191,17 +204,12 @@ class YouTubeOptionsFlowHandler(OptionsFlowWithConfigEntry): service = await get_resource( self.hass, self.config_entry.data[CONF_TOKEN][CONF_ACCESS_TOKEN] ) - # pylint: disable=no-member - subscription_request: HttpRequest = service.subscriptions().list( - part="snippet", mine=True, maxResults=50 - ) - response = await self.hass.async_add_executor_job(subscription_request.execute) selectable_channels = [ SelectOptionDict( value=subscription["snippet"]["resourceId"]["channelId"], label=subscription["snippet"]["title"], ) - for subscription in response["items"] + async for subscription in _get_subscriptions(self.hass, service) ] return self.async_show_form( step_id="init", diff --git a/homeassistant/components/youtube/coordinator.py b/homeassistant/components/youtube/coordinator.py index d3430d8329c..693d2550f53 100644 --- a/homeassistant/components/youtube/coordinator.py +++ b/homeassistant/components/youtube/coordinator.py @@ -53,16 +53,31 @@ class YouTubeDataUpdateCoordinator(DataUpdateCoordinator): async def _async_update_data(self) -> dict[str, Any]: service = await self._auth.get_resource() - channels = self.config_entry.options[CONF_CHANNELS] - channel_request: HttpRequest = service.channels().list( - part="snippet,statistics", id=",".join(channels), maxResults=50 - ) - response: dict = await self.hass.async_add_executor_job(channel_request.execute) + channels = await self._get_channels(service) return await self.hass.async_add_executor_job( - self._get_channel_data, service, response["items"] + self._get_channel_data, service, channels ) + async def _get_channels(self, service: Resource) -> list[dict[str, Any]]: + data = [] + received_channels = 0 + channels = self.config_entry.options[CONF_CHANNELS] + while received_channels < len(channels): + # We're slicing the channels in chunks of 50 to avoid making the URI too long + end = min(received_channels + 50, len(channels) - 1) + channel_request: HttpRequest = service.channels().list( + part="snippet,statistics", + id=",".join(channels[received_channels:end]), + maxResults=50, + ) + response: dict = await self.hass.async_add_executor_job( + channel_request.execute + ) + data.extend(response["items"]) + received_channels += len(response["items"]) + return data + def _get_channel_data( self, service: Resource, channels: list[dict[str, Any]] ) -> dict[str, Any]: diff --git a/tests/components/youtube/__init__.py b/tests/components/youtube/__init__.py index 391ff4b3a22..15a43d7a62f 100644 --- a/tests/components/youtube/__init__.py +++ b/tests/components/youtube/__init__.py @@ -59,7 +59,13 @@ class MockSubscriptions: """Initialize mock subscriptions.""" self._fixture = fixture - def list(self, part: str, mine: bool, maxResults: int | None = None) -> MockRequest: + def list( + self, + part: str, + mine: bool, + maxResults: int | None = None, + pageToken: str | None = None, + ) -> MockRequest: """Return a fixture.""" return MockRequest(fixture=self._fixture)