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',
|
'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):
|
class PodLibrary(BaseLibrary):
|
||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
self.db = gpod.Database(path)
|
self.db = gpod.Database(path)
|
||||||
self.syncing = False
|
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
|
@classmethod
|
||||||
def by_name(cls, name):
|
def by_name(cls, name):
|
||||||
return cls(os.path.join(os.path.expanduser('~'), '.gvfs', name))
|
return cls(os.path.join(os.path.expanduser('~'), '.gvfs', name))
|
||||||
|
|
@ -82,7 +82,11 @@ class PodLibrary(BaseLibrary):
|
||||||
self.db.copy_delayed_files()
|
self.db.copy_delayed_files()
|
||||||
|
|
||||||
def get(self, query=None):
|
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):
|
def save(self):
|
||||||
self._stop_sync()
|
self._stop_sync()
|
||||||
|
|
|
||||||
|
|
@ -267,6 +267,12 @@ class Query(object):
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
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='*'):
|
def statement(self, columns='*'):
|
||||||
"""Returns (query, subvals) where clause is a sqlite SELECT statement
|
"""Returns (query, subvals) where clause is a sqlite SELECT statement
|
||||||
to enact this query and subvals is a list of values to substitute in
|
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
|
"""An abstract query that searches in a specific field for a
|
||||||
pattern.
|
pattern.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, field, pattern):
|
def __init__(self, field, pattern):
|
||||||
if field not in item_keys:
|
if field not in item_keys:
|
||||||
raise InvalidFieldError(field + ' is not an item key')
|
raise InvalidFieldError(field + ' is not an item key')
|
||||||
|
|
@ -307,13 +312,14 @@ class FieldQuery(Query):
|
||||||
|
|
||||||
class MatchQuery(FieldQuery):
|
class MatchQuery(FieldQuery):
|
||||||
"""A query that looks for exact matches in an item field."""
|
"""A query that looks for exact matches in an item field."""
|
||||||
|
|
||||||
def clause(self):
|
def clause(self):
|
||||||
return self.field + " = ?", [self.pattern]
|
return self.field + " = ?", [self.pattern]
|
||||||
|
|
||||||
|
def match(self, item):
|
||||||
|
return self.pattern == getattr(item, self.field)
|
||||||
|
|
||||||
class SubstringQuery(FieldQuery):
|
class SubstringQuery(FieldQuery):
|
||||||
"""A query that matches a substring in a specific item field."""
|
"""A query that matches a substring in a specific item field."""
|
||||||
|
|
||||||
def clause(self):
|
def clause(self):
|
||||||
search = '%' + (self.pattern.replace('\\','\\\\').replace('%','\\%')
|
search = '%' + (self.pattern.replace('\\','\\\\').replace('%','\\%')
|
||||||
.replace('_','\\_')) + '%'
|
.replace('_','\\_')) + '%'
|
||||||
|
|
@ -321,11 +327,13 @@ class SubstringQuery(FieldQuery):
|
||||||
subvals = [search]
|
subvals = [search]
|
||||||
return clause, subvals
|
return clause, subvals
|
||||||
|
|
||||||
|
def match(self, item):
|
||||||
|
return self.pattern.lower() in getattr(item, self.field).lower()
|
||||||
|
|
||||||
class CollectionQuery(Query):
|
class CollectionQuery(Query):
|
||||||
"""An abstract query class that aggregates other queries. Can be indexed
|
"""An abstract query class that aggregates other queries. Can be indexed
|
||||||
like a list to access the sub-queries.
|
like a list to access the sub-queries.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, subqueries = ()):
|
def __init__(self, subqueries = ()):
|
||||||
self.subqueries = subqueries
|
self.subqueries = subqueries
|
||||||
|
|
||||||
|
|
@ -404,8 +412,8 @@ class CollectionQuery(Query):
|
||||||
|
|
||||||
class AnySubstringQuery(CollectionQuery):
|
class AnySubstringQuery(CollectionQuery):
|
||||||
"""A query that matches a substring in any metadata field. """
|
"""A query that matches a substring in any metadata field. """
|
||||||
|
|
||||||
def __init__(self, pattern):
|
def __init__(self, pattern):
|
||||||
|
self.pattern = pattern
|
||||||
subqueries = []
|
subqueries = []
|
||||||
for field in metadata_rw_keys:
|
for field in metadata_rw_keys:
|
||||||
subqueries.append(SubstringQuery(field, pattern))
|
subqueries.append(SubstringQuery(field, pattern))
|
||||||
|
|
@ -414,6 +422,17 @@ class AnySubstringQuery(CollectionQuery):
|
||||||
def clause(self):
|
def clause(self):
|
||||||
return self.clause_with_joiner('or')
|
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):
|
class MutableCollectionQuery(CollectionQuery):
|
||||||
"""A collection query whose subqueries may be modified after the query is
|
"""A collection query whose subqueries may be modified after the query is
|
||||||
initialized.
|
initialized.
|
||||||
|
|
@ -426,11 +445,17 @@ class AndQuery(MutableCollectionQuery):
|
||||||
def clause(self):
|
def clause(self):
|
||||||
return self.clause_with_joiner('and')
|
return self.clause_with_joiner('and')
|
||||||
|
|
||||||
|
def match(self, item):
|
||||||
|
return all([q.match(item) for q in self.subqueries])
|
||||||
|
|
||||||
class TrueQuery(Query):
|
class TrueQuery(Query):
|
||||||
"""A query that always matches."""
|
"""A query that always matches."""
|
||||||
def clause(self):
|
def clause(self):
|
||||||
return '1', ()
|
return '1', ()
|
||||||
|
|
||||||
|
def match(self, item):
|
||||||
|
return True
|
||||||
|
|
||||||
class ResultIterator(object):
|
class ResultIterator(object):
|
||||||
"""An iterator into an item query result set."""
|
"""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 = cmdln.Cmdln.get_optparser(self)
|
||||||
parser.add_option('-l', '--library', dest='libpath',
|
parser.add_option('-l', '--library', dest='libpath',
|
||||||
help='the library database file to use')
|
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
|
return parser
|
||||||
|
|
||||||
def postoptparse(self):
|
def postoptparse(self):
|
||||||
|
|
@ -119,10 +121,14 @@ class BeetsApp(cmdln.Cmdln):
|
||||||
self.config.add_section(sec)
|
self.config.add_section(sec)
|
||||||
|
|
||||||
# Open library file.
|
# 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')
|
directory = self.config.get('beets', 'directory')
|
||||||
path_format = self.config.get('beets', 'path_format')
|
path_format = self.config.get('beets', 'path_format')
|
||||||
|
|
||||||
self.lib = Library(os.path.expanduser(libpath),
|
self.lib = Library(os.path.expanduser(libpath),
|
||||||
directory,
|
directory,
|
||||||
path_format)
|
path_format)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue