def load(self): all_tag_names = set() common_tags = None counts = dict() for file in self.files: tags = set() for name, values in file.metadata.rawitems(): if not name.startswith("~"): tags.add((name, tuple(sorted(values)))) all_tag_names.add(name) counts[name] = counts.get(name, 0) + 1 if common_tags is None: common_tags = tags else: common_tags = common_tags.intersection(tags) common_tag_names = set([a for (a, b) in common_tags]) different_tag_names = all_tag_names.difference(common_tag_names) total = len(self.files) for name in different_tag_names: item = QtGui.QTreeWidgetItem(self.ui.tags) item.setText(0, display_tag_name(name)) item.setData(0, QtCore.Qt.UserRole, QtCore.QVariant(name)) font = item.font(1) font.setItalic(True) item.setFont(1, font) missing = total - counts[name] if not missing: value = ungettext("(different across %d file)", "(different across %d files)", total) % total else: value = ungettext("(missing from %d file)", "(missing from %d files)", missing) % missing item.setText(1, value) for name, values in common_tags: for value in values: item = QtGui.QTreeWidgetItem(self.ui.tags) item.setText(0, display_tag_name(name)) item.setData(0, QtCore.Qt.UserRole, QtCore.QVariant(name)) item.setText(1, value) if len(self.files) == 1: if self.config.setting['enable_ratings']: ratings = self.files[0].metadata.getall('~rating') if len(ratings) > 0: self.ui.rating.setRating(int(ratings[0])) for mime, data in self.files[0].metadata.images: item = QtGui.QListWidgetItem() pixmap = QtGui.QPixmap() pixmap.loadFromData(data) icon = QtGui.QIcon(pixmap) item.setIcon(icon) self.ui.artwork_list.addItem(item)
def _update_items(self, result=None, error=None): if self.editing: return if not (self.files or self.tracks): result = None self.tag_diff = result if result is None: self.setRowCount(0) return self.setRowCount(len(result.tag_names)) orig_flags = QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled new_flags = orig_flags | QtCore.Qt.ItemIsEditable for i, name in enumerate(result.tag_names): tag_item = self.item(i, 0) orig_item = self.item(i, 1) new_item = self.item(i, 2) if not tag_item: tag_item = QtWidgets.QTableWidgetItem() tag_item.setFlags(orig_flags) font = tag_item.font() font.setBold(True) tag_item.setFont(font) self.setItem(i, 0, tag_item) if not orig_item: orig_item = QtWidgets.QTableWidgetItem() orig_item.setFlags(orig_flags) self.setItem(i, 1, orig_item) if not new_item: new_item = QtWidgets.QTableWidgetItem() self.setItem(i, 2, new_item) tag_item.setText(display_tag_name(name)) self.set_item_value(orig_item, self.tag_diff.orig, name) if name == "~length": new_item.setFlags(orig_flags) else: new_item.setFlags(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) new_item.setForeground(color)
def _update_items(self, result=None, error=None): if self.editing: return if not (self.files or self.tracks): result = None self.tag_diff = result if result is None: self.setRowCount(0) return self.setRowCount(len(result.tag_names)) orig_flags = QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled new_flags = orig_flags | QtCore.Qt.ItemIsEditable for i, name in enumerate(result.tag_names): tag_item = self.item(i, 0) orig_item = self.item(i, 1) new_item = self.item(i, 2) if not tag_item: tag_item = QtWidgets.QTableWidgetItem() tag_item.setFlags(orig_flags) font = tag_item.font() font.setBold(True) tag_item.setFont(font) self.setItem(i, 0, tag_item) if not orig_item: orig_item = QtWidgets.QTableWidgetItem() orig_item.setFlags(orig_flags) self.setItem(i, 1, orig_item) if not new_item: new_item = QtWidgets.QTableWidgetItem() self.setItem(i, 2, new_item) tag_item.setText(display_tag_name(name)) self.set_item_value(orig_item, self.tag_diff.orig, name) if name == "~length": new_item.setFlags(orig_flags) else: new_item.setFlags(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) new_item.setForeground(color)
def add_tag(self): dialog = EditTagDialog('', None, self) if dialog.exec_(): name = dialog.name value = dialog.value item = QtGui.QTreeWidgetItem(self.ui.tags) item.setText(0, display_tag_name(name)) item.setData(0, QtCore.Qt.UserRole, QtCore.QVariant(name)) item.setText(1, value) self.changed.add(name)
def _update_items(self, result=None, error=None): if result is None or error is not None: self.tag_diff.clear() self.tag_names = None self.setRowCount(0) self.updating = False if self.update_pending: self.update() return self.itemChanged.disconnect(self.item_changed) self.setRowCount(len(self.tag_names)) orig_flags = QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled new_flags = orig_flags | QtCore.Qt.ItemIsEditable for i, name in enumerate(self.tag_names): length = name == "~length" tag_item = self.item(i, 0) orig_item = self.item(i, 1) new_item = self.item(i, 2) if not tag_item: tag_item = QtGui.QTableWidgetItem() tag_item.setFlags(orig_flags) font = tag_item.font() font.setBold(True) tag_item.setFont(font) self.setItem(i, 0, tag_item) if not orig_item: orig_item = QtGui.QTableWidgetItem() orig_item.setFlags(orig_flags) self.setItem(i, 1, orig_item) if not new_item: new_item = QtGui.QTableWidgetItem() self.setItem(i, 2, new_item) tag_item.setText(display_tag_name(name)) self.set_item_value(orig_item, self.tag_diff.orig, name) new_item.setFlags(orig_flags if length else new_flags) self.set_item_value(new_item, self.tag_diff.new, name) color = self.colors.get(self.tag_status(name), self.colors[TagStatus.NoChange]) orig_item.setForeground(color) new_item.setForeground(color) self.itemChanged.connect(self.item_changed) self.updating = False if self.update_pending: self.update()
def _update_items(self, result=None, error=None): if result is None or error is not None: self.orig_tags.clear() self.new_tags.clear() self.tag_names = None self.setRowCount(0) self.updating = False if self.update_pending: self.update() return self._item_signals = False self.setRowCount(len(self.tag_names)) flags = QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled for i, name in enumerate(self.tag_names): if self.tag_status(name) == "empty": continue tag_item = self.item(i, 0) orig_item = self.item(i, 1) new_item = self.item(i, 2) if not tag_item: tag_item = QtGui.QTableWidgetItem() tag_item.setFlags(QtCore.Qt.ItemIsEnabled) font = tag_item.font() font.setBold(True) tag_item.setFont(font) self.setItem(i, 0, tag_item) if not orig_item: orig_item = QtGui.QTableWidgetItem() orig_item.setFlags(flags) self.setItem(i, 1, orig_item) if not new_item: new_item = QtGui.QTableWidgetItem() self.setItem(i, 2, new_item) new_item.setFlags(flags if name == "~length" else flags | QtCore.Qt.ItemIsEditable) tag_item.setText(display_tag_name(name)) self.set_item_value(orig_item, self.orig_tags, name) if new_item != self._editing: self.set_item_value(new_item, self.new_tags, name) self.set_row_colors(i) self._item_signals = True self.updating = False if self.update_pending: self.update()
def _update_items(self, result=None, error=None): if result is None or error is not None: self.orig_tags.clear() self.new_tags.clear() self.tag_names = None self.setRowCount(0) self.updating = False if self.update_pending: self.update() return self.itemChanged.disconnect(self.item_changed) self.setRowCount(len(self.tag_names)) flags = QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled for i, name in enumerate(self.tag_names): tag_item = self.item(i, 0) orig_item = self.item(i, 1) new_item = self.item(i, 2) if not tag_item: tag_item = QtGui.QTableWidgetItem() tag_item.setFlags(flags) font = tag_item.font() font.setBold(True) tag_item.setFont(font) self.setItem(i, 0, tag_item) if not orig_item: orig_item = QtGui.QTableWidgetItem() orig_item.setFlags(flags) self.setItem(i, 1, orig_item) if not new_item: new_item = QtGui.QTableWidgetItem() self.setItem(i, 2, new_item) tag_item.setText(display_tag_name(name)) self.set_item_value(orig_item, self.orig_tags, name) new_item.setFlags(flags if name == "~length" else flags | QtCore.Qt.ItemIsEditable) self.set_item_value(new_item, self.new_tags, name) self.set_row_colors(i) self.itemChanged.connect(self.item_changed) self.updating = False if self.update_pending: self.update()
def edit_tag(self, item=None, column=None): if item is None: items = self.ui.tags.selectedItems() if not items: return item = items[0] name = unicode(item.data(0, QtCore.Qt.UserRole).toString()) value = item.text(1) dialog = EditTagDialog(name, value, self) if dialog.exec_(): if value != dialog.value: name = dialog.name value = dialog.value item.setText(0, display_tag_name(name)) item.setData(0, QtCore.Qt.UserRole, QtCore.QVariant(name)) font = item.font(1) font.setItalic(False) item.setFont(1, font) item.setText(1, value) self.changed.add(name)
def edit_tag(self, item=None, column=None): if item is None: items = self.ui.tags.selectedItems() if not items: return item = items[0] name = unicode(item.data(0, QtCore.Qt.UserRole).toString()) value = item.text(1) dialog = EditTagDialog(name, value, self) if dialog.exec_(): if value != dialog.value or name != dialog.name: name = dialog.name value = dialog.value item.setText(0, display_tag_name(name)) item.setData(0, QtCore.Qt.UserRole, QtCore.QVariant(name)) font = item.font(1) font.setItalic(False) item.setFont(1, font) item.setText(1, value) self.changed.add(name)
def _update_tags(self): self.selection_mutex.lock() files = self.files tracks = self.tracks self.selection_mutex.unlock() if not (files or tracks): return None tag_diff = TagDiff() orig_tags = tag_diff.orig new_tags = tag_diff.new # existing_tags are orig_tags that would not be overwritten by # any new_tags, assuming clear_existing_tags is disabled. existing_tags = set() tag_diff.objects = len(files) clear_existing_tags = config.setting["clear_existing_tags"] for file in files: new_metadata = file.new_metadata orig_metadata = file.orig_metadata tags = set(new_metadata.keys() + orig_metadata.keys()) for name in filter(lambda x: not x.startswith("~") and file.supports_tag(x), tags): new_values = new_metadata.getall(name) orig_values = orig_metadata.getall(name) if not ((new_values and name not in existing_tags) or clear_existing_tags): new_values = list(orig_values or [""]) existing_tags.add(name) 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) for track in tracks: if track.num_linked_files == 0: for name, values in dict.iteritems(track.metadata): if not name.startswith("~"): tag_diff.add(name, values, values, True) length = str(track.metadata.length) tag_diff.add("~length", length, length, False) tag_diff.objects += 1 all_tags = set(orig_tags.keys() + new_tags.keys()) tag_names = COMMON_TAGS + \ sorted(all_tags.difference(COMMON_TAGS), key=lambda x: display_tag_name(x).lower()) if config.persist["show_changes_first"]: tags_by_status = {} for tag in tag_names: tags_by_status.setdefault(tag_diff.tag_status(tag), []).append(tag) for status in (TagStatus.Changed, TagStatus.Added, TagStatus.Removed, TagStatus.NoChange): tag_diff.tag_names += tags_by_status.pop(status, []) else: tag_diff.tag_names = [ tag for tag in tag_names if tag_diff.status[tag] != TagStatus.Empty] return tag_diff
def test_display_tag_name(self): self.assertEqual('Artist', display_tag_name('artist')) self.assertEqual('Lyrics', display_tag_name('lyrics:')) self.assertEqual('Comment [Foo]', display_tag_name('comment:Foo'))
def get_display_name(self, item): return display_tag_name(item)
def get_tooltip(self, position): if self._doc.characterAt(position) != '%': return None tag = self._read_allowed_chars(position + 1) return display_tag_name(tag)
def _update_tags(self): self.selection_mutex.lock() files = self.files tracks = self.tracks self.selection_mutex.unlock() if not (files or tracks): return None self.colors = { TagStatus.NOCHANGE: self.palette().color(QtGui.QPalette.Text), TagStatus.REMOVED: QtGui.QBrush(interface_colors.get_qcolor('tagstatus_removed')), TagStatus.ADDED: QtGui.QBrush(interface_colors.get_qcolor('tagstatus_added')), TagStatus.CHANGED: QtGui.QBrush(interface_colors.get_qcolor('tagstatus_changed')) } tag_diff = TagDiff(max_length_diff=config. setting["ignore_track_duration_difference_under"]) orig_tags = tag_diff.orig new_tags = tag_diff.new # existing_tags are orig_tags that would not be overwritten by # any new_tags, assuming clear_existing_tags is disabled. existing_tags = set() tag_diff.objects = len(files) clear_existing_tags = config.setting["clear_existing_tags"] for file in files: new_metadata = file.new_metadata orig_metadata = file.orig_metadata tags = set(list(new_metadata.keys()) + list(orig_metadata.keys())) for name in filter( lambda x: not x.startswith("~") and file.supports_tag(x), tags): new_values = new_metadata.getall(name) orig_values = orig_metadata.getall(name) if not ((new_values and name not in existing_tags) or clear_existing_tags): new_values = list(orig_values or [""]) existing_tags.add(name) 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) for track in tracks: if track.num_linked_files == 0: for name, values in track.metadata.rawitems(): if not name.startswith("~"): tag_diff.add(name, values, values, True) length = str(track.metadata.length) tag_diff.add("~length", length, length, False) tag_diff.objects += 1 all_tags = set(list(orig_tags.keys()) + list(new_tags.keys())) common_tags = [ tag for tag in config.setting['metadatabox_top_tags'] if tag in all_tags ] tag_names = common_tags + sorted( all_tags.difference(common_tags), key=lambda x: display_tag_name(x).lower()) if config.persist["show_changes_first"]: tags_by_status = {} for tag in tag_names: tags_by_status.setdefault(tag_diff.tag_status(tag), []).append(tag) for status in (TagStatus.CHANGED, TagStatus.ADDED, TagStatus.REMOVED, TagStatus.NOCHANGE): tag_diff.tag_names += tags_by_status.pop(status, []) else: tag_diff.tag_names = [ tag for tag in tag_names if tag_diff.status[tag] != TagStatus.EMPTY ] return tag_diff
def _update_tags(self, new_selection=True, drop_album_caches=False): self.selection_mutex.lock() files = self.files tracks = self.tracks self.selection_mutex.unlock() if not (files or tracks): return None if new_selection or drop_album_caches: self._single_file_album = len(set([file.metadata["album"] for file in files])) == 1 self._single_track_album = len(set([track.metadata["album"] for track in tracks])) == 1 while not new_selection: # Just an if with multiple exit points # If we are dealing with the same selection # skip updates unless it we are dealing with a single file/track if len(files) == 1: break if len(tracks) == 1: break # Or if we are dealing with a single cluster/album if self._single_file_album: break if self._single_track_album: break return self.tag_diff self.colors = { TagStatus.NOCHANGE: self.palette().color(QtGui.QPalette.Text), TagStatus.REMOVED: QtGui.QBrush(interface_colors.get_qcolor('tagstatus_removed')), TagStatus.ADDED: QtGui.QBrush(interface_colors.get_qcolor('tagstatus_added')), TagStatus.CHANGED: QtGui.QBrush(interface_colors.get_qcolor('tagstatus_changed')) } config = get_config() tag_diff = TagDiff(max_length_diff=config.setting["ignore_track_duration_difference_under"]) orig_tags = tag_diff.orig new_tags = tag_diff.new tag_diff.objects = len(files) clear_existing_tags = config.setting["clear_existing_tags"] top_tags = config.setting['metadatabox_top_tags'] top_tags_set = set(top_tags) for file in files: new_metadata = file.new_metadata orig_metadata = file.orig_metadata tags = set(list(new_metadata.keys()) + list(orig_metadata.keys())) for name in filter(lambda x: not x.startswith("~") and file.supports_tag(x), tags): new_values = new_metadata.getall(name) orig_values = orig_metadata.getall(name) if not clear_existing_tags and not new_values: new_values = list(orig_values or [""]) removed = name in new_metadata.deleted_tags tag_diff.add(name, orig_values, new_values, True, removed, top_tags=top_tags_set) tag_diff.add("~length", str(orig_metadata.length), str(new_metadata.length), removable=False, readonly=True) for track in tracks: if track.num_linked_files == 0: for name, new_values in track.metadata.rawitems(): if not name.startswith("~"): if name in track.orig_metadata: orig_values = track.orig_metadata.getall(name) else: orig_values = new_values tag_diff.add(name, orig_values, new_values, True) length = str(track.metadata.length) tag_diff.add("~length", length, length, removable=False, readonly=True) tag_diff.objects += 1 all_tags = set(list(orig_tags.keys()) + list(new_tags.keys())) common_tags = [tag for tag in top_tags if tag in all_tags] tag_names = common_tags + sorted(all_tags.difference(common_tags), key=lambda x: display_tag_name(x).lower()) if config.persist["show_changes_first"]: tags_by_status = {} for tag in tag_names: tags_by_status.setdefault(tag_diff.tag_status(tag), []).append(tag) for status in (TagStatus.CHANGED, TagStatus.ADDED, TagStatus.REMOVED, TagStatus.NOCHANGE): tag_diff.tag_names += tags_by_status.pop(status, []) else: tag_diff.tag_names = [ tag for tag in tag_names if tag_diff.status[tag] != TagStatus.EMPTY] return tag_diff
def _update_items(self, result=None, error=None): if self.editing: return if not (self.files or self.tracks): result = None self.tag_diff = result if result is None: self.setRowCount(0) return self.setRowCount(len(result.tag_names)) orig_flags = QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled new_flags = orig_flags | QtCore.Qt.ItemIsEditable for i, name in enumerate(result.tag_names): tag_item = self.item(i, 0) orig_item = self.item(i, 1) new_item = self.item(i, 2) if not tag_item: tag_item = QtWidgets.QTableWidgetItem() tag_item.setFlags(orig_flags) font = tag_item.font() font.setBold(True) tag_item.setFont(font) self.setItem(i, 0, tag_item) if not orig_item: orig_item = QtWidgets.QTableWidgetItem() orig_item.setFlags(orig_flags) self.setItem(i, 1, orig_item) if not new_item: new_item = QtWidgets.QTableWidgetItem() self.setItem(i, 2, new_item) tag_item.setText(display_tag_name(name)) self.set_item_value(orig_item, self.tag_diff.orig, name) if name == "~length": new_item.setFlags(orig_flags) else: new_item.setFlags(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) new_item.setForeground(color) alignment = QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop tag_item.setTextAlignment(alignment) orig_item.setTextAlignment(alignment) new_item.setTextAlignment(alignment) # Expand the row for multiline content, but limit the maximum # row height. row_height = min(self.sizeHintForRow(i), 160) self.setRowHeight(i, row_height)