diff --git a/NEWS b/NEWS index bf00aed0b..afc3f3016 100644 --- a/NEWS +++ b/NEWS @@ -4,7 +4,10 @@ format can be used to customize where these tracks are stored. While importing, you can choose the "as Tracks" (T) option to add singletons to your library. The query "singleton:true" matches only - singleton tracks; "singleton:false" matches only album tracks. + singleton tracks; "singleton:false" matches only album tracks. The + importer and autotagger can also import singleton tracks, either + using the -s command-line flag or interactively. The lastid plugin + has been extended to support matching individual items as well. * The importer/autotagger system has been heavily refactored. If anything breaks as a result, please let me know. * Support for album art embedded in files. The "embedcoverart" plugin diff --git a/beets/autotag/__init__.py b/beets/autotag/__init__.py index cab804370..3a82dca93 100644 --- a/beets/autotag/__init__.py +++ b/beets/autotag/__init__.py @@ -537,12 +537,13 @@ def tag_item(item): # Candidate metadata from search. for track_info in mb.match_track(item.artist, item.title): - dist = track_distance(item, track_info) + dist = track_distance(item, track_info, incl_artist=True) candidates.append((dist, track_info)) # Add candidates from plugins. - #TODO - # candidates.extend(plugins.item_candidates(item)) + for track_info in plugins.item_candidates(item): + dist = track_distance(item, track_info, incl_artist=True) + candidates.append((dist, track_info)) # Sort by distance and return with recommendation. log.debug('Found %i candidates.' % len(candidates)) diff --git a/beets/plugins.py b/beets/plugins.py index 135676dce..b7982df8c 100755 --- a/beets/plugins.py +++ b/beets/plugins.py @@ -57,6 +57,12 @@ class BeetsPlugin(object): """ return () + def item_candidates(self, item): + """Should return a sequence of MusicBrainz track info + dictionaries that match the item provided. + """ + return () + def configure(self, config): """This method is called with the ConfigParser object after the CLI starts up. @@ -173,6 +179,14 @@ def candidates(items): out.extend(plugin.candidates(items)) return out +def item_candidates(item): + """Gets MusicBrainz candidates for an item from the plugins. + """ + out = [] + for plugin in find_plugins(): + out.extend(plugin.item_candidates(item)) + return out + def configure(config): """Sends the configuration object to each plugin.""" for plugin in find_plugins(): diff --git a/beetsplug/lastid.py b/beetsplug/lastid.py index 18e98980f..c5e77d490 100644 --- a/beetsplug/lastid.py +++ b/beetsplug/lastid.py @@ -114,7 +114,8 @@ class LastIdPlugin(BeetsPlugin): dist_max += autotag.ARTIST_WEIGHT log.debug('Last artist (%s/%s) distance: %f' % - (last_artist, info['artist'], dist/dist_max if dist_max > 0.0 else 0.0)) + (last_artist, info['artist'], + dist/dist_max if dist_max > 0.0 else 0.0)) #fixme: artist MBID currently ignored (as in vanilla tagger) return dist, dist_max @@ -137,5 +138,31 @@ class LastIdPlugin(BeetsPlugin): log.debug('Matched last candidates: %s' % ', '.join([cand['album'] for cand in cands])) - + return cands + + def item_candidates(self, item): + last_data = match(item.path) + if not last_data: + return () + + # Have a MusicBrainz track ID? + if last_data['track_mbid']: + log.debug('Have a track ID from last.fm: %s' % + last_data['track_mbid']) + id_track = mb.track_for_id(last_data['track_mbid']) + if id_track: + log.debug('Matched by track ID.') + return (id_track,) + + # Do a full search. + criteria = { + 'artist': last_data['artist'], + 'title': last_data['title'], + } + if last_data['artist_mbid']: + criteria['artistid'] = last_data['artist_mbid'] + cands = list(mb.find_tracks(criteria)) + + log.debug('Matched last track candidates: %s' % + ', '.join([cand['title'] for cand in cands])) return cands