diff --git a/beets/library.py b/beets/library.py index 3108bb7e3..71ef84993 100644 --- a/beets/library.py +++ b/beets/library.py @@ -1,4 +1,4 @@ -import sqlite3, os, sys, operator +import sqlite3, os, sys, operator, re from beets.tag import MediaFile, FileTypeError from string import Template @@ -187,6 +187,7 @@ class Item(object): + class QueryElement(object): """A building block for library queries.""" def clause(self): @@ -198,19 +199,36 @@ class QueryElement(object): class SubstringQueryElement(QueryElement): """A query element that matches a substring in a specific item field.""" - def __init__(self, field, value): + def __init__(self, field, pattern): if field not in item_keys: raise InvalidFieldError(field + ' is not an item key') self.field = field - self.value = value + self.pattern = pattern def clause(self): - search = '%' + (self.value.replace('\\','\\\\').replace('%','\\%') + search = '%' + (self.pattern.replace('\\','\\\\').replace('%','\\%') .replace('_','\\_')) + '%' clause = self.field + " like ? escape '\\'" subvals = [search] return (clause, subvals) +class AnySubstringQueryElement(QueryElement): + """A query element that matches a substring in any item field.""" + + def __init__(self, pattern): + self.pattern = pattern + + def clause(self): + clause_parts = [] + subvals = [] + for field in item_keys: + el_clause, el_subvals = (SubstringQueryElement(field, self.pattern) + .clause()) + clause_parts.append('(' + el_clause + ')') + subvals += el_subvals + clause = ' or '.join(clause_parts) + return clause, subvals + class AndQueryElement(QueryElement): """A conjunction of a list of other query elements. Can be indexed like a list to access the sub-elements.""" @@ -237,7 +255,7 @@ class AndQueryElement(QueryElement): clause_parts.append('(' + el_clause + ')') subvals += el_subvals clause = ' and '.join(clause_parts) - return (clause, subvals) + return clause, subvals @classmethod def from_dict(cls, matches): @@ -248,6 +266,56 @@ class AndQueryElement(QueryElement): elements.append(SubstringQueryElement(key, pattern)) return cls(elements) + + # regular expression for _parse_query, below + _pq_regex = re.compile(r'(?:^|(?<=\s))' # zero-width match for whitespace or + # beginning of string + + # non-grouping optional segment for the keyword + r'(?:' + r'(\S+?)' # the keyword + r'(? match anything + for item in lib.get(q): + print item.artist + ' - ' + item.album + ' - ' + item.title + if __name__ == "__main__": # parse options usage = """usage: %prog [options] command @@ -34,7 +41,7 @@ command is one of: add, remove, update, write, list, help""" #(remove, ['remove', 'rm']), #(update, ['update', 'up']), #(write, ['write', 'wr', 'w']), - #(list, ['list', 'ls']), + (ls, ['list', 'ls']), (help, ['help', 'h']) ] for test_command in avail_commands: