mirror of
https://github.com/beetbox/beets.git
synced 2026-01-08 17:08:12 +01:00
Merge pull request #291 from jbaiter/discogs_albumid
Support for manually entered IDs in plugins
This commit is contained in:
commit
303cd9ba00
8 changed files with 84 additions and 52 deletions
|
|
@ -167,11 +167,17 @@ TrackMatch = namedtuple('TrackMatch', ['distance', 'info'])
|
|||
# Aggregation of sources.
|
||||
|
||||
def _album_for_id(album_id):
|
||||
"""Get an album corresponding to a MusicBrainz release ID."""
|
||||
"""Get a list of albums corresponding to a release ID."""
|
||||
candidates = []
|
||||
try:
|
||||
return mb.album_for_id(album_id)
|
||||
out = mb.album_for_id(album_id)
|
||||
except mb.MusicBrainzAPIError as exc:
|
||||
exc.log(log)
|
||||
if out:
|
||||
candidates.append(out)
|
||||
out = plugins.album_for_id(album_id)
|
||||
candidates.extend(x for x in out if x is not None)
|
||||
return candidates
|
||||
|
||||
def _track_for_id(track_id):
|
||||
"""Get an item for a recording MBID."""
|
||||
|
|
|
|||
|
|
@ -482,11 +482,7 @@ def tag_album(items, search_artist=None, search_album=None,
|
|||
candidates = {}
|
||||
|
||||
# Try to find album indicated by MusicBrainz IDs.
|
||||
if search_id:
|
||||
log.debug('Searching for album ID: ' + search_id)
|
||||
id_info = hooks._album_for_id(search_id)
|
||||
else:
|
||||
id_info = match_by_id(items)
|
||||
id_info = match_by_id(items)
|
||||
if id_info:
|
||||
_add_candidate(items, candidates, id_info)
|
||||
rec = _recommendation(candidates.values())
|
||||
|
|
@ -499,13 +495,6 @@ def tag_album(items, search_artist=None, search_album=None,
|
|||
log.debug('ID match.')
|
||||
return cur_artist, cur_album, candidates.values(), rec
|
||||
|
||||
# If searching by ID, don't continue to metadata search.
|
||||
if search_id is not None:
|
||||
if candidates:
|
||||
return cur_artist, cur_album, candidates.values(), rec
|
||||
else:
|
||||
return cur_artist, cur_album, [], recommendation.none
|
||||
|
||||
# Search terms.
|
||||
if not (search_artist and search_album):
|
||||
# No explicit search terms -- use current metadata.
|
||||
|
|
@ -519,8 +508,12 @@ def tag_album(items, search_artist=None, search_album=None,
|
|||
log.debug(u'Album might be VA: %s' % str(va_likely))
|
||||
|
||||
# Get the results from the data sources.
|
||||
search_cands = hooks._album_candidates(items, search_artist, search_album,
|
||||
va_likely)
|
||||
if search_id:
|
||||
log.debug('Searching for album ID: ' + search_id)
|
||||
search_cands = hooks._album_for_id(search_id)
|
||||
else:
|
||||
search_cands = hooks._album_candidates(items, search_artist,
|
||||
search_album, va_likely)
|
||||
log.debug(u'Evaluating %i candidates.' % len(search_cands))
|
||||
for info in search_cands:
|
||||
_add_candidate(items, candidates, info)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
"""
|
||||
import logging
|
||||
import musicbrainzngs
|
||||
import re
|
||||
import traceback
|
||||
|
||||
import beets.autotag.hooks
|
||||
|
|
@ -326,13 +327,19 @@ def album_for_id(albumid):
|
|||
object or None if the album is not found. May raise a
|
||||
MusicBrainzAPIError.
|
||||
"""
|
||||
# Find the first thing that looks like a UUID/MBID.
|
||||
match = re.search('[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}', albumid)
|
||||
if not match:
|
||||
log.error('Invalid MBID.')
|
||||
return None
|
||||
try:
|
||||
res = musicbrainzngs.get_release_by_id(albumid, RELEASE_INCLUDES)
|
||||
res = musicbrainzngs.get_release_by_id(match.group(),
|
||||
RELEASE_INCLUDES)
|
||||
except musicbrainzngs.ResponseError:
|
||||
log.debug('Album ID match failed.')
|
||||
return None
|
||||
except musicbrainzngs.MusicBrainzError as exc:
|
||||
raise MusicBrainzAPIError(exc, 'get release by ID', albumid,
|
||||
raise MusicBrainzAPIError(exc, 'get release by ID', match.group(),
|
||||
traceback.format_exc())
|
||||
return album_info(res['release'])
|
||||
|
||||
|
|
|
|||
|
|
@ -101,6 +101,12 @@ class BeetsPlugin(object):
|
|||
"""
|
||||
return {}
|
||||
|
||||
def album_for_id(self, album_id):
|
||||
"""Should return an AlbumInfo object or None if no matching release
|
||||
was found.
|
||||
"""
|
||||
return None
|
||||
|
||||
|
||||
listeners = None
|
||||
|
||||
|
|
@ -266,6 +272,17 @@ def item_candidates(item, artist, title):
|
|||
out.extend(plugin.item_candidates(item, artist, title))
|
||||
return out
|
||||
|
||||
def album_for_id(album_id):
|
||||
out = []
|
||||
for plugin in find_plugins():
|
||||
try:
|
||||
out.append(plugin.album_for_id(album_id))
|
||||
except Exception:
|
||||
log.warn('** error running album_for_id in plugin %s'
|
||||
% plugin.name)
|
||||
log.warn(traceback.format_exc())
|
||||
return out
|
||||
|
||||
def configure(config):
|
||||
"""Sends the configuration object to each plugin."""
|
||||
for plugin in find_plugins():
|
||||
|
|
|
|||
|
|
@ -589,20 +589,11 @@ def manual_search(singleton):
|
|||
return artist.strip(), name.strip()
|
||||
|
||||
def manual_id(singleton):
|
||||
"""Input a MusicBrainz ID, either for an album ("release") or a
|
||||
track ("recording"). If no valid ID is entered, returns None.
|
||||
"""Input an ID, either for an album ("release") or a track ("recording").
|
||||
"""
|
||||
prompt = 'Enter MusicBrainz %s ID:' % \
|
||||
prompt = 'Enter %s ID:' % \
|
||||
('recording' if singleton else 'release')
|
||||
entry = input_(prompt).strip()
|
||||
|
||||
# Find the first thing that looks like a UUID/MBID.
|
||||
match = re.search('[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}', entry)
|
||||
if match:
|
||||
return match.group()
|
||||
else:
|
||||
log.error('Invalid MBID.')
|
||||
return None
|
||||
return input_(prompt).strip()
|
||||
|
||||
class TerminalImportSession(importer.ImportSession):
|
||||
"""An import session that runs in a terminal.
|
||||
|
|
|
|||
|
|
@ -67,6 +67,30 @@ class DiscogsPlugin(BeetsPlugin):
|
|||
log.debug('Discogs API Error: %s (query: %s' % (e, query))
|
||||
return []
|
||||
|
||||
def album_for_id(self, album_id):
|
||||
"""Fetches an album by its Discogs ID and returns an AlbumInfo object
|
||||
or None if the album is not found.
|
||||
"""
|
||||
log.debug('Searching discogs for release %s' % str(album_id))
|
||||
# Discogs-IDs are simple integers. We only look for those at the end
|
||||
# of an input string as to avoid confusion with other metadata plugins.
|
||||
# An optional bracket can follow the integer, as this is how discogs
|
||||
# displays the release ID on its webpage.
|
||||
match = re.search(r'(^|\[*r|discogs\.com/.+/release/)(\d+)($|\])',
|
||||
album_id)
|
||||
if not match:
|
||||
return None
|
||||
result = Release(match.group(2))
|
||||
# Try to obtain title to verify that we indeed have a valid Release
|
||||
try:
|
||||
getattr(result, 'title')
|
||||
except DiscogsAPIError as e:
|
||||
if e.message != '404 Not Found':
|
||||
log.debug('Discogs API Error: %s (query: %s)'
|
||||
% (e, result._uri))
|
||||
return None
|
||||
return self.get_album_info(result)
|
||||
|
||||
def get_albums(self, query):
|
||||
"""Returns a list of AlbumInfo objects for a discogs search query.
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -277,6 +277,22 @@ class MBAlbumInfoTest(unittest.TestCase):
|
|||
self.assertEqual(track.artist_sort, 'TRACK ARTIST SORT NAME')
|
||||
self.assertEqual(track.artist_credit, 'TRACK ARTIST CREDIT')
|
||||
|
||||
def test_album_for_id_correct(self):
|
||||
id_string = "28e32c71-1450-463e-92bf-e0a46446fc11"
|
||||
out = mb.album_for_id(id_string)
|
||||
self.assertEqual(out.album_id, id_string)
|
||||
|
||||
def test_album_for_id_non_id_returns_none(self):
|
||||
id_string = "blah blah"
|
||||
out = mb.album_for_id(id_string)
|
||||
self.assertEqual(out, None)
|
||||
|
||||
def test_album_for_id_url_finds_id(self):
|
||||
id_string = "28e32c71-1450-463e-92bf-e0a46446fc11"
|
||||
id_url = "http://musicbrainz.org/entity/%s" % id_string
|
||||
out = mb.album_for_id(id_url)
|
||||
self.assertEqual(out.album_id, id_string)
|
||||
|
||||
class ArtistFlatteningTest(unittest.TestCase):
|
||||
def _credit_dict(self, suffix=''):
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -585,28 +585,6 @@ class ShowdiffTest(_common.TestCase):
|
|||
|
||||
self.assertEqual(complete_diff, partial_diff)
|
||||
|
||||
AN_ID = "28e32c71-1450-463e-92bf-e0a46446fc11"
|
||||
class ManualIDTest(_common.TestCase):
|
||||
def setUp(self):
|
||||
super(ManualIDTest, self).setUp()
|
||||
_common.log.setLevel(logging.CRITICAL)
|
||||
self.io.install()
|
||||
|
||||
def test_id_accepted(self):
|
||||
self.io.addinput(AN_ID)
|
||||
out = commands.manual_id(False)
|
||||
self.assertEqual(out, AN_ID)
|
||||
|
||||
def test_non_id_returns_none(self):
|
||||
self.io.addinput("blah blah")
|
||||
out = commands.manual_id(False)
|
||||
self.assertEqual(out, None)
|
||||
|
||||
def test_url_finds_id(self):
|
||||
self.io.addinput("http://musicbrainz.org/entity/%s?something" % AN_ID)
|
||||
out = commands.manual_id(False)
|
||||
self.assertEqual(out, AN_ID)
|
||||
|
||||
class ShowChangeTest(_common.TestCase):
|
||||
def setUp(self):
|
||||
super(ShowChangeTest, self).setUp()
|
||||
|
|
|
|||
Loading…
Reference in a new issue