diff --git a/beets/config_default.yaml b/beets/config_default.yaml index 9dfb8ea0f..100267ece 100644 --- a/beets/config_default.yaml +++ b/beets/config_default.yaml @@ -17,6 +17,7 @@ import: singletons: no default_action: apply +clutter: ["Thumbs.DB", ".DS_Store"] ignore: [".*", "*~"] replace: '[\\/]': _ diff --git a/beets/importer.py b/beets/importer.py index 61e490400..8ad8eca70 100644 --- a/beets/importer.py +++ b/beets/importer.py @@ -513,7 +513,11 @@ class ImportTask(object): call when the file in question may not have been removed. """ if self.toppath and not os.path.exists(filename): - util.prune_dirs(os.path.dirname(filename), self.toppath) + util.prune_dirs( + os.path.dirname(filename), + self.toppath, + clutter=config['clutter'].get(list) + ) # Full-album pipeline stages. diff --git a/beets/util/__init__.py b/beets/util/__init__.py index 7bcd7eab4..096cc319c 100644 --- a/beets/util/__init__.py +++ b/beets/util/__init__.py @@ -193,11 +193,26 @@ def mkdirall(path): raise FilesystemError(exc, 'create', (ancestor,), traceback.format_exc()) +def fnmatch_all(names, patterns): + """ + `names` and `patterns` should be iterables. + Returns True if all names match any of the patterns. + """ + for name in names: + matches = False + for pattern in patterns: + matches = fnmatch.fnmatch(name, pattern) + if matches: + break + if not matches: + return False + return True + def prune_dirs(path, root=None, clutter=('.DS_Store', 'Thumbs.db')): """If path is an empty directory, then remove it. Recursively remove path's ancestry up to root (which is never removed) where there are empty directories. If path is not contained in root, then nothing is - removed. Filenames in clutter are ignored when determining + removed. Glob patterns in clutter are ignored when determining emptiness. If root is not provided, then only path may be removed (i.e., no recursive removal). """ @@ -224,13 +239,12 @@ def prune_dirs(path, root=None, clutter=('.DS_Store', 'Thumbs.db')): if not os.path.exists(directory): # Directory gone already. continue - - if all(fn in clutter for fn in os.listdir(directory)): + if fnmatch_all(os.listdir(directory), clutter): # Directory contains only clutter (or nothing). try: shutil.rmtree(directory) except OSError: - break + break else: break diff --git a/test/test_importer.py b/test/test_importer.py index 2bad42e4c..c00662328 100644 --- a/test/test_importer.py +++ b/test/test_importer.py @@ -332,6 +332,14 @@ class ImportApplyTest(_common.TestCase): _call_stages(self.session, [self.i], self.info, toppath=self.srcdir) self.assertNotExists(os.path.dirname(self.srcpath)) + def test_apply_with_move_prunes_with_extra_clutter(self): + f = open(os.path.join(self.srcdir, 'testalbum', 'alog.log'), 'w') + f.close() + config['clutter'] = ['*.log'] + config['import']['move'] = True + _call_stages(self.session, [self.i], self.info, toppath=self.srcdir) + self.assertNotExists(os.path.dirname(self.srcpath)) + def test_manipulate_files_with_null_move(self): """It should be possible to "move" a file even when the file is already at the destination.