-"""Schema definition of Audiobookshelf.
+"""Schema definition of Audiobookshelf (ABS).
https://api.audiobookshelf.org/
+
+Some schema definitions have variants. Take book as example:
+https://api.audiobookshelf.org/#book
+Naming Scheme in this file:
+ - the standard definition has nothing added
+ - minified/ expanded: here, 2 additional variants
+
+Sometimes these variants remove or change attributes in such a way, that
+it makes sense to define a base class for inheritance.
"""
from dataclasses import dataclass, field
@dataclass
class ABSAudioTrack(BaseModel):
- """ABS audioTrack.
+ """ABS audioTrack. No variants.
https://api.audiobookshelf.org/#audio-track
"""
- index: int
+ index: int | None
start_offset: Annotated[float, Alias("startOffset")] = 0.0
duration: float = 0.0
title: str = ""
@dataclass
-class ABSPodcastEpisodeExpanded(BaseModel):
- """ABSPodcastEpisode.
+class ABSBookChapter(BaseModel):
+ """
+ ABSBookChapter. No variants.
- https://api.audiobookshelf.org/#podcast-episode
+ https://api.audiobookshelf.org/#book-chapter
"""
- library_item_id: Annotated[str, Alias("libraryItemId")]
- id_: Annotated[str, Alias("id")]
- index: int | None
- # audio_file: # not needed for mass application
- published_at: Annotated[int | None, Alias("publishedAt")] # ms posix epoch
- added_at: Annotated[int | None, Alias("addedAt")] # ms posix epoch
- updated_at: Annotated[int | None, Alias("updatedAt")] # ms posix epoch
- audio_track: Annotated[ABSAudioTrack, Alias("audioTrack")]
- size: int # in bytes
- season: str = ""
- episode: str = ""
- episode_type: Annotated[str, Alias("episodeType")] = ""
- title: str = ""
- subtitle: str = ""
- description: str = ""
- enclosure: str = ""
- pub_date: Annotated[str, Alias("pubDate")] = ""
- guid: str = ""
- # chapters
- duration: float = 0.0
+ id_: Annotated[int, Alias("id")]
+ start: float
+ end: float
+ title: str
@dataclass
-class ABSPodcastMetaData(BaseModel):
- """PodcastMetaData https://api.audiobookshelf.org/?shell#podcasts."""
+class ABSAudioBookmark(BaseModel):
+ """ABSAudioBookmark. No variants.
- title: str | None
- author: str | None
- description: str | None
- release_date: Annotated[str | None, Alias("releaseDate")]
- genres: list[str] | None
- feed_url: Annotated[str | None, Alias("feedUrl")]
- image_url: Annotated[str | None, Alias("imageUrl")]
- itunes_page_url: Annotated[str | None, Alias("itunesPageUrl")]
- itunes_id: Annotated[int | None, Alias("itunesId")]
- itunes_artist_id: Annotated[int | None, Alias("itunesArtistId")]
- explicit: bool
- language: str | None
- type_: Annotated[str | None, Alias("type")]
+ https://api.audiobookshelf.org/#audio-bookmark
+ """
+
+ library_item_id: Annotated[str, Alias("libraryItemId")]
+ title: str
+ time: float # seconds
+ created_at: Annotated[int, Alias("createdAt")] # unix epoch ms
@dataclass
-class ABSPodcastMedia(BaseModel):
- """ABSPodcastMedia."""
+class ABSUserPermissions(BaseModel):
+ """ABSUserPermissions. No variants.
- metadata: ABSPodcastMetaData
- cover_path: Annotated[str, Alias("coverPath")]
- episodes: list[ABSPodcastEpisodeExpanded]
- num_episodes: Annotated[int, Alias("numEpisodes")] = 0
+ https://api.audiobookshelf.org/#user-permissions
+ """
+
+ download: bool
+ update: bool
+ delete: bool
+ upload: bool
+ access_all_libraries: Annotated[bool, Alias("accessAllLibraries")]
+ access_all_tags: Annotated[bool, Alias("accessAllTags")]
+ access_explicit_content: Annotated[bool, Alias("accessExplicitContent")]
@dataclass
-class ABSPodcast(BaseModel):
- """ABSPodcast.
+class ABSLibrary(BaseModel):
+ """ABSLibrary. No variants.
- Depending on endpoint we get different results. This class does not
- fully reflect https://api.audiobookshelf.org/#podcast.
+ https://api.audiobookshelf.org/#library
+ Only attributes we need
"""
id_: Annotated[str, Alias("id")]
- media: ABSPodcastMedia
+ name: str
+ # folders
+ # displayOrder: Integer
+ # icon: String
+ media_type: Annotated[str, Alias("mediaType")]
+ provider: str
+ # settings
+ created_at: Annotated[int, Alias("createdAt")]
+ last_update: Annotated[int, Alias("lastUpdate")]
@dataclass
-class ABSAuthorMinified(BaseModel):
- """ABSAuthor.
+class ABSDeviceInfo(BaseModel):
+ """ABSDeviceInfo. No variants.
- https://api.audiobookshelf.org/#author
+ https://api.audiobookshelf.org/#device-info-parameters
+ https://api.audiobookshelf.org/#device-info
+ https://github.com/advplyr/audiobookshelf/blob/master/server/objects/DeviceInfo.js#L3
"""
- id_: Annotated[str, Alias("id")]
- name: str
+ device_id: Annotated[str, Alias("deviceId")] = ""
+ client_name: Annotated[str, Alias("clientName")] = ""
+ client_version: Annotated[str, Alias("clientVersion")] = ""
+ manufacturer: str = ""
+ model: str = ""
+ # sdkVersion # meant for an Android client
+
+
+### Author: https://api.audiobookshelf.org/#author
@dataclass
-class ABSSeriesSequence(BaseModel):
- """Series Sequence.
+class ABSAuthorMinified(BaseModel):
+ """ABSAuthorMinified.
- https://api.audiobookshelf.org/#series
+ https://api.audiobookshelf.org/#author
"""
id_: Annotated[str, Alias("id")]
name: str
- sequence: str | None
@dataclass
-class ABSAudioBookMetaData(BaseModel):
- """ABSAudioBookMetaData.
-
- https://api.audiobookshelf.org/#book-metadata
- """
+class ABSAuthor(ABSAuthorMinified):
+ """ABSAuthor."""
- title: str
- subtitle: str
- authors: list[ABSAuthorMinified]
- narrators: list[str]
- series: list[ABSSeriesSequence]
- genres: list[str] | None
- published_year: Annotated[str | None, Alias("publishedYear")]
- published_date: Annotated[str | None, Alias("publishedDate")]
- publisher: str | None
- description: str | None
- isbn: str | None
asin: str | None
- language: str | None
- explicit: bool
+ description: str | None
+ image_path: Annotated[str | None, Alias("imagePath")]
+ added_at: Annotated[int, Alias("addedAt")] # ms epoch
+ updated_at: Annotated[int, Alias("updatedAt")] # ms epoch
@dataclass
-class ABSAudioBookChapter(BaseModel):
- """
- ABSAudioBookChapter.
+class ABSAuthorExpanded(ABSAuthor):
+ """ABSAuthorExpanded."""
- https://api.audiobookshelf.org/#book-chapter
- """
+ num_books: Annotated[int, Alias("numBooks")]
- id_: Annotated[int, Alias("id")]
- start: float
- end: float
- title: str
+
+### Series: https://api.audiobookshelf.org/#series
@dataclass
-class ABSAudioBookMedia(BaseModel):
- """ABSAudioBookMedia.
+class _ABSSeriesBase(BaseModel):
+ """_ABSSeriesBase."""
- Helper class due to API endpoint used.
- """
+ id_: Annotated[str, Alias("id")]
+ name: str
- metadata: ABSAudioBookMetaData
- cover_path: Annotated[str, Alias("coverPath")]
- chapters: list[ABSAudioBookChapter]
- duration: float
- tracks: list[ABSAudioTrack]
+
+@dataclass
+class ABSSeries(_ABSSeriesBase):
+ """ABSSeries."""
+
+ description: str | None
+ added_at: Annotated[int, Alias("addedAt")] # ms epoch
+ updated_at: Annotated[int, Alias("updatedAt")] # ms epoch
+
+
+@dataclass
+class ABSSeriesNumBooks(_ABSSeriesBase):
+ """ABSSeriesNumBooks."""
+
+ name_ignore_prefix: Annotated[str, Alias("nameIgnorePrefix")]
+ library_item_ids: Annotated[list[str], Alias("libraryItemIds")]
+ num_books: Annotated[int, Alias("numBooks")]
@dataclass
-class ABSAudioBook(BaseModel):
- """ABSAudioBook.
+class ABSSeriesSequence(BaseModel):
+ """Series Sequence.
- Depending on endpoint we get different results. This class does not
- full reflect https://api.audiobookshelf.org/#book.
+ https://api.audiobookshelf.org/#series
"""
id_: Annotated[str, Alias("id")]
- media: ABSAudioBookMedia
+ name: str
+ sequence: str | None
+
+
+# another variant, ABSSeriesBooks is further down
+
+
+### https://api.audiobookshelf.org/#media-progress
@dataclass
class ABSMediaProgress(BaseModel):
- """ABSMediaProgress.
-
- https://api.audiobookshelf.org/#media-progress
- """
+ """ABSMediaProgress."""
id_: Annotated[str, Alias("id")]
library_item_id: Annotated[str, Alias("libraryItemId")]
finished_at: Annotated[int | None, Alias("finishedAt")] # ms epoch
-@dataclass
-class ABSAudioBookmark(BaseModel):
- """ABSAudioBookmark."""
-
- library_item_id: Annotated[str, Alias("libraryItemId")]
- title: str
- time: float # seconds
- created_at: Annotated[int, Alias("createdAt")] # unix epoch ms
-
-
-@dataclass
-class ABSUserPermissions(BaseModel):
- """ABSUserPermissions."""
-
- download: bool
- update: bool
- delete: bool
- upload: bool
- access_all_libraries: Annotated[bool, Alias("accessAllLibraries")]
- access_all_tags: Annotated[bool, Alias("accessAllTags")]
- access_explicit_content: Annotated[bool, Alias("accessExplicitContent")]
+# two additional progress variants, 'with media' book and podcast, further down.
@dataclass
# item_tags_accessible: Annotated[list[str], Alias("itemTagsAccessible")]
-@dataclass
-class ABSLoginResponse(BaseModel):
- """ABSLoginResponse."""
-
- user: ABSUser
-
- # this seems to be missing
- # user_default_library_id: Annotated[str, Alias("defaultLibraryId")]
-
-
-@dataclass
-class ABSLibrary(BaseModel):
- """ABSLibrary.
-
- Only attributes we need
- """
-
- id_: Annotated[str, Alias("id")]
- name: str
- # folders
- # displayOrder: Integer
- # icon: String
- media_type: Annotated[str, Alias("mediaType")]
- provider: str
- # settings
- created_at: Annotated[int, Alias("createdAt")]
- last_update: Annotated[int, Alias("lastUpdate")]
-
-
-@dataclass
-class ABSLibrariesResponse(BaseModel):
- """ABSLibrariesResponse."""
-
- libraries: list[ABSLibrary]
-
-
-@dataclass
-class ABSLibraryItem(BaseModel):
- """ABSLibraryItem."""
-
- id_: Annotated[str, Alias("id")]
-
-
-@dataclass
-class ABSLibrariesItemsResponse(BaseModel):
- """ABSLibrariesItemsResponse.
-
- https://api.audiobookshelf.org/#get-a-library-39-s-items
- """
-
- results: list[ABSLibraryItem]
-
-
-# Schema to enable sessions:
-@dataclass
-class ABSDeviceInfo(BaseModel):
- """ABSDeviceInfo.
-
- https://api.audiobookshelf.org/#device-info-parameters
- https://api.audiobookshelf.org/#device-info
- https://github.com/advplyr/audiobookshelf/blob/master/server/objects/DeviceInfo.js#L3
- """
-
- device_id: Annotated[str, Alias("deviceId")] = ""
- client_name: Annotated[str, Alias("clientName")] = ""
- client_version: Annotated[str, Alias("clientVersion")] = ""
- manufacturer: str = ""
- model: str = ""
- # sdkVersion # meant for an Android client
-
-
-@dataclass
-class ABSPlayRequest(BaseModel):
- """ABSPlayRequest.
-
- https://api.audiobookshelf.org/#play-a-library-item-or-podcast-episode
- """
-
- device_info: Annotated[ABSDeviceInfo, Alias("deviceInfo")]
- force_direct_play: Annotated[bool, Alias("forceDirectPlay")] = False
- force_transcode: Annotated[bool, Alias("forceTranscode")] = False
- supported_mime_types: Annotated[list[str], Alias("supportedMimeTypes")] = field(
- default_factory=list
- )
- media_player: Annotated[str, Alias("mediaPlayer")] = "unknown"
+# two additional user variants do exist
class ABSPlayMethod(Enum):
LOCAL = 3
+### https://api.audiobookshelf.org/#playback-session
+
+
@dataclass
class ABSPlaybackSession(BaseModel):
- """ABSPlaybackSessionExpanded.
-
- https://api.audiobookshelf.org/#play-method
- """
+ """ABSPlaybackSession."""
id_: Annotated[str, Alias("id")]
user_id: Annotated[str, Alias("userId")]
@dataclass
class ABSPlaybackSessionExpanded(ABSPlaybackSession):
- """ABSPlaybackSessionExpanded.
-
- https://api.audiobookshelf.org/#play-method
- """
+ """ABSPlaybackSessionExpanded."""
audio_tracks: Annotated[list[ABSAudioTrack], Alias("audioTracks")]
# libraryItem:
+### https://api.audiobookshelf.org/#podcast-metadata
+
+
@dataclass
-class ABSSessionUpdate(BaseModel):
- """
- ABSSessionUpdate.
+class ABSPodcastMetadata(BaseModel):
+ """ABSPodcastMetadata."""
- Can be used as optional data to sync or closing request.
- unit is seconds
+ title: str | None
+ author: str | None
+ description: str | None
+ release_date: Annotated[str | None, Alias("releaseDate")]
+ genres: list[str] | None
+ feed_url: Annotated[str | None, Alias("feedUrl")]
+ image_url: Annotated[str | None, Alias("imageUrl")]
+ itunes_page_url: Annotated[str | None, Alias("itunesPageUrl")]
+ itunes_id: Annotated[int | None, Alias("itunesId")]
+ itunes_artist_id: Annotated[int | None, Alias("itunesArtistId")]
+ explicit: bool
+ language: str | None
+ type_: Annotated[str | None, Alias("type")]
+
+
+@dataclass
+class ABSPodcastMetadataMinified(ABSPodcastMetadata):
+ """ABSPodcastMetadataMinified."""
+
+ title_ignore_prefix: Annotated[str, Alias("titleIgnorePrefix")]
+
+
+ABSPodcastMetaDataExpanded = ABSPodcastMetadataMinified
+
+### https://api.audiobookshelf.org/#podcast-episode
+
+
+@dataclass
+class ABSPodcastEpisode(BaseModel):
+ """ABSPodcastEpisode."""
+
+ library_item_id: Annotated[str, Alias("libraryItemId")]
+ id_: Annotated[str, Alias("id")]
+ index: int | None
+ # audio_file: # not needed for mass application
+ published_at: Annotated[int | None, Alias("publishedAt")] # ms posix epoch
+ added_at: Annotated[int | None, Alias("addedAt")] # ms posix epoch
+ updated_at: Annotated[int | None, Alias("updatedAt")] # ms posix epoch
+ season: str = ""
+ episode: str = ""
+ episode_type: Annotated[str, Alias("episodeType")] = ""
+ title: str = ""
+ subtitle: str = ""
+ description: str = ""
+ enclosure: str = ""
+ pub_date: Annotated[str, Alias("pubDate")] = ""
+ guid: str = ""
+ # chapters
+
+
+@dataclass
+class ABSPodcastEpisodeExpanded(BaseModel):
+ """ABSPodcastEpisode.
+
+ https://api.audiobookshelf.org/#podcast-episode
"""
- current_time: Annotated[float, Alias("currentTime")]
- time_listened: Annotated[float, Alias("timeListened")]
+ library_item_id: Annotated[str, Alias("libraryItemId")]
+ id_: Annotated[str, Alias("id")]
+ index: int | None
+ # audio_file: # not needed for mass application
+ published_at: Annotated[int | None, Alias("publishedAt")] # ms posix epoch
+ added_at: Annotated[int | None, Alias("addedAt")] # ms posix epoch
+ updated_at: Annotated[int | None, Alias("updatedAt")] # ms posix epoch
+ audio_track: Annotated[ABSAudioTrack, Alias("audioTrack")]
+ size: int # in bytes
+ season: str = ""
+ episode: str = ""
+ episode_type: Annotated[str, Alias("episodeType")] = ""
+ title: str = ""
+ subtitle: str = ""
+ description: str = ""
+ enclosure: str = ""
+ pub_date: Annotated[str, Alias("pubDate")] = ""
+ guid: str = ""
+ # chapters
+ duration: float = 0.0
+
+
+@dataclass
+class _ABSPodcastBase(BaseModel):
+ """_ABSPodcastBase."""
+
+ cover_path: Annotated[str, Alias("coverPath")]
+
+
+### https://api.audiobookshelf.org/#podcast
+
+
+@dataclass
+class ABSPodcast(_ABSPodcastBase):
+ """ABSPodcast."""
+
+ metadata: ABSPodcastMetadata
+ library_item_id: Annotated[str, Alias("libraryItemId")]
+ tags: list[str]
+ episodes: list[ABSPodcastEpisode]
+
+
+@dataclass
+class ABSPodcastMinified(_ABSPodcastBase):
+ """ABSPodcastMinified."""
+
+ metadata: ABSPodcastMetadataMinified
+ size: int # bytes
+ num_episodes: Annotated[int, Alias("numEpisodes")] = 0
+
+
+@dataclass
+class ABSPodcastExpanded(_ABSPodcastBase):
+ """ABSPodcastEpisodeExpanded."""
+
+ size: int # bytes
+ metadata: ABSPodcastMetaDataExpanded
+ episodes: list[ABSPodcastEpisodeExpanded]
+
+
+### https://api.audiobookshelf.org/#book-metadata
+
+
+@dataclass
+class _ABSBookMetadataBase(BaseModel):
+ """_ABSBookMetadataBase."""
+
+ title: str
+ subtitle: str
+ genres: list[str] | None
+ published_year: Annotated[str | None, Alias("publishedYear")]
+ published_date: Annotated[str | None, Alias("publishedDate")]
+ publisher: str | None
+ description: str | None
+ isbn: str | None
+ asin: str | None
+ language: str | None
+ explicit: bool
+
+
+@dataclass
+class ABSBookMetadata(_ABSBookMetadataBase):
+ """ABSBookMetadata."""
+
+ authors: list[ABSAuthorMinified]
+ narrators: list[str]
+ series: list[ABSSeriesSequence]
+
+
+@dataclass
+class ABSBookMetadataMinified(_ABSBookMetadataBase):
+ """ABSBookMetadataMinified."""
+
+ # these are normally there
+ title_ignore_prefix: Annotated[str, Alias("titleIgnorePrefix")]
+ author_name: Annotated[str, Alias("authorName")]
+ author_name_lf: Annotated[str, Alias("authorNameLF")]
+ narrator_name: Annotated[str, Alias("narratorName")]
+ series_name: Annotated[str, Alias("seriesName")]
+
+
+@dataclass
+class ABSBookMetadataExpanded(ABSBookMetadata, ABSBookMetadataMinified):
+ """ABSAudioBookMetaDataExpanded."""
+
+
+### https://api.audiobookshelf.org/#book
+
+
+@dataclass
+class _ABSBookBase(BaseModel):
+ """_ABSBookBase."""
+
+ tags: list[str]
+ cover_path: Annotated[str | None, Alias("coverPath")]
+
+
+@dataclass
+class ABSBook(_ABSBookBase):
+ """ABSBook."""
+
+ library_item_id: Annotated[str, Alias("libraryItemId")]
+ metadata: ABSBookMetadata
+ # audioFiles
+ chapters: list[ABSBookChapter]
+ # ebookFile
+
+
+@dataclass
+class ABSBookMinified(_ABSBookBase):
+ """ABSBookBase."""
+
+ metadata: ABSBookMetadataMinified
+ num_tracks: Annotated[int, Alias("numTracks")]
+ num_audiofiles: Annotated[int, Alias("numAudioFiles")]
+ num_chapters: Annotated[int, Alias("numChapters")]
+ duration: float # in s
+ size: int # in bytes
+ # ebookFormat
+
+
+@dataclass
+class ABSBookExpanded(_ABSBookBase):
+ """ABSBookExpanded."""
+
+ library_item_id: Annotated[str, Alias("libraryItemId")]
+ metadata: ABSBookMetadataExpanded
+ chapters: list[ABSBookChapter]
duration: float
+ size: int # bytes
+ tracks: list[ABSAudioTrack]
+
+
+### https://api.audiobookshelf.org/#library-item
+
+
+@dataclass
+class _ABSLibraryItemBase(BaseModel):
+ """_ABSLibraryItemBase."""
+
+ id_: Annotated[str, Alias("id")]
+ ino: str
+ library_id: Annotated[str, Alias("libraryId")]
+ folder_id: Annotated[str, Alias("folderId")]
+ path: str
+ relative_path: Annotated[str, Alias("relPath")]
+ is_file: Annotated[bool, Alias("isFile")]
+ last_modified_ms: Annotated[int, Alias("mtimeMs")] # epoch
+ last_changed_ms: Annotated[int, Alias("ctimeMs")] # epoch
+ birthtime_ms: Annotated[int, Alias("birthtimeMs")] # epoch
+ added_at: Annotated[int, Alias("addedAt")] # ms epoch
+ updated_at: Annotated[int, Alias("updatedAt")] # ms epoch
+ is_missing: Annotated[bool, Alias("isMissing")]
+ is_invalid: Annotated[bool, Alias("isInvalid")]
+ media_type: Annotated[str, Alias("mediaType")]
+
+
+@dataclass
+class _ABSLibraryItem(_ABSLibraryItemBase):
+ """ABSLibraryItem."""
+
+ last_scan: Annotated[int | None, Alias("lastScan")] # ms epoch
+ scan_version: Annotated[str | None, Alias("scanVersion")]
+ # libraryFiles
+
+
+@dataclass
+class ABSLibraryItemBook(_ABSLibraryItem):
+ """ABSLibraryItemBook."""
+
+ media: ABSBook
+
+
+@dataclass
+class ABSLibraryItemBookSeries(ABSLibraryItemBook):
+ """ABSLibraryItemNormalBookSeries.
+
+ Special class, when having the scheme of SeriesBooks, see
+ https://api.audiobookshelf.org/#series, it gets an extra
+ sequence key.
+ """
+
+ sequence: int
+
+
+@dataclass
+class ABSLibraryItemPodcast(_ABSLibraryItem):
+ """ABSLibraryItemPodcast."""
+
+ media: ABSPodcast
+
+
+@dataclass
+class _ABSLibraryItemMinified(_ABSLibraryItemBase):
+ """ABSLibraryItemMinified."""
+
+ num_files: Annotated[int, Alias("numFiles")]
+ size: int # bytes
+
+
+@dataclass
+class ABSLibraryItemMinifiedBook(_ABSLibraryItemMinified):
+ """ABSLibraryItemMinifiedBook."""
+
+ media: ABSBookMinified
+
+
+@dataclass
+class ABSLibraryItemMinifiedPodcast(_ABSLibraryItemMinified):
+ """ABSLibraryItemMinifiedBook."""
+
+ media: ABSPodcastMinified
+
+
+@dataclass
+class _ABSLibraryItemExpanded(_ABSLibraryItemBase):
+ """ABSLibraryItemExpanded."""
+
+ size: int # bytes
+
+
+@dataclass
+class ABSLibraryItemExpandedBook(_ABSLibraryItemExpanded):
+ """ABSLibraryItemExpanded."""
+
+ media: ABSBookExpanded
+
+
+@dataclass
+class ABSLibraryItemExpandedPodcast(_ABSLibraryItemExpanded):
+ """ABSLibraryItemExpanded."""
+
+ media: ABSPodcastExpanded
+
+
+# extra classes down here so they can make proper references
+
+
+@dataclass
+class ABSSeriesBooks(_ABSSeriesBase):
+ """ABSSeriesBooks."""
+
+ added_at: Annotated[int, Alias("addedAt")] # ms epoch
+ name_ignore_prefix: Annotated[str, Alias("nameIgnorePrefix")]
+ name_ignore_prefix_sort: Annotated[str, Alias("nameIgnorePrefixSort")]
+ type_: Annotated[str, Alias("type")]
+ books: list[ABSLibraryItemBookSeries]
+ total_duration: Annotated[float, Alias("totalDuration")] # s
+
+
+@dataclass
+class ABSMediaProgressWithMediaBook(ABSMediaProgress):
+ """ABSMediaProgressWithMediaBook."""
+
+ media: ABSBookExpanded
+
+
+@dataclass
+class ABSMediaProgressWithMediaPodcast(ABSMediaProgress):
+ """ABSMediaProgressWithMediaBook."""
+
+ media: ABSPodcastExpanded
+ episode: ABSPodcastEpisode
+
+
+### Response to API Requests
+
+
+@dataclass
+class ABSLoginResponse(BaseModel):
+ """ABSLoginResponse."""
+
+ user: ABSUser
+
+ # this seems to be missing
+ # user_default_library_id: Annotated[str, Alias("defaultLibraryId")]
+
+
+@dataclass
+class ABSLibrariesResponse(BaseModel):
+ """ABSLibrariesResponse."""
+
+ libraries: list[ABSLibrary]
@dataclass
num_pages: Annotated[int, Alias("numPages")]
items_per_page: Annotated[int, Alias("itemsPerPage")]
sessions: list[ABSPlaybackSession]
+
+
+@dataclass
+class ABSLibrariesItemsMinifiedBookResponse(BaseModel):
+ """ABSLibrariesItemsResponse.
+
+ https://api.audiobookshelf.org/#get-a-library-39-s-items
+ No matter what options I append to the request, I always end up with
+ minified items. Maybe a bug in abs. If that would be fixed, there is
+ potential for reduced in API calls.
+ """
+
+ results: list[ABSLibraryItemMinifiedBook]
+
+
+@dataclass
+class ABSLibrariesItemsMinifiedPodcastResponse(BaseModel):
+ """ABSLibrariesItemsResponse.
+
+ see above.
+ """
+
+ results: list[ABSLibraryItemMinifiedPodcast]
+
+
+### Requests to API we can make
+
+
+@dataclass
+class ABSPlayRequest(BaseModel):
+ """ABSPlayRequest.
+
+ https://api.audiobookshelf.org/#play-a-library-item-or-podcast-episode
+ """
+
+ device_info: Annotated[ABSDeviceInfo, Alias("deviceInfo")]
+ force_direct_play: Annotated[bool, Alias("forceDirectPlay")] = False
+ force_transcode: Annotated[bool, Alias("forceTranscode")] = False
+ supported_mime_types: Annotated[list[str], Alias("supportedMimeTypes")] = field(
+ default_factory=list
+ )
+ media_player: Annotated[str, Alias("mediaPlayer")] = "unknown"
+
+
+@dataclass
+class ABSSessionUpdate(BaseModel):
+ """
+ ABSSessionUpdate.
+
+ Can be used as optional data to sync or closing request.
+ unit is seconds
+ """
+
+ current_time: Annotated[float, Alias("currentTime")]
+ time_listened: Annotated[float, Alias("timeListened")]
+ duration: float