diff --git a/beets/library.py b/beets/library.py index 30e514046..dfb197586 100644 --- a/beets/library.py +++ b/beets/library.py @@ -427,6 +427,8 @@ class CollectionQuery(Query): subqueries.append(SubstringQuery(key.lower(), pattern)) elif key.lower() == 'singleton': subqueries.append(SingletonQuery(util.str2bool(pattern))) + elif key.lower() == 'path': + subqueries.append(PathQuery(pattern)) if not subqueries: # no terms in query subqueries = [TrueQuery()] return cls(subqueries) @@ -485,6 +487,20 @@ class TrueQuery(Query): def match(self, item): return True +class PathQuery(Query): + """A query that matches all items under a given path.""" + def __init__(self, path): + self.file_path = normpath(path) # As a file. + self.dir_path = os.path.join(path, '') # As a directory (prefix). + + def match(self, item): + return (item.path == self.file_path) or \ + item.path.startswith(self.dir_path) + + def clause(self): + dir_pat = self.dir_path + '%' + return '(path = ?) || (path LIKE ?)', (self.file_path, dir_pat) + class ResultIterator(object): """An iterator into an item query result set.""" diff --git a/test/test_query.py b/test/test_query.py index 7a4f94f24..5f7c4946f 100644 --- a/test/test_query.py +++ b/test/test_query.py @@ -14,7 +14,6 @@ """Various tests for querying the library database. """ - import unittest import os @@ -226,6 +225,38 @@ class MemoryGetTest(unittest.TestCase, AssertsMixin): names = [a.album for a in results] self.assertEqual(names, ['the album']) +class PathQueryTest(unittest.TestCase, AssertsMixin): + def setUp(self): + self.lib = beets.library.Library(':memory:') + + path_item = _common.item() + path_item.path = '/a/b/c.mp3' + path_item.title = 'path item' + self.lib.add(path_item) + + def test_path_exact_match(self): + q = 'path:/a/b/c.mp3' + results = self.lib.items(q) + self.assert_matched(results, 'path item') + self.assert_done(results) + + def test_parent_directory_no_slash(self): + q = 'path:/a' + results = self.lib.items(q) + self.assert_matched(results, 'path item') + self.assert_done(results) + + def test_parent_directory_with_slash(self): + q = 'path:/a/' + results = self.lib.items(q) + self.assert_matched(results, 'path item') + self.assert_done(results) + + def test_no_match(self): + q = 'path:/xyzzy/' + results = self.lib.items(q) + self.assert_done(results) + class BrowseTest(unittest.TestCase, AssertsMixin): def setUp(self): self.lib = beets.library.Library(