mirror of
https://github.com/fergalmoran/picard.git
synced 2026-02-27 10:03:59 +00:00
841 lines
24 KiB
Python
841 lines
24 KiB
Python
# -*- coding: utf-8 -*-
|
|
#
|
|
# Picard, the next-generation MusicBrainz tagger
|
|
# Copyright (C) 2006-2007 Lukáš Lalinský
|
|
# Copyright (C) 2007 Javier Kohen
|
|
# Copyright (C) 2008 Philipp Wolfer
|
|
#
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
import re
|
|
import operator
|
|
from collections import namedtuple
|
|
from inspect import getargspec
|
|
from picard.metadata import Metadata
|
|
from picard.metadata import MULTI_VALUED_JOINER
|
|
from picard.plugin import ExtensionPoint
|
|
|
|
|
|
class ScriptError(Exception):
|
|
pass
|
|
|
|
|
|
class ParseError(ScriptError):
|
|
pass
|
|
|
|
|
|
class EndOfFile(ParseError):
|
|
pass
|
|
|
|
|
|
class SyntaxError(ParseError):
|
|
pass
|
|
|
|
|
|
class UnknownFunction(ScriptError):
|
|
pass
|
|
|
|
|
|
class ScriptText(unicode):
|
|
|
|
def eval(self, state):
|
|
return self
|
|
|
|
|
|
class ScriptVariable(object):
|
|
|
|
def __init__(self, name):
|
|
self.name = name
|
|
|
|
def __repr__(self):
|
|
return '<ScriptVariable %%%s%%>' % self.name
|
|
|
|
def eval(self, state):
|
|
name = self.name
|
|
if name.startswith(u"_"):
|
|
name = u"~" + name[1:]
|
|
return state.context.get(name, u"")
|
|
|
|
|
|
FunctionRegistryItem = namedtuple("FunctionRegistryItem",
|
|
["function", "eval_args",
|
|
"argcount"])
|
|
Bound = namedtuple("Bound", ["lower", "upper"])
|
|
|
|
|
|
class ScriptFunction(object):
|
|
|
|
def __init__(self, name, args, parser):
|
|
try:
|
|
argnum_bound = parser.functions[name].argcount
|
|
argcount = len(args)
|
|
if argnum_bound and not (argnum_bound.lower <= argcount
|
|
and (argnum_bound.upper is None
|
|
or len(args) <= argnum_bound.upper)):
|
|
raise ScriptError(
|
|
"Wrong number of arguments for $%s: Expected %s, got %i at position %i, line %i"
|
|
% (name,
|
|
str(argnum_bound.lower)
|
|
if argnum_bound.upper is None
|
|
else "%i - %i" % (argnum_bound.lower, argnum_bound.upper),
|
|
argcount,
|
|
parser._x,
|
|
parser._y))
|
|
except KeyError:
|
|
raise UnknownFunction("Unknown function '%s'" % name)
|
|
|
|
self.name = name
|
|
self.args = args
|
|
|
|
def __repr__(self):
|
|
return "<ScriptFunction $%s(%r)>" % (self.name, self.args)
|
|
|
|
def eval(self, parser):
|
|
function, eval_args, num_args = parser.functions[self.name]
|
|
if eval_args:
|
|
args = [arg.eval(parser) for arg in self.args]
|
|
else:
|
|
args = self.args
|
|
return function(parser, *args)
|
|
|
|
|
|
class ScriptExpression(list):
|
|
|
|
def eval(self, state):
|
|
result = []
|
|
for item in self:
|
|
result.append(item.eval(state))
|
|
return "".join(result)
|
|
|
|
|
|
def isidentif(ch):
|
|
return ch.isalnum() or ch == '_'
|
|
|
|
|
|
class ScriptParser(object):
|
|
|
|
r"""Tagger script parser.
|
|
|
|
Grammar:
|
|
text ::= [^$%] | '\$' | '\%' | '\(' | '\)' | '\,'
|
|
argtext ::= [^$%(),] | '\$' | '\%' | '\(' | '\)' | '\,'
|
|
identifier ::= [a-zA-Z0-9_]
|
|
variable ::= '%' identifier '%'
|
|
function ::= '$' identifier '(' (argument (',' argument)*)? ')'
|
|
expression ::= (variable | function | text)*
|
|
argument ::= (variable | function | argtext)*
|
|
"""
|
|
|
|
_function_registry = ExtensionPoint()
|
|
_cache = {}
|
|
|
|
def __raise_eof(self):
|
|
raise EndOfFile("Unexpected end of script at position %d, line %d" % (self._x, self._y))
|
|
|
|
def __raise_char(self, ch):
|
|
#line = self._text[self._line:].split("\n", 1)[0]
|
|
#cursor = " " * (self._pos - self._line - 1) + "^"
|
|
#raise SyntaxError("Unexpected character '%s' at position %d, line %d\n%s\n%s" % (ch, self._x, self._y, line, cursor))
|
|
raise SyntaxError("Unexpected character '%s' at position %d, line %d" % (ch, self._x, self._y))
|
|
|
|
def read(self):
|
|
try:
|
|
ch = self._text[self._pos]
|
|
except IndexError:
|
|
return None
|
|
else:
|
|
self._pos += 1
|
|
self._px = self._x
|
|
self._py = self._y
|
|
if ch == '\n':
|
|
self._line = self._pos
|
|
self._x = 1
|
|
self._y += 1
|
|
else:
|
|
self._x += 1
|
|
return ch
|
|
|
|
def unread(self):
|
|
self._pos -= 1
|
|
self._x = self._px
|
|
self._y = self._py
|
|
|
|
def parse_arguments(self):
|
|
results = []
|
|
while True:
|
|
result, ch = self.parse_expression(False)
|
|
results.append(result)
|
|
if ch == ')':
|
|
# Only an empty expression as first argument
|
|
# is the same as no argument given.
|
|
if len(results) == 1 and results[0] == []:
|
|
return []
|
|
return results
|
|
|
|
def parse_function(self):
|
|
start = self._pos
|
|
while True:
|
|
ch = self.read()
|
|
if ch == '(':
|
|
name = self._text[start:self._pos-1]
|
|
if name not in self.functions:
|
|
raise UnknownFunction("Unknown function '%s'" % name)
|
|
return ScriptFunction(name, self.parse_arguments(), self)
|
|
elif ch is None:
|
|
self.__raise_eof()
|
|
elif not isidentif(ch):
|
|
self.__raise_char(ch)
|
|
|
|
def parse_variable(self):
|
|
begin = self._pos
|
|
while True:
|
|
ch = self.read()
|
|
if ch == '%':
|
|
return ScriptVariable(self._text[begin:self._pos-1])
|
|
elif ch is None:
|
|
self.__raise_eof()
|
|
elif not isidentif(ch) and ch != ':':
|
|
self.__raise_char(ch)
|
|
|
|
def parse_text(self, top):
|
|
text = []
|
|
while True:
|
|
ch = self.read()
|
|
if ch == "\\":
|
|
ch = self.read()
|
|
if ch == 'n':
|
|
text.append('\n')
|
|
elif ch == 't':
|
|
text.append('\t')
|
|
elif ch not in "$%(),\\":
|
|
self.__raise_char(ch)
|
|
else:
|
|
text.append(ch)
|
|
elif ch is None:
|
|
break
|
|
elif not top and ch == '(':
|
|
self.__raise_char(ch)
|
|
elif ch in '$%' or (not top and ch in ',)'):
|
|
self.unread()
|
|
break
|
|
else:
|
|
text.append(ch)
|
|
return ScriptText("".join(text))
|
|
|
|
def parse_expression(self, top):
|
|
tokens = ScriptExpression()
|
|
while True:
|
|
ch = self.read()
|
|
if ch is None:
|
|
if top:
|
|
break
|
|
else:
|
|
self.__raise_eof()
|
|
elif not top and ch in ',)':
|
|
break
|
|
elif ch == '$':
|
|
tokens.append(self.parse_function())
|
|
elif ch == '%':
|
|
tokens.append(self.parse_variable())
|
|
else:
|
|
self.unread()
|
|
tokens.append(self.parse_text(top))
|
|
return (tokens, ch)
|
|
|
|
def load_functions(self):
|
|
self.functions = {}
|
|
for name, item in ScriptParser._function_registry:
|
|
self.functions[name] = item
|
|
|
|
def parse(self, script, functions=False):
|
|
"""Parse the script."""
|
|
self._text = script
|
|
self._pos = 0
|
|
self._px = self._x = 1
|
|
self._py = self._y = 1
|
|
self._line = 0
|
|
if not functions:
|
|
self.load_functions()
|
|
return self.parse_expression(True)[0]
|
|
|
|
def eval(self, script, context=None, file=None):
|
|
"""Parse and evaluate the script."""
|
|
self.context = context if context is not None else Metadata()
|
|
self.file = file
|
|
self.load_functions()
|
|
key = hash(script)
|
|
if key not in ScriptParser._cache:
|
|
ScriptParser._cache[key] = self.parse(script, True)
|
|
return ScriptParser._cache[key].eval(self)
|
|
|
|
|
|
def register_script_function(function, name=None, eval_args=True,
|
|
check_argcount=True):
|
|
"""Registers a script function. If ``name`` is ``None``,
|
|
``function.__name__`` will be used.
|
|
If ``eval_args`` is ``False``, the arguments will not be evaluated before being
|
|
passed to ``function``.
|
|
If ``check_argcount`` is ``False`` the number of arguments passed to the
|
|
function will not be verified."""
|
|
|
|
args, varargs, keywords, defaults = getargspec(function)
|
|
args = len(args) - 1 # -1 for the parser
|
|
varargs = varargs is not None
|
|
defaults = len(defaults) if defaults else 0
|
|
|
|
argcount = Bound(args - defaults, args if not varargs else None)
|
|
|
|
if name is None:
|
|
name = function.__name__
|
|
ScriptParser._function_registry.register(function.__module__,
|
|
(name, FunctionRegistryItem(
|
|
function, eval_args,
|
|
argcount if argcount and check_argcount else False)
|
|
)
|
|
)
|
|
|
|
|
|
def _compute_int(operation, *args):
|
|
return str(reduce(operation, map(int, args)))
|
|
|
|
|
|
def _compute_logic(operation, *args):
|
|
return operation(args)
|
|
|
|
|
|
def func_if(parser, _if, _then, _else=None):
|
|
"""If ``if`` is not empty, it returns ``then``, otherwise it returns ``else``."""
|
|
if _if.eval(parser):
|
|
return _then.eval(parser)
|
|
elif _else:
|
|
return _else.eval(parser)
|
|
return ''
|
|
|
|
|
|
def func_if2(parser, *args):
|
|
"""Returns first non empty argument."""
|
|
for arg in args:
|
|
arg = arg.eval(parser)
|
|
if arg:
|
|
return arg
|
|
return ''
|
|
|
|
|
|
def func_noop(parser, *args):
|
|
"""Does nothing :)"""
|
|
return ''
|
|
|
|
|
|
def func_left(parser, text, length):
|
|
"""Returns first ``num`` characters from ``text``."""
|
|
try:
|
|
return text[:int(length)]
|
|
except ValueError:
|
|
return ""
|
|
|
|
|
|
def func_right(parser, text, length):
|
|
"""Returns last ``num`` characters from ``text``."""
|
|
try:
|
|
return text[-int(length):]
|
|
except ValueError:
|
|
return ""
|
|
|
|
|
|
def func_lower(parser, text):
|
|
"""Returns ``text`` in lower case."""
|
|
return text.lower()
|
|
|
|
|
|
def func_upper(parser, text):
|
|
"""Returns ``text`` in upper case."""
|
|
return text.upper()
|
|
|
|
|
|
def func_pad(parser, text, length, char):
|
|
try:
|
|
return char * (int(length) - len(text)) + text
|
|
except ValueError:
|
|
return ""
|
|
|
|
|
|
def func_strip(parser, text):
|
|
return re.sub(r"\s+", " ", text).strip()
|
|
|
|
|
|
def func_replace(parser, text, old, new):
|
|
return text.replace(old, new)
|
|
|
|
|
|
def func_in(parser, text, needle):
|
|
if needle in text:
|
|
return "1"
|
|
else:
|
|
return ""
|
|
|
|
|
|
def func_inmulti(parser, text, value, separator=MULTI_VALUED_JOINER):
|
|
"""Splits ``text`` by ``separator``, and returns true if the resulting list contains ``value``."""
|
|
return func_in(parser, text.split(separator) if separator else [text], value)
|
|
|
|
|
|
def func_rreplace(parser, text, old, new):
|
|
return re.sub(old, new, text)
|
|
|
|
|
|
def func_rsearch(parser, text, pattern):
|
|
match = re.search(pattern, text)
|
|
if match:
|
|
try:
|
|
return match.group(1)
|
|
except IndexError:
|
|
return match.group(0)
|
|
return u""
|
|
|
|
|
|
def func_num(parser, text, length):
|
|
try:
|
|
format = "%%0%dd" % min(int(length), 20)
|
|
except ValueError:
|
|
return ""
|
|
try:
|
|
value = int(text)
|
|
except ValueError:
|
|
value = 0
|
|
return format % value
|
|
|
|
|
|
def func_unset(parser, name):
|
|
"""Unsets the variable ``name``."""
|
|
if name.startswith("_"):
|
|
name = "~" + name[1:]
|
|
# Allow wild-card unset for certain keys
|
|
if name in ('performer:*', 'comment:*', 'lyrics:*'):
|
|
name = name[:-1]
|
|
for key in parser.context.keys():
|
|
if key.startswith(name):
|
|
del parser.context[key]
|
|
return ""
|
|
try:
|
|
del parser.context[name]
|
|
except KeyError:
|
|
pass
|
|
return ""
|
|
|
|
|
|
def func_set(parser, name, value):
|
|
"""Sets the variable ``name`` to ``value``."""
|
|
if value:
|
|
if name.startswith("_"):
|
|
name = "~" + name[1:]
|
|
parser.context[name] = value
|
|
else:
|
|
func_unset(parser, name)
|
|
return ""
|
|
|
|
|
|
def func_setmulti(parser, name, value, separator=MULTI_VALUED_JOINER):
|
|
"""Sets the variable ``name`` to ``value`` as a list; splitting by the passed string, or "; " otherwise."""
|
|
return func_set(parser, name, value.split(separator) if value and separator else value)
|
|
|
|
|
|
def func_get(parser, name):
|
|
"""Returns the variable ``name`` (equivalent to ``%name%``)."""
|
|
if name.startswith("_"):
|
|
name = "~" + name[1:]
|
|
return parser.context.get(name, u"")
|
|
|
|
|
|
def func_copy(parser, new, old):
|
|
"""Copies content of variable ``old`` to variable ``new``."""
|
|
if new.startswith("_"):
|
|
new = "~" + new[1:]
|
|
if old.startswith("_"):
|
|
old = "~" + old[1:]
|
|
parser.context[new] = parser.context.getall(old)[:]
|
|
return ""
|
|
|
|
|
|
def func_copymerge(parser, new, old):
|
|
"""Copies content of variable ``old`` and appends it into variable ``new``, removing duplicates. This is normally
|
|
used to merge a multi-valued variable into another, existing multi-valued variable."""
|
|
if new.startswith("_"):
|
|
new = "~" + new[1:]
|
|
if old.startswith("_"):
|
|
old = "~" + old[1:]
|
|
newvals = parser.context.getall(new)
|
|
oldvals = parser.context.getall(old)
|
|
parser.context[new] = newvals + list(set(oldvals) - set(newvals))
|
|
return ""
|
|
|
|
|
|
def func_trim(parser, text, char=None):
|
|
"""Trims all leading and trailing whitespaces from ``text``. The optional
|
|
second parameter specifies the character to trim."""
|
|
if char:
|
|
return text.strip(char)
|
|
else:
|
|
return text.strip()
|
|
|
|
|
|
def func_add(parser, x, y, *args):
|
|
"""Adds ``y`` to ``x``.
|
|
Can be used with an arbitrary number of arguments.
|
|
Eg: $add(x, y, z) = ((x + y) + z)
|
|
"""
|
|
try:
|
|
return _compute_int(operator.add, x, y, *args)
|
|
except ValueError:
|
|
return ""
|
|
|
|
|
|
def func_sub(parser, x, y, *args):
|
|
"""Subtracts ``y`` from ``x``.
|
|
Can be used with an arbitrary number of arguments.
|
|
Eg: $sub(x, y, z) = ((x - y) - z)
|
|
"""
|
|
try:
|
|
return _compute_int(operator.sub, x, y, *args)
|
|
except ValueError:
|
|
return ""
|
|
|
|
|
|
def func_div(parser, x, y, *args):
|
|
"""Divides ``x`` by ``y``.
|
|
Can be used with an arbitrary number of arguments.
|
|
Eg: $div(x, y, z) = ((x / y) / z)
|
|
"""
|
|
try:
|
|
return _compute_int(operator.div, x, y, *args)
|
|
except ValueError:
|
|
return ""
|
|
|
|
|
|
def func_mod(parser, x, y, *args):
|
|
"""Returns the remainder of ``x`` divided by ``y``.
|
|
Can be used with an arbitrary number of arguments.
|
|
Eg: $mod(x, y, z) = ((x % y) % z)
|
|
"""
|
|
try:
|
|
return _compute_int(operator.mod, x, y, *args)
|
|
except ValueError:
|
|
return ""
|
|
|
|
|
|
def func_mul(parser, x, y, *args):
|
|
"""Multiplies ``x`` by ``y``.
|
|
Can be used with an arbitrary number of arguments.
|
|
Eg: $mul(x, y, z) = ((x * y) * z)
|
|
"""
|
|
try:
|
|
return _compute_int(operator.mul, x, y, *args)
|
|
except ValueError:
|
|
return ""
|
|
|
|
|
|
def func_or(parser, x, y, *args):
|
|
"""Returns true, if either ``x`` or ``y`` not empty.
|
|
Can be used with an arbitrary number of arguments. The result is
|
|
true if ANY of the arguments is not empty.
|
|
"""
|
|
if _compute_logic(any, x, y, *args):
|
|
return "1"
|
|
else:
|
|
return ""
|
|
|
|
|
|
def func_and(parser, x, y, *args):
|
|
"""Returns true, if both ``x`` and ``y`` are not empty.
|
|
Can be used with an arbitrary number of arguments. The result is
|
|
true if ALL of the arguments are not empty.
|
|
"""
|
|
if _compute_logic(all, x, y, *args):
|
|
return "1"
|
|
else:
|
|
return ""
|
|
|
|
|
|
def func_not(parser, x):
|
|
"""Returns true, if ``x`` is empty."""
|
|
if not x:
|
|
return "1"
|
|
else:
|
|
return ""
|
|
|
|
|
|
def func_eq(parser, x, y):
|
|
"""Returns true, if ``x`` equals ``y``."""
|
|
if x == y:
|
|
return "1"
|
|
else:
|
|
return ""
|
|
|
|
|
|
def func_ne(parser, x, y):
|
|
"""Returns true, if ``x`` not equals ``y``."""
|
|
if x != y:
|
|
return "1"
|
|
else:
|
|
return ""
|
|
|
|
|
|
def func_lt(parser, x, y):
|
|
"""Returns true, if ``x`` is lower than ``y``."""
|
|
try:
|
|
if int(x) < int(y):
|
|
return "1"
|
|
except ValueError:
|
|
pass
|
|
return ""
|
|
|
|
|
|
def func_lte(parser, x, y):
|
|
"""Returns true, if ``x`` is lower than or equals ``y``."""
|
|
try:
|
|
if int(x) <= int(y):
|
|
return "1"
|
|
except ValueError:
|
|
pass
|
|
return ""
|
|
|
|
|
|
def func_gt(parser, x, y):
|
|
"""Returns true, if ``x`` is greater than ``y``."""
|
|
try:
|
|
if int(x) > int(y):
|
|
return "1"
|
|
except ValueError:
|
|
pass
|
|
return ""
|
|
|
|
|
|
def func_gte(parser, x, y):
|
|
"""Returns true, if ``x`` is greater than or equals ``y``."""
|
|
try:
|
|
if int(x) >= int(y):
|
|
return "1"
|
|
except ValueError:
|
|
pass
|
|
return ""
|
|
|
|
|
|
def func_len(parser, text=""):
|
|
return str(len(text))
|
|
|
|
|
|
def func_performer(parser, pattern="", join=", "):
|
|
values = []
|
|
for name, value in parser.context.items():
|
|
if name.startswith("performer:") and pattern in name:
|
|
values.append(value)
|
|
return join.join(values)
|
|
|
|
|
|
def func_matchedtracks(parser, arg):
|
|
if parser.file and parser.file.parent:
|
|
return str(parser.file.parent.album.get_num_matched_tracks())
|
|
return "0"
|
|
|
|
|
|
def func_is_complete(parser):
|
|
if (parser.file and parser.file.parent
|
|
and parser.file.parent.album.is_complete()):
|
|
return "1"
|
|
return "0"
|
|
|
|
|
|
def func_firstalphachar(parser, text="", nonalpha="#"):
|
|
if len(text) == 0:
|
|
return nonalpha
|
|
firstchar = text[0]
|
|
if firstchar.isalpha():
|
|
return firstchar.upper()
|
|
else:
|
|
return nonalpha
|
|
|
|
|
|
def func_initials(parser, text=""):
|
|
return "".join(a[:1] for a in text.split(" ") if a[:1].isalpha())
|
|
|
|
|
|
def func_firstwords(parser, text, length):
|
|
try:
|
|
length = int(length)
|
|
except ValueError as e:
|
|
length = 0
|
|
if len(text) <= length:
|
|
return text
|
|
else:
|
|
if text[length] == ' ':
|
|
return text[:length]
|
|
return text[:length].rsplit(' ', 1)[0]
|
|
|
|
|
|
def func_startswith(parser, text, prefix):
|
|
if text.startswith(prefix):
|
|
return "1"
|
|
return "0"
|
|
|
|
|
|
def func_endswith(parser, text, suffix):
|
|
if text.endswith(suffix):
|
|
return "1"
|
|
return "0"
|
|
|
|
|
|
def func_truncate(parser, text, length):
|
|
try:
|
|
length = int(length)
|
|
except ValueError as e:
|
|
length = None
|
|
return text[:length].rstrip()
|
|
|
|
|
|
def func_swapprefix(parser, text, *prefixes):
|
|
"""
|
|
Moves the specified prefixes to the end of text.
|
|
If no prefix is specified 'A' and 'The' are taken as default.
|
|
"""
|
|
# Inspired by the swapprefix plugin by Philipp Wolfer.
|
|
|
|
text, prefix = _delete_prefix(parser, text, *prefixes)
|
|
if prefix != '':
|
|
return text + ', ' + prefix
|
|
return text
|
|
|
|
|
|
def func_delprefix(parser, text, *prefixes):
|
|
"""
|
|
Deletes the specified prefixes.
|
|
If no prefix is specified 'A' and 'The' are taken as default.
|
|
"""
|
|
# Inspired by the swapprefix plugin by Philipp Wolfer.
|
|
|
|
return _delete_prefix(parser, text, *prefixes)[0]
|
|
|
|
|
|
def _delete_prefix(parser, text, *prefixes):
|
|
"""
|
|
Worker function to deletes the specified prefixes.
|
|
Returns remaining string and deleted part separately.
|
|
If no prefix is specified 'A' and 'The' used.
|
|
"""
|
|
# Inspired by the swapprefix plugin by Philipp Wolfer.
|
|
|
|
if not prefixes:
|
|
prefixes = ('A', 'The')
|
|
text = text.strip()
|
|
match = re.match('(' + r'\s+)|('.join(prefixes) + r'\s+)', text)
|
|
if match:
|
|
pref = match.group()
|
|
return text[len(pref):], pref.strip()
|
|
return text, ''
|
|
|
|
|
|
def func_eq_any(parser, x, *args):
|
|
"""
|
|
Return True if one string matches any of one or more other strings.
|
|
$eq_any(a,b,c ...) is functionally equivalent to $or($eq(a,b),$eq(a,c) ...)
|
|
Example: $if($eq_any(%artist%,foo,bar,baz),$set(engineer,test))
|
|
"""
|
|
# Inspired by the eq2 plugin by Brian Schweitzer.
|
|
return '1' if x in args else ''
|
|
|
|
|
|
def func_ne_all(parser, x, *args):
|
|
"""
|
|
Return True if one string doesn't match all of one or more other strings.
|
|
$ne_all(a,b,c ...) is functionally equivalent to $and($ne(a,b),$ne(a,c) ...)
|
|
Example: $if($ne_all(%artist%,foo,bar,baz),$set(engineer,test))
|
|
"""
|
|
# Inspired by the ne2 plugin by Brian Schweitzer.
|
|
return '1' if x not in args else ''
|
|
|
|
|
|
def func_eq_all(parser, x, *args):
|
|
"""
|
|
Return True if all string are equal.
|
|
$eq_all(a,b,c ...) is functionally equivalent to $and($eq(a,b),$eq(a,c) ...)
|
|
Example: $if($eq_all(%albumartist%,%artist%,Justin Bieber),$set(engineer,Meat Loaf))
|
|
"""
|
|
for i in args:
|
|
if x != i:
|
|
return ''
|
|
return '1'
|
|
|
|
|
|
def func_ne_any(parser, x, *args):
|
|
"""
|
|
Return True if all strings are not equal.
|
|
$ne_any(a,b,c ...) is functionally equivalent to $or($ne(a,b),$ne(a,c) ...)
|
|
Example: $if($ne_any(%albumartist%,%trackartist%,%composer%),$set(lyricist,%composer%))
|
|
"""
|
|
return func_not(parser, func_eq_all(parser, x, *args))
|
|
|
|
|
|
register_script_function(func_if, "if", eval_args=False)
|
|
register_script_function(func_if2, "if2", eval_args=False)
|
|
register_script_function(func_noop, "noop", eval_args=False)
|
|
register_script_function(func_left, "left")
|
|
register_script_function(func_right, "right")
|
|
register_script_function(func_lower, "lower")
|
|
register_script_function(func_upper, "upper")
|
|
register_script_function(func_pad, "pad")
|
|
register_script_function(func_strip, "strip")
|
|
register_script_function(func_replace, "replace")
|
|
register_script_function(func_rreplace, "rreplace")
|
|
register_script_function(func_rsearch, "rsearch")
|
|
register_script_function(func_num, "num")
|
|
register_script_function(func_unset, "unset")
|
|
register_script_function(func_set, "set")
|
|
register_script_function(func_setmulti, "setmulti")
|
|
register_script_function(func_get, "get")
|
|
register_script_function(func_trim, "trim")
|
|
register_script_function(func_add, "add")
|
|
register_script_function(func_sub, "sub")
|
|
register_script_function(func_div, "div")
|
|
register_script_function(func_mod, "mod")
|
|
register_script_function(func_mul, "mul")
|
|
register_script_function(func_or, "or")
|
|
register_script_function(func_and, "and")
|
|
register_script_function(func_not, "not")
|
|
register_script_function(func_eq, "eq")
|
|
register_script_function(func_ne, "ne")
|
|
register_script_function(func_lt, "lt")
|
|
register_script_function(func_lte, "lte")
|
|
register_script_function(func_gt, "gt")
|
|
register_script_function(func_gte, "gte")
|
|
register_script_function(func_in, "in")
|
|
register_script_function(func_inmulti, "inmulti")
|
|
register_script_function(func_copy, "copy")
|
|
register_script_function(func_copymerge, "copymerge")
|
|
register_script_function(func_len, "len")
|
|
register_script_function(func_performer, "performer")
|
|
register_script_function(func_matchedtracks, "matchedtracks")
|
|
register_script_function(func_is_complete, "is_complete")
|
|
register_script_function(func_firstalphachar, "firstalphachar")
|
|
register_script_function(func_initials, "initials")
|
|
register_script_function(func_firstwords, "firstwords")
|
|
register_script_function(func_startswith, "startswith")
|
|
register_script_function(func_endswith, "endswith")
|
|
register_script_function(func_truncate, "truncate")
|
|
register_script_function(func_swapprefix, "swapprefix", check_argcount=False)
|
|
register_script_function(func_delprefix, "delprefix", check_argcount=False)
|
|
register_script_function(func_eq_any, "eq_any", check_argcount=False)
|
|
register_script_function(func_ne_all, "ne_all", check_argcount=False)
|
|
register_script_function(func_eq_all, "eq_all", check_argcount=False)
|
|
register_script_function(func_ne_any, "ne_any", check_argcount=False)
|