mpd-now-playable/src/mpd_now_playable/song/musicbrainz.py

107 lines
4.4 KiB
Python

from dataclasses import dataclass
from functools import partial
from typing import Annotated, TypedDict
from uuid import UUID
from pydantic import Field
from ..tools.types import MaybePlural, option_fmap, un_maybe_plural
option_uuid = partial(option_fmap, UUID)
OptionUUID = Annotated[UUID | None, Field(default=None)]
def to_uuids(values: MaybePlural[str] | None) -> list[UUID]:
return [UUID(i) for i in un_maybe_plural(values)]
class MusicBrainzTags(TypedDict, total=False):
"""
The MusicBrainz tags mpd-now-playable expects and will load (all optional).
They're named slightly differently than the actual MusicBrainz IDs they
store - use to_brainz to map them across to their canonical form.
https://picard-docs.musicbrainz.org/downloads/MusicBrainz_Picard_Tag_Map.html
"""
#: MusicBrainz Recording ID
musicbrainz_trackid: str
#: MusicBrainz Track ID
musicbrainz_releasetrackid: str
#: MusicBrainz Artist ID
musicbrainz_artistid: MaybePlural[str]
#: MusicBrainz Release ID
musicbrainz_albumid: MaybePlural[str]
#: MusicBrainz Release Artist ID
musicbrainz_albumartistid: MaybePlural[str]
#: MusicBrainz Release Group ID
musicbrainz_releasegroupid: str
#: MusicBrainz Work ID
musicbrainz_workid: str
@dataclass(slots=True)
class MusicBrainzIds:
#: A MusicBrainz recording represents audio from a specific performance.
#: For example, if the same song was released as a studio recording and as
#: a live performance, those two versions of the song are different
#: recordings. The song itself is considered a "work", of which two
#: recordings were made. However, recordings are not always associated with
#: a work in the MusicBrainz database, and Picard won't load work IDs by
#: default (you have to enable "use track relationships" in the options),
#: so recording IDs are a much more reliable way to identify a particular
#: song.
#: https://musicbrainz.org/doc/Recording
recording: OptionUUID
#: A MusicBrainz work represents the idea of a particular song or creation
#: (it doesn't have to be audio). Each work may have multiple recordings
#: (studio versus live, different performers, etc.), with the work ID
#: grouping them together.
#: https://musicbrainz.org/doc/Work
work: OptionUUID
#: A MusicBrainz track represents a specific instance of a recording
#: appearing as part of some release. For example, if the same song appears
#: on both two-CD and four-CD versions of a soundtrack, then it will be
#: considered the same "recording" in both cases, but different "tracks".
#: https://musicbrainz.org/doc/Track
track: OptionUUID
#: A MusicBrainz artist is pretty intuitively the artist who recorded the
#: song. This particular ID refers to the individual recording's artist or
#: artists, which may be distinct from the release artist below when a
#: release contains recordings from many different artists.
#: https://musicbrainz.org/doc/Artist
artist: list[UUID]
#: A MusicBrainz release roughly corresponds to an "album", and indeed is
#: stored in a tag called MUSICBRAINZ_ALBUMID. The more general name is
#: meant to encompass all the different ways music can be released.
#: https://musicbrainz.org/doc/Release
release: list[UUID]
#: Again, the release artist corresponds to an "album artist". These MBIDs
#: refer to the same artists in the MusicBrainz database that individual
#: recordings' artist MBIDs do.
release_artist: list[UUID]
#: A MusicBrainz release group roughly corresponds to "all the editions of
#: a particular album". For example, if the same album were released on CD,
#: vinyl records, and as a digital download, then all of those would be
#: different releases but share a release group. Note that MPD's support
#: for this tag is relatively new (July 2023) and doesn't seem especially
#: reliable, so it might be missing here even if your music has been tagged
#: with it. Not sure why. https://musicbrainz.org/doc/Release_Group
release_group: OptionUUID
def to_brainz(tags: MusicBrainzTags) -> MusicBrainzIds:
return MusicBrainzIds(
recording=option_uuid(tags.get("musicbrainz_trackid")),
work=option_uuid(tags.get("musicbrainz_workid")),
track=option_uuid(tags.get("musicbrainz_releasetrackid")),
artist=to_uuids(tags.get("musicbrainz_artistid")),
release=to_uuids(tags.get("musicbrainz_albumid")),
release_artist=to_uuids(tags.get("musicbrainz_albumartistid")),
release_group=option_uuid(tags.get("musicbrainz_releasegroupid")),
)