Skip to content

Commit 9b8ddab

Browse files
committed
Merge branch 'dev' of https://github.com/music-assistant/server into dev
2 parents f5eef0e + 427f57c commit 9b8ddab

File tree

6 files changed

+590
-256
lines changed

6 files changed

+590
-256
lines changed

music_assistant/providers/audiobookshelf/__init__.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@
3636
from music_assistant.models.music_provider import MusicProvider
3737
from music_assistant.providers.audiobookshelf.abs_client import ABSClient
3838
from music_assistant.providers.audiobookshelf.abs_schema import (
39-
ABSAudioBook,
4039
ABSDeviceInfo,
4140
ABSLibrary,
41+
ABSLibraryItemExpandedBook,
42+
ABSLibraryItemExpandedPodcast,
4243
ABSPlaybackSessionExpanded,
43-
ABSPodcast,
4444
ABSPodcastEpisodeExpanded,
4545
)
4646

@@ -174,7 +174,7 @@ async def sync_library(self, media_types: tuple[MediaType, ...]) -> None:
174174
await self._client.sync()
175175
await super().sync_library(media_types=media_types)
176176

177-
def _parse_podcast(self, abs_podcast: ABSPodcast) -> Podcast:
177+
def _parse_podcast(self, abs_podcast: ABSLibraryItemExpandedPodcast) -> Podcast:
178178
"""Translate ABSPodcast to MassPodcast."""
179179
title = abs_podcast.media.metadata.title
180180
# Per API doc title may be None.
@@ -185,7 +185,7 @@ def _parse_podcast(self, abs_podcast: ABSPodcast) -> Podcast:
185185
name=title,
186186
publisher=abs_podcast.media.metadata.author,
187187
provider=self.lookup_key,
188-
total_episodes=abs_podcast.media.num_episodes,
188+
total_episodes=len(abs_podcast.media.episodes),
189189
provider_mappings={
190190
ProviderMapping(
191191
item_id=abs_podcast.id_,
@@ -309,7 +309,7 @@ async def get_podcast_episode(self, prov_episode_id: str) -> PodcastEpisode:
309309
episode_cnt += 1
310310
raise MediaNotFoundError("Episode not found")
311311

312-
async def _parse_audiobook(self, abs_audiobook: ABSAudioBook) -> Audiobook:
312+
async def _parse_audiobook(self, abs_audiobook: ABSLibraryItemExpandedBook) -> Audiobook:
313313
mass_audiobook = Audiobook(
314314
item_id=abs_audiobook.id_,
315315
provider=self.lookup_key,
@@ -431,7 +431,9 @@ async def get_stream_details(
431431
return await self._get_stream_details_audiobook(abs_audiobook)
432432
raise MediaNotFoundError("Stream unknown")
433433

434-
async def _get_stream_details_audiobook(self, abs_audiobook: ABSAudioBook) -> StreamDetails:
434+
async def _get_stream_details_audiobook(
435+
self, abs_audiobook: ABSLibraryItemExpandedBook
436+
) -> StreamDetails:
435437
"""Only single audio file in audiobook."""
436438
self.logger.debug(
437439
f"Using direct playback for audiobook {abs_audiobook.media.metadata.title}"
@@ -554,7 +556,9 @@ async def _browse_lib(
554556
if library is None:
555557
raise MediaNotFoundError("Lib missing.")
556558

557-
def get_item_mapping(item: ABSAudioBook | ABSPodcast) -> ItemMapping:
559+
def get_item_mapping(
560+
item: ABSLibraryItemExpandedBook | ABSLibraryItemExpandedPodcast,
561+
) -> ItemMapping:
558562
title = item.media.metadata.title
559563
if title is None:
560564
title = "UNKNOWN"

music_assistant/providers/audiobookshelf/abs_client.py

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,20 @@
1212
from music_assistant_models.media_items import UniqueList
1313

1414
from music_assistant.providers.audiobookshelf.abs_schema import (
15-
ABSAudioBook,
1615
ABSDeviceInfo,
17-
ABSLibrariesItemsResponse,
16+
ABSLibrariesItemsMinifiedBookResponse,
17+
ABSLibrariesItemsMinifiedPodcastResponse,
1818
ABSLibrariesResponse,
1919
ABSLibrary,
20-
ABSLibraryItem,
20+
ABSLibraryItemExpandedBook,
21+
ABSLibraryItemExpandedPodcast,
22+
ABSLibraryItemMinifiedBook,
23+
ABSLibraryItemMinifiedPodcast,
2124
ABSLoginResponse,
2225
ABSMediaProgress,
2326
ABSPlaybackSession,
2427
ABSPlaybackSessionExpanded,
2528
ABSPlayRequest,
26-
ABSPodcast,
2729
ABSSessionsResponse,
2830
ABSSessionUpdate,
2931
ABSUser,
@@ -163,43 +165,54 @@ async def sync(self) -> None:
163165
self.podcast_libraries.append(library)
164166
self.user = await self.get_authenticated_user()
165167

166-
async def get_all_podcasts(self) -> AsyncGenerator[ABSPodcast]:
168+
async def get_all_podcasts(self) -> AsyncGenerator[ABSLibraryItemExpandedPodcast]:
167169
"""Get all available podcasts."""
168170
for library in self.podcast_libraries:
169171
async for podcast in self.get_all_podcasts_by_library(library):
170172
yield podcast
171173

172174
async def _get_lib_items(self, lib: ABSLibrary) -> AsyncGenerator[bytes]:
173-
"""Get library items with pagination."""
175+
"""Get library items with pagination.
176+
177+
Note:
178+
- minified=1 -> minified items. However, there appears to be
179+
a bug in abs, so we always get minified items. Still there for
180+
consistency
181+
- collapseseries=0 -> even if books are part of a series, they will be single items
182+
"""
174183
page_cnt = 0
175184
while True:
176185
data = await self._get(
177-
f"/libraries/{lib.id_}/items",
186+
f"/libraries/{lib.id_}/items?minified=1&collapseseries=0",
178187
params={"limit": LIMIT_ITEMS_PER_PAGE, "page": page_cnt},
179188
)
180189
page_cnt += 1
181190
yield data
182191

183-
async def get_all_podcasts_by_library(self, lib: ABSLibrary) -> AsyncGenerator[ABSPodcast]:
192+
async def get_all_podcasts_by_library(
193+
self, lib: ABSLibrary
194+
) -> AsyncGenerator[ABSLibraryItemExpandedPodcast]:
184195
"""Get all podcasts in a library."""
185196
async for podcast_data in self._get_lib_items(lib):
186-
podcast_list = ABSLibrariesItemsResponse.from_json(podcast_data).results
197+
podcast_list = ABSLibrariesItemsMinifiedPodcastResponse.from_json(podcast_data).results
187198
if not podcast_list: # [] if page exceeds
188199
return
189200

190-
async def _get_id(plist: list[ABSLibraryItem] = podcast_list) -> AsyncGenerator[str]:
201+
async def _get_id(
202+
plist: list[ABSLibraryItemMinifiedPodcast] = podcast_list,
203+
) -> AsyncGenerator[str]:
191204
for entry in plist:
192205
yield entry.id_
193206

194207
async for id_ in _get_id():
195208
podcast = await self.get_podcast(id_)
196209
yield podcast
197210

198-
async def get_podcast(self, id_: str) -> ABSPodcast:
211+
async def get_podcast(self, id_: str) -> ABSLibraryItemExpandedPodcast:
199212
"""Get a single Podcast by ID."""
200213
# this endpoint gives more podcast extra data
201214
data = await self._get(f"items/{id_}?expanded=1")
202-
return ABSPodcast.from_json(data)
215+
return ABSLibraryItemExpandedPodcast.from_json(data)
203216

204217
async def _get_progress_ms(
205218
self,
@@ -288,32 +301,36 @@ async def update_audiobook_progress(
288301
endpoint = f"me/progress/{audiobook_id}"
289302
await self._update_progress(endpoint, progress_s, duration_s, is_finished)
290303

291-
async def get_all_audiobooks(self) -> AsyncGenerator[ABSAudioBook]:
304+
async def get_all_audiobooks(self) -> AsyncGenerator[ABSLibraryItemExpandedBook]:
292305
"""Get all audiobooks."""
293306
for library in self.audiobook_libraries:
294307
async for book in self.get_all_audiobooks_by_library(library):
295308
yield book
296309

297-
async def get_all_audiobooks_by_library(self, lib: ABSLibrary) -> AsyncGenerator[ABSAudioBook]:
310+
async def get_all_audiobooks_by_library(
311+
self, lib: ABSLibrary
312+
) -> AsyncGenerator[ABSLibraryItemExpandedBook]:
298313
"""Get all Audiobooks in a library."""
299314
async for audiobook_data in self._get_lib_items(lib):
300-
audiobook_list = ABSLibrariesItemsResponse.from_json(audiobook_data).results
315+
audiobook_list = ABSLibrariesItemsMinifiedBookResponse.from_json(audiobook_data).results
301316
if not audiobook_list: # [] if page exceeds
302317
return
303318

304-
async def _get_id(alist: list[ABSLibraryItem] = audiobook_list) -> AsyncGenerator[str]:
319+
async def _get_id(
320+
alist: list[ABSLibraryItemMinifiedBook] = audiobook_list,
321+
) -> AsyncGenerator[str]:
305322
for entry in alist:
306323
yield entry.id_
307324

308325
async for id_ in _get_id():
309326
audiobook = await self.get_audiobook(id_)
310327
yield audiobook
311328

312-
async def get_audiobook(self, id_: str) -> ABSAudioBook:
329+
async def get_audiobook(self, id_: str) -> ABSLibraryItemExpandedBook:
313330
"""Get a single Audiobook by ID."""
314331
# this endpoint gives more audiobook extra data
315332
audiobook = await self._get(f"items/{id_}?expanded=1")
316-
return ABSAudioBook.from_json(audiobook)
333+
return ABSLibraryItemExpandedBook.from_json(audiobook)
317334

318335
async def get_playback_session_podcast(
319336
self, device_info: ABSDeviceInfo, podcast_id: str, episode_id: str

0 commit comments

Comments
 (0)