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 "" except UnknownFunction: return ""
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): """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 _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': self.ui.file_naming_format.toPlainText(), 'move_files_to': os.path.normpath(self.ui.move_files_to.text()) } 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 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 _script_to_filename(self, 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], basestring): metadata[name] = sanitize_filename(metadata[name]) format = format.replace("\t", "").replace("\n", "") filename = ScriptParser().eval(format, metadata, self) if settings["ascii_filenames"]: if isinstance(filename, unicode): 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 filename = filename.replace("\x00", "") return filename
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 # Run metadata plugins while self._metadata_plugins: try: self._metadata_plugins.popleft()() except: self.log.error(traceback.format_exc()) if not self._requests: # Prepare parser for user's script script = None if self.config.setting["enable_tagger_script"]: script = self.config.setting["tagger_script"] parser = ScriptParser() for track in self._new_tracks: # Update the track with new album metadata, in case it # was modified by plugins. for key, values in self._new_metadata.rawitems(): track.metadata[key] = values[:] # Run tagger script for each track if script: 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 if script: 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 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(unicode(self.ui.file_naming_format.toPlainText())) except Exception as e: raise OptionsCheckError("", str(e)) if self.ui.rename_files.isChecked(): if not unicode(self.ui.file_naming_format.toPlainText()).strip(): raise OptionsCheckError("", _("The file naming format must not be empty."))
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 process(self, script): files = self.files for file in files: m = file.metadata parser = ScriptParser() try: parser.eval(script, m) except: log.error(traceback.format_exc()) m.strip_whitespace()
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 _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 _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(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 _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 _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 _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 "" except TypeError, e: return "" except UnknownFunction, e: return ""
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 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 _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 _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 _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 check(self): parser = ScriptParser() try: parser.eval(unicode(self.ui.tagger_script.toPlainText())) except Exception as e: raise OptionsCheckError(_("Script Error"), str(e))
class ScriptParserTest(unittest.TestCase): def setUp(self): config.setting = { 'enabled_plugins': '', } self.parser = ScriptParser() def func_noargstest(parser): return "" register_script_function(func_noargstest, "noargstest") def assertScriptResultEquals(self, script, expected, context=None): """Asserts that evaluating `script` returns `expected`. Args: script: The tagger script expected: The expected result context: A Metadata object with pre-set tags or None """ actual = self.parser.eval(script, context=context) self.assertEqual(actual, expected, "'%s' evaluated to '%s', expected '%s'" % (script, actual, expected)) def test_cmd_noop(self): self.assertScriptResultEquals("$noop()", "") self.assertScriptResultEquals("$noop(abcdefg)", "") def test_cmd_if(self): self.assertScriptResultEquals("$if(1,a,b)", "a") self.assertScriptResultEquals("$if(,a,b)", "b") def test_cmd_if2(self): self.assertScriptResultEquals("$if2(,a,b)", "a") self.assertScriptResultEquals("$if2($noop(),b)", "b") def test_cmd_left(self): self.assertScriptResultEquals("$left(abcd,2)", "ab") def test_cmd_right(self): self.assertScriptResultEquals("$right(abcd,2)", "cd") def test_cmd_set(self): self.assertScriptResultEquals("$set(test,aaa)%test%", "aaa") def test_cmd_set_empty(self): self.assertScriptResultEquals("$set(test,)%test%", "") def test_cmd_set_multi_valued(self): context = Metadata() context["source"] = ["multi", "valued"] self.parser.eval("$set(test,%source%)", context) self.assertEqual(context.getall("test"), ["multi; valued"]) # list has only a single value def test_cmd_setmulti_multi_valued(self): context = Metadata() context["source"] = ["multi", "valued"] self.assertEqual("", self.parser.eval("$setmulti(test,%source%)", context)) # no return value self.assertEqual(context.getall("source"), context.getall("test")) def test_cmd_setmulti_multi_valued_wth_spaces(self): context = Metadata() context["source"] = ["multi, multi", "valued, multi"] self.assertEqual("", self.parser.eval("$setmulti(test,%source%)", context)) # no return value self.assertEqual(context.getall("source"), context.getall("test")) def test_cmd_setmulti_not_multi_valued(self): context = Metadata() context["source"] = "multi, multi" self.assertEqual("", self.parser.eval("$setmulti(test,%source%)", context)) # no return value self.assertEqual(context.getall("source"), context.getall("test")) def test_cmd_setmulti_will_remove_empty_items(self): context = Metadata() context["source"] = ["", "multi", ""] self.assertEqual("", self.parser.eval("$setmulti(test,%source%)", context)) # no return value self.assertEqual(["multi"], context.getall("test")) def test_cmd_setmulti_custom_splitter_string(self): context = Metadata() self.assertEqual("", self.parser.eval("$setmulti(test,multi##valued##test##,##)", context)) # no return value self.assertEqual(["multi", "valued", "test"], context.getall("test")) def test_cmd_setmulti_empty_splitter_does_nothing(self): context = Metadata() self.assertEqual("", self.parser.eval("$setmulti(test,multi; valued,)", context)) # no return value self.assertEqual(["multi; valued"], context.getall("test")) def test_cmd_get(self): context = Metadata() context["test"] = "aaa" self.assertScriptResultEquals("$get(test)", "aaa", context) context["test2"] = ["multi", "valued"] self.assertScriptResultEquals("$get(test2)", "multi; valued", context) def test_cmd_num(self): self.assertScriptResultEquals("$num(3,3)", "003") self.assertScriptResultEquals("$num(03,3)", "003") self.assertScriptResultEquals("$num(123,2)", "123") def test_cmd_or(self): self.assertScriptResultEquals("$or(,)", "") self.assertScriptResultEquals("$or(,,)", "") self.assertScriptResultEquals("$or(,q)", "1") self.assertScriptResultEquals("$or(q,)", "1") self.assertScriptResultEquals("$or(q,q)", "1") self.assertScriptResultEquals("$or(q,,)", "1") def test_cmd_and(self): self.assertScriptResultEquals("$and(,)", "") self.assertScriptResultEquals("$and(,q)", "") self.assertScriptResultEquals("$and(q,)", "") self.assertScriptResultEquals("$and(q,q,)", "") self.assertScriptResultEquals("$and(q,q)", "1") self.assertScriptResultEquals("$and(q,q,q)", "1") def test_cmd_not(self): self.assertScriptResultEquals("$not($noop())", "1") self.assertScriptResultEquals("$not(q)", "") def test_cmd_add(self): self.assertScriptResultEquals("$add(1,2)", "3") self.assertScriptResultEquals("$add(1,2,3)", "6") def test_cmd_sub(self): self.assertScriptResultEquals("$sub(1,2)", "-1") self.assertScriptResultEquals("$sub(2,1)", "1") self.assertScriptResultEquals("$sub(4,2,1)", "1") def test_cmd_div(self): self.assertScriptResultEquals("$div(9,3)", "3") self.assertScriptResultEquals("$div(10,3)", "3") self.assertScriptResultEquals("$div(30,3,3)", "3") def test_cmd_mod(self): self.assertScriptResultEquals("$mod(9,3)", "0") self.assertScriptResultEquals("$mod(10,3)", "1") self.assertScriptResultEquals("$mod(10,6,3)", "1") def test_cmd_mul(self): self.assertScriptResultEquals("$mul(9,3)", "27") self.assertScriptResultEquals("$mul(10,3)", "30") self.assertScriptResultEquals("$mul(2,5,3)", "30") def test_cmd_eq(self): self.assertScriptResultEquals("$eq(,)", "1") self.assertScriptResultEquals("$eq(,$noop())", "1") self.assertScriptResultEquals("$eq(,q)", "") self.assertScriptResultEquals("$eq(q,q)", "1") self.assertScriptResultEquals("$eq(q,)", "") def test_cmd_ne(self): self.assertScriptResultEquals("$ne(,)", "") self.assertScriptResultEquals("$ne(,$noop())", "") self.assertScriptResultEquals("$ne(,q)", "1") self.assertScriptResultEquals("$ne(q,q)", "") self.assertScriptResultEquals("$ne(q,)", "1") def test_cmd_lower(self): self.assertScriptResultEquals("$lower(AbeCeDA)", "abeceda") def test_cmd_upper(self): self.assertScriptResultEquals("$upper(AbeCeDA)", "ABECEDA") def test_cmd_rreplace(self): self.assertScriptResultEquals(r'''$rreplace(test \(disc 1\),\\s\\\(disc \\d+\\\),)''', "test") def test_cmd_rsearch(self): self.assertScriptResultEquals(r"$rsearch(test \(disc 1\),\\\(disc \(\\d+\)\\\))", "1") def test_arguments(self): self.assertTrue( self.parser.eval( r"$set(bleh,$rsearch(test \(disc 1\),\\\(disc \(\\d+\)\\\)))) $set(wer,1)")) def test_cmd_gt(self): self.assertScriptResultEquals("$gt(10,4)", "1") self.assertScriptResultEquals("$gt(6,4)", "1") def test_cmd_gte(self): self.assertScriptResultEquals("$gte(10,10)", "1") self.assertScriptResultEquals("$gte(10,4)", "1") self.assertScriptResultEquals("$gte(6,4)", "1") def test_cmd_lt(self): self.assertScriptResultEquals("$lt(4,10)", "1") self.assertScriptResultEquals("$lt(4,6)", "1") def test_cmd_lte(self): self.assertScriptResultEquals("$lte(10,10)", "1") self.assertScriptResultEquals("$lte(4,10)", "1") self.assertScriptResultEquals("$lte(4,6)", "1") def test_cmd_len(self): self.assertScriptResultEquals("$len(abcdefg)", "7") self.assertScriptResultEquals("$len(0)", "1") self.assertScriptResultEquals("$len()", "0") def test_cmd_firstalphachar(self): self.assertScriptResultEquals("$firstalphachar(abc)", "A") self.assertScriptResultEquals("$firstalphachar(Abc)", "A") self.assertScriptResultEquals("$firstalphachar(1abc)", "#") self.assertScriptResultEquals("$firstalphachar(...abc)", "#") self.assertScriptResultEquals("$firstalphachar(1abc,_)", "_") self.assertScriptResultEquals("$firstalphachar(...abc,_)", "_") self.assertScriptResultEquals("$firstalphachar()", "#") self.assertScriptResultEquals("$firstalphachar(,_)", "_") self.assertScriptResultEquals("$firstalphachar( abc)", "#") def test_cmd_initials(self): self.assertScriptResultEquals("$initials(Abc def Ghi)", "AdG") self.assertScriptResultEquals("$initials(Abc #def Ghi)", "AG") self.assertScriptResultEquals("$initials(Abc 1def Ghi)", "AG") self.assertScriptResultEquals("$initials(Abc)", "A") self.assertScriptResultEquals("$initials()", "") def test_cmd_firstwords(self): self.assertScriptResultEquals("$firstwords(Abc Def Ghi,11)", "Abc Def Ghi") self.assertScriptResultEquals("$firstwords(Abc Def Ghi,12)", "Abc Def Ghi") self.assertScriptResultEquals("$firstwords(Abc Def Ghi,7)", "Abc Def") self.assertScriptResultEquals("$firstwords(Abc Def Ghi,8)", "Abc Def") self.assertScriptResultEquals("$firstwords(Abc Def Ghi,6)", "Abc") self.assertScriptResultEquals("$firstwords(Abc Def Ghi,0)", "") self.assertScriptResultEquals("$firstwords(Abc Def Ghi,NaN)", "") self.assertScriptResultEquals("$firstwords(Abc Def Ghi,)", "") def test_cmd_startswith(self): self.assertScriptResultEquals("$startswith(abc,a)", "1") self.assertScriptResultEquals("$startswith(abc,abc)", "1") self.assertScriptResultEquals("$startswith(abc,)", "1") self.assertScriptResultEquals("$startswith(abc,b)", "0") self.assertScriptResultEquals("$startswith(abc,Ab)", "0") def test_cmd_endswith(self): self.assertScriptResultEquals("$endswith(abc,c)", "1") self.assertScriptResultEquals("$endswith(abc,abc)", "1") self.assertScriptResultEquals("$endswith(abc,)", "1") self.assertScriptResultEquals("$endswith(abc,b)", "0") self.assertScriptResultEquals("$endswith(abc,bC)", "0") def test_cmd_truncate(self): self.assertScriptResultEquals("$truncate(abcdefg,0)", "") self.assertScriptResultEquals("$truncate(abcdefg,7)", "abcdefg") self.assertScriptResultEquals("$truncate(abcdefg,3)", "abc") self.assertScriptResultEquals("$truncate(abcdefg,10)", "abcdefg") self.assertScriptResultEquals("$truncate(abcdefg,)", "abcdefg") self.assertScriptResultEquals("$truncate(abcdefg,NaN)", "abcdefg") def test_cmd_copy(self): context = Metadata() tagsToCopy = ["tag1", "tag2"] context["source"] = tagsToCopy context["target"] = ["will", "be", "overwritten"] self.parser.eval("$copy(target,source)", context) self.assertEqual(self.parser.context.getall("target"), tagsToCopy) def _eval_and_check_copymerge(self, context, expected): self.parser.eval("$copymerge(target,source)", context) self.assertEqual(self.parser.context.getall("target"), expected) def test_cmd_copymerge_notarget(self): context = Metadata() tagsToCopy = ["tag1", "tag2"] context["source"] = tagsToCopy self._eval_and_check_copymerge(context, tagsToCopy) def test_cmd_copymerge_nosource(self): context = Metadata() target = ["tag1", "tag2"] context["target"] = target self._eval_and_check_copymerge(context, target) def test_cmd_copymerge_removedupes(self): context = Metadata() context["target"] = ["tag1", "tag2", "tag1"] context["source"] = ["tag2", "tag3", "tag2"] self._eval_and_check_copymerge(context, ["tag1", "tag2", "tag3"]) def test_cmd_copymerge_nonlist(self): context = Metadata() context["target"] = "targetval" context["source"] = "sourceval" self._eval_and_check_copymerge(context, ["targetval", "sourceval"]) def test_cmd_eq_any(self): self.assertScriptResultEquals("$eq_any(abc,def,ghi,jkl)", "") self.assertScriptResultEquals("$eq_any(abc,def,ghi,jkl,abc)", "1") def test_cmd_ne_all(self): self.assertScriptResultEquals("$ne_all(abc,def,ghi,jkl)", "1") self.assertScriptResultEquals("$ne_all(abc,def,ghi,jkl,abc)", "") def test_cmd_eq_all(self): self.assertScriptResultEquals("$eq_all(abc,abc,abc,abc)", "1") self.assertScriptResultEquals("$eq_all(abc,abc,def,ghi)", "") def test_cmd_ne_any(self): self.assertScriptResultEquals("$ne_any(abc,abc,abc,abc)", "") self.assertScriptResultEquals("$ne_any(abc,abc,def,ghi)", "1") def test_cmd_swapprefix(self): self.assertScriptResultEquals("$swapprefix(A stitch in time)", "stitch in time, A") self.assertScriptResultEquals("$swapprefix(The quick brown fox)", "quick brown fox, The") self.assertScriptResultEquals("$swapprefix(How now brown cow)", "How now brown cow") self.assertScriptResultEquals("$swapprefix(When the red red robin)", "When the red red robin") self.assertScriptResultEquals("$swapprefix(A stitch in time,How,When,Who)", "A stitch in time") self.assertScriptResultEquals("$swapprefix(The quick brown fox,How,When,Who)", "The quick brown fox") self.assertScriptResultEquals("$swapprefix(How now brown cow,How,When,Who)", "now brown cow, How") self.assertScriptResultEquals("$swapprefix(When the red red robin,How,When,Who)", "the red red robin, When") def test_cmd_delprefix(self): self.assertScriptResultEquals("$delprefix(A stitch in time)", "stitch in time") self.assertScriptResultEquals("$delprefix(The quick brown fox)", "quick brown fox") self.assertScriptResultEquals("$delprefix(How now brown cow)", "How now brown cow") self.assertScriptResultEquals("$delprefix(When the red red robin)", "When the red red robin") self.assertScriptResultEquals("$delprefix(A stitch in time,How,When,Who)", "A stitch in time") self.assertScriptResultEquals("$delprefix(The quick brown fox,How,When,Who)", "The quick brown fox") self.assertScriptResultEquals("$delprefix(How now brown cow,How,When,Who)", "now brown cow") self.assertScriptResultEquals("$delprefix(When the red red robin,How,When,Who)", "the red red robin") def test_default_filenaming(self): context = Metadata() context['albumartist'] = u'albumartist' context['artist'] = u'artist' context['album'] = u'album' context['totaldiscs'] = 2 context['discnumber'] = 1 context['tracknumber'] = 8 context['title'] = u'title' result = self.parser.eval(_DEFAULT_FILE_NAMING_FORMAT, context) self.assertEqual(result, u'albumartist/album/1-08 title') context['~multiartist'] = '1' result = self.parser.eval(_DEFAULT_FILE_NAMING_FORMAT, context) self.assertEqual(result, u'albumartist/album/1-08 artist - title') def test_default_NAT_filenaming(self): context = Metadata() context['artist'] = u'artist' context['album'] = u'[non-album tracks]' context['title'] = u'title' result = self.parser.eval(_DEFAULT_FILE_NAMING_FORMAT, context) self.assertEqual(result, u'artist/title') def test_cmd_with_not_arguments(self): try: self.parser.eval("$noargstest()") except ScriptError: self.fail("Function noargs raised ScriptError unexpectedly.") def test_cmd_unset_simple(self): context = Metadata() context['title'] = u'Foo' context['album'] = u'Foo' context['artist'] = u'Foo' self.parser.eval("$unset(album)", context) self.assertNotIn('album', context) def test_cmd_unset_prefix(self): context = Metadata() context['title'] = u'Foo' context['~rating'] = u'4' self.parser.eval("$unset(_rating)", context) self.assertNotIn('~rating', context) def test_cmd_unset_multi(self): context = Metadata() context['performer:foo'] = u'Foo' context['performer:bar'] = u'Foo' self.parser.eval("$unset(performer:*)", context) self.assertNotIn('performer:bar', context) self.assertNotIn('performer:foo', context) def test_cmd_inmulti(self): context = Metadata() # Test with single-value string context["foo"] = "First:A; Second:B; Third:C" # Tests with $in for comparison purposes self.assertScriptResultEquals("$in(%foo%,Second:B)", "1", context) self.assertScriptResultEquals("$in(%foo%,irst:A; Second:B; Thi)", "1", context) self.assertScriptResultEquals("$in(%foo%,First:A; Second:B; Third:C)", "1", context) # Base $inmulti tests self.assertScriptResultEquals("$inmulti(%foo%,Second:B)", "", context) self.assertScriptResultEquals("$inmulti(%foo%,irst:A; Second:B; Thi)", "", context) self.assertScriptResultEquals("$inmulti(%foo%,First:A; Second:B; Third:C)", "1", context) # Test separator override but with existing separator - results should be same as base self.assertScriptResultEquals("$inmulti(%foo%,Second:B,; )", "", context) self.assertScriptResultEquals("$inmulti(%foo%,irst:A; Second:B; Thi,; )", "", context) self.assertScriptResultEquals("$inmulti(%foo%,First:A; Second:B; Third:C,; )", "1", context) # Test separator override self.assertScriptResultEquals("$inmulti(%foo%,First:A,:)", "", context) self.assertScriptResultEquals("$inmulti(%foo%,Second:B,:)", "", context) self.assertScriptResultEquals("$inmulti(%foo%,Third:C,:)", "", context) self.assertScriptResultEquals("$inmulti(%foo%,First,:)", "1", context) self.assertScriptResultEquals("$inmulti(%foo%,A; Second,:)", "1", context) self.assertScriptResultEquals("$inmulti(%foo%,B; Third,:)", "1", context) self.assertScriptResultEquals("$inmulti(%foo%,C,:)", "1", context) # Test with multi-values context["foo"] = ["First:A", "Second:B", "Third:C"] # Tests with $in for comparison purposes self.assertScriptResultEquals("$in(%foo%,Second:B)", "1", context) self.assertScriptResultEquals("$in(%foo%,irst:A; Second:B; Thi)", "1", context) self.assertScriptResultEquals("$in(%foo%,First:A; Second:B; Third:C)", "1", context) # Base $inmulti tests self.assertScriptResultEquals("$inmulti(%foo%,Second:B)", "1", context) self.assertScriptResultEquals("$inmulti(%foo%,irst:A; Second:B; Thi)", "", context) self.assertScriptResultEquals("$inmulti(%foo%,First:A; Second:B; Third:C)", "", context) # Test separator override but with existing separator - results should be same as base self.assertScriptResultEquals("$inmulti(%foo%,Second:B,; )", "1", context) self.assertScriptResultEquals("$inmulti(%foo%,irst:A; Second:B; Thi,; )", "", context) self.assertScriptResultEquals("$inmulti(%foo%,First:A; Second:B; Third:C,; )", "", context) # Test separator override self.assertScriptResultEquals("$inmulti(%foo%,First:A,:)", "", context) self.assertScriptResultEquals("$inmulti(%foo%,Second:B,:)", "", context) self.assertScriptResultEquals("$inmulti(%foo%,Third:C,:)", "", context) self.assertScriptResultEquals("$inmulti(%foo%,First,:)", "1", context) self.assertScriptResultEquals("$inmulti(%foo%,A; Second,:)", "1", context) self.assertScriptResultEquals("$inmulti(%foo%,B; Third,:)", "1", context) self.assertScriptResultEquals("$inmulti(%foo%,C,:)", "1", context) def test_cmd_lenmulti(self): context = Metadata() context["foo"] = "First:A; Second:B; Third:C" context["bar"] = ["First:A", "Second:B", "Third:C"] # Tests with $len for comparison purposes self.assertScriptResultEquals("$len(%foo%)", "26", context) self.assertScriptResultEquals("$len(%bar%)", "26", context) # Base $lenmulti tests self.assertScriptResultEquals("$lenmulti(%foo%)", "1", context) self.assertScriptResultEquals("$lenmulti(%bar%)", "3", context) self.assertScriptResultEquals("$lenmulti(%foo%.)", "3", context) # Test separator override but with existing separator - results should be same as base self.assertScriptResultEquals("$lenmulti(%foo%,; )", "1", context) self.assertScriptResultEquals("$lenmulti(%bar%,; )", "3", context) self.assertScriptResultEquals("$lenmulti(%foo%.,; )", "3", context) # Test separator override self.assertScriptResultEquals("$lenmulti(%foo%,:)", "4", context) self.assertScriptResultEquals("$lenmulti(%bar%,:)", "4", context) self.assertScriptResultEquals("$lenmulti(%foo%.,:)", "4", context) def test_required_kwonly_parameters(self): def func(a, *, required_kwarg): pass with self.assertRaises(TypeError, msg="Functions with required keyword-only parameters are not supported"): register_script_function(func) def test_optional_kwonly_parameters(self): def func(a, *, optional_kwarg=1): pass register_script_function(func)
class ScriptParserTest(PicardTestCase): def setUp(self): super().setUp() config.setting = { 'enabled_plugins': '', } self.parser = ScriptParser() def func_noargstest(parser): return "" register_script_function(func_noargstest, "noargstest") def assertScriptResultEquals(self, script, expected, context=None, file=None): """Asserts that evaluating `script` returns `expected`. Args: script: The tagger script expected: The expected result context: A Metadata object with pre-set tags or None """ actual = self.parser.eval(script, context=context, file=file) self.assertEqual( actual, expected, "'%s' evaluated to '%s', expected '%s'" % (script, actual, expected)) def test_cmd_noop(self): self.assertScriptResultEquals("$noop()", "") self.assertScriptResultEquals("$noop(abcdefg)", "") def test_cmd_if(self): self.assertScriptResultEquals("$if(1,a,b)", "a") self.assertScriptResultEquals("$if(,a,b)", "b") def test_cmd_if2(self): self.assertScriptResultEquals("$if2(,a,b)", "a") self.assertScriptResultEquals("$if2($noop(),b)", "b") def test_cmd_left(self): self.assertScriptResultEquals("$left(abcd,2)", "ab") def test_cmd_right(self): self.assertScriptResultEquals("$right(abcd,2)", "cd") def test_cmd_set(self): self.assertScriptResultEquals("$set(test,aaa)%test%", "aaa") def test_cmd_set_empty(self): self.assertScriptResultEquals("$set(test,)%test%", "") def test_cmd_set_multi_valued(self): context = Metadata() context["source"] = ["multi", "valued"] self.parser.eval("$set(test,%source%)", context) self.assertEqual(context.getall("test"), ["multi; valued"]) # list has only a single value def test_cmd_setmulti_multi_valued(self): context = Metadata() context["source"] = ["multi", "valued"] self.assertEqual("", self.parser.eval("$setmulti(test,%source%)", context)) # no return value self.assertEqual(context.getall("source"), context.getall("test")) def test_cmd_setmulti_multi_valued_wth_spaces(self): context = Metadata() context["source"] = ["multi, multi", "valued, multi"] self.assertEqual("", self.parser.eval("$setmulti(test,%source%)", context)) # no return value self.assertEqual(context.getall("source"), context.getall("test")) def test_cmd_setmulti_not_multi_valued(self): context = Metadata() context["source"] = "multi, multi" self.assertEqual("", self.parser.eval("$setmulti(test,%source%)", context)) # no return value self.assertEqual(context.getall("source"), context.getall("test")) def test_cmd_setmulti_will_remove_empty_items(self): context = Metadata() context["source"] = ["", "multi", ""] self.assertEqual("", self.parser.eval("$setmulti(test,%source%)", context)) # no return value self.assertEqual(["multi"], context.getall("test")) def test_cmd_setmulti_custom_splitter_string(self): context = Metadata() self.assertEqual("", self.parser.eval( "$setmulti(test,multi##valued##test##,##)", context)) # no return value self.assertEqual(["multi", "valued", "test"], context.getall("test")) def test_cmd_setmulti_empty_splitter_does_nothing(self): context = Metadata() self.assertEqual("", self.parser.eval("$setmulti(test,multi; valued,)", context)) # no return value self.assertEqual(["multi; valued"], context.getall("test")) def test_cmd_get(self): context = Metadata() context["test"] = "aaa" self.assertScriptResultEquals("$get(test)", "aaa", context) context["test2"] = ["multi", "valued"] self.assertScriptResultEquals("$get(test2)", "multi; valued", context) def test_cmd_num(self): self.assertScriptResultEquals("$num(3,3)", "003") self.assertScriptResultEquals("$num(03,3)", "003") self.assertScriptResultEquals("$num(123,2)", "123") def test_cmd_or(self): self.assertScriptResultEquals("$or(,)", "") self.assertScriptResultEquals("$or(,,)", "") self.assertScriptResultEquals("$or(,q)", "1") self.assertScriptResultEquals("$or(q,)", "1") self.assertScriptResultEquals("$or(q,q)", "1") self.assertScriptResultEquals("$or(q,,)", "1") def test_cmd_and(self): self.assertScriptResultEquals("$and(,)", "") self.assertScriptResultEquals("$and(,q)", "") self.assertScriptResultEquals("$and(q,)", "") self.assertScriptResultEquals("$and(q,q,)", "") self.assertScriptResultEquals("$and(q,q)", "1") self.assertScriptResultEquals("$and(q,q,q)", "1") def test_cmd_not(self): self.assertScriptResultEquals("$not($noop())", "1") self.assertScriptResultEquals("$not(q)", "") def test_cmd_add(self): self.assertScriptResultEquals("$add(1,2)", "3") self.assertScriptResultEquals("$add(1,2,3)", "6") def test_cmd_sub(self): self.assertScriptResultEquals("$sub(1,2)", "-1") self.assertScriptResultEquals("$sub(2,1)", "1") self.assertScriptResultEquals("$sub(4,2,1)", "1") def test_cmd_div(self): self.assertScriptResultEquals("$div(9,3)", "3") self.assertScriptResultEquals("$div(10,3)", "3") self.assertScriptResultEquals("$div(30,3,3)", "3") def test_cmd_mod(self): self.assertScriptResultEquals("$mod(9,3)", "0") self.assertScriptResultEquals("$mod(10,3)", "1") self.assertScriptResultEquals("$mod(10,6,3)", "1") def test_cmd_mul(self): self.assertScriptResultEquals("$mul(9,3)", "27") self.assertScriptResultEquals("$mul(10,3)", "30") self.assertScriptResultEquals("$mul(2,5,3)", "30") def test_cmd_eq(self): self.assertScriptResultEquals("$eq(,)", "1") self.assertScriptResultEquals("$eq(,$noop())", "1") self.assertScriptResultEquals("$eq(,q)", "") self.assertScriptResultEquals("$eq(q,q)", "1") self.assertScriptResultEquals("$eq(q,)", "") def test_cmd_ne(self): self.assertScriptResultEquals("$ne(,)", "") self.assertScriptResultEquals("$ne(,$noop())", "") self.assertScriptResultEquals("$ne(,q)", "1") self.assertScriptResultEquals("$ne(q,q)", "") self.assertScriptResultEquals("$ne(q,)", "1") def test_cmd_lower(self): self.assertScriptResultEquals("$lower(AbeCeDA)", "abeceda") def test_cmd_upper(self): self.assertScriptResultEquals("$upper(AbeCeDA)", "ABECEDA") def test_cmd_pad(self): self.assertScriptResultEquals("$pad(abc de,10,-)", "----abc de") self.assertScriptResultEquals("$pad(abc de,e,-)", "") self.assertScriptResultEquals("$pad(abc de,6,-)", "abc de") self.assertScriptResultEquals("$pad(abc de,3,-)", "abc de") self.assertScriptResultEquals("$pad(abc de,0,-)", "abc de") self.assertScriptResultEquals("$pad(abc de,-3,-)", "abc de") def test_cmd_replace(self): self.assertScriptResultEquals("$replace(abc ab abd a,ab,test)", "testc test testd a") def test_cmd_strip(self): self.assertScriptResultEquals("$strip( \t abc de \n f )", "abc de f") def test_cmd_rreplace(self): self.assertScriptResultEquals( r'''$rreplace(test \(disc 1\),\\s\\\(disc \\d+\\\),)''', "test") def test_cmd_rsearch(self): self.assertScriptResultEquals( r"$rsearch(test \(disc 1\),\\\(disc \(\\d+\)\\\))", "1") def test_arguments(self): self.assertTrue( self.parser.eval( r"$set(bleh,$rsearch(test \(disc 1\),\\\(disc \(\\d+\)\\\)))) $set(wer,1)" )) def test_cmd_gt(self): self.assertScriptResultEquals("$gt(10,4)", "1") self.assertScriptResultEquals("$gt(6,4)", "1") def test_cmd_gte(self): self.assertScriptResultEquals("$gte(10,10)", "1") self.assertScriptResultEquals("$gte(10,4)", "1") self.assertScriptResultEquals("$gte(6,4)", "1") def test_cmd_lt(self): self.assertScriptResultEquals("$lt(4,10)", "1") self.assertScriptResultEquals("$lt(4,6)", "1") def test_cmd_lte(self): self.assertScriptResultEquals("$lte(10,10)", "1") self.assertScriptResultEquals("$lte(4,10)", "1") self.assertScriptResultEquals("$lte(4,6)", "1") def test_cmd_len(self): self.assertScriptResultEquals("$len(abcdefg)", "7") self.assertScriptResultEquals("$len(0)", "1") self.assertScriptResultEquals("$len()", "0") def test_cmd_firstalphachar(self): self.assertScriptResultEquals("$firstalphachar(abc)", "A") self.assertScriptResultEquals("$firstalphachar(Abc)", "A") self.assertScriptResultEquals("$firstalphachar(1abc)", "#") self.assertScriptResultEquals("$firstalphachar(...abc)", "#") self.assertScriptResultEquals("$firstalphachar(1abc,_)", "_") self.assertScriptResultEquals("$firstalphachar(...abc,_)", "_") self.assertScriptResultEquals("$firstalphachar()", "#") self.assertScriptResultEquals("$firstalphachar(,_)", "_") self.assertScriptResultEquals("$firstalphachar( abc)", "#") def test_cmd_initials(self): self.assertScriptResultEquals("$initials(Abc def Ghi)", "AdG") self.assertScriptResultEquals("$initials(Abc #def Ghi)", "AG") self.assertScriptResultEquals("$initials(Abc 1def Ghi)", "AG") self.assertScriptResultEquals("$initials(Abc)", "A") self.assertScriptResultEquals("$initials()", "") def test_cmd_firstwords(self): self.assertScriptResultEquals("$firstwords(Abc Def Ghi,11)", "Abc Def Ghi") self.assertScriptResultEquals("$firstwords(Abc Def Ghi,12)", "Abc Def Ghi") self.assertScriptResultEquals("$firstwords(Abc Def Ghi,7)", "Abc Def") self.assertScriptResultEquals("$firstwords(Abc Def Ghi,8)", "Abc Def") self.assertScriptResultEquals("$firstwords(Abc Def Ghi,6)", "Abc") self.assertScriptResultEquals("$firstwords(Abc Def Ghi,0)", "") self.assertScriptResultEquals("$firstwords(Abc Def Ghi,NaN)", "") self.assertScriptResultEquals("$firstwords(Abc Def Ghi,)", "") def test_cmd_startswith(self): self.assertScriptResultEquals("$startswith(abc,a)", "1") self.assertScriptResultEquals("$startswith(abc,abc)", "1") self.assertScriptResultEquals("$startswith(abc,)", "1") self.assertScriptResultEquals("$startswith(abc,b)", "0") self.assertScriptResultEquals("$startswith(abc,Ab)", "0") def test_cmd_endswith(self): self.assertScriptResultEquals("$endswith(abc,c)", "1") self.assertScriptResultEquals("$endswith(abc,abc)", "1") self.assertScriptResultEquals("$endswith(abc,)", "1") self.assertScriptResultEquals("$endswith(abc,b)", "0") self.assertScriptResultEquals("$endswith(abc,bC)", "0") def test_cmd_truncate(self): self.assertScriptResultEquals("$truncate(abcdefg,0)", "") self.assertScriptResultEquals("$truncate(abcdefg,7)", "abcdefg") self.assertScriptResultEquals("$truncate(abcdefg,3)", "abc") self.assertScriptResultEquals("$truncate(abcdefg,10)", "abcdefg") self.assertScriptResultEquals("$truncate(abcdefg,)", "abcdefg") self.assertScriptResultEquals("$truncate(abcdefg,NaN)", "abcdefg") def test_cmd_copy(self): context = Metadata() tagsToCopy = ["tag1", "tag2"] context["source"] = tagsToCopy context["target"] = ["will", "be", "overwritten"] self.parser.eval("$copy(target,source)", context) self.assertEqual(self.parser.context.getall("target"), tagsToCopy) def _eval_and_check_copymerge(self, context, expected): self.parser.eval("$copymerge(target,source)", context) self.assertEqual(self.parser.context.getall("target"), expected) def test_cmd_copymerge_notarget(self): context = Metadata() tagsToCopy = ["tag1", "tag2"] context["source"] = tagsToCopy self._eval_and_check_copymerge(context, tagsToCopy) def test_cmd_copymerge_nosource(self): context = Metadata() target = ["tag1", "tag2"] context["target"] = target self._eval_and_check_copymerge(context, target) def test_cmd_copymerge_removedupes(self): context = Metadata() context["target"] = ["tag1", "tag2", "tag1"] context["source"] = ["tag2", "tag3", "tag2"] self._eval_and_check_copymerge(context, ["tag1", "tag2", "tag3"]) def test_cmd_copymerge_nonlist(self): context = Metadata() context["target"] = "targetval" context["source"] = "sourceval" self._eval_and_check_copymerge(context, ["targetval", "sourceval"]) def test_cmd_eq_any(self): self.assertScriptResultEquals("$eq_any(abc,def,ghi,jkl)", "") self.assertScriptResultEquals("$eq_any(abc,def,ghi,jkl,abc)", "1") def test_cmd_ne_all(self): self.assertScriptResultEquals("$ne_all(abc,def,ghi,jkl)", "1") self.assertScriptResultEquals("$ne_all(abc,def,ghi,jkl,abc)", "") def test_cmd_eq_all(self): self.assertScriptResultEquals("$eq_all(abc,abc,abc,abc)", "1") self.assertScriptResultEquals("$eq_all(abc,abc,def,ghi)", "") def test_cmd_ne_any(self): self.assertScriptResultEquals("$ne_any(abc,abc,abc,abc)", "") self.assertScriptResultEquals("$ne_any(abc,abc,def,ghi)", "1") def test_cmd_title(self): self.assertScriptResultEquals("$title(abc Def g)", "Abc Def G") self.assertScriptResultEquals("$title(Abc Def G)", "Abc Def G") self.assertScriptResultEquals("$title(abc def g)", "Abc Def G") self.assertScriptResultEquals("$title(#1abc 4def - g)", "#1abc 4def - G") self.assertScriptResultEquals("$title(abcd \\(efg hi jkl mno\\))", "Abcd (Efg Hi Jkl Mno)") self.assertScriptResultEquals("$title(...abcd)", "...Abcd") def test_cmd_swapprefix(self): self.assertScriptResultEquals("$swapprefix(A stitch in time)", "stitch in time, A") self.assertScriptResultEquals("$swapprefix(The quick brown fox)", "quick brown fox, The") self.assertScriptResultEquals("$swapprefix(How now brown cow)", "How now brown cow") self.assertScriptResultEquals("$swapprefix(When the red red robin)", "When the red red robin") self.assertScriptResultEquals( "$swapprefix(A stitch in time,How,When,Who)", "A stitch in time") self.assertScriptResultEquals( "$swapprefix(The quick brown fox,How,When,Who)", "The quick brown fox") self.assertScriptResultEquals( "$swapprefix(How now brown cow,How,When,Who)", "now brown cow, How") self.assertScriptResultEquals( "$swapprefix(When the red red robin,How,When,Who)", "the red red robin, When") def test_cmd_delprefix(self): self.assertScriptResultEquals("$delprefix(A stitch in time)", "stitch in time") self.assertScriptResultEquals("$delprefix(The quick brown fox)", "quick brown fox") self.assertScriptResultEquals("$delprefix(How now brown cow)", "How now brown cow") self.assertScriptResultEquals("$delprefix(When the red red robin)", "When the red red robin") self.assertScriptResultEquals( "$delprefix(A stitch in time,How,When,Who)", "A stitch in time") self.assertScriptResultEquals( "$delprefix(The quick brown fox,How,When,Who)", "The quick brown fox") self.assertScriptResultEquals( "$delprefix(How now brown cow,How,When,Who)", "now brown cow") self.assertScriptResultEquals( "$delprefix(When the red red robin,How,When,Who)", "the red red robin") def test_default_filenaming(self): context = Metadata() context['albumartist'] = u'albumartist' context['artist'] = u'artist' context['album'] = u'album' context['totaldiscs'] = 2 context['discnumber'] = 1 context['tracknumber'] = 8 context['title'] = u'title' result = self.parser.eval(DEFAULT_FILE_NAMING_FORMAT, context) self.assertEqual(result, u'albumartist/album/1-08 title') context['~multiartist'] = '1' result = self.parser.eval(DEFAULT_FILE_NAMING_FORMAT, context) self.assertEqual(result, u'albumartist/album/1-08 artist - title') def test_default_NAT_filenaming(self): context = Metadata() context['artist'] = u'artist' context['album'] = u'[non-album tracks]' context['title'] = u'title' result = self.parser.eval(DEFAULT_FILE_NAMING_FORMAT, context) self.assertEqual(result, u'artist/title') def test_cmd_with_not_arguments(self): self.parser.eval("$noargstest()") def test_cmd_with_wrong_argcount_or(self): # $or() requires at least 2 arguments areg = r"^Wrong number of arguments for \$or: Expected at least 2, " with self.assertRaisesRegex(ScriptError, areg): self.parser.eval('$or(0)') def test_cmd_with_wrong_argcount_eq(self): # $eq() requires exactly 2 arguments areg = r"^Wrong number of arguments for \$eq: Expected exactly 2, " with self.assertRaisesRegex(ScriptError, areg): self.parser.eval('$eq(0)') with self.assertRaisesRegex(ScriptError, areg): self.parser.eval('$eq(0,0,0)') def test_cmd_with_wrong_argcount_if(self): areg = r"^Wrong number of arguments for \$if: Expected between 2 and 3, " # $if() requires 2 or 3 arguments with self.assertRaisesRegex(ScriptError, areg): self.parser.eval('$if(1)') with self.assertRaisesRegex(ScriptError, areg): self.parser.eval('$if(1,a,b,c)') def test_cmd_unset_simple(self): context = Metadata() context['title'] = u'Foo' context['album'] = u'Foo' context['artist'] = u'Foo' self.parser.eval("$unset(album)", context) self.assertNotIn('album', context) def test_cmd_unset_prefix(self): context = Metadata() context['title'] = u'Foo' context['~rating'] = u'4' self.parser.eval("$unset(_rating)", context) self.assertNotIn('~rating', context) def test_cmd_unset_multi(self): context = Metadata() context['performer:foo'] = u'Foo' context['performer:bar'] = u'Foo' self.parser.eval("$unset(performer:*)", context) self.assertNotIn('performer:bar', context) self.assertNotIn('performer:foo', context) def test_cmd_inmulti(self): context = Metadata() # Test with single-value string context["foo"] = "First:A; Second:B; Third:C" # Tests with $in for comparison purposes self.assertScriptResultEquals("$in(%foo%,Second:B)", "1", context) self.assertScriptResultEquals("$in(%foo%,irst:A; Second:B; Thi)", "1", context) self.assertScriptResultEquals("$in(%foo%,First:A; Second:B; Third:C)", "1", context) # Base $inmulti tests self.assertScriptResultEquals("$inmulti(%foo%,Second:B)", "", context) self.assertScriptResultEquals("$inmulti(%foo%,irst:A; Second:B; Thi)", "", context) self.assertScriptResultEquals( "$inmulti(%foo%,First:A; Second:B; Third:C)", "1", context) # Test separator override but with existing separator - results should be same as base self.assertScriptResultEquals("$inmulti(%foo%,Second:B,; )", "", context) self.assertScriptResultEquals( "$inmulti(%foo%,irst:A; Second:B; Thi,; )", "", context) self.assertScriptResultEquals( "$inmulti(%foo%,First:A; Second:B; Third:C,; )", "1", context) # Test separator override self.assertScriptResultEquals("$inmulti(%foo%,First:A,:)", "", context) self.assertScriptResultEquals("$inmulti(%foo%,Second:B,:)", "", context) self.assertScriptResultEquals("$inmulti(%foo%,Third:C,:)", "", context) self.assertScriptResultEquals("$inmulti(%foo%,First,:)", "1", context) self.assertScriptResultEquals("$inmulti(%foo%,A; Second,:)", "1", context) self.assertScriptResultEquals("$inmulti(%foo%,B; Third,:)", "1", context) self.assertScriptResultEquals("$inmulti(%foo%,C,:)", "1", context) # Test with multi-values context["foo"] = ["First:A", "Second:B", "Third:C"] # Tests with $in for comparison purposes self.assertScriptResultEquals("$in(%foo%,Second:B)", "1", context) self.assertScriptResultEquals("$in(%foo%,irst:A; Second:B; Thi)", "1", context) self.assertScriptResultEquals("$in(%foo%,First:A; Second:B; Third:C)", "1", context) # Base $inmulti tests self.assertScriptResultEquals("$inmulti(%foo%,Second:B)", "1", context) self.assertScriptResultEquals("$inmulti(%foo%,irst:A; Second:B; Thi)", "", context) self.assertScriptResultEquals( "$inmulti(%foo%,First:A; Second:B; Third:C)", "", context) # Test separator override but with existing separator - results should be same as base self.assertScriptResultEquals("$inmulti(%foo%,Second:B,; )", "1", context) self.assertScriptResultEquals( "$inmulti(%foo%,irst:A; Second:B; Thi,; )", "", context) self.assertScriptResultEquals( "$inmulti(%foo%,First:A; Second:B; Third:C,; )", "", context) # Test separator override self.assertScriptResultEquals("$inmulti(%foo%,First:A,:)", "", context) self.assertScriptResultEquals("$inmulti(%foo%,Second:B,:)", "", context) self.assertScriptResultEquals("$inmulti(%foo%,Third:C,:)", "", context) self.assertScriptResultEquals("$inmulti(%foo%,First,:)", "1", context) self.assertScriptResultEquals("$inmulti(%foo%,A; Second,:)", "1", context) self.assertScriptResultEquals("$inmulti(%foo%,B; Third,:)", "1", context) self.assertScriptResultEquals("$inmulti(%foo%,C,:)", "1", context) def test_cmd_lenmulti(self): context = Metadata() context["foo"] = "First:A; Second:B; Third:C" context["bar"] = ["First:A", "Second:B", "Third:C"] # Tests with $len for comparison purposes self.assertScriptResultEquals("$len(%foo%)", "26", context) self.assertScriptResultEquals("$len(%bar%)", "26", context) # Base $lenmulti tests self.assertScriptResultEquals("$lenmulti(%foo%)", "1", context) self.assertScriptResultEquals("$lenmulti(%bar%)", "3", context) self.assertScriptResultEquals("$lenmulti(%foo%.)", "3", context) # Test separator override but with existing separator - results should be same as base self.assertScriptResultEquals("$lenmulti(%foo%,; )", "1", context) self.assertScriptResultEquals("$lenmulti(%bar%,; )", "3", context) self.assertScriptResultEquals("$lenmulti(%foo%.,; )", "3", context) # Test separator override self.assertScriptResultEquals("$lenmulti(%foo%,:)", "4", context) self.assertScriptResultEquals("$lenmulti(%bar%,:)", "4", context) self.assertScriptResultEquals("$lenmulti(%foo%.,:)", "4", context) def test_matchedtracks(self): file = MagicMock() file.parent.album.get_num_matched_tracks.return_value = 42 self.assertScriptResultEquals("$matchedtracks()", "42", file=file) self.assertScriptResultEquals("$matchedtracks()", "0") # The following only is possible for backward compatibility, arg is unused self.assertScriptResultEquals("$matchedtracks(arg)", "0") def test_required_kwonly_parameters(self): def func(a, *, required_kwarg): pass with self.assertRaises( TypeError, msg= "Functions with required keyword-only parameters are not supported" ): register_script_function(func) def test_optional_kwonly_parameters(self): def func(a, *, optional_kwarg=1): pass register_script_function(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() 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 = []
class ScriptParserTest(unittest.TestCase): def setUp(self): config.setting = { 'enabled_plugins': '', } self.parser = ScriptParser() def test_cmd_noop(self): self.assertEqual(self.parser.eval("$noop()"), "") def test_cmd_if(self): self.assertEqual(self.parser.eval("$if(1,a,b)"), "a") self.assertEqual(self.parser.eval("$if(,a,b)"), "b") def test_cmd_if2(self): self.assertEqual(self.parser.eval("$if2(,a,b)"), "a") self.assertEqual(self.parser.eval("$if2($noop(),b)"), "b") def test_cmd_left(self): self.assertEqual(self.parser.eval("$left(abcd,2)"), "ab") def test_cmd_right(self): self.assertEqual(self.parser.eval("$right(abcd,2)"), "cd") def test_cmd_set(self): self.assertEqual(self.parser.eval("$set(test,aaa)%test%"), "aaa") def test_cmd_set_empty(self): self.assertEqual(self.parser.eval("$set(test,)%test%"), "") def test_cmd_set_multi_valued(self): context = Metadata() context["source"] = ["multi", "valued"] self.parser.eval("$set(test,%source%)", context) self.assertEqual(context.getall("test"), ["multi; valued"]) # list has only a single value def test_cmd_setmulti_multi_valued(self): context = Metadata() context["source"] = ["multi", "valued"] self.assertEqual("", self.parser.eval("$setmulti(test,%source%)", context)) # no return value self.assertEqual(context.getall("source"), context.getall("test")) def test_cmd_setmulti_multi_valued_wth_spaces(self): context = Metadata() context["source"] = ["multi, multi", "valued, multi"] self.assertEqual("", self.parser.eval("$setmulti(test,%source%)", context)) # no return value self.assertEqual(context.getall("source"), context.getall("test")) def test_cmd_setmulti_not_multi_valued(self): context = Metadata() context["source"] = "multi, multi" self.assertEqual("", self.parser.eval("$setmulti(test,%source%)", context)) # no return value self.assertEqual(context.getall("source"), context.getall("test")) def test_cmd_setmulti_will_remove_empty_items(self): context = Metadata() context["source"] = ["", "multi", ""] self.assertEqual("", self.parser.eval("$setmulti(test,%source%)", context)) # no return value self.assertEqual(["multi"], context.getall("test")) def test_cmd_setmulti_custom_splitter_string(self): context = Metadata() self.assertEqual("", self.parser.eval( "$setmulti(test,multi##valued##test##,##)", context)) # no return value self.assertEqual(["multi", "valued", "test"], context.getall("test")) def test_cmd_setmulti_empty_splitter_does_nothing(self): context = Metadata() self.assertEqual("", self.parser.eval("$setmulti(test,multi; valued,)", context)) # no return value self.assertEqual(["multi; valued"], context.getall("test")) def test_cmd_get(self): context = Metadata() context["test"] = "aaa" self.assertEqual(self.parser.eval("$get(test)", context), "aaa") context["test2"] = ["multi", "valued"] self.assertEqual(self.parser.eval("$get(test2)", context), "multi; valued") def test_cmd_num(self): self.assertEqual(self.parser.eval("$num(3,3)"), "003") self.assertEqual(self.parser.eval("$num(03,3)"), "003") self.assertEqual(self.parser.eval("$num(123,2)"), "123") def test_cmd_or(self): self.assertEqual(self.parser.eval("$or(,)"), "") self.assertEqual(self.parser.eval("$or(,q)"), "1") self.assertEqual(self.parser.eval("$or(q,)"), "1") self.assertEqual(self.parser.eval("$or(q,q)"), "1") def test_cmd_and(self): self.assertEqual(self.parser.eval("$and(,)"), "") self.assertEqual(self.parser.eval("$and(,q)"), "") self.assertEqual(self.parser.eval("$and(q,)"), "") self.assertEqual(self.parser.eval("$and(q,q)"), "1") def test_cmd_not(self): self.assertEqual(self.parser.eval("$not($noop())"), "1") self.assertEqual(self.parser.eval("$not(q)"), "") def test_cmd_add(self): self.assertEqual(self.parser.eval("$add(1,2)"), "3") def test_cmd_sub(self): self.assertEqual(self.parser.eval("$sub(1,2)"), "-1") self.assertEqual(self.parser.eval("$sub(2,1)"), "1") def test_cmd_div(self): self.assertEqual(self.parser.eval("$div(9,3)"), "3") self.assertEqual(self.parser.eval("$div(10,3)"), "3") def test_cmd_mod(self): self.assertEqual(self.parser.eval("$mod(9,3)"), "0") self.assertEqual(self.parser.eval("$mod(10,3)"), "1") def test_cmd_mul(self): self.assertEqual(self.parser.eval("$mul(9,3)"), "27") self.assertEqual(self.parser.eval("$mul(10,3)"), "30") def test_cmd_eq(self): self.assertEqual(self.parser.eval("$eq(,)"), "1") self.assertEqual(self.parser.eval("$eq(,$noop())"), "1") self.assertEqual(self.parser.eval("$eq(,q)"), "") self.assertEqual(self.parser.eval("$eq(q,q)"), "1") self.assertEqual(self.parser.eval("$eq(q,)"), "") def test_cmd_ne(self): self.assertEqual(self.parser.eval("$ne(,)"), "") self.assertEqual(self.parser.eval("$ne(,$noop())"), "") self.assertEqual(self.parser.eval("$ne(,q)"), "1") self.assertEqual(self.parser.eval("$ne(q,q)"), "") self.assertEqual(self.parser.eval("$ne(q,)"), "1") def test_cmd_lower(self): self.assertEqual(self.parser.eval("$lower(AbeCeDA)"), "abeceda") def test_cmd_upper(self): self.assertEqual(self.parser.eval("$upper(AbeCeDA)"), "ABECEDA") def test_cmd_rreplace(self): self.assertEqual( self.parser.eval( r'''$rreplace(test \(disc 1\),\\s\\\(disc \\d+\\\),)'''), "test") def test_cmd_rsearch(self): self.assertEqual( self.parser.eval( r"$rsearch(test \(disc 1\),\\\(disc \(\\d+\)\\\))"), "1") def test_arguments(self): self.assertTrue( self.parser.eval( r"$set(bleh,$rsearch(test \(disc 1\),\\\(disc \(\\d+\)\\\)))) $set(wer,1)" )) def test_cmd_gt(self): self.assertEqual(self.parser.eval("$gt(10,4)"), "1") self.assertEqual(self.parser.eval("$gt(6,4)"), "1") def test_cmd_gte(self): self.assertEqual(self.parser.eval("$gte(10,10)"), "1") self.assertEqual(self.parser.eval("$gte(10,4)"), "1") self.assertEqual(self.parser.eval("$gte(6,4)"), "1") def test_cmd_lt(self): self.assertEqual(self.parser.eval("$lt(4,10)"), "1") self.assertEqual(self.parser.eval("$lt(4,6)"), "1") def test_cmd_lte(self): self.assertEqual(self.parser.eval("$lte(10,10)"), "1") self.assertEqual(self.parser.eval("$lte(4,10)"), "1") self.assertEqual(self.parser.eval("$lte(4,6)"), "1") def test_cmd_len(self): self.assertEqual(self.parser.eval("$len(abcdefg)"), "7") self.assertEqual(self.parser.eval("$len()"), "0") def test_cmd_firstalphachar(self): self.assertEqual(self.parser.eval("$firstalphachar(abc)"), "A") self.assertEqual(self.parser.eval("$firstalphachar(Abc)"), "A") self.assertEqual(self.parser.eval("$firstalphachar(1abc)"), "#") self.assertEqual(self.parser.eval("$firstalphachar(...abc)"), "#") self.assertEqual(self.parser.eval("$firstalphachar(1abc,_)"), "_") self.assertEqual(self.parser.eval("$firstalphachar(...abc,_)"), "_") self.assertEqual(self.parser.eval("$firstalphachar()"), "#") self.assertEqual(self.parser.eval("$firstalphachar(,_)"), "_") self.assertEqual(self.parser.eval("$firstalphachar( abc)"), "#") def test_cmd_initials(self): self.assertEqual(self.parser.eval("$initials(Abc def Ghi)"), "AdG") self.assertEqual(self.parser.eval("$initials(Abc #def Ghi)"), "AG") self.assertEqual(self.parser.eval("$initials(Abc 1def Ghi)"), "AG") self.assertEqual(self.parser.eval("$initials(Abc)"), "A") self.assertEqual(self.parser.eval("$initials()"), "") def test_cmd_firstwords(self): self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,11)"), "Abc Def Ghi") self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,12)"), "Abc Def Ghi") self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,7)"), "Abc Def") self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,8)"), "Abc Def") self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,6)"), "Abc") self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,0)"), "") self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,NaN)"), "") self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,)"), "") def test_cmd_truncate(self): self.assertEqual(self.parser.eval("$truncate(abcdefg,0)"), "") self.assertEqual(self.parser.eval("$truncate(abcdefg,7)"), "abcdefg") self.assertEqual(self.parser.eval("$truncate(abcdefg,3)"), "abc") self.assertEqual(self.parser.eval("$truncate(abcdefg,10)"), "abcdefg") self.assertEqual(self.parser.eval("$truncate(abcdefg,)"), "abcdefg") self.assertEqual(self.parser.eval("$truncate(abcdefg,NaN)"), "abcdefg") def test_cmd_copy(self): context = Metadata() tagsToCopy = ["tag1", "tag2"] context["source"] = tagsToCopy context["target"] = ["will", "be", "overwritten"] self.parser.eval("$copy(target,source)", context) self.assertEqual(self.parser.context.getall("target"), tagsToCopy) def _eval_and_check_copymerge(self, context, expected): self.parser.eval("$copymerge(target,source)", context) self.assertEqual(self.parser.context.getall("target"), expected) def test_cmd_copymerge_notarget(self): context = Metadata() tagsToCopy = ["tag1", "tag2"] context["source"] = tagsToCopy self._eval_and_check_copymerge(context, tagsToCopy) def test_cmd_copymerge_nosource(self): context = Metadata() target = ["tag1", "tag2"] context["target"] = target self._eval_and_check_copymerge(context, target) def test_cmd_copymerge_removedupes(self): context = Metadata() context["target"] = ["tag1", "tag2"] context["source"] = ["tag2", "tag3"] self._eval_and_check_copymerge(context, ["tag1", "tag2", "tag3"]) def test_cmd_copymerge_nonlist(self): context = Metadata() context["target"] = "targetval" context["source"] = "sourceval" self._eval_and_check_copymerge(context, ["targetval", "sourceval"])
class ScriptParserTest(unittest.TestCase): def setUp(self): QtCore.QObject.config = FakeConfig() self.parser = ScriptParser() def test_cmd_noop(self): self.failUnlessEqual(self.parser.eval("$noop()"), "") def test_cmd_if(self): self.failUnlessEqual(self.parser.eval("$if(1,a,b)"), "a") self.failUnlessEqual(self.parser.eval("$if(,a,b)"), "b") def test_cmd_if2(self): self.failUnlessEqual(self.parser.eval("$if2(,a,b)"), "a") self.failUnlessEqual(self.parser.eval("$if2($noop(),b)"), "b") def test_cmd_left(self): self.failUnlessEqual(self.parser.eval("$left(abcd,2)"), "ab") def test_cmd_right(self): self.failUnlessEqual(self.parser.eval("$right(abcd,2)"), "cd") def test_cmd_set(self): self.failUnlessEqual(self.parser.eval("$set(test,aaa)%test%"), "aaa") def test_cmd_get(self): self.failUnlessEqual(self.parser.eval("$get(test)", {"test": "aaa"}), "aaa") def test_cmd_num(self): self.failUnlessEqual(self.parser.eval("$num(3,3)"), "003") self.failUnlessEqual(self.parser.eval("$num(03,3)"), "003") self.failUnlessEqual(self.parser.eval("$num(123,2)"), "123") def test_cmd_or(self): self.failUnlessEqual(self.parser.eval("$or(,)"), "") self.failUnlessEqual(self.parser.eval("$or(,q)"), "1") self.failUnlessEqual(self.parser.eval("$or(q,)"), "1") self.failUnlessEqual(self.parser.eval("$or(q,q)"), "1") def test_cmd_and(self): self.failUnlessEqual(self.parser.eval("$and(,)"), "") self.failUnlessEqual(self.parser.eval("$and(,q)"), "") self.failUnlessEqual(self.parser.eval("$and(q,)"), "") self.failUnlessEqual(self.parser.eval("$and(q,q)"), "1") def test_cmd_not(self): self.failUnlessEqual(self.parser.eval("$not($noop())"), "1") self.failUnlessEqual(self.parser.eval("$not(q)"), "") def test_cmd_add(self): self.failUnlessEqual(self.parser.eval("$add(1,2)"), "3") def test_cmd_sub(self): self.failUnlessEqual(self.parser.eval("$sub(1,2)"), "-1") self.failUnlessEqual(self.parser.eval("$sub(2,1)"), "1") def test_cmd_div(self): self.failUnlessEqual(self.parser.eval("$div(9,3)"), "3") self.failUnlessEqual(self.parser.eval("$div(10,3)"), "3") def test_cmd_mod(self): self.failUnlessEqual(self.parser.eval("$mod(9,3)"), "0") self.failUnlessEqual(self.parser.eval("$mod(10,3)"), "1") def test_cmd_mul(self): self.failUnlessEqual(self.parser.eval("$mul(9,3)"), "27") self.failUnlessEqual(self.parser.eval("$mul(10,3)"), "30") def test_cmd_eq(self): self.failUnlessEqual(self.parser.eval("$eq(,)"), "1") self.failUnlessEqual(self.parser.eval("$eq(,$noop())"), "1") self.failUnlessEqual(self.parser.eval("$eq(,q)"), "") self.failUnlessEqual(self.parser.eval("$eq(q,q)"), "1") self.failUnlessEqual(self.parser.eval("$eq(q,)"), "") def test_cmd_ne(self): self.failUnlessEqual(self.parser.eval("$ne(,)"), "") self.failUnlessEqual(self.parser.eval("$ne(,$noop())"), "") self.failUnlessEqual(self.parser.eval("$ne(,q)"), "1") self.failUnlessEqual(self.parser.eval("$ne(q,q)"), "") self.failUnlessEqual(self.parser.eval("$ne(q,)"), "1") def test_cmd_lower(self): self.failUnlessEqual(self.parser.eval("$lower(AbeCeDA)"), "abeceda") def test_cmd_upper(self): self.failUnlessEqual(self.parser.eval("$upper(AbeCeDA)"), "ABECEDA") def test_cmd_rreplace(self): self.failUnlessEqual( self.parser.eval(r'''$rreplace(test \(disc 1\),\\s\\\(disc \\d+\\\),)'''), "test" ) def test_cmd_rsearch(self): self.failUnlessEqual( self.parser.eval(r"$rsearch(test \(disc 1\),\\\(disc \(\\d+\)\\\))"), "1" ) def test_arguments(self): self.failUnless( self.parser.eval( r"$set(bleh,$rsearch(test \(disc 1\),\\\(disc \(\\d+\)\\\)))) $set(wer,1)")) def test_cmd_gt(self): self.failUnlessEqual(self.parser.eval("$gt(10,4)"), "1") self.failUnlessEqual(self.parser.eval("$gt(6,4)"), "1") def test_cmd_gte(self): self.failUnlessEqual(self.parser.eval("$gte(10,10)"), "1") self.failUnlessEqual(self.parser.eval("$gte(10,4)"), "1") self.failUnlessEqual(self.parser.eval("$gte(6,4)"), "1") def test_cmd_lt(self): self.failUnlessEqual(self.parser.eval("$lt(4,10)"), "1") self.failUnlessEqual(self.parser.eval("$lt(4,6)"), "1") def test_cmd_lte(self): self.failUnlessEqual(self.parser.eval("$lte(10,10)"), "1") self.failUnlessEqual(self.parser.eval("$lte(4,10)"), "1") self.failUnlessEqual(self.parser.eval("$lte(4,6)"), "1") def test_cmd_len(self): self.failUnlessEqual(self.parser.eval("$len(abcdefg)"), "7") self.failUnlessEqual(self.parser.eval("$len()"), "0") def test_cmd_firstalphachar(self): self.failUnlessEqual(self.parser.eval("$firstalphachar(abc)"), "A") self.failUnlessEqual(self.parser.eval("$firstalphachar(Abc)"), "A") self.failUnlessEqual(self.parser.eval("$firstalphachar(1abc)"), "#") self.failUnlessEqual(self.parser.eval("$firstalphachar(...abc)"), "#") self.failUnlessEqual(self.parser.eval("$firstalphachar(1abc,_)"), "_") self.failUnlessEqual(self.parser.eval("$firstalphachar(...abc,_)"), "_") self.failUnlessEqual(self.parser.eval("$firstalphachar()"), "#") self.failUnlessEqual(self.parser.eval("$firstalphachar(,_)"), "_") self.failUnlessEqual(self.parser.eval("$firstalphachar( abc)"), "#") def test_cmd_initials(self): self.failUnlessEqual(self.parser.eval("$initials(Abc def Ghi)"), "AdG") self.failUnlessEqual(self.parser.eval("$initials(Abc #def Ghi)"), "AG") self.failUnlessEqual(self.parser.eval("$initials(Abc 1def Ghi)"), "AG") self.failUnlessEqual(self.parser.eval("$initials(Abc)"), "A") self.failUnlessEqual(self.parser.eval("$initials()"), "") def test_cmd_firstwords(self): self.failUnlessEqual(self.parser.eval("$firstwords(Abc Def Ghi,11)"), "Abc Def Ghi") self.failUnlessEqual(self.parser.eval("$firstwords(Abc Def Ghi,12)"), "Abc Def Ghi") self.failUnlessEqual(self.parser.eval("$firstwords(Abc Def Ghi,7)"), "Abc Def") self.failUnlessEqual(self.parser.eval("$firstwords(Abc Def Ghi,8)"), "Abc Def") self.failUnlessEqual(self.parser.eval("$firstwords(Abc Def Ghi,6)"), "Abc") self.failUnlessEqual(self.parser.eval("$firstwords(Abc Def Ghi,0)"), "") self.failUnlessEqual(self.parser.eval("$firstwords(Abc Def Ghi,NaN)"), "") self.failUnlessEqual(self.parser.eval("$firstwords(Abc Def Ghi,)"), "") def test_cmd_truncate(self): self.failUnlessEqual(self.parser.eval("$truncate(abcdefg,0)"), "") self.failUnlessEqual(self.parser.eval("$truncate(abcdefg,7)"), "abcdefg") self.failUnlessEqual(self.parser.eval("$truncate(abcdefg,3)"), "abc") self.failUnlessEqual(self.parser.eval("$truncate(abcdefg,10)"), "abcdefg") self.failUnlessEqual(self.parser.eval("$truncate(abcdefg,)"), "abcdefg") self.failUnlessEqual(self.parser.eval("$truncate(abcdefg,NaN)"), "abcdefg")
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 setUp(self): QtCore.QObject.config = FakeConfig() self.parser = ScriptParser()
def check_format(self): parser = ScriptParser() try: parser.eval(unicode(self.ui.file_naming_format.toPlainText())) except Exception, e: raise OptionsCheckError("", str(e))
class ScriptParserTest(unittest.TestCase): def setUp(self): config.setting = { 'enabled_plugins': '', } self.parser = ScriptParser() def test_cmd_noop(self): self.assertEqual(self.parser.eval("$noop()"), "") def test_cmd_if(self): self.assertEqual(self.parser.eval("$if(1,a,b)"), "a") self.assertEqual(self.parser.eval("$if(,a,b)"), "b") def test_cmd_if2(self): self.assertEqual(self.parser.eval("$if2(,a,b)"), "a") self.assertEqual(self.parser.eval("$if2($noop(),b)"), "b") def test_cmd_left(self): self.assertEqual(self.parser.eval("$left(abcd,2)"), "ab") def test_cmd_right(self): self.assertEqual(self.parser.eval("$right(abcd,2)"), "cd") def test_cmd_set(self): self.assertEqual(self.parser.eval("$set(test,aaa)%test%"), "aaa") def test_cmd_set_empty(self): self.assertEqual(self.parser.eval("$set(test,)%test%"), "") def test_cmd_set_multi_valued(self): context = Metadata() context["source"] = ["multi", "valued"] self.parser.eval("$set(test,%source%)", context) self.assertEqual(context.getall("test"), ["multi; valued"]) # list has only a single value def test_cmd_setmulti_multi_valued(self): context = Metadata() context["source"] = ["multi", "valued"] self.assertEqual("", self.parser.eval("$setmulti(test,%source%)", context)) # no return value self.assertEqual(context.getall("source"), context.getall("test")) def test_cmd_setmulti_multi_valued_wth_spaces(self): context = Metadata() context["source"] = ["multi, multi", "valued, multi"] self.assertEqual("", self.parser.eval("$setmulti(test,%source%)", context)) # no return value self.assertEqual(context.getall("source"), context.getall("test")) def test_cmd_setmulti_not_multi_valued(self): context = Metadata() context["source"] = "multi, multi" self.assertEqual("", self.parser.eval("$setmulti(test,%source%)", context)) # no return value self.assertEqual(context.getall("source"), context.getall("test")) def test_cmd_setmulti_will_remove_empty_items(self): context = Metadata() context["source"] = ["", "multi", ""] self.assertEqual("", self.parser.eval("$setmulti(test,%source%)", context)) # no return value self.assertEqual(["multi"], context.getall("test")) def test_cmd_setmulti_custom_splitter_string(self): context = Metadata() self.assertEqual("", self.parser.eval( "$setmulti(test,multi##valued##test##,##)", context)) # no return value self.assertEqual(["multi", "valued", "test"], context.getall("test")) def test_cmd_setmulti_empty_splitter_does_nothing(self): context = Metadata() self.assertEqual("", self.parser.eval("$setmulti(test,multi; valued,)", context)) # no return value self.assertEqual(["multi; valued"], context.getall("test")) def test_cmd_get(self): context = Metadata() context["test"] = "aaa" self.assertEqual(self.parser.eval("$get(test)", context), "aaa") context["test2"] = ["multi", "valued"] self.assertEqual(self.parser.eval("$get(test2)", context), "multi; valued") def test_cmd_num(self): self.assertEqual(self.parser.eval("$num(3,3)"), "003") self.assertEqual(self.parser.eval("$num(03,3)"), "003") self.assertEqual(self.parser.eval("$num(123,2)"), "123") def test_cmd_or(self): self.assertEqual(self.parser.eval("$or(,)"), "") self.assertEqual(self.parser.eval("$or(,q)"), "1") self.assertEqual(self.parser.eval("$or(q,)"), "1") self.assertEqual(self.parser.eval("$or(q,q)"), "1") def test_cmd_and(self): self.assertEqual(self.parser.eval("$and(,)"), "") self.assertEqual(self.parser.eval("$and(,q)"), "") self.assertEqual(self.parser.eval("$and(q,)"), "") self.assertEqual(self.parser.eval("$and(q,q)"), "1") def test_cmd_not(self): self.assertEqual(self.parser.eval("$not($noop())"), "1") self.assertEqual(self.parser.eval("$not(q)"), "") def test_cmd_add(self): self.assertEqual(self.parser.eval("$add(1,2)"), "3") def test_cmd_sub(self): self.assertEqual(self.parser.eval("$sub(1,2)"), "-1") self.assertEqual(self.parser.eval("$sub(2,1)"), "1") def test_cmd_div(self): self.assertEqual(self.parser.eval("$div(9,3)"), "3") self.assertEqual(self.parser.eval("$div(10,3)"), "3") def test_cmd_mod(self): self.assertEqual(self.parser.eval("$mod(9,3)"), "0") self.assertEqual(self.parser.eval("$mod(10,3)"), "1") def test_cmd_mul(self): self.assertEqual(self.parser.eval("$mul(9,3)"), "27") self.assertEqual(self.parser.eval("$mul(10,3)"), "30") def test_cmd_eq(self): self.assertEqual(self.parser.eval("$eq(,)"), "1") self.assertEqual(self.parser.eval("$eq(,$noop())"), "1") self.assertEqual(self.parser.eval("$eq(,q)"), "") self.assertEqual(self.parser.eval("$eq(q,q)"), "1") self.assertEqual(self.parser.eval("$eq(q,)"), "") def test_cmd_ne(self): self.assertEqual(self.parser.eval("$ne(,)"), "") self.assertEqual(self.parser.eval("$ne(,$noop())"), "") self.assertEqual(self.parser.eval("$ne(,q)"), "1") self.assertEqual(self.parser.eval("$ne(q,q)"), "") self.assertEqual(self.parser.eval("$ne(q,)"), "1") def test_cmd_lower(self): self.assertEqual(self.parser.eval("$lower(AbeCeDA)"), "abeceda") def test_cmd_upper(self): self.assertEqual(self.parser.eval("$upper(AbeCeDA)"), "ABECEDA") def test_cmd_rreplace(self): self.assertEqual( self.parser.eval( r'''$rreplace(test \(disc 1\),\\s\\\(disc \\d+\\\),)'''), "test") def test_cmd_rsearch(self): self.assertEqual( self.parser.eval( r"$rsearch(test \(disc 1\),\\\(disc \(\\d+\)\\\))"), "1") def test_arguments(self): self.assertTrue( self.parser.eval( r"$set(bleh,$rsearch(test \(disc 1\),\\\(disc \(\\d+\)\\\)))) $set(wer,1)" )) def test_cmd_gt(self): self.assertEqual(self.parser.eval("$gt(10,4)"), "1") self.assertEqual(self.parser.eval("$gt(6,4)"), "1") def test_cmd_gte(self): self.assertEqual(self.parser.eval("$gte(10,10)"), "1") self.assertEqual(self.parser.eval("$gte(10,4)"), "1") self.assertEqual(self.parser.eval("$gte(6,4)"), "1") def test_cmd_lt(self): self.assertEqual(self.parser.eval("$lt(4,10)"), "1") self.assertEqual(self.parser.eval("$lt(4,6)"), "1") def test_cmd_lte(self): self.assertEqual(self.parser.eval("$lte(10,10)"), "1") self.assertEqual(self.parser.eval("$lte(4,10)"), "1") self.assertEqual(self.parser.eval("$lte(4,6)"), "1") def test_cmd_len(self): self.assertEqual(self.parser.eval("$len(abcdefg)"), "7") self.assertEqual(self.parser.eval("$len()"), "0") def test_cmd_firstalphachar(self): self.assertEqual(self.parser.eval("$firstalphachar(abc)"), "A") self.assertEqual(self.parser.eval("$firstalphachar(Abc)"), "A") self.assertEqual(self.parser.eval("$firstalphachar(1abc)"), "#") self.assertEqual(self.parser.eval("$firstalphachar(...abc)"), "#") self.assertEqual(self.parser.eval("$firstalphachar(1abc,_)"), "_") self.assertEqual(self.parser.eval("$firstalphachar(...abc,_)"), "_") self.assertEqual(self.parser.eval("$firstalphachar()"), "#") self.assertEqual(self.parser.eval("$firstalphachar(,_)"), "_") self.assertEqual(self.parser.eval("$firstalphachar( abc)"), "#") def test_cmd_initials(self): self.assertEqual(self.parser.eval("$initials(Abc def Ghi)"), "AdG") self.assertEqual(self.parser.eval("$initials(Abc #def Ghi)"), "AG") self.assertEqual(self.parser.eval("$initials(Abc 1def Ghi)"), "AG") self.assertEqual(self.parser.eval("$initials(Abc)"), "A") self.assertEqual(self.parser.eval("$initials()"), "") def test_cmd_firstwords(self): self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,11)"), "Abc Def Ghi") self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,12)"), "Abc Def Ghi") self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,7)"), "Abc Def") self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,8)"), "Abc Def") self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,6)"), "Abc") self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,0)"), "") self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,NaN)"), "") self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,)"), "") def test_cmd_truncate(self): self.assertEqual(self.parser.eval("$truncate(abcdefg,0)"), "") self.assertEqual(self.parser.eval("$truncate(abcdefg,7)"), "abcdefg") self.assertEqual(self.parser.eval("$truncate(abcdefg,3)"), "abc") self.assertEqual(self.parser.eval("$truncate(abcdefg,10)"), "abcdefg") self.assertEqual(self.parser.eval("$truncate(abcdefg,)"), "abcdefg") self.assertEqual(self.parser.eval("$truncate(abcdefg,NaN)"), "abcdefg") def test_cmd_copy(self): context = Metadata() tagsToCopy = ["tag1", "tag2"] context["source"] = tagsToCopy context["target"] = ["will", "be", "overwritten"] self.parser.eval("$copy(target,source)", context) self.assertEqual(self.parser.context.getall("target"), tagsToCopy) def _eval_and_check_copymerge(self, context, expected): self.parser.eval("$copymerge(target,source)", context) self.assertEqual(self.parser.context.getall("target"), expected) def test_cmd_copymerge_notarget(self): context = Metadata() tagsToCopy = ["tag1", "tag2"] context["source"] = tagsToCopy self._eval_and_check_copymerge(context, tagsToCopy) def test_cmd_copymerge_nosource(self): context = Metadata() target = ["tag1", "tag2"] context["target"] = target self._eval_and_check_copymerge(context, target) def test_cmd_copymerge_removedupes(self): context = Metadata() context["target"] = ["tag1", "tag2"] context["source"] = ["tag2", "tag3"] self._eval_and_check_copymerge(context, ["tag1", "tag2", "tag3"]) def test_cmd_copymerge_nonlist(self): context = Metadata() context["target"] = "targetval" context["source"] = "sourceval" self._eval_and_check_copymerge(context, ["targetval", "sourceval"]) def test_cmd_eq_any(self): self.assertEqual(self.parser.eval("$eq_any(abc,def,ghi,jkl)"), "") self.assertEqual(self.parser.eval("$eq_any(abc,def,ghi,jkl,abc)"), "1") def test_cmd_ne_all(self): self.assertEqual(self.parser.eval("$ne_all(abc,def,ghi,jkl)"), "1") self.assertEqual(self.parser.eval("$ne_all(abc,def,ghi,jkl,abc)"), "") def test_cmd_eq_all(self): self.assertEqual(self.parser.eval("$eq_all(abc,abc,abc,abc)"), "1") self.assertEqual(self.parser.eval("$eq_all(abc,abc,def,ghi)"), "") def test_cmd_ne_any(self): self.assertEqual(self.parser.eval("$ne_any(abc,abc,abc,abc)"), "") self.assertEqual(self.parser.eval("$ne_any(abc,abc,def,ghi)"), "1") def test_cmd_swapprefix(self): self.assertEqual(self.parser.eval("$swapprefix(A stitch in time)"), "stitch in time, A") self.assertEqual(self.parser.eval("$swapprefix(The quick brown fox)"), "quick brown fox, The") self.assertEqual(self.parser.eval("$swapprefix(How now brown cow)"), "How now brown cow") self.assertEqual( self.parser.eval("$swapprefix(When the red red robin)"), "When the red red robin") self.assertEqual( self.parser.eval("$swapprefix(A stitch in time,How,When,Who)"), "A stitch in time") self.assertEqual( self.parser.eval("$swapprefix(The quick brown fox,How,When,Who)"), "The quick brown fox") self.assertEqual( self.parser.eval("$swapprefix(How now brown cow,How,When,Who)"), "now brown cow, How") self.assertEqual( self.parser.eval( "$swapprefix(When the red red robin,How,When,Who)"), "the red red robin, When") def test_cmd_delprefix(self): self.assertEqual(self.parser.eval("$delprefix(A stitch in time)"), "stitch in time") self.assertEqual(self.parser.eval("$delprefix(The quick brown fox)"), "quick brown fox") self.assertEqual(self.parser.eval("$delprefix(How now brown cow)"), "How now brown cow") self.assertEqual( self.parser.eval("$delprefix(When the red red robin)"), "When the red red robin") self.assertEqual( self.parser.eval("$delprefix(A stitch in time,How,When,Who)"), "A stitch in time") self.assertEqual( self.parser.eval("$delprefix(The quick brown fox,How,When,Who)"), "The quick brown fox") self.assertEqual( self.parser.eval("$delprefix(How now brown cow,How,When,Who)"), "now brown cow") self.assertEqual( self.parser.eval( "$delprefix(When the red red robin,How,When,Who)"), "the red red robin") def test_default_filenaming(self): context = Metadata() context['albumartist'] = u'albumartist' context['artist'] = u'artist' context['album'] = u'album' context['totaldiscs'] = 2 context['discnumber'] = 1 context['tracknumber'] = 8 context['title'] = u'title' result = self.parser.eval(_DEFAULT_FILE_NAMING_FORMAT, context) self.assertEqual(result, u'albumartist/album/1-08 title') context['~multiartist'] = '1' result = self.parser.eval(_DEFAULT_FILE_NAMING_FORMAT, context) self.assertEqual(result, u'albumartist/album/1-08 artist - title') def test_default_NAT_filenaming(self): context = Metadata() context['artist'] = u'artist' context['album'] = u'[non-album tracks]' context['title'] = u'title' result = self.parser.eval(_DEFAULT_FILE_NAMING_FORMAT, context) self.assertEqual(result, u'artist/title')
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 _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()
class ScriptParserTest(unittest.TestCase): def setUp(self): config.setting = { 'enabled_plugins': '', } self.parser = ScriptParser() def func_noargstest(parser): return "" register_script_function(func_noargstest, "noargstest") def test_cmd_noop(self): self.assertEqual(self.parser.eval("$noop()"), "") self.assertEqual(self.parser.eval("$noop(abcdefg)"), "") def test_cmd_if(self): self.assertEqual(self.parser.eval("$if(1,a,b)"), "a") self.assertEqual(self.parser.eval("$if(,a,b)"), "b") def test_cmd_if2(self): self.assertEqual(self.parser.eval("$if2(,a,b)"), "a") self.assertEqual(self.parser.eval("$if2($noop(),b)"), "b") def test_cmd_left(self): self.assertEqual(self.parser.eval("$left(abcd,2)"), "ab") def test_cmd_right(self): self.assertEqual(self.parser.eval("$right(abcd,2)"), "cd") def test_cmd_set(self): self.assertEqual(self.parser.eval("$set(test,aaa)%test%"), "aaa") def test_cmd_set_empty(self): self.assertEqual(self.parser.eval("$set(test,)%test%"), "") def test_cmd_set_multi_valued(self): context = Metadata() context["source"] = ["multi", "valued"] self.parser.eval("$set(test,%source%)", context) self.assertEqual(context.getall("test"), ["multi; valued"]) # list has only a single value def test_cmd_setmulti_multi_valued(self): context = Metadata() context["source"] = ["multi", "valued"] self.assertEqual("", self.parser.eval("$setmulti(test,%source%)", context)) # no return value self.assertEqual(context.getall("source"), context.getall("test")) def test_cmd_setmulti_multi_valued_wth_spaces(self): context = Metadata() context["source"] = ["multi, multi", "valued, multi"] self.assertEqual("", self.parser.eval("$setmulti(test,%source%)", context)) # no return value self.assertEqual(context.getall("source"), context.getall("test")) def test_cmd_setmulti_not_multi_valued(self): context = Metadata() context["source"] = "multi, multi" self.assertEqual("", self.parser.eval("$setmulti(test,%source%)", context)) # no return value self.assertEqual(context.getall("source"), context.getall("test")) def test_cmd_setmulti_will_remove_empty_items(self): context = Metadata() context["source"] = ["", "multi", ""] self.assertEqual("", self.parser.eval("$setmulti(test,%source%)", context)) # no return value self.assertEqual(["multi"], context.getall("test")) def test_cmd_setmulti_custom_splitter_string(self): context = Metadata() self.assertEqual("", self.parser.eval("$setmulti(test,multi##valued##test##,##)", context)) # no return value self.assertEqual(["multi", "valued", "test"], context.getall("test")) def test_cmd_setmulti_empty_splitter_does_nothing(self): context = Metadata() self.assertEqual("", self.parser.eval("$setmulti(test,multi; valued,)", context)) # no return value self.assertEqual(["multi; valued"], context.getall("test")) def test_cmd_get(self): context = Metadata() context["test"] = "aaa" self.assertEqual(self.parser.eval("$get(test)", context), "aaa") context["test2"] = ["multi", "valued"] self.assertEqual(self.parser.eval("$get(test2)", context), "multi; valued") def test_cmd_num(self): self.assertEqual(self.parser.eval("$num(3,3)"), "003") self.assertEqual(self.parser.eval("$num(03,3)"), "003") self.assertEqual(self.parser.eval("$num(123,2)"), "123") def test_cmd_or(self): self.assertEqual(self.parser.eval("$or(,)"), "") self.assertEqual(self.parser.eval("$or(,,)"), "") self.assertEqual(self.parser.eval("$or(,q)"), "1") self.assertEqual(self.parser.eval("$or(q,)"), "1") self.assertEqual(self.parser.eval("$or(q,q)"), "1") self.assertEqual(self.parser.eval("$or(q,,)"), "1") def test_cmd_and(self): self.assertEqual(self.parser.eval("$and(,)"), "") self.assertEqual(self.parser.eval("$and(,q)"), "") self.assertEqual(self.parser.eval("$and(q,)"), "") self.assertEqual(self.parser.eval("$and(q,q,)"), "") self.assertEqual(self.parser.eval("$and(q,q)"), "1") self.assertEqual(self.parser.eval("$and(q,q,q)"), "1") def test_cmd_not(self): self.assertEqual(self.parser.eval("$not($noop())"), "1") self.assertEqual(self.parser.eval("$not(q)"), "") def test_cmd_add(self): self.assertEqual(self.parser.eval("$add(1,2)"), "3") self.assertEqual(self.parser.eval("$add(1,2,3)"), "6") def test_cmd_sub(self): self.assertEqual(self.parser.eval("$sub(1,2)"), "-1") self.assertEqual(self.parser.eval("$sub(2,1)"), "1") self.assertEqual(self.parser.eval("$sub(4,2,1)"), "1") def test_cmd_div(self): self.assertEqual(self.parser.eval("$div(9,3)"), "3") self.assertEqual(self.parser.eval("$div(10,3)"), "3") self.assertEqual(self.parser.eval("$div(30,3,3)"), "3") def test_cmd_mod(self): self.assertEqual(self.parser.eval("$mod(9,3)"), "0") self.assertEqual(self.parser.eval("$mod(10,3)"), "1") self.assertEqual(self.parser.eval("$mod(10,6,3)"), "1") def test_cmd_mul(self): self.assertEqual(self.parser.eval("$mul(9,3)"), "27") self.assertEqual(self.parser.eval("$mul(10,3)"), "30") self.assertEqual(self.parser.eval("$mul(2,5,3)"), "30") def test_cmd_eq(self): self.assertEqual(self.parser.eval("$eq(,)"), "1") self.assertEqual(self.parser.eval("$eq(,$noop())"), "1") self.assertEqual(self.parser.eval("$eq(,q)"), "") self.assertEqual(self.parser.eval("$eq(q,q)"), "1") self.assertEqual(self.parser.eval("$eq(q,)"), "") def test_cmd_ne(self): self.assertEqual(self.parser.eval("$ne(,)"), "") self.assertEqual(self.parser.eval("$ne(,$noop())"), "") self.assertEqual(self.parser.eval("$ne(,q)"), "1") self.assertEqual(self.parser.eval("$ne(q,q)"), "") self.assertEqual(self.parser.eval("$ne(q,)"), "1") def test_cmd_lower(self): self.assertEqual(self.parser.eval("$lower(AbeCeDA)"), "abeceda") def test_cmd_upper(self): self.assertEqual(self.parser.eval("$upper(AbeCeDA)"), "ABECEDA") def test_cmd_rreplace(self): self.assertEqual( self.parser.eval(r'''$rreplace(test \(disc 1\),\\s\\\(disc \\d+\\\),)'''), "test" ) def test_cmd_rsearch(self): self.assertEqual( self.parser.eval(r"$rsearch(test \(disc 1\),\\\(disc \(\\d+\)\\\))"), "1" ) def test_arguments(self): self.assertTrue( self.parser.eval( r"$set(bleh,$rsearch(test \(disc 1\),\\\(disc \(\\d+\)\\\)))) $set(wer,1)")) def test_cmd_gt(self): self.assertEqual(self.parser.eval("$gt(10,4)"), "1") self.assertEqual(self.parser.eval("$gt(6,4)"), "1") def test_cmd_gte(self): self.assertEqual(self.parser.eval("$gte(10,10)"), "1") self.assertEqual(self.parser.eval("$gte(10,4)"), "1") self.assertEqual(self.parser.eval("$gte(6,4)"), "1") def test_cmd_lt(self): self.assertEqual(self.parser.eval("$lt(4,10)"), "1") self.assertEqual(self.parser.eval("$lt(4,6)"), "1") def test_cmd_lte(self): self.assertEqual(self.parser.eval("$lte(10,10)"), "1") self.assertEqual(self.parser.eval("$lte(4,10)"), "1") self.assertEqual(self.parser.eval("$lte(4,6)"), "1") def test_cmd_len(self): self.assertEqual(self.parser.eval("$len(abcdefg)"), "7") self.assertEqual(self.parser.eval("$len(0)"), "1") self.assertEqual(self.parser.eval("$len()"), "0") def test_cmd_firstalphachar(self): self.assertEqual(self.parser.eval("$firstalphachar(abc)"), "A") self.assertEqual(self.parser.eval("$firstalphachar(Abc)"), "A") self.assertEqual(self.parser.eval("$firstalphachar(1abc)"), "#") self.assertEqual(self.parser.eval("$firstalphachar(...abc)"), "#") self.assertEqual(self.parser.eval("$firstalphachar(1abc,_)"), "_") self.assertEqual(self.parser.eval("$firstalphachar(...abc,_)"), "_") self.assertEqual(self.parser.eval("$firstalphachar()"), "#") self.assertEqual(self.parser.eval("$firstalphachar(,_)"), "_") self.assertEqual(self.parser.eval("$firstalphachar( abc)"), "#") def test_cmd_initials(self): self.assertEqual(self.parser.eval("$initials(Abc def Ghi)"), "AdG") self.assertEqual(self.parser.eval("$initials(Abc #def Ghi)"), "AG") self.assertEqual(self.parser.eval("$initials(Abc 1def Ghi)"), "AG") self.assertEqual(self.parser.eval("$initials(Abc)"), "A") self.assertEqual(self.parser.eval("$initials()"), "") def test_cmd_firstwords(self): self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,11)"), "Abc Def Ghi") self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,12)"), "Abc Def Ghi") self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,7)"), "Abc Def") self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,8)"), "Abc Def") self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,6)"), "Abc") self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,0)"), "") self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,NaN)"), "") self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,)"), "") def test_cmd_startswith(self): self.assertEqual(self.parser.eval("$startswith(abc,a)"), "1") self.assertEqual(self.parser.eval("$startswith(abc,abc)"), "1") self.assertEqual(self.parser.eval("$startswith(abc,)"), "1") self.assertEqual(self.parser.eval("$startswith(abc,b)"), "0") self.assertEqual(self.parser.eval("$startswith(abc,Ab)"), "0") def test_cmd_endswith(self): self.assertEqual(self.parser.eval("$endswith(abc,c)"), "1") self.assertEqual(self.parser.eval("$endswith(abc,abc)"), "1") self.assertEqual(self.parser.eval("$endswith(abc,)"), "1") self.assertEqual(self.parser.eval("$endswith(abc,b)"), "0") self.assertEqual(self.parser.eval("$endswith(abc,bC)"), "0") def test_cmd_truncate(self): self.assertEqual(self.parser.eval("$truncate(abcdefg,0)"), "") self.assertEqual(self.parser.eval("$truncate(abcdefg,7)"), "abcdefg") self.assertEqual(self.parser.eval("$truncate(abcdefg,3)"), "abc") self.assertEqual(self.parser.eval("$truncate(abcdefg,10)"), "abcdefg") self.assertEqual(self.parser.eval("$truncate(abcdefg,)"), "abcdefg") self.assertEqual(self.parser.eval("$truncate(abcdefg,NaN)"), "abcdefg") def test_cmd_copy(self): context = Metadata() tagsToCopy = ["tag1", "tag2"] context["source"] = tagsToCopy context["target"] = ["will", "be", "overwritten"] self.parser.eval("$copy(target,source)", context) self.assertEqual(self.parser.context.getall("target"), tagsToCopy) def _eval_and_check_copymerge(self, context, expected): self.parser.eval("$copymerge(target,source)", context) self.assertEqual(self.parser.context.getall("target"), expected) def test_cmd_copymerge_notarget(self): context = Metadata() tagsToCopy = ["tag1", "tag2"] context["source"] = tagsToCopy self._eval_and_check_copymerge(context, tagsToCopy) def test_cmd_copymerge_nosource(self): context = Metadata() target = ["tag1", "tag2"] context["target"] = target self._eval_and_check_copymerge(context, target) def test_cmd_copymerge_removedupes(self): context = Metadata() context["target"] = ["tag1", "tag2"] context["source"] = ["tag2", "tag3"] self._eval_and_check_copymerge(context, ["tag1", "tag2", "tag3"]) def test_cmd_copymerge_nonlist(self): context = Metadata() context["target"] = "targetval" context["source"] = "sourceval" self._eval_and_check_copymerge(context, ["targetval", "sourceval"]) def test_cmd_eq_any(self): self.assertEqual(self.parser.eval("$eq_any(abc,def,ghi,jkl)"), "") self.assertEqual(self.parser.eval("$eq_any(abc,def,ghi,jkl,abc)"), "1") def test_cmd_ne_all(self): self.assertEqual(self.parser.eval("$ne_all(abc,def,ghi,jkl)"), "1") self.assertEqual(self.parser.eval("$ne_all(abc,def,ghi,jkl,abc)"), "") def test_cmd_eq_all(self): self.assertEqual(self.parser.eval("$eq_all(abc,abc,abc,abc)"), "1") self.assertEqual(self.parser.eval("$eq_all(abc,abc,def,ghi)"), "") def test_cmd_ne_any(self): self.assertEqual(self.parser.eval("$ne_any(abc,abc,abc,abc)"), "") self.assertEqual(self.parser.eval("$ne_any(abc,abc,def,ghi)"), "1") def test_cmd_swapprefix(self): self.assertEqual(self.parser.eval("$swapprefix(A stitch in time)"), "stitch in time, A") self.assertEqual(self.parser.eval("$swapprefix(The quick brown fox)"), "quick brown fox, The") self.assertEqual(self.parser.eval("$swapprefix(How now brown cow)"), "How now brown cow") self.assertEqual(self.parser.eval("$swapprefix(When the red red robin)"), "When the red red robin") self.assertEqual(self.parser.eval("$swapprefix(A stitch in time,How,When,Who)"), "A stitch in time") self.assertEqual(self.parser.eval("$swapprefix(The quick brown fox,How,When,Who)"), "The quick brown fox") self.assertEqual(self.parser.eval("$swapprefix(How now brown cow,How,When,Who)"), "now brown cow, How") self.assertEqual(self.parser.eval("$swapprefix(When the red red robin,How,When,Who)"), "the red red robin, When") def test_cmd_delprefix(self): self.assertEqual(self.parser.eval("$delprefix(A stitch in time)"), "stitch in time") self.assertEqual(self.parser.eval("$delprefix(The quick brown fox)"), "quick brown fox") self.assertEqual(self.parser.eval("$delprefix(How now brown cow)"), "How now brown cow") self.assertEqual(self.parser.eval("$delprefix(When the red red robin)"), "When the red red robin") self.assertEqual(self.parser.eval("$delprefix(A stitch in time,How,When,Who)"), "A stitch in time") self.assertEqual(self.parser.eval("$delprefix(The quick brown fox,How,When,Who)"), "The quick brown fox") self.assertEqual(self.parser.eval("$delprefix(How now brown cow,How,When,Who)"), "now brown cow") self.assertEqual(self.parser.eval("$delprefix(When the red red robin,How,When,Who)"), "the red red robin") def test_default_filenaming(self): context = Metadata() context['albumartist'] = u'albumartist' context['artist'] = u'artist' context['album'] = u'album' context['totaldiscs'] = 2 context['discnumber'] = 1 context['tracknumber'] = 8 context['title'] = u'title' result = self.parser.eval(_DEFAULT_FILE_NAMING_FORMAT, context) self.assertEqual(result, u'albumartist/album/1-08 title') context['~multiartist'] = '1' result = self.parser.eval(_DEFAULT_FILE_NAMING_FORMAT, context) self.assertEqual(result, u'albumartist/album/1-08 artist - title') def test_default_NAT_filenaming(self): context = Metadata() context['artist'] = u'artist' context['album'] = u'[non-album tracks]' context['title'] = u'title' result = self.parser.eval(_DEFAULT_FILE_NAMING_FORMAT, context) self.assertEqual(result, u'artist/title') def test_cmd_with_not_arguments(self): try: self.parser.eval("$noargstest()") except ScriptError: self.fail("Function noargs raised ScriptError unexpectedly.") def test_cmd_unset_simple(self): context = Metadata() context['title'] = u'Foo' context['album'] = u'Foo' context['artist'] = u'Foo' self.parser.eval("$unset(album)", context) self.assertNotIn('album', context) def test_cmd_unset_prefix(self): context = Metadata() context['title'] = u'Foo' context['~rating'] = u'4' self.parser.eval("$unset(_rating)", context) self.assertNotIn('~rating', context) def test_cmd_unset_multi(self): context = Metadata() context['performer:foo'] = u'Foo' context['performer:bar'] = u'Foo' self.parser.eval("$unset(performer:*)", context) self.assertNotIn('performer:bar', context) self.assertNotIn('performer:foo', context)
def eval(self, script, context=None, file=None): result = ScriptParser.eval(self, script, context, file) return result.split(STOP_MARKER)[0]
class ScriptParserTest(unittest.TestCase): def setUp(self): config.setting = { 'enabled_plugins': '', } self.parser = ScriptParser() def test_cmd_noop(self): self.assertEqual(self.parser.eval("$noop()"), "") def test_cmd_if(self): self.assertEqual(self.parser.eval("$if(1,a,b)"), "a") self.assertEqual(self.parser.eval("$if(,a,b)"), "b") def test_cmd_if2(self): self.assertEqual(self.parser.eval("$if2(,a,b)"), "a") self.assertEqual(self.parser.eval("$if2($noop(),b)"), "b") def test_cmd_left(self): self.assertEqual(self.parser.eval("$left(abcd,2)"), "ab") def test_cmd_right(self): self.assertEqual(self.parser.eval("$right(abcd,2)"), "cd") def test_cmd_set(self): self.assertEqual(self.parser.eval("$set(test,aaa)%test%"), "aaa") def test_cmd_set_empty(self): self.assertEqual(self.parser.eval("$set(test,)%test%"), "") def test_cmd_set_multi_valued(self): context = Metadata() context["source"] = ["multi", "valued"] self.parser.eval("$set(test,%source%)", context) self.assertEqual(context.getall("test"), ["multi; valued"]) # list has only a single value def test_cmd_setmulti_multi_valued(self): context = Metadata() context["source"] = ["multi", "valued"] self.assertEqual("", self.parser.eval("$setmulti(test,%source%)", context)) # no return value self.assertEqual(context.getall("source"), context.getall("test")) def test_cmd_setmulti_multi_valued_wth_spaces(self): context = Metadata() context["source"] = ["multi, multi", "valued, multi"] self.assertEqual("", self.parser.eval("$setmulti(test,%source%)", context)) # no return value self.assertEqual(context.getall("source"), context.getall("test")) def test_cmd_setmulti_not_multi_valued(self): context = Metadata() context["source"] = "multi, multi" self.assertEqual("", self.parser.eval("$setmulti(test,%source%)", context)) # no return value self.assertEqual(context.getall("source"), context.getall("test")) def test_cmd_setmulti_will_remove_empty_items(self): context = Metadata() context["source"] = ["", "multi", ""] self.assertEqual("", self.parser.eval("$setmulti(test,%source%)", context)) # no return value self.assertEqual(["multi"], context.getall("test")) def test_cmd_setmulti_custom_splitter_string(self): context = Metadata() self.assertEqual("", self.parser.eval("$setmulti(test,multi##valued##test##,##)", context)) # no return value self.assertEqual(["multi", "valued", "test"], context.getall("test")) def test_cmd_setmulti_empty_splitter_does_nothing(self): context = Metadata() self.assertEqual("", self.parser.eval("$setmulti(test,multi; valued,)", context)) # no return value self.assertEqual(["multi; valued"], context.getall("test")) def test_cmd_get(self): context = Metadata() context["test"] = "aaa" self.assertEqual(self.parser.eval("$get(test)", context), "aaa") context["test2"] = ["multi", "valued"] self.assertEqual(self.parser.eval("$get(test2)", context), "multi; valued") def test_cmd_num(self): self.assertEqual(self.parser.eval("$num(3,3)"), "003") self.assertEqual(self.parser.eval("$num(03,3)"), "003") self.assertEqual(self.parser.eval("$num(123,2)"), "123") def test_cmd_or(self): self.assertEqual(self.parser.eval("$or(,)"), "") self.assertEqual(self.parser.eval("$or(,q)"), "1") self.assertEqual(self.parser.eval("$or(q,)"), "1") self.assertEqual(self.parser.eval("$or(q,q)"), "1") def test_cmd_and(self): self.assertEqual(self.parser.eval("$and(,)"), "") self.assertEqual(self.parser.eval("$and(,q)"), "") self.assertEqual(self.parser.eval("$and(q,)"), "") self.assertEqual(self.parser.eval("$and(q,q)"), "1") def test_cmd_not(self): self.assertEqual(self.parser.eval("$not($noop())"), "1") self.assertEqual(self.parser.eval("$not(q)"), "") def test_cmd_add(self): self.assertEqual(self.parser.eval("$add(1,2)"), "3") def test_cmd_sub(self): self.assertEqual(self.parser.eval("$sub(1,2)"), "-1") self.assertEqual(self.parser.eval("$sub(2,1)"), "1") def test_cmd_div(self): self.assertEqual(self.parser.eval("$div(9,3)"), "3") self.assertEqual(self.parser.eval("$div(10,3)"), "3") def test_cmd_mod(self): self.assertEqual(self.parser.eval("$mod(9,3)"), "0") self.assertEqual(self.parser.eval("$mod(10,3)"), "1") def test_cmd_mul(self): self.assertEqual(self.parser.eval("$mul(9,3)"), "27") self.assertEqual(self.parser.eval("$mul(10,3)"), "30") def test_cmd_eq(self): self.assertEqual(self.parser.eval("$eq(,)"), "1") self.assertEqual(self.parser.eval("$eq(,$noop())"), "1") self.assertEqual(self.parser.eval("$eq(,q)"), "") self.assertEqual(self.parser.eval("$eq(q,q)"), "1") self.assertEqual(self.parser.eval("$eq(q,)"), "") def test_cmd_ne(self): self.assertEqual(self.parser.eval("$ne(,)"), "") self.assertEqual(self.parser.eval("$ne(,$noop())"), "") self.assertEqual(self.parser.eval("$ne(,q)"), "1") self.assertEqual(self.parser.eval("$ne(q,q)"), "") self.assertEqual(self.parser.eval("$ne(q,)"), "1") def test_cmd_lower(self): self.assertEqual(self.parser.eval("$lower(AbeCeDA)"), "abeceda") def test_cmd_upper(self): self.assertEqual(self.parser.eval("$upper(AbeCeDA)"), "ABECEDA") def test_cmd_rreplace(self): self.assertEqual( self.parser.eval(r'''$rreplace(test \(disc 1\),\\s\\\(disc \\d+\\\),)'''), "test" ) def test_cmd_rsearch(self): self.assertEqual( self.parser.eval(r"$rsearch(test \(disc 1\),\\\(disc \(\\d+\)\\\))"), "1" ) def test_arguments(self): self.assertTrue( self.parser.eval( r"$set(bleh,$rsearch(test \(disc 1\),\\\(disc \(\\d+\)\\\)))) $set(wer,1)")) def test_cmd_gt(self): self.assertEqual(self.parser.eval("$gt(10,4)"), "1") self.assertEqual(self.parser.eval("$gt(6,4)"), "1") def test_cmd_gte(self): self.assertEqual(self.parser.eval("$gte(10,10)"), "1") self.assertEqual(self.parser.eval("$gte(10,4)"), "1") self.assertEqual(self.parser.eval("$gte(6,4)"), "1") def test_cmd_lt(self): self.assertEqual(self.parser.eval("$lt(4,10)"), "1") self.assertEqual(self.parser.eval("$lt(4,6)"), "1") def test_cmd_lte(self): self.assertEqual(self.parser.eval("$lte(10,10)"), "1") self.assertEqual(self.parser.eval("$lte(4,10)"), "1") self.assertEqual(self.parser.eval("$lte(4,6)"), "1") def test_cmd_len(self): self.assertEqual(self.parser.eval("$len(abcdefg)"), "7") self.assertEqual(self.parser.eval("$len()"), "0") def test_cmd_firstalphachar(self): self.assertEqual(self.parser.eval("$firstalphachar(abc)"), "A") self.assertEqual(self.parser.eval("$firstalphachar(Abc)"), "A") self.assertEqual(self.parser.eval("$firstalphachar(1abc)"), "#") self.assertEqual(self.parser.eval("$firstalphachar(...abc)"), "#") self.assertEqual(self.parser.eval("$firstalphachar(1abc,_)"), "_") self.assertEqual(self.parser.eval("$firstalphachar(...abc,_)"), "_") self.assertEqual(self.parser.eval("$firstalphachar()"), "#") self.assertEqual(self.parser.eval("$firstalphachar(,_)"), "_") self.assertEqual(self.parser.eval("$firstalphachar( abc)"), "#") def test_cmd_initials(self): self.assertEqual(self.parser.eval("$initials(Abc def Ghi)"), "AdG") self.assertEqual(self.parser.eval("$initials(Abc #def Ghi)"), "AG") self.assertEqual(self.parser.eval("$initials(Abc 1def Ghi)"), "AG") self.assertEqual(self.parser.eval("$initials(Abc)"), "A") self.assertEqual(self.parser.eval("$initials()"), "") def test_cmd_firstwords(self): self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,11)"), "Abc Def Ghi") self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,12)"), "Abc Def Ghi") self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,7)"), "Abc Def") self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,8)"), "Abc Def") self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,6)"), "Abc") self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,0)"), "") self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,NaN)"), "") self.assertEqual(self.parser.eval("$firstwords(Abc Def Ghi,)"), "") def test_cmd_truncate(self): self.assertEqual(self.parser.eval("$truncate(abcdefg,0)"), "") self.assertEqual(self.parser.eval("$truncate(abcdefg,7)"), "abcdefg") self.assertEqual(self.parser.eval("$truncate(abcdefg,3)"), "abc") self.assertEqual(self.parser.eval("$truncate(abcdefg,10)"), "abcdefg") self.assertEqual(self.parser.eval("$truncate(abcdefg,)"), "abcdefg") self.assertEqual(self.parser.eval("$truncate(abcdefg,NaN)"), "abcdefg") def test_cmd_copy(self): context = Metadata() tagsToCopy = ["tag1", "tag2"] context["source"] = tagsToCopy context["target"] = ["will", "be", "overwritten"] self.parser.eval("$copy(target,source)", context) self.assertEqual(self.parser.context.getall("target"), tagsToCopy) def _eval_and_check_copymerge(self, context, expected): self.parser.eval("$copymerge(target,source)", context) self.assertEqual(self.parser.context.getall("target"), expected) def test_cmd_copymerge_notarget(self): context = Metadata() tagsToCopy = ["tag1", "tag2"] context["source"] = tagsToCopy self._eval_and_check_copymerge(context, tagsToCopy) def test_cmd_copymerge_nosource(self): context = Metadata() target = ["tag1", "tag2"] context["target"] = target self._eval_and_check_copymerge(context, target) def test_cmd_copymerge_removedupes(self): context = Metadata() context["target"] = ["tag1", "tag2"] context["source"] = ["tag2", "tag3"] self._eval_and_check_copymerge(context, ["tag1", "tag2", "tag3"]) def test_cmd_copymerge_nonlist(self): context = Metadata() context["target"] = "targetval" context["source"] = "sourceval" self._eval_and_check_copymerge(context, ["targetval", "sourceval"])