diff --git a/beets/library.py b/beets/library.py index 8bba847ea..7cf700b0c 100644 --- a/beets/library.py +++ b/beets/library.py @@ -66,6 +66,20 @@ def _ancestry(path): out.insert(0, path) return out +def _walk_files(path): + """Like os.walk, but only yields the files in the directory tree. The full + pathnames to the files (under path) are given. Also, if path is a file, + _walk_files just yields that.""" + if os.path.isfile(path): + yield path + else: + for root, dirs, files in os.walk(path): + for filebase in files: + yield os.path.join(root, filebase) + + + + class Item(object): def __init__(self, values, library=None): @@ -112,7 +126,7 @@ class Item(object): #### interaction with the database #### - def load(self, fetch_id=None): + def load(self, load_id=None): """Refresh the item's metadata from the library database. If fetch_id is not specified, use the current item's id.""" @@ -195,7 +209,7 @@ class Item(object): if read_path is None: read_path = self.path f = MediaFile(read_path) - + for key in metadata_keys: self.record[key] = getattr(f, key) self.record['path'] = read_path # don't use self.path because there's @@ -230,7 +244,7 @@ class Item(object): # build the mapping for substitution in the path template, beginning # with the values from the database mapping = {} - for key in item_keys: + for key in metadata_keys: value = self.record[key] # sanitize the value for inclusion in a path: # replace / and leading . with _ @@ -539,17 +553,18 @@ class Library(object): #### main interface #### - def add(self, path): + def add(self, path, copy=False): """Add a file to the library or recursively search a directory and add - all its contents.""" + all its contents. If copy is True, copy files to their destination in + the library directory while adding.""" - for root, dirs, files in os.walk(path): - for filebase in files: - filepath = os.path.join(root, filebase) - try: - Item.from_path(_normpath(filepath), self) - except FileTypeError: - _log(filepath + ' of unknown type, skipping') + for f in _walk_files(path): + try: + i = Item.from_path(_normpath(f), self) + if copy: + i.move(copy=True) + except FileTypeError: + _log(f + ' of unknown type, skipping') def get(self, query=None): """Returns a ResultIterator to the items matching query, which may be diff --git a/bts.py b/bts.py index 545d6223a..fb3ad8b13 100755 --- a/bts.py +++ b/bts.py @@ -16,7 +16,8 @@ def ls(lib, criteria): def imp(lib, paths): for path in paths: - pass + lib.add(path, copy=True) + lib.save() if __name__ == "__main__": # parse options diff --git a/test/tag.py b/test/tag.py index 22cd34d47..418d66770 100755 --- a/test/tag.py +++ b/test/tag.py @@ -116,19 +116,50 @@ correct_dicts = { 'comments': u'', 'bpm': 0, 'comp': False + }, + + # empty.mp3 has had its ID3 tag deleted with mp3info -d + 'empty': { + 'title': u'', + 'artist': u'', + 'album': u'', + 'genre': u'', + 'composer': u'', + 'grouping': u'', + 'year': 0, + 'track': 0, + 'maxtrack': 0, + 'disc': 0, + 'maxdisc': 0, + 'lyrics': u'', + 'comments': u'', + 'bpm': 0, + 'comp': False } } +def suite_for_file(path, correct_dict): + s = unittest.TestSuite() + for field in correct_dict.keys(): + s.addTest(MakeReadingTest(path, correct_dict, field)()) + s.addTest(MakeWritingTest(path, correct_dict, field)()) + return s + def suite(): s = unittest.TestSuite() + + # General tests. for kind in ('m4a', 'mp3'): for tagset in ('full', 'partial', 'min'): path = 'rsrc' + os.sep + tagset + '.' + kind correct_dict = correct_dicts[tagset] - for field in correct_dict.keys(): - s.addTest(MakeReadingTest(path, correct_dict, field)()) - s.addTest(MakeWritingTest(path, correct_dict, field)()) + s.addTest(suite_for_file(path, correct_dict)) + + # Special test for missing ID3 tag. + s.addTest(suite_for_file('rsrc' + os.sep + 'empty.mp3', + correct_dicts['empty'])) + return s if __name__ == '__main__':