Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add advanced rewrite plugin #4845

Merged
merged 4 commits into from
Oct 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions beetsplug/advancedrewrite.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# This file is part of beets.
# Copyright 2023, Max Rumpf.
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.

"""Plugin to rewrite fields based on a given query."""

from collections import defaultdict
import shlex

import confuse
from beets import ui
from beets.dbcore import AndQuery, query_from_strings
from beets.library import Item, Album
from beets.plugins import BeetsPlugin


def rewriter(field, rules):
"""Template field function factory.

Create a template field function that rewrites the given field
with the given rewriting rules.
``rules`` must be a list of (query, replacement) pairs.
"""
def fieldfunc(item):
value = item._values_fixed[field]
for query, replacement in rules:
if query.match(item):
# Rewrite activated.
return replacement
# Not activated; return original value.
return value

return fieldfunc


class AdvancedRewritePlugin(BeetsPlugin):
"""Plugin to rewrite fields based on a given query."""

def __init__(self):
"""Parse configuration and register template fields for rewriting."""
super().__init__()

template = confuse.Sequence({
'match': str,
'field': str,
'replacement': str,
})

# Gather all the rewrite rules for each field.
rules = defaultdict(list)
for rule in self.config.get(template):
query = query_from_strings(AndQuery, Item, prefixes={},
query_parts=shlex.split(rule['match']))
fieldname = rule['field']
replacement = rule['replacement']
if fieldname not in Item._fields:
raise ui.UserError(
"invalid field name (%s) in rewriter" % fieldname)
self._log.debug('adding template field {0} → {1}',
fieldname, replacement)
rules[fieldname].append((query, replacement))
if fieldname == 'artist':
# Special case for the artist field: apply the same
# rewrite for "albumartist" as well.
rules['albumartist'].append((query, replacement))

# Replace each template field with the new rewriter function.
for fieldname, fieldrules in rules.items():
getter = rewriter(fieldname, fieldrules)
self.template_fields[fieldname] = getter
if fieldname in Album._fields:
self.album_template_fields[fieldname] = getter
2 changes: 2 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ New features:
* :doc:`/plugins/fetchart`: Fix the error with CoverArtArchive where no cover
would be found when the `maxwidth` option matches a pre-sized thumbnail size,
but no thumbnail is provided by CAA. We now fallback to the raw image.
* :doc:`/plugins/advancedrewrite`: Add an advanced version of the `rewrite`
plugin which allows to replace fields based on a given library query.

Bug fixes:

Expand Down
39 changes: 39 additions & 0 deletions docs/plugins/advancedrewrite.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
Advanced Rewrite Plugin
=======================

The ``advancedrewrite`` plugin lets you easily substitute values
in your templates and path formats, similarly to the :doc:`/plugins/rewrite`.
Please make sure to read the documentation of that plugin first.

The *advanced* rewrite plugin doesn't match the rewritten field itself,
but instead checks if the given item matches a :doc:`query </reference/query>`.
Only then, the field is replaced with the given value.

To use advanced field rewriting, first enable the ``advancedrewrite`` plugin
(see :ref:`using-plugins`).
Then, make a ``advancedrewrite:`` section in your config file to contain
your rewrite rules.

In contrast to the normal ``rewrite`` plugin, you need to provide a list
of replacement rule objects, each consisting of a query, a field name,
and the replacement value.

For example, to credit all songs of ODD EYE CIRCLE before 2023
to their original group name, you can use the following rule::

advancedrewrite:
- match: "mb_artistid:dec0f331-cb08-4c8e-9c9f-aeb1f0f6d88c year:..2022"
field: artist
replacement: "이달의 소녀 오드아이써클"

As a convenience, the plugin applies patterns for the ``artist`` field to the
``albumartist`` field as well. (Otherwise, you would probably want to duplicate
every rule for ``artist`` and ``albumartist``.)

A word of warning: This plugin theoretically only applies to templates and path
formats; it initially does not modify files' metadata tags or the values
tracked by beets' library database, but since it *rewrites all field lookups*,
it modifies the file's metadata anyway. See comments in issue :bug:`2786`.

As an alternative to this plugin the simpler :doc:`/plugins/rewrite` or
similar :doc:`/plugins/substitute` can be used.
4 changes: 4 additions & 0 deletions docs/plugins/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ following to your configuration::

absubmit
acousticbrainz
advancedrewrite
albumtypes
aura
autobpm
Expand Down Expand Up @@ -246,6 +247,9 @@ Path Formats
:doc:`rewrite <rewrite>`
Substitute values in path formats.

:doc:`advancedrewrite <advancedrewrite>`
Substitute field values for items matching a query.

:doc:`substitute <substitute>`
As an alternative to :doc:`rewrite <rewrite>`, use this plugin. The main
difference between them is that this plugin never modifies the files
Expand Down
Loading