From 6b7ff8926645ab07cb4623612dd5251a27202d46 Mon Sep 17 00:00:00 2001 From: Fabian Munkes <105975993+fmunkes@users.noreply.github.com> Date: Sat, 20 Dec 2025 11:12:18 +0100 Subject: [PATCH] fix: podcast parser helpers not handling exception (#2861) --- music_assistant/helpers/podcast_parsers.py | 21 ++++++++++++++++--- music_assistant/providers/gpodder/__init__.py | 4 ++-- .../providers/itunes_podcasts/__init__.py | 15 ++++++------- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/music_assistant/helpers/podcast_parsers.py b/music_assistant/helpers/podcast_parsers.py index 886eb601..31b2a2f6 100644 --- a/music_assistant/helpers/podcast_parsers.py +++ b/music_assistant/helpers/podcast_parsers.py @@ -6,7 +6,9 @@ from typing import Any import aiohttp import podcastparser +from aiohttp.client import ClientError from music_assistant_models.enums import ContentType, ImageType, MediaType +from music_assistant_models.errors import MediaNotFoundError from music_assistant_models.media_items import ( AudioFormat, ItemMapping, @@ -32,11 +34,24 @@ async def get_podcastparser_dict( # but, reports on discord show, that also the opposite may be true for headers in [{"User-Agent": "Mozilla/5.0"}, {}]: # raises ClientError on status failure - response = await session.get(feed_url, headers=headers, raise_for_status=True) - assert response is not None # for type checking + # ClientError is the base class of all possible Error, i.e. not authorized, + # url doesn't exist etc. + try: + response = await session.get(feed_url, headers=headers, raise_for_status=True) + except ClientError: + continue + break + if response is None: + # we did not get a single acceptable response + raise MediaNotFoundError( + f"Did not get acceptable response while trying to access {feed_url}." + ) feed_data = await response.read() feed_stream = BytesIO(feed_data) - return podcastparser.parse(feed_url, feed_stream, max_episodes=max_episodes) # type: ignore[no-any-return] + try: + return podcastparser.parse(feed_url, feed_stream, max_episodes=max_episodes) # type: ignore[no-any-return] + except podcastparser.FeedParseError: + raise MediaNotFoundError(f"The url at {feed_url} returns invalid RSS data.") def parse_podcast( diff --git a/music_assistant/providers/gpodder/__init__.py b/music_assistant/providers/gpodder/__init__.py index 3aafc94c..3918fa00 100644 --- a/music_assistant/providers/gpodder/__init__.py +++ b/music_assistant/providers/gpodder/__init__.py @@ -18,7 +18,6 @@ import time from collections.abc import AsyncGenerator from typing import TYPE_CHECKING, Any -from aiohttp.client_exceptions import ClientError from music_assistant_models.config_entries import ConfigEntry, ConfigValueType, ProviderConfig from music_assistant_models.enums import ( ConfigEntryType, @@ -354,7 +353,7 @@ class GPodder(MusicProvider): feed_url=feed_url, max_episodes=self.max_episodes, ) - except ClientError: + except MediaNotFoundError: self.logger.warning(f"Was unable to obtain podcast with feed {feed_url}") continue await self._cache_set_podcast(feed_url, parsed_podcast) @@ -612,6 +611,7 @@ class GPodder(MusicProvider): default=None, ) if parsed_podcast is None: + # raises MediaNotFoundError parsed_podcast = await get_podcastparser_dict( session=self.mass.http_session, feed_url=prov_podcast_id, diff --git a/music_assistant/providers/itunes_podcasts/__init__.py b/music_assistant/providers/itunes_podcasts/__init__.py index ddb97665..1352b0d4 100644 --- a/music_assistant/providers/itunes_podcasts/__init__.py +++ b/music_assistant/providers/itunes_podcasts/__init__.py @@ -8,7 +8,6 @@ from typing import TYPE_CHECKING, Any import aiofiles import orjson -from aiohttp.client_exceptions import ClientError from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption from music_assistant_models.enums import ( ConfigEntryType, @@ -334,14 +333,12 @@ class ITunesPodcastsProvider(MusicProvider): default=None, ) if parsed_podcast is None: - try: - parsed_podcast = await get_podcastparser_dict( - session=self.mass.http_session, - feed_url=prov_podcast_id, - max_episodes=self.max_episodes, - ) - except ClientError as exc: - raise MediaNotFoundError from exc + # get_podcastparser_dict raises MediaNotFoundError if data is invalid + parsed_podcast = await get_podcastparser_dict( + session=self.mass.http_session, + feed_url=prov_podcast_id, + max_episodes=self.max_episodes, + ) await self._cache_set_podcast(feed_url=prov_podcast_id, parsed_podcast=parsed_podcast) # this is a dictionary from podcastparser -- 2.34.1