def album_candidates(items, artist, album, va_likely): """Search for album matches. ``items`` is a list of Item objects that make up the album. ``artist`` and ``album`` are the respective names (strings), which may be derived from the item list or may be entered by the user. ``va_likely`` is a boolean indicating whether the album is likely to be a "various artists" release. """ # Base candidates if we have album and artist to match. if artist and album: try: for candidate in mb.match_album(artist, album, len(items)): yield candidate except mb.MusicBrainzAPIError as exc: exc.log(log) # Also add VA matches from MusicBrainz where appropriate. if va_likely and album: try: for candidate in mb.match_album(None, album, len(items)): yield candidate except mb.MusicBrainzAPIError as exc: exc.log(log) # Candidates from plugins. for candidate in plugins.candidates(items, artist, album, va_likely): yield candidate
def album_candidates(items, artist, album, va_likely): """Search for album matches. ``items`` is a list of Item objects that make up the album. ``artist`` and ``album`` are the respective names (strings), which may be derived from the item list or may be entered by the user. ``va_likely`` is a boolean indicating whether the album is likely to be a "various artists" release. """ out = [] # Base candidates if we have album and artist to match. if artist and album: try: out.extend(mb.match_album(artist, album, len(items))) except mb.MusicBrainzAPIError as exc: exc.log(log) # Also add VA matches from MusicBrainz where appropriate. if va_likely and album: try: out.extend(mb.match_album(None, album, len(items))) except mb.MusicBrainzAPIError as exc: exc.log(log) # Candidates from plugins. out.extend(plugins.candidates(items, artist, album, va_likely)) # Notify subscribed plugins about fetched album info for a in out: plugins.send('albuminfo_received', info=a) return out
def test_match_album(self): mbid = "d2a6f856-b553-40a0-ac54-a321e8e2da99" with mock.patch("musicbrainzngs.search_releases") as sp: sp.return_value = {"release-list": [{"id": mbid}]} with mock.patch("musicbrainzngs.get_release_by_id") as gp: gp.return_value = { "release": { "title": "hi", "id": mbid, "medium-list": [ { "track-list": [ {"recording": {"title": "foo", "id": "bar", "length": 42}, "position": 9} ], "position": 5, } ], "artist-credit": [{"artist": {"name": "some-artist", "id": "some-id"}}], "release-group": {"id": "another-id"}, } } ai = list(mb.match_album("hello", "there"))[0] sp.assert_called_with(artist="hello", release="there", limit=5) gp.assert_called_with(mbid, mock.ANY) self.assertEqual(ai.tracks[0].title, "foo") self.assertEqual(ai.album, "hi")
def candidates(self, items): last_artist, last_artist_id = get_cur_artist(items) # Search MusicBrainz based on Last.fm metadata. cands = list(mb.match_album(last_artist, '', len(items))) log.debug('Matched last candidates: %s' % ', '.join([cand.album for cand in cands])) return cands
def _album_candidates(items, artist, album, va_likely): """Search for album matches. ``items`` is a list of Item objects that make up the album. ``artist`` and ``album`` are the respective names (strings), which may be derived from the item list or may be entered by the user. ``va_likely`` is a boolean indicating whether the album is likely to be a "various artists" release. """ out = [] # Base candidates if we have album and artist to match. if artist and album: out.extend(mb.match_album(artist, album, len(items))) # Also add VA matches from MusicBrainz where appropriate. if va_likely and album: out.extend(mb.match_album(None, album, len(items))) # Candidates from plugins. out.extend(plugins.candidates(items)) return out
def test_match_album(self): mbid = 'd2a6f856-b553-40a0-ac54-a321e8e2da99' with mock.patch('musicbrainzngs.search_releases') as sp: sp.return_value = { 'release-list': [{ 'id': mbid, }], } with mock.patch('musicbrainzngs.get_release_by_id') as gp: gp.return_value = { 'release': { 'title': 'hi', 'id': mbid, 'medium-list': [{ 'track-list': [{ 'id': 'baz', 'recording': { 'title': 'foo', 'id': 'bar', 'length': 42, }, 'position': 9, 'number': 'A1', }], 'position': 5, }], 'artist-credit': [{ 'artist': { 'name': 'some-artist', 'id': 'some-id', }, }], 'release-group': { 'id': 'another-id', } } } ai = list(mb.match_album('hello', 'there'))[0] sp.assert_called_with(artist='hello', release='there', limit=5) gp.assert_called_with(mbid, mock.ANY) self.assertEqual(ai.tracks[0].title, 'foo') self.assertEqual(ai.album, 'hi')
def test_match_album(self): mbid = 'd2a6f856-b553-40a0-ac54-a321e8e2da99' with mock.patch('musicbrainzngs.search_releases') as sp: sp.return_value = { 'release-list': [{ 'id': mbid, }], } with mock.patch('musicbrainzngs.get_release_by_id') as gp: gp.return_value = { 'release': { 'title': 'hi', 'id': mbid, 'medium-list': [{ 'track-list': [{ 'recording': { 'title': 'foo', 'id': 'bar', 'length': 42, }, 'position': 9, 'number': 'A1', }], 'position': 5, }], 'artist-credit': [{ 'artist': { 'name': 'some-artist', 'id': 'some-id', }, }], 'release-group': { 'id': 'another-id', } } } ai = list(mb.match_album('hello', 'there'))[0] sp.assert_called_with(artist='hello', release='there', limit=5) gp.assert_called_with(mbid, mock.ANY) self.assertEqual(ai.tracks[0].title, 'foo') self.assertEqual(ai.album, 'hi')
def test_match_album_empty(self): with mock.patch('musicbrainzngs.search_releases') as p: ail = list(mb.match_album(' ', ' ')) self.assertFalse(p.called) self.assertEqual(ail, [])
def tag_album(items, search_artist=None, search_album=None): """Bundles together the functionality used to infer tags for a set of items comprised by an album. Returns everything relevant: - The current artist. - The current album. - A list of (distance, items, info) tuples where info is a dictionary containing the inferred tags and items is a reordered version of the input items list. The candidates are sorted by distance (i.e., best match first). - A recommendation, one of RECOMMEND_STRONG, RECOMMEND_MEDIUM, or RECOMMEND_NONE; indicating that the first candidate is very likely, it is somewhat likely, or no conclusion could be reached. If search_artist and search_album are provided, then they are used as search terms in place of the current metadata. May raise an AutotagError if existing metadata is insufficient. """ # Get current metadata. cur_artist, cur_album = current_metadata(items) log.debug('Tagging %s - %s' % (cur_artist, cur_album)) # The output result tuples (keyed by MB album ID). out_tuples = {} # Try to find album indicated by MusicBrainz IDs. id_info = match_by_id(items) if id_info: validate_candidate(items, out_tuples, id_info) if out_tuples: # If we have a very good MBID match, return immediately. # Otherwise, this match will compete against metadata-based # matches. rec = recommendation(out_tuples.values()) if rec == RECOMMEND_STRONG: log.debug('ID match.') return cur_artist, cur_album, out_tuples.values(), rec # Search terms. if not (search_artist and search_album): # No explicit search terms -- use current metadata. search_artist, search_album = cur_artist, cur_album log.debug('Search terms: %s - %s' % (search_artist, search_album)) # Get candidate metadata from search. if search_artist and search_album: candidates = mb.match_album(search_artist, search_album, len(items), MAX_CANDIDATES) candidates = list(candidates) else: candidates = [] # Get candidates from plugins. candidates.extend(plugins.candidates(items)) # Get the distance to each candidate. log.debug('Evaluating %i candidates.' % len(candidates)) for info in candidates: validate_candidate(items, out_tuples, info) # Sort by distance. out_tuples = out_tuples.values() out_tuples.sort() rec = recommendation(out_tuples) return cur_artist, cur_album, out_tuples, rec
def tag_album(items, config, search_artist=None, search_album=None): """Bundles together the functionality used to infer tags for a set of items comprised by an album. Returns everything relevant: - The current artist. - The current album. - A list of (distance, items, info) tuples where info is a dictionary containing the inferred tags and items is a reordered version of the input items list. The candidates are sorted by distance (i.e., best match first). - A recommendation, one of RECOMMEND_STRONG, RECOMMEND_MEDIUM, or RECOMMEND_NONE; indicating that the first candidate is very likely, it is somewhat likely, or no conclusion could be reached. If search_artist and search_album are provided, then they are used as search terms in place of the current metadata. May raise an AutotagError if existing metadata is insufficient. """ # Get current metadata. cur_artist, cur_album, artist_consensus = current_metadata(items) log.debug('Tagging %s - %s' % (cur_artist, cur_album)) # The output result tuples (keyed by MB album ID). out_tuples = {} # Try to find album indicated by MusicBrainz IDs. id_info = match_by_id(items) if id_info: validate_candidate(items, out_tuples, id_info) if out_tuples: # If we have a very good MBID match, return immediately. # Otherwise, this match will compete against metadata-based # matches. rec = recommendation(out_tuples.values()) if rec == RECOMMEND_STRONG and not config.interactive_autotag: log.debug('ID match.') return cur_artist, cur_album, out_tuples.values(), rec # Search terms. if not (search_artist and search_album): # No explicit search terms -- use current metadata. search_artist, search_album = cur_artist, cur_album log.debug(u'Search terms: %s - %s' % (search_artist, search_album)) # Get candidate metadata from search. if search_artist and search_album: candidates = mb.match_album(search_artist, search_album, len(items), MAX_CANDIDATES) candidates = list(candidates) else: candidates = [] # Possibly add "various artists" search. if search_album and ((not artist_consensus) or \ (search_artist.lower() in VA_ARTISTS) or \ any(item.comp for item in items)): log.debug(u'Possibly Various Artists; adding matches.') candidates.extend( mb.match_album(None, search_album, len(items), MAX_CANDIDATES)) # Get candidates from plugins. candidates.extend(plugins.candidates(items)) # Get the distance to each candidate. log.debug(u'Evaluating %i candidates.' % len(candidates)) for info in candidates: validate_candidate(items, out_tuples, info) # Sort by distance. out_tuples = out_tuples.values() out_tuples.sort() rec = recommendation(out_tuples) return cur_artist, cur_album, out_tuples, rec