mirror of
https://github.com/beetbox/beets.git
synced 2025-12-07 09:04:33 +01:00
musicbrainz: update patches
This commit is contained in:
parent
bef0bcbaa6
commit
0980c82959
3 changed files with 88 additions and 172 deletions
|
|
@ -49,7 +49,7 @@ from mediafile import Image, MediaFile
|
||||||
|
|
||||||
import beets
|
import beets
|
||||||
import beets.plugins
|
import beets.plugins
|
||||||
from beets import autotag, importer, logging, util
|
from beets import importer, logging, util
|
||||||
from beets.autotag.hooks import AlbumInfo, TrackInfo
|
from beets.autotag.hooks import AlbumInfo, TrackInfo
|
||||||
from beets.importer import ImportSession
|
from beets.importer import ImportSession
|
||||||
from beets.library import Album, Item, Library
|
from beets.library import Album, Item, Library
|
||||||
|
|
@ -791,40 +791,37 @@ class AutotagStub:
|
||||||
length = 2
|
length = 2
|
||||||
|
|
||||||
def install(self):
|
def install(self):
|
||||||
self.mb_match_album = autotag.mb.match_album
|
self.patchers = [
|
||||||
self.mb_match_track = autotag.mb.match_track
|
patch("beets.plugins.album_for_id", lambda *_: None),
|
||||||
self.mb_album_for_id = autotag.mb.album_for_id
|
patch("beets.plugins.track_for_id", lambda *_: None),
|
||||||
self.mb_track_for_id = autotag.mb.track_for_id
|
patch("beets.plugins.candidates", self.candidates),
|
||||||
|
patch("beets.plugins.item_candidates", self.item_candidates),
|
||||||
autotag.mb.match_album = self.match_album
|
]
|
||||||
autotag.mb.match_track = self.match_track
|
for p in self.patchers:
|
||||||
autotag.mb.album_for_id = self.album_for_id
|
p.start()
|
||||||
autotag.mb.track_for_id = self.track_for_id
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def restore(self):
|
def restore(self):
|
||||||
autotag.mb.match_album = self.mb_match_album
|
for p in self.patchers:
|
||||||
autotag.mb.match_track = self.mb_match_track
|
p.stop()
|
||||||
autotag.mb.album_for_id = self.mb_album_for_id
|
|
||||||
autotag.mb.track_for_id = self.mb_track_for_id
|
|
||||||
|
|
||||||
def match_album(self, albumartist, album, tracks, extra_tags):
|
def candidates(self, items, artist, album, va_likely, extra_tags=None):
|
||||||
if self.matching == self.IDENT:
|
if self.matching == self.IDENT:
|
||||||
yield self._make_album_match(albumartist, album, tracks)
|
yield self._make_album_match(artist, album, len(items))
|
||||||
|
|
||||||
elif self.matching == self.GOOD:
|
elif self.matching == self.GOOD:
|
||||||
for i in range(self.length):
|
for i in range(self.length):
|
||||||
yield self._make_album_match(albumartist, album, tracks, i)
|
yield self._make_album_match(artist, album, len(items), i)
|
||||||
|
|
||||||
elif self.matching == self.BAD:
|
elif self.matching == self.BAD:
|
||||||
for i in range(self.length):
|
for i in range(self.length):
|
||||||
yield self._make_album_match(albumartist, album, tracks, i + 1)
|
yield self._make_album_match(artist, album, len(items), i + 1)
|
||||||
|
|
||||||
elif self.matching == self.MISSING:
|
elif self.matching == self.MISSING:
|
||||||
yield self._make_album_match(albumartist, album, tracks, missing=1)
|
yield self._make_album_match(artist, album, len(items), missing=1)
|
||||||
|
|
||||||
def match_track(self, artist, title):
|
def item_candidates(self, item, artist, title):
|
||||||
yield TrackInfo(
|
yield TrackInfo(
|
||||||
title=title.replace("Tag", "Applied"),
|
title=title.replace("Tag", "Applied"),
|
||||||
track_id="trackid",
|
track_id="trackid",
|
||||||
|
|
@ -834,12 +831,6 @@ class AutotagStub:
|
||||||
index=0,
|
index=0,
|
||||||
)
|
)
|
||||||
|
|
||||||
def album_for_id(self, mbid):
|
|
||||||
return None
|
|
||||||
|
|
||||||
def track_for_id(self, mbid):
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _make_track_match(self, artist, album, number):
|
def _make_track_match(self, artist, album, number):
|
||||||
return TrackInfo(
|
return TrackInfo(
|
||||||
title="Applied Track %d" % number,
|
title="Applied Track %d" % number,
|
||||||
|
|
|
||||||
|
|
@ -363,7 +363,7 @@ class ChangeRepresentation:
|
||||||
self.indent_header + f"Match ({dist_string(self.match.distance)}):"
|
self.indent_header + f"Match ({dist_string(self.match.distance)}):"
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.match.info.get("album"):
|
if isinstance(self.match.info, autotag.hooks.AlbumInfo):
|
||||||
# Matching an album - print that
|
# Matching an album - print that
|
||||||
artist_album_str = (
|
artist_album_str = (
|
||||||
f"{self.match.info.artist}" + f" - {self.match.info.album}"
|
f"{self.match.info.artist}" + f" - {self.match.info.album}"
|
||||||
|
|
|
||||||
|
|
@ -1061,26 +1061,22 @@ class InferAlbumDataTest(BeetsTestCase):
|
||||||
assert not self.items[0].comp
|
assert not self.items[0].comp
|
||||||
|
|
||||||
|
|
||||||
def match_album_mock(*args, **kwargs):
|
def album_candidates_mock(*args, **kwargs):
|
||||||
"""Create an AlbumInfo object for testing."""
|
"""Create an AlbumInfo object for testing."""
|
||||||
track_info = TrackInfo(
|
yield AlbumInfo(
|
||||||
title="new title",
|
|
||||||
track_id="trackid",
|
|
||||||
index=0,
|
|
||||||
)
|
|
||||||
album_info = AlbumInfo(
|
|
||||||
artist="artist",
|
artist="artist",
|
||||||
album="album",
|
album="album",
|
||||||
tracks=[track_info],
|
tracks=[TrackInfo(title="new title", track_id="trackid", index=0)],
|
||||||
album_id="albumid",
|
album_id="albumid",
|
||||||
artist_id="artistid",
|
artist_id="artistid",
|
||||||
flex="flex",
|
flex="flex",
|
||||||
)
|
)
|
||||||
return iter([album_info])
|
|
||||||
|
|
||||||
|
|
||||||
@patch("beets.autotag.mb.match_album", Mock(side_effect=match_album_mock))
|
@patch("beets.plugins.candidates", Mock(side_effect=album_candidates_mock))
|
||||||
class ImportDuplicateAlbumTest(ImportTestCase):
|
class ImportDuplicateAlbumTest(PluginMixin, ImportTestCase):
|
||||||
|
plugin = "musicbrainz"
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
|
|
||||||
|
|
@ -1186,20 +1182,16 @@ class ImportDuplicateAlbumTest(ImportTestCase):
|
||||||
return album
|
return album
|
||||||
|
|
||||||
|
|
||||||
def match_track_mock(*args, **kwargs):
|
def item_candidates_mock(*args, **kwargs):
|
||||||
return iter(
|
yield TrackInfo(
|
||||||
[
|
|
||||||
TrackInfo(
|
|
||||||
artist="artist",
|
artist="artist",
|
||||||
title="title",
|
title="title",
|
||||||
track_id="new trackid",
|
track_id="new trackid",
|
||||||
index=0,
|
index=0,
|
||||||
)
|
)
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@patch("beets.autotag.mb.match_track", Mock(side_effect=match_track_mock))
|
@patch("beets.plugins.item_candidates", Mock(side_effect=item_candidates_mock))
|
||||||
class ImportDuplicateSingletonTest(ImportTestCase):
|
class ImportDuplicateSingletonTest(ImportTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
|
|
@ -1633,7 +1625,7 @@ class ReimportTest(AutotagImportTestCase):
|
||||||
|
|
||||||
def test_reimported_album_not_preserves_flexattr(self):
|
def test_reimported_album_not_preserves_flexattr(self):
|
||||||
self._setup_session()
|
self._setup_session()
|
||||||
assert self._album().data_source == "original_source"
|
|
||||||
self.importer.run()
|
self.importer.run()
|
||||||
assert self._album().data_source == "match_source"
|
assert self._album().data_source == "match_source"
|
||||||
|
|
||||||
|
|
@ -1657,6 +1649,7 @@ class ImportPretendTest(AutotagImportTestCase):
|
||||||
assert len(self.lib.albums()) == 0
|
assert len(self.lib.albums()) == 0
|
||||||
|
|
||||||
return [line for line in logs if not line.startswith("Sending event:")]
|
return [line for line in logs if not line.startswith("Sending event:")]
|
||||||
|
assert self._album().data_source == "original_source"
|
||||||
|
|
||||||
def test_import_singletons_pretend(self):
|
def test_import_singletons_pretend(self):
|
||||||
assert self.__run(self.setup_singleton_importer(pretend=True)) == [
|
assert self.__run(self.setup_singleton_importer(pretend=True)) == [
|
||||||
|
|
@ -1681,112 +1674,64 @@ class ImportPretendTest(AutotagImportTestCase):
|
||||||
assert self.__run(importer) == [f"No files imported from {empty_path}"]
|
assert self.__run(importer) == [f"No files imported from {empty_path}"]
|
||||||
|
|
||||||
|
|
||||||
# Helpers for ImportMusicBrainzIdTest.
|
def mocked_get_album_by_id(id_):
|
||||||
|
"""Return album candidate for the given id.
|
||||||
|
|
||||||
|
The two albums differ only in the release title and artist name, so that
|
||||||
def mocked_get_release_by_id(
|
ID_RELEASE_0 is a closer match to the items created by
|
||||||
id_, includes=[], release_status=[], release_type=[]
|
ImportHelper.prepare_album_for_import().
|
||||||
):
|
"""
|
||||||
"""Mimic musicbrainzngs.get_release_by_id, accepting only a restricted list
|
|
||||||
of MB ids (ID_RELEASE_0, ID_RELEASE_1). The returned dict differs only in
|
|
||||||
the release title and artist name, so that ID_RELEASE_0 is a closer match
|
|
||||||
to the items created by ImportHelper.prepare_album_for_import()."""
|
|
||||||
# Map IDs to (release title, artist), so the distances are different.
|
# Map IDs to (release title, artist), so the distances are different.
|
||||||
releases = {
|
album, artist = {
|
||||||
ImportMusicBrainzIdTest.ID_RELEASE_0: ("VALID_RELEASE_0", "TAG ARTIST"),
|
ImportIdTest.ID_RELEASE_0: ("VALID_RELEASE_0", "TAG ARTIST"),
|
||||||
ImportMusicBrainzIdTest.ID_RELEASE_1: (
|
ImportIdTest.ID_RELEASE_1: ("VALID_RELEASE_1", "DISTANT_MATCH"),
|
||||||
"VALID_RELEASE_1",
|
}[id_]
|
||||||
"DISTANT_MATCH",
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return AlbumInfo(
|
||||||
"release": {
|
album_id=id_,
|
||||||
"title": releases[id_][0],
|
album=album,
|
||||||
"id": id_,
|
artist_id="some-id",
|
||||||
"medium-list": [
|
artist=artist,
|
||||||
{
|
albumstatus="Official",
|
||||||
"track-list": [
|
tracks=[
|
||||||
{
|
TrackInfo(
|
||||||
"id": "baz",
|
track_id="bar",
|
||||||
"recording": {
|
title="foo",
|
||||||
"title": "foo",
|
artist_id="some-id",
|
||||||
"id": "bar",
|
artist=artist,
|
||||||
"length": 59,
|
length=59,
|
||||||
},
|
index=9,
|
||||||
"position": 9,
|
track_allt="A2",
|
||||||
"number": "A2",
|
)
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"position": 5,
|
)
|
||||||
}
|
|
||||||
],
|
|
||||||
"artist-credit": [
|
|
||||||
{
|
|
||||||
"artist": {
|
|
||||||
"name": releases[id_][1],
|
|
||||||
"id": "some-id",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"release-group": {
|
|
||||||
"id": "another-id",
|
|
||||||
},
|
|
||||||
"status": "Official",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def mocked_get_recording_by_id(
|
def mocked_get_track_by_id(id_):
|
||||||
id_, includes=[], release_status=[], release_type=[]
|
"""Return track candidate for the given id.
|
||||||
):
|
|
||||||
"""Mimic musicbrainzngs.get_recording_by_id, accepting only a restricted
|
The two tracks differ only in the release title and artist name, so that
|
||||||
list of MB ids (ID_RECORDING_0, ID_RECORDING_1). The returned dict differs
|
ID_RELEASE_0 is a closer match to the items created by
|
||||||
only in the recording title and artist name, so that ID_RECORDING_0 is a
|
ImportHelper.prepare_album_for_import().
|
||||||
closer match to the items created by ImportHelper.prepare_album_for_import().
|
|
||||||
"""
|
"""
|
||||||
# Map IDs to (recording title, artist), so the distances are different.
|
# Map IDs to (recording title, artist), so the distances are different.
|
||||||
releases = {
|
title, artist = {
|
||||||
ImportMusicBrainzIdTest.ID_RECORDING_0: (
|
ImportIdTest.ID_RECORDING_0: ("VALID_RECORDING_0", "TAG ARTIST"),
|
||||||
"VALID_RECORDING_0",
|
ImportIdTest.ID_RECORDING_1: ("VALID_RECORDING_1", "DISTANT_MATCH"),
|
||||||
"TAG ARTIST",
|
}[id_]
|
||||||
),
|
|
||||||
ImportMusicBrainzIdTest.ID_RECORDING_1: (
|
|
||||||
"VALID_RECORDING_1",
|
|
||||||
"DISTANT_MATCH",
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return TrackInfo(
|
||||||
"recording": {
|
track_id=id_,
|
||||||
"title": releases[id_][0],
|
title=title,
|
||||||
"id": id_,
|
artist_id="some-id",
|
||||||
"length": 59,
|
artist=artist,
|
||||||
"artist-credit": [
|
length=59,
|
||||||
{
|
)
|
||||||
"artist": {
|
|
||||||
"name": releases[id_][1],
|
|
||||||
"id": "some-id",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@patch(
|
@patch("beets.plugins.track_for_id", Mock(side_effect=mocked_get_track_by_id))
|
||||||
"musicbrainzngs.get_recording_by_id",
|
@patch("beets.plugins.album_for_id", Mock(side_effect=mocked_get_album_by_id))
|
||||||
Mock(side_effect=mocked_get_recording_by_id),
|
class ImportIdTest(ImportTestCase):
|
||||||
)
|
|
||||||
@patch(
|
|
||||||
"musicbrainzngs.get_release_by_id",
|
|
||||||
Mock(side_effect=mocked_get_release_by_id),
|
|
||||||
)
|
|
||||||
class ImportMusicBrainzIdTest(ImportTestCase):
|
|
||||||
"""Test the --musicbrainzid argument."""
|
|
||||||
|
|
||||||
MB_RELEASE_PREFIX = "https://musicbrainz.org/release/"
|
|
||||||
MB_RECORDING_PREFIX = "https://musicbrainz.org/recording/"
|
|
||||||
ID_RELEASE_0 = "00000000-0000-0000-0000-000000000000"
|
ID_RELEASE_0 = "00000000-0000-0000-0000-000000000000"
|
||||||
ID_RELEASE_1 = "11111111-1111-1111-1111-111111111111"
|
ID_RELEASE_1 = "11111111-1111-1111-1111-111111111111"
|
||||||
ID_RECORDING_0 = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
|
ID_RECORDING_0 = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
|
||||||
|
|
@ -1797,21 +1742,14 @@ class ImportMusicBrainzIdTest(ImportTestCase):
|
||||||
self.prepare_album_for_import(1)
|
self.prepare_album_for_import(1)
|
||||||
|
|
||||||
def test_one_mbid_one_album(self):
|
def test_one_mbid_one_album(self):
|
||||||
self.setup_importer(
|
self.setup_importer(search_ids=[self.ID_RELEASE_0])
|
||||||
search_ids=[self.MB_RELEASE_PREFIX + self.ID_RELEASE_0]
|
|
||||||
)
|
|
||||||
|
|
||||||
self.importer.add_choice(importer.action.APPLY)
|
self.importer.add_choice(importer.action.APPLY)
|
||||||
self.importer.run()
|
self.importer.run()
|
||||||
assert self.lib.albums().get().album == "VALID_RELEASE_0"
|
assert self.lib.albums().get().album == "VALID_RELEASE_0"
|
||||||
|
|
||||||
def test_several_mbid_one_album(self):
|
def test_several_mbid_one_album(self):
|
||||||
self.setup_importer(
|
self.setup_importer(search_ids=[self.ID_RELEASE_0, self.ID_RELEASE_1])
|
||||||
search_ids=[
|
|
||||||
self.MB_RELEASE_PREFIX + self.ID_RELEASE_0,
|
|
||||||
self.MB_RELEASE_PREFIX + self.ID_RELEASE_1,
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
self.importer.add_choice(2) # Pick the 2nd best match (release 1).
|
self.importer.add_choice(2) # Pick the 2nd best match (release 1).
|
||||||
self.importer.add_choice(importer.action.APPLY)
|
self.importer.add_choice(importer.action.APPLY)
|
||||||
|
|
@ -1819,9 +1757,7 @@ class ImportMusicBrainzIdTest(ImportTestCase):
|
||||||
assert self.lib.albums().get().album == "VALID_RELEASE_1"
|
assert self.lib.albums().get().album == "VALID_RELEASE_1"
|
||||||
|
|
||||||
def test_one_mbid_one_singleton(self):
|
def test_one_mbid_one_singleton(self):
|
||||||
self.setup_singleton_importer(
|
self.setup_singleton_importer(search_ids=[self.ID_RECORDING_0])
|
||||||
search_ids=[self.MB_RECORDING_PREFIX + self.ID_RECORDING_0]
|
|
||||||
)
|
|
||||||
|
|
||||||
self.importer.add_choice(importer.action.APPLY)
|
self.importer.add_choice(importer.action.APPLY)
|
||||||
self.importer.run()
|
self.importer.run()
|
||||||
|
|
@ -1829,10 +1765,7 @@ class ImportMusicBrainzIdTest(ImportTestCase):
|
||||||
|
|
||||||
def test_several_mbid_one_singleton(self):
|
def test_several_mbid_one_singleton(self):
|
||||||
self.setup_singleton_importer(
|
self.setup_singleton_importer(
|
||||||
search_ids=[
|
search_ids=[self.ID_RECORDING_0, self.ID_RECORDING_1]
|
||||||
self.MB_RECORDING_PREFIX + self.ID_RECORDING_0,
|
|
||||||
self.MB_RECORDING_PREFIX + self.ID_RECORDING_1,
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self.importer.add_choice(2) # Pick the 2nd best match (recording 1).
|
self.importer.add_choice(2) # Pick the 2nd best match (recording 1).
|
||||||
|
|
@ -1845,11 +1778,7 @@ class ImportMusicBrainzIdTest(ImportTestCase):
|
||||||
task = importer.ImportTask(
|
task = importer.ImportTask(
|
||||||
paths=self.import_dir, toppath="top path", items=[_common.item()]
|
paths=self.import_dir, toppath="top path", items=[_common.item()]
|
||||||
)
|
)
|
||||||
task.search_ids = [
|
task.search_ids = [self.ID_RELEASE_0, self.ID_RELEASE_1]
|
||||||
self.MB_RELEASE_PREFIX + self.ID_RELEASE_0,
|
|
||||||
self.MB_RELEASE_PREFIX + self.ID_RELEASE_1,
|
|
||||||
"an invalid and discarded id",
|
|
||||||
]
|
|
||||||
|
|
||||||
task.lookup_candidates()
|
task.lookup_candidates()
|
||||||
assert {"VALID_RELEASE_0", "VALID_RELEASE_1"} == {
|
assert {"VALID_RELEASE_0", "VALID_RELEASE_1"} == {
|
||||||
|
|
@ -1861,11 +1790,7 @@ class ImportMusicBrainzIdTest(ImportTestCase):
|
||||||
task = importer.SingletonImportTask(
|
task = importer.SingletonImportTask(
|
||||||
toppath="top path", item=_common.item()
|
toppath="top path", item=_common.item()
|
||||||
)
|
)
|
||||||
task.search_ids = [
|
task.search_ids = [self.ID_RECORDING_0, self.ID_RECORDING_1]
|
||||||
self.MB_RECORDING_PREFIX + self.ID_RECORDING_0,
|
|
||||||
self.MB_RECORDING_PREFIX + self.ID_RECORDING_1,
|
|
||||||
"an invalid and discarded id",
|
|
||||||
]
|
|
||||||
|
|
||||||
task.lookup_candidates()
|
task.lookup_candidates()
|
||||||
assert {"VALID_RECORDING_0", "VALID_RECORDING_1"} == {
|
assert {"VALID_RECORDING_0", "VALID_RECORDING_1"} == {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue