diff --git a/docs/formatting.md b/docs/formatting.md index f7212079..3a9ac302 100644 --- a/docs/formatting.md +++ b/docs/formatting.md @@ -63,6 +63,12 @@ Conversion specifiers allow to *convert* the value to a different form or type. {foo!C} Foo Bar + + g + Slugify a value + {foo!g} + foo-bar + j Serialize value to a JSON formatted string diff --git a/gallery_dl/formatter.py b/gallery_dl/formatter.py index 192950bc..dd32b8a5 100644 --- a/gallery_dl/formatter.py +++ b/gallery_dl/formatter.py @@ -381,6 +381,7 @@ _CONVERSIONS = { "T": util.datetime_to_timestamp_string, "d": text.parse_timestamp, "U": text.unescape, + "g": text.slugify, "S": util.to_string, "s": str, "r": repr, diff --git a/gallery_dl/text.py b/gallery_dl/text.py index 97ef3acb..79cf016e 100644 --- a/gallery_dl/text.py +++ b/gallery_dl/text.py @@ -39,6 +39,16 @@ def split_html(txt): return [] +def slugify(value): + """Convert a string to a URL slug + + Adapted from: + https://github.com/django/django/blob/master/django/utils/text.py + """ + value = re.sub(r"[^\w\s-]", "", str(value).lower()) + return re.sub(r"[-\s]+", "-", value).strip("-_") + + def ensure_http_scheme(url, scheme="https://"): """Prepend 'scheme' to 'url' if it doesn't have one""" if url and not url.startswith(("https://", "http://")): diff --git a/test/test_formatter.py b/test/test_formatter.py index 41fe2e21..b3353326 100644 --- a/test/test_formatter.py +++ b/test/test_formatter.py @@ -58,6 +58,7 @@ class TestFormatter(unittest.TestCase): self._run_test("{dt!T}", "1262304000") self._run_test("{l!j}", '["a", "b", "c"]') self._run_test("{dt!j}", '"2010-01-01 00:00:00"') + self._run_test("{a!g}", "hello-world") with self.assertRaises(KeyError): self._run_test("{a!q}", "hello world") diff --git a/test/test_text.py b/test/test_text.py index ffed7267..0ac77671 100644 --- a/test/test_text.py +++ b/test/test_text.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -# Copyright 2015-2021 Mike Fährmann +# Copyright 2015-2022 Mike Fährmann # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as @@ -75,6 +75,23 @@ class TestText(unittest.TestCase): for value in INVALID: self.assertEqual(f(value), empty) + def test_slugify(self, f=text.slugify): + self.assertEqual(f("Hello World"), "hello-world") + self.assertEqual(f("-HeLLo---World-"), "hello-world") + self.assertEqual(f("_-H#e:l#l:o+\t+W?o!rl=d-_"), "hello-world") + self.assertEqual(f("_Hello_World_"), "hello_world") + + self.assertEqual(f(""), "") + self.assertEqual(f("-"), "") + self.assertEqual(f("--"), "") + + self.assertEqual(f(()), "") + self.assertEqual(f([]), "") + self.assertEqual(f({}), "") + self.assertEqual(f(None), "none") + self.assertEqual(f(1), "1") + self.assertEqual(f(2.3), "23") + def test_ensure_http_scheme(self, f=text.ensure_http_scheme): result = "https://example.org/filename.ext"