mirror of
https://github.com/beetbox/beets.git
synced 2025-12-24 09:33:46 +01:00
MediaFile can be extended with custom fields
This commit is contained in:
parent
a2a8b244d7
commit
863b9fb4af
2 changed files with 67 additions and 7 deletions
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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__)
|
||||
|
|
|
|||
Loading…
Reference in a new issue