MetadataTypes = int | bool | str | list[str]
-@dataclass
+@dataclass(kw_only=True)
class AudioFormat(DataClassDictMixin):
"""Model for AudioFormat details."""
return int(self.sample_rate * (self.bit_depth / 8) * self.channels)
-@dataclass(frozen=True)
+@dataclass(frozen=True, kw_only=True)
class ProviderMapping(DataClassDictMixin):
"""Model for a MediaItem's provider mapping details."""
return self.provider_instance == other.provider_instance and self.item_id == other.item_id
-@dataclass(frozen=True)
+@dataclass(frozen=True, kw_only=True)
class MediaItemLink(DataClassDictMixin):
"""Model for a link."""
return self.url == other.url
-@dataclass(frozen=True)
+@dataclass(frozen=True, kw_only=True)
class MediaItemImage(DataClassDictMixin):
"""Model for a image."""
return self.__hash__() == other.__hash__()
-@dataclass(frozen=True)
+@dataclass(frozen=True, kw_only=True)
class MediaItemChapter(DataClassDictMixin):
"""Model for a chapter."""
return self.chapter_id == other.chapter_id
-@dataclass
+@dataclass(kw_only=True)
class MediaItemMetadata(DataClassDictMixin):
"""Model for a MediaItem's metadata."""
return self.uri == other.uri
-@dataclass
+@dataclass(kw_only=True)
class ItemMapping(DataClassDictMixin):
"""Representation of a minimized item object."""
MediaItemType = Artist | Album | Track | Radio | Playlist | BrowseFolder
-@dataclass
+@dataclass(kw_only=True)
class PagedItems(DataClassDictMixin):
"""Model for a paged listing."""
)
-@dataclass
+@dataclass(kw_only=True)
class SearchResults(DataClassDictMixin):
"""Model for results from a search query."""
return MediaItem.from_dict(media_item)
-@dataclass
+@dataclass(kw_only=True)
class StreamDetails(DataClassDictMixin):
"""Model for streamdetails."""
total = offset + count
else:
total = await self.mass.music.database.get_count_from_query(sql_query, params)
- return PagedItems(items, count, limit, offset, total)
+ return PagedItems(items=items, count=count, limit=limit, offset=offset, total=total)
async def iter_library_items(
self,
img_data = await create_collage(self.mass, list(images))
async with aiofiles.open(img_path, "wb") as _file:
await _file.write(img_data)
- playlist.metadata.images = [MediaItemImage(ImageType.THUMB, img_path, "file")]
+ playlist.metadata.images = [
+ MediaItemImage(type=ImageType.THUMB, path=img_path, provider="file")
+ ]
except Exception as err:
LOGGER.debug("Error while creating playlist image", exc_info=err)
# set timestamp, used to determine when this function was last called
)
cur_db_ids.add(library_item.item_id)
except MusicAssistantError as err:
- self.logger.warning("Skipping sync of item %s: %s", prov_item.uri, str(err))
+ self.logger.warning(
+ "Skipping sync of item %s - error details: %s", prov_item.uri, str(err)
+ )
# process deletions (= no longer in library)
cache_key = f"library_items.{media_type}.{self.instance_id}"
duration=track.duration,
artists=[
ItemMapping(
- MediaType.ARTIST,
- str(track.artist.id),
- self.instance_id,
- track.artist.name,
+ media_type=MediaType.ARTIST,
+ item_id=str(track.artist.id),
+ provider=self.domain,
+ name=track.artist.name,
)
],
album=ItemMapping(
- MediaType.ALBUM,
- str(track.album.id),
- self.instance_id,
- track.album.title,
+ media_type=MediaType.ALBUM,
+ item_id=str(track.album.id),
+ provider=self.domain,
+ name=track.album.title,
),
provider_mappings={
ProviderMapping(
if not items:
continue
for item in items:
- metadata.images.append(MediaItemImage(img_type, item["url"]))
+ metadata.images.append(MediaItemImage(type=img_type, path=item["url"]))
return metadata
return None
if not items:
continue
for item in items:
- metadata.images.append(MediaItemImage(img_type, item["url"]))
+ metadata.images.append(MediaItemImage(type=img_type, path=item["url"]))
return metadata
return None
# much space and bandwidth. Instead we set the filename as value so the image can
# be retrieved later in realtime.
track.metadata.images = [
- MediaItemImage(ImageType.THUMB, file_item.path, self.instance_id)
+ MediaItemImage(type=ImageType.THUMB, url=file_item.path, provider=self.instance_id)
]
if track.album and not track.album.metadata.images:
provider=self.instance_id,
name=name,
provider_mappings={
- ProviderMapping(artist_path, self.instance_id, self.instance_id, url=artist_path)
+ ProviderMapping(
+ item_id=artist_path,
+ provider_domain=self.domain,
+ provider_instance=self.instance_id,
+ url=artist_path,
+ )
},
mbid=VARIOUS_ARTISTS_ID_MBID if compare_strings(name, VARIOUS_ARTISTS_NAME) else None,
)
artists=artists,
provider_mappings={
ProviderMapping(
- album_path, self.instance_id, self.instance_id, url=album_path, barcode=barcode
+ item_id=album_path,
+ provider_domain=self.instance_id,
+ provider_instance=self.instance_id,
+ url=album_path,
+ barcode=barcode,
)
},
)
if item.ext != ext:
continue
try:
- images.append(MediaItemImage(ImageType(item.name), item.path, self.instance_id))
+ images.append(
+ MediaItemImage(
+ type=ImageType(item.name), path=item.path, provider=self.instance_id
+ )
+ )
except ValueError:
for filename in ("folder", "cover", "albumart", "artist"):
if item.name.lower().startswith(filename):
images.append(
- MediaItemImage(ImageType.THUMB, item.path, self.instance_id)
+ MediaItemImage(
+ type=ImageType.THUMB, path=item.path, provider=self.instance_id
+ )
)
break
return images
def _get_item_mapping(self, media_type: MediaType, key: str, name: str) -> ItemMapping:
return ItemMapping(
- media_type,
- key,
- self.instance_id,
- name,
+ media_type=media_type,
+ item_id=key,
+ provider=self.instance_id,
+ name=name,
)
async def _get_or_create_artist_by_name(self, artist_name) -> Artist:
if plex_album.year:
album.year = plex_album.year
if thumb := plex_album.firstAttr("thumb", "parentThumb", "grandparentThumb"):
- album.metadata.images = [MediaItemImage(ImageType.THUMB, thumb, self.instance_id)]
+ album.metadata.images = [
+ MediaItemImage(type=ImageType.THUMB, url=thumb, provider=self.instance_id)
+ ]
if plex_album.summary:
album.metadata.description = plex_album.summary
album.artists.append(
- self._get_item_mapping(MediaType.ARTIST, plex_album.parentKey, plex_album.parentTitle)
+ self._get_item_mapping(
+ type=MediaType.ARTIST, url=plex_album.parentKey, provider=plex_album.parentTitle
+ )
)
return album
if plex_artist.summary:
artist.metadata.description = plex_artist.summary
if thumb := plex_artist.firstAttr("thumb", "parentThumb", "grandparentThumb"):
- artist.metadata.images = [MediaItemImage(ImageType.THUMB, thumb, self.instance_id)]
+ artist.metadata.images = [
+ MediaItemImage(type=ImageType.THUMB, path=thumb, url=self.instance_id)
+ ]
return artist
async def _parse_playlist(self, plex_playlist: PlexPlaylist) -> Playlist:
if plex_playlist.summary:
playlist.metadata.description = plex_playlist.summary
if thumb := plex_playlist.firstAttr("thumb", "parentThumb", "grandparentThumb"):
- playlist.metadata.images = [MediaItemImage(ImageType.THUMB, thumb, self.instance_id)]
+ playlist.metadata.images = [
+ MediaItemImage(type=ImageType.THUMB, url=thumb, provider=self.instance_id)
+ ]
playlist.is_editable = True
return playlist
raise InvalidDataError("No artist was found for track")
if thumb := plex_track.firstAttr("thumb", "parentThumb", "grandparentThumb"):
- track.metadata.images = [MediaItemImage(ImageType.THUMB, thumb, self.instance_id)]
+ track.metadata.images = [
+ MediaItemImage(type=ImageType.THUMB, url=thumb, provider=self.instance_id)
+ ]
if plex_track.parentKey:
track.album = self._get_item_mapping(
MediaType.ALBUM, plex_track.parentKey, plex_track.parentKey
if plex_track.chapters:
track.metadata.chapters = [
MediaItemChapter(
- plex_chapter.id, plex_chapter.start, plex_chapter.end, plex_chapter.title
+ chapter_id=plex_chapter.id,
+ position_start=plex_chapter.start,
+ position_end=plex_chapter.end,
+ title=plex_chapter.title,
)
for plex_chapter in plex_track.chapters
]
artist.mbid = VARIOUS_ARTISTS_ID_MBID
artist.name = VARIOUS_ARTISTS_NAME
if img := self.__get_image(artist_obj):
- artist.metadata.images = [MediaItemImage(ImageType.THUMB, img)]
+ artist.metadata.images = [MediaItemImage(type=ImageType.THUMB, path=img)]
if artist_obj.get("biography"):
artist.metadata.description = artist_obj["biography"].get("content")
return artist
if "genre" in album_obj:
album.metadata.genres = {album_obj["genre"]["name"]}
if img := self.__get_image(album_obj):
- album.metadata.images = [MediaItemImage(ImageType.THUMB, img)]
+ album.metadata.images = [MediaItemImage(type=ImageType.THUMB, path=img)]
if "label" in album_obj:
album.metadata.label = album_obj["label"]["name"]
if (released_at := album_obj.get("released_at")) and released_at != 0:
if track_obj.get("parental_warning"):
track.metadata.explicit = True
if img := self.__get_image(track_obj):
- track.metadata.images = [MediaItemImage(ImageType.THUMB, img)]
+ track.metadata.images = [MediaItemImage(type=ImageType.THUMB, path=img)]
return track
or playlist_obj["is_collaborative"]
)
if img := self.__get_image(playlist_obj):
- playlist.metadata.images = [MediaItemImage(ImageType.THUMB, img)]
+ playlist.metadata.images = [MediaItemImage(type=ImageType.THUMB, path=img)]
playlist.metadata.checksum = str(playlist_obj["updated_at"])
return playlist
name="",
label=country.name,
)
- folder.metadata.images = [MediaItemImage(ImageType.THUMB, country.favicon)]
+ folder.metadata.images = [
+ MediaItemImage(type=ImageType.THUMB, path=country.favicon)
+ ]
sub_items.append(folder)
return BrowseFolder(
item_id="country",
)
radio.metadata.label = radio_obj.tags
radio.metadata.popularity = radio_obj.votes
- radio.metadata.links = [MediaItemLink(LinkType.WEBSITE, radio_obj.homepage)]
- radio.metadata.images = [MediaItemImage(ImageType.THUMB, radio_obj.favicon)]
+ radio.metadata.links = [MediaItemLink(type=LinkType.WEBSITE, url=radio_obj.homepage)]
+ radio.metadata.images = [MediaItemImage(type=ImageType.THUMB, path=radio_obj.favicon)]
return radio
artist.metadata.description = artist_obj["description"]
if artist_obj.get("avatar_url"):
img_url = artist_obj["avatar_url"]
- artist.metadata.images = [MediaItemImage(ImageType.THUMB, img_url)]
+ artist.metadata.images = [MediaItemImage(type=ImageType.THUMB, path=img_url)]
return artist
async def _parse_playlist(self, playlist_obj: dict) -> Playlist:
playlist.metadata.description = playlist_obj["description"]
if playlist_obj.get("artwork_url"):
playlist.metadata.images = [
- MediaItemImage(ImageType.THUMB, playlist_obj["artwork_url"])
+ MediaItemImage(type=ImageType.THUMB, path=playlist_obj["artwork_url"])
]
if playlist_obj.get("genre"):
playlist.metadata.genres = playlist_obj["genre"]
track.artists.append(artist)
if track_obj.get("artwork_url"):
- track.metadata.images = [MediaItemImage(ImageType.THUMB, track_obj["artwork_url"])]
+ track.metadata.images = [
+ MediaItemImage(type=ImageType.THUMB, path=track_obj["artwork_url"])
+ ]
if track_obj.get("description"):
track.metadata.description = track_obj["description"]
if track_obj.get("genre"):
for img in artist_obj["images"]:
img_url = img["url"]
if "2a96cbd8b46e442fc41c2b86b821562f" not in img_url:
- artist.metadata.images = [MediaItemImage(ImageType.THUMB, img_url)]
+ artist.metadata.images = [MediaItemImage(type=ImageType.THUMB, path=img_url)]
break
return artist
if "genres" in album_obj:
album.metadata.genre = set(album_obj["genres"])
if album_obj.get("images"):
- album.metadata.images = [MediaItemImage(ImageType.THUMB, album_obj["images"][0]["url"])]
+ album.metadata.images = [
+ MediaItemImage(type=ImageType.THUMB, path=album_obj["images"][0]["url"])
+ ]
if "label" in album_obj:
album.metadata.label = album_obj["label"]
if album_obj.get("release_date"):
track.album = await self._parse_album(track_obj["album"])
if track_obj["album"].get("images"):
track.metadata.images = [
- MediaItemImage(ImageType.THUMB, track_obj["album"]["images"][0]["url"])
+ MediaItemImage(
+ type=ImageType.THUMB, path=track_obj["album"]["images"][0]["url"]
+ )
]
if track_obj.get("copyright"):
track.metadata.copyright = track_obj["copyright"]
)
if playlist_obj.get("images"):
playlist.metadata.images = [
- MediaItemImage(ImageType.THUMB, playlist_obj["images"][0]["url"])
+ MediaItemImage(type=ImageType.THUMB, path=playlist_obj["images"][0]["url"])
]
playlist.metadata.checksum = str(playlist_obj["snapshot_id"])
return playlist
metadata.links = set()
for key, link_type in LINK_MAPPING.items():
if link := artist_obj.get(key):
- metadata.links.add(MediaItemLink(link_type, link))
+ metadata.links.add(MediaItemLink(type=link_type, url=link))
# description/biography
if desc := artist_obj.get(f"strBiography{self.mass.metadata.preferred_language}"):
metadata.description = desc
for key, img_type in IMG_MAPPING.items():
for postfix in ("", "2", "3", "4", "5", "6", "7", "8", "9", "10"):
if img := artist_obj.get(f"{key}{postfix}"):
- metadata.images.append(MediaItemImage(img_type, img))
+ metadata.images.append(MediaItemImage(type=img_type, path=img))
else:
break
return metadata
metadata.links = set()
if link := album_obj.get("strWikipediaID"):
metadata.links.add(
- MediaItemLink(LinkType.WIKIPEDIA, f"https://wikipedia.org/wiki/{link}")
+ MediaItemLink(type=LinkType.WIKIPEDIA, url=f"https://wikipedia.org/wiki/{link}")
)
if link := album_obj.get("strAllMusicID"):
metadata.links.add(
- MediaItemLink(LinkType.ALLMUSIC, f"https://www.allmusic.com/album/{link}")
+ MediaItemLink(type=LinkType.ALLMUSIC, url=f"https://www.allmusic.com/album/{link}")
)
# description
for key, img_type in IMG_MAPPING.items():
for postfix in ("", "2", "3", "4", "5", "6", "7", "8", "9", "10"):
if img := album_obj.get(f"{key}{postfix}"):
- metadata.images.append(MediaItemImage(img_type, img))
+ metadata.images.append(MediaItemImage(type=img_type, path=img))
else:
break
return metadata
for key, img_type in IMG_MAPPING.items():
for postfix in ("", "2", "3", "4", "5", "6", "7", "8", "9", "10"):
if img := track_obj.get(f"{key}{postfix}"):
- metadata.images.append(MediaItemImage(img_type, img))
+ metadata.images.append(MediaItemImage(type=img_type, path=img))
else:
break
return metadata
image_url = await self._get_image_url(artist_obj, 750)
artist.metadata.images = [
MediaItemImage(
- ImageType.THUMB,
- image_url,
+ type=ImageType.THUMB,
+ path=image_url,
)
]
except Exception:
image_url = await self._get_image_url(album_obj, 1280)
album.metadata.images = [
MediaItemImage(
- ImageType.THUMB,
- image_url,
+ type=ImageType.THUMB,
+ path=image_url,
)
]
except Exception:
image_url = await self._get_image_url(playlist_obj, 1080)
playlist.metadata.images = [
MediaItemImage(
- ImageType.THUMB,
- image_url,
+ type=ImageType.THUMB,
+ path=image_url,
)
]
except Exception:
radio.metadata.description = details["text"]
# images
if img := details.get("image"):
- radio.metadata.images = [MediaItemImage(ImageType.THUMB, img)]
+ radio.metadata.images = [MediaItemImage(type=ImageType.THUMB, path=img)]
if img := details.get("logo"):
- radio.metadata.images = [MediaItemImage(ImageType.LOGO, img)]
+ radio.metadata.images = [MediaItemImage(type=ImageType.LOGO, path=img)]
return radio
async def get_stream_details(self, item_id: str) -> StreamDetails:
provider=self.domain,
name=artist,
provider_mappings={
- ProviderMapping(artist, self.domain, self.instance_id, available=False)
+ ProviderMapping(
+ item_id=artist,
+ provider_domain=self.domain,
+ provider_instance=self.instance_id,
+ available=False,
+ )
},
)
)
if media_info.has_cover_image:
- media_item.metadata.images = [MediaItemImage(ImageType.THUMB, url, True)]
+ media_item.metadata.images = [
+ MediaItemImage(type=ImageType.THUMB, path=url, provider="embedded")
+ ]
return media_item
async def _get_media_info(
async def _parse_thumbnails(cls, thumbnails_obj: dict) -> list[MediaItemImage]:
"""Parse and sort a list of thumbnails and return the highest quality."""
thumb = sorted(thumbnails_obj, key=itemgetter("width"), reverse=True)[0]
- return [MediaItemImage(ImageType.THUMB, thumb["url"])]
+ return [MediaItemImage(type=ImageType.THUMB, path=thumb["url"])]
@classmethod
async def _parse_stream_format(cls, track_obj: dict) -> dict: