Simplified the configuration of the regular expressions for th ihate plugin

Added the docs
This commit is contained in:
Malte Ried 2014-12-30 14:11:45 +01:00
parent 11008494c3
commit 8addf3ef39
3 changed files with 43 additions and 278 deletions

View file

@ -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

View file

@ -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.

View file

@ -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)