initial support for multiple autotag candidates

This commit is contained in:
Adrian Sampson 2010-05-25 21:26:12 -07:00
parent ba6186c962
commit be315b6f33
2 changed files with 72 additions and 33 deletions

View file

@ -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
View file

@ -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']),