diff --git a/beets/plugins.py b/beets/plugins.py index 990fe0874..b5c3d421b 100644 --- a/beets/plugins.py +++ b/beets/plugins.py @@ -151,10 +151,9 @@ class BeetsPlugin(metaclass=abc.ABCMeta): list ) listeners: ClassVar[dict[EventType, list[Listener]]] = defaultdict(list) - - template_funcs: TFuncMap[str] - template_fields: TFuncMap[Item] - album_template_fields: TFuncMap[Album] + template_funcs: ClassVar[TFuncMap[str]] = {} + template_fields: ClassVar[TFuncMap[Item]] = {} + album_template_fields: ClassVar[TFuncMap[Album]] = {} name: str config: ConfigView @@ -220,9 +219,14 @@ class BeetsPlugin(metaclass=abc.ABCMeta): self.name = name or self.__module__.split(".")[-1] self.config = beets.config[self.name] - self.template_funcs = {} - self.template_fields = {} - self.album_template_fields = {} + # Set class attributes if they are not already set + # for the type of plugin. + if not self.template_funcs: + self.template_funcs = {} # type: ignore[misc] + if not self.template_fields: + self.template_fields = {} # type: ignore[misc] + if not self.album_template_fields: + self.album_template_fields = {} # type: ignore[misc] self.early_import_stages = [] self.import_stages = [] @@ -356,6 +360,33 @@ class BeetsPlugin(metaclass=abc.ABCMeta): self._set_log_level_and_params(logging.WARNING, func) ) + @classmethod + def template_func(cls, name: str) -> Callable[[TFunc[str]], TFunc[str]]: + """Decorator that registers a path template function. The + function will be invoked as ``%name{}`` from path format + strings. + """ + + def helper(func: TFunc[str]) -> TFunc[str]: + cls.template_funcs[name] = func + return func + + return helper + + @classmethod + def template_field(cls, name: str) -> Callable[[TFunc[Item]], TFunc[Item]]: + """Decorator that registers a path template field computation. + The value will be referenced as ``$name`` from path format + strings. The function must accept a single parameter, the Item + being formatted. + """ + + def helper(func: TFunc[Item]) -> TFunc[Item]: + cls.template_fields[name] = func + return func + + return helper + def get_plugin_names() -> list[str]: """Discover and return the set of plugin names to be loaded.