mirror of
https://github.com/beetbox/beets.git
synced 2026-02-09 00:41:57 +01:00
Normalize dashes to underscores in musicbrainz data fields
This commit is contained in:
parent
680473b9e5
commit
e0842c44db
8 changed files with 723 additions and 665 deletions
|
|
@ -169,7 +169,7 @@ class MusicBrainzAPI(RequestHandler):
|
|||
if includes:
|
||||
kwargs["inc"] = "+".join(includes)
|
||||
|
||||
return self._group_relations(
|
||||
return self._normalize_data(
|
||||
self.get_json(f"{self.api_root}/{resource}", params=kwargs)
|
||||
)
|
||||
|
||||
|
|
@ -240,29 +240,39 @@ class MusicBrainzAPI(RequestHandler):
|
|||
|
||||
@singledispatchmethod
|
||||
@classmethod
|
||||
def _group_relations(cls, data: Any) -> Any:
|
||||
"""Normalize MusicBrainz 'relations' into type-keyed fields recursively.
|
||||
def _normalize_data(cls, data: Any) -> Any:
|
||||
"""Normalize MusicBrainz relation structures into easier-to-use shapes.
|
||||
|
||||
This helper rewrites payloads that use a generic 'relations' list into
|
||||
a structure that is easier to consume downstream. When a mapping
|
||||
contains 'relations', those entries are regrouped by their 'target-type'
|
||||
and stored under keys like '<target-type>-relations'. The original
|
||||
'relations' key is removed to avoid ambiguous access patterns.
|
||||
|
||||
The transformation is applied recursively so that nested objects and
|
||||
sequences are normalized consistently, while non-container values are
|
||||
left unchanged.
|
||||
This default handler is a no-op that returns non-container values
|
||||
unchanged. Specialized handlers for sequences and mappings perform the
|
||||
actual transformations described below.
|
||||
"""
|
||||
return data
|
||||
|
||||
@_group_relations.register(list)
|
||||
@_normalize_data.register(list)
|
||||
@classmethod
|
||||
def _(cls, data: list[Any]) -> list[Any]:
|
||||
return [cls._group_relations(i) for i in data]
|
||||
"""Apply normalization to each element of a sequence recursively.
|
||||
|
||||
@_group_relations.register(dict)
|
||||
Sequences received from the MusicBrainz API may contain nested mappings
|
||||
that require transformation. This handler maps the normalization step
|
||||
over the sequence and preserves order.
|
||||
"""
|
||||
return [cls._normalize_data(i) for i in data]
|
||||
|
||||
@_normalize_data.register(dict)
|
||||
@classmethod
|
||||
def _(cls, data: JSONDict) -> JSONDict:
|
||||
"""Transform mappings by regrouping relationships and normalizing keys.
|
||||
|
||||
When a mapping contains a generic 'relations' list, entries are grouped
|
||||
by their 'target-type' and placed under keys like
|
||||
'<target-type>_relations' with the 'target-type' field removed from each
|
||||
entry. All other mapping keys have hyphens converted to underscores and
|
||||
their values are normalized recursively to ensure a consistent shape
|
||||
throughout the payload.
|
||||
"""
|
||||
output_data = {}
|
||||
for k, v in list(data.items()):
|
||||
if k == "relations":
|
||||
get_target_type = operator.methodcaller("get", "target-type")
|
||||
|
|
@ -273,13 +283,12 @@ class MusicBrainzAPI(RequestHandler):
|
|||
{k: v for k, v in item.items() if k != "target-type"}
|
||||
for item in group
|
||||
]
|
||||
data[f"{target_type}-relations"] = cls._group_relations(
|
||||
relations
|
||||
output_data[f"{target_type}_relations"] = (
|
||||
cls._normalize_data(relations)
|
||||
)
|
||||
data.pop("relations")
|
||||
else:
|
||||
data[k] = cls._group_relations(v)
|
||||
return data
|
||||
output_data[k.replace("-", "_")] = cls._normalize_data(v)
|
||||
return output_data
|
||||
|
||||
|
||||
class MusicBrainzAPIMixin:
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ class MusicBrainzPseudoReleasePlugin(MusicBrainzPlugin):
|
|||
|
||||
return [
|
||||
pr_id
|
||||
for rel in data.get("release-relations", [])
|
||||
for rel in data.get("release_relations", [])
|
||||
if (pr_id := self._wanted_pseudo_release_id(album_id, rel))
|
||||
is not None
|
||||
]
|
||||
|
|
@ -176,7 +176,7 @@ class MusicBrainzPseudoReleasePlugin(MusicBrainzPlugin):
|
|||
def _has_desired_script(self, release: JSONDict) -> bool:
|
||||
if len(self._scripts) == 0:
|
||||
return False
|
||||
elif script := release.get("text-representation", {}).get("script"):
|
||||
elif script := release.get("text_representation", {}).get("script"):
|
||||
return script in self._scripts
|
||||
else:
|
||||
return False
|
||||
|
|
@ -216,9 +216,9 @@ class MusicBrainzPseudoReleasePlugin(MusicBrainzPlugin):
|
|||
if len(config["import"]["languages"].as_str_seq()) > 0:
|
||||
return
|
||||
|
||||
lang = raw_pseudo_release.get("text-representation", {}).get("language")
|
||||
artist_credits = raw_pseudo_release.get("release-group", {}).get(
|
||||
"artist-credit", []
|
||||
lang = raw_pseudo_release.get("text_representation", {}).get("language")
|
||||
artist_credits = raw_pseudo_release.get("release_group", {}).get(
|
||||
"artist_credit", []
|
||||
)
|
||||
aliases = [
|
||||
artist_credit.get("artist", {}).get("aliases", [])
|
||||
|
|
|
|||
|
|
@ -160,9 +160,9 @@ def _multi_artist_credit(
|
|||
|
||||
# Artist sort name.
|
||||
if alias:
|
||||
artist_sort_parts.append(alias["sort-name"])
|
||||
elif "sort-name" in el["artist"]:
|
||||
artist_sort_parts.append(el["artist"]["sort-name"])
|
||||
artist_sort_parts.append(alias["sort_name"])
|
||||
elif "sort_name" in el["artist"]:
|
||||
artist_sort_parts.append(el["artist"]["sort_name"])
|
||||
else:
|
||||
artist_sort_parts.append(cur_artist_name)
|
||||
|
||||
|
|
@ -245,10 +245,10 @@ def _preferred_release_event(
|
|||
].as_str_seq()
|
||||
|
||||
for country in preferred_countries:
|
||||
for event in release.get("release-events", {}):
|
||||
for event in release.get("release_events", {}):
|
||||
try:
|
||||
if area := event.get("area"):
|
||||
if country in area["iso-3166-1-codes"]:
|
||||
if country in area["iso_3166_1_codes"]:
|
||||
return country, event["date"]
|
||||
except KeyError:
|
||||
pass
|
||||
|
|
@ -381,28 +381,28 @@ class MusicBrainzPlugin(MusicBrainzAPIMixin, MetadataSourcePlugin):
|
|||
data_url=track_url(recording["id"]),
|
||||
)
|
||||
|
||||
if recording.get("artist-credit"):
|
||||
if recording.get("artist_credit"):
|
||||
# Get the artist names.
|
||||
(
|
||||
info.artist,
|
||||
info.artist_sort,
|
||||
info.artist_credit,
|
||||
) = _flatten_artist_credit(recording["artist-credit"])
|
||||
) = _flatten_artist_credit(recording["artist_credit"])
|
||||
|
||||
(
|
||||
info.artists,
|
||||
info.artists_sort,
|
||||
info.artists_credit,
|
||||
) = _multi_artist_credit(
|
||||
recording["artist-credit"], include_join_phrase=False
|
||||
recording["artist_credit"], include_join_phrase=False
|
||||
)
|
||||
|
||||
info.artists_ids = _artist_ids(recording["artist-credit"])
|
||||
info.artists_ids = _artist_ids(recording["artist_credit"])
|
||||
info.artist_id = info.artists_ids[0]
|
||||
|
||||
if recording.get("artist-relations"):
|
||||
if recording.get("artist_relations"):
|
||||
info.remixer = _get_related_artist_names(
|
||||
recording["artist-relations"], relation_type="remixer"
|
||||
recording["artist_relations"], relation_type="remixer"
|
||||
)
|
||||
|
||||
if recording.get("length"):
|
||||
|
|
@ -416,7 +416,7 @@ class MusicBrainzPlugin(MusicBrainzAPIMixin, MetadataSourcePlugin):
|
|||
lyricist = []
|
||||
composer = []
|
||||
composer_sort = []
|
||||
for work_relation in recording.get("work-relations", ()):
|
||||
for work_relation in recording.get("work_relations", ()):
|
||||
if work_relation["type"] != "performance":
|
||||
continue
|
||||
info.work = work_relation["work"]["title"]
|
||||
|
|
@ -425,7 +425,7 @@ class MusicBrainzPlugin(MusicBrainzAPIMixin, MetadataSourcePlugin):
|
|||
info.work_disambig = work_relation["work"]["disambiguation"]
|
||||
|
||||
for artist_relation in work_relation["work"].get(
|
||||
"artist-relations", ()
|
||||
"artist_relations", ()
|
||||
):
|
||||
if "type" in artist_relation:
|
||||
type = artist_relation["type"]
|
||||
|
|
@ -434,7 +434,7 @@ class MusicBrainzPlugin(MusicBrainzAPIMixin, MetadataSourcePlugin):
|
|||
elif type == "composer":
|
||||
composer.append(artist_relation["artist"]["name"])
|
||||
composer_sort.append(
|
||||
artist_relation["artist"]["sort-name"]
|
||||
artist_relation["artist"]["sort_name"]
|
||||
)
|
||||
if lyricist:
|
||||
info.lyricist = ", ".join(lyricist)
|
||||
|
|
@ -443,7 +443,7 @@ class MusicBrainzPlugin(MusicBrainzAPIMixin, MetadataSourcePlugin):
|
|||
info.composer_sort = ", ".join(composer_sort)
|
||||
|
||||
arranger = []
|
||||
for artist_relation in recording.get("artist-relations", ()):
|
||||
for artist_relation in recording.get("artist_relations", ()):
|
||||
if "type" in artist_relation:
|
||||
type = artist_relation["type"]
|
||||
if type == "arranger":
|
||||
|
|
@ -464,7 +464,7 @@ class MusicBrainzPlugin(MusicBrainzAPIMixin, MetadataSourcePlugin):
|
|||
"""
|
||||
# Get artist name using join phrases.
|
||||
artist_name, artist_sort_name, artist_credit_name = (
|
||||
_flatten_artist_credit(release["artist-credit"])
|
||||
_flatten_artist_credit(release["artist_credit"])
|
||||
)
|
||||
|
||||
(
|
||||
|
|
@ -472,7 +472,7 @@ class MusicBrainzPlugin(MusicBrainzAPIMixin, MetadataSourcePlugin):
|
|||
artists_sort_names,
|
||||
artists_credit_names,
|
||||
) = _multi_artist_credit(
|
||||
release["artist-credit"], include_join_phrase=False
|
||||
release["artist_credit"], include_join_phrase=False
|
||||
)
|
||||
|
||||
ntracks = sum(len(m["tracks"]) for m in release["media"])
|
||||
|
|
@ -508,10 +508,10 @@ class MusicBrainzPlugin(MusicBrainzAPIMixin, MetadataSourcePlugin):
|
|||
|
||||
all_tracks = medium["tracks"]
|
||||
if (
|
||||
"data-tracks" in medium
|
||||
"data_tracks" in medium
|
||||
and not config["match"]["ignore_data_tracks"]
|
||||
):
|
||||
all_tracks += medium["data-tracks"]
|
||||
all_tracks += medium["data_tracks"]
|
||||
track_count = len(all_tracks)
|
||||
|
||||
if "pregap" in medium:
|
||||
|
|
@ -548,30 +548,30 @@ class MusicBrainzPlugin(MusicBrainzAPIMixin, MetadataSourcePlugin):
|
|||
# Prefer track data, where present, over recording data.
|
||||
if track.get("title"):
|
||||
ti.title = track["title"]
|
||||
if track.get("artist-credit"):
|
||||
if track.get("artist_credit"):
|
||||
# Get the artist names.
|
||||
(
|
||||
ti.artist,
|
||||
ti.artist_sort,
|
||||
ti.artist_credit,
|
||||
) = _flatten_artist_credit(track["artist-credit"])
|
||||
) = _flatten_artist_credit(track["artist_credit"])
|
||||
|
||||
(
|
||||
ti.artists,
|
||||
ti.artists_sort,
|
||||
ti.artists_credit,
|
||||
) = _multi_artist_credit(
|
||||
track["artist-credit"], include_join_phrase=False
|
||||
track["artist_credit"], include_join_phrase=False
|
||||
)
|
||||
|
||||
ti.artists_ids = _artist_ids(track["artist-credit"])
|
||||
ti.artists_ids = _artist_ids(track["artist_credit"])
|
||||
ti.artist_id = ti.artists_ids[0]
|
||||
if track.get("length"):
|
||||
ti.length = int(track["length"]) / (1000.0)
|
||||
|
||||
track_infos.append(ti)
|
||||
|
||||
album_artist_ids = _artist_ids(release["artist-credit"])
|
||||
album_artist_ids = _artist_ids(release["artist_credit"])
|
||||
info = beets.autotag.hooks.AlbumInfo(
|
||||
album=release["title"],
|
||||
album_id=release["id"],
|
||||
|
|
@ -593,38 +593,38 @@ class MusicBrainzPlugin(MusicBrainzAPIMixin, MetadataSourcePlugin):
|
|||
if info.va:
|
||||
info.artist = config["va_name"].as_str()
|
||||
info.asin = release.get("asin")
|
||||
info.releasegroup_id = release["release-group"]["id"]
|
||||
info.releasegroup_id = release["release_group"]["id"]
|
||||
info.albumstatus = release.get("status")
|
||||
|
||||
if release["release-group"].get("title"):
|
||||
info.release_group_title = release["release-group"].get("title")
|
||||
if release["release_group"].get("title"):
|
||||
info.release_group_title = release["release_group"].get("title")
|
||||
|
||||
# Get the disambiguation strings at the release and release group level.
|
||||
if release["release-group"].get("disambiguation"):
|
||||
info.releasegroupdisambig = release["release-group"].get(
|
||||
if release["release_group"].get("disambiguation"):
|
||||
info.releasegroupdisambig = release["release_group"].get(
|
||||
"disambiguation"
|
||||
)
|
||||
if release.get("disambiguation"):
|
||||
info.albumdisambig = release.get("disambiguation")
|
||||
|
||||
if reltype := release["release-group"].get("primary-type"):
|
||||
if reltype := release["release_group"].get("primary_type"):
|
||||
info.albumtype = reltype.lower()
|
||||
|
||||
# Set the new-style "primary" and "secondary" release types.
|
||||
albumtypes = []
|
||||
if "primary-type" in release["release-group"]:
|
||||
rel_primarytype = release["release-group"]["primary-type"]
|
||||
if "primary_type" in release["release_group"]:
|
||||
rel_primarytype = release["release_group"]["primary_type"]
|
||||
if rel_primarytype:
|
||||
albumtypes.append(rel_primarytype.lower())
|
||||
if "secondary-types" in release["release-group"]:
|
||||
if release["release-group"]["secondary-types"]:
|
||||
for sec_type in release["release-group"]["secondary-types"]:
|
||||
if "secondary_types" in release["release_group"]:
|
||||
if release["release_group"]["secondary_types"]:
|
||||
for sec_type in release["release_group"]["secondary_types"]:
|
||||
albumtypes.append(sec_type.lower())
|
||||
info.albumtypes = albumtypes
|
||||
|
||||
# Release events.
|
||||
info.country, release_date = _preferred_release_event(release)
|
||||
release_group_date = release["release-group"].get("first-release-date")
|
||||
release_group_date = release["release_group"].get("first_release_date")
|
||||
if not release_date:
|
||||
# Fall back if release-specific date is not available.
|
||||
release_date = release_group_date
|
||||
|
|
@ -634,17 +634,17 @@ class MusicBrainzPlugin(MusicBrainzAPIMixin, MetadataSourcePlugin):
|
|||
_set_date_str(info, release_group_date, True)
|
||||
|
||||
# Label name.
|
||||
if release.get("label-info"):
|
||||
label_info = release["label-info"][0]
|
||||
if release.get("label_info"):
|
||||
label_info = release["label_info"][0]
|
||||
if label_info.get("label"):
|
||||
label = label_info["label"]["name"]
|
||||
if label != "[no label]":
|
||||
info.label = label
|
||||
info.catalognum = label_info.get("catalog-number")
|
||||
info.catalognum = label_info.get("catalog_number")
|
||||
|
||||
# Text representation data.
|
||||
if release.get("text-representation"):
|
||||
rep = release["text-representation"]
|
||||
if release.get("text_representation"):
|
||||
rep = release["text_representation"]
|
||||
info.script = rep.get("script")
|
||||
info.language = rep.get("language")
|
||||
|
||||
|
|
@ -659,7 +659,7 @@ class MusicBrainzPlugin(MusicBrainzAPIMixin, MetadataSourcePlugin):
|
|||
|
||||
if self.config["genres"]:
|
||||
sources = [
|
||||
release["release-group"].get(self.genres_field, []),
|
||||
release["release_group"].get(self.genres_field, []),
|
||||
release.get(self.genres_field, []),
|
||||
]
|
||||
genres: Counter[str] = Counter()
|
||||
|
|
@ -676,7 +676,7 @@ class MusicBrainzPlugin(MusicBrainzAPIMixin, MetadataSourcePlugin):
|
|||
wanted_sources = {
|
||||
site for site, wanted in external_ids.items() if wanted
|
||||
}
|
||||
if wanted_sources and (url_rels := release.get("url-relations")):
|
||||
if wanted_sources and (url_rels := release.get("url_relations")):
|
||||
urls = {}
|
||||
|
||||
for source, url in product(wanted_sources, url_rels):
|
||||
|
|
@ -800,7 +800,7 @@ class MusicBrainzPlugin(MusicBrainzAPIMixin, MetadataSourcePlugin):
|
|||
actual_res = None
|
||||
|
||||
if res.get("status") == "Pseudo-Release" and (
|
||||
relations := res.get("release-relations")
|
||||
relations := res.get("release_relations")
|
||||
):
|
||||
for rel in relations:
|
||||
if (
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ class TestMBPseudoPlugin(TestMBPseudoMixin):
|
|||
official_release: JSONDict,
|
||||
json_key: str,
|
||||
):
|
||||
del official_release["release-relations"][0][json_key]
|
||||
del official_release["release_relations"][0][json_key]
|
||||
|
||||
album_info = mbpseudo_plugin.album_info(official_release)
|
||||
assert not isinstance(album_info, PseudoAlbumInfo)
|
||||
|
|
@ -174,8 +174,8 @@ class TestMBPseudoPlugin(TestMBPseudoMixin):
|
|||
mbpseudo_plugin: MusicBrainzPseudoReleasePlugin,
|
||||
official_release: JSONDict,
|
||||
):
|
||||
official_release["release-relations"][0]["release"][
|
||||
"text-representation"
|
||||
official_release["release_relations"][0]["release"][
|
||||
"text_representation"
|
||||
]["script"] = "Null"
|
||||
|
||||
album_info = mbpseudo_plugin.album_info(official_release)
|
||||
|
|
|
|||
|
|
@ -51,18 +51,18 @@ class MBAlbumInfoTest(MusicBrainzTestCase):
|
|||
"id": "ALBUM ID",
|
||||
"asin": "ALBUM ASIN",
|
||||
"disambiguation": "R_DISAMBIGUATION",
|
||||
"release-group": {
|
||||
"primary-type": "Album",
|
||||
"first-release-date": date_str,
|
||||
"release_group": {
|
||||
"primary_type": "Album",
|
||||
"first_release_date": date_str,
|
||||
"id": "RELEASE GROUP ID",
|
||||
"disambiguation": "RG_DISAMBIGUATION",
|
||||
},
|
||||
"artist-credit": [
|
||||
"artist_credit": [
|
||||
{
|
||||
"artist": {
|
||||
"name": "ARTIST NAME",
|
||||
"id": "ARTIST ID",
|
||||
"sort-name": "ARTIST SORT NAME",
|
||||
"sort_name": "ARTIST SORT NAME",
|
||||
},
|
||||
"name": "ARTIST CREDIT",
|
||||
}
|
||||
|
|
@ -71,30 +71,30 @@ class MBAlbumInfoTest(MusicBrainzTestCase):
|
|||
"media": [],
|
||||
"genres": [{"count": 1, "name": "GENRE"}],
|
||||
"tags": [{"count": 1, "name": "TAG"}],
|
||||
"label-info": [
|
||||
"label_info": [
|
||||
{
|
||||
"catalog-number": "CATALOG NUMBER",
|
||||
"catalog_number": "CATALOG NUMBER",
|
||||
"label": {"name": "LABEL NAME"},
|
||||
}
|
||||
],
|
||||
"text-representation": {
|
||||
"text_representation": {
|
||||
"script": "SCRIPT",
|
||||
"language": "LANGUAGE",
|
||||
},
|
||||
"country": "COUNTRY",
|
||||
"status": "STATUS",
|
||||
"barcode": "BARCODE",
|
||||
"release-events": [{"area": None, "date": "2021-03-26"}],
|
||||
"release_events": [{"area": None, "date": "2021-03-26"}],
|
||||
}
|
||||
|
||||
if multi_artist_credit:
|
||||
release["artist-credit"][0]["joinphrase"] = " & "
|
||||
release["artist-credit"].append(
|
||||
release["artist_credit"][0]["joinphrase"] = " & "
|
||||
release["artist_credit"].append(
|
||||
{
|
||||
"artist": {
|
||||
"name": "ARTIST 2 NAME",
|
||||
"id": "ARTIST 2 ID",
|
||||
"sort-name": "ARTIST 2 SORT NAME",
|
||||
"sort_name": "ARTIST 2 SORT NAME",
|
||||
},
|
||||
"name": "ARTIST MULTI CREDIT",
|
||||
}
|
||||
|
|
@ -117,25 +117,25 @@ class MBAlbumInfoTest(MusicBrainzTestCase):
|
|||
if track_artist:
|
||||
# Similarly, track artists can differ from recording
|
||||
# artists.
|
||||
track["artist-credit"] = [
|
||||
track["artist_credit"] = [
|
||||
{
|
||||
"artist": {
|
||||
"name": "TRACK ARTIST NAME",
|
||||
"id": "TRACK ARTIST ID",
|
||||
"sort-name": "TRACK ARTIST SORT NAME",
|
||||
"sort_name": "TRACK ARTIST SORT NAME",
|
||||
},
|
||||
"name": "TRACK ARTIST CREDIT",
|
||||
}
|
||||
]
|
||||
|
||||
if multi_artist_credit:
|
||||
track["artist-credit"][0]["joinphrase"] = " & "
|
||||
track["artist-credit"].append(
|
||||
track["artist_credit"][0]["joinphrase"] = " & "
|
||||
track["artist_credit"].append(
|
||||
{
|
||||
"artist": {
|
||||
"name": "TRACK ARTIST 2 NAME",
|
||||
"id": "TRACK ARTIST 2 ID",
|
||||
"sort-name": "TRACK ARTIST 2 SORT NAME",
|
||||
"sort_name": "TRACK ARTIST 2 SORT NAME",
|
||||
},
|
||||
"name": "TRACK ARTIST 2 CREDIT",
|
||||
}
|
||||
|
|
@ -157,7 +157,7 @@ class MBAlbumInfoTest(MusicBrainzTestCase):
|
|||
{
|
||||
"position": "1",
|
||||
"tracks": track_list,
|
||||
"data-tracks": data_track_list,
|
||||
"data_tracks": data_track_list,
|
||||
"format": medium_format,
|
||||
"title": "MEDIUM TITLE",
|
||||
}
|
||||
|
|
@ -182,39 +182,39 @@ class MBAlbumInfoTest(MusicBrainzTestCase):
|
|||
if duration is not None:
|
||||
track["length"] = duration
|
||||
if artist:
|
||||
track["artist-credit"] = [
|
||||
track["artist_credit"] = [
|
||||
{
|
||||
"artist": {
|
||||
"name": "RECORDING ARTIST NAME",
|
||||
"id": "RECORDING ARTIST ID",
|
||||
"sort-name": "RECORDING ARTIST SORT NAME",
|
||||
"sort_name": "RECORDING ARTIST SORT NAME",
|
||||
},
|
||||
"name": "RECORDING ARTIST CREDIT",
|
||||
}
|
||||
]
|
||||
if multi_artist_credit:
|
||||
track["artist-credit"][0]["joinphrase"] = " & "
|
||||
track["artist-credit"].append(
|
||||
track["artist_credit"][0]["joinphrase"] = " & "
|
||||
track["artist_credit"].append(
|
||||
{
|
||||
"artist": {
|
||||
"name": "RECORDING ARTIST 2 NAME",
|
||||
"id": "RECORDING ARTIST 2 ID",
|
||||
"sort-name": "RECORDING ARTIST 2 SORT NAME",
|
||||
"sort_name": "RECORDING ARTIST 2 SORT NAME",
|
||||
},
|
||||
"name": "RECORDING ARTIST 2 CREDIT",
|
||||
}
|
||||
)
|
||||
if remixer:
|
||||
track["artist-relations"] = [
|
||||
track["artist_relations"] = [
|
||||
{
|
||||
"type": "remixer",
|
||||
"type-id": "RELATION TYPE ID",
|
||||
"type_id": "RELATION TYPE ID",
|
||||
"direction": "RECORDING RELATION DIRECTION",
|
||||
"artist": {
|
||||
"id": "RECORDING REMIXER ARTIST ID",
|
||||
"type": "RECORDING REMIXER ARTIST TYPE",
|
||||
"name": "RECORDING REMIXER ARTIST NAME",
|
||||
"sort-name": "RECORDING REMIXER ARTIST SORT NAME",
|
||||
"sort_name": "RECORDING REMIXER ARTIST SORT NAME",
|
||||
},
|
||||
}
|
||||
]
|
||||
|
|
@ -354,7 +354,7 @@ class MBAlbumInfoTest(MusicBrainzTestCase):
|
|||
|
||||
def test_detect_various_artists(self):
|
||||
release = self._make_release(None)
|
||||
release["artist-credit"][0]["artist"]["id"] = (
|
||||
release["artist_credit"][0]["artist"]["id"] = (
|
||||
musicbrainz.VARIOUS_ARTISTS_ID
|
||||
)
|
||||
d = self.mb.album_info(release)
|
||||
|
|
@ -429,7 +429,7 @@ class MBAlbumInfoTest(MusicBrainzTestCase):
|
|||
|
||||
def test_missing_language(self):
|
||||
release = self._make_release(None)
|
||||
del release["text-representation"]["language"]
|
||||
del release["text_representation"]["language"]
|
||||
d = self.mb.album_info(release)
|
||||
assert d.language is None
|
||||
|
||||
|
|
@ -697,7 +697,7 @@ class ArtistFlatteningTest(unittest.TestCase):
|
|||
return {
|
||||
"artist": {
|
||||
"name": f"NAME{suffix}",
|
||||
"sort-name": f"SORT{suffix}",
|
||||
"sort_name": f"SORT{suffix}",
|
||||
},
|
||||
"name": f"CREDIT{suffix}",
|
||||
}
|
||||
|
|
@ -706,7 +706,7 @@ class ArtistFlatteningTest(unittest.TestCase):
|
|||
alias = {
|
||||
"name": f"ALIAS{suffix}",
|
||||
"locale": locale,
|
||||
"sort-name": f"ALIASSORT{suffix}",
|
||||
"sort_name": f"ALIASSORT{suffix}",
|
||||
}
|
||||
if primary:
|
||||
alias["primary"] = "primary"
|
||||
|
|
@ -810,7 +810,7 @@ class MBLibraryTest(MusicBrainzTestCase):
|
|||
"position": 5,
|
||||
}
|
||||
],
|
||||
"artist-credit": [
|
||||
"artist_credit": [
|
||||
{
|
||||
"artist": {
|
||||
"name": "some-artist",
|
||||
|
|
@ -818,10 +818,10 @@ class MBLibraryTest(MusicBrainzTestCase):
|
|||
},
|
||||
}
|
||||
],
|
||||
"release-group": {
|
||||
"release_group": {
|
||||
"id": "another-id",
|
||||
},
|
||||
"release-relations": [
|
||||
"release_relations": [
|
||||
{
|
||||
"type": "transl-tracklisting",
|
||||
"direction": "backward",
|
||||
|
|
@ -852,7 +852,7 @@ class MBLibraryTest(MusicBrainzTestCase):
|
|||
"position": 5,
|
||||
}
|
||||
],
|
||||
"artist-credit": [
|
||||
"artist_credit": [
|
||||
{
|
||||
"artist": {
|
||||
"name": "some-artist",
|
||||
|
|
@ -860,7 +860,7 @@ class MBLibraryTest(MusicBrainzTestCase):
|
|||
},
|
||||
}
|
||||
],
|
||||
"release-group": {
|
||||
"release_group": {
|
||||
"id": "another-id",
|
||||
},
|
||||
"country": "COUNTRY",
|
||||
|
|
@ -897,7 +897,7 @@ class MBLibraryTest(MusicBrainzTestCase):
|
|||
"position": 5,
|
||||
}
|
||||
],
|
||||
"artist-credit": [
|
||||
"artist_credit": [
|
||||
{
|
||||
"artist": {
|
||||
"name": "some-artist",
|
||||
|
|
@ -905,7 +905,7 @@ class MBLibraryTest(MusicBrainzTestCase):
|
|||
},
|
||||
}
|
||||
],
|
||||
"release-group": {
|
||||
"release_group": {
|
||||
"id": "another-id",
|
||||
},
|
||||
}
|
||||
|
|
@ -941,7 +941,7 @@ class MBLibraryTest(MusicBrainzTestCase):
|
|||
"position": 5,
|
||||
}
|
||||
],
|
||||
"artist-credit": [
|
||||
"artist_credit": [
|
||||
{
|
||||
"artist": {
|
||||
"name": "some-artist",
|
||||
|
|
@ -949,7 +949,7 @@ class MBLibraryTest(MusicBrainzTestCase):
|
|||
},
|
||||
}
|
||||
],
|
||||
"release-group": {
|
||||
"release_group": {
|
||||
"id": "another-id",
|
||||
},
|
||||
}
|
||||
|
|
@ -985,7 +985,7 @@ class MBLibraryTest(MusicBrainzTestCase):
|
|||
"position": 5,
|
||||
}
|
||||
],
|
||||
"artist-credit": [
|
||||
"artist_credit": [
|
||||
{
|
||||
"artist": {
|
||||
"name": "some-artist",
|
||||
|
|
@ -993,10 +993,10 @@ class MBLibraryTest(MusicBrainzTestCase):
|
|||
},
|
||||
}
|
||||
],
|
||||
"release-group": {
|
||||
"release_group": {
|
||||
"id": "another-id",
|
||||
},
|
||||
"release-relations": [
|
||||
"release_relations": [
|
||||
{
|
||||
"type": "remaster",
|
||||
"direction": "backward",
|
||||
|
|
@ -1097,10 +1097,10 @@ class TestMusicBrainzPlugin(PluginMixin):
|
|||
"position": 5,
|
||||
}
|
||||
],
|
||||
"artist-credit": [
|
||||
"artist_credit": [
|
||||
{"artist": {"name": "some-artist", "id": "some-id"}}
|
||||
],
|
||||
"release-group": {"id": "another-id"},
|
||||
"release_group": {"id": "another-id"},
|
||||
},
|
||||
)
|
||||
candidates = list(mb.candidates([], "hello", "there", False))
|
||||
|
|
|
|||
|
|
@ -1,26 +1,44 @@
|
|||
from beetsplug._utils.musicbrainz import MusicBrainzAPI
|
||||
|
||||
|
||||
def test_group_relations():
|
||||
def test_normalize_data():
|
||||
raw_release = {
|
||||
"id": "r1",
|
||||
"relations": [
|
||||
{"target-type": "artist", "type": "vocal", "name": "A"},
|
||||
{"target-type": "url", "type": "streaming", "url": "http://s"},
|
||||
{"target-type": "url", "type": "purchase", "url": "http://p"},
|
||||
{
|
||||
"target-type": "artist",
|
||||
"type": "vocal",
|
||||
"type-id": "0fdbe3c6-7700-4a31-ae54-b53f06ae1cfa",
|
||||
"name": "A",
|
||||
},
|
||||
{
|
||||
"target-type": "url",
|
||||
"type": "streaming",
|
||||
"type-id": "b5f3058a-666c-406f-aafb-f9249fc7b122",
|
||||
"url": "http://s",
|
||||
},
|
||||
{
|
||||
"target-type": "url",
|
||||
"type": "purchase for download",
|
||||
"type-id": "92777657-504c-4acb-bd33-51a201bd57e1",
|
||||
"url": "http://p",
|
||||
},
|
||||
{
|
||||
"target-type": "work",
|
||||
"type": "performance",
|
||||
"type-id": "a3005666-a872-32c3-ad06-98af558e99b0",
|
||||
"work": {
|
||||
"relations": [
|
||||
{
|
||||
"artist": {"name": "幾田りら"},
|
||||
"target-type": "artist",
|
||||
"type": "composer",
|
||||
"type-id": "d59d99ea-23d4-4a80-b066-edca32ee158f",
|
||||
},
|
||||
{
|
||||
"target-type": "url",
|
||||
"type": "lyrics",
|
||||
"type-id": "e38e65aa-75e0-42ba-ace0-072aeb91a538",
|
||||
"url": {
|
||||
"resource": "https://utaten.com/lyric/tt24121002/"
|
||||
},
|
||||
|
|
@ -29,10 +47,12 @@ def test_group_relations():
|
|||
"artist": {"name": "幾田りら"},
|
||||
"target-type": "artist",
|
||||
"type": "lyricist",
|
||||
"type-id": "3e48faba-ec01-47fd-8e89-30e81161661c",
|
||||
},
|
||||
{
|
||||
"target-type": "url",
|
||||
"type": "lyrics",
|
||||
"type-id": "e38e65aa-75e0-42ba-ace0-072aeb91a538",
|
||||
"url": {
|
||||
"resource": "https://www.uta-net.com/song/366579/"
|
||||
},
|
||||
|
|
@ -45,30 +65,59 @@ def test_group_relations():
|
|||
],
|
||||
}
|
||||
|
||||
assert MusicBrainzAPI._group_relations(raw_release) == {
|
||||
assert MusicBrainzAPI._normalize_data(raw_release) == {
|
||||
"id": "r1",
|
||||
"artist-relations": [{"type": "vocal", "name": "A"}],
|
||||
"url-relations": [
|
||||
{"type": "streaming", "url": "http://s"},
|
||||
{"type": "purchase", "url": "http://p"},
|
||||
"artist_relations": [
|
||||
{
|
||||
"type": "vocal",
|
||||
"type_id": "0fdbe3c6-7700-4a31-ae54-b53f06ae1cfa",
|
||||
"name": "A",
|
||||
}
|
||||
],
|
||||
"work-relations": [
|
||||
"url_relations": [
|
||||
{
|
||||
"type": "streaming",
|
||||
"type_id": "b5f3058a-666c-406f-aafb-f9249fc7b122",
|
||||
"url": "http://s",
|
||||
},
|
||||
{
|
||||
"type": "purchase for download",
|
||||
"type_id": "92777657-504c-4acb-bd33-51a201bd57e1",
|
||||
"url": "http://p",
|
||||
},
|
||||
],
|
||||
"work_relations": [
|
||||
{
|
||||
"type": "performance",
|
||||
"type_id": "a3005666-a872-32c3-ad06-98af558e99b0",
|
||||
"work": {
|
||||
"artist-relations": [
|
||||
{"type": "composer", "artist": {"name": "幾田りら"}},
|
||||
{"type": "lyricist", "artist": {"name": "幾田りら"}},
|
||||
"artist_relations": [
|
||||
{
|
||||
"type": "composer",
|
||||
"type_id": "d59d99ea-23d4-4a80-b066-edca32ee158f",
|
||||
"artist": {
|
||||
"name": "幾田りら",
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "lyricist",
|
||||
"type_id": "3e48faba-ec01-47fd-8e89-30e81161661c",
|
||||
"artist": {
|
||||
"name": "幾田りら",
|
||||
},
|
||||
},
|
||||
],
|
||||
"url-relations": [
|
||||
"url_relations": [
|
||||
{
|
||||
"type": "lyrics",
|
||||
"type_id": "e38e65aa-75e0-42ba-ace0-072aeb91a538",
|
||||
"url": {
|
||||
"resource": "https://utaten.com/lyric/tt24121002/"
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "lyrics",
|
||||
"type_id": "e38e65aa-75e0-42ba-ace0-072aeb91a538",
|
||||
"url": {
|
||||
"resource": "https://www.uta-net.com/song/366579/"
|
||||
},
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"aliases": [],
|
||||
"artist-credit": [
|
||||
"artist_credit": [
|
||||
{
|
||||
"artist": {
|
||||
"aliases": [
|
||||
|
|
@ -11,9 +11,9 @@
|
|||
"locale": "en",
|
||||
"name": "Lilas Ikuta",
|
||||
"primary": true,
|
||||
"sort-name": "Ikuta, Lilas",
|
||||
"sort_name": "Ikuta, Lilas",
|
||||
"type": "Artist name",
|
||||
"type-id": "894afba6-2816-3c24-8072-eadb66bd04bc"
|
||||
"type_id": "894afba6-2816-3c24-8072-eadb66bd04bc"
|
||||
}
|
||||
],
|
||||
"country": "JP",
|
||||
|
|
@ -34,7 +34,7 @@
|
|||
],
|
||||
"id": "55e42264-ef27-49d8-93fd-29f930dc96e4",
|
||||
"name": "幾田りら",
|
||||
"sort-name": "Ikuta, Lilas",
|
||||
"sort_name": "Ikuta, Lilas",
|
||||
"tags": [
|
||||
{
|
||||
"count": 1,
|
||||
|
|
@ -46,7 +46,7 @@
|
|||
}
|
||||
],
|
||||
"type": "Person",
|
||||
"type-id": "b6e035f4-3ce9-331c-97df-83397230b0df"
|
||||
"type_id": "b6e035f4-3ce9-331c-97df-83397230b0df"
|
||||
},
|
||||
"joinphrase": "",
|
||||
"name": "Lilas Ikuta"
|
||||
|
|
@ -54,7 +54,7 @@
|
|||
],
|
||||
"asin": null,
|
||||
"barcode": null,
|
||||
"cover-art-archive": {
|
||||
"cover_art_archive": {
|
||||
"artwork": false,
|
||||
"back": false,
|
||||
"count": 0,
|
||||
|
|
@ -64,19 +64,19 @@
|
|||
"disambiguation": "",
|
||||
"genres": [],
|
||||
"id": "dc3ee2df-0bc1-49eb-b8c4-34473d279a43",
|
||||
"label-info": [],
|
||||
"label_info": [],
|
||||
"media": [
|
||||
{
|
||||
"format": "Digital Media",
|
||||
"format-id": "907a28d9-b3b2-3ef6-89a8-7b18d91d4794",
|
||||
"format_id": "907a28d9-b3b2-3ef6-89a8-7b18d91d4794",
|
||||
"id": "606faab7-60fa-3a8b-a40f-2c66150cce81",
|
||||
"position": 1,
|
||||
"title": "",
|
||||
"track-count": 1,
|
||||
"track-offset": 0,
|
||||
"track_count": 1,
|
||||
"track_offset": 0,
|
||||
"tracks": [
|
||||
{
|
||||
"artist-credit": [
|
||||
"artist_credit": [
|
||||
{
|
||||
"artist": {
|
||||
"aliases": [
|
||||
|
|
@ -87,18 +87,18 @@
|
|||
"locale": "en",
|
||||
"name": "Lilas Ikuta",
|
||||
"primary": true,
|
||||
"sort-name": "Ikuta, Lilas",
|
||||
"sort_name": "Ikuta, Lilas",
|
||||
"type": "Artist name",
|
||||
"type-id": "894afba6-2816-3c24-8072-eadb66bd04bc"
|
||||
"type_id": "894afba6-2816-3c24-8072-eadb66bd04bc"
|
||||
}
|
||||
],
|
||||
"country": "JP",
|
||||
"disambiguation": "",
|
||||
"id": "55e42264-ef27-49d8-93fd-29f930dc96e4",
|
||||
"name": "幾田りら",
|
||||
"sort-name": "Ikuta, Lilas",
|
||||
"sort_name": "Ikuta, Lilas",
|
||||
"type": "Person",
|
||||
"type-id": "b6e035f4-3ce9-331c-97df-83397230b0df"
|
||||
"type_id": "b6e035f4-3ce9-331c-97df-83397230b0df"
|
||||
},
|
||||
"joinphrase": "",
|
||||
"name": "Lilas Ikuta"
|
||||
|
|
@ -110,43 +110,43 @@
|
|||
"position": 1,
|
||||
"recording": {
|
||||
"aliases": [],
|
||||
"artist-credit": [
|
||||
"artist_credit": [
|
||||
{
|
||||
"artist": {
|
||||
"country": "JP",
|
||||
"disambiguation": "",
|
||||
"id": "55e42264-ef27-49d8-93fd-29f930dc96e4",
|
||||
"name": "幾田りら",
|
||||
"sort-name": "Ikuta, Lilas",
|
||||
"sort_name": "Ikuta, Lilas",
|
||||
"type": "Person",
|
||||
"type-id": "b6e035f4-3ce9-331c-97df-83397230b0df"
|
||||
"type_id": "b6e035f4-3ce9-331c-97df-83397230b0df"
|
||||
},
|
||||
"joinphrase": "",
|
||||
"name": "幾田りら"
|
||||
}
|
||||
],
|
||||
"artist-relations": [
|
||||
"artist_relations": [
|
||||
{
|
||||
"artist": {
|
||||
"country": "JP",
|
||||
"disambiguation": "Japanese composer/arranger/guitarist, agehasprings",
|
||||
"id": "f24241fb-4d89-4bf2-8336-3f2a7d2c0025",
|
||||
"name": "KOHD",
|
||||
"sort-name": "KOHD",
|
||||
"sort_name": "KOHD",
|
||||
"type": "Person",
|
||||
"type-id": "b6e035f4-3ce9-331c-97df-83397230b0df"
|
||||
"type_id": "b6e035f4-3ce9-331c-97df-83397230b0df"
|
||||
},
|
||||
"attribute-ids": {},
|
||||
"attribute-values": {},
|
||||
"attribute_ids": {},
|
||||
"attribute_values": {},
|
||||
"attributes": [],
|
||||
"begin": null,
|
||||
"direction": "backward",
|
||||
"end": null,
|
||||
"ended": false,
|
||||
"source-credit": "",
|
||||
"target-credit": "",
|
||||
"source_credit": "",
|
||||
"target_credit": "",
|
||||
"type": "arranger",
|
||||
"type-id": "22661fb8-cdb7-4f67-8385-b2a8be6c9f0d"
|
||||
"type_id": "22661fb8-cdb7-4f67-8385-b2a8be6c9f0d"
|
||||
},
|
||||
{
|
||||
"artist": {
|
||||
|
|
@ -154,21 +154,21 @@
|
|||
"disambiguation": "",
|
||||
"id": "55e42264-ef27-49d8-93fd-29f930dc96e4",
|
||||
"name": "幾田りら",
|
||||
"sort-name": "Ikuta, Lilas",
|
||||
"sort_name": "Ikuta, Lilas",
|
||||
"type": "Person",
|
||||
"type-id": "b6e035f4-3ce9-331c-97df-83397230b0df"
|
||||
"type_id": "b6e035f4-3ce9-331c-97df-83397230b0df"
|
||||
},
|
||||
"attribute-ids": {},
|
||||
"attribute-values": {},
|
||||
"attribute_ids": {},
|
||||
"attribute_values": {},
|
||||
"attributes": [],
|
||||
"begin": "2025",
|
||||
"direction": "backward",
|
||||
"end": "2025",
|
||||
"ended": true,
|
||||
"source-credit": "",
|
||||
"target-credit": "Lilas Ikuta",
|
||||
"source_credit": "",
|
||||
"target_credit": "Lilas Ikuta",
|
||||
"type": "phonographic copyright",
|
||||
"type-id": "7fd5fbc0-fbf4-4d04-be23-417d50a4dc30"
|
||||
"type_id": "7fd5fbc0-fbf4-4d04-be23-417d50a4dc30"
|
||||
},
|
||||
{
|
||||
"artist": {
|
||||
|
|
@ -176,21 +176,21 @@
|
|||
"disambiguation": "",
|
||||
"id": "1d27ab8a-a0df-47cf-b4cc-d2d7a0712a05",
|
||||
"name": "山本秀哉",
|
||||
"sort-name": "Yamamoto, Shuya",
|
||||
"sort_name": "Yamamoto, Shuya",
|
||||
"type": "Person",
|
||||
"type-id": "b6e035f4-3ce9-331c-97df-83397230b0df"
|
||||
"type_id": "b6e035f4-3ce9-331c-97df-83397230b0df"
|
||||
},
|
||||
"attribute-ids": {},
|
||||
"attribute-values": {},
|
||||
"attribute_ids": {},
|
||||
"attribute_values": {},
|
||||
"attributes": [],
|
||||
"begin": null,
|
||||
"direction": "backward",
|
||||
"end": null,
|
||||
"ended": false,
|
||||
"source-credit": "",
|
||||
"target-credit": "",
|
||||
"source_credit": "",
|
||||
"target_credit": "",
|
||||
"type": "producer",
|
||||
"type-id": "5c0ceac3-feb4-41f0-868d-dc06f6e27fc0"
|
||||
"type_id": "5c0ceac3-feb4-41f0-868d-dc06f6e27fc0"
|
||||
},
|
||||
{
|
||||
"artist": {
|
||||
|
|
@ -198,25 +198,25 @@
|
|||
"disambiguation": "",
|
||||
"id": "55e42264-ef27-49d8-93fd-29f930dc96e4",
|
||||
"name": "幾田りら",
|
||||
"sort-name": "Ikuta, Lilas",
|
||||
"sort_name": "Ikuta, Lilas",
|
||||
"type": "Person",
|
||||
"type-id": "b6e035f4-3ce9-331c-97df-83397230b0df"
|
||||
"type_id": "b6e035f4-3ce9-331c-97df-83397230b0df"
|
||||
},
|
||||
"attribute-ids": {},
|
||||
"attribute-values": {},
|
||||
"attribute_ids": {},
|
||||
"attribute_values": {},
|
||||
"attributes": [],
|
||||
"begin": null,
|
||||
"direction": "backward",
|
||||
"end": null,
|
||||
"ended": false,
|
||||
"source-credit": "",
|
||||
"target-credit": "",
|
||||
"source_credit": "",
|
||||
"target_credit": "",
|
||||
"type": "vocal",
|
||||
"type-id": "0fdbe3c6-7700-4a31-ae54-b53f06ae1cfa"
|
||||
"type_id": "0fdbe3c6-7700-4a31-ae54-b53f06ae1cfa"
|
||||
}
|
||||
],
|
||||
"disambiguation": "",
|
||||
"first-release-date": "2025-01-10",
|
||||
"first_release_date": "2025-01-10",
|
||||
"genres": [],
|
||||
"id": "781724c1-a039-41e6-bd9b-770c3b9d5b8e",
|
||||
"isrcs": [
|
||||
|
|
@ -225,53 +225,53 @@
|
|||
"length": 179546,
|
||||
"tags": [],
|
||||
"title": "百花繚乱",
|
||||
"url-relations": [
|
||||
"url_relations": [
|
||||
{
|
||||
"attribute-ids": {},
|
||||
"attribute-values": {},
|
||||
"attribute_ids": {},
|
||||
"attribute_values": {},
|
||||
"attributes": [],
|
||||
"begin": null,
|
||||
"direction": "forward",
|
||||
"end": null,
|
||||
"ended": false,
|
||||
"source-credit": "",
|
||||
"target-credit": "",
|
||||
"source_credit": "",
|
||||
"target_credit": "",
|
||||
"type": "free streaming",
|
||||
"type-id": "7e41ef12-a124-4324-afdb-fdbae687a89c",
|
||||
"type_id": "7e41ef12-a124-4324-afdb-fdbae687a89c",
|
||||
"url": {
|
||||
"id": "d076eaf9-5fde-4f6e-a946-cde16b67aa3b",
|
||||
"resource": "https://open.spotify.com/track/782PTXsbAWB70ySDZ5NHmP"
|
||||
}
|
||||
},
|
||||
{
|
||||
"attribute-ids": {},
|
||||
"attribute-values": {},
|
||||
"attribute_ids": {},
|
||||
"attribute_values": {},
|
||||
"attributes": [],
|
||||
"begin": null,
|
||||
"direction": "forward",
|
||||
"end": null,
|
||||
"ended": false,
|
||||
"source-credit": "",
|
||||
"target-credit": "",
|
||||
"source_credit": "",
|
||||
"target_credit": "",
|
||||
"type": "purchase for download",
|
||||
"type-id": "92777657-504c-4acb-bd33-51a201bd57e1",
|
||||
"type_id": "92777657-504c-4acb-bd33-51a201bd57e1",
|
||||
"url": {
|
||||
"id": "64879627-6eca-4755-98b5-b2234a8dbc61",
|
||||
"resource": "https://music.apple.com/jp/song/1857886416"
|
||||
}
|
||||
},
|
||||
{
|
||||
"attribute-ids": {},
|
||||
"attribute-values": {},
|
||||
"attribute_ids": {},
|
||||
"attribute_values": {},
|
||||
"attributes": [],
|
||||
"begin": null,
|
||||
"direction": "forward",
|
||||
"end": null,
|
||||
"ended": false,
|
||||
"source-credit": "",
|
||||
"target-credit": "",
|
||||
"source_credit": "",
|
||||
"target_credit": "",
|
||||
"type": "streaming",
|
||||
"type-id": "b5f3058a-666c-406f-aafb-f9249fc7b122",
|
||||
"type_id": "b5f3058a-666c-406f-aafb-f9249fc7b122",
|
||||
"url": {
|
||||
"id": "64879627-6eca-4755-98b5-b2234a8dbc61",
|
||||
"resource": "https://music.apple.com/jp/song/1857886416"
|
||||
|
|
@ -279,42 +279,42 @@
|
|||
}
|
||||
],
|
||||
"video": false,
|
||||
"work-relations": [
|
||||
"work_relations": [
|
||||
{
|
||||
"attribute-ids": {},
|
||||
"attribute-values": {},
|
||||
"attribute_ids": {},
|
||||
"attribute_values": {},
|
||||
"attributes": [],
|
||||
"begin": null,
|
||||
"direction": "forward",
|
||||
"end": null,
|
||||
"ended": false,
|
||||
"source-credit": "",
|
||||
"target-credit": "",
|
||||
"source_credit": "",
|
||||
"target_credit": "",
|
||||
"type": "performance",
|
||||
"type-id": "a3005666-a872-32c3-ad06-98af558e99b0",
|
||||
"type_id": "a3005666-a872-32c3-ad06-98af558e99b0",
|
||||
"work": {
|
||||
"artist-relations": [
|
||||
"artist_relations": [
|
||||
{
|
||||
"artist": {
|
||||
"country": "JP",
|
||||
"disambiguation": "",
|
||||
"id": "55e42264-ef27-49d8-93fd-29f930dc96e4",
|
||||
"name": "幾田りら",
|
||||
"sort-name": "Ikuta, Lilas",
|
||||
"sort_name": "Ikuta, Lilas",
|
||||
"type": "Person",
|
||||
"type-id": "b6e035f4-3ce9-331c-97df-83397230b0df"
|
||||
"type_id": "b6e035f4-3ce9-331c-97df-83397230b0df"
|
||||
},
|
||||
"attribute-ids": {},
|
||||
"attribute-values": {},
|
||||
"attribute_ids": {},
|
||||
"attribute_values": {},
|
||||
"attributes": [],
|
||||
"begin": null,
|
||||
"direction": "backward",
|
||||
"end": null,
|
||||
"ended": false,
|
||||
"source-credit": "",
|
||||
"target-credit": "",
|
||||
"source_credit": "",
|
||||
"target_credit": "",
|
||||
"type": "composer",
|
||||
"type-id": "d59d99ea-23d4-4a80-b066-edca32ee158f"
|
||||
"type_id": "d59d99ea-23d4-4a80-b066-edca32ee158f"
|
||||
},
|
||||
{
|
||||
"artist": {
|
||||
|
|
@ -322,21 +322,21 @@
|
|||
"disambiguation": "",
|
||||
"id": "55e42264-ef27-49d8-93fd-29f930dc96e4",
|
||||
"name": "幾田りら",
|
||||
"sort-name": "Ikuta, Lilas",
|
||||
"sort_name": "Ikuta, Lilas",
|
||||
"type": "Person",
|
||||
"type-id": "b6e035f4-3ce9-331c-97df-83397230b0df"
|
||||
"type_id": "b6e035f4-3ce9-331c-97df-83397230b0df"
|
||||
},
|
||||
"attribute-ids": {},
|
||||
"attribute-values": {},
|
||||
"attribute_ids": {},
|
||||
"attribute_values": {},
|
||||
"attributes": [],
|
||||
"begin": null,
|
||||
"direction": "backward",
|
||||
"end": null,
|
||||
"ended": false,
|
||||
"source-credit": "",
|
||||
"target-credit": "",
|
||||
"source_credit": "",
|
||||
"target_credit": "",
|
||||
"type": "lyricist",
|
||||
"type-id": "3e48faba-ec01-47fd-8e89-30e81161661c"
|
||||
"type_id": "3e48faba-ec01-47fd-8e89-30e81161661c"
|
||||
}
|
||||
],
|
||||
"attributes": [],
|
||||
|
|
@ -349,37 +349,37 @@
|
|||
],
|
||||
"title": "百花繚乱",
|
||||
"type": "Song",
|
||||
"type-id": "f061270a-2fd6-32f1-a641-f0f8676d14e6",
|
||||
"url-relations": [
|
||||
"type_id": "f061270a-2fd6-32f1-a641-f0f8676d14e6",
|
||||
"url_relations": [
|
||||
{
|
||||
"attribute-ids": {},
|
||||
"attribute-values": {},
|
||||
"attribute_ids": {},
|
||||
"attribute_values": {},
|
||||
"attributes": [],
|
||||
"begin": null,
|
||||
"direction": "backward",
|
||||
"end": null,
|
||||
"ended": false,
|
||||
"source-credit": "",
|
||||
"target-credit": "",
|
||||
"source_credit": "",
|
||||
"target_credit": "",
|
||||
"type": "lyrics",
|
||||
"type-id": "e38e65aa-75e0-42ba-ace0-072aeb91a538",
|
||||
"type_id": "e38e65aa-75e0-42ba-ace0-072aeb91a538",
|
||||
"url": {
|
||||
"id": "dfac3640-6b23-4991-a59c-7cb80e8eb950",
|
||||
"resource": "https://utaten.com/lyric/tt24121002/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"attribute-ids": {},
|
||||
"attribute-values": {},
|
||||
"attribute_ids": {},
|
||||
"attribute_values": {},
|
||||
"attributes": [],
|
||||
"begin": null,
|
||||
"direction": "backward",
|
||||
"end": null,
|
||||
"ended": false,
|
||||
"source-credit": "",
|
||||
"target-credit": "",
|
||||
"source_credit": "",
|
||||
"target_credit": "",
|
||||
"type": "lyrics",
|
||||
"type-id": "e38e65aa-75e0-42ba-ace0-072aeb91a538",
|
||||
"type_id": "e38e65aa-75e0-42ba-ace0-072aeb91a538",
|
||||
"url": {
|
||||
"id": "b1b5d5df-e79d-4cda-bb2a-8014e5505415",
|
||||
"resource": "https://www.uta-net.com/song/366579/"
|
||||
|
|
@ -396,11 +396,11 @@
|
|||
}
|
||||
],
|
||||
"packaging": null,
|
||||
"packaging-id": null,
|
||||
"packaging_id": null,
|
||||
"quality": "normal",
|
||||
"release-group": {
|
||||
"release_group": {
|
||||
"aliases": [],
|
||||
"artist-credit": [
|
||||
"artist_credit": [
|
||||
{
|
||||
"artist": {
|
||||
"aliases": [
|
||||
|
|
@ -411,54 +411,54 @@
|
|||
"locale": "en",
|
||||
"name": "Lilas Ikuta",
|
||||
"primary": true,
|
||||
"sort-name": "Ikuta, Lilas",
|
||||
"sort_name": "Ikuta, Lilas",
|
||||
"type": "Artist name",
|
||||
"type-id": "894afba6-2816-3c24-8072-eadb66bd04bc"
|
||||
"type_id": "894afba6-2816-3c24-8072-eadb66bd04bc"
|
||||
}
|
||||
],
|
||||
"country": "JP",
|
||||
"disambiguation": "",
|
||||
"id": "55e42264-ef27-49d8-93fd-29f930dc96e4",
|
||||
"name": "幾田りら",
|
||||
"sort-name": "Ikuta, Lilas",
|
||||
"sort_name": "Ikuta, Lilas",
|
||||
"type": "Person",
|
||||
"type-id": "b6e035f4-3ce9-331c-97df-83397230b0df"
|
||||
"type_id": "b6e035f4-3ce9-331c-97df-83397230b0df"
|
||||
},
|
||||
"joinphrase": "",
|
||||
"name": "幾田りら"
|
||||
}
|
||||
],
|
||||
"disambiguation": "",
|
||||
"first-release-date": "2025-01-10",
|
||||
"first_release_date": "2025-01-10",
|
||||
"genres": [],
|
||||
"id": "da0d6bbb-f44b-4fff-8739-9d72db0402a1",
|
||||
"primary-type": "Single",
|
||||
"primary-type-id": "d6038452-8ee0-3f68-affc-2de9a1ede0b9",
|
||||
"secondary-type-ids": [],
|
||||
"secondary-types": [],
|
||||
"primary_type": "Single",
|
||||
"primary_type_id": "d6038452-8ee0-3f68-affc-2de9a1ede0b9",
|
||||
"secondary_type_ids": [],
|
||||
"secondary_types": [],
|
||||
"tags": [],
|
||||
"title": "百花繚乱"
|
||||
},
|
||||
"release-relations": [
|
||||
"release_relations": [
|
||||
{
|
||||
"attribute-ids": {},
|
||||
"attribute-values": {},
|
||||
"attribute_ids": {},
|
||||
"attribute_values": {},
|
||||
"attributes": [],
|
||||
"begin": null,
|
||||
"direction": "backward",
|
||||
"end": null,
|
||||
"ended": false,
|
||||
"release": {
|
||||
"artist-credit": [
|
||||
"artist_credit": [
|
||||
{
|
||||
"artist": {
|
||||
"country": "JP",
|
||||
"disambiguation": "",
|
||||
"id": "55e42264-ef27-49d8-93fd-29f930dc96e4",
|
||||
"name": "幾田りら",
|
||||
"sort-name": "Ikuta, Lilas",
|
||||
"sort_name": "Ikuta, Lilas",
|
||||
"type": null,
|
||||
"type-id": null
|
||||
"type_id": null
|
||||
},
|
||||
"joinphrase": "",
|
||||
"name": "幾田りら"
|
||||
|
|
@ -471,43 +471,43 @@
|
|||
"id": "a5ce1d11-2e32-45a4-b37f-c1589d46b103",
|
||||
"media": [],
|
||||
"packaging": null,
|
||||
"packaging-id": null,
|
||||
"packaging_id": null,
|
||||
"quality": "normal",
|
||||
"release-events": [
|
||||
"release_events": [
|
||||
{
|
||||
"area": {
|
||||
"disambiguation": "",
|
||||
"id": "525d4e18-3d00-31b9-a58b-a146a916de8f",
|
||||
"iso-3166-1-codes": [
|
||||
"iso_3166_1_codes": [
|
||||
"XW"
|
||||
],
|
||||
"name": "[Worldwide]",
|
||||
"sort-name": "[Worldwide]",
|
||||
"sort_name": "[Worldwide]",
|
||||
"type": null,
|
||||
"type-id": null
|
||||
"type_id": null
|
||||
},
|
||||
"date": "2025-01-10"
|
||||
}
|
||||
],
|
||||
"release-group": null,
|
||||
"release_group": null,
|
||||
"status": null,
|
||||
"status-id": null,
|
||||
"text-representation": {
|
||||
"status_id": null,
|
||||
"text_representation": {
|
||||
"language": "jpn",
|
||||
"script": "Jpan"
|
||||
},
|
||||
"title": "百花繚乱"
|
||||
},
|
||||
"source-credit": "",
|
||||
"target-credit": "",
|
||||
"source_credit": "",
|
||||
"target_credit": "",
|
||||
"type": "transl-tracklisting",
|
||||
"type-id": "fc399d47-23a7-4c28-bfcf-0607a562b644"
|
||||
"type_id": "fc399d47-23a7-4c28-bfcf-0607a562b644"
|
||||
}
|
||||
],
|
||||
"status": "Pseudo-Release",
|
||||
"status-id": "41121bb9-3413-3818-8a9a-9742318349aa",
|
||||
"status_id": "41121bb9-3413-3818-8a9a-9742318349aa",
|
||||
"tags": [],
|
||||
"text-representation": {
|
||||
"text_representation": {
|
||||
"language": "eng",
|
||||
"script": "Latn"
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue