diff --git a/beets/autotag/match.py b/beets/autotag/match.py index 68fb81665..9a69f4ae4 100644 --- a/beets/autotag/match.py +++ b/beets/autotag/match.py @@ -323,6 +323,10 @@ def match_by_id(items): def _recommendation(results): """Given a sorted list of AlbumMatch or TrackMatch objects, return a recommendation based on the results' distances. + + If the recommendation is higher than the configured maximum for albums with + missing/extra tracks or differing track lengths/numbers, the recommendation + will be downgraded to the match configured maximum. """ if not results: # No candidates: no recommendation. @@ -330,13 +334,8 @@ def _recommendation(results): 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 = recommendation.medium - else: - # Strong recommendation level. - rec = recommendation.strong + # Strong recommendation level. + rec = recommendation.strong elif min_dist <= config['match']['medium_rec_thresh'].as_number(): # Medium recommendation level. rec = recommendation.medium @@ -344,12 +343,45 @@ def _recommendation(results): # Only a single candidate. rec = recommendation.low elif results[1].distance - min_dist >= \ - config['match']['rec_gap_thresh'].as_number(): + config['match']['rec_gap_thresh'].as_number(): # Gap between first two candidates is large. rec = recommendation.low else: # No conclusion. rec = recommendation.none + # Downgrade recommendation according to configured maximums. + if isinstance(results[0], hooks.AlbumMatch): + # Maximum recommendations. + max_rec = {} + for trigger in ('partial', 'tracklength', 'tracknumber'): + max_rec[trigger] = \ + config['match']['max_rec'][trigger].as_choice({ + 'strong': recommendation.strong, + 'medium': recommendation.medium, + 'low': recommendation.low, + 'none': recommendation.none, + }) + # Partial match. + if rec > max_rec['partial'] and \ + (results[0].extra_items or results[0].extra_tracks): + rec = max_rec['partial'] + downgraded = False + # Check track number and duration for each item. + for item, track_info in results[0].mapping.items(): + # Track length differs. + if rec > max_rec['tracklength'] and item.length and \ + track_info.length and \ + abs(item.length - track_info.length) > \ + TRACK_LENGTH_GRACE: + rec = max_rec['tracklength'] + downgraded = True + # Track number differs. + elif rec > max_rec['tracknumber'] and item.track not in \ + (track_info.index, track_info.medium_index): + rec = max_rec['tracknumber'] + downgraded = True + if downgraded: + break return rec def _add_candidate(items, results, info): diff --git a/beets/config_default.yaml b/beets/config_default.yaml index 100267ece..032a457ff 100644 --- a/beets/config_default.yaml +++ b/beets/config_default.yaml @@ -60,3 +60,7 @@ match: strong_rec_thresh: 0.04 medium_rec_thresh: 0.25 rec_gap_thresh: 0.25 + max_rec: + partial: low + tracklength: medium + tracknumber: medium diff --git a/docs/changelog.rst b/docs/changelog.rst index a14d895e9..b2c35afbd 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -10,6 +10,8 @@ New configuration options: is when considering a candidate. * :ref:`none_rec_action` lets you skip the prompt, and automatically choose an action, when there is no good candidate. Thanks to Tai Lee. +* :ref:`max_rec` lets you define a maximum recommendation for albums with + missing/extra tracks or differing track lengths/numbers. * :ref:`clutter` controls which files should be ignored when cleaning up empty directories. Thanks to Steinþór Pálsson. * :doc:`/plugins/lastgenre`: A new configuration option lets you choose to @@ -31,7 +33,7 @@ Other new stuff: * Some changes to the way candidates are recommended for selection, thanks to Tai Lee: - * Partial album matches are never "strong" recommendations. + * Partial album matches are downgraded to a "low" recommendation by default. * When a match isn't great but is either better than all the others or the only match, it is given a "low" (rather than "medium") recommendation. * There is no prompt default (i.e., input is required) when matches are diff --git a/docs/reference/config.rst b/docs/reference/config.rst index f73ac7220..f7a97c121 100644 --- a/docs/reference/config.rst +++ b/docs/reference/config.rst @@ -346,6 +346,24 @@ and the next-best match is above the *gap* threshold, the importer will suggest that match but not automatically confirm it. Otherwise, you'll see a list of options to choose from. +.. _max_rec: + +max_rec +~~~~~~~ + +You can define a maximum recommendation for albums with missing/extra tracks or +differing track lengths/numbers:: + + match: + max_rec: + partial: low + tracklength: medium + tracknumber: medium + +If a recommendation is higher than the configured maximum and the condition is +met, the recommendation will be downgraded. The maximum for each condition can +be one of ``none``, ``low``, ``medium`` or ``strong``. + .. _path-format-config: Path Format Configuration