def remove_file(self, file): if file not in self.linked_files: return self.linked_files.remove(file) self.num_linked_files -= 1 file.copy_metadata(file.orig_metadata) self.album._remove_file(self, file) file.metadata_images_changed.disconnect( self.update_orig_metadata_images) if self.num_linked_files > 0: self.metadata.copy(self.linked_files[-1].orig_metadata) else: self.metadata.copy(self.orig_metadata) # Restore to non-associated state for s_name, s_text in enabled_tagger_scripts_texts(): parser = ScriptParser() try: parser.eval(s_text, self.metadata) except: log.exception("Failed to run tagger script %s on track", s_name) self.metadata.strip_whitespace() self.update()
def accept(self): profile_id = self.ui.save_to_profile.currentData() if profile_id != USER_SETTINGS_PROFILE_ID: profile_name = self._get_profile_title_from_id(profile_id) message_box = QtWidgets.QMessageBox(self) message_box.setIcon(QtWidgets.QMessageBox.Warning) message_box.setWindowModality(QtCore.Qt.WindowModal) message_box.setWindowTitle(_("Save to Profile")) message_box.setText( _("Changes will only be saved to the profile: \"%s\"\n\nDo you want to continue?" ) % profile_name) message_box.setStandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.Cancel) if message_box.exec_() != QtWidgets.QMessageBox.Yes: return for page in self.pages: try: page.check() except OptionsCheckError as e: self._show_page_error(page, e) return except Exception as e: log.exception('Failed checking options page %r', page) self._show_page_error(page, e) return for page in self.pages: try: page.save() except Exception as e: log.exception('Failed saving options page %r', page) self._show_page_error(page, e) return self.reset_profile() super().accept()
def _save(self, filename, metadata): if config.setting['aac_save_ape']: super()._save(filename, metadata) elif config.setting['remove_ape_from_aac']: try: mutagen.apev2.delete(encode_filename(filename)) except BaseException: log.exception('Error removing APEv2 tags from %s', filename)
def run_scripts(metadata): for s_name, s_text in enabled_tagger_scripts_texts(): parser = ScriptParser() try: parser.eval(s_text, metadata) except ScriptError: log.exception("Failed to run tagger script %s on track", s_name) metadata.strip_whitespace()
def _finalize_loading_album(self): 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(): parser = ScriptParser() for track in self._new_tracks: # Run tagger script for each track try: parser.eval(s_text, track.metadata) except ScriptError: log.exception("Failed to run tagger script %s on track", s_name) track.metadata.strip_whitespace() track.scripted_metadata.update(track.metadata) # Run tagger script for the album itself try: parser.eval(s_text, self._new_metadata) except ScriptError: log.exception("Failed to run tagger script %s on album", s_name) self._new_metadata.strip_whitespace() unmatched_files = [ file for track in self.tracks for file in track.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 self.loaded = True self.status = AlbumStatus.LOADED self.match_files(unmatched_files + self.unmatched_files.files) self.enable_update_metadata_images(True) self.update_metadata_images() self.update() self.tagger.window.set_statusbar_message( N_('Album %(id)s loaded: %(artist)s - %(album)s'), { 'id': self.id, 'artist': self.metadata['albumartist'], 'album': self.metadata['album'] }, timeout=3000) for func, always in self._after_load_callbacks: func() self._after_load_callbacks = [] if self.item.isSelected(): self.tagger.window.refresh_metadatabox()
def _run_script(self, script): s_name = script[1] s_text = script[3] parser = ScriptParser() for obj in self._get_unique_metadata_objects(): try: parser.eval(s_text, obj.metadata) obj.update() except ScriptError as e: log.exception('Error running tagger script "%s" on object %r', s_name, obj) msg = N_('Script error in "%(script)s": %(message)s') mparms = { 'script': s_name, 'message': string_(e), } self.tagger.window.set_statusbar_message(msg, mparms)
def _run_script(self, script): s_name = script[1] s_text = script[3] parser = ScriptParser() for obj in self._get_unique_metadata_objects(): try: parser.eval(s_text, obj.metadata) obj.update() except ScriptError as e: log.exception('Error running tagger script "%s" on object %r', s_name, obj) msg = N_('Script error in "%(script)s": %(message)s') mparms = { 'script': s_name, 'message': str(e), } self.tagger.window.set_statusbar_message(msg, mparms)
def _parse_recording(self, recording): m = self.metadata recording_to_metadata(recording, m, self) self._customize_metadata() run_track_metadata_processors(self.album, m, None, recording) for s_name, s_text in enabled_tagger_scripts_texts(): parser = ScriptParser() try: parser.eval(s_text, m) except ScriptError: log.exception("Failed to run tagger script %s on track", s_name) m.strip_whitespace() self.loaded = True if self.callback: self.callback() self.callback = None self.tagger.nats.update(True)
def accept(self): for page in self.pages: try: page.check() except OptionsCheckError as e: self._show_page_error(page, e) return except Exception as e: log.exception('Failed checking options page %r', page) self._show_page_error(page, e) return for page in self.pages: try: page.save() except Exception as e: log.exception('Failed saving options page %r', page) self._show_page_error(page, e) return super().accept()
def _parse_recording(self, recording): m = self.metadata recording_to_metadata(recording, m, self) self.orig_metadata.copy(m) self._customize_metadata() run_track_metadata_processors(self.album, m, None, recording) for s_name, s_text in enabled_tagger_scripts_texts(): parser = ScriptParser() try: parser.eval(s_text, m) except ScriptError: log.exception("Failed to run tagger script %s on track", s_name) m.strip_whitespace() self.loaded = True self.status = None if self.callback: self.callback() self.callback = None self.album.update(True)
def update_file_metadata(self, file): if file not in self.linked_files: return file.copy_metadata(self.orig_metadata) file.metadata['~extension'] = file.orig_metadata['~extension'] self.metadata.copy(file.metadata) # Re-run tagger scripts with updated metadata for s_name, s_text in enabled_tagger_scripts_texts(): parser = ScriptParser() try: parser.eval(s_text, file.metadata) parser.eval(s_text, self.metadata) except: log.exception("Failed to run tagger script %s on file", s_name) file.metadata.strip_whitespace() self.metadata.strip_whitespace() file.metadata.changed = True file.update(signal=False) self.update()
def _finalize_loading(self, error): if error: self.metadata.clear() self.status = _("[could not load album %s]") % self.id del self._new_metadata del self._new_tracks self.update() return if self._requests > 0: return if not self._tracks_loaded: artists = set() totalalbumtracks = 0 absolutetracknumber = 0 va = self._new_metadata['musicbrainz_albumartistid'] == VARIOUS_ARTISTS_ID djmix_ars = {} if hasattr(self._new_metadata, "_djmix_ars"): djmix_ars = self._new_metadata._djmix_ars for medium_node in self._release_node['media']: mm = Metadata() mm.copy(self._new_metadata) medium_to_metadata(medium_node, mm) discpregap = False for dj in djmix_ars.get(mm["discnumber"], []): mm.add("djmixer", dj) if 'discs' in medium_node: discids = [disc.get('id') for disc in medium_node['discs']] mm['~musicbrainz_discids'] = discids mm['musicbrainz_discid'] = list(self._discids.intersection(discids)) if "pregap" in medium_node: discpregap = True absolutetracknumber += 1 track = self._finalize_loading_track(medium_node['pregap'], mm, artists, va, absolutetracknumber, discpregap) track.metadata['~pregap'] = "1" track_count = medium_node['track-count'] if track_count: tracklist_node = medium_node['tracks'] for track_node in tracklist_node: absolutetracknumber += 1 track = self._finalize_loading_track(track_node, mm, artists, va, absolutetracknumber, discpregap) if "data-tracks" in medium_node: for track_node in medium_node['data-tracks']: absolutetracknumber += 1 track = self._finalize_loading_track(track_node, mm, artists, va, absolutetracknumber, discpregap) track.metadata['~datatrack'] = "1" totalalbumtracks = str(absolutetracknumber) for track in self._new_tracks: track.metadata["~totalalbumtracks"] = totalalbumtracks if len(artists) > 1: track.metadata["~multiartist"] = "1" del self._release_node del self._release_artist_nodes self._tracks_loaded = True if not self._requests: self.enable_update_metadata_images(False) # Prepare parser for user's script for s_name, s_text in enabled_tagger_scripts_texts(): parser = ScriptParser() for track in self._new_tracks: # Run tagger script for each track try: parser.eval(s_text, track.metadata) except ScriptError: log.exception("Failed to run tagger script %s on track", s_name) track.metadata.strip_whitespace() # Run tagger script for the album itself try: parser.eval(s_text, self._new_metadata) except ScriptError: log.exception("Failed to run tagger script %s on album", s_name) 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): file.move(self.unmatched_files) self.metadata = self._new_metadata self.tracks = self._new_tracks del self._new_metadata del self._new_tracks self.loaded = True self.status = None self.match_files(self.unmatched_files.files) self.enable_update_metadata_images(True) self.update() self.tagger.window.set_statusbar_message( N_('Album %(id)s loaded: %(artist)s - %(album)s'), { 'id': self.id, 'artist': self.metadata['albumartist'], 'album': self.metadata['album'] }, timeout=3000 ) for func in self._after_load_callbacks: func() self._after_load_callbacks = []
def _finalize_loading(self, error): if error: self.metadata.clear() self.status = _("[could not load album %s]") % self.id del self._new_metadata del self._new_tracks self.update() return if self._requests > 0: return if not self._tracks_loaded: artists = set() all_media = [] absolutetracknumber = 0 va = self._new_metadata['musicbrainz_albumartistid'] == VARIOUS_ARTISTS_ID djmix_ars = {} if hasattr(self._new_metadata, "_djmix_ars"): djmix_ars = self._new_metadata._djmix_ars for medium_node in self._release_node['media']: mm = Metadata() mm.copy(self._new_metadata) medium_to_metadata(medium_node, mm) discpregap = False format = medium_node.get('format') if format: all_media.append(format) for dj in djmix_ars.get(mm["discnumber"], []): mm.add("djmixer", dj) if 'discs' in medium_node: discids = [disc.get('id') for disc in medium_node['discs']] mm['~musicbrainz_discids'] = discids mm['musicbrainz_discid'] = list(self._discids.intersection(discids)) if "pregap" in medium_node: discpregap = True absolutetracknumber += 1 track = self._finalize_loading_track(medium_node['pregap'], mm, artists, va, absolutetracknumber, discpregap) track.metadata['~pregap'] = "1" track_count = medium_node['track-count'] if track_count: tracklist_node = medium_node['tracks'] for track_node in tracklist_node: absolutetracknumber += 1 track = self._finalize_loading_track(track_node, mm, artists, va, absolutetracknumber, discpregap) if "data-tracks" in medium_node: for track_node in medium_node['data-tracks']: absolutetracknumber += 1 track = self._finalize_loading_track(track_node, mm, artists, va, absolutetracknumber, discpregap) track.metadata['~datatrack'] = "1" totalalbumtracks = absolutetracknumber self._new_metadata['~totalalbumtracks'] = totalalbumtracks # Generate a list of unique media, but keep order of first appearance self._new_metadata['media'] = " / ".join(list(OrderedDict.fromkeys(all_media))) for track in self._new_tracks: track.metadata["~totalalbumtracks"] = totalalbumtracks if len(artists) > 1: track.metadata["~multiartist"] = "1" del self._release_node del self._release_artist_nodes self._tracks_loaded = True if not self._requests: self.enable_update_metadata_images(False) # Prepare parser for user's script for s_name, s_text in enabled_tagger_scripts_texts(): parser = ScriptParser() for track in self._new_tracks: # Run tagger script for each track try: parser.eval(s_text, track.metadata) except ScriptError: log.exception("Failed to run tagger script %s on track", s_name) track.metadata.strip_whitespace() # Run tagger script for the album itself try: parser.eval(s_text, self._new_metadata) except ScriptError: log.exception("Failed to run tagger script %s on album", s_name) 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): file.move(self.unmatched_files) self.metadata = self._new_metadata self.tracks = self._new_tracks del self._new_metadata del self._new_tracks self.loaded = True self.status = None self.match_files(self.unmatched_files.files) self.enable_update_metadata_images(True) self.update() self.tagger.window.set_statusbar_message( N_('Album %(id)s loaded: %(artist)s - %(album)s'), { 'id': self.id, 'artist': self.metadata['albumartist'], 'album': self.metadata['album'] }, timeout=3000 ) for func in self._after_load_callbacks: func() self._after_load_callbacks = []
def __init__(self, default_page=None, parent=None): super().__init__(parent) self.setWindowModality(QtCore.Qt.ApplicationModal) self.setAttribute(QtCore.Qt.WA_DeleteOnClose) from picard.ui.ui_options import Ui_Dialog self.ui = Ui_Dialog() self.ui.setupUi(self) self.ui.reset_all_button = QtWidgets.QPushButton( _("&Restore all Defaults")) self.ui.reset_all_button.setToolTip( _("Reset all of Picard's settings")) self.ui.reset_button = QtWidgets.QPushButton(_("Restore &Defaults")) self.ui.reset_button.setToolTip( _("Reset all settings for current option page")) ok = StandardButton(StandardButton.OK) ok.setText(_("Make It So!")) self.ui.buttonbox.addButton(ok, QtWidgets.QDialogButtonBox.AcceptRole) self.ui.buttonbox.addButton(StandardButton(StandardButton.CANCEL), QtWidgets.QDialogButtonBox.RejectRole) self.ui.buttonbox.addButton(StandardButton(StandardButton.HELP), QtWidgets.QDialogButtonBox.HelpRole) self.ui.buttonbox.addButton(self.ui.reset_all_button, QtWidgets.QDialogButtonBox.ActionRole) self.ui.buttonbox.addButton(self.ui.reset_button, QtWidgets.QDialogButtonBox.ActionRole) self.ui.buttonbox.accepted.connect(self.accept) self.ui.buttonbox.rejected.connect(self.reject) self.ui.reset_all_button.clicked.connect(self.confirm_reset_all) self.ui.reset_button.clicked.connect(self.confirm_reset) self.ui.buttonbox.helpRequested.connect(self.show_help) self.ui.attached_profiles_button = QtWidgets.QPushButton( _("Attached Profiles")) self.ui.attached_profiles_button.setToolTip( _("Show which profiles are attached to the options on this page")) self.ui.buttonbox.addButton(self.ui.attached_profiles_button, QtWidgets.QDialogButtonBox.ActionRole) self.ui.attached_profiles_button.clicked.connect( self.show_attached_profiles_dialog) config = get_config() self.pages = [] for Page in page_classes: try: page = Page(self.ui.pages_stack) self.pages.append(page) except Exception: log.exception('Failed initializing options page %r', page) self.item_to_page = {} self.page_to_item = {} self.default_item = None if not default_page: default_page = config.persist["options_last_active_page"] self.add_pages(None, default_page, self.ui.pages_tree) # work-around to set optimal option pane width self.ui.pages_tree.expandAll() max_page_name = self.ui.pages_tree.sizeHintForColumn( 0) + 2 * self.ui.pages_tree.frameWidth() self.ui.dialog_splitter.setSizes( [max_page_name, self.geometry().width() - max_page_name]) self.ui.pages_tree.setHeaderLabels([""]) self.ui.pages_tree.header().hide() self.ui.pages_tree.itemSelectionChanged.connect(self.switch_page) self.restoreWindowState() self.finished.connect(self.saveWindowState) for page in self.pages: try: page.load() except Exception: log.exception('Failed loading options page %r', page) self.disable_page(page.NAME) self.ui.pages_tree.setCurrentItem(self.default_item) self.profile_page = self.get_page('profiles') self.profile_page.signal_refresh.connect( self.update_from_profile_changes) self.first_enter = True self.installEventFilter(self) self.highlight_enabled_profile_options() current_page = self.item_to_page[self.ui.pages_tree.currentItem()] self.set_profiles_button_and_highlight(current_page)
def __init__(self, default_page=None, parent=None): super().__init__(parent) self.setWindowModality(QtCore.Qt.ApplicationModal) self.setAttribute(QtCore.Qt.WA_DeleteOnClose) from picard.ui.ui_options import Ui_Dialog self.ui = Ui_Dialog() self.ui.setupUi(self) self.ui.reset_all_button = QtWidgets.QPushButton( _("&Restore all Defaults")) self.ui.reset_all_button.setToolTip( _("Reset all of Picard's settings")) self.ui.reset_button = QtWidgets.QPushButton(_("Restore &Defaults")) self.ui.reset_button.setToolTip( _("Reset all settings for current option page")) ok = StandardButton(StandardButton.OK) ok.setText(_("Make It So!")) self.ui.buttonbox.addButton(ok, QtWidgets.QDialogButtonBox.AcceptRole) self.ui.buttonbox.addButton(StandardButton(StandardButton.CANCEL), QtWidgets.QDialogButtonBox.RejectRole) self.ui.buttonbox.addButton(StandardButton(StandardButton.HELP), QtWidgets.QDialogButtonBox.HelpRole) self.ui.buttonbox.addButton(self.ui.reset_all_button, QtWidgets.QDialogButtonBox.ActionRole) self.ui.buttonbox.addButton(self.ui.reset_button, QtWidgets.QDialogButtonBox.ActionRole) self.ui.buttonbox.accepted.connect(self.accept) self.ui.buttonbox.rejected.connect(self.reject) self.ui.reset_all_button.clicked.connect(self.confirm_reset_all) self.ui.reset_button.clicked.connect(self.confirm_reset) self.ui.buttonbox.helpRequested.connect(self.help) self.pages = [] for Page in page_classes: try: page = Page(self.ui.pages_stack) self.pages.append(page) except Exception: log.exception('Failed initializing options page %r', page) self.item_to_page = {} self.page_to_item = {} self.default_item = None self.add_pages(None, default_page, self.ui.pages_tree) # work-around to set optimal option pane width self.ui.pages_tree.expandAll() max_page_name = self.ui.pages_tree.sizeHintForColumn( 0) + 2 * self.ui.pages_tree.frameWidth() self.ui.pages_tree.collapseAll() self.ui.splitter.setSizes( [max_page_name, self.geometry().width() - max_page_name]) self.ui.pages_tree.setHeaderLabels([""]) self.ui.pages_tree.header().hide() self.ui.pages_tree.itemSelectionChanged.connect(self.switch_page) self.restoreWindowState() self.finished.connect(self.saveWindowState) for page in self.pages: try: page.load() except Exception: log.exception('Failed loading options page %r', page) self.disable_page(page.NAME) self.ui.pages_tree.setCurrentItem(self.default_item)
def __init__(self, default_page=None, parent=None): super().__init__(parent) self.setWindowModality(QtCore.Qt.ApplicationModal) self.setAttribute(QtCore.Qt.WA_DeleteOnClose) from picard.ui.ui_options import Ui_Dialog self.ui = Ui_Dialog() self.ui.setupUi(self) self.ui.reset_all_button = QtWidgets.QPushButton( _("&Restore all Defaults")) self.ui.reset_all_button.setToolTip( _("Reset all of Picard's settings")) self.ui.reset_button = QtWidgets.QPushButton(_("Restore &Defaults")) self.ui.reset_button.setToolTip( _("Reset all settings for current option page")) ok = StandardButton(StandardButton.OK) ok.setText(_("Make It So!")) self.ui.buttonbox.addButton(ok, QtWidgets.QDialogButtonBox.AcceptRole) self.ui.buttonbox.addButton(StandardButton(StandardButton.CANCEL), QtWidgets.QDialogButtonBox.RejectRole) self.ui.buttonbox.addButton(StandardButton(StandardButton.HELP), QtWidgets.QDialogButtonBox.HelpRole) self.ui.buttonbox.addButton(self.ui.reset_all_button, QtWidgets.QDialogButtonBox.ActionRole) self.ui.buttonbox.addButton(self.ui.reset_button, QtWidgets.QDialogButtonBox.ActionRole) self.ui.buttonbox.accepted.connect(self.accept) self.ui.buttonbox.rejected.connect(self.reject) self.ui.reset_all_button.clicked.connect(self.confirm_reset_all) self.ui.reset_button.clicked.connect(self.confirm_reset) self.ui.buttonbox.helpRequested.connect(self.show_help) profile_help = StandardButton(StandardButton.HELP) profile_help.setText(_("Profile Help")) self.ui.profiles_buttonbox.addButton( profile_help, QtWidgets.QDialogButtonBox.HelpRole) self.ui.profiles_buttonbox.helpRequested.connect( self.show_profile_help) self.ui.attached_profiles_button = QtWidgets.QPushButton( _("Attached Profiles")) self.ui.attached_profiles_button.setToolTip( _("Show which profiles are attached to the options on this page")) self.ui.profiles_buttonbox.addButton( self.ui.attached_profiles_button, QtWidgets.QDialogButtonBox.ActionRole) self.ui.attached_profiles_button.clicked.connect( self.show_attached_profiles_dialog) config = get_config() self.pages = [] for Page in page_classes: try: page = Page(self.ui.pages_stack) self.pages.append(page) except Exception: log.exception('Failed initializing options page %r', page) self.item_to_page = {} self.page_to_item = {} self.default_item = None if not default_page: default_page = config.persist["options_last_active_page"] self.add_pages(None, default_page, self.ui.pages_tree) # work-around to set optimal option pane width self.ui.pages_tree.expandAll() max_page_name = self.ui.pages_tree.sizeHintForColumn( 0) + 2 * self.ui.pages_tree.frameWidth() self.ui.dialog_splitter.setSizes( [max_page_name, self.geometry().width() - max_page_name]) self.ui.pages_tree.setHeaderLabels([""]) self.ui.pages_tree.header().hide() self.ui.pages_tree.itemSelectionChanged.connect(self.switch_page) self.restoreWindowState() self.finished.connect(self.saveWindowState) for page in self.pages: try: page.load() except Exception: log.exception('Failed loading options page %r', page) self.disable_page(page.NAME) self.ui.pages_tree.setCurrentItem(self.default_item) self.first_enter = True self.installEventFilter(self) self.USER_SETTINGS_TITLE = _("User base settings") if config.profiles[SettingConfigSection.PROFILES_KEY]: self.ui.profile_frame.show() self.ui.save_to_profile.clear() self.ui.save_to_profile.addItem(self.USER_SETTINGS_TITLE, USER_SETTINGS_PROFILE_ID) index = 0 for idx, item in enumerate( config.profiles[SettingConfigSection.PROFILES_KEY], start=1): self.ui.save_to_profile.addItem(item["title"], item["id"]) if not index and item["enabled"]: index = idx self.ui.save_to_profile.currentIndexChanged.connect( self.switch_profile) self.ui.save_to_profile.setCurrentIndex(index) else: self.ui.profile_frame.hide()