mirror of
https://github.com/beetbox/beets.git
synced 2026-02-03 14:01:49 +01:00
encapsulate importer configuration in an object
This commit is contained in:
parent
23392525ec
commit
c0467c3724
5 changed files with 106 additions and 64 deletions
|
|
@ -26,7 +26,6 @@ from beets import autotag
|
||||||
from beets import library
|
from beets import library
|
||||||
import beets.autotag.art
|
import beets.autotag.art
|
||||||
from beets import plugins
|
from beets import plugins
|
||||||
from beets.ui import commands
|
|
||||||
|
|
||||||
CHOICE_SKIP = 'CHOICE_SKIP'
|
CHOICE_SKIP = 'CHOICE_SKIP'
|
||||||
CHOICE_ASIS = 'CHOICE_ASIS'
|
CHOICE_ASIS = 'CHOICE_ASIS'
|
||||||
|
|
@ -115,6 +114,21 @@ def progress_get(toppath):
|
||||||
return state[PROGRESS_KEY].get(toppath)
|
return state[PROGRESS_KEY].get(toppath)
|
||||||
|
|
||||||
|
|
||||||
|
# The configuration structure.
|
||||||
|
|
||||||
|
class ImportConfig(object):
|
||||||
|
"""Contains all the settings used during an import session. Should
|
||||||
|
be used in a "write-once" way -- everything is set up initially and
|
||||||
|
then never touched again.
|
||||||
|
"""
|
||||||
|
__slots__ = ['lib', 'paths', 'resume', 'logfile', 'color', 'quiet',
|
||||||
|
'quiet_fallback', 'copy', 'write', 'art', 'delete',
|
||||||
|
'choose_match_func']
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
for slot in self.__slots__:
|
||||||
|
setattr(self, slot, kwargs[slot])
|
||||||
|
|
||||||
|
|
||||||
# The importer task class.
|
# The importer task class.
|
||||||
|
|
||||||
class ImportTask(object):
|
class ImportTask(object):
|
||||||
|
|
@ -216,14 +230,14 @@ class ImportTask(object):
|
||||||
|
|
||||||
# Core autotagger pipeline stages.
|
# Core autotagger pipeline stages.
|
||||||
|
|
||||||
def read_albums(paths, resume):
|
def read_albums(config):
|
||||||
"""A generator yielding all the albums (as ImportTask objects) found
|
"""A generator yielding all the albums (as ImportTask objects) found
|
||||||
in the user-specified list of paths. `progress` specifies whether
|
in the user-specified list of paths. `progress` specifies whether
|
||||||
the resuming feature should be used. It may be True (resume if
|
the resuming feature should be used. It may be True (resume if
|
||||||
possible), False (never resume), or None (ask).
|
possible), False (never resume), or None (ask).
|
||||||
"""
|
"""
|
||||||
# Use absolute paths.
|
# Use absolute paths.
|
||||||
paths = [library._normpath(path) for path in paths]
|
paths = [library._normpath(path) for path in config.paths]
|
||||||
|
|
||||||
# Check the user-specified directories.
|
# Check the user-specified directories.
|
||||||
for path in paths:
|
for path in paths:
|
||||||
|
|
@ -231,7 +245,7 @@ def read_albums(paths, resume):
|
||||||
raise ui.UserError('not a directory: ' + path)
|
raise ui.UserError('not a directory: ' + path)
|
||||||
|
|
||||||
# Look for saved progress.
|
# Look for saved progress.
|
||||||
progress = resume is not False
|
progress = config.resume is not False
|
||||||
if progress:
|
if progress:
|
||||||
resume_dirs = {}
|
resume_dirs = {}
|
||||||
for path in paths:
|
for path in paths:
|
||||||
|
|
@ -239,7 +253,7 @@ def read_albums(paths, resume):
|
||||||
if resume_dir:
|
if resume_dir:
|
||||||
|
|
||||||
# Either accept immediately or prompt for input to decide.
|
# Either accept immediately or prompt for input to decide.
|
||||||
if resume:
|
if config.resume:
|
||||||
do_resume = True
|
do_resume = True
|
||||||
ui.print_('Resuming interrupted import of %s' % path)
|
ui.print_('Resuming interrupted import of %s' % path)
|
||||||
else:
|
else:
|
||||||
|
|
@ -272,7 +286,7 @@ def read_albums(paths, resume):
|
||||||
# Indicate the directory is finished.
|
# Indicate the directory is finished.
|
||||||
yield ImportTask.done_sentinel(toppath)
|
yield ImportTask.done_sentinel(toppath)
|
||||||
|
|
||||||
def initial_lookup():
|
def initial_lookup(config):
|
||||||
"""A coroutine for performing the initial MusicBrainz lookup for an
|
"""A coroutine for performing the initial MusicBrainz lookup for an
|
||||||
album. It accepts lists of Items and yields
|
album. It accepts lists of Items and yields
|
||||||
(items, cur_artist, cur_album, candidates, rec) tuples. If no match
|
(items, cur_artist, cur_album, candidates, rec) tuples. If no match
|
||||||
|
|
@ -291,13 +305,13 @@ def initial_lookup():
|
||||||
task.set_null_match()
|
task.set_null_match()
|
||||||
task = yield task
|
task = yield task
|
||||||
|
|
||||||
def user_query(lib, logfile, color, quiet, quiet_fallback):
|
def user_query(config):
|
||||||
"""A coroutine for interfacing with the user about the tagging
|
"""A coroutine for interfacing with the user about the tagging
|
||||||
process. lib is the Library to import into and logfile may be
|
process. lib is the Library to import into and logfile may be
|
||||||
a file-like object for logging the import process. The coroutine
|
a file-like object for logging the import process. The coroutine
|
||||||
accepts and yields ImportTask objects.
|
accepts and yields ImportTask objects.
|
||||||
"""
|
"""
|
||||||
lib = _reopen_lib(lib)
|
lib = _reopen_lib(config.lib)
|
||||||
first = True
|
first = True
|
||||||
task = None
|
task = None
|
||||||
while True:
|
while True:
|
||||||
|
|
@ -313,16 +327,14 @@ def user_query(lib, logfile, color, quiet, quiet_fallback):
|
||||||
print_(task.path)
|
print_(task.path)
|
||||||
|
|
||||||
# Ask the user for a choice.
|
# Ask the user for a choice.
|
||||||
choice = commands.choose_match(task.path, task.items, task.cur_artist,
|
choice = config.choose_match_func(task, config)
|
||||||
task.cur_album, task.candidates,
|
|
||||||
task.rec, color, quiet, quiet_fallback)
|
|
||||||
task.set_choice(choice)
|
task.set_choice(choice)
|
||||||
|
|
||||||
# Log certain choices.
|
# Log certain choices.
|
||||||
if choice is CHOICE_ASIS:
|
if choice is CHOICE_ASIS:
|
||||||
tag_log(logfile, 'asis', task.path)
|
tag_log(config.logfile, 'asis', task.path)
|
||||||
elif choice is CHOICE_SKIP:
|
elif choice is CHOICE_SKIP:
|
||||||
tag_log(logfile, 'skip', task.path)
|
tag_log(config.logfile, 'skip', task.path)
|
||||||
|
|
||||||
# Check for duplicates if we have a match.
|
# Check for duplicates if we have a match.
|
||||||
if choice == CHOICE_ASIS or isinstance(choice, tuple):
|
if choice == CHOICE_ASIS or isinstance(choice, tuple):
|
||||||
|
|
@ -333,35 +345,35 @@ def user_query(lib, logfile, color, quiet, quiet_fallback):
|
||||||
artist = task.info['artist']
|
artist = task.info['artist']
|
||||||
album = task.info['album']
|
album = task.info['album']
|
||||||
if _duplicate_check(lib, artist, album):
|
if _duplicate_check(lib, artist, album):
|
||||||
tag_log(logfile, 'duplicate', task.path)
|
tag_log(config.logfile, 'duplicate', task.path)
|
||||||
print_("This album is already in the library!")
|
print_("This album is already in the library!")
|
||||||
task.set_choice(CHOICE_SKIP)
|
task.set_choice(CHOICE_SKIP)
|
||||||
|
|
||||||
def apply_choices(lib, copy, write, art, delete, progress):
|
def apply_choices(config):
|
||||||
"""A coroutine for applying changes to albums during the autotag
|
"""A coroutine for applying changes to albums during the autotag
|
||||||
process. The parameters to the generator control the behavior of
|
process. The parameters to the generator control the behavior of
|
||||||
the import. The coroutine accepts ImportTask objects and yields
|
the import. The coroutine accepts ImportTask objects and yields
|
||||||
nothing.
|
nothing.
|
||||||
"""
|
"""
|
||||||
lib = _reopen_lib(lib)
|
lib = _reopen_lib(config.lib)
|
||||||
while True:
|
while True:
|
||||||
task = yield
|
task = yield
|
||||||
# Don't do anything if we're skipping the album or we're done.
|
# Don't do anything if we're skipping the album or we're done.
|
||||||
if task.choice_flag == CHOICE_SKIP or task.sentinel:
|
if task.choice_flag == CHOICE_SKIP or task.sentinel:
|
||||||
if progress:
|
if config.resume is not False:
|
||||||
task.save_progress()
|
task.save_progress()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Change metadata, move, and copy.
|
# Change metadata, move, and copy.
|
||||||
if task.should_write_tags():
|
if task.should_write_tags():
|
||||||
autotag.apply_metadata(task.items, task.info)
|
autotag.apply_metadata(task.items, task.info)
|
||||||
if copy and delete:
|
if config.copy and config.delete:
|
||||||
old_paths = [os.path.realpath(item.path)
|
old_paths = [os.path.realpath(item.path)
|
||||||
for item in task.items]
|
for item in task.items]
|
||||||
for item in task.items:
|
for item in task.items:
|
||||||
if copy:
|
if config.copy:
|
||||||
item.move(lib, True, task.should_create_album())
|
item.move(lib, True, task.should_create_album())
|
||||||
if write and task.should_write_tags():
|
if config.write and task.should_write_tags():
|
||||||
item.write()
|
item.write()
|
||||||
|
|
||||||
# Add items to library. We consolidate this at the end to avoid
|
# Add items to library. We consolidate this at the end to avoid
|
||||||
|
|
@ -376,7 +388,7 @@ def apply_choices(lib, copy, write, art, delete, progress):
|
||||||
lib.add(item)
|
lib.add(item)
|
||||||
|
|
||||||
# Get album art if requested.
|
# Get album art if requested.
|
||||||
if art and task.should_fetch_art():
|
if config.art and task.should_fetch_art():
|
||||||
artpath = beets.autotag.art.art_for_album(task.info)
|
artpath = beets.autotag.art.art_for_album(task.info)
|
||||||
if artpath:
|
if artpath:
|
||||||
albuminfo.set_art(artpath)
|
albuminfo.set_art(artpath)
|
||||||
|
|
@ -392,7 +404,7 @@ def apply_choices(lib, copy, write, art, delete, progress):
|
||||||
plugins.send('item_imported', lib=lib, item=item)
|
plugins.send('item_imported', lib=lib, item=item)
|
||||||
|
|
||||||
# Finally, delete old files.
|
# Finally, delete old files.
|
||||||
if copy and delete:
|
if config.copy and config.delete:
|
||||||
new_paths = [os.path.realpath(item.path) for item in task.items]
|
new_paths = [os.path.realpath(item.path) for item in task.items]
|
||||||
for old_path in old_paths:
|
for old_path in old_paths:
|
||||||
# Only delete files that were actually moved.
|
# Only delete files that were actually moved.
|
||||||
|
|
@ -400,38 +412,38 @@ def apply_choices(lib, copy, write, art, delete, progress):
|
||||||
os.remove(library._syspath(old_path))
|
os.remove(library._syspath(old_path))
|
||||||
|
|
||||||
# Update progress.
|
# Update progress.
|
||||||
if progress:
|
if config.resume is not False:
|
||||||
task.save_progress()
|
task.save_progress()
|
||||||
|
|
||||||
|
|
||||||
# Non-autotagged import (always sequential).
|
# Non-autotagged import (always sequential).
|
||||||
#TODO probably no longer necessary; use the same machinery?
|
#TODO probably no longer necessary; use the same machinery?
|
||||||
|
|
||||||
def simple_import(lib, paths, copy, delete, resume):
|
def simple_import(config):
|
||||||
"""Add files from the paths to the library without changing any
|
"""Add files from the paths to the library without changing any
|
||||||
tags.
|
tags.
|
||||||
"""
|
"""
|
||||||
for task in read_albums(paths, resume):
|
for task in read_albums(config):
|
||||||
if task.sentinel:
|
if task.sentinel:
|
||||||
task.save_progress()
|
task.save_progress()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if copy:
|
if config.copy:
|
||||||
if delete:
|
if config.delete:
|
||||||
old_paths = [os.path.realpath(item.path) for item in task.items]
|
old_paths = [os.path.realpath(item.path) for item in task.items]
|
||||||
for item in task.items:
|
for item in task.items:
|
||||||
item.move(lib, True, True)
|
item.move(config.lib, True, True)
|
||||||
|
|
||||||
album = lib.add_album(task.items, True)
|
album = config.lib.add_album(task.items, True)
|
||||||
lib.save()
|
config.lib.save()
|
||||||
|
|
||||||
# Announce that we added an album.
|
# Announce that we added an album.
|
||||||
plugins.send('album_imported', album=album)
|
plugins.send('album_imported', album=album)
|
||||||
|
|
||||||
if resume is not False:
|
if config.resume is not False:
|
||||||
task.save_progress()
|
task.save_progress()
|
||||||
|
|
||||||
if copy and delete:
|
if config.copy and config.delete:
|
||||||
new_paths = [os.path.realpath(item.path) for item in task.items]
|
new_paths = [os.path.realpath(item.path) for item in task.items]
|
||||||
for old_path in old_paths:
|
for old_path in old_paths:
|
||||||
# Only delete files that were actually moved.
|
# Only delete files that were actually moved.
|
||||||
|
|
|
||||||
|
|
@ -227,31 +227,32 @@ def manual_search():
|
||||||
album = raw_input('Album: ').decode(sys.stdin.encoding)
|
album = raw_input('Album: ').decode(sys.stdin.encoding)
|
||||||
return artist.strip(), album.strip()
|
return artist.strip(), album.strip()
|
||||||
|
|
||||||
def choose_match(path, items, cur_artist, cur_album, candidates,
|
def choose_match(task, config):
|
||||||
rec, color, quiet, quiet_fallback):
|
|
||||||
"""Given an initial autotagging of items, go through an interactive
|
"""Given an initial autotagging of items, go through an interactive
|
||||||
dance with the user to ask for a choice of metadata. Returns an
|
dance with the user to ask for a choice of metadata. Returns an
|
||||||
(info, items) pair, CHOICE_ASIS, or CHOICE_SKIP.
|
(info, items) pair, CHOICE_ASIS, or CHOICE_SKIP.
|
||||||
"""
|
"""
|
||||||
if quiet:
|
if config.quiet:
|
||||||
# No input; just make a decision.
|
# No input; just make a decision.
|
||||||
if rec == autotag.RECOMMEND_STRONG:
|
if task.rec == autotag.RECOMMEND_STRONG:
|
||||||
dist, items, info = candidates[0]
|
dist, items, info = task.candidates[0]
|
||||||
show_change(cur_artist, cur_album, items, info, dist, color)
|
show_change(task.cur_artist, task.cur_album, items, info, dist,
|
||||||
|
config.color)
|
||||||
return info, items
|
return info, items
|
||||||
else:
|
else:
|
||||||
if quiet_fallback == importer.CHOICE_SKIP:
|
if config.quiet_fallback == importer.CHOICE_SKIP:
|
||||||
print_('Skipping.')
|
print_('Skipping.')
|
||||||
elif quiet_fallback == importer.CHOICE_ASIS:
|
elif config.quiet_fallback == importer.CHOICE_ASIS:
|
||||||
print_('Importing as-is.')
|
print_('Importing as-is.')
|
||||||
else:
|
else:
|
||||||
assert(False)
|
assert(False)
|
||||||
return quiet_fallback
|
return config.quiet_fallback
|
||||||
|
|
||||||
# Loop until we have a choice.
|
# Loop until we have a choice.
|
||||||
while True:
|
while True:
|
||||||
# Ask for a choice from the user.
|
# Ask for a choice from the user.
|
||||||
choice = choose_candidate(cur_artist, cur_album, candidates, rec, color)
|
choice = choose_candidate(task.cur_artist, task.cur_album,
|
||||||
|
task.candidates, task.rec, config.color)
|
||||||
|
|
||||||
# Choose which tags to use.
|
# Choose which tags to use.
|
||||||
if choice in (importer.CHOICE_SKIP, importer.CHOICE_ASIS,
|
if choice in (importer.CHOICE_SKIP, importer.CHOICE_ASIS,
|
||||||
|
|
@ -301,15 +302,31 @@ def import_files(lib, paths, copy, write, autot, logpath, art, threaded,
|
||||||
# Never ask for input in quiet mode.
|
# Never ask for input in quiet mode.
|
||||||
if resume is None and quiet:
|
if resume is None and quiet:
|
||||||
resume = False
|
resume = False
|
||||||
|
|
||||||
|
# Set up import configuration.
|
||||||
|
config = importer.ImportConfig(
|
||||||
|
paths = paths,
|
||||||
|
resume = resume,
|
||||||
|
lib = lib,
|
||||||
|
logfile = logfile,
|
||||||
|
color = color,
|
||||||
|
quiet = quiet,
|
||||||
|
quiet_fallback = quiet_fallback,
|
||||||
|
copy = copy,
|
||||||
|
write = write,
|
||||||
|
art = art,
|
||||||
|
delete = delete,
|
||||||
|
choose_match_func = choose_match,
|
||||||
|
)
|
||||||
|
|
||||||
# Perform the import.
|
# Perform the import.
|
||||||
if autot:
|
if autot:
|
||||||
# Autotag. Set up the pipeline.
|
# Autotag. Set up the pipeline.
|
||||||
pl = pipeline.Pipeline([
|
pl = pipeline.Pipeline([
|
||||||
importer.read_albums(paths, resume),
|
importer.read_albums(config),
|
||||||
importer.initial_lookup(),
|
importer.initial_lookup(config),
|
||||||
importer.user_query(lib, logfile, color, quiet, quiet_fallback),
|
importer.user_query(config),
|
||||||
importer.apply_choices(lib, copy, write, art, delete, resume is not False),
|
importer.apply_choices(config),
|
||||||
])
|
])
|
||||||
|
|
||||||
# Run the pipeline.
|
# Run the pipeline.
|
||||||
|
|
@ -323,7 +340,7 @@ def import_files(lib, paths, copy, write, autot, logpath, art, threaded,
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
# Simple import without autotagging. Always sequential.
|
# Simple import without autotagging. Always sequential.
|
||||||
importer.simple_import(lib, paths, copy, delete, resume)
|
importer.simple_import(config)
|
||||||
|
|
||||||
# If we were logging, close the file.
|
# If we were logging, close the file.
|
||||||
if logfile:
|
if logfile:
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import os
|
||||||
# Mangle the search path to include the beets sources.
|
# Mangle the search path to include the beets sources.
|
||||||
sys.path.insert(0, '..')
|
sys.path.insert(0, '..')
|
||||||
import beets.library
|
import beets.library
|
||||||
|
from beets import importer
|
||||||
|
|
||||||
# Dummy item creation.
|
# Dummy item creation.
|
||||||
def item(): return beets.library.Item({
|
def item(): return beets.library.Item({
|
||||||
|
|
@ -38,6 +39,25 @@ def item(): return beets.library.Item({
|
||||||
'album_id': None,
|
'album_id': None,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
# Dummy import stuff.
|
||||||
|
def iconfig(lib, **kwargs):
|
||||||
|
config = importer.ImportConfig(
|
||||||
|
lib = lib,
|
||||||
|
paths = None,
|
||||||
|
resume = False,
|
||||||
|
logfile = None,
|
||||||
|
color = False,
|
||||||
|
quiet = True,
|
||||||
|
quiet_fallback = importer.CHOICE_SKIP,
|
||||||
|
copy = True,
|
||||||
|
write = False,
|
||||||
|
art = False,
|
||||||
|
delete = False,
|
||||||
|
choose_match_func = lambda x, y: importer.CHOICE_SKIP,
|
||||||
|
)
|
||||||
|
for k, v in kwargs.items():
|
||||||
|
setattr(config, k, v)
|
||||||
|
return config
|
||||||
|
|
||||||
# Mock timing.
|
# Mock timing.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,22 +64,19 @@ class ImportApplyTest(unittest.TestCase, _common.ExtraAsserts):
|
||||||
coro.send(task)
|
coro.send(task)
|
||||||
|
|
||||||
def test_apply_no_delete(self):
|
def test_apply_no_delete(self):
|
||||||
coro = importer.apply_choices(self.lib, True, False, False,
|
coro = importer.apply_choices(_common.iconfig(self.lib, delete=False))
|
||||||
False, False)
|
|
||||||
coro.next() # Prime coroutine.
|
coro.next() # Prime coroutine.
|
||||||
self._call_apply(coro, [self.i], self.info)
|
self._call_apply(coro, [self.i], self.info)
|
||||||
self.assertExists(self.srcpath)
|
self.assertExists(self.srcpath)
|
||||||
|
|
||||||
def test_apply_with_delete(self):
|
def test_apply_with_delete(self):
|
||||||
coro = importer.apply_choices(self.lib, True, False, False,
|
coro = importer.apply_choices(_common.iconfig(self.lib, delete=True))
|
||||||
True, False)
|
|
||||||
coro.next() # Prime coroutine.
|
coro.next() # Prime coroutine.
|
||||||
self._call_apply(coro, [self.i], self.info)
|
self._call_apply(coro, [self.i], self.info)
|
||||||
self.assertNotExists(self.srcpath)
|
self.assertNotExists(self.srcpath)
|
||||||
|
|
||||||
def test_apply_asis_uses_album_path(self):
|
def test_apply_asis_uses_album_path(self):
|
||||||
coro = importer.apply_choices(self.lib, True, False, False,
|
coro = importer.apply_choices(_common.iconfig(self.lib))
|
||||||
False, False)
|
|
||||||
coro.next() # Prime coroutine.
|
coro.next() # Prime coroutine.
|
||||||
self._call_apply_choice(coro, [self.i], importer.CHOICE_ASIS)
|
self._call_apply_choice(coro, [self.i], importer.CHOICE_ASIS)
|
||||||
self.assertExists(
|
self.assertExists(
|
||||||
|
|
@ -87,8 +84,7 @@ class ImportApplyTest(unittest.TestCase, _common.ExtraAsserts):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_apply_match_uses_album_path(self):
|
def test_apply_match_uses_album_path(self):
|
||||||
coro = importer.apply_choices(self.lib, True, False, False,
|
coro = importer.apply_choices(_common.iconfig(self.lib))
|
||||||
False, False)
|
|
||||||
coro.next() # Prime coroutine.
|
coro.next() # Prime coroutine.
|
||||||
self._call_apply(coro, [self.i], self.info)
|
self._call_apply(coro, [self.i], self.info)
|
||||||
self.assertExists(
|
self.assertExists(
|
||||||
|
|
@ -96,8 +92,7 @@ class ImportApplyTest(unittest.TestCase, _common.ExtraAsserts):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_apply_as_tracks_uses_singleton_path(self):
|
def test_apply_as_tracks_uses_singleton_path(self):
|
||||||
coro = importer.apply_choices(self.lib, True, False, False,
|
coro = importer.apply_choices(_common.iconfig(self.lib))
|
||||||
False, False)
|
|
||||||
coro.next() # Prime coroutine.
|
coro.next() # Prime coroutine.
|
||||||
self._call_apply_choice(coro, [self.i], importer.CHOICE_TRACKS)
|
self._call_apply_choice(coro, [self.i], importer.CHOICE_TRACKS)
|
||||||
self.assertExists(
|
self.assertExists(
|
||||||
|
|
|
||||||
|
|
@ -249,15 +249,13 @@ class AutotagTest(unittest.TestCase):
|
||||||
self.io.restore()
|
self.io.restore()
|
||||||
|
|
||||||
def _no_candidates_test(self, result):
|
def _no_candidates_test(self, result):
|
||||||
res = commands.choose_match(
|
task = importer.ImportTask(
|
||||||
|
'toppath',
|
||||||
'path',
|
'path',
|
||||||
[_common.item()], # items
|
[_common.item()],
|
||||||
'artist',
|
|
||||||
'album',
|
|
||||||
[], # candidates
|
|
||||||
autotag.RECOMMEND_NONE,
|
|
||||||
True, False, importer.CHOICE_SKIP
|
|
||||||
)
|
)
|
||||||
|
task.set_match('artist', 'album', [], autotag.RECOMMEND_NONE)
|
||||||
|
res = commands.choose_match(task, _common.iconfig(None, quiet=False))
|
||||||
self.assertEqual(res, result)
|
self.assertEqual(res, result)
|
||||||
self.assertTrue('No match' in self.io.getoutput())
|
self.assertTrue('No match' in self.io.getoutput())
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue