Skip to content

Commit

Permalink
Refactor chapters and podcast episodes
Browse files Browse the repository at this point in the history
  • Loading branch information
marcelveldt committed Jan 6, 2025
1 parent 0a5f46b commit 94ea134
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 45 deletions.
3 changes: 1 addition & 2 deletions music_assistant_models/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,8 @@ class MediaType(StrEnum, metaclass=MediaTypeMeta):
PLAYLIST = "playlist"
RADIO = "radio"
AUDIOBOOK = "audiobook"
CHAPTER = "chapter"
PODCAST = "podcast"
EPISODE = "episode"
PODCAST_EPISODE = "podcast_episode"
FOLDER = "folder"
ANNOUNCEMENT = "announcement"
FLOW_STREAM = "flow_stream"
Expand Down
29 changes: 8 additions & 21 deletions music_assistant_models/media_items/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,15 @@
Artist,
Audiobook,
BrowseFolder,
Chapter,
Episode,
ItemMapping,
MediaItem,
Playlist,
Podcast,
PodcastEpisode,
Radio,
Track,
)
from .metadata import MediaItemImage, MediaItemLink, MediaItemMetadata
from .metadata import MediaItemChapter, MediaItemImage, MediaItemLink, MediaItemMetadata
from .provider_mapping import ProviderMapping

__all__ = [
Expand All @@ -36,42 +35,32 @@
"AudioFormat",
"Audiobook",
"BrowseFolder",
"Chapter",
"Episode",
"ItemMapping",
"MediaItem",
"MediaItemChapter",
"MediaItemImage",
"MediaItemLink",
"MediaItemMetadata",
"MediaItemMetadata",
"Metadata",
"MetadataProvider",
"MetadataProviderStatus",
"MetadataProviderType",
"Playlist",
"PlaylistTrack",
"Podcast",
"PodcastEpisode",
"ProviderMapping",
"Radio",
"Track",
"UniqueList",
]

MediaItemType = (
Artist
| Album
| Track
| Radio
| Playlist
| Audiobook
| Chapter
| Podcast
| Episode
| BrowseFolder
Artist | Album | Track | Radio | Playlist | Audiobook | Podcast | PodcastEpisode | BrowseFolder
)

# directly playable media items
PlayableMediaItemType = Track | Radio | Chapter | Episode
PlayableMediaItemType = Track | Radio | Audiobook | PodcastEpisode


@dataclass(kw_only=True)
Expand Down Expand Up @@ -103,12 +92,10 @@ def media_from_dict(media_item: dict[str, Any]) -> MediaItemType | ItemMapping:
return Radio.from_dict(media_item)
if media_item["media_type"] == "audiobook":
return Audiobook.from_dict(media_item)
if media_item["media_type"] == "chapter":
return Chapter.from_dict(media_item)
if media_item["media_type"] == "podcast":
return Podcast.from_dict(media_item)
if media_item["media_type"] == "episode":
return Episode.from_dict(media_item)
if media_item["media_type"] == "podcast_episode":
return PodcastEpisode.from_dict(media_item)
raise InvalidDataError("Unknown media type")


Expand Down
20 changes: 4 additions & 16 deletions music_assistant_models/media_items/media_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,28 +255,16 @@ class Audiobook(MediaItem):
__eq__ = _MediaItemBase.__eq__

publisher: str | None = None
total_chapters: int | None = None
authors: UniqueList[str] = field(default_factory=UniqueList)
narrators: UniqueList[str] = field(default_factory=UniqueList)
media_type: MediaType = MediaType.AUDIOBOOK


@dataclass(kw_only=True)
class Chapter(MediaItem):
"""Model for an Audiobook Chapter."""

__hash__ = _MediaItemBase.__hash__
__eq__ = _MediaItemBase.__eq__

position: int # sort position / chapter number
audiobook: Audiobook | ItemMapping
duration: int = 0
# resume point info
# set to None if unknown/unsupported by provider
# which will let MA fallback to an internal resume point
fully_played: bool | None = None
resume_position_ms: int | None = None
media_type: MediaType = MediaType.CHAPTER

media_type: MediaType = MediaType.AUDIOBOOK


@dataclass(kw_only=True)
Expand All @@ -292,7 +280,7 @@ class Podcast(MediaItem):


@dataclass(kw_only=True)
class Episode(MediaItem):
class PodcastEpisode(MediaItem):
"""Model for a Podcast Episode."""

__hash__ = _MediaItemBase.__hash__
Expand All @@ -308,7 +296,7 @@ class Episode(MediaItem):
fully_played: bool | None = None
resume_position_ms: int | None = None

media_type: MediaType = MediaType.EPISODE
media_type: MediaType = MediaType.PODCAST_EPISODE


@dataclass(kw_only=True)
Expand Down
24 changes: 23 additions & 1 deletion music_assistant_models/media_items/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from __future__ import annotations

from dataclasses import dataclass, fields
from dataclasses import dataclass, field, fields

from mashumaro import DataClassDictMixin

Expand Down Expand Up @@ -49,6 +49,25 @@ def __eq__(self, other: object) -> bool:
return self.__hash__() == other.__hash__()


@dataclass(frozen=True, kw_only=True)
class MediaItemChapter(DataClassDictMixin):
"""Model for a MediaItem's chapter/bookmark."""

position: int # sort position/number
name: str # friendly name
start: float # start position in seconds
end: float | None = None # start position in seconds if known

@property
def duration(self) -> float:
"""Return duration of chapter."""
return self.end - self.start if self.end else 0

def __hash__(self) -> int:
"""Return custom hash."""
return hash(self.position)


@dataclass(kw_only=True)
class MediaItemMetadata(DataClassDictMixin):
"""Model for a MediaItem's metadata."""
Expand All @@ -70,6 +89,9 @@ class MediaItemMetadata(DataClassDictMixin):
popularity: int | None = None
release_date: str | None = None
languages: UniqueList[str] | None = None
# chapters is a list of available chapters, sorted by position
# most commonly used for audiobooks and podcast episodes
chapters: list[MediaItemChapter] = field(default_factory=list)
# last_refresh: timestamp the (full) metadata was last collected
last_refresh: int | None = None

Expand Down
6 changes: 1 addition & 5 deletions music_assistant_models/queue_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,7 @@ def get_image(media_item: PlayableMediaItemType | None) -> MediaItemImage | None
return media_item.image
if media_item.media_type == MediaType.TRACK and (album := getattr(media_item, "album", None)):
return get_image(album)
if media_item.media_type == MediaType.CHAPTER and (
audiobook := getattr(media_item, "audiobook", None)
):
return get_image(audiobook)
if media_item.media_type == MediaType.EPISODE and (
if media_item.media_type == MediaType.PODCAST_EPISODE and (
podcast := getattr(media_item, "podcast", None)
):
return get_image(podcast)
Expand Down

0 comments on commit 94ea134

Please sign in to comment.