diff --git a/beets/plugins.py b/beets/plugins.py index 2b68f2d02..8611b92a6 100755 --- a/beets/plugins.py +++ b/beets/plugins.py @@ -16,8 +16,10 @@ import logging import traceback -from collections import defaultdict import inspect +import re +from collections import defaultdict + import beets from beets import mediafile @@ -402,3 +404,32 @@ def send(event, **arguments): argspec = inspect.getargspec(handler).args args = dict((k, v) for k, v in arguments.items() if k in argspec) handler(**args) + + +def feat_tokens(for_artist=True): + """Return a regular expression that matches phrases like "featuring" + that separate a main artist or a song title from secondary artists. + The `for_artist` option determines whether the regex should be + suitable for matching artist fields (the default) or title fields. + """ + feat_words = ['ft', 'featuring', 'feat', 'feat.', 'ft.'] + if for_artist: + feat_words += ['with', 'vs', 'and', 'con', '&'] + return '(?<=\s)(?:{0})(?=\s)'.format( + '|'.join(re.escape(x) for x in feat_words) + ) + + +def sanitize_choices(choices, choices_all): + """Clean up a stringlist configuration attribute: keep only choices + elements present in choices_all, remove duplicate elements, expand '*' + wildcard while keeping original stringlist order. + """ + seen = set() + others = [x for x in choices_all if x not in choices] + res = [] + for s in choices: + if s in list(choices_all) + ['*']: + if not (s in seen or seen.add(s)): + res.extend(list(others) if s == '*' else [s]) + return res diff --git a/beets/util/__init__.py b/beets/util/__init__.py index 54cf423e1..529bbb2f3 100644 --- a/beets/util/__init__.py +++ b/beets/util/__init__.py @@ -678,17 +678,3 @@ def max_filename_length(path, limit=MAX_FILENAME_LENGTH): return min(res[9], limit) else: return limit - - -def feat_tokens(for_artist=True): - """Return a regular expression that matches phrases like "featuring" - that separate a main artist or a song title from secondary artists. - The `for_artist` option determines whether the regex should be - suitable for matching artist fields (the default) or title fields. - """ - feat_words = ['ft', 'featuring', 'feat', 'feat.', 'ft.'] - if for_artist: - feat_words += ['with', 'vs', 'and', 'con', '&'] - return '(?<=\s)(?:{0})(?=\s)'.format( - '|'.join(re.escape(x) for x in feat_words) - ) diff --git a/beetsplug/fetchart.py b/beetsplug/fetchart.py index 98e9fda3e..aa670c5ab 100644 --- a/beetsplug/fetchart.py +++ b/beetsplug/fetchart.py @@ -22,12 +22,12 @@ from tempfile import NamedTemporaryFile import requests -from beets.plugins import BeetsPlugin -from beets.util.artresizer import ArtResizer +from beets import plugins from beets import importer from beets import ui from beets import util from beets import config +from beets.util.artresizer import ArtResizer try: import itunes @@ -319,23 +319,7 @@ def batch_fetch_art(lib, albums, force, maxwidth=None): message)) -def sanitize_sources(sources): - """Clean up the user's configured source list. Remove unknown or - duplicate sources while keeping original order. - """ - seen = set() - others = set(SOURCES_ALL) - set(sources) - res = [] - for s in sources: - if s in SOURCES_ALL + ['*']: - if not (s in seen or seen.add(s)): - res.extend(list(others) if s == '*' else [s]) - if not HAVE_ITUNES and 'itunes' in res: - res.remove('itunes') - return res - - -class FetchArtPlugin(BeetsPlugin): +class FetchArtPlugin(plugins.BeetsPlugin): def __init__(self): super(FetchArtPlugin, self).__init__() @@ -359,8 +343,10 @@ class FetchArtPlugin(BeetsPlugin): self.import_stages = [self.fetch_art] self.register_listener('import_task_files', self.assign_art) - self.config['sources'] = sanitize_sources( - self.config['sources'].as_str_seq()) + if not HAVE_ITUNES and u'itunes' in SOURCES_ALL: + SOURCES_ALL.remove(u'itunes') + self.config['sources'] = plugins.sanitize_choices( + self.config['sources'].as_str_seq(), SOURCES_ALL) # Asynchronous; after music is added to the library. def fetch_art(self, session, task): diff --git a/beetsplug/ftintitle.py b/beetsplug/ftintitle.py index e83836e0e..75134ae9c 100644 --- a/beetsplug/ftintitle.py +++ b/beetsplug/ftintitle.py @@ -14,9 +14,9 @@ """Moves "featured" artists to the title from the artist field. """ -from beets.plugins import BeetsPlugin +from beets import plugins from beets import ui -from beets.util import displayable_path, feat_tokens +from beets.util import displayable_path from beets import config import logging import re @@ -31,7 +31,7 @@ def split_on_feat(artist): may be a string or None if none is present. """ # split on the first "feat". - regex = re.compile(feat_tokens(), re.IGNORECASE) + regex = re.compile(plugins.feat_tokens(), re.IGNORECASE) parts = [s.strip() for s in regex.split(artist, 1)] if len(parts) == 1: return parts[0], None @@ -42,7 +42,7 @@ def split_on_feat(artist): def contains_feat(title): """Determine whether the title contains a "featured" marker. """ - return bool(re.search(feat_tokens(), title, flags=re.IGNORECASE)) + return bool(re.search(plugins.feat_tokens(), title, flags=re.IGNORECASE)) def update_metadata(item, feat_part, drop_feat): @@ -110,7 +110,7 @@ def ft_in_title(item, drop_feat): ui.print_() -class FtInTitlePlugin(BeetsPlugin): +class FtInTitlePlugin(plugins.BeetsPlugin): def __init__(self): super(FtInTitlePlugin, self).__init__() diff --git a/beetsplug/lyrics.py b/beetsplug/lyrics.py index 290142790..8cdee70e6 100644 --- a/beetsplug/lyrics.py +++ b/beetsplug/lyrics.py @@ -26,10 +26,8 @@ import difflib import itertools from HTMLParser import HTMLParseError -from beets.plugins import BeetsPlugin -from beets import ui -from beets import config -from beets.util import feat_tokens +from beets import plugins +from beets import config, ui # Global logger. @@ -86,10 +84,17 @@ def unescape(text): return out -def extract_text(html, starttag): +def extract_text_between(html, start_marker, end_marker): + _, html = html.split(start_marker, 1) + html, _ = html.split(end_marker, 1) + return _scrape_strip_cruft(html, True) + + +def extract_text_in(html, starttag): """Extract the text from a