Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
77eecde
Normalize dashes to underscores in musicbrainz data fields
snejus Jan 13, 2026
cca2560
Type musicbrainz fully
snejus Jan 16, 2026
cfb6708
Simplify parsing recordings
snejus Jan 21, 2026
39a777e
Rename track -> recording in tests for clarity
snejus Jan 17, 2026
6a0e8e7
Fix recording parsing
snejus Jan 17, 2026
18be4a4
Ensure correct data shape in recordings tests
snejus Jan 21, 2026
a716e6f
Refactor track_info taking into account Recording data shape
snejus Jan 21, 2026
bdf344d
Simplify aliases parsing
snejus Jan 22, 2026
97ab5de
Simplify multi artist credit parsing
snejus Jan 22, 2026
b80ee56
Use ArtistCreditFactory for test artist_credit construction
snejus Jan 22, 2026
c480bf5
Remove _artist_ids
snejus Jan 22, 2026
41082fb
Use ArtistFactory
snejus Jan 22, 2026
626eed8
Refactor _get_related_artist_names
snejus Jan 22, 2026
881c829
Remove album_url, track_url
snejus Jan 22, 2026
96bfe12
Refactor _preferred_release_event
snejus Jan 23, 2026
8f10a80
Refactor date parsing
snejus Jan 23, 2026
d48b911
Use ReleaseGroupFactory
snejus Jan 27, 2026
feabcae
Refactor release group parsing
snejus Jan 23, 2026
bac5576
Refactor parsing genre
snejus Jan 24, 2026
522fcef
Refactor parsing label info
snejus Jan 25, 2026
d572f63
Refactor parsing external ids
snejus Jan 25, 2026
2e2bb71
Refactor parsing language and script
snejus Jan 25, 2026
daf8b6b
Enforce asin, disambiguation, fix status
snejus Jan 25, 2026
acfb5b7
Use RecordingFactory in tests
snejus Jan 25, 2026
ada63da
Refactor parsing medium track
snejus Jan 27, 2026
7c4f060
Refactor medium parsing
snejus Jan 27, 2026
01ea63e
Introduce ReleaseFactory
snejus Jan 27, 2026
2ee259c
Make use of ReleaseFactory
snejus Jan 31, 2026
f867887
Fix typing issues in tests
snejus Jan 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
465 changes: 437 additions & 28 deletions beetsplug/_utils/musicbrainz.py

Large diffs are not rendered by default.

24 changes: 15 additions & 9 deletions beetsplug/mbpseudo.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@
from beets.library import Item
from beetsplug._typing import JSONDict

from ._utils.musicbrainz import (
Release,
ReleaseRelation,
ReleaseRelationRelease,

Check failure on line 49 in beetsplug/mbpseudo.py

View workflow job for this annotation

GitHub Actions / Check linting

Ruff (F401)

beetsplug/mbpseudo.py:49:9: F401 `._utils.musicbrainz.ReleaseRelationRelease` imported but unused
)

_STATUS_PSEUDO = "Pseudo-Release"


Expand Down Expand Up @@ -133,7 +139,7 @@
yield album_info

@override
def album_info(self, release: JSONDict) -> AlbumInfo:
def album_info(self, release: Release) -> AlbumInfo:
official_release = super().album_info(release)

if release.get("status") == _STATUS_PSEUDO:
Expand All @@ -147,7 +153,7 @@

if self.config["custom_tags_only"].get(bool):
self._replace_artist_with_alias(
raw_pseudo_release, pseudo_release

Check failure on line 156 in beetsplug/mbpseudo.py

View workflow job for this annotation

GitHub Actions / Check types with mypy

Argument 1 to "_replace_artist_with_alias" of "MusicBrainzPseudoReleasePlugin" has incompatible type "Release"; expected "dict[str, Any]"
)
self._add_custom_tags(official_release, pseudo_release)
return official_release
Expand All @@ -161,30 +167,30 @@
else:
return official_release

def _intercept_mb_release(self, data: JSONDict) -> list[str]:
def _intercept_mb_release(self, data: Release) -> list[str]:
album_id = data["id"] if "id" in data else None
if self._has_desired_script(data) or not isinstance(album_id, str):
return []

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
]

def _has_desired_script(self, release: JSONDict) -> bool:
def _has_desired_script(self, release: Release) -> 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

def _wanted_pseudo_release_id(
self,
album_id: str,
relation: JSONDict,
relation: ReleaseRelation,
) -> str | None:
if (
len(self._scripts) == 0
Expand All @@ -195,7 +201,7 @@
return None

release = relation["release"]
if "id" in release and self._has_desired_script(release):

Check failure on line 204 in beetsplug/mbpseudo.py

View workflow job for this annotation

GitHub Actions / Check types with mypy

Argument 1 to "_has_desired_script" of "MusicBrainzPseudoReleasePlugin" has incompatible type "ReleaseRelationRelease"; expected "Release"
self._log.debug(
"Adding pseudo-release {0} for main release {1}",
release["id"],
Expand All @@ -216,9 +222,9 @@
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", [])
Expand Down
Loading
Loading