mirror of
https://github.com/fergalmoran/picard.git
synced 2026-01-09 01:54:00 +00:00
Merge pull request #1698 from phw/PICARD-2028-optimize-coverart-update
PICARD-2028: Optimize and speedup coverart updates
This commit is contained in:
@@ -113,6 +113,7 @@ class AlbumArtist(DataObject):
|
||||
|
||||
class Album(DataObject, Item):
|
||||
|
||||
metadata_images_changed = QtCore.pyqtSignal()
|
||||
release_group_loaded = QtCore.pyqtSignal()
|
||||
|
||||
def __init__(self, album_id, discid=None):
|
||||
@@ -131,6 +132,7 @@ class Album(DataObject, Item):
|
||||
self._discids.add(discid)
|
||||
self._after_load_callbacks = []
|
||||
self.unmatched_files = Cluster(_("Unmatched Files"), special=True, related_album=self, hide_if_empty=True)
|
||||
self.unmatched_files.metadata_images_changed.connect(self.update_metadata_images)
|
||||
self.status = None
|
||||
self._album_artists = []
|
||||
self.update_metadata_images_enabled = True
|
||||
@@ -166,7 +168,7 @@ class Album(DataObject, Item):
|
||||
if track_discids:
|
||||
track.metadata['musicbrainz_discid'] = track_discids
|
||||
track.update()
|
||||
for file in track.linked_files:
|
||||
for file in track.files:
|
||||
file.metadata['musicbrainz_discid'] = track_discids
|
||||
file.update()
|
||||
|
||||
@@ -376,6 +378,7 @@ class Album(DataObject, Item):
|
||||
self.enable_update_metadata_images(False)
|
||||
for track in self._new_tracks:
|
||||
track.orig_metadata.copy(track.metadata)
|
||||
track.metadata_images_changed.connect(self.update_metadata_images)
|
||||
|
||||
# Prepare parser for user's script
|
||||
for s_name, s_text in enabled_tagger_scripts_texts():
|
||||
@@ -396,11 +399,11 @@ class Album(DataObject, Item):
|
||||
self._new_metadata.strip_whitespace()
|
||||
|
||||
for track in self.tracks:
|
||||
track.metadata_images_changed.connect(self.update_metadata_images)
|
||||
for file in list(track.linked_files):
|
||||
for file in list(track.files):
|
||||
file.move(self.unmatched_files)
|
||||
self.metadata = self._new_metadata
|
||||
self.orig_metadata.copy(self.metadata)
|
||||
self.orig_metadata.images.clear()
|
||||
self.tracks = self._new_tracks
|
||||
del self._new_metadata
|
||||
del self._new_tracks
|
||||
@@ -513,12 +516,10 @@ class Album(DataObject, Item):
|
||||
self._files += 1
|
||||
self.update(update_tracks=False)
|
||||
add_metadata_images(self, [file])
|
||||
file.metadata_images_changed.connect(self.update_metadata_images)
|
||||
|
||||
def _remove_file(self, track, file):
|
||||
self._files -= 1
|
||||
self.update(update_tracks=False)
|
||||
file.metadata_images_changed.disconnect(self.update_metadata_images)
|
||||
remove_metadata_images(self, [file])
|
||||
|
||||
def _match_files(self, files, threshold=0):
|
||||
@@ -632,7 +633,7 @@ class Album(DataObject, Item):
|
||||
def is_modified(self):
|
||||
if self.tracks:
|
||||
for track in self.tracks:
|
||||
for file in track.linked_files:
|
||||
for file in track.files:
|
||||
if not file.is_saved():
|
||||
return True
|
||||
return False
|
||||
@@ -640,7 +641,7 @@ class Album(DataObject, Item):
|
||||
def get_num_unsaved_files(self):
|
||||
count = 0
|
||||
for track in self.tracks:
|
||||
for file in track.linked_files:
|
||||
for file in track.files:
|
||||
if not file.is_saved():
|
||||
count += 1
|
||||
return count
|
||||
@@ -718,9 +719,9 @@ class Album(DataObject, Item):
|
||||
if not self.update_metadata_images_enabled:
|
||||
return
|
||||
|
||||
update_metadata_images(self)
|
||||
|
||||
self.update(False)
|
||||
if update_metadata_images(self):
|
||||
self.update(False)
|
||||
self.metadata_images_changed.emit()
|
||||
|
||||
def keep_original_images(self):
|
||||
self.enable_update_metadata_images(False)
|
||||
@@ -746,7 +747,7 @@ class NatAlbum(Album):
|
||||
for track in self.tracks:
|
||||
if old_album_title == track.metadata["album"]:
|
||||
track.metadata["album"] = self.metadata["album"]
|
||||
for file in track.linked_files:
|
||||
for file in track.files:
|
||||
track.update_file_metadata(file)
|
||||
self.enable_update_metadata_images(True)
|
||||
super().update(update_tracks)
|
||||
|
||||
@@ -67,21 +67,26 @@ from picard.util.imagelist import (
|
||||
)
|
||||
from picard.util.progresscheckpoints import ProgressCheckpoints
|
||||
|
||||
from picard.ui.item import Item
|
||||
from picard.ui.item import (
|
||||
FileListItem,
|
||||
Item,
|
||||
)
|
||||
|
||||
|
||||
class FileList(QtCore.QObject, Item):
|
||||
class FileList(QtCore.QObject, FileListItem):
|
||||
|
||||
metadata_images_changed = QtCore.pyqtSignal()
|
||||
|
||||
def __init__(self, files=None):
|
||||
QtCore.QObject.__init__(self)
|
||||
FileListItem.__init__(self, files)
|
||||
self.metadata = Metadata()
|
||||
self.orig_metadata = Metadata()
|
||||
self.files = files or []
|
||||
self.update_metadata_images_enabled = True
|
||||
for file in self.files:
|
||||
if self.can_show_coverart:
|
||||
file.metadata_images_changed.connect(self.update_metadata_images)
|
||||
update_metadata_images(self)
|
||||
if self.files:
|
||||
update_metadata_images(self)
|
||||
|
||||
def iterfiles(self, save=False):
|
||||
for file in self.files:
|
||||
@@ -94,20 +99,6 @@ class FileList(QtCore.QObject, Item):
|
||||
def can_show_coverart(self):
|
||||
return True
|
||||
|
||||
def enable_update_metadata_images(self, enabled):
|
||||
self.update_metadata_images_enabled = enabled
|
||||
|
||||
def update_metadata_images(self):
|
||||
if self.update_metadata_images_enabled and self.can_show_coverart:
|
||||
update_metadata_images(self)
|
||||
|
||||
def keep_original_images(self):
|
||||
self.enable_update_metadata_images(False)
|
||||
for file in list(self.files):
|
||||
file.keep_original_images()
|
||||
self.enable_update_metadata_images(True)
|
||||
self.update_metadata_images()
|
||||
|
||||
|
||||
class Cluster(FileList):
|
||||
|
||||
@@ -375,13 +366,6 @@ class Cluster(FileList):
|
||||
|
||||
yield album_name, artist_name, (files[i] for i in album)
|
||||
|
||||
def enable_update_metadata_images(self, enabled):
|
||||
self.update_metadata_images_enabled = enabled
|
||||
|
||||
def update_metadata_images(self):
|
||||
if self.update_metadata_images_enabled and self.can_show_coverart:
|
||||
update_metadata_images(self)
|
||||
|
||||
|
||||
class UnclusteredFiles(Cluster):
|
||||
|
||||
|
||||
@@ -251,6 +251,7 @@ class File(QtCore.QObject, Item):
|
||||
if tag in preserved_tags or tag in PRESERVED_TAGS:
|
||||
saved_metadata[tag] = values
|
||||
deleted_tags = self.metadata.deleted_tags
|
||||
images_changed = self.metadata.images != metadata.images
|
||||
self.metadata.copy(metadata)
|
||||
for info in FILE_INFO_TAGS:
|
||||
metadata[info] = self.orig_metadata[info]
|
||||
@@ -262,12 +263,14 @@ class File(QtCore.QObject, Item):
|
||||
|
||||
if acoustid and "acoustid_id" not in metadata.deleted_tags:
|
||||
self.metadata["acoustid_id"] = acoustid
|
||||
self.metadata_images_changed.emit()
|
||||
if images_changed:
|
||||
self.metadata_images_changed.emit()
|
||||
|
||||
def keep_original_images(self):
|
||||
self.metadata.images = self.orig_metadata.images.copy()
|
||||
self.update(signal=False)
|
||||
self.metadata_images_changed.emit()
|
||||
if self.metadata.images != self.orig_metadata.images:
|
||||
self.metadata.images = self.orig_metadata.images.copy()
|
||||
self.update(signal=False)
|
||||
self.metadata_images_changed.emit()
|
||||
|
||||
def has_error(self):
|
||||
return self.state == File.ERROR
|
||||
@@ -365,6 +368,7 @@ class File(QtCore.QObject, Item):
|
||||
temp_info = {}
|
||||
for info in FILE_INFO_TAGS:
|
||||
temp_info[info] = self.orig_metadata[info]
|
||||
images_changed = self.orig_metadata.images != self.new_metadata.images
|
||||
# Data is copied from New to Original because New may be
|
||||
# a subclass to handle id3v23
|
||||
if config.setting["clear_existing_tags"]:
|
||||
@@ -381,7 +385,8 @@ class File(QtCore.QObject, Item):
|
||||
self.clear_errors()
|
||||
self.clear_pending()
|
||||
self._add_path_to_metadata(self.orig_metadata)
|
||||
self.metadata_images_changed.emit()
|
||||
if images_changed:
|
||||
self.metadata_images_changed.emit()
|
||||
|
||||
# run post save hook
|
||||
run_file_post_save_processors(self)
|
||||
|
||||
@@ -750,7 +750,7 @@ class Tagger(QtWidgets.QApplication):
|
||||
elif isinstance(obj, NonAlbumTrack):
|
||||
self.remove_nat(obj)
|
||||
elif isinstance(obj, Track):
|
||||
files.extend(obj.linked_files)
|
||||
files.extend(obj.files)
|
||||
elif isinstance(obj, Album):
|
||||
self.window.set_statusbar_message(
|
||||
N_("Removing album %(id)s: %(artist)s - %(album)s"),
|
||||
|
||||
@@ -70,14 +70,12 @@ from picard.script import (
|
||||
enabled_tagger_scripts_texts,
|
||||
)
|
||||
from picard.util.imagelist import (
|
||||
ImageList,
|
||||
add_metadata_images,
|
||||
remove_metadata_images,
|
||||
update_metadata_images,
|
||||
)
|
||||
from picard.util.textencoding import asciipunct
|
||||
|
||||
from picard.ui.item import Item
|
||||
from picard.ui.item import FileListItem
|
||||
|
||||
|
||||
_TRANSLATE_TAGS = {
|
||||
@@ -137,39 +135,44 @@ class TrackArtist(DataObject):
|
||||
super().__init__(ta_id)
|
||||
|
||||
|
||||
class Track(DataObject, Item):
|
||||
class Track(DataObject, FileListItem):
|
||||
|
||||
metadata_images_changed = QtCore.pyqtSignal()
|
||||
|
||||
def __init__(self, track_id, album=None):
|
||||
DataObject.__init__(self, track_id)
|
||||
self.album = album
|
||||
self.linked_files = []
|
||||
self.num_linked_files = 0
|
||||
FileListItem.__init__(self)
|
||||
self.metadata = Metadata()
|
||||
self.orig_metadata = Metadata()
|
||||
self.album = album
|
||||
self.num_linked_files = 0
|
||||
self.scripted_metadata = Metadata()
|
||||
self._track_artists = []
|
||||
|
||||
def __repr__(self):
|
||||
return '<Track %s %r>' % (self.id, self.metadata["title"])
|
||||
|
||||
@property
|
||||
def linked_files(self):
|
||||
"""For backward compatibility with old code in plugins"""
|
||||
return self.files
|
||||
|
||||
def add_file(self, file):
|
||||
if file not in self.linked_files:
|
||||
if file not in self.files:
|
||||
track_will_expand = self.num_linked_files == 1
|
||||
self.linked_files.append(file)
|
||||
self.files.append(file)
|
||||
self.num_linked_files += 1
|
||||
self.update_file_metadata(file)
|
||||
add_metadata_images(self, [file])
|
||||
self.album._add_file(self, file)
|
||||
file.metadata_images_changed.connect(self.update_orig_metadata_images)
|
||||
file.metadata_images_changed.connect(self.update_metadata_images)
|
||||
run_file_post_addition_to_track_processors(self, file)
|
||||
if track_will_expand:
|
||||
# Files get expanded, ensure the existing item renders correctly
|
||||
self.linked_files[0].update_item()
|
||||
self.files[0].update_item()
|
||||
|
||||
def update_file_metadata(self, file):
|
||||
if file not in self.linked_files:
|
||||
if file not in self.files:
|
||||
return
|
||||
# Run the scripts for the file to allow usage of
|
||||
# file specific metadata and variables
|
||||
@@ -191,12 +194,12 @@ class Track(DataObject, Item):
|
||||
self.update()
|
||||
|
||||
def remove_file(self, file):
|
||||
if file not in self.linked_files:
|
||||
if file not in self.files:
|
||||
return
|
||||
self.linked_files.remove(file)
|
||||
self.files.remove(file)
|
||||
self.num_linked_files -= 1
|
||||
file.metadata_images_changed.disconnect(self.update_metadata_images)
|
||||
file.copy_metadata(file.orig_metadata, preserve_deleted=False)
|
||||
file.metadata_images_changed.disconnect(self.update_orig_metadata_images)
|
||||
self.album._remove_file(self, file)
|
||||
remove_metadata_images(self, [file])
|
||||
run_file_post_removal_from_track_processors(self, file)
|
||||
@@ -218,23 +221,19 @@ class Track(DataObject, Item):
|
||||
if self.item:
|
||||
self.item.update()
|
||||
|
||||
def iterfiles(self, save=False):
|
||||
for file in self.linked_files:
|
||||
yield file
|
||||
|
||||
def is_linked(self):
|
||||
return self.num_linked_files > 0
|
||||
|
||||
def can_save(self):
|
||||
"""Return if this object can be saved."""
|
||||
for file in self.linked_files:
|
||||
for file in self.files:
|
||||
if file.can_save():
|
||||
return True
|
||||
return False
|
||||
|
||||
def can_remove(self):
|
||||
"""Return if this object can be removed."""
|
||||
for file in self.linked_files:
|
||||
for file in self.files:
|
||||
if file.can_remove():
|
||||
return True
|
||||
return False
|
||||
@@ -254,7 +253,7 @@ class Track(DataObject, Item):
|
||||
elif column in m:
|
||||
return m[column]
|
||||
elif self.num_linked_files == 1:
|
||||
return self.linked_files[0].column(column)
|
||||
return self.files[0].column(column)
|
||||
|
||||
def is_video(self):
|
||||
return self.metadata['~video'] == '1'
|
||||
@@ -348,18 +347,6 @@ class Track(DataObject, Item):
|
||||
genre = [join_genres.join(genre)]
|
||||
self.metadata['genre'] = genre
|
||||
|
||||
def update_orig_metadata_images(self):
|
||||
update_metadata_images(self)
|
||||
|
||||
def keep_original_images(self):
|
||||
for file in self.linked_files:
|
||||
file.keep_original_images()
|
||||
self.update_orig_metadata_images()
|
||||
if self.linked_files:
|
||||
self.metadata.images = self.orig_metadata.images.copy()
|
||||
else:
|
||||
self.metadata.images = ImageList()
|
||||
|
||||
|
||||
class NonAlbumTrack(Track):
|
||||
|
||||
@@ -410,7 +397,7 @@ class NonAlbumTrack(Track):
|
||||
return
|
||||
try:
|
||||
self._parse_recording(recording)
|
||||
for file in self.linked_files:
|
||||
for file in self.files:
|
||||
self.update_file_metadata(file)
|
||||
except Exception:
|
||||
self._set_error(traceback.format_exc())
|
||||
|
||||
@@ -48,7 +48,7 @@ from picard import (
|
||||
log,
|
||||
)
|
||||
from picard.album import Album
|
||||
from picard.cluster import FileList
|
||||
from picard.cluster import Cluster
|
||||
from picard.const import MAX_COVERS_TO_STACK
|
||||
from picard.coverart.image import (
|
||||
CoverArtImage,
|
||||
@@ -59,6 +59,7 @@ from picard.track import Track
|
||||
from picard.util import imageinfo
|
||||
from picard.util.lrucache import LRUCache
|
||||
|
||||
from picard.ui.item import FileListItem
|
||||
from picard.ui.widgets import ActiveLabel
|
||||
|
||||
|
||||
@@ -278,10 +279,22 @@ class CoverArtThumbnail(ActiveLabel):
|
||||
def set_image_replace(obj, coverartimage):
|
||||
obj.metadata.images.strip_front_images()
|
||||
obj.metadata.images.append(coverartimage)
|
||||
obj.metadata_images_changed.emit()
|
||||
|
||||
|
||||
def set_image_append(obj, coverartimage):
|
||||
obj.metadata.images.append(coverartimage)
|
||||
obj.metadata_images_changed.emit()
|
||||
|
||||
|
||||
def iter_file_parents(file):
|
||||
parent = file.parent
|
||||
if parent:
|
||||
yield parent
|
||||
if isinstance(parent, Track) and parent.album:
|
||||
yield parent.album
|
||||
elif isinstance(parent, Cluster) and parent.related_album:
|
||||
yield parent.related_album
|
||||
|
||||
|
||||
HTML_IMG_SRC_REGEX = re.compile(r'<img .*?src="(.*?)"', re.UNICODE)
|
||||
@@ -347,17 +360,35 @@ class CoverArtBox(QtWidgets.QGroupBox):
|
||||
self.cover_art_label.setText(_('New Cover Art'))
|
||||
self.orig_cover_art_label.setText(_('Original Cover Art'))
|
||||
|
||||
def show(self):
|
||||
self.update_display(True)
|
||||
super().show()
|
||||
def set_item(self, item):
|
||||
if not item.can_show_coverart:
|
||||
return
|
||||
|
||||
if self.item and hasattr(self.item, 'metadata_images_changed'):
|
||||
self.item.metadata_images_changed.disconnect(self.update_metadata)
|
||||
self.item = item
|
||||
if hasattr(self.item, 'metadata_images_changed'):
|
||||
self.item.metadata_images_changed.connect(self.update_metadata)
|
||||
self.update_metadata()
|
||||
|
||||
def update_metadata(self):
|
||||
if not self.item:
|
||||
return
|
||||
|
||||
metadata = self.item.metadata
|
||||
orig_metadata = None
|
||||
if isinstance(self.item, Track):
|
||||
track = self.item
|
||||
if track.num_linked_files == 1:
|
||||
orig_metadata = track.files[0].orig_metadata
|
||||
elif hasattr(self.item, 'orig_metadata'):
|
||||
orig_metadata = self.item.orig_metadata
|
||||
|
||||
def set_metadata(self, metadata, orig_metadata, item):
|
||||
if not metadata or not metadata.images:
|
||||
self.cover_art.set_metadata(orig_metadata)
|
||||
else:
|
||||
self.cover_art.set_metadata(metadata)
|
||||
self.orig_cover_art.set_metadata(orig_metadata)
|
||||
self.item = item
|
||||
self.update_display()
|
||||
|
||||
def fetch_remote_image(self, url, fallback_data=None):
|
||||
@@ -461,60 +492,49 @@ class CoverArtBox(QtWidgets.QGroupBox):
|
||||
set_image = set_image_append
|
||||
debug_info = "Appending dropped %r to %r"
|
||||
|
||||
update = True
|
||||
if isinstance(self.item, Album):
|
||||
album = self.item
|
||||
album.enable_update_metadata_images(False)
|
||||
set_image(album, coverartimage)
|
||||
for track in album.tracks:
|
||||
track.enable_update_metadata_images(False)
|
||||
set_image(track, coverartimage)
|
||||
track.metadata_images_changed.emit()
|
||||
for file in album.iterfiles():
|
||||
set_image(file, coverartimage)
|
||||
file.metadata_images_changed.emit()
|
||||
file.update(signal=False)
|
||||
for track in album.tracks:
|
||||
track.enable_update_metadata_images(True)
|
||||
album.enable_update_metadata_images(True)
|
||||
album.update_metadata_images()
|
||||
album.update(False)
|
||||
elif isinstance(self.item, FileList):
|
||||
cluster = self.item
|
||||
cluster.enable_update_metadata_images(False)
|
||||
set_image(cluster, coverartimage)
|
||||
for file in cluster.iterfiles():
|
||||
album.update(update_tracks=False)
|
||||
elif isinstance(self.item, FileListItem):
|
||||
parents = set()
|
||||
filelist = self.item
|
||||
filelist.enable_update_metadata_images(False)
|
||||
set_image(filelist, coverartimage)
|
||||
for file in filelist.iterfiles():
|
||||
for parent in iter_file_parents(file):
|
||||
parent.enable_update_metadata_images(False)
|
||||
parents.add(parent)
|
||||
set_image(file, coverartimage)
|
||||
file.metadata_images_changed.emit()
|
||||
file.update(signal=False)
|
||||
cluster.enable_update_metadata_images(True)
|
||||
cluster.update_metadata_images()
|
||||
cluster.update()
|
||||
elif isinstance(self.item, Track):
|
||||
track = self.item
|
||||
track.album.enable_update_metadata_images(False)
|
||||
set_image(track, coverartimage)
|
||||
track.metadata_images_changed.emit()
|
||||
for file in track.iterfiles():
|
||||
set_image(file, coverartimage)
|
||||
file.metadata_images_changed.emit()
|
||||
file.update(signal=False)
|
||||
track.album.enable_update_metadata_images(True)
|
||||
track.album.update_metadata_images()
|
||||
track.album.update(False)
|
||||
track.update()
|
||||
for parent in parents:
|
||||
set_image(parent, coverartimage)
|
||||
parent.enable_update_metadata_images(True)
|
||||
if isinstance(parent, Album):
|
||||
parent.update(update_tracks=False)
|
||||
else:
|
||||
parent.update()
|
||||
filelist.enable_update_metadata_images(True)
|
||||
filelist.update()
|
||||
elif isinstance(self.item, File):
|
||||
file = self.item
|
||||
set_image(file, coverartimage)
|
||||
file.metadata_images_changed.emit()
|
||||
file.update()
|
||||
else:
|
||||
debug_info = "Dropping %r to %r is not handled"
|
||||
update = False
|
||||
|
||||
log.debug(debug_info, coverartimage, self.item)
|
||||
|
||||
if update:
|
||||
self.cover_art.set_metadata(self.item.metadata)
|
||||
self.show()
|
||||
|
||||
def choose_local_file(self):
|
||||
file_chooser = QtWidgets.QFileDialog(self)
|
||||
file_chooser.setNameFilters([
|
||||
|
||||
@@ -399,7 +399,7 @@ class TrackInfoDialog(InfoDialog):
|
||||
tabWidget.setTabText(tab_index, _("&Info"))
|
||||
text = ngettext("%i file in this track", "%i files in this track",
|
||||
track.num_linked_files) % track.num_linked_files
|
||||
info_files = [format_file_info(file_) for file_ in track.linked_files]
|
||||
info_files = [format_file_info(file_) for file_ in track.files]
|
||||
text += '<hr />' + '<hr />'.join(info_files)
|
||||
self.ui.info.setText(text)
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
from picard import log
|
||||
from picard.util.imagelist import update_metadata_images
|
||||
|
||||
|
||||
class Item(object):
|
||||
@@ -97,3 +98,31 @@ class Item(object):
|
||||
|
||||
def clear_errors(self):
|
||||
self._errors = []
|
||||
|
||||
|
||||
class FileListItem(Item):
|
||||
|
||||
def __init__(self, files=None):
|
||||
super().__init__()
|
||||
self.files = files or []
|
||||
self.update_metadata_images_enabled = True
|
||||
|
||||
def iterfiles(self, save=False):
|
||||
for file in self.files:
|
||||
yield file
|
||||
|
||||
def enable_update_metadata_images(self, enabled):
|
||||
self.update_metadata_images_enabled = enabled
|
||||
|
||||
def update_metadata_images(self):
|
||||
if self.update_metadata_images_enabled and self.can_show_coverart:
|
||||
if update_metadata_images(self):
|
||||
self.metadata_images_changed.emit()
|
||||
|
||||
def keep_original_images(self):
|
||||
self.enable_update_metadata_images(False)
|
||||
for file in list(self.files):
|
||||
if file.can_show_coverart:
|
||||
file.keep_original_images()
|
||||
self.enable_update_metadata_images(True)
|
||||
self.update_metadata_images()
|
||||
|
||||
@@ -1018,7 +1018,7 @@ class TrackItem(TreeItem):
|
||||
def update(self, update_album=True, update_files=True):
|
||||
track = self.obj
|
||||
if track.num_linked_files == 1:
|
||||
file = track.linked_files[0]
|
||||
file = track.files[0]
|
||||
file.item = self
|
||||
color = TrackItem.track_colors[file.state]
|
||||
bgcolor = get_match_color(file.similarity, TreeItem.base_color)
|
||||
@@ -1053,14 +1053,14 @@ class TrackItem(TreeItem):
|
||||
oldnum = newnum
|
||||
for i in range(oldnum): # update existing items
|
||||
item = self.child(i)
|
||||
file = track.linked_files[i]
|
||||
file = track.files[i]
|
||||
item.obj = file
|
||||
file.item = item
|
||||
item.update(update_track=False)
|
||||
if newnum > oldnum: # add new items
|
||||
items = []
|
||||
for i in range(newnum - 1, oldnum - 1, -1):
|
||||
item = FileItem(track.linked_files[i], False)
|
||||
item = FileItem(track.files[i], False)
|
||||
item.update(update_track=False)
|
||||
items.append(item)
|
||||
self.addChildren(items)
|
||||
|
||||
@@ -1077,7 +1077,7 @@ class MainWindow(QtWidgets.QMainWindow, PreserveGeometry):
|
||||
def show_more_tracks(self):
|
||||
obj = self.selected_objects[0]
|
||||
if isinstance(obj, Track):
|
||||
obj = obj.linked_files[0]
|
||||
obj = obj.files[0]
|
||||
dialog = TrackSearchDialog(self)
|
||||
dialog.load_similar_tracks(obj)
|
||||
dialog.exec_()
|
||||
@@ -1168,8 +1168,6 @@ class MainWindow(QtWidgets.QMainWindow, PreserveGeometry):
|
||||
|
||||
self.update_actions()
|
||||
|
||||
metadata = None
|
||||
orig_metadata = None
|
||||
obj = None
|
||||
|
||||
# Clear any existing status bar messages
|
||||
@@ -1181,8 +1179,6 @@ class MainWindow(QtWidgets.QMainWindow, PreserveGeometry):
|
||||
if len(objects) == 1:
|
||||
obj = list(objects)[0]
|
||||
if isinstance(obj, File):
|
||||
metadata = obj.metadata
|
||||
orig_metadata = obj.orig_metadata
|
||||
if obj.state == obj.ERROR:
|
||||
msg = N_("%(filename)s (error: %(error)s)")
|
||||
mparms = {
|
||||
@@ -1196,10 +1192,8 @@ class MainWindow(QtWidgets.QMainWindow, PreserveGeometry):
|
||||
}
|
||||
self.set_statusbar_message(msg, mparms, echo=None, history=None)
|
||||
elif isinstance(obj, Track):
|
||||
metadata = obj.metadata
|
||||
if obj.num_linked_files == 1:
|
||||
file = obj.linked_files[0]
|
||||
orig_metadata = file.orig_metadata
|
||||
file = obj.files[0]
|
||||
if file.has_error():
|
||||
msg = N_("%(filename)s (%(similarity)d%%) (error: %(error)s)")
|
||||
mparms = {
|
||||
@@ -1215,22 +1209,15 @@ class MainWindow(QtWidgets.QMainWindow, PreserveGeometry):
|
||||
}
|
||||
self.set_statusbar_message(msg, mparms, echo=None,
|
||||
history=None)
|
||||
elif isinstance(obj, Album):
|
||||
metadata = obj.metadata
|
||||
orig_metadata = obj.orig_metadata
|
||||
elif obj.can_show_coverart:
|
||||
metadata = obj.metadata
|
||||
else:
|
||||
elif new_selection:
|
||||
# Create a temporary file list which allows changing cover art for all selected files
|
||||
files = self.tagger.get_files_from_objects(objects)
|
||||
obj = FileList(files)
|
||||
metadata = obj.metadata
|
||||
orig_metadata = obj.orig_metadata
|
||||
|
||||
if new_selection:
|
||||
self.metadata_box.selection_dirty = True
|
||||
self.cover_art_box.set_item(obj)
|
||||
self.metadata_box.update(drop_album_caches=drop_album_caches)
|
||||
self.cover_art_box.set_metadata(metadata, orig_metadata, obj)
|
||||
self.selection_updated.emit(objects)
|
||||
|
||||
def refresh_metadatabox(self):
|
||||
|
||||
@@ -395,7 +395,7 @@ class MetadataBox(QtWidgets.QTableWidget):
|
||||
track_albums = set()
|
||||
for file in self.files:
|
||||
objects = [file]
|
||||
if file.parent in self.tracks and len(self.files & set(file.parent.linked_files)) == 1:
|
||||
if file.parent in self.tracks and len(self.files & set(file.parent.files)) == 1:
|
||||
objects.append(file.parent)
|
||||
file_tracks.append(file.parent)
|
||||
track_albums.add(file.parent.album)
|
||||
@@ -503,7 +503,7 @@ class MetadataBox(QtWidgets.QTableWidget):
|
||||
files.add(obj)
|
||||
elif isinstance(obj, Track):
|
||||
tracks.add(obj)
|
||||
files.update(obj.linked_files)
|
||||
files.update(obj.files)
|
||||
elif isinstance(obj, Cluster) and obj.can_edit_tags():
|
||||
objects.add(obj)
|
||||
files.update(obj.files)
|
||||
@@ -511,7 +511,7 @@ class MetadataBox(QtWidgets.QTableWidget):
|
||||
objects.add(obj)
|
||||
tracks.update(obj.tracks)
|
||||
for track in obj.tracks:
|
||||
files.update(track.linked_files)
|
||||
files.update(track.files)
|
||||
objects.update(files)
|
||||
objects.update(tracks)
|
||||
self.selection_dirty = False
|
||||
|
||||
@@ -95,7 +95,7 @@ class RatingWidget(QtWidgets.QWidget):
|
||||
track = self._track
|
||||
rating = str(self._rating)
|
||||
track.metadata["~rating"] = rating
|
||||
for file in track.linked_files:
|
||||
for file in track.files:
|
||||
file.metadata["~rating"] = rating
|
||||
if config.setting["submit_ratings"]:
|
||||
ratings = {("recording", track.id): self._rating}
|
||||
|
||||
@@ -82,4 +82,4 @@ class ScriptsMenu(QtWidgets.QMenu):
|
||||
yield from self._get_metadata_objects(obj.tracks)
|
||||
yield from self._get_metadata_objects(obj.unmatched_files.iterfiles())
|
||||
if isinstance(obj, Track):
|
||||
yield from self._get_metadata_objects(obj.linked_files)
|
||||
yield from self._get_metadata_objects(obj.files)
|
||||
|
||||
@@ -53,6 +53,8 @@ class ImageList(MutableSequence):
|
||||
return sorted(self, key=lambda image: image.normalized_types())
|
||||
|
||||
def __eq__(self, other):
|
||||
if len(self) != len(other):
|
||||
return False
|
||||
return self._sorted() == other._sorted()
|
||||
|
||||
def copy(self):
|
||||
@@ -124,22 +126,29 @@ def _process_images(state, src_obj):
|
||||
|
||||
|
||||
def _update_state(obj, state):
|
||||
changed = False
|
||||
for src_obj in state.sources:
|
||||
_process_images(state, src_obj)
|
||||
|
||||
if state.update_new_metadata:
|
||||
obj.metadata.images = ImageList(state.new_images)
|
||||
updated_images = ImageList(state.new_images)
|
||||
changed |= updated_images != obj.metadata.images
|
||||
obj.metadata.images = updated_images
|
||||
obj.metadata.has_common_images = state.has_common_new_images
|
||||
|
||||
if state.update_orig_metadata:
|
||||
obj.orig_metadata.images = ImageList(state.orig_images)
|
||||
updated_images = ImageList(state.orig_images)
|
||||
changed |= updated_images != obj.orig_metadata.images
|
||||
obj.orig_metadata.images = updated_images
|
||||
obj.orig_metadata.has_common_images = state.has_common_orig_images
|
||||
|
||||
return changed
|
||||
|
||||
|
||||
# TODO: use functools.singledispatch when py3 is supported
|
||||
def _get_state(obj):
|
||||
from picard.album import Album
|
||||
from picard.cluster import (Cluster, FileList)
|
||||
from picard.cluster import FileList
|
||||
from picard.track import Track
|
||||
|
||||
state = ImageListState()
|
||||
@@ -147,16 +156,13 @@ def _get_state(obj):
|
||||
if isinstance(obj, Album):
|
||||
for track in obj.tracks:
|
||||
state.sources.append(track)
|
||||
state.sources += track.linked_files
|
||||
state.sources += track.files
|
||||
state.sources += obj.unmatched_files.files
|
||||
state.update_new_metadata = True
|
||||
state.update_orig_metadata = True
|
||||
elif isinstance(obj, Track):
|
||||
state.sources = obj.linked_files
|
||||
state.update_orig_metadata = True
|
||||
elif isinstance(obj, Cluster):
|
||||
state.sources = obj.files
|
||||
state.update_new_metadata = True
|
||||
state.update_orig_metadata = True
|
||||
elif isinstance(obj, FileList):
|
||||
state.sources = obj.files
|
||||
state.update_new_metadata = True
|
||||
@@ -188,8 +194,10 @@ def update_metadata_images(obj):
|
||||
|
||||
Args:
|
||||
obj: A `Cluster`, `Album` or `Track` object with `metadata` property
|
||||
Returns:
|
||||
bool: True, if images where changed, False otherwise
|
||||
"""
|
||||
_update_state(obj, _get_state(obj))
|
||||
return _update_state(obj, _get_state(obj))
|
||||
|
||||
|
||||
def _add_images(metadata, added_images):
|
||||
|
||||
@@ -67,72 +67,72 @@ class UpdateMetadataImagesTest(PicardTestCase):
|
||||
def test_update_cluster_images(self):
|
||||
cluster = Cluster('Test')
|
||||
cluster.files = list(self.test_files)
|
||||
update_metadata_images(cluster)
|
||||
self.assertTrue(update_metadata_images(cluster))
|
||||
self.assertEqual(set(self.test_images), set(cluster.metadata.images))
|
||||
self.assertFalse(cluster.metadata.has_common_images)
|
||||
|
||||
cluster.files.remove(self.test_files[2])
|
||||
update_metadata_images(cluster)
|
||||
self.assertFalse(update_metadata_images(cluster))
|
||||
self.assertEqual(set(self.test_images), set(cluster.metadata.images))
|
||||
self.assertFalse(cluster.metadata.has_common_images)
|
||||
|
||||
cluster.files.remove(self.test_files[0])
|
||||
update_metadata_images(cluster)
|
||||
self.assertTrue(update_metadata_images(cluster))
|
||||
self.assertEqual(set(self.test_images[1:]), set(cluster.metadata.images))
|
||||
self.assertTrue(cluster.metadata.has_common_images)
|
||||
|
||||
cluster.files.append(self.test_files[2])
|
||||
update_metadata_images(cluster)
|
||||
self.assertFalse(update_metadata_images(cluster))
|
||||
self.assertEqual(set(self.test_images[1:]), set(cluster.metadata.images))
|
||||
self.assertTrue(cluster.metadata.has_common_images)
|
||||
|
||||
def test_update_track_images(self):
|
||||
track = Track('00000000-0000-0000-0000-000000000000')
|
||||
track.linked_files = list(self.test_files)
|
||||
update_metadata_images(track)
|
||||
track.files = list(self.test_files)
|
||||
self.assertTrue(update_metadata_images(track))
|
||||
self.assertEqual(set(self.test_images), set(track.orig_metadata.images))
|
||||
self.assertFalse(track.orig_metadata.has_common_images)
|
||||
|
||||
track.linked_files.remove(self.test_files[2])
|
||||
update_metadata_images(track)
|
||||
track.files.remove(self.test_files[2])
|
||||
self.assertFalse(update_metadata_images(track))
|
||||
self.assertEqual(set(self.test_images), set(track.orig_metadata.images))
|
||||
self.assertFalse(track.orig_metadata.has_common_images)
|
||||
|
||||
track.linked_files.remove(self.test_files[0])
|
||||
update_metadata_images(track)
|
||||
track.files.remove(self.test_files[0])
|
||||
self.assertTrue(update_metadata_images(track))
|
||||
self.assertEqual(set(self.test_images[1:]), set(track.orig_metadata.images))
|
||||
self.assertTrue(track.orig_metadata.has_common_images)
|
||||
|
||||
track.linked_files.append(self.test_files[2])
|
||||
update_metadata_images(track)
|
||||
track.files.append(self.test_files[2])
|
||||
self.assertFalse(update_metadata_images(track))
|
||||
self.assertEqual(set(self.test_images[1:]), set(track.orig_metadata.images))
|
||||
self.assertTrue(track.orig_metadata.has_common_images)
|
||||
|
||||
def test_update_album_images(self):
|
||||
album = Album('00000000-0000-0000-0000-000000000000')
|
||||
track1 = Track('00000000-0000-0000-0000-000000000001')
|
||||
track1.linked_files.append(self.test_files[0])
|
||||
track1.files.append(self.test_files[0])
|
||||
track2 = Track('00000000-0000-0000-0000-000000000002')
|
||||
track2.linked_files.append(self.test_files[1])
|
||||
track2.files.append(self.test_files[1])
|
||||
album.tracks = [track1, track2]
|
||||
album.unmatched_files.files.append(self.test_files[2])
|
||||
update_metadata_images(album)
|
||||
self.assertTrue(update_metadata_images(album))
|
||||
self.assertEqual(set(self.test_images), set(album.orig_metadata.images))
|
||||
self.assertFalse(album.orig_metadata.has_common_images)
|
||||
|
||||
album.tracks.remove(track2)
|
||||
update_metadata_images(album)
|
||||
self.assertFalse(update_metadata_images(album))
|
||||
self.assertEqual(set(self.test_images), set(album.orig_metadata.images))
|
||||
self.assertFalse(album.orig_metadata.has_common_images)
|
||||
|
||||
# album.unmatched_files.files.remove(self.test_files[2])
|
||||
album.tracks.remove(track1)
|
||||
update_metadata_images(album)
|
||||
self.assertTrue(update_metadata_images(album))
|
||||
self.assertEqual(set(self.test_images[1:]), set(album.orig_metadata.images))
|
||||
self.assertTrue(album.orig_metadata.has_common_images)
|
||||
|
||||
album.tracks.append(track2)
|
||||
update_metadata_images(album)
|
||||
self.assertFalse(update_metadata_images(album))
|
||||
self.assertEqual(set(self.test_images[1:]), set(album.orig_metadata.images))
|
||||
self.assertTrue(album.orig_metadata.has_common_images)
|
||||
|
||||
@@ -171,27 +171,27 @@ class RemoveMetadataImagesTest(PicardTestCase):
|
||||
|
||||
def test_remove_from_track(self):
|
||||
track = Track('00000000-0000-0000-0000-000000000000')
|
||||
track.linked_files = list(self.test_files)
|
||||
track.files = list(self.test_files)
|
||||
update_metadata_images(track)
|
||||
track.linked_files.remove(self.test_files[0])
|
||||
track.files.remove(self.test_files[0])
|
||||
remove_metadata_images(track, [self.test_files[0]])
|
||||
self.assertEqual(set(self.test_images[1:]), set(track.orig_metadata.images))
|
||||
self.assertTrue(track.orig_metadata.has_common_images)
|
||||
|
||||
def test_remove_from_track_with_common_images(self):
|
||||
track = Track('00000000-0000-0000-0000-000000000000')
|
||||
track.linked_files = list(self.test_files[1:])
|
||||
track.files = list(self.test_files[1:])
|
||||
update_metadata_images(track)
|
||||
track.linked_files.remove(self.test_files[1])
|
||||
track.files.remove(self.test_files[1])
|
||||
remove_metadata_images(track, [self.test_files[1]])
|
||||
self.assertEqual(set(self.test_images[1:]), set(track.orig_metadata.images))
|
||||
self.assertTrue(track.orig_metadata.has_common_images)
|
||||
|
||||
def test_remove_from_empty_track(self):
|
||||
track = Track('00000000-0000-0000-0000-000000000000')
|
||||
track.linked_files.append(File('test1.flac'))
|
||||
track.files.append(File('test1.flac'))
|
||||
update_metadata_images(track)
|
||||
remove_metadata_images(track, [track.linked_files[0]])
|
||||
remove_metadata_images(track, [track.files[0]])
|
||||
self.assertEqual(set(), set(track.orig_metadata.images))
|
||||
self.assertTrue(track.orig_metadata.has_common_images)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user