MediaFile can be extended with custom fields

This commit is contained in:
Thomas Scholtes 2014-04-03 00:44:57 +02:00
parent a2a8b244d7
commit 863b9fb4af
2 changed files with 67 additions and 7 deletions

View file

@ -1265,6 +1265,25 @@ class MediaFile(object):
if isinstance(descriptor, MediaField):
yield property
@classmethod
def add_field(cls, name, descriptor):
"""Add a field to store custom tags.
``name`` is the name of the property the field is accessed
through. It must not already exist for the class. If the name
coincides with the name of a property of ``Item`` it will be set
from the item in ``item.write()``.
``descriptor`` must be an instance of ``MediaField``.
"""
if not isinstance(descriptor, MediaField):
raise ValueError(
u'{} must be an instance of MediaField'.format(descriptor))
if name in cls.__dict__:
raise ValueError(
u'property "{}" already exists on MediaField'.format(name))
setattr(cls, name, descriptor)
def update(self, dict, id3v23=False):
"""Update tags from the dictionary and write them to the file.

View file

@ -23,8 +23,10 @@ import time
import _common
from _common import unittest
from beets.mediafile import MediaFile, Image
from beets.library import ITEM_KEYS_WRITABLE, ITEM_KEYS
from beets.mediafile import MediaFile, MediaField, Image, \
MP3StorageStyle, StorageStyle, \
MP4StorageStyle, ASFStorageStyle
from beets.library import ITEM_KEYS_WRITABLE, ITEM_KEYS, Item
class ArtTestMixin(object):
@ -252,7 +254,50 @@ class GenreListTestMixin(object):
self.assertItemsEqual(mediafile.genres, [u'the genre', u'another'])
class ReadWriteTestBase(ArtTestMixin, GenreListTestMixin, LazySaveTestMixin):
field_extension = MediaField(
MP3StorageStyle('TKEY'),
MP4StorageStyle('----:com.apple.iTunes:initialkey'),
StorageStyle('INITIALKEY'),
ASFStorageStyle('INITIALKEY'),
)
class ExtendedFieldTestMixin(object):
def test_extended_field_write(self):
MediaFile.add_field('initialkey', field_extension)
mediafile = self._mediafile_fixture('empty')
mediafile.initialkey = 'F#'
mediafile.save()
mediafile = MediaFile(mediafile.path)
self.assertEqual(mediafile.initialkey, 'F#')
delattr(MediaFile, 'initialkey')
def test_extended_attribute_from_item(self):
MediaFile.add_field('initialkey', field_extension)
mediafile = self._mediafile_fixture('empty')
self.assertEqual(mediafile.initialkey, '')
item = Item(path=mediafile.path, initialkey='Gb')
item.write()
mediafile = MediaFile(mediafile.path)
self.assertEqual(mediafile.initialkey, 'Gb')
delattr(MediaFile, 'initialkey')
def test_invalid_descriptor(self):
with self.assertRaises(ValueError) as cm:
MediaFile.add_field('somekey', True)
self.assertIn('must be an instance of MediaField', str(cm.exception))
def test_overwrite_property(self):
with self.assertRaises(ValueError) as cm:
MediaFile.add_field('artist', MediaField())
self.assertIn('property "artist" already exists', str(cm.exception))
class ReadWriteTestBase(ArtTestMixin, GenreListTestMixin,
LazySaveTestMixin, ExtendedFieldTestMixin):
"""Test writing and reading tags. Subclasses must set ``extension`` and
``audio_properties``.
"""
@ -744,10 +789,6 @@ class MediaFieldTest(unittest.TestCase):
fields.extend(('encoder', 'images', 'genres', 'albumtype'))
self.assertItemsEqual(MediaFile.fields(), fields)
def test_item_keys_writable_migration(self):
keys_writable = set(MediaFile.fields()).intersection(ITEM_KEYS)
self.assertItemsEqual(keys_writable, ITEM_KEYS_WRITABLE)
def suite():
return unittest.TestLoader().loadTestsFromName(__name__)