Skip to content

Commit d3df50e

Browse files
committed
PICARD-187: Support for manually removing cover art
PICARD-187: Support for manually removing cover art finishing PICARD-187
1 parent a2d2161 commit d3df50e

File tree

7 files changed

+109
-1
lines changed

7 files changed

+109
-1
lines changed

picard/formats/apev2.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,11 @@ def _remove_deleted_tags(self, metadata, tags):
263263
if real_name in tags:
264264
del tags[real_name]
265265

266+
if self.metadata.images._deleted:
267+
for tag in tags:
268+
if tag.lower().startswith('cover art'):
269+
del tags[tag]
270+
266271
def _get_tag_name(self, name):
267272
if name in self.__casemap:
268273
return self.__casemap[name]

picard/formats/asf.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,8 @@ def _remove_deleted_tags(self, metadata, tags):
304304
real_name = self._get_tag_name(tag)
305305
if real_name and real_name in tags:
306306
del tags[real_name]
307+
if self.metadata.images._deleted:
308+
del tags['WM/Picture']
307309

308310
@classmethod
309311
def supports_tag(cls, name):

picard/formats/id3.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,11 @@ def _remove_deleted_tags(self, metadata, tags):
659659
except KeyError:
660660
pass
661661

662+
if self.metadata.images._deleted:
663+
for key, frame in tags.items():
664+
if frame.FrameID == 'APIC':
665+
del tags[key]
666+
662667
@classmethod
663668
def supports_tag(cls, name):
664669
return ((name and not name.startswith('~') and name not in UNSUPPORTED_TAGS)

picard/formats/mp4.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,9 @@ def _remove_deleted_tags(self, metadata, tags):
340340
if tag not in {'totaltracks', 'totaldiscs'}:
341341
del tags[real_name]
342342

343+
if self.metadata.images._deleted:
344+
del tags['covr']
345+
343346
@classmethod
344347
def supports_tag(cls, name):
345348
return (name

picard/formats/vorbis.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ def _save(self, filename, metadata):
321321
base64.b64encode(picture.write()).decode('ascii'))
322322

323323
file.tags.update(tags)
324-
324+
self._clear_cover_art(file)
325325
self._remove_deleted_tags(metadata, file.tags)
326326

327327
kwargs = {}
@@ -358,6 +358,15 @@ def _remove_deleted_tags(self, metadata, tags):
358358
del tags[tag]
359359
del tags[real_name]
360360

361+
def _clear_cover_art(self, file):
362+
if self.metadata.images._deleted:
363+
if self._File == mutagen.flac.FLAC:
364+
file.clear_pictures()
365+
else:
366+
for tag in file:
367+
if tag.lower().startswith('metadata_block_picture'):
368+
del file.tags[tag]
369+
361370
def _get_tag_name(self, name):
362371
if name == '~rating':
363372
config = get_config()

picard/ui/coverartbox.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,11 @@ def open_release_page(self):
348348
lookup.album_lookup(self.release)
349349

350350

351+
def image_delete(obj, image):
352+
obj.metadata.images.strip_selected_image(image)
353+
obj.metadata_images_changed.emit()
354+
355+
351356
def set_image_replace(obj, coverartimage):
352357
obj.metadata.images.strip_front_images()
353358
obj.metadata.images.append(coverartimage)
@@ -545,6 +550,71 @@ def load_remote_image(self, url, data):
545550
log.warning("Can't load image: %s", e)
546551
return
547552

553+
def delete_cover_art(self):
554+
if not self.item or not self.item.metadata.images:
555+
if not self.item.orig_metadata.images:
556+
return
557+
558+
cover_art_list = [image.source or _("Unnamed Cover Art") for image in self.item.metadata.images]
559+
560+
selected_item, ok_pressed = QtWidgets.QInputDialog.getItem(self, _("Delete Cover Art"),
561+
_("Select the cover art image to delete:"),
562+
cover_art_list, 0, False)
563+
if ok_pressed:
564+
selected_image_index = cover_art_list.index(selected_item)
565+
selected_image = self.item.metadata.images[selected_image_index]
566+
try:
567+
self.delete_cover_art_for_item(self.item, selected_image)
568+
except CoverArtImageError as e:
569+
log.error("Can't delete image: %s", e)
570+
return
571+
572+
def delete_cover_art_for_item(self, item, selected_image):
573+
metadata = item.metadata
574+
if not metadata or not metadata.images:
575+
return
576+
577+
debug_info = "Deleting %r from %r"
578+
579+
if isinstance(item, Album):
580+
item.enable_update_metadata_images(False)
581+
for track in item.tracks:
582+
track.enable_update_metadata_images(False)
583+
image_delete(track, selected_image)
584+
for file in item.iterfiles():
585+
image_delete(file, selected_image)
586+
file.update(signal=False)
587+
for track in item.tracks:
588+
track.enable_update_metadata_images(True)
589+
item.enable_update_metadata_images(True)
590+
item.update(update_tracks=False)
591+
elif isinstance(item, FileListItem):
592+
parents = set()
593+
item.enable_update_metadata_images(False)
594+
image_delete(item, selected_image)
595+
for file in item.iterfiles():
596+
for parent in iter_file_parents(file):
597+
parent.enable_update_metadata_images(False)
598+
parents.add(parent)
599+
image_delete(file, selected_image)
600+
file.update(signal=False)
601+
for parent in parents:
602+
image_delete(parent, selected_image)
603+
parent.enable_update_metadata_images(True)
604+
if isinstance(parent, Album):
605+
parent.update(update_tracks=False)
606+
else:
607+
parent.update()
608+
item.enable_update_metadata_images(True)
609+
item.update()
610+
elif isinstance(item, File):
611+
image_delete(item, selected_image)
612+
item.update()
613+
else:
614+
debug_info = "Dropping %r to %r is not handled"
615+
616+
log.debug(debug_info, selected_image, item)
617+
548618
def _try_load_remote_image(self, url, data):
549619
coverartimage = CoverArtImage(
550620
url=url.toString(),
@@ -634,6 +704,13 @@ def contextMenuEvent(self, event):
634704
show_more_details_action.triggered.connect(self.show_cover_art_info)
635705
menu.addAction(show_more_details_action)
636706

707+
delete_cover_art_action = QtGui.QAction(_("Delete cover art"), self.parent)
708+
if self.item and self.item.can_show_coverart and self.item.metadata.images:
709+
delete_cover_art_action.triggered.connect(self.delete_cover_art)
710+
else:
711+
delete_cover_art_action.setEnabled(False)
712+
menu.addAction(delete_cover_art_action)
713+
637714
if self.orig_cover_art.isVisible():
638715
name = _("Keep original cover art")
639716
use_orig_value_action = QtGui.QAction(name, self.parent)

picard/util/imagelist.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ def __init__(self, iterable=()):
3333
self._images = list(iterable)
3434
self._hash_dict = {}
3535
self._changed = True
36+
self._deleted = False
3637

3738
def __len__(self):
3839
return len(self._images)
@@ -50,6 +51,7 @@ def __delitem__(self, index):
5051

5152
def insert(self, index, value):
5253
self._changed = True
54+
self._deleted = False
5355
return self._images.insert(index, value)
5456

5557
def __repr__(self):
@@ -95,6 +97,11 @@ def strip_front_images(self):
9597
self._images = [image for image in self._images if not image.is_front_image()]
9698
self._changed = True
9799

100+
def strip_selected_image(self, image):
101+
if image in self._images:
102+
self._images.remove(image)
103+
self._deleted = not self._images
104+
98105
def hash_dict(self):
99106
if self._changed:
100107
self._hash_dict = {img.datahash.hash(): img for img in self._images}

0 commit comments

Comments
 (0)