diff --git a/beetsplug/ihate.py b/beetsplug/ihate.py index 18f5e96fb..b7980e02c 100644 --- a/beetsplug/ihate.py +++ b/beetsplug/ihate.py @@ -15,7 +15,6 @@ """Warns you about things you hate (or even blocks import).""" import logging -import os import re from beets import config from beets.plugins import BeetsPlugin @@ -51,57 +50,24 @@ class IHatePlugin(BeetsPlugin): self.config.add({ 'warn': [], 'skip': [], - 'regex_ignore_case': False, - 'regex_invert_folder_result': False, - 'regex_invert_file_result': False, - 'regex_folder_name': '.*', - 'regex_file_name': '.*' + 'path': '.*' }) - flags = re.IGNORECASE if self.config['regex_ignore_case'].get() else 0 - - self.invert_folder_album_result = \ - self.invert_folder_singleton_result = \ - self.config['regex_invert_folder_result'].get() - self.invert_file_album_result = \ - self.invert_file_singleton_result = \ - self.config['regex_invert_file_result'].get() - self.folder_name_album_regex = \ - self.folder_name_singleton_regex = \ - re.compile(self.config['regex_folder_name'].get(), flags) - self.file_name_album_regex = \ - self.file_name_singleton_regex = \ - re.compile(self.config['regex_file_name'].get(), flags) + self.path_album_regex = \ + self.path_singleton_regex = \ + re.compile(self.config['path'].get()) if 'album' in self.config: album_config = self.config['album'] - if 'regex_invert_folder_result' in album_config: - self.invert_folder_album_result = album_config[ - 'regex_invert_folder_result'].get() - if 'regex_invert_file_result' in album_config: - self.invert_file_album_result = album_config[ - 'regex_invert_file_result'].get() - if 'regex_folder_name' in album_config: - self.folder_name_album_regex = re.compile( - album_config['regex_folder_name'].get(), flags) - if 'regex_file_name' in album_config: - self.file_name_album_regex = re.compile( - album_config['regex_file_name'].get(), flags) + if 'path' in album_config: + self.path_album_regex = re.compile( + album_config['path'].get()) if 'singleton' in self.config: singleton_config = self.config['singleton'] - if 'regex_invert_folder_result' in singleton_config: - self.invert_folder_singleton_result = singleton_config[ - 'regex_invert_folder_result'].get() - if 'regex_invert_file_result' in singleton_config: - self.invert_file_singleton_result = singleton_config[ - 'regex_invert_file_result'].get() - if 'regex_folder_name' in singleton_config: - self.folder_name_singleton_regex = re.compile( - singleton_config['regex_folder_name'].get(), flags) - if 'regex_file_name' in singleton_config: - self.file_name_singleton_regex = re.compile( - singleton_config['regex_file_name'].get(), flags) + if 'path' in singleton_config: + self.path_singleton_regex = re.compile( + singleton_config['path'].get()) @classmethod def do_i_hate_this(cls, task, action_patterns): @@ -142,76 +108,25 @@ class IHatePlugin(BeetsPlugin): if task.items and len(task.items) > 0: items_to_import = [] for item in task.items: - if self.file_filter(item['path'], session.paths): + if self.file_filter(item['path']): items_to_import.append(item) if len(items_to_import) > 0: task.items = items_to_import else: task.choice_flag = action.SKIP elif isinstance(task, SingletonImportTask): - if not self.file_filter(task.item['path'], session.paths): + if not self.file_filter(task.item['path']): task.choice_flag = action.SKIP - def file_filter(self, full_path, base_paths): + def file_filter(self, full_path): """Checks if the configured regular expressions allow the import of the file given in full_path. """ - # The folder regex only checks the folder names starting from the - # longest base path. Find this folder. - matched_base_path = '' - for base_path in base_paths: - if full_path.startswith(base_path) and len(base_path) > len( - matched_base_path): - matched_base_path = base_path - relative_path = full_path[len(matched_base_path):] - - if os.path.isdir(full_path): - path = relative_path - file_name = None - else: - path, file_name = os.path.split(relative_path) - path, folder_name = os.path.split(path) - import_config = dict(config['import']) if 'singletons' not in import_config or not import_config[ 'singletons']: # Album - - # Folder - while len(folder_name) > 0: - matched = self.folder_name_album_regex.match( - folder_name) is not None - matched = not matched if self.invert_folder_album_result else \ - matched - if not matched: - return False - path, folder_name = os.path.split(path) - - # File - matched = self.file_name_album_regex.match( - file_name) is not None - matched = not matched if self.invert_file_album_result else matched - if not matched: - return False - return True + return self.path_album_regex.match(full_path) is not None else: # Singleton - - # Folder - while len(folder_name) > 0: - matched = self.folder_name_singleton_regex.match( - folder_name) is not None - matched = not matched if \ - self.invert_folder_singleton_result else matched - if not matched: - return False - path, folder_name = os.path.split(path) - - # File - matched = self.file_name_singleton_regex.match( - file_name) is not None - matched = not matched if self.invert_file_singleton_result else \ - matched - if not matched: - return False - return True + return self.path_singleton_regex.match(full_path) is not None diff --git a/docs/plugins/ihate.rst b/docs/plugins/ihate.rst index f2224bf5a..3fb5e4ffc 100644 --- a/docs/plugins/ihate.rst +++ b/docs/plugins/ihate.rst @@ -4,7 +4,8 @@ IHate Plugin The ``ihate`` plugin allows you to automatically skip things you hate during import or warn you about them. You specify queries (see :doc:`/reference/query`) and the plugin skips (or warns about) albums or items -that match any query. +that match any query. You can also specify regular expressions to filter files +to import regarding of their path and name. To use the ``ihate`` plugin, enable it in your configuration (see :ref:`using-plugins`). @@ -19,6 +20,13 @@ file. The available options are: Default: ``[]`` (empty list). - **warn**: Print a warning message for matches in this list of queries. Default: ``[]``. +- **path**: A regular expression to filter files based on its path and name. + Default: ``.*`` (everything) +- **album** and **singleton**: You may specify different regular expressions + used for imports of albums and singletons. This way, you can automatically + skip singletons when importing albums if the names (and paths) of the files + are distinguishable via a regex. The path regex defined here take precedence + over the global ``path`` option. Here's an example:: @@ -33,5 +41,12 @@ Here's an example:: - genre:polka - artist:manowar - album:christmas + path: .*\d\d[^/]+$ + # will only import files which names start with two digits + album: + path: .*\d\d[^/]+$ + singleton: + path: .*/(?!\d\d)[^/]+$ The plugin trusts your decision in "as-is" imports. + diff --git a/test/test_ihate.py b/test/test_ihate.py index 7b4b3de25..af996e740 100644 --- a/test/test_ihate.py +++ b/test/test_ihate.py @@ -12,7 +12,7 @@ from test.helper import capture_log from test.test_importer import ImportHelper -class IHatePluginTest(ImportHelper): +class IHatePluginTest(unittest.TestCase, ImportHelper): def setUp(self): self.setup_beets() self.__create_import_dir(2) @@ -142,207 +142,42 @@ class IHatePluginTest(ImportHelper): def test_import_nothing(self): self.__reset_config() - config['ihate']['regex_invert_folder_result'] = True - config['ihate']['regex_invert_file_result'] = True + config['ihate']['path'] = 'not_there' self.__run([]) # Global options - def test_import_global_match_folder(self): + def test_import_global(self): self.__reset_config() - config['ihate']['regex_folder_name'] = 'artist' + config['ihate']['path'] = '.*track_1.*\.mp3' self.__run([self.artist_paths[0], - self.artist_paths[1]]) - - def test_import_global_invert_folder(self): - self.__reset_config() - config['ihate']['regex_folder_name'] = 'artist' - config['ihate']['regex_invert_folder_result'] = True - self.__run([self.misc_paths[0], - self.misc_paths[1]]) - - def test_import_global_match_file(self): - self.__reset_config() - config['ihate']['regex_file_name'] = '.*2.*' - self.__run([self.artist_paths[1], - self.album_paths[1], - self.misc_paths[1]]) - - def test_import_global_invert_file(self): - self.__reset_config() - config['ihate']['regex_file_name'] = '.*2.*' - config['ihate']['regex_invert_file_result'] = True - self.__run([self.artist_paths[0], - self.album_paths[0], self.misc_paths[0]]) - - def test_import_global_match_folder_case_sensitive(self): - self.__reset_config() - config['ihate']['regex_folder_name'] = 'Artist' - self.__run([]) - - def test_import_global_match_folder_ignore_case(self): - self.__reset_config() - config['ihate']['regex_ignore_case'] = True - config['ihate']['regex_folder_name'] = 'Artist' self.__run([self.artist_paths[0], - self.artist_paths[1]]) + self.misc_paths[0]], singletons=True) # Album options - def test_import_album_match_folder(self): + def test_import_album(self): self.__reset_config() - config['ihate']['album']['regex_folder_name'] = 'artist' + config['ihate']['album']['path'] = '.*track_1.*\.mp3' self.__run([self.artist_paths[0], - self.artist_paths[1]]) - self.__run(self.all_paths, singletons=True) - - def test_import_album_invert_folder(self): - self.__reset_config() - config['ihate']['album']['regex_folder_name'] = 'artist' - config['ihate']['album']['regex_invert_folder_result'] = True - self.__run([self.misc_paths[0], - self.misc_paths[1]]) - self.__run(self.all_paths, singletons=True) - - def test_import_album_match_file(self): - self.__reset_config() - config['ihate']['album']['regex_file_name'] = '.*2.*' - self.__run([self.artist_paths[1], - self.album_paths[1], - self.misc_paths[1]]) - self.__run(self.all_paths, singletons=True) - - def test_import_album_invert_file(self): - self.__reset_config() - config['ihate']['album']['regex_file_name'] = '.*2.*' - config['ihate']['album']['regex_invert_file_result'] = True - self.__run([self.artist_paths[0], - self.album_paths[0], self.misc_paths[0]]) self.__run(self.all_paths, singletons=True) - def test_import_album_match_folder_case_sensitive(self): - self.__reset_config() - config['ihate']['album']['regex_folder_name'] = 'Artist' - self.__run([]) - self.__run(self.all_paths, singletons=True) - - def test_import_album_match_folder_ignore_case(self): - self.__reset_config() - config['ihate']['regex_ignore_case'] = True - config['ihate']['album']['regex_folder_name'] = 'Artist' - self.__run([self.artist_paths[0], - self.artist_paths[1]]) - self.__run(self.all_paths, singletons=True) - # Singleton options - def test_import_singleton_match_folder(self): + def test_import_singleton(self): self.__reset_config() - config['ihate']['singleton']['regex_folder_name'] = 'artist' + config['ihate']['singleton']['path'] = '.*track_1.*\.mp3' self.__run([self.artist_paths[0], - self.artist_paths[1]], singletons=True) - self.__run(self.all_paths) - - def test_import_singleton_invert_folder(self): - self.__reset_config() - config['ihate']['singleton']['regex_folder_name'] = 'artist' - config['ihate']['singleton']['regex_invert_folder_result'] = True - self.__run([self.misc_paths[0], - self.misc_paths[1]], singletons=True) - self.__run(self.all_paths) - - def test_import_singleton_match_file(self): - self.__reset_config() - config['ihate']['singleton']['regex_file_name'] = '.*2.*' - self.__run([self.artist_paths[1], - self.album_paths[1], - self.misc_paths[1]], singletons=True) - self.__run(self.all_paths) - - def test_import_singleton_invert_file(self): - self.__reset_config() - config['ihate']['singleton']['regex_file_name'] = '.*2.*' - config['ihate']['singleton']['regex_invert_file_result'] = True - self.__run([self.artist_paths[0], - self.album_paths[0], self.misc_paths[0]], singletons=True) self.__run(self.all_paths) - def test_import_singleton_match_folder_case_sensitive(self): - self.__reset_config() - config['ihate']['singleton']['regex_folder_name'] = 'Artist' - self.__run([], singletons=True) - self.__run(self.all_paths) - - def test_import_singleton_match_folder_ignore_case(self): - self.__reset_config() - config['ihate']['regex_ignore_case'] = True - config['ihate']['singleton']['regex_folder_name'] = 'Artist' - self.__run([self.artist_paths[0], - self.artist_paths[1]], singletons=True) - self.__run(self.all_paths) - # Album and singleton options - def test_import_both_match_folder(self): + def test_import_both(self): self.__reset_config() - config['ihate']['album']['regex_folder_name'] = 'artist' - config['ihate']['singleton']['regex_folder_name'] = 'misc' + config['ihate']['album']['path'] = '.*track_1.*\.mp3' + config['ihate']['singleton']['path'] = '.*track_2.*\.mp3' self.__run([self.artist_paths[0], - self.artist_paths[1]]) - self.__run([self.misc_paths[0], - self.misc_paths[1]], singletons=True) - - def test_import_both_invert_folder(self): - self.__reset_config() - config['ihate']['album']['regex_folder_name'] = 'artist' - config['ihate']['album']['regex_invert_folder_result'] = True - config['ihate']['singleton']['regex_folder_name'] = 'misc' - config['ihate']['singleton']['regex_invert_folder_result'] = True - self.__run([self.misc_paths[0], - self.misc_paths[1]]) - self.__run([self.artist_paths[0], - self.artist_paths[1], - self.album_paths[0], - self.album_paths[1]], singletons=True) - - def test_import_both_match_file(self): - self.__reset_config() - config['ihate']['album']['regex_file_name'] = '.*2.*' - config['ihate']['singleton']['regex_file_name'] = '.*1.*' - self.__run([self.artist_paths[1], - self.album_paths[1], - self.misc_paths[1]]) - self.__run([self.artist_paths[0], - self.album_paths[0], - self.misc_paths[0]], singletons=True) - - def test_import_both_invert_file(self): - self.__reset_config() - config['ihate']['album']['regex_file_name'] = '.*2.*' - config['ihate']['album']['regex_invert_file_result'] = True - config['ihate']['singleton']['regex_file_name'] = '.*1.*' - config['ihate']['singleton']['regex_invert_file_result'] = True - self.__run([self.artist_paths[0], - self.album_paths[0], self.misc_paths[0]]) self.__run([self.artist_paths[1], - self.album_paths[1], - self.misc_paths[1]], singletons=True) - - def test_import_both_match_folder_case_sensitive(self): - self.__reset_config() - config['ihate']['album']['regex_folder_name'] = 'Artist' - config['ihate']['singleton']['regex_folder_name'] = 'Misc' - self.__run([]) - self.__run([], singletons=True) - - def test_import_both_match_folder_ignore_case(self): - self.__reset_config() - config['ihate']['regex_ignore_case'] = True - config['ihate']['album']['regex_folder_name'] = 'Artist' - config['ihate']['singleton']['regex_folder_name'] = 'Misc' - self.__run([self.artist_paths[0], - self.artist_paths[1]]) - self.__run([self.misc_paths[0], self.misc_paths[1]], singletons=True)