Delete tags from media files

This commit is contained in:
Thomas Scholtes 2014-04-05 00:17:25 +02:00
parent 9d9f1b539f
commit 663d91c4b2
2 changed files with 65 additions and 2 deletions

View file

@ -342,8 +342,9 @@ class StorageStyle(object):
describe more sophisticated translations or format-specific access
strategies.
MediaFile uses a StorageStyle via two methods: ``get()`` and
``set()``. It passes a Mutagen file object to each.
MediaFile uses a StorageStyle via three methods: ``get()``,
``set()``, and ``delete()``. It passes a Mutagen file object to
each.
Internally, the StorageStyle implements ``get()`` and ``set()``
using two steps that may be overridden by subtypes. To get a value,
@ -447,6 +448,12 @@ class StorageStyle(object):
return value
def delete(self, mutagen_file):
"""Remove the tag from the file.
"""
if self.key in mutagen_file:
del mutagen_file[self.key]
class ListStorageStyle(StorageStyle):
"""Abstract storage style that provides access to lists.
@ -572,6 +579,12 @@ class MP4TupleStorageStyle(MP4StorageStyle):
items[self.index] = int(value)
self.store(mutagen_file, items)
def delete(self, mutagen_file):
if self.index == 0:
super(MP4TupleStorageStyle, self).delete(mutagen_file)
else:
self.set(mutagen_file, None)
class MP4ListStorageStyle(ListStorageStyle, MP4StorageStyle):
pass
@ -725,6 +738,15 @@ class MP3DescStorageStyle(MP3StorageStyle):
except IndexError:
return None
def delete(self, mutagen_file):
frame = None
for frame in mutagen_file.tags.getall(self.key):
if frame.desc.lower() == self.description.lower():
break
if frame is not None:
del mutagen_file[frame.HashKey]
class MP3SlashPackStorageStyle(MP3StorageStyle):
"""Store value as part of pair that is serialized as a slash-
@ -752,6 +774,12 @@ class MP3SlashPackStorageStyle(MP3StorageStyle):
items.pop() # Do not store last value
self.store(mutagen_file, '/'.join(map(unicode, items)))
def delete(self, mutagen_file):
if self.pack_pos == 0:
super(MP3SlashPackStorageStyle, self).delete(mutagen_file)
else:
self.set(mutagen_file, None)
class MP3ImageStorageStyle(ListStorageStyle, MP3StorageStyle):
"""Converts between APIC frames and ``Image`` instances.
@ -943,6 +971,10 @@ class MediaField(object):
value = self._none_value()
for style in self.styles(mediafile.mgfile):
style.set(mediafile.mgfile, value)
def __delete__(self, mediafile):
for style in self.styles(mediafile.mgfile):
style.delete(mediafile.mgfile)
def _none_value(self):
"""Get an appropriate "null" value for this field's type. This
@ -1083,6 +1115,9 @@ class DateItemField(MediaField):
items[self.item_pos] = value
self.date_field._set_date_tuple(mediafile, *items)
def __delete__(self, mediafile):
self.__set__(mediafile, None)
class CoverArtField(MediaField):
"""A descriptor that provides access to the *raw image data* for the

View file

@ -580,6 +580,34 @@ class ReadWriteTestBase(ArtTestMixin, GenreListTestMixin,
self.assertEqual(mediafile.year, 0)
self.assertEqual(mediafile.date, datetime.date.min)
def test_delete_tag(self):
mediafile = self._mediafile_fixture('full')
keys = self.full_initial_tags.keys()
keys.remove('art')
for key in keys:
self.assertIsNotNone(getattr(mediafile, key))
delattr(mediafile, key)
mediafile.save()
mediafile = MediaFile(mediafile.path)
# TODO Eventually the tags should have None values
empty_tags = dict((k, v) for k, v in self.empty_tags.items()
if k in keys)
self.assertTags(mediafile, empty_tags)
def test_delete_packed_total(self):
mediafile = self._mediafile_fixture('full')
delattr(mediafile, 'tracktotal')
delattr(mediafile, 'disctotal')
mediafile.save()
mediafile = MediaFile(mediafile.path)
self.assertEqual(mediafile.track, self.full_initial_tags['track'])
self.assertEqual(mediafile.disc, self.full_initial_tags['disc'])
def assertTags(self, mediafile, tags):
errors = []
for key, value in tags.items():