mirror of
https://github.com/beetbox/beets.git
synced 2025-12-06 16:42:42 +01:00
implement get() querying for device libraries
This commit is contained in:
parent
cd9bb22270
commit
bae5ca5d70
3 changed files with 58 additions and 23 deletions
|
|
@ -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))
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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."""
|
||||
|
||||
|
|
|
|||
10
bts
10
bts
|
|
@ -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,10 +121,14 @@ class BeetsApp(cmdln.Cmdln):
|
|||
self.config.add_section(sec)
|
||||
|
||||
# Open library file.
|
||||
libpath = self.options.libpath or self.config.get('beets', 'library')
|
||||
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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue