mirror of
https://github.com/beetbox/beets.git
synced 2026-01-04 23:12:51 +01:00
Merge branch 'selective_modify2'
Conflicts: docs/changelog.rst closes #1843
This commit is contained in:
commit
19f2cea5bf
5 changed files with 130 additions and 12 deletions
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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/
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in a new issue