diff --git a/docs/formatting.md b/docs/formatting.md
index f188a538..9ed3ae22 100644
--- a/docs/formatting.md
+++ b/docs/formatting.md
@@ -192,6 +192,12 @@ Format specifiers can be used for advanced formatting by using the options provi
{foo:Ro/()/} |
F()() Bar |
+
+ C<conversion(s)>/ |
+ Apply Conversions to the current value |
+ {tags:CSgc/} |
+ "Sun-tree-water" |
+
S<order>/ |
Sort a list. <order> can be either ascending or descending/reverse. (default: a) |
diff --git a/gallery_dl/formatter.py b/gallery_dl/formatter.py
index 0b212d5e..43ee756c 100644
--- a/gallery_dl/formatter.py
+++ b/gallery_dl/formatter.py
@@ -325,6 +325,26 @@ def _parse_slice(format_spec, default):
return apply_slice
+def _parse_conversion(format_spec, default):
+ conversions, _, format_spec = format_spec.partition(_SEPARATOR)
+ convs = [_CONVERSIONS[c] for c in conversions[1:]]
+ fmt = _build_format_func(format_spec, default)
+
+ if len(conversions) <= 2:
+
+ def convert_one(obj):
+ return fmt(conv(obj))
+ conv = _CONVERSIONS[conversions[1]]
+ return convert_one
+
+ def convert_many(obj):
+ for conv in convs:
+ obj = conv(obj)
+ return fmt(obj)
+ convs = [_CONVERSIONS[c] for c in conversions[1:]]
+ return convert_many
+
+
def _parse_maxlen(format_spec, default):
maxlen, replacement, format_spec = format_spec.split(_SEPARATOR, 2)
maxlen = text.parse_int(maxlen[1:])
@@ -447,6 +467,7 @@ _CONVERSIONS = {
_FORMAT_SPECIFIERS = {
"?": _parse_optional,
"[": _parse_slice,
+ "C": _parse_conversion,
"D": _parse_datetime,
"L": _parse_maxlen,
"J": _parse_join,
diff --git a/test/test_formatter.py b/test/test_formatter.py
index 73e958cc..4c74a441 100644
--- a/test/test_formatter.py
+++ b/test/test_formatter.py
@@ -267,6 +267,11 @@ class TestFormatter(unittest.TestCase):
"{a:Sort-reverse}", # starts with 'S', contains 'r'
"['w', 'r', 'o', 'l', 'h', 'd', 'O', 'L', 'L', 'E', ' ']")
+ def test_specifier_conversions(self):
+ self._run_test("{a:Cl}" , "hello world")
+ self._run_test("{h:CHC}" , "Foo & Bar")
+ self._run_test("{l:CSulc}", "A, b, c")
+
def test_chain_special(self):
# multiple replacements
self._run_test("{a:Rh/C/RE/e/RL/l/}", "Cello wOrld")