diff --git a/docs/formatting.md b/docs/formatting.md
index f01e4193..b9c4355e 100644
--- a/docs/formatting.md
+++ b/docs/formatting.md
@@ -139,6 +139,12 @@ Format specifiers can be used for advanced formatting by using the options provi
{empty:?[/]/} |
|
+
+ [<start>:<stop>] |
+ Applies a Slicing operation to the current value, similar to Field Names |
+ {foo:[1:-1]} |
+ oo Ba |
+
L<maxlen>/<repl>/ |
Replaces the entire output with <repl> if its length exceeds <maxlen> |
diff --git a/gallery_dl/formatter.py b/gallery_dl/formatter.py
index f2c2d331..107c8ed6 100644
--- a/gallery_dl/formatter.py
+++ b/gallery_dl/formatter.py
@@ -232,12 +232,7 @@ def parse_field_name(field_name):
func = operator.itemgetter
try:
if ":" in key:
- start, _, stop = key.partition(":")
- stop, _, step = stop.partition(":")
- start = int(start) if start else None
- stop = int(stop) if stop else None
- step = int(step) if step else None
- key = slice(start, stop, step)
+ key = _slice(key)
except TypeError:
pass # key is an integer
@@ -246,6 +241,16 @@ def parse_field_name(field_name):
return first, funcs
+def _slice(indices):
+ start, _, stop = indices.partition(":")
+ stop, _, step = stop.partition(":")
+ return slice(
+ int(start) if start else None,
+ int(stop) if stop else None,
+ int(step) if step else None,
+ )
+
+
def parse_format_spec(format_spec, conversion):
fmt = build_format_func(format_spec)
if not conversion:
@@ -283,6 +288,8 @@ def build_format_func(format_spec):
fmt = format_spec[0]
if fmt == "?":
return _parse_optional(format_spec)
+ if fmt == "[":
+ return _parse_slice(format_spec)
if fmt == "L":
return _parse_maxlen(format_spec)
if fmt == "J":
@@ -305,6 +312,16 @@ def _parse_optional(format_spec):
return optional
+def _parse_slice(format_spec):
+ indices, _, format_spec = format_spec.partition("]")
+ slice = _slice(indices[1:])
+ fmt = build_format_func(format_spec)
+
+ def apply_slice(obj):
+ return fmt(obj[slice])
+ return apply_slice
+
+
def _parse_maxlen(format_spec):
maxlen, replacement, format_spec = format_spec.split("/", 2)
maxlen = text.parse_int(maxlen[1:])
diff --git a/test/test_formatter.py b/test/test_formatter.py
index dc818b00..5b8ca0ab 100644
--- a/test/test_formatter.py
+++ b/test/test_formatter.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-# Copyright 2021 Mike Fährmann
+# Copyright 2021-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
@@ -135,6 +135,21 @@ class TestFormatter(unittest.TestCase):
self._run_test("{a[:50:2]}", v[:50:2])
self._run_test("{a[::]}" , v)
+ self._run_test("{a:[1:10]}" , v[1:10])
+ self._run_test("{a:[-10:-1]}", v[-10:-1])
+ self._run_test("{a:[5:]}" , v[5:])
+ self._run_test("{a:[50:]}", v[50:])
+ self._run_test("{a:[:5]}" , v[:5])
+ self._run_test("{a:[:50]}", v[:50])
+ self._run_test("{a:[:]}" , v)
+ self._run_test("{a:[1:10:2]}" , v[1:10:2])
+ self._run_test("{a:[-10:-1:2]}", v[-10:-1:2])
+ self._run_test("{a:[5::2]}" , v[5::2])
+ self._run_test("{a:[50::2]}", v[50::2])
+ self._run_test("{a:[:5:2]}" , v[:5:2])
+ self._run_test("{a:[:50:2]}", v[:50:2])
+ self._run_test("{a:[::]}" , v)
+
def test_maxlen(self):
v = self.kwdict["a"]
self._run_test("{a:L5/foo/}" , "foo")
@@ -177,6 +192,9 @@ class TestFormatter(unittest.TestCase):
# join-and-replace
self._run_test("{l:J-/Rb/E/}", "a-E-c")
+ # join and slice
+ self._run_test("{l:J-/[1:-1]}", "-b-")
+
# optional-and-maxlen
self._run_test("{d[a]:?>/L1/too long/}", "")
self._run_test("{d[c]:?>/L5/too long/}", "")