working on field titlecasing

This commit is contained in:
Henry 2025-10-17 19:32:49 -07:00
parent 117f8adfac
commit 944299078c
2 changed files with 84 additions and 11 deletions

View file

@ -16,6 +16,7 @@
Title case logic is derived from the python-titlecase library."""
from beets.plugins import BeetsPlugin
from beets.dbcore import types
from beets import ui
from titlecase import titlecase
from typing import TYPE_CHECKING
@ -27,8 +28,31 @@ if TYPE_CHECKING:
__author__ = "henryoberholtzer@gmail.com"
__version__ = "1.0"
# These fields are excluded to avoid modifying anything
# that may be case sensistive, or important to database
# function
EXCLUDED_INFO_FIELDS = set([
'id',
'mb_workid',
'mb_trackid',
'mb_albumid',
'mb_artistid',
'mb_albumartistid',
'mb_albumartistids',
'mb_releasetrackid',
'acoustid_fingerprint',
'acoustid_id',
'mb_releasegroupid',
'asin',
'isrc',
'bitrate_mode',
'encoder_info',
'encoder_settings'
])
class TitlecasePlugin(BeetsPlugin):
preserve: dict[str, str] = {}
fields_to_process: set[str] = []
def __init__(self) -> None:
super().__init__()
@ -45,10 +69,10 @@ class TitlecasePlugin(BeetsPlugin):
preserve - provide a list of words/acronyms with specific case requirements
small_first_last - if small characters should be title cased at beginning
titlecase_metadata - if metadata fields should have title case applied
include_fields - fields to apply titlecase to, default is all, except select excluded fields
exclude_fields - fields to exclude from titlecase to, default is none
include_fields - fields to apply titlecase to, default is all
titlecase will not interact with possibly case sensitive fields like id,
path or album_id
NOTE: titlecase will not interact with possibly case sensitive fields like id,
path or album_id. Paths are best modified in path config.
"""
# Register template function
self.template_funcs["titlecase"] = self.titlecase
@ -61,11 +85,38 @@ class TitlecasePlugin(BeetsPlugin):
self._command.parser.add_option(
"-p",
"--preserve",
help="preserve the case of the given word"
help="preserve the case of the given word, in addition to those configured."
)
self._command.parser.add_option(
"-f",
"--field",
help="apply to the following fields."
)
for word in self.config["preserve"].as_str_seq():
self.preserve[word.upper()] = word
self.__init_field_list__()
def __init_field_list__(self) -> None:
""" Creates the set for fields to process in tagging.
If we have include_fields from config, the shared fields will be used.
Then, any fields specified to be excluded will be removed.
This will result in exclude_fields overriding include_fields.
Last, the EXCLUDED_INFO_FIELDS are removed to prevent unitentional modification.
"""
initial_field_list = set([
k for k, v in Item()._fields.items() if
isinstance(v, types.STRING) or
isinstance(v, types.SEMICOLONS_SPACE_DSV) or
isinstance(v, types.MULTI_VALUE_DSV)
)
if (incl := self.config["include_fields"].as_str_seq()):
initial_field_list = initial_field_list.intersection(set(incl))
if (excl := self.config["exclude_fields"].as_str_seq()):
initial_field_list -= set(excl)
initial_field_list -= set(EXCLUDED_INFO_FIELDS)
self.fields_to_process = basic_fields_list
def __preserved__(self, word, **kwargs) -> str | None:
""" Callback function for words to preserve case of."""
@ -73,15 +124,35 @@ class TitlecasePlugin(BeetsPlugin):
return preserved_word
return None
def titlecase_fields(self, text: str) -> str:
return titlecase(text)
def commands(self) -> list[ui.Subcommand]:
def func(lib, opts, args):
self.config.set_args(opts)
def titlecase_fields(self, item: Item):
""" Applies titlecase to fields, except
those excluded by the default exclusions and the
set exclude lists.
"""
for field in self.fields_to_process:
init_str = getattr(item, field, "")
if init_str:
cased = self.titlecase(old_str)
self._log.info(f"{field}: {init_str} -> {cased}")
setattr(item, field, cased)
else:
self._log.info(f"{field}: no string present")
def titlecase(self, text: str) -> str:
""" Titlecase the given text """
""" Titlecase the given text. """
return titlecase(text,
small_first_last=self.config["small_first_last"],
callback=self.__preserved__)
small_first_last=self.config["small_first_last"],
callback=self.__preserved__)
def imported(self, session: ImportSession, task: ImportTask) -> None:
""" Import hook for titlecasing on import """
return
""" Import hook for titlecasing on import. """
for item in task.imported_items():
self._log.info(f"titlecasing {item.title}:")
self.titlecase_fields(item)
item.store()

View file

@ -60,3 +60,5 @@ class TitlecasePluginTest(BeetsTestCase):
def test_imported(self):
assert 1 == 3
def test_init_field_list(self):