[path] implement 'path-convert' option (#493 #6582)

This commit is contained in:
Mike Fährmann
2025-08-14 18:51:09 +02:00
parent b4a897aede
commit 682e29a535
3 changed files with 66 additions and 33 deletions

View File

@@ -277,6 +277,19 @@ Description
* ``"windows"``: ``". "``
extractor.*.path-convert
------------------------
Type
`Conversion(s)`_
Example
* ``"g"``
* ``"Wl"``
Description
`Conversion(s)`_ to apply to each path segment after
`path-restrict <extractor.*.path-restrict_>`__
replacements.
extractor.*.path-extended
-------------------------
Type
@@ -8674,6 +8687,7 @@ Description
.. _download archive: `extractor.*.archive`_
.. _Action(s): Action_
.. _Conversion(s): https://gdl-org.github.io/docs/formatting.html#conversions
.. _.netrc: https://stackoverflow.com/tags/.netrc/info
.. _Last-Modified: https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.29
.. _datetime: https://docs.python.org/3/library/datetime.html#datetime-objects

View File

@@ -63,6 +63,7 @@
"path-replace" : "_",
"path-remove" : "\\u0000-\\u001f\\u007f",
"path-strip" : "auto",
"path-convert" : null,
"path-extended": true,
"metadata-extractor": null,

View File

@@ -90,6 +90,7 @@ class PathFormat():
restrict = config("path-restrict", "auto")
replace = config("path-replace", "_")
conv = config("path-convert")
if restrict == "auto":
restrict = "\\\\|/<>:\"?*" if WINDOWS else "/"
elif restrict == "unix":
@@ -100,10 +101,10 @@ class PathFormat():
restrict = "^0-9A-Za-z_."
elif restrict == "ascii+":
restrict = "^0-9@-[\\]-{ #-)+-.;=!}~"
self.clean_segment = self._build_cleanfunc(restrict, replace)
self.clean_segment = _build_cleanfunc(restrict, replace, conv)
remove = config("path-remove", "\x00-\x1f\x7f")
self.clean_path = self._build_cleanfunc(remove, "")
self.clean_path = _build_cleanfunc(remove, "")
strip = config("path-strip", "auto")
if strip == "auto":
@@ -133,37 +134,6 @@ class PathFormat():
basedir = self.clean_path(basedir)
self.basedirectory = basedir
def _build_cleanfunc(self, chars, repl):
if not chars:
return util.identity
elif isinstance(chars, dict):
if 0 not in chars:
chars = self._process_repl_dict(chars)
chars[0] = None
def func(x, table=str.maketrans(chars)):
return x.translate(table)
elif len(chars) == 1:
def func(x, c=chars, r=repl):
return x.replace(c, r)
else:
return functools.partial(util.re(f"[{chars}]").sub, repl)
return func
def _process_repl_dict(self, chars):
# can't modify 'chars' while *directly* iterating over its keys
for char in [c for c in chars if len(c) > 1]:
if len(char) == 3 and char[1] == "-":
citer = range(ord(char[0]), ord(char[2])+1)
else:
citer = char
repl = chars.pop(char)
for c in citer:
chars[c] = repl
return chars
def open(self, mode="wb"):
"""Open file and return a corresponding file object"""
try:
@@ -382,3 +352,51 @@ class PathFormat():
break
self.set_mtime()
def _build_convertfunc(func, conv):
if len(conv) <= 1:
conv = formatter._CONVERSIONS[conv]
return lambda x: conv(func(x))
def convert_many(x):
x = func(x)
for conv in convs:
x = conv(x)
return x
convs = [formatter._CONVERSIONS[c] for c in conv]
return convert_many
def _build_cleanfunc(chars, repl, conv=None):
if not chars:
func = util.identity
elif isinstance(chars, dict):
if 0 not in chars:
chars = _process_repl_dict(chars)
chars[0] = None
def func(x):
return x.translate(table)
table = str.maketrans(chars)
elif len(chars) == 1:
def func(x):
return x.replace(chars, repl)
else:
func = functools.partial(util.re(f"[{chars}]").sub, repl)
return _build_convertfunc(func, conv) if conv else func
def _process_repl_dict(chars):
# can't modify 'chars' while *directly* iterating over its keys
for char in [c for c in chars if len(c) > 1]:
if len(char) == 3 and char[1] == "-":
citer = range(ord(char[0]), ord(char[2])+1)
else:
citer = char
repl = chars.pop(char)
for c in citer:
chars[c] = repl
return chars