mirror of
https://github.com/beetbox/beets.git
synced 2026-01-13 19:52:48 +01:00
Refactor search options to ordinary choices
`PromptChoice`s can now return `Proposal` values, which makes these two options just "normal" actions that could be provided by plugins.
This commit is contained in:
parent
6c825970ac
commit
2f6538aee2
4 changed files with 33 additions and 66 deletions
|
|
@ -23,7 +23,7 @@ from beets import config
|
|||
|
||||
# Parts of external interface.
|
||||
from .hooks import AlbumInfo, TrackInfo, AlbumMatch, TrackMatch # noqa
|
||||
from .match import tag_item, tag_album # noqa
|
||||
from .match import tag_item, tag_album, Proposal # noqa
|
||||
from .match import Recommendation # noqa
|
||||
|
||||
# Global logger.
|
||||
|
|
|
|||
|
|
@ -43,8 +43,7 @@ from enum import Enum
|
|||
from beets import mediafile
|
||||
|
||||
action = Enum('action',
|
||||
['SKIP', 'ASIS', 'TRACKS', 'MANUAL', 'APPLY', 'MANUAL_ID',
|
||||
'ALBUMS', 'RETAG'])
|
||||
['SKIP', 'ASIS', 'TRACKS', 'APPLY', 'ALBUMS', 'RETAG'])
|
||||
# The RETAG action represents "don't apply any match, but do record
|
||||
# new metadata". It's not reachable via the standard command prompt but
|
||||
# can be used by plugins.
|
||||
|
|
@ -443,7 +442,6 @@ class ImportTask(BaseImportTask):
|
|||
indicates that an action has been selected for this task.
|
||||
"""
|
||||
# Not part of the task structure:
|
||||
assert choice not in (action.MANUAL, action.MANUAL_ID)
|
||||
assert choice != action.APPLY # Only used internally.
|
||||
if choice in (action.SKIP, action.ASIS, action.TRACKS, action.ALBUMS,
|
||||
action.RETAG):
|
||||
|
|
|
|||
|
|
@ -509,7 +509,7 @@ def choose_candidate(candidates, singleton, rec, cur_artist=None,
|
|||
to the user.
|
||||
|
||||
Returns one of the following:
|
||||
* the result of the choice, which may be SKIP, ASIS, TRACKS, or MANUAL
|
||||
* the result of the choice, which may be SKIP, ASIS, or TRACKS
|
||||
* a candidate (an AlbumMatch/TrackMatch object)
|
||||
* a chosen `PromptChoice` from `extra_choices`
|
||||
"""
|
||||
|
|
@ -536,21 +536,17 @@ def choose_candidate(candidates, singleton, rec, cur_artist=None,
|
|||
print_(u'For help, see: '
|
||||
u'http://beets.readthedocs.org/en/latest/faq.html#nomatch')
|
||||
opts = (u'Use as-is', u'as Tracks', u'Group albums', u'Skip',
|
||||
u'Enter search', u'enter Id', u'aBort')
|
||||
u'aBort')
|
||||
sel = ui.input_options(opts + extra_opts)
|
||||
if sel == u'u':
|
||||
return importer.action.ASIS
|
||||
elif sel == u't':
|
||||
assert not singleton
|
||||
return importer.action.TRACKS
|
||||
elif sel == u'e':
|
||||
return importer.action.MANUAL
|
||||
elif sel == u's':
|
||||
return importer.action.SKIP
|
||||
elif sel == u'b':
|
||||
raise importer.ImportAbort()
|
||||
elif sel == u'i':
|
||||
return importer.action.MANUAL_ID
|
||||
elif sel == u'g':
|
||||
return importer.action.ALBUMS
|
||||
elif sel in extra_actions:
|
||||
|
|
@ -603,11 +599,10 @@ def choose_candidate(candidates, singleton, rec, cur_artist=None,
|
|||
|
||||
# Ask the user for a choice.
|
||||
if singleton:
|
||||
opts = (u'Skip', u'Use as-is', u'Enter search', u'enter Id',
|
||||
u'aBort')
|
||||
opts = (u'Skip', u'Use as-is', u'aBort')
|
||||
else:
|
||||
opts = (u'Skip', u'Use as-is', u'as Tracks', u'Group albums',
|
||||
u'Enter search', u'enter Id', u'aBort')
|
||||
u'aBort')
|
||||
sel = ui.input_options(opts + extra_opts,
|
||||
numrange=(1, len(candidates)))
|
||||
if sel == u's':
|
||||
|
|
@ -616,15 +611,11 @@ def choose_candidate(candidates, singleton, rec, cur_artist=None,
|
|||
return importer.action.ASIS
|
||||
elif sel == u'm':
|
||||
pass
|
||||
elif sel == u'e':
|
||||
return importer.action.MANUAL
|
||||
elif sel == u't':
|
||||
assert not singleton
|
||||
return importer.action.TRACKS
|
||||
elif sel == u'b':
|
||||
raise importer.ImportAbort()
|
||||
elif sel == u'i':
|
||||
return importer.action.MANUAL_ID
|
||||
elif sel == u'g':
|
||||
return importer.action.ALBUMS
|
||||
elif sel in extra_actions:
|
||||
|
|
@ -650,11 +641,10 @@ def choose_candidate(candidates, singleton, rec, cur_artist=None,
|
|||
# Ask for confirmation.
|
||||
if singleton:
|
||||
opts = (u'Apply', u'More candidates', u'Skip', u'Use as-is',
|
||||
u'Enter search', u'enter Id', u'aBort')
|
||||
u'aBort')
|
||||
else:
|
||||
opts = (u'Apply', u'More candidates', u'Skip', u'Use as-is',
|
||||
u'as Tracks', u'Group albums', u'Enter search',
|
||||
u'enter Id', u'aBort')
|
||||
u'as Tracks', u'Group albums', u'aBort')
|
||||
default = config['import']['default_action'].as_choice({
|
||||
u'apply': u'a',
|
||||
u'skip': u's',
|
||||
|
|
@ -676,17 +666,13 @@ def choose_candidate(candidates, singleton, rec, cur_artist=None,
|
|||
elif sel == u't':
|
||||
assert not singleton
|
||||
return importer.action.TRACKS
|
||||
elif sel == u'e':
|
||||
return importer.action.MANUAL
|
||||
elif sel == u'b':
|
||||
raise importer.ImportAbort()
|
||||
elif sel == u'i':
|
||||
return importer.action.MANUAL_ID
|
||||
elif sel in extra_actions:
|
||||
return extra_actions[sel]
|
||||
|
||||
|
||||
def manual_search(task):
|
||||
def manual_search(session, task):
|
||||
"""Get a new `Proposal` using manual search criteria.
|
||||
|
||||
Input either an artist and album (for full albums) or artist and
|
||||
|
|
@ -704,7 +690,7 @@ def manual_search(task):
|
|||
return autotag.tag_item(task.item, artist, name)
|
||||
|
||||
|
||||
def manual_id(task):
|
||||
def manual_id(session, task):
|
||||
"""Get a new `Proposal` using a manually-entered ID.
|
||||
|
||||
Input an ID, either for an album ("release") or a track ("recording").
|
||||
|
|
@ -747,8 +733,12 @@ class TerminalImportSession(importer.ImportSession):
|
|||
# Loop until we have a choice.
|
||||
candidates, rec = task.candidates, task.rec
|
||||
while True:
|
||||
# Gather additional menu choices from plugins.
|
||||
extra_choices = self._get_plugin_choices(task)
|
||||
# Set up menu choices.
|
||||
choices = [
|
||||
PromptChoice(u'e', u'Enter search', manual_search),
|
||||
PromptChoice(u'i', u'enter Id', manual_id),
|
||||
]
|
||||
choices += self._get_plugin_choices(task)
|
||||
|
||||
# Ask for a choice from the user. The result of
|
||||
# `choose_candidate` may be an `importer.action`, an
|
||||
|
|
@ -756,7 +746,7 @@ class TerminalImportSession(importer.ImportSession):
|
|||
# `PromptChoice`.
|
||||
choice = choose_candidate(
|
||||
candidates, False, rec, task.cur_artist, task.cur_album,
|
||||
itemcount=len(task.items), extra_choices=extra_choices
|
||||
itemcount=len(task.items), extra_choices=choices
|
||||
)
|
||||
|
||||
# Basic choices that require no more action here.
|
||||
|
|
@ -765,28 +755,16 @@ class TerminalImportSession(importer.ImportSession):
|
|||
# Pass selection to main control flow.
|
||||
return choice
|
||||
|
||||
# Manual search. We ask for the search terms and run the
|
||||
# loop again.
|
||||
elif choice is importer.action.MANUAL:
|
||||
# Try again with manual search terms.
|
||||
prop = manual_search(task)
|
||||
candidates = prop.candidates
|
||||
rec = prop.recommendation
|
||||
|
||||
# Manual ID. We prompt for the ID and run the loop again.
|
||||
elif choice is importer.action.MANUAL_ID:
|
||||
# Try a manually-entered ID.
|
||||
prop = manual_id(task)
|
||||
candidates = prop.candidates
|
||||
rec = prop.recommendation
|
||||
|
||||
# Plugin-provided choices. We invoke the associated callback
|
||||
# function.
|
||||
elif choice in extra_choices:
|
||||
elif choice in choices:
|
||||
post_choice = choice.callback(self, task)
|
||||
if isinstance(post_choice, importer.action):
|
||||
# MANUAL and MANUAL_ID have no effect, even if returned.
|
||||
return post_choice
|
||||
elif isinstance(post_choice, autotag.Proposal):
|
||||
# Use the new candidates and continue around the loop.
|
||||
candidates = post_choice.candidates
|
||||
rec = post_choice.recommendation
|
||||
|
||||
# Otherwise, we have a specific match selection.
|
||||
else:
|
||||
|
|
@ -813,11 +791,15 @@ class TerminalImportSession(importer.ImportSession):
|
|||
return action
|
||||
|
||||
while True:
|
||||
extra_choices = self._get_plugin_choices(task)
|
||||
choices = [
|
||||
PromptChoice(u'e', u'Enter search', manual_search),
|
||||
PromptChoice(u'i', u'enter Id', manual_id),
|
||||
]
|
||||
choices += self._get_plugin_choices(task)
|
||||
|
||||
# Ask for a choice.
|
||||
choice = choose_candidate(candidates, True, rec, item=task.item,
|
||||
extra_choices=extra_choices)
|
||||
extra_choices=choices)
|
||||
|
||||
if choice in (importer.action.SKIP, importer.action.ASIS):
|
||||
return choice
|
||||
|
|
@ -825,23 +807,13 @@ class TerminalImportSession(importer.ImportSession):
|
|||
elif choice == importer.action.TRACKS:
|
||||
assert False # TRACKS is only legal for albums.
|
||||
|
||||
elif choice == importer.action.MANUAL:
|
||||
# Continue in the loop with a new set of candidates.
|
||||
prop = manual_search(task)
|
||||
candidates = prop.candidates
|
||||
rec = prop.recommendation
|
||||
|
||||
elif choice == importer.action.MANUAL_ID:
|
||||
# Ask for a track ID.
|
||||
prop = manual_id(task)
|
||||
candidates = prop.candidates
|
||||
rec = prop.recommendation
|
||||
|
||||
elif choice in extra_choices:
|
||||
elif choice in choices:
|
||||
post_choice = choice.callback(self, task)
|
||||
if isinstance(post_choice, importer.action):
|
||||
# MANUAL and MANUAL_ID have no effect, even if returned.
|
||||
return post_choice
|
||||
elif isinstance(post_choice, autotag.Proposal):
|
||||
candidates = post_choice.candidates
|
||||
rec = post_choice.recommendation
|
||||
|
||||
else:
|
||||
# Chose a candidate.
|
||||
|
|
|
|||
|
|
@ -593,7 +593,4 @@ by the choices on the core importer prompt, and hence should not be used:
|
|||
|
||||
Additionally, the callback function can optionally specify the next action to
|
||||
be performed by returning one of the values from ``importer.action``, which
|
||||
will be passed to the main loop upon the callback has been processed. Note that
|
||||
``action.MANUAL`` and ``action.MANUAL_ID`` will have no effect even if
|
||||
returned by the callback, due to the current architecture of the import
|
||||
process.
|
||||
will be passed to the main loop upon the callback has been processed.
|
||||
|
|
|
|||
Loading…
Reference in a new issue