diff --git a/beets/autotag/__init__.py b/beets/autotag/__init__.py index 89b00194e..279288d11 100644 --- a/beets/autotag/__init__.py +++ b/beets/autotag/__init__.py @@ -25,8 +25,7 @@ from beets.util import sorted_walk, ancestry, displayable_path from .hooks import AlbumInfo, TrackInfo, AlbumMatch, TrackMatch from .match import AutotagError from .match import tag_item, tag_album -from .match import \ - RECOMMEND_STRONG, RECOMMEND_MEDIUM, RECOMMEND_LOW, RECOMMEND_NONE +from .match import recommendation # Global logger. log = logging.getLogger('beets') diff --git a/beets/autotag/match.py b/beets/autotag/match.py index aacb64d10..68fb81665 100644 --- a/beets/autotag/match.py +++ b/beets/autotag/match.py @@ -25,6 +25,7 @@ from unidecode import unidecode from beets import plugins from beets import config from beets.util import levenshtein, plurality +from beets.util.enumeration import enum from beets.autotag import hooks # Distance parameters. @@ -71,11 +72,8 @@ SD_REPLACE = [ (r'&', 'and'), ] -# Recommendation constants. -RECOMMEND_STRONG = 'RECOMMEND_STRONG' -RECOMMEND_MEDIUM = 'RECOMMEND_MEDIUM' -RECOMMEND_LOW = 'RECOMMEND_LOW' -RECOMMEND_NONE = 'RECOMMEND_NONE' +# Recommendation enumeration. +recommendation = enum('none', 'low', 'medium', 'strong', name='recommendation') # Artist signals that indicate "various artists". These are used at the # album level to determine whether a given release is likely a VA @@ -322,37 +320,36 @@ def match_by_id(items): log.debug('No album ID consensus.') return None -def recommendation(results): +def _recommendation(results): """Given a sorted list of AlbumMatch or TrackMatch objects, return a - recommendation flag (RECOMMEND_STRONG, RECOMMEND_MEDIUM, - RECOMMEND_NONE) based on the results' distances. + recommendation based on the results' distances. """ if not results: # No candidates: no recommendation. - rec = RECOMMEND_NONE + rec = recommendation.none else: min_dist = results[0].distance if min_dist < config['match']['strong_rec_thresh'].as_number(): # Partial matches get downgraded to "medium". if isinstance(results[0], hooks.AlbumMatch) and \ (results[0].extra_items or results[0].extra_tracks): - rec = RECOMMEND_MEDIUM + rec = recommendation.medium else: # Strong recommendation level. - rec = RECOMMEND_STRONG + rec = recommendation.strong elif min_dist <= config['match']['medium_rec_thresh'].as_number(): # Medium recommendation level. - rec = RECOMMEND_MEDIUM + rec = recommendation.medium elif len(results) == 1: # Only a single candidate. - rec = RECOMMEND_LOW + rec = recommendation.low elif results[1].distance - min_dist >= \ config['match']['rec_gap_thresh'].as_number(): # Gap between first two candidates is large. - rec = RECOMMEND_LOW + rec = recommendation.low else: # No conclusion. - rec = RECOMMEND_NONE + rec = recommendation.none return rec def _add_candidate(items, results, info): @@ -386,10 +383,7 @@ def tag_album(items, search_artist=None, search_album=None, - The current album. - A list of AlbumMatch objects. 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. + - A recommendation. If search_artist and search_album or search_id are provided, then they are used as search terms in place of the current metadata. May raise an AutotagError if existing metadata is insufficient. @@ -410,13 +404,13 @@ def tag_album(items, search_artist=None, search_album=None, id_info = match_by_id(items) if id_info: _add_candidate(items, candidates, id_info) - rec = recommendation(candidates.values()) + rec = _recommendation(candidates.values()) log.debug('Album ID match recommendation is ' + str(rec)) if candidates and not config['import']['timid']: # If we have a very good MBID match, return immediately. # Otherwise, this match will compete against metadata-based # matches. - if rec == RECOMMEND_STRONG: + if rec == recommendation.strong: log.debug('ID match.') return cur_artist, cur_album, candidates.values(), rec @@ -425,7 +419,7 @@ def tag_album(items, search_artist=None, search_album=None, if candidates: return cur_artist, cur_album, candidates.values(), rec else: - return cur_artist, cur_album, [], RECOMMEND_NONE + return cur_artist, cur_album, [], recommendation.none # Search terms. if not (search_artist and search_album): @@ -448,7 +442,7 @@ def tag_album(items, search_artist=None, search_album=None, # Sort and get the recommendation. candidates = sorted(candidates.itervalues()) - rec = recommendation(candidates) + rec = _recommendation(candidates) return cur_artist, cur_album, candidates, rec def tag_item(item, search_artist=None, search_title=None, @@ -473,8 +467,8 @@ def tag_item(item, search_artist=None, search_title=None, candidates[track_info.track_id] = \ hooks.TrackMatch(dist, track_info) # If this is a good match, then don't keep searching. - rec = recommendation(candidates.values()) - if rec == RECOMMEND_STRONG and not config['import']['timid']: + rec = _recommendation(candidates.values()) + if rec == recommendation.strong and not config['import']['timid']: log.debug('Track ID match.') return candidates.values(), rec @@ -483,7 +477,7 @@ def tag_item(item, search_artist=None, search_title=None, if candidates: return candidates.values(), rec else: - return [], RECOMMEND_NONE + return [], recommendation.none # Search terms. if not (search_artist and search_title): @@ -498,5 +492,5 @@ def tag_item(item, search_artist=None, search_title=None, # Sort by distance and return with recommendation. log.debug('Found %i candidates.' % len(candidates)) candidates = sorted(candidates.itervalues()) - rec = recommendation(candidates) + rec = _recommendation(candidates) return candidates, rec diff --git a/beets/ui/commands.py b/beets/ui/commands.py index e55c09f8c..e4f04cc63 100644 --- a/beets/ui/commands.py +++ b/beets/ui/commands.py @@ -28,6 +28,7 @@ import beets from beets import ui from beets.ui import print_, input_, decargs from beets import autotag +from beets.autotag import recommendation from beets import plugins from beets import importer from beets.util import syspath, normpath, ancestry, displayable_path @@ -277,7 +278,7 @@ def _summary_judment(rec): made. """ if config['import']['quiet']: - if rec == autotag.RECOMMEND_STRONG: + if rec == recommendation.strong: return importer.action.APPLY else: action = config['import']['quiet_fallback'].as_choice({ @@ -285,7 +286,7 @@ def _summary_judment(rec): 'asis': importer.action.ASIS, }) - elif rec == autotag.RECOMMEND_NONE: + elif rec == recommendation.none: action = config['import']['none_rec_action'].as_choice({ 'skip': importer.action.SKIP, 'asis': importer.action.ASIS, @@ -352,13 +353,13 @@ def choose_candidate(candidates, singleton, rec, cur_artist=None, # Is the change good enough? bypass_candidates = False - if rec != autotag.RECOMMEND_NONE: + if rec != recommendation.none: match = candidates[0] bypass_candidates = True while True: # Display and choose from candidates. - require = rec in (autotag.RECOMMEND_NONE, autotag.RECOMMEND_LOW) + require = rec <= recommendation.low if not bypass_candidates: # Display list of candidates. @@ -441,7 +442,7 @@ def choose_candidate(candidates, singleton, rec, cur_artist=None, show_change(cur_artist, cur_album, match) # Exact match => tag automatically if we're not in timid mode. - if rec == autotag.RECOMMEND_STRONG and not config['import']['timid']: + if rec == recommendation.strong and not config['import']['timid']: return match # Ask for confirmation. diff --git a/test/test_ui.py b/test/test_ui.py index a426d1a89..a5a98571c 100644 --- a/test/test_ui.py +++ b/test/test_ui.py @@ -455,7 +455,7 @@ class AutotagTest(_common.TestCase): 'path', [_common.item()], ) - task.set_candidates('artist', 'album', [], autotag.RECOMMEND_NONE) + task.set_candidates('artist', 'album', [], autotag.recommendation.none) session = _common.import_session(cli=True) res = session.choose_match(task) self.assertEqual(res, result)