from __future__ import annotations
-import asyncio
+from collections.abc import AsyncGenerator
from typing import TYPE_CHECKING, Any
from music_assistant_models.enums import MediaType, ProviderFeature
self,
item_id: str,
provider_instance_id_or_domain: str,
- ) -> UniqueList[PodcastEpisode]:
+ ) -> AsyncGenerator[PodcastEpisode, None]:
"""Return podcast episodes for the given provider podcast id."""
# always check if we have a library item for this podcast
if provider_instance_id_or_domain == "library":
break
# podcast episodes are not stored in the db/library
# so we always need to fetch them from the provider
- episodes = await self._get_provider_podcast_episodes(
+ async for episode in self._get_provider_podcast_episodes(
item_id, provider_instance_id_or_domain
- )
- return sorted(episodes, key=lambda x: x.position)
+ ):
+ yield episode
async def episode(
self,
async def _get_provider_podcast_episodes(
self, item_id: str, provider_instance_id_or_domain: str
- ) -> list[PodcastEpisode]:
+ ) -> AsyncGenerator[PodcastEpisode, None]:
"""Return podcast episodes for the given provider podcast id."""
prov: MusicProvider = self.mass.get_provider(provider_instance_id_or_domain)
if prov is None:
- return []
- # grab the episodes from the provider
- # note that we do not cache any of this because its
- # always a rather small list and we want fresh resume info
- items = await prov.get_podcast_episodes(item_id)
+ return
async def set_resume_position(episode: PodcastEpisode) -> None:
if episode.fully_played is not None or episode.resume_position_ms:
if resume_info_db_row["fully_played"] is not None:
episode.fully_played = resume_info_db_row["fully_played"]
- await asyncio.gather(*[set_resume_position(x) for x in items])
- return items
+ # grab the episodes from the provider
+ # note that we do not cache any of this because its
+ # always a rather small list and we want fresh resume info
+ async for item in prov.get_podcast_episodes(item_id):
+ await set_resume_position(item)
+ yield item
async def radio_mode_base_tracks(
self,
"Fetching episode(s) and resume point to play for Podcast %s",
podcast.name,
)
- all_episodes = await self.mass.music.podcasts.episodes(podcast.item_id, podcast.provider)
+ all_episodes = [
+ x async for x in self.mass.music.podcasts.episodes(podcast.item_id, podcast.provider)
+ ]
+ all_episodes.sort(key=lambda x: x.position)
# if a episode was provided, a user explicitly selected a episode to play
# so we need to find the index of the episode in the list
if isinstance(episode, PodcastEpisode):
async def get_podcast_episodes(
self,
prov_podcast_id: str,
- ) -> list[PodcastEpisode]:
+ ) -> AsyncGenerator[PodcastEpisode, None]:
"""Get all PodcastEpisodes for given podcast id."""
+ yield
if ProviderFeature.LIBRARY_PODCASTS in self.supported_features:
raise NotImplementedError
base_url=str(self.config.get_value(CONF_URL)).rstrip("/"),
)
- async def get_podcast_episodes(self, prov_podcast_id: str) -> list[PodcastEpisode]:
+ async def get_podcast_episodes(
+ self, prov_podcast_id: str
+ ) -> AsyncGenerator[PodcastEpisode, None]:
"""Get all podcast episodes of podcast.
Adds progress information.
"""
abs_podcast = await self._get_abs_expanded_podcast(prov_podcast_id=prov_podcast_id)
- episode_list = []
episode_cnt = 1
# the user has the progress of all media items
# so we use a single api call here to obtain possibly many
base_url=str(self.config.get_value(CONF_URL)).rstrip("/"),
media_progress=progress,
)
- episode_list.append(mass_episode)
+ yield mass_episode
episode_cnt += 1
- return episode_list
async def get_podcast_episode(
self, prov_episode_id: str, add_progress: bool = True
async def get_podcast(self, prov_podcast_id: str) -> Podcast:
"""Get full podcast details by id."""
- for episode in await self.get_podcast_episodes(prov_podcast_id):
+ async for episode in self.get_podcast_episodes(prov_podcast_id):
assert isinstance(episode.podcast, Podcast)
return episode.podcast
msg = f"Podcast not found: {prov_podcast_id}"
)
return result
- async def get_podcast_episodes(self, prov_podcast_id: str) -> list[PodcastEpisode]:
+ async def get_podcast_episodes(
+ self, prov_podcast_id: str
+ ) -> AsyncGenerator[PodcastEpisode, None]:
"""Get podcast episodes for given podcast id."""
episodes: list[PodcastEpisode] = []
continue
tm.create_task(_process_podcast_episode(item))
- return episodes
+ for episode in episodes:
+ yield episode
async def _parse_playlist_line(self, line: str, playlist_path: str) -> Track | None:
"""Try to parse a track from a playlist line."""
async def get_podcast_episode(self, prov_episode_id: str) -> PodcastEpisode:
"""Get (full) podcast episode details by id."""
podcast_id, _ = prov_episode_id.split(EP_CHAN_SEP)
- for episode in await self.get_podcast_episodes(podcast_id):
+ async for episode in self.get_podcast_episodes(podcast_id):
if episode.item_id == prov_episode_id:
return episode
msg = f"Episode {prov_episode_id} not found"
async def get_podcast_episodes(
self,
prov_podcast_id: str,
- ) -> list[PodcastEpisode]:
+ ) -> AsyncGenerator[PodcastEpisode, None]:
"""Get all Episodes for given podcast id."""
if not self._enable_podcasts:
- return []
-
+ return
channels = await self._run_async(
self._conn.getPodcasts, incEpisodes=True, pid=prov_podcast_id
)
-
channel = channels[0]
- episodes = []
for episode in channel.episodes:
- episodes.append(self._parse_epsiode(episode, channel))
- return episodes
+ yield self._parse_epsiode(episode, channel)
async def get_podcast(self, prov_podcast_id: str) -> Podcast:
"""Get full Podcast details by id."""
async def get_podcast_episodes(
self,
prov_podcast_id: str,
- ) -> list[PodcastEpisode]:
+ ) -> AsyncGenerator[PodcastEpisode, None]:
"""List all episodes for the podcast."""
- episodes = []
if prov_podcast_id != self.podcast_id:
raise Exception(f"Podcast id not in provider: {prov_podcast_id}")
for idx, episode in enumerate(self.parsed["episodes"]):
- episodes.append(await self._parse_episode(episode, idx))
- return episodes
+ yield await self._parse_episode(episode, idx)
async def get_stream_details(self, item_id: str, media_type: MediaType) -> StreamDetails:
"""Get streamdetails for a track/radio."""
async def get_podcast_episodes(
self,
prov_podcast_id: str,
- ) -> list[PodcastEpisode]:
+ ) -> AsyncGenerator[PodcastEpisode, None]:
"""Get all PodcastEpisodes for given podcast id."""
num_episodes = 25
- return [
- await self.get_podcast_episode(f"{prov_podcast_id}_{episode_idx}")
- for episode_idx in range(num_episodes)
- ]
+ for episode_idx in range(num_episodes):
+ yield await self.get_podcast_episode(f"{prov_podcast_id}_{episode_idx}")
async def get_podcast_episode(self, prov_episode_id: str) -> PodcastEpisode:
"""Get (full) podcast episode details by id."""
podcast_obj = await get_podcast(prov_podcast_id, headers=self._headers)
return self._parse_podcast(podcast_obj)
- async def get_podcast_episodes(self, prov_podcast_id: str) -> list[PodcastEpisode]:
+ async def get_podcast_episodes(
+ self, prov_podcast_id: str
+ ) -> AsyncGenerator[PodcastEpisode, None]:
"""Get all episodes from a podcast."""
podcast_obj = await get_podcast(prov_podcast_id, headers=self._headers)
podcast_obj["podcastId"] = prov_podcast_id
podcast = self._parse_podcast(podcast_obj)
- episodes = []
for index, episode_obj in enumerate(podcast_obj.get("episodes", []), start=1):
episode = self._parse_podcast_episode(episode_obj, podcast)
ep_index = episode_obj.get("index") or index
episode.position = ep_index
- episodes.append(episode)
- return episodes
+ yield episode
async def get_podcast_episode(self, prov_episode_id: str) -> PodcastEpisode:
"""Get a single Podcast Episode."""