diff --git a/mediafile.py b/mediafile.py index f5579c0..04ab144 100644 --- a/mediafile.py +++ b/mediafile.py @@ -450,7 +450,7 @@ class StorageStyle(object): """ def __init__(self, key, as_type=six.text_type, suffix=None, - float_places=2): + float_places=2, read_only=False): """Create a basic storage strategy. Parameters: - `key`: The key on the Mutagen file object used to access the @@ -462,11 +462,16 @@ def __init__(self, key, as_type=six.text_type, suffix=None, - `float_places`: When the value is a floating-point number and encoded as a string, the number of digits to store after the decimal point. + - `read_only`: When true, writing to this field is disabled. + Primary use case is so wrongly named fields can be addressed + in a graceful manner. This does not block the delete method. + """ self.key = key self.as_type = as_type self.suffix = suffix self.float_places = float_places + self.read_only = read_only # Convert suffix to correct string type. if self.suffix and self.as_type is six.text_type \ @@ -1198,7 +1203,8 @@ def __set__(self, mediafile, value): if value is None: value = self._none_value() for style in self.styles(mediafile.mgfile): - style.set(mediafile.mgfile, value) + if not style.read_only: + style.set(mediafile.mgfile, value) def __delete__(self, mediafile): for style in self.styles(mediafile.mgfile): @@ -1233,7 +1239,8 @@ def __get__(self, mediafile, _): def __set__(self, mediafile, values): for style in self.styles(mediafile.mgfile): - style.set_list(mediafile.mgfile, values) + if not style.read_only: + style.set_list(mediafile.mgfile, values) def single_field(self): """Returns a ``MediaField`` descriptor that gets and sets the diff --git a/test/rsrc/read_only_tag.m4a b/test/rsrc/read_only_tag.m4a new file mode 100644 index 0000000..2a08b76 Binary files /dev/null and b/test/rsrc/read_only_tag.m4a differ diff --git a/test/test_mediafile_edge.py b/test/test_mediafile_edge.py index 8d31de0..681a25b 100644 --- a/test/test_mediafile_edge.py +++ b/test/test_mediafile_edge.py @@ -403,6 +403,41 @@ def test_image_encoding(self): self._delete_test() +class ReadOnlyTagTest(unittest.TestCase, _common.TempDirMixin): + def setUp(self): + self.create_temp_dir() + self.key = "READ_ONLY_TEST" + self.field = mediafile.MediaField( + mediafile.MP3StorageStyle(self.key, read_only=True), + mediafile.MP4StorageStyle( + "----:com.apple.iTunes:" + self.key, read_only=True), + mediafile.StorageStyle(self.key, read_only=True), + mediafile.ASFStorageStyle(self.key, read_only=True), + ) + + if "read_only_test" not in mediafile.MediaFile.fields(): + mediafile.MediaFile.add_field("read_only_test", self.field) + + def test_read(self): + path = os.path.join(_common.RSRC, b'empty.flac') + mf = mediafile.MediaFile(path) + mf.mgfile.tags[self.key] = "don't" + self.assertEqual("don't", mf.read_only_test) + + def test_write(self): + src = os.path.join(_common.RSRC, b'empty.flac') + path = os.path.join(self.temp_dir, b'test.flac') + shutil.copy(src, path) + mf = mediafile.MediaFile(path) + mf.read_only_field = "something terrible" + mf.path = os.path.join(self.temp_dir, b'test.flac') + mf.save() + self.assertNotIn(self.key, mf.mgfile.tags) + + def tearDown(self): + self.remove_temp_dir() + + def suite(): return unittest.TestLoader().loadTestsFromName(__name__)