Repository: beetbox/beets

Issue:
remove: Option to disable confirmation prompt
Please add`--force` or `--quiet` options to `beet remove` command. There are times we really want to delete lots of stuff but can't do so due to having to manually input Y to each and every deletion request.



Discussion:
Good idea! We have a confirmation option for other commands, so why not this one? `-f` seems like a good name for the option, for symmetry with the Unix `rm`.


Resolving patch:
diff --git a/beets/ui/commands.py b/beets/ui/commands.py
--- a/beets/ui/commands.py
+++ b/beets/ui/commands.py
@@ -1194,31 +1194,33 @@ def update_func(lib, opts, args):
 
 # remove: Remove items from library, delete files.
 
-def remove_items(lib, query, album, delete):
+def remove_items(lib, query, album, delete, force):
     """Remove items matching query from lib. If album, then match and
     remove whole albums. If delete, also remove files from disk.
     """
     # Get the matching items.
     items, albums = _do_query(lib, query, album)
 
-    # Prepare confirmation with user.
-    print_()
-    if delete:
-        fmt = u'$path - $title'
-        prompt = u'Really DELETE %i file%s (y/n)?' % \
-                 (len(items), 's' if len(items) > 1 else '')
-    else:
-        fmt = ''
-        prompt = u'Really remove %i item%s from the library (y/n)?' % \
-                 (len(items), 's' if len(items) > 1 else '')
+    # Confirm file removal if not forcing removal.
+    if not force:
+        # Prepare confirmation with user.
+        print_()
+        if delete:
+            fmt = u'$path - $title'
+            prompt = u'Really DELETE %i file%s (y/n)?' % \
+                     (len(items), 's' if len(items) > 1 else '')
+        else:
+            fmt = ''
+            prompt = u'Really remove %i item%s from the library (y/n)?' % \
+                     (len(items), 's' if len(items) > 1 else '')
 
-    # Show all the items.
-    for item in items:
-        ui.print_(format(item, fmt))
+        # Show all the items.
+        for item in items:
+            ui.print_(format(item, fmt))
 
-    # Confirm with user.
-    if not ui.input_yn(prompt, True):
-        return
+        # Confirm with user.
+        if not ui.input_yn(prompt, True):
+            return
 
     # Remove (and possibly delete) items.
     with lib.transaction():
@@ -1227,7 +1229,7 @@ def remove_items(lib, query, album, delete):
 
 
 def remove_func(lib, opts, args):
-    remove_items(lib, decargs(args), opts.album, opts.delete)
+    remove_items(lib, decargs(args), opts.album, opts.delete, opts.force)
 
 
 remove_cmd = ui.Subcommand(
@@ -1237,6 +1239,10 @@ def remove_func(lib, opts, args):
     u"-d", u"--delete", action="store_true",
     help=u"also remove files from disk"
 )
+remove_cmd.parser.add_option(
+    u"-f", u"--force", action="store_true",
+    help=u"do not ask when removing items"
+)
 remove_cmd.parser.add_album_option()
 remove_cmd.func = remove_func
 default_commands.append(remove_cmd)


Test patch:
diff --git a/test/test_ui.py b/test/test_ui.py
--- a/test/test_ui.py
+++ b/test/test_ui.py
@@ -134,14 +134,26 @@ def setUp(self):
 
     def test_remove_items_no_delete(self):
         self.io.addinput('y')
-        commands.remove_items(self.lib, u'', False, False)
+        commands.remove_items(self.lib, u'', False, False, False)
         items = self.lib.items()
         self.assertEqual(len(list(items)), 0)
         self.assertTrue(os.path.exists(self.i.path))
 
     def test_remove_items_with_delete(self):
         self.io.addinput('y')
-        commands.remove_items(self.lib, u'', False, True)
+        commands.remove_items(self.lib, u'', False, True, False)
+        items = self.lib.items()
+        self.assertEqual(len(list(items)), 0)
+        self.assertFalse(os.path.exists(self.i.path))
+
+    def test_remove_items_with_force_no_delete(self):
+        commands.remove_items(self.lib, u'', False, False, True)
+        items = self.lib.items()
+        self.assertEqual(len(list(items)), 0)
+        self.assertTrue(os.path.exists(self.i.path))
+
+    def test_remove_items_with_force_delete(self):
+        commands.remove_items(self.lib, u'', False, True, True)
         items = self.lib.items()
         self.assertEqual(len(list(items)), 0)
         self.assertFalse(os.path.exists(self.i.path))