From b1a45fda0b16bebdb08c6b1bc8d35f8d1e0d08d4 Mon Sep 17 00:00:00 2001 From: "adrian.sampson" Date: Sat, 7 Feb 2009 04:13:36 +0000 Subject: [PATCH] added bitrate and length to mediafile --HG-- extra : convert_revision : svn%3A41726ec3-264d-0410-9c23-a9f1637257cc/trunk%40119 --- beets/library.py | 19 +++++++++++++------ beets/mediafile.py | 10 +++++++++- test/rsrc/test.blb | Bin 4096 -> 4096 bytes test/test_db.py | 4 +++- test/test_mediafile.py | 39 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 64 insertions(+), 8 deletions(-) diff --git a/beets/library.py b/beets/library.py index fe04603a0..51dbef08f 100644 --- a/beets/library.py +++ b/beets/library.py @@ -4,8 +4,10 @@ from string import Template # Fields in the "items" table; all the metadata available for items in the # library. These are used directly in SQL; they are vulnerable to injection if -# accessible to the user. -metadata_fields = [ +# accessible to the user. The fields are divided into read-write +# metadata, all metadata (inlcuding read-only attributes), and all +# fields (i.e., including non-metadata attributes). +metadata_rw_fields = [ ('title', 'text'), ('artist', 'text'), ('album', 'text'), @@ -24,10 +26,15 @@ metadata_fields = [ ('bpm', 'int'), ('comp', 'bool'), ] +metadata_fields = [ + ('length', 'real'), + ('bitrate', 'int'), +] + metadata_rw_fields item_fields = [ ('id', 'integer primary key'), ('path', 'text'), ] + metadata_fields +metadata_rw_keys = map(operator.itemgetter(0), metadata_rw_fields) metadata_keys = map(operator.itemgetter(0), metadata_fields) item_keys = map(operator.itemgetter(0), item_fields) @@ -241,7 +248,7 @@ class Item(object): def write(self): """Writes the item's metadata to the associated file.""" f = MediaFile(self.path) - for key in metadata_keys: + for key in metadata_rw_keys: setattr(f, key, getattr(self, key)) f.save() @@ -262,7 +269,7 @@ class Item(object): value = getattr(self, key) # sanitize the value for inclusion in a path: # replace / and leading . with _ - if isinstance(value, str) or isinstance(value, unicode): + if isinstance(value, basestring): value.replace(os.sep, '_') re.sub(r'[' + os.sep + r']|^\.', '_', value) elif key in ('track', 'tracktotal', 'disc', 'disctotal'): @@ -466,11 +473,11 @@ class CollectionQuery(Query): return cls(subqueries) class AnySubstringQuery(CollectionQuery): - """A query that matches a substring in any item field. """ + """A query that matches a substring in any metadata field. """ def __init__(self, pattern): subqueries = [] - for field in item_keys: + for field in metadata_rw_keys: subqueries.append(SubstringQuery(field, pattern)) super(AnySubstringQuery, self).__init__(subqueries) diff --git a/beets/mediafile.py b/beets/mediafile.py index b73675daa..4982dee58 100644 --- a/beets/mediafile.py +++ b/beets/mediafile.py @@ -505,4 +505,12 @@ class MediaFile(object): as_type = bool), flac = StorageStyle('compilation') ) - + + @property + def length(self): + return self.mgfile.info.length + + @property + def bitrate(self): + return self.mgfile.info.bitrate + diff --git a/test/rsrc/test.blb b/test/rsrc/test.blb index f7c39bc769e63127b358313f5b6da7030e7a97e0..7a8abc5f70042f293122744701163bfd288a16ee 100644 GIT binary patch delta 219 zcmZorXi%6SEy}`x3P60Oi3V02stnBG%x9TYH#Tl&0G&B*wTo zjClhGmo5Wy8q-ZCeMa4ljk1g^jrA;IlNEVIC)@JLO%CHdE!L>cEXvTRD6Q?T%*)Hf v2nM{oldbuth&6IC0j2m2wdHwHr4%M}@`sBxiZcSGHMJErh0&#F@uvU)XZ{}y diff --git a/test/test_db.py b/test/test_db.py index 4cc01725d..032a481b3 100755 --- a/test/test_db.py +++ b/test/test_db.py @@ -30,6 +30,8 @@ def item(lib=None): return beets.library.Item({ 'bpm': 8, 'comp': True, 'path': 'somepath', + 'length': 60.0, + 'bitrate': 128000, }, lib) np = beets.library._normpath @@ -173,4 +175,4 @@ def suite(): return unittest.TestLoader().loadTestsFromName(__name__) if __name__ == '__main__': - unittest.main(defaultTest='suite') \ No newline at end of file + unittest.main(defaultTest='suite') diff --git a/test/test_mediafile.py b/test/test_mediafile.py index ee302fd06..cecc5cba4 100755 --- a/test/test_mediafile.py +++ b/test/test_mediafile.py @@ -21,6 +21,21 @@ def MakeReadingTest(path, correct_dict, field): repr(got) + ') when testing ' + os.path.basename(path)) return ReadingTest +def MakeReadOnlyTest(path, field, value): + class ReadOnlyTest(unittest.TestCase): + def setUp(self): + self.f = beets.mediafile.MediaFile(path) + def runTest(self): + got = getattr(self.f, field) + fail_msg = field + ' incorrect (expected ' + \ + repr(value) + ', got ' + repr(got) + \ + ') on ' + os.path.basename(path) + if field == 'length': + self.assertTrue(value-0.1 < got < value+0.1, fail_msg) + else: + self.assertEqual(got, value, fail_msg) + return ReadOnlyTest + def MakeWritingTest(path, correct_dict, field, testsuffix='_test'): class WritingTest(unittest.TestCase): @@ -162,6 +177,24 @@ correct_dicts = { } +read_only_correct_dicts = { + + 'full.mp3': { + 'length': 1.0, + 'bitrate': 80000, + }, + + 'full.flac': { + 'length': 1.0, + }, + + 'full.m4a': { + 'length': 1.0, + 'bitrate': 64000, + }, + +} + def suite_for_file(path, correct_dict, writing=True): s = unittest.TestSuite() for field in correct_dict: @@ -191,6 +224,12 @@ def suite(): # Special test for advanced release date. s.addTest(suite_for_file(os.path.join('rsrc', 'date.mp3'), correct_dicts['date'])) + + # Read-only attribute tests. + for fname, correct_dict in read_only_correct_dicts.iteritems(): + path = os.path.join('rsrc', fname) + for field, value in correct_dict.iteritems(): + s.addTest(MakeReadOnlyTest(path, field, value)()) return s