mirror of
https://github.com/beetbox/beets.git
synced 2025-12-30 04:22:40 +01:00
Initial title case plugin written and working, needs to apply to tags
This commit is contained in:
parent
347802afea
commit
8d11ed51d2
4 changed files with 125 additions and 1 deletions
51
beetsplug/titlecase.py
Normal file
51
beetsplug/titlecase.py
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
# This file is part of beets.
|
||||
# Copyright 2025, Henry Oberholtzer
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Apply NYT manual of style title case rules, to paths and tag text.
|
||||
Title case logic is derived from the python-titlecase library."""
|
||||
|
||||
from beets.plugins import BeetsPlugin
|
||||
from titlecase import titlecase
|
||||
|
||||
__author__ = "henryoberholtzer@gmail.com"
|
||||
__version__ = "1.0"
|
||||
|
||||
class TitlecasePlugin(BeetsPlugin):
|
||||
preserve: dict[str, str] = {}
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
# Register template function
|
||||
self.template_funcs["titlecase"] = self.titlecase
|
||||
|
||||
self.config.add(
|
||||
{
|
||||
"preserve": [],
|
||||
"small_first_last": True
|
||||
}
|
||||
)
|
||||
print(self.config)
|
||||
for word in self.config["preserve"].as_str_seq():
|
||||
self.preserve[word.upper()] = word
|
||||
|
||||
def __preserved__(self, word, **kwargs) -> str | None:
|
||||
""" Callback function for words to preserve case of."""
|
||||
if (preserved_word := self.preserve.get(word.upper(), "")):
|
||||
return preserved_word
|
||||
return None
|
||||
|
||||
def titlecase(self, text: str) -> str:
|
||||
""" Titlecase the given text """
|
||||
return titlecase(text,
|
||||
small_first_last=self.config["small_first_last"],
|
||||
callback=self.__preserved__)
|
||||
18
poetry.lock
generated
18
poetry.lock
generated
|
|
@ -2182,6 +2182,8 @@ files = [
|
|||
{file = "pycairo-1.28.0-cp313-cp313-win32.whl", hash = "sha256:d13352429d8a08a1cb3607767d23d2fb32e4c4f9faa642155383980ec1478c24"},
|
||||
{file = "pycairo-1.28.0-cp313-cp313-win_amd64.whl", hash = "sha256:082aef6b3a9dcc328fa648d38ed6b0a31c863e903ead57dd184b2e5f86790140"},
|
||||
{file = "pycairo-1.28.0-cp313-cp313-win_arm64.whl", hash = "sha256:026afd53b75291917a7412d9fe46dcfbaa0c028febd46ff1132d44a53ac2c8b6"},
|
||||
{file = "pycairo-1.28.0-cp314-cp314-win32.whl", hash = "sha256:d0ab30585f536101ad6f09052fc3895e2a437ba57531ea07223d0e076248025d"},
|
||||
{file = "pycairo-1.28.0-cp314-cp314-win_amd64.whl", hash = "sha256:94f2ed204999ab95a0671a0fa948ffbb9f3d6fb8731fe787917f6d022d9c1c0f"},
|
||||
{file = "pycairo-1.28.0-cp39-cp39-win32.whl", hash = "sha256:3ed16d48b8a79cc584cb1cb0ad62dfb265f2dda6d6a19ef5aab181693e19c83c"},
|
||||
{file = "pycairo-1.28.0-cp39-cp39-win_amd64.whl", hash = "sha256:da0d1e6d4842eed4d52779222c6e43d254244a486ca9fdab14e30042fd5bdf28"},
|
||||
{file = "pycairo-1.28.0-cp39-cp39-win_arm64.whl", hash = "sha256:458877513eb2125513122e8aa9c938630e94bb0574f94f4fb5ab55eb23d6e9ac"},
|
||||
|
|
@ -3364,6 +3366,19 @@ files = [
|
|||
{file = "threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "titlecase"
|
||||
version = "2.4.1"
|
||||
description = "Python Port of John Gruber's titlecase.pl"
|
||||
optional = true
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "titlecase-2.4.1.tar.gz", hash = "sha256:7d83a277ccbbda11a2944e78a63e5ccaf3d32f828c594312e4862f9a07f635f5"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
regex = ["regex (>=2020.4.4)"]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.10.2"
|
||||
|
|
@ -3624,9 +3639,10 @@ replaygain = ["PyGObject"]
|
|||
scrub = ["mutagen"]
|
||||
sonosupdate = ["soco"]
|
||||
thumbnails = ["Pillow", "pyxdg"]
|
||||
titlecase = ["titlecase"]
|
||||
web = ["flask", "flask-cors"]
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = ">=3.9,<4"
|
||||
content-hash = "faea27878ce1ca3f1335fd83e027b289351c51c73550bda72bf501a9c82166f7"
|
||||
content-hash = "c5a6a4710beb5bf1e4bbd97f2a590ee020a567aea2341aad5f846eda13ebb94e"
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ requests = { version = "*", optional = true }
|
|||
resampy = { version = ">=0.4.3", optional = true }
|
||||
requests-oauthlib = { version = ">=0.6.1", optional = true }
|
||||
soco = { version = "*", optional = true }
|
||||
titlecase = { version = ">=2.4.1", optional = true }
|
||||
|
||||
pydata-sphinx-theme = { version = "*", optional = true }
|
||||
sphinx = { version = "*", optional = true }
|
||||
|
|
@ -151,6 +152,7 @@ replaygain = [
|
|||
scrub = ["mutagen"]
|
||||
sonosupdate = ["soco"]
|
||||
thumbnails = ["Pillow", "pyxdg"]
|
||||
titlecase = ["titlecase"]
|
||||
web = ["flask", "flask-cors"]
|
||||
|
||||
[tool.poetry.scripts]
|
||||
|
|
|
|||
55
test/plugins/test_titlecase.py
Normal file
55
test/plugins/test_titlecase.py
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# This file is part of beets.
|
||||
# Copyright 2025, Henry Oberholtzer
|
||||
#
|
||||
# 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.
|
||||
|
||||
""" Tests for the 'titlecase' plugin"""
|
||||
|
||||
import pytest
|
||||
|
||||
from beets import config
|
||||
from beets.test.helper import BeetsTestCase
|
||||
from beetsplug.titlecase import TitlecasePlugin
|
||||
|
||||
@pytest.mark.parametrize("given, expected",
|
||||
[("PENDULUM", "Pendulum"),
|
||||
("Aaron-carl", "Aaron-Carl"),
|
||||
("LTJ bukem", "LTJ Bukem"),
|
||||
("Freaky chakra Vs. Single Cell Orchestra",
|
||||
"Freaky Chakra vs. Single Cell Orchestra")
|
||||
])
|
||||
def test_basic_titlecase(given, expected):
|
||||
""" Assert that general behavior is as expected. """
|
||||
assert TitlecasePlugin().titlecase(given) == expected
|
||||
|
||||
|
||||
class TitlecasePluginTest(BeetsTestCase):
|
||||
|
||||
def test_preserved_case(self):
|
||||
""" Test using given strings to preserve case """
|
||||
names_to_preserve = ["easyFun", "A.D.O.R.",
|
||||
"D.R.", "ABBA", "LaTeX"]
|
||||
config["titlecase"]["preserve"] = names_to_preserve
|
||||
for name in names_to_preserve:
|
||||
assert TitlecasePlugin().titlecase(
|
||||
name.lower()) == name
|
||||
|
||||
def test_small_first_last(self):
|
||||
config["titlecase"]["small_first_last"] = False
|
||||
assert TitlecasePlugin().titlecase(
|
||||
"A Simple Trial") == "a Simple Trial"
|
||||
config["titlecase"]["small_first_last"] = True
|
||||
assert TitlecasePlugin().titlecase(
|
||||
"A simple Trial") == "A Simple Trial"
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in a new issue