From: Eric Munson Date: Thu, 8 May 2025 15:17:27 +0000 (-0400) Subject: Subsonic: Protect all list iteration from possible NoneType (#2180) X-Git-Url: https://git.kitaultman.com/?a=commitdiff_plain;h=9ab07eeae95276f381cbd6cae093b42dcfe3b62d;p=music-assistant-server.git Subsonic: Protect all list iteration from possible NoneType (#2180) Fix: Subsonic: Protect all list iteration from possible NoneType The specification allows for these fields to be None, so we need to check that they exist before iterating them. Signed-off-by: Eric B Munson --- diff --git a/music_assistant/providers/opensubsonic/parsers.py b/music_assistant/providers/opensubsonic/parsers.py index faef6d86..24a7f924 100644 --- a/music_assistant/providers/opensubsonic/parsers.py +++ b/music_assistant/providers/opensubsonic/parsers.py @@ -7,6 +7,7 @@ from datetime import datetime from typing import TYPE_CHECKING from music_assistant_models.enums import ImageType, MediaType +from music_assistant_models.errors import MediaNotFoundError from music_assistant_models.media_items import ( Album, Artist, @@ -234,6 +235,9 @@ def parse_epsiode( """Parse an Open Subsonic Podcast Episode into an MA PodcastEpisode.""" eid = f"{sonic_episode.channel_id}{EP_CHAN_SEP}{sonic_episode.id}" pos = 1 + if not sonic_channel.episode: + raise MediaNotFoundError(f"Podcast Channel '{sonic_channel.id}' missing episode list") + for ep in sonic_channel.episode: if ep.id == sonic_episode.id: break diff --git a/music_assistant/providers/opensubsonic/sonic_provider.py b/music_assistant/providers/opensubsonic/sonic_provider.py index 535f11da..1abe5bff 100644 --- a/music_assistant/providers/opensubsonic/sonic_provider.py +++ b/music_assistant/providers/opensubsonic/sonic_provider.py @@ -272,6 +272,9 @@ class OpenSonicProvider(MusicProvider): chan_id, ep_id = eid.split(EP_CHAN_SEP) chan = await self._run_async(self._conn.get_podcasts, inc_episodes=True, pid=chan_id) + if not chan[0].episode: + raise MediaNotFoundError(f"Missing episode list for podcast channel '{chan[0].id}'") + for episode in chan[0].episode: if episode.id == ep_id: return episode @@ -330,7 +333,14 @@ class OpenSonicProvider(MusicProvider): async def get_library_artists(self) -> AsyncGenerator[Artist, None]: """Provide a generator for reading all artists.""" artists = await self._run_async(self._conn.get_artists) + + if not artists.index: + return + for index in artists.index: + if not index.artist: + continue + for artist in index.artist: yield parse_artist(self.instance_id, artist) @@ -431,8 +441,9 @@ class OpenSonicProvider(MusicProvider): msg = f"Album {prov_album_id} not found" raise MediaNotFoundError(msg) from e tracks = [] - for sonic_song in sonic_album.song: - tracks.append(self._parse_track(sonic_song)) + if sonic_album.song: + for sonic_song in sonic_album.song: + tracks.append(self._parse_track(sonic_song)) return tracks async def get_artist(self, prov_artist_id: str) -> Artist: @@ -501,8 +512,9 @@ class OpenSonicProvider(MusicProvider): msg = f"Album {prov_artist_id} not found" raise MediaNotFoundError(msg) from e albums = [] - for entry in sonic_artist.album: - albums.append(parse_album(self.logger, self.instance_id, entry)) + if sonic_artist.album: + for entry in sonic_artist.album: + albums.append(parse_album(self.logger, self.instance_id, entry)) return albums async def get_playlist(self, prov_playlist_id: str) -> Playlist: