mirror of https://github.com/streamlink/streamlink
cli.utils.formatter: respect max file name length
This commit is contained in:
parent
54803f5a80
commit
eecf60935a
|
@ -2,17 +2,18 @@ from pathlib import Path
|
|||
from typing import Optional
|
||||
|
||||
from streamlink.utils.formatter import Formatter as _BaseFormatter
|
||||
from streamlink_cli.utils.path import replace_chars, replace_path
|
||||
from streamlink_cli.utils.path import replace_chars, replace_path, truncate_path
|
||||
|
||||
|
||||
class Formatter(_BaseFormatter):
|
||||
title = _BaseFormatter.format
|
||||
|
||||
def path(self, pathlike: str, charmap: Optional[str] = None) -> Path:
|
||||
def path(self, pathlike: str, charmap: Optional[str] = None, max_filename_length: int = 255) -> Path:
|
||||
def mapper(s):
|
||||
return replace_chars(s, charmap)
|
||||
|
||||
def format_part(part):
|
||||
return self._format(part, mapper, {})
|
||||
def format_part(part: str, isfile: bool) -> str:
|
||||
formatted = self._format(part, mapper, {})
|
||||
return truncate_path(formatted, max_filename_length, keep_extension=isfile)
|
||||
|
||||
return replace_path(pathlike, format_part)
|
||||
|
|
|
@ -57,9 +57,12 @@ def truncate_path(path: str, length: int = 255, keep_extension: bool = True) ->
|
|||
return f"{decoded}.{parts[1]}"
|
||||
|
||||
|
||||
def replace_path(pathlike: Union[str, Path], mapper: Callable[[str], str]) -> Path:
|
||||
def get_part(part):
|
||||
newpart = mapper(part)
|
||||
def replace_path(pathlike: Union[str, Path], mapper: Callable[[str, bool], str]) -> Path:
|
||||
def get_part(part: str, isfile: bool) -> str:
|
||||
newpart = mapper(part, isfile)
|
||||
return REPLACEMENT if part != newpart and newpart in SPECIAL_PATH_PARTS else newpart
|
||||
|
||||
return Path(*(get_part(part) for part in Path(pathlike).expanduser().parts))
|
||||
parts = Path(pathlike).expanduser().parts
|
||||
last = len(parts) - 1
|
||||
|
||||
return Path(*(get_part(part, i == last) for i, part in enumerate(parts)))
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from datetime import datetime
|
||||
from os.path import sep
|
||||
from pathlib import Path
|
||||
from string import ascii_letters as alphabet
|
||||
from unittest.mock import Mock, call, patch
|
||||
|
||||
import pytest
|
||||
|
@ -79,3 +80,19 @@ class TestCLIFormatter:
|
|||
path = formatter.path(f"{{current}}{sep}{{parent}}{sep}{{dots}}{sep}{{separator}}{sep}foo{sep}.{sep}..{sep}bar")
|
||||
assert path == Path("_", "_", "...", "_", "foo", ".", "..", "bar"), \
|
||||
"Formats the path's parts separately and ignores current and parent directories in substitutions only"
|
||||
|
||||
def test_path_truncation_ascii(self, formatter: Formatter):
|
||||
formatter.mapping.update({
|
||||
"dir": lambda: alphabet * 10,
|
||||
"file": lambda: alphabet * 10,
|
||||
})
|
||||
path = formatter.path(f"{{dir}}.fakeext{sep}{{file}}.ext")
|
||||
assert path == Path((alphabet * 10)[:255], f"{(alphabet * 10)[:251]}.ext")
|
||||
|
||||
def test_path_truncation_unicode(self, formatter: Formatter):
|
||||
formatter.mapping.update({
|
||||
"dir": lambda: "🐻" * 512,
|
||||
"file": lambda: "🐻" * 512,
|
||||
})
|
||||
path = formatter.path(f"{{dir}}.fakeext{sep}{{file}}.ext")
|
||||
assert path == Path("🐻" * 63, f"{'🐻' * 62}.ext")
|
||||
|
|
|
@ -59,7 +59,7 @@ def test_replace_chars_replacement():
|
|||
|
||||
|
||||
def test_replace_path():
|
||||
def mapper(s):
|
||||
def mapper(s, *_):
|
||||
return dict(foo=".", bar="..").get(s, s)
|
||||
|
||||
path = Path("foo", ".", "bar", "..", "baz")
|
||||
|
@ -70,15 +70,15 @@ def test_replace_path():
|
|||
@pytest.mark.posix_only()
|
||||
@pytest.mark.parametrize("os_environ", [pytest.param({"HOME": "/home/foo"}, id="posix")], indirect=True)
|
||||
def test_replace_path_expanduser_posix(os_environ):
|
||||
assert replace_path("~/bar", lambda s: s) == Path("/home/foo/bar")
|
||||
assert replace_path("foo/bar", lambda s: dict(foo="~").get(s, s)) == Path("~/bar")
|
||||
assert replace_path("~/bar", lambda s, *_: s) == Path("/home/foo/bar")
|
||||
assert replace_path("foo/bar", lambda s, *_: dict(foo="~").get(s, s)) == Path("~/bar")
|
||||
|
||||
|
||||
@pytest.mark.windows_only()
|
||||
@pytest.mark.parametrize("os_environ", [pytest.param({"USERPROFILE": "C:\\Users\\foo"}, id="windows")], indirect=True)
|
||||
def test_replace_path_expanduser_windows(os_environ):
|
||||
assert replace_path("~\\bar", lambda s: s) == Path("C:\\Users\\foo\\bar")
|
||||
assert replace_path("foo\\bar", lambda s: dict(foo="~").get(s, s)) == Path("~\\bar")
|
||||
assert replace_path("~\\bar", lambda s, *_: s) == Path("C:\\Users\\foo\\bar")
|
||||
assert replace_path("foo\\bar", lambda s, *_: dict(foo="~").get(s, s)) == Path("~\\bar")
|
||||
|
||||
|
||||
text = alphabet * 10
|
||||
|
|
Loading…
Reference in New Issue