def _example_to_filename(self, file): settings = SettingsOverride( config.setting, { 'ascii_filenames': self.ui.ascii_filenames.isChecked(), 'file_naming_format': self.ui.file_naming_format.toPlainText(), 'move_files': self.ui.move_files.isChecked(), 'move_files_to': os.path.normpath( self.ui.move_files_to.text()), 'rename_files': self.ui.rename_files.isChecked(), 'windows_compatibility': self.ui.windows_compatibility.isChecked(), }) try: if config.setting["enable_tagger_scripts"]: for s_pos, s_name, s_enabled, s_text in config.setting[ "list_of_scripts"]: if s_enabled and s_text: parser = ScriptParser() parser.eval(s_text, file.metadata) filename = file.make_filename(file.filename, file.metadata, settings) if not settings["move_files"]: return os.path.basename(filename) return filename except ScriptError: return "" except TypeError: return ""
def _example_to_filename(self, file): settings = { 'windows_compatibility': self.ui.windows_compatibility.isChecked(), 'ascii_filenames': self.ui.ascii_filenames.isChecked(), 'rename_files': self.ui.rename_files.isChecked(), 'move_files': self.ui.move_files.isChecked(), 'use_va_format': False, # TODO remove 'file_naming_format': unicode(self.ui.file_naming_format.toPlainText()), 'move_files_to': os.path.normpath(unicode(self.ui.move_files_to.text())) } try: if config.setting["enable_tagger_script"]: script = config.setting["tagger_script"] parser = ScriptParser() parser.eval(script, file.metadata) filename = file._make_filename(file.filename, file.metadata, settings) if not settings["move_files"]: return os.path.basename(filename) return filename except SyntaxError: return "" except ScriptError: return "" except TypeError: return ""
def _script_to_filename(self, naming_format, file_metadata, settings=None): if settings is None: settings = config.setting metadata = Metadata() if config.setting["clear_existing_tags"]: metadata.copy(file_metadata) else: metadata.copy(self.orig_metadata) metadata.update(file_metadata) # make sure every metadata can safely be used in a path name for name in metadata.keys(): if isinstance(metadata[name], str): metadata[name] = sanitize_filename(metadata[name]) naming_format = naming_format.replace("\t", "").replace("\n", "") filename = ScriptParser().eval(naming_format, metadata, self) if settings["ascii_filenames"]: if isinstance(filename, str): filename = unaccent(filename) filename = replace_non_ascii(filename) # replace incompatible characters if settings["windows_compatibility"] or sys.platform == "win32": filename = replace_win32_incompat(filename) # remove null characters if isinstance(filename, (bytes, bytearray)): filename = filename.replace(b"\x00", "") return filename
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 _example_to_filename(self, file): settings = { 'windows_compatible_filenames': self.ui.windows_compatible_filenames.isChecked(), 'ascii_filenames': self.ui.ascii_filenames.isChecked(), 'rename_files': self.ui.rename_files.isChecked(), 'move_files': self.config.setting["move_files"], 'use_va_format': self.ui.use_va_format.isChecked(), 'file_naming_format': unicode(self.ui.file_naming_format.toPlainText()), 'va_file_naming_format': unicode(self.ui.va_file_naming_format.toPlainText()), 'move_files_to': os.path.normpath(unicode(self.config.setting["move_files_to"])), } try: if self.config.setting["enable_tagger_script"]: script = self.config.setting["tagger_script"] parser = ScriptParser() parser.eval(script, file.metadata) filename = file._make_filename(file.filename, file.metadata, settings) return filename except SyntaxError, e: return ""
def _example_to_filename(self, file): """Produce the before and after file naming example tuple for the specified file. Args: file (File): File to produce example before and after names Returns: tuple: Example before and after names for the specified file """ try: if self.settings["enable_tagger_scripts"]: for s_pos, s_name, s_enabled, s_text in self.settings[ "list_of_scripts"]: if s_enabled and s_text: parser = ScriptParser() parser.eval(s_text, file.metadata) filename_before = file.filename filename_after = file.make_filename(filename_before, file.metadata, self.settings) if not self.settings["move_files"]: return os.path.basename(filename_before), os.path.basename( filename_after) return filename_before, filename_after except ScriptError: return "", "" except TypeError: return "", ""
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 setUp(self): config.setting = { 'enabled_plugins': '', } self.parser = ScriptParser() def func_noargstest(parser): return "" register_script_function(func_noargstest, "noargstest")
def check_format(self): parser = ScriptParser() try: parser.eval(self.script_text) except Exception as e: raise ScriptCheckError("", str(e)) if self.ui.rename_files.isChecked(): if not self.script_text.strip(): raise ScriptCheckError("", _("The file naming format must not be empty."))
def check_format(self): parser = ScriptParser() try: parser.eval(self.ui.file_naming_format.toPlainText()) except Exception as e: raise OptionsCheckError("", string_(e)) if self.ui.rename_files.isChecked(): if not self.ui.file_naming_format.toPlainText().strip(): raise OptionsCheckError("", _("The file naming format must not be empty."))
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 check_format(self): """Parse the file naming script and check for errors. """ config = get_config() parser = ScriptParser() script_text = self.get_script() try: parser.eval(script_text) except Exception as e: raise ScriptCheckError("", str(e)) if config.setting["rename_files"]: if not self.get_script(): raise ScriptCheckError( "", _("The file naming format must not be empty."))
def _make_image_filename(self, filename, dirname, metadata): filename = ScriptParser().eval(filename, metadata) if config.setting["ascii_filenames"]: if isinstance(filename, unicode): filename = unaccent(filename) filename = replace_non_ascii(filename) if not filename: filename = "cover" if not os.path.isabs(filename): filename = os.path.join(dirname, filename) # replace incompatible characters if config.setting["windows_compatibility"] or sys.platform == "win32": filename = replace_win32_incompat(filename) # remove null characters filename = filename.replace("\x00", "") return encode_filename(filename)
def script_to_filename(naming_format, metadata, file=None, settings=None): if settings is None: settings = config.setting # make sure every metadata can safely be used in a path name for name in metadata: values = [sanitize_filename(str(v)) for v in metadata.getall(name)] metadata.set(name, values) naming_format = naming_format.replace("\t", "").replace("\n", "") filename = ScriptParser().eval(naming_format, metadata, file) if settings["ascii_filenames"]: filename = replace_non_ascii(filename, pathsave=True) # replace incompatible characters if settings["windows_compatibility"] or sys.platform == "win32": filename = replace_win32_incompat(filename) # remove null characters filename = filename.replace("\x00", "") return filename
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 _script_to_filename(self, format, file_metadata, settings): metadata = Metadata() metadata.copy(file_metadata) # make sure every metadata can safely be used in a path name for name in metadata.keys(): if isinstance(metadata[name], basestring): metadata[name] = sanitize_filename(metadata[name]) format = format.replace("\t", "").replace("\n", "") filename = ScriptParser().eval(format, metadata, self) # replace incompatible characters if settings["windows_compatible_filenames"] or sys.platform == "win32": filename = replace_win32_incompat(filename) if settings["ascii_filenames"]: if isinstance(filename, unicode): filename = unaccent(filename) filename = replace_non_ascii(filename) return filename
def _parse_recording(self, recording): recording_to_metadata(recording, self, self.config) self._customize_metadata() m = self.metadata run_track_metadata_processors(self.album, m, None, recording) if self.config.setting["enable_tagger_script"]: script = self.config.setting["tagger_script"] if script: parser = ScriptParser() try: parser.eval(script, m) except: self.log.error(traceback.format_exc()) m.strip_whitespace() self.loaded = True if self.callback: self.callback() self.tagger.nats.update(True)
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 _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) if config.setting["enable_tagger_scripts"]: for s_pos, s_name, s_enabled, s_text in config.setting[ "list_of_scripts"]: if s_enabled and s_text: parser = ScriptParser() try: parser.eval(s_text, m) except: log.error(traceback.format_exc()) m.strip_whitespace() self.loaded = True if self.callback: self.callback() self.callback = None self.tagger.nats.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 script_to_filename_with_metadata(naming_format, metadata, file=None, settings=None): """Creates a valid filename from a script with the given metadata. Args: naming_format: A string containing the tagger script. The result of executing this script will be the filename. metadata: A Metadata object. The metadata will not be modified. file: A File object (optional) settings: The settings. If not set config.setting will be used. Returns: A tuple with the filename as first element and the updated metadata with changes from the script as second. """ if settings is None: config = get_config() settings = config.setting # make sure every metadata can safely be used in a path name win_compat = IS_WIN or settings["windows_compatibility"] new_metadata = Metadata() for name in metadata: new_metadata[name] = [ sanitize_filename(str(v), win_compat=win_compat) for v in metadata.getall(name) ] naming_format = naming_format.replace("\t", "").replace("\n", "") filename = ScriptParser().eval(naming_format, new_metadata, file) if settings["ascii_filenames"]: filename = replace_non_ascii(filename, pathsave=True, win_compat=win_compat) # replace incompatible characters if win_compat: filename = replace_win32_incompat(filename) # remove null characters filename = filename.replace("\x00", "") return (filename, new_metadata)
def script_to_filename(naming_format, metadata, file=None, settings=None): if settings is None: settings = config.setting # make sure every metadata can safely be used in a path name win_compat = IS_WIN or settings["windows_compatibility"] meta = Metadata() for name in metadata: meta[name] = [ sanitize_filename(str(v), win_compat=win_compat) for v in metadata.getall(name) ] naming_format = naming_format.replace("\t", "").replace("\n", "") filename = ScriptParser().eval(naming_format, meta, file) if settings["ascii_filenames"]: filename = replace_non_ascii(filename, pathsave=True, win_compat=win_compat) # replace incompatible characters if win_compat: filename = replace_win32_incompat(filename) # remove null characters filename = filename.replace("\x00", "") return filename
def _make_image_filename(self, filename, dirname, _metadata): metadata = Metadata() metadata.copy(_metadata) metadata["coverart_maintype"] = self.maintype metadata["coverart_comment"] = self.comment if self.is_front: metadata.add_unique("coverart_types", "front") for cover_type in self.types: metadata.add_unique("coverart_types", cover_type) filename = ScriptParser().eval(filename, metadata) if config.setting["ascii_filenames"]: filename = replace_non_ascii(filename, pathsave=True) if not filename: filename = "cover" if not os.path.isabs(filename): filename = os.path.join(dirname, filename) # replace incompatible characters if config.setting["windows_compatibility"] or sys.platform == "win32": filename = replace_win32_incompat(filename) # remove null characters if isinstance(filename, bytes): filename = filename.replace(b"\x00", "") return encode_filename(filename)
def setUp(self): QtCore.QObject.config = FakeConfig() self.parser = ScriptParser()
def check_va_format(self): parser = ScriptParser() try: parser.eval(unicode(self.ui.va_file_naming_format.toPlainText())) except Exception, e: raise OptionsCheckError("", str(e))
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.medium_list[0].medium: 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 "pregap" in medium_node.children: discpregap = True absolutetracknumber += 1 track = self._finalize_loading_track( medium_node.pregap[0], mm, artists, va, absolutetracknumber, discpregap) track.metadata['~pregap'] = "1" for track_node in medium_node.track_list[0].track: absolutetracknumber += 1 track = self._finalize_loading_track( track_node, mm, artists, va, absolutetracknumber, discpregap) if "data_track_list" in medium_node.children: for track_node in medium_node.data_track_list[0].track: 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 self._tracks_loaded = True if not self._requests: # Prepare parser for user's script if config.setting["enable_tagger_script"]: script = config.setting["tagger_script"] if script: parser = ScriptParser() for track in self._new_tracks: # Run tagger script for each track try: parser.eval(script, track.metadata) except: self.error_append(traceback.format_exc()) # Strip leading/trailing whitespace track.metadata.strip_whitespace() # Run tagger script for the album itself try: parser.eval(script, self._new_metadata) except: self.error_append(traceback.format_exc()) self._new_metadata.strip_whitespace() for track in self.tracks: 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.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 check(self): parser = ScriptParser() try: parser.eval(self.ui.tagger_script.toPlainText()) except Exception as e: raise ScriptCheckError(_("Script Error"), str(e))
def _finalize_loading(self, error): if error: self.metadata.clear() self.metadata['album'] = _("[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 djmix_ars = {} if hasattr(self._new_metadata, "_djmix_ars"): djmix_ars = self._new_metadata._djmix_ars for medium_node in self._release_node.medium_list[0].medium: mm = Metadata() mm.copy(self._new_metadata) medium_to_metadata(medium_node, mm) totalalbumtracks += int(mm["totaltracks"]) for dj in djmix_ars.get(mm["discnumber"], []): mm.add("djmixer", dj) for track_node in medium_node.track_list[0].track: track = Track(track_node.recording[0].id, self) self._new_tracks.append(track) # Get track metadata tm = track.metadata tm.copy(mm) track_to_metadata(track_node, track, self.config) track._customize_metadata() self._new_metadata.length += tm.length artists.add(tm["musicbrainz_artistid"]) # Run track metadata plugins try: run_track_metadata_processors(self, tm, self._release_node, track_node) except: self.log.error(traceback.format_exc()) totalalbumtracks = str(totalalbumtracks) for track in self._new_tracks: track.metadata["~totalalbumtracks"] = totalalbumtracks if len(artists) > 1: track.metadata["compilation"] = "1" del self._release_node self._tracks_loaded = True if not self._requests: # Prepare parser for user's script if self.config.setting["enable_tagger_script"]: script = self.config.setting["tagger_script"] if script: parser = ScriptParser() for track in self._new_tracks: # Run tagger script for each track try: parser.eval(script, track.metadata) except: self.log.error(traceback.format_exc()) # Strip leading/trailing whitespace track.metadata.strip_whitespace() # Run tagger script for the album itself try: parser.eval(script, self._new_metadata) except: self.log.error(traceback.format_exc()) self._new_metadata.strip_whitespace() for track in self.tracks: 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.match_files(self.unmatched_files.files) self.update() self.tagger.window.set_statusbar_message('Album %s loaded', self.id, timeout=3000) while self._after_load_callbacks.qsize() > 0: func = self._after_load_callbacks.get() func()
def setUp(self): config.setting = { 'enabled_plugins': '', } self.parser = ScriptParser()
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 = []