Merge branch 'selective_modify2'

Conflicts:
	docs/changelog.rst

closes #1843
This commit is contained in:
Peter Kessen 2016-02-04 17:57:35 +01:00
commit 19f2cea5bf
5 changed files with 130 additions and 12 deletions

View file

@ -367,6 +367,31 @@ def input_yn(prompt, require=False):
return sel == 'y'
def input_select_items(prompt, items, rep):
"""Prompts the user to use all, none or some of the items
Will return the list of items the user selected
prompt: prompt to use for all and for selective choice
items: full list of items
rep: function which represents an item to the user
is called with the item as argument
function is responsive for newline at input
"""
out_items = []
choice = input_options(
('y', 'n', 's'), False,
'%s? (Yes/no/select)' % prompt)
print() # go to a new line
if choice == 'y':
out_items = items
elif choice == 's':
for item in items:
rep(item)
if input_yn('%s? (yes/no)' % prompt, True):
out_items.append(item)
print() # go to a new line
return out_items
# Human output formatting.
def human_bytes(size):

View file

@ -1347,13 +1347,7 @@ def modify_items(lib, mods, dels, query, write, move, album, confirm):
.format(len(objs), 'album' if album else 'item'))
changed = set()
for obj in objs:
obj.update(mods)
for field in dels:
try:
del obj[field]
except KeyError:
pass
if ui.show_model_changes(obj):
if print_and_modify(obj, mods, dels):
changed.add(obj)
# Still something to do?
@ -1372,8 +1366,9 @@ def modify_items(lib, mods, dels, query, write, move, album, confirm):
else:
extra = ''
if not ui.input_yn('Really modify%s (Y/n)?' % extra):
return
changed = ui.input_select_items(
'Really modify%s' % extra, changed,
lambda o: print_and_modify(o, mods, dels))
# Apply changes to database and files
with lib.transaction():
@ -1381,6 +1376,21 @@ def modify_items(lib, mods, dels, query, write, move, album, confirm):
obj.try_sync(write, move)
def print_and_modify(obj, mods, dels):
"""Print the modifications to an item
and return a bool indicating whether any changes were made
mods: modifications
dels: fields to delete
"""
obj.update(mods)
for field in dels:
try:
del obj[field]
except KeyError:
pass
return ui.show_model_changes(obj)
def modify_parse_args(args):
"""Split the arguments for the modify subcommand into query parts,
assignments (field=value), and deletions (field!). Returns the result as
@ -1439,7 +1449,7 @@ default_commands.append(modify_cmd)
# move: Move/copy files to the library or a new base directory.
def move_items(lib, dest, query, copy, album, pretend):
def move_items(lib, dest, query, copy, album, pretend, confirm=False):
"""Moves or copies items to a new base directory, given by dest. If
dest is None, then the library's base directory is used, making the
command "consolidate" files.
@ -1453,6 +1463,7 @@ def move_items(lib, dest, query, copy, album, pretend):
objs = [o for o in objs if (isalbummoved if album else isitemmoved)(o)]
action = 'Copying' if copy else 'Moving'
act = 'copy' if copy else 'move'
entity = 'album' if album else 'item'
log.info(u'{0} {1} {2}{3}.', action, len(objs), entity,
's' if len(objs) != 1 else '')
@ -1467,6 +1478,12 @@ def move_items(lib, dest, query, copy, album, pretend):
show_path_changes([(obj.path, obj.destination(basedir=dest))
for obj in objs])
else:
if confirm:
objs = ui.input_select_items(
'Really %s' % act, objs,
lambda o: show_path_changes(
[(o.path, o.destination(basedir=dest))]))
for obj in objs:
log.debug(u'moving: {0}', util.displayable_path(obj.path))
@ -1481,7 +1498,8 @@ def move_func(lib, opts, args):
if not os.path.isdir(dest):
raise ui.UserError('no such directory: %s' % dest)
move_items(lib, dest, decargs(args), opts.copy, opts.album, opts.pretend)
move_items(lib, dest, decargs(args), opts.copy, opts.album, opts.pretend,
opts.timid)
move_cmd = ui.Subcommand(
@ -1497,7 +1515,12 @@ move_cmd.parser.add_option(
)
move_cmd.parser.add_option(
'-p', '--pretend', default=False, action='store_true',
help='show how files would be moved, but don\'t touch anything')
help='show how files would be moved, but don\'t touch anything'
)
move_cmd.parser.add_option(
'-t', '--timid', dest='timid', action='store_true',
help='always confirm all actions'
)
move_cmd.parser.add_album_option()
move_cmd.func = move_func
default_commands.append(move_cmd)

View file

@ -29,6 +29,10 @@ New:
their values. Thanks to :user:`GuilhermeHideki`. :bug:`1812`
* The :ref:`fields-cmd` command now displays flexible attributes.
Thanks to :user:`GuilhermeHideki`. :bug:`1818`
* The :ref:`modify-cmd` command lets you interactive select tracks to apply
changes. :bug:`1843`
* The :ref:`move-cmd` command accepts `-t`, `--timid` switch now to confirm
or interactive select tracks process. :bug:`1843`
.. _Google Code-In: https://codein.withgoogle.com/
.. _AcousticBrainz: http://acousticbrainz.org/

View file

@ -231,6 +231,22 @@ class ModifyTest(unittest.TestCase, TestHelper):
item.load()
self.assertEqual(0, item.mtime)
def test_selective_modify(self):
title = "Tracktitle"
album = "album"
origArtist = "composer"
newArtist = "coverArtist"
for i in range(0, 10):
self.add_item_fixture(title="{0}{1}".format(title, i),
artist=origArtist,
album=album)
self.modify_inp('s\ny\ny\ny\nn\nn\ny\ny\ny\ny\nn',
title, "artist={0}".format(newArtist))
origItems = self.lib.items("artist:{0}".format(origArtist))
newItems = self.lib.items("artist:{0}".format(newArtist))
self.assertEqual(len(list(origItems)), 3)
self.assertEqual(len(list(newItems)), 7)
# Album Tests
def test_modify_album(self):

View file

@ -21,6 +21,56 @@ from test._common import unittest
from beets import ui
class InputMethodsTest(_common.TestCase):
def setUp(self):
super(InputMethodsTest, self).setUp()
self.io.install()
def _print_helper(self, s):
print(s)
def _print_helper2(self, s, prefix):
print(prefix, s)
def test_input_select_items(self):
full_items = ['1', '2', '3', '4', '5']
# Test no
self.io.addinput('n')
items = ui.input_select_items(
"Prompt", full_items, self._print_helper)
self.assertEqual(items, [])
# Test yes
self.io.addinput('y')
items = ui.input_select_items(
"Prompt", full_items, self._print_helper)
self.assertEqual(items, full_items)
# Test selective 1
self.io.addinput('s')
self.io.addinput('n')
self.io.addinput('y')
self.io.addinput('n')
self.io.addinput('y')
self.io.addinput('n')
items = ui.input_select_items(
"Prompt", full_items, self._print_helper)
self.assertEqual(items, ['2', '4'])
# Test selective 2
self.io.addinput('s')
self.io.addinput('y')
self.io.addinput('y')
self.io.addinput('n')
self.io.addinput('y')
self.io.addinput('n')
items = ui.input_select_items(
"Prompt", full_items,
lambda s: self._print_helper2(s, "Prefix"))
self.assertEqual(items, ['1', '2', '4'])
class InitTest(_common.LibTestCase):
def setUp(self):
super(InitTest, self).setUp()