implement get() querying for device libraries

This commit is contained in:
Adrian Sampson 2010-04-06 17:49:19 -07:00
parent cd9bb22270
commit bae5ca5d70
3 changed files with 58 additions and 23 deletions

View file

@ -33,19 +33,19 @@ FIELD_MAP = {
'tracks': 'tracktotal',
}
def track_to_item(track):
data = {}
for dname, bname in FIELD_MAP.items():
data[bname] = track[dname]
data['length'] = float(track['tracklen']) / 1000
data['path'] = track.ipod_filename()
return Item(data)
class PodLibrary(BaseLibrary):
def __init__(self, path):
self.db = gpod.Database(path)
self.syncing = False
# Browsing convenience.
def artists(self, query=None):
raise NotImplementedError
def albums(self, artist=None, query=None):
raise NotImplementedError
def items(self, artist=None, album=None, title=None, query=None):
raise NotImplementedError
@classmethod
def by_name(cls, name):
return cls(os.path.join(os.path.expanduser('~'), '.gvfs', name))
@ -65,7 +65,7 @@ class PodLibrary(BaseLibrary):
if hasattr(gpod, 'itdb_stop_sync'):
gpod.itdb_stop_sync(self.db._itdb)
self.syncing = False
def add(self, item):
self._start_sync()
track = self.db.new_Track()
@ -82,7 +82,11 @@ class PodLibrary(BaseLibrary):
self.db.copy_delayed_files()
def get(self, query=None):
raise NotImplementedError
query = self._get_query(query)
for track in self.db:
item = track_to_item(track)
if query.match(item):
yield item
def save(self):
self._stop_sync()

View file

@ -267,6 +267,12 @@ class Query(object):
"""
raise NotImplementedError
def match(self, item):
"""Check whether this query matches a given Item. Can be used to
perform queries on arbitrary sets of Items.
"""
raise NotImplementedError
def statement(self, columns='*'):
"""Returns (query, subvals) where clause is a sqlite SELECT statement
to enact this query and subvals is a list of values to substitute in
@ -298,7 +304,6 @@ class FieldQuery(Query):
"""An abstract query that searches in a specific field for a
pattern.
"""
def __init__(self, field, pattern):
if field not in item_keys:
raise InvalidFieldError(field + ' is not an item key')
@ -307,13 +312,14 @@ class FieldQuery(Query):
class MatchQuery(FieldQuery):
"""A query that looks for exact matches in an item field."""
def clause(self):
return self.field + " = ?", [self.pattern]
def match(self, item):
return self.pattern == getattr(item, self.field)
class SubstringQuery(FieldQuery):
"""A query that matches a substring in a specific item field."""
def clause(self):
search = '%' + (self.pattern.replace('\\','\\\\').replace('%','\\%')
.replace('_','\\_')) + '%'
@ -321,11 +327,13 @@ class SubstringQuery(FieldQuery):
subvals = [search]
return clause, subvals
def match(self, item):
return self.pattern.lower() in getattr(item, self.field).lower()
class CollectionQuery(Query):
"""An abstract query class that aggregates other queries. Can be indexed
like a list to access the sub-queries.
"""
def __init__(self, subqueries = ()):
self.subqueries = subqueries
@ -404,8 +412,8 @@ class CollectionQuery(Query):
class AnySubstringQuery(CollectionQuery):
"""A query that matches a substring in any metadata field. """
def __init__(self, pattern):
self.pattern = pattern
subqueries = []
for field in metadata_rw_keys:
subqueries.append(SubstringQuery(field, pattern))
@ -414,6 +422,17 @@ class AnySubstringQuery(CollectionQuery):
def clause(self):
return self.clause_with_joiner('or')
def match(self, item):
for fld in metadata_rw_keys:
try:
val = getattr(item, fld)
except KeyError:
continue
if isinstance(val, basestring) and \
self.pattern.lower() in val.lower():
return True
return False
class MutableCollectionQuery(CollectionQuery):
"""A collection query whose subqueries may be modified after the query is
initialized.
@ -426,11 +445,17 @@ class AndQuery(MutableCollectionQuery):
def clause(self):
return self.clause_with_joiner('and')
def match(self, item):
return all([q.match(item) for q in self.subqueries])
class TrueQuery(Query):
"""A query that always matches."""
def clause(self):
return '1', ()
def match(self, item):
return True
class ResultIterator(object):
"""An iterator into an item query result set."""

20
bts
View file

@ -108,6 +108,8 @@ class BeetsApp(cmdln.Cmdln):
parser = cmdln.Cmdln.get_optparser(self)
parser.add_option('-l', '--library', dest='libpath',
help='the library database file to use')
parser.add_option('-d', '--device', dest='device',
help="the name of the device library to use")
return parser
def postoptparse(self):
@ -119,13 +121,17 @@ class BeetsApp(cmdln.Cmdln):
self.config.add_section(sec)
# Open library file.
libpath = self.options.libpath or self.config.get('beets', 'library')
directory = self.config.get('beets', 'directory')
path_format = self.config.get('beets', 'path_format')
self.lib = Library(os.path.expanduser(libpath),
directory,
path_format)
if self.options.device:
from beets.device import PodLibrary
self.lib = PodLibrary.by_name(self.options.device)
else:
libpath = self.options.libpath or \
self.config.get('beets', 'library')
directory = self.config.get('beets', 'directory')
path_format = self.config.get('beets', 'path_format')
self.lib = Library(os.path.expanduser(libpath),
directory,
path_format)
@cmdln.alias("imp", "im")
def do_import(self, subcmd, opts, *paths):