mirror of
https://github.com/fergalmoran/picard.git
synced 2026-03-02 03:23:59 +00:00
PICARD-546,287: Add support for tag removal
This commit is contained in:
@@ -141,11 +141,13 @@ class File(QtCore.QObject, Item):
|
||||
values = self.orig_metadata.getall(tag)
|
||||
if values:
|
||||
saved_metadata[tag] = values
|
||||
deleted_tags = self.metadata.deleted_tags
|
||||
self.metadata.copy(metadata)
|
||||
self.metadata.deleted_tags = deleted_tags
|
||||
for tag, values in saved_metadata.iteritems():
|
||||
self.metadata.set(tag, values)
|
||||
|
||||
self.metadata["acoustid_id"] = acoustid
|
||||
if acoustid:
|
||||
self.metadata["acoustid_id"] = acoustid
|
||||
|
||||
def has_error(self):
|
||||
return self.state == File.ERROR
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
from __future__ import absolute_import
|
||||
import re
|
||||
import mutagen.apev2
|
||||
import mutagen.monkeysaudio
|
||||
import mutagen.musepack
|
||||
@@ -163,9 +164,44 @@ class APEv2File(File):
|
||||
tags['Cover Art (Front)'] = mutagen.apev2.APEValue(cover_filename + '\0' + image.data, mutagen.apev2.BINARY)
|
||||
break # can't save more than one item with the same name
|
||||
# (mp3tags does this, but it's against the specs)
|
||||
|
||||
|
||||
for tag in metadata.deleted_tags:
|
||||
real_name = str(self._get_tag_name(tag))
|
||||
if real_name in ('Lyrics','Comment','Performer'):
|
||||
tag_type = "\(%s\)" % tag.split(':',1)[1]
|
||||
for item in tags.get(real_name):
|
||||
if re.search(tag_type,item):
|
||||
tags.get(real_name).remove(item)
|
||||
elif tag in ('totaltracks', 'totaldiscs'):
|
||||
tagstr = real_name.lower() + 'number'
|
||||
try:
|
||||
tags[real_name] = metadata[tagstr]
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
del tags[real_name]
|
||||
|
||||
|
||||
tags.save(encode_filename(filename))
|
||||
|
||||
|
||||
def _get_tag_name(self, name):
|
||||
if name.startswith('lyrics:'):
|
||||
return 'Lyrics'
|
||||
elif name == 'date':
|
||||
return 'Year'
|
||||
elif name == ('tracknumber','totaltracks'):
|
||||
return 'Track'
|
||||
elif name == ('discnumber', 'totaldiscs'):
|
||||
return 'Disc'
|
||||
elif name.startswith('performer:') or name.startswith('comment:'):
|
||||
return name.split(':',1)[0].title()
|
||||
elif name in self.__rtranslate:
|
||||
return self.__rtranslate[name]
|
||||
else:
|
||||
return name.title()
|
||||
|
||||
class MusepackFile(APEv2File):
|
||||
|
||||
"""Musepack file."""
|
||||
|
||||
@@ -209,7 +209,30 @@ class ASFFile(File):
|
||||
continue
|
||||
name = self.__TRANS[name]
|
||||
file.tags[name] = map(unicode, values)
|
||||
|
||||
for tag in metadata.deleted_tags:
|
||||
real_name = self._get_tag_name(tag)
|
||||
if real_name and real_name in file.tags:
|
||||
if tag == 'totaldiscs':
|
||||
try:
|
||||
file.tags[real_name] = map(unicode,metadata['discnumber'])
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
del file.tags[real_name]
|
||||
|
||||
file.save()
|
||||
|
||||
def supports_tag(self, name):
|
||||
return name in self.__TRANS
|
||||
|
||||
|
||||
def _get_tag_name(self, name):
|
||||
|
||||
if name.startswith('lyrics'):
|
||||
return 'lyrics'
|
||||
elif name == 'totaldiscs':
|
||||
return self.__TRANS['discnumber']
|
||||
else:
|
||||
return self.__TRANS[name]
|
||||
|
||||
|
||||
@@ -397,6 +397,19 @@ class ID3File(File):
|
||||
if tipl.people:
|
||||
tags.add(tipl)
|
||||
|
||||
self._build_inverse_dic()
|
||||
|
||||
for tag in metadata.deleted_tags:
|
||||
real_name = self._get_tag_name(tag)
|
||||
log.debug(real_name)
|
||||
log.debug(tag)
|
||||
if real_name == 'POPM':
|
||||
for key, frame in tags.items():
|
||||
if frame.FrameID == 'POPM' and frame.email == config.setting['rating_user_email']:
|
||||
del tags[key]
|
||||
elif real_name in tags:
|
||||
del tags[real_name]
|
||||
|
||||
self._save_tags(tags, encode_filename(filename))
|
||||
|
||||
if self._IsMP3 and config.setting["remove_ape_from_mp3"]:
|
||||
@@ -404,6 +417,18 @@ class ID3File(File):
|
||||
mutagen.apev2.delete(encode_filename(filename))
|
||||
except:
|
||||
pass
|
||||
def _build_inverse_dic(self):
|
||||
self.__itranslate = {}
|
||||
for key, value in self.__translate.items():
|
||||
self.__itranslate[value] = key
|
||||
for key, value in self.__translate_freetext.items():
|
||||
self.__itranslate[value] = key
|
||||
|
||||
def _get_tag_name(self,name):
|
||||
if name in self.__itranslate:
|
||||
return self.__itranslate[name]
|
||||
elif name == '~rating':
|
||||
return 'POPM'
|
||||
|
||||
def _get_file(self, filename):
|
||||
raise NotImplementedError()
|
||||
|
||||
@@ -210,6 +210,11 @@ class MP4File(File):
|
||||
if covr:
|
||||
file.tags["covr"] = covr
|
||||
|
||||
for tag in metadata.deleted_tags:
|
||||
real_name = self._get_tag_name(tag)
|
||||
if real_name and real_name in file.tags:
|
||||
del file.tags[real_name]
|
||||
|
||||
file.save()
|
||||
|
||||
def supports_tag(self, name):
|
||||
@@ -218,6 +223,26 @@ class MP4File(File):
|
||||
or name in self.__other_supported_tags\
|
||||
or name.startswith('lyrics:')
|
||||
|
||||
def _get_tag_name(self, name):
|
||||
if name.startswith('lyrics:'):
|
||||
return 'lyrics'
|
||||
if name in self.__r_text_tags:
|
||||
return self.__r_text_tags[name]
|
||||
elif name in self.__r_bool_tags:
|
||||
return self.__r_bool_tags[name]
|
||||
elif name in self.__r_int_tags:
|
||||
return self.__r_int_tags[name]
|
||||
elif name in self.__r_freeform_tags:
|
||||
return self.__r_freeform_tags[name]
|
||||
elif name == "musicip_fingerprint":
|
||||
return "----:com.apple.iTunes:fingerprint"
|
||||
elif name == "tracknumber":
|
||||
return "trkn"
|
||||
elif name == "discnumber":
|
||||
return "disk"
|
||||
else:
|
||||
return None
|
||||
|
||||
def _info(self, metadata, file):
|
||||
super(MP4File, self)._info(metadata, file)
|
||||
if hasattr(file.info, 'codec_description') and file.info.codec_description:
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
import base64
|
||||
import re
|
||||
import mutagen.flac
|
||||
import mutagen.ogg
|
||||
import mutagen.oggflac
|
||||
@@ -209,6 +210,17 @@ class VCommentFile(File):
|
||||
base64.standard_b64encode(picture.write()))
|
||||
|
||||
file.tags.update(tags)
|
||||
for tag in metadata.deleted_tags:
|
||||
real_name = self._get_tag_name(tag)
|
||||
if real_name and real_name in file.tags:
|
||||
if real_name == 'performer' or real_name == 'comment':
|
||||
tag_type = "\(%s\)" % tag.split(':',1)[1]
|
||||
for item in file.tags.get(real_name):
|
||||
if re.search(tag_type,item):
|
||||
file.tags.get(real_name).remove(item)
|
||||
else:
|
||||
del file.tags[real_name]
|
||||
|
||||
kwargs = {}
|
||||
if is_flac and config.setting["remove_id3_from_flac"]:
|
||||
kwargs["deleteid3"] = True
|
||||
@@ -217,6 +229,25 @@ class VCommentFile(File):
|
||||
except TypeError:
|
||||
file.save()
|
||||
|
||||
def _get_tag_name(self, name):
|
||||
if name == '~rating':
|
||||
if config.setting['rating_user_email']:
|
||||
return 'rating:%s' % config.setting['rating_user_email']
|
||||
else:
|
||||
return 'rating'
|
||||
elif name.startswith("~"):
|
||||
return None
|
||||
elif name.startswith('lyrics:'):
|
||||
return 'lyrics'
|
||||
elif name.startswith('performer:') or name.startswith('comment:'):
|
||||
return name.split(':', 1)[0]
|
||||
elif name == 'musicip_fingerprint':
|
||||
return 'fingerprint'
|
||||
elif name in self.__rtranslate:
|
||||
return self.__rtranslate[name]
|
||||
else:
|
||||
return name
|
||||
|
||||
|
||||
class FLACFile(VCommentFile):
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ class Metadata(dict):
|
||||
def __init__(self):
|
||||
super(Metadata, self).__init__()
|
||||
self.images = []
|
||||
self.deleted_tags = set()
|
||||
self.length = 0
|
||||
|
||||
def append_image(self, coverartimage):
|
||||
@@ -222,11 +223,13 @@ class Metadata(dict):
|
||||
self.images = other.images[:]
|
||||
if other.length:
|
||||
self.length = other.length
|
||||
self.deleted_tags.update(other.deleted_tags)
|
||||
|
||||
def clear(self):
|
||||
dict.clear(self)
|
||||
self.images = []
|
||||
self.length = 0
|
||||
self.deleted_tags = set()
|
||||
|
||||
def getall(self, name):
|
||||
return dict.get(self, name, [])
|
||||
@@ -243,15 +246,17 @@ class Metadata(dict):
|
||||
|
||||
def set(self, name, values):
|
||||
dict.__setitem__(self, name, values)
|
||||
if name in self.deleted_tags:
|
||||
self.deleted_tags.remove(name)
|
||||
|
||||
def __setitem__(self, name, values):
|
||||
if not isinstance(values, list):
|
||||
values = [values]
|
||||
values = filter(None, map(unicode, values))
|
||||
if len(values):
|
||||
dict.__setitem__(self, name, values)
|
||||
self.set(name, values)
|
||||
else:
|
||||
self.pop(name, None)
|
||||
self.delete(name)
|
||||
|
||||
def add(self, name, value):
|
||||
if value or value == 0:
|
||||
@@ -261,6 +266,12 @@ class Metadata(dict):
|
||||
if value not in self.getall(name):
|
||||
self.add(name, value)
|
||||
|
||||
def delete(self, name):
|
||||
if name in self:
|
||||
self.pop(name, None)
|
||||
self.deleted_tags.add(name)
|
||||
|
||||
|
||||
def iteritems(self):
|
||||
for name, values in dict.iteritems(self):
|
||||
for value in values:
|
||||
|
||||
@@ -111,16 +111,15 @@ class TagDiff(object):
|
||||
else:
|
||||
return orig != new
|
||||
|
||||
def add(self, tag, orig_values, new_values, removable):
|
||||
def add(self, tag, orig_values, new_values, removable=False):
|
||||
if orig_values:
|
||||
self.orig.add(tag, orig_values)
|
||||
|
||||
if new_values:
|
||||
self.new.add(tag, new_values)
|
||||
|
||||
if orig_values and not new_values:
|
||||
if (orig_values and not new_values) or removed:
|
||||
self.status[tag] |= TagStatus.Removed
|
||||
removable = False
|
||||
elif new_values and not orig_values:
|
||||
self.status[tag] |= TagStatus.Added
|
||||
removable = True
|
||||
@@ -412,7 +411,8 @@ class MetadataBox(QtGui.QTableWidget):
|
||||
new_values = list(orig_values or [""])
|
||||
existing_tags.add(name)
|
||||
|
||||
tag_diff.add(name, orig_values, new_values, clear_existing_tags)
|
||||
removed = name in new_metadata.deleted_tags
|
||||
tag_diff.add(name, orig_values, new_values, True, removed)
|
||||
|
||||
tag_diff.add("~length",
|
||||
str(orig_metadata.length), str(new_metadata.length), False)
|
||||
@@ -491,6 +491,13 @@ class MetadataBox(QtGui.QTableWidget):
|
||||
new_item.setFlags(orig_flags if length else new_flags)
|
||||
self.set_item_value(new_item, self.tag_diff.new, name)
|
||||
|
||||
font = new_item.font()
|
||||
if result.tag_status(name) == TagStatus.Removed:
|
||||
font.setStrikeOut(True)
|
||||
else:
|
||||
font.setStrikeOut(False)
|
||||
new_item.setFont(font)
|
||||
|
||||
color = self.colors.get(result.tag_status(name),
|
||||
self.colors[TagStatus.NoChange])
|
||||
orig_item.setForeground(color)
|
||||
|
||||
Reference in New Issue
Block a user