Skip to content

Commit 3c46364

Browse files
committed
PICARD-187: Support for manually removing cover art
PICARD-187: Support for manually removing cover art finishing PICARD-187
1 parent 1222821 commit 3c46364

File tree

7 files changed

+116
-3
lines changed

7 files changed

+116
-3
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: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,11 @@ def open_release_page(self):
349349
lookup.album_lookup(self.release)
350350

351351

352+
def image_delete(obj, image):
353+
obj.metadata.images.strip_selected_image(image)
354+
obj.metadata_images_changed.emit()
355+
356+
352357
def set_image_replace(obj, coverartimage):
353358
obj.metadata.images.strip_front_images()
354359
obj.metadata.images.append(coverartimage)
@@ -457,10 +462,11 @@ def update_metadata(self):
457462
orig_metadata = self.item.orig_metadata
458463

459464
if not metadata or not metadata.images:
460-
self.cover_art.set_metadata(orig_metadata)
465+
self.cover_art.set_metadata(None)
466+
self.orig_cover_art.set_metadata(None)
461467
else:
462468
self.cover_art.set_metadata(metadata)
463-
self.orig_cover_art.set_metadata(orig_metadata)
469+
self.orig_cover_art.set_metadata(orig_metadata)
464470
self.update_display()
465471

466472
def fetch_remote_image(self, url, fallback_data=None):
@@ -547,6 +553,71 @@ def load_remote_image(self, url, data):
547553
log.warning("Can't load image: %s", e)
548554
return
549555

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

710+
delete_cover_art_action = QtGui.QAction(_("Delete cover art"), self.parent)
711+
if self.item and self.item.can_show_coverart and self.item.metadata.images:
712+
delete_cover_art_action.triggered.connect(self.delete_cover_art)
713+
else:
714+
delete_cover_art_action.setEnabled(False)
715+
menu.addAction(delete_cover_art_action)
716+
639717
if self.orig_cover_art.isVisible():
640718
name = _("Keep original cover art")
641719
use_orig_value_action = QtGui.QAction(name, self.parent)

picard/util/imagelist.py

Lines changed: 11 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,12 +97,21 @@ 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}
101108
self._changed = False
102109
return self._hash_dict
103110

111+
@property
112+
def deleted(self):
113+
return self._deleted
114+
104115

105116
class ImageListState:
106117
def __init__(self):

0 commit comments

Comments
 (0)