mirror of
https://github.com/beetbox/beets.git
synced 2026-02-27 09:41:51 +01:00
initial support for multiple autotag candidates
This commit is contained in:
parent
ba6186c962
commit
be315b6f33
2 changed files with 72 additions and 33 deletions
|
|
@ -283,8 +283,9 @@ def tag_album(items):
|
|||
and a little bit more:
|
||||
- The list of items, possibly reordered.
|
||||
- The current metadata: an (artist, album) tuple.
|
||||
- The inferred metadata dictionary.
|
||||
- The distance between the current and new metadata.
|
||||
- A list of (distance, info) tuples where info is a dictionary
|
||||
containing the inferred tags. The list is sorted by
|
||||
distance (i.e., best match first).
|
||||
May raise an AutotagError if existing metadata is insufficient.
|
||||
"""
|
||||
# Get current and candidate metadata.
|
||||
|
|
@ -293,10 +294,9 @@ def tag_album(items):
|
|||
raise InsufficientMetadataError()
|
||||
candidates = mb.match_album(cur_artist, cur_album, len(items))
|
||||
|
||||
best = None
|
||||
best_dist = None
|
||||
# Get the distance to each candidate.
|
||||
dist_and_cands = []
|
||||
for info in _first_n(candidates, MAX_CANDIDATES):
|
||||
|
||||
# Make sure the album has the correct number of tracks.
|
||||
if len(items) != len(info['tracks']):
|
||||
continue
|
||||
|
|
@ -309,16 +309,13 @@ def tag_album(items):
|
|||
# Get the change distance.
|
||||
dist = distance(items, info)
|
||||
|
||||
# Compare this to the best.
|
||||
if best_dist is None or dist < best_dist:
|
||||
best_dist = dist
|
||||
best = info
|
||||
|
||||
# No suitable candidates.
|
||||
if best is None or best_dist > GIVEUP_DIST:
|
||||
#fixme Remove restriction on track numbers then requery for
|
||||
# diagnosis.
|
||||
raise UnknownAlbumError()
|
||||
dist_and_cands.append((dist, info))
|
||||
|
||||
return items, (cur_artist, cur_album), best, best_dist
|
||||
if not dist_and_cands:
|
||||
raise UnknownAlbumError('so feasible matches found')
|
||||
|
||||
# Sort by distance.
|
||||
dist_and_cands.sort()
|
||||
|
||||
return items, (cur_artist, cur_album), dist_and_cands
|
||||
|
||||
|
|
|
|||
76
bts
76
bts
|
|
@ -63,6 +63,60 @@ def _input_yn(prompt, require=False):
|
|||
return False
|
||||
resp = raw_input("Type 'y' or 'n': ").strip()
|
||||
|
||||
def choose_candidate(items, cur_artist, cur_album, candidates):
|
||||
"""Given current metadata and a sorted list of
|
||||
(distance, candidate) pairs, ask the user for a selection
|
||||
of which candidate to use. Returns the selected candidate. If no
|
||||
candidate is judged good enough, returns None.
|
||||
"""
|
||||
# Is the change good enough?
|
||||
THRESH = 0.1 #fixme
|
||||
top_dist, top_info = candidates[0]
|
||||
bypass_candidates = False
|
||||
if top_dist <= THRESH:
|
||||
dist, info = top_dist, top_info
|
||||
bypass_candidates = True
|
||||
|
||||
while True:
|
||||
# Display and choose from candidates.
|
||||
if not bypass_candidates:
|
||||
print 'Candidates:'
|
||||
for i, (dist, info) in enumerate(candidates):
|
||||
print '%i. %s - %s (%f)' % (i+1, info['artist'],
|
||||
info['album'], dist)
|
||||
sel = None
|
||||
while not sel:
|
||||
# Ask the user for a choice.
|
||||
inp = raw_input('Enter a number or "s" to skip: ')
|
||||
inp = inp.strip()
|
||||
if inp.lower().startswith('s'):
|
||||
return None
|
||||
try:
|
||||
sel = int(inp)
|
||||
except ValueError:
|
||||
pass
|
||||
if not (1 <= sel <= len(candidates)):
|
||||
sel = None
|
||||
dist, info = candidates[sel-1]
|
||||
bypass_candidates = False
|
||||
|
||||
# Show what we're about to do.
|
||||
if cur_artist != info['artist'] or cur_album != info['album']:
|
||||
print "Correcting tags from:"
|
||||
print ' %s - %s' % (cur_artist, cur_album)
|
||||
print "To:"
|
||||
print ' %s - %s' % (info['artist'], info['album'])
|
||||
else:
|
||||
print "Tagging: %s - %s" % (info['artist'], info['album'])
|
||||
print '(Distance: %f)' % dist
|
||||
for item, track_data in zip(items, info['tracks']):
|
||||
if item.title != track_data['title']:
|
||||
print " * %s -> %s" % (item.title, track_data['title'])
|
||||
|
||||
# Warn if change is significant.
|
||||
if dist > 0.0:
|
||||
if not _input_yn("Apply change ([y]/n)? "):
|
||||
continue
|
||||
|
||||
def tag_album(items, lib, copy=True, write=True):
|
||||
"""Import items into lib, tagging them as an album. If copy, then
|
||||
|
|
@ -71,27 +125,15 @@ def tag_album(items, lib, copy=True, write=True):
|
|||
"""
|
||||
# Infer tags.
|
||||
try:
|
||||
items,(cur_artist,cur_album),info,dist = autotag.tag_album(items)
|
||||
items,(cur_artist,cur_album),candidates = autotag.tag_album(items)
|
||||
except autotag.AutotagError:
|
||||
print "Untaggable album:", os.path.dirname(items[0].path)
|
||||
return
|
||||
|
||||
# Show what we're about to do.
|
||||
if cur_artist != info['artist'] or cur_album != info['album']:
|
||||
print "Correcting tags from:"
|
||||
print ' %s - %s' % (cur_artist, cur_album)
|
||||
print "To:"
|
||||
print ' %s - %s' % (info['artist'], info['album'])
|
||||
else:
|
||||
print "Tagging: %s - %s" % (info['artist'], info['album'])
|
||||
for item, track_data in zip(items, info['tracks']):
|
||||
if item.title != track_data['title']:
|
||||
print " * %s -> %s" % (item.title, track_data['title'])
|
||||
|
||||
# Warn if change is significant.
|
||||
if dist > 0.0:
|
||||
if not _input_yn("Apply change ([y]/n)? "):
|
||||
return
|
||||
# Choose which tags to use.
|
||||
info = choose_candidate(items, cur_artist, cur_album, candidates)
|
||||
if not info:
|
||||
return
|
||||
|
||||
# Ensure that we don't have the album already.
|
||||
q = library.AndQuery((library.MatchQuery('artist', info['artist']),
|
||||
|
|
|
|||
Loading…
Reference in a new issue