Example #1
0
 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 ""
Example #2
0
    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 ""
Example #3
0
 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 ""
Example #4
0
    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 "", ""
Example #5
0
 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 ""
Example #6
0
 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
Example #7
0
File: file.py Project: Freso/picard
 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
Example #8
0
    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()
Example #9
0
 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()
Example #10
0
 def setUp(self):
     config.setting = {
         'enabled_plugins': '',
     }
     self.parser = ScriptParser()
     def func_noargstest(parser):
         return ""
     register_script_function(func_noargstest, "noargstest")
Example #11
0
 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."))
Example #12
0
 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."))
Example #13
0
 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."))
Example #14
0
    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()
Example #15
0
 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."))
Example #16
0
 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)
Example #17
0
 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)
Example #18
0
    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)
Example #19
0
    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
Example #21
0
 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)
Example #22
0
 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)
Example #23
0
    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)
Example #24
0
    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)
Example #25
0
    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)
Example #26
0
 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 ""
Example #27
0
    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)
Example #28
0
    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()
Example #29
0
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)
Example #30
0
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
Example #31
0
 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)
Example #32
0
    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()
Example #33
0
 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
Example #34
0
    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()
Example #35
0
    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 = []
Example #36
0
 def check(self):
     parser = ScriptParser()
     try:
         parser.eval(self.ui.tagger_script.toPlainText())
     except Exception as e:
         raise ScriptCheckError(_("Script Error"), str(e))
Example #37
0
    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()
Example #38
0
 def check(self):
     parser = ScriptParser()
     try:
         parser.eval(unicode(self.ui.tagger_script.toPlainText()))
     except Exception as e:
         raise OptionsCheckError(_("Script Error"), str(e))
Example #39
0
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)
Example #40
0
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)
Example #41
0
 def setUp(self):
     config.setting = {
         'enabled_plugins': '',
     }
     self.parser = ScriptParser()
Example #42
0
    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 = []
Example #43
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"])
Example #44
0
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")
Example #45
0
    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 = []
Example #46
0
 def setUp(self):
     QtCore.QObject.config = FakeConfig()
     self.parser = ScriptParser()
Example #47
0
 def check_format(self):
     parser = ScriptParser()
     try:
         parser.eval(unicode(self.ui.file_naming_format.toPlainText()))
     except Exception, e:
         raise OptionsCheckError("", str(e))
Example #48
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"])

    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')
Example #49
0
 def setUp(self):
     config.setting = {
         'enabled_plugins': '',
     }
     self.parser = ScriptParser()
Example #50
0
    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 = []
Example #51
0
    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()
Example #52
0
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)
Example #53
0
 def eval(self, script, context=None, file=None):
     result = ScriptParser.eval(self, script, context, file)
     return result.split(STOP_MARKER)[0]
Example #54
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"])