From 84cd611f89341915b3e3b9e908c3a157013cf172 Mon Sep 17 00:00:00 2001 From: Jesse Bannon Date: Fri, 24 Mar 2023 11:15:54 -0700 Subject: [PATCH] [BUGFIX] File name too long with -thumb.jpg, upgrade docker alpine image (#560) --- docker/Dockerfile | 7 ++-- setup.cfg | 1 + src/ytdl_sub/__init__.py | 4 +- .../validators/file_path_validators.py | 21 +++++++--- .../validators/test_file_path_validators.py | 39 ++++++++++++++++--- 5 files changed, 55 insertions(+), 17 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 521775ec..63d2ce19 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM ghcr.io/linuxserver/baseimage-alpine:3.17 +FROM ghcr.io/linuxserver/baseimage-alpine:edge ############################################################################### # YTDL-SUB INSTALL @@ -12,7 +12,7 @@ RUN mkdir -p /config && \ g++ \ nano \ make \ - python3=~3.10 \ + "python3>=3.10" \ py3-pip \ fontconfig \ py3-setuptools && \ @@ -30,13 +30,14 @@ RUN mkdir -p /config && \ cd -; \ fi && \ # Install ytdl-sub, ensure it is installed properly - pip install --no-cache-dir ytdl_sub-*.whl && \ + python3 -m pip install --no-cache-dir ytdl_sub-*.whl && \ ytdl-sub -h && \ # Delete unneeded packages after install rm ytdl_sub-*.whl && \ apk del \ g++ \ make \ + py3-pip \ py3-setuptools ############################################################################### diff --git a/setup.cfg b/setup.cfg index 218ca950..2abb1bce 100644 --- a/setup.cfg +++ b/setup.cfg @@ -15,6 +15,7 @@ classifiers = License :: Public Domain Environment :: Console Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 [options.entry_points] console_scripts = diff --git a/src/ytdl_sub/__init__.py b/src/ytdl_sub/__init__.py index 2bf1fbf3..94aae306 100644 --- a/src/ytdl_sub/__init__.py +++ b/src/ytdl_sub/__init__.py @@ -1,2 +1,2 @@ -__pypi_version__ = "2023.03.15" -__local_version__ = "2023.03.15+e69933d" +__pypi_version__ = "2023.03.24.post7" +__local_version__ = "2023.03.24+14e4a4b" diff --git a/src/ytdl_sub/validators/file_path_validators.py b/src/ytdl_sub/validators/file_path_validators.py index 2c6b65cd..617328a5 100644 --- a/src/ytdl_sub/validators/file_path_validators.py +++ b/src/ytdl_sub/validators/file_path_validators.py @@ -16,6 +16,9 @@ if IS_WINDOWS: else: _MAX_FILE_NAME_BYTES = os.pathconf("/", "PC_NAME_MAX") +# Save file-name bytes for the -thumb.jpg portion +_MAX_BASE_FILE_NAME_BYTES = _MAX_FILE_NAME_BYTES - len("-thumb.jpg".encode("utf-8")) - 8 + class FFmpegFileValidator(StringValidator): _expected_value_type_name = "ffmpeg dependency" @@ -90,17 +93,24 @@ class StringFormatterFileNameValidator(StringFormatterValidator, FilePathValidat @classmethod def _get_extension_split(cls, file_name: str) -> Tuple[str, str]: + """ + Returns + ------- + file_name, ext (including .) + """ if file_name.endswith(".info.json"): - ext = "info.json" + ext = ".info.json" + elif file_name.endswith("-thumb.jpg"): + ext = "-thumb.jpg" elif any(file_name.endswith(f".{subtitle_ext}") for subtitle_ext in SUBTITLE_EXTENSIONS): file_name_split = file_name.split(".") ext = file_name_split[-1] # Try to capture .lang.ext if len(file_name_split) > 2 and len(file_name_split[-2]) < 6: - ext = f"{file_name_split[-2]}.{file_name_split[-1]}" + ext = f".{file_name_split[-2]}.{file_name_split[-1]}" else: - ext = file_name.rsplit(".", maxsplit=1)[-1] + ext = f".{file_name.rsplit('.', maxsplit=1)[-1]}" return file_name[: -len(ext)], ext @@ -108,11 +118,10 @@ class StringFormatterFileNameValidator(StringFormatterValidator, FilePathValidat def _truncate_file_name(cls, file_name: str) -> str: file_sub_name, file_ext = cls._get_extension_split(file_name) - desired_size = _MAX_FILE_NAME_BYTES - len(file_ext.encode("utf-8")) - 1 - while len(file_sub_name.encode("utf-8")) > desired_size: + while len(file_sub_name.encode("utf-8")) > _MAX_BASE_FILE_NAME_BYTES: file_sub_name = file_sub_name[:-1] - return f"{file_sub_name}.{file_ext}" + return f"{file_sub_name}{file_ext}" def apply_formatter(self, variable_dict: Dict[str, str]) -> str: """Turn into a Path, then a string, to get correct directory separators""" diff --git a/tests/unit/validators/test_file_path_validators.py b/tests/unit/validators/test_file_path_validators.py index de0061f4..a27314e6 100644 --- a/tests/unit/validators/test_file_path_validators.py +++ b/tests/unit/validators/test_file_path_validators.py @@ -10,18 +10,16 @@ from ytdl_sub.validators.file_path_validators import StringFormatterFileNameVali class TestStringFormatterFilePathValidator: @pytest.mark.parametrize( "ext", - [ - "mp4", - "info.json", - ] - + [f"en-US.{ext}" for ext in SUBTITLE_EXTENSIONS], + ["mp4", "info.json", "-thumb.jpg"] + [f"en-US.{ext}" for ext in SUBTITLE_EXTENSIONS], ) @pytest.mark.parametrize("file_name_char", ["a", "𒃀"]) @pytest.mark.parametrize("file_name_len", [10, 10000]) def test_truncates_file_name_successfully( self, ext: str, file_name_char: str, file_name_len: int ): - ext = f".{ext}" # pytest args with . in the beginning act weird + if "thumb" not in ext: # do not put . in front of -thumb + ext = f".{ext}" # pytest args with . in the beginning act weird + with tempfile.TemporaryDirectory() as temp_dir: file_name = (file_name_char * file_name_len) + ext file_path = str(Path(temp_dir) / file_name) @@ -40,3 +38,32 @@ class TestStringFormatterFilePathValidator: assert len(dir_paths) == 1 assert Path(truncated_file_path) == dir_paths[0] + + @pytest.mark.parametrize( + "ext", + ["mp4", "info.json", "-thumb.jpg"] + [f"en-US.{ext}" for ext in SUBTITLE_EXTENSIONS], + ) + def test_truncates_file_names_successfully(self, ext: str): + if "thumb" not in ext: # do not put . in front of -thumb + ext = f".{ext}" # pytest args with . in the beginning act weird + + base_file_name = "s2023.e031701 - 𝗪𝗔𝗥𝗡𝗜𝗡𝗚: LG Secretly Overhaul This OLED Feature on C3 & G3… Should You Buy C2 Instead?" + + with tempfile.TemporaryDirectory() as temp_dir: + file_path = str(Path(temp_dir) / f"{base_file_name}{ext}") + + formatter = StringFormatterFileNameValidator(name="test", value=str(file_path)) + truncated_file_path = formatter.apply_formatter({}) + + assert truncated_file_path == str( + Path(temp_dir) + / f"s2023.e031701 - 𝗪𝗔𝗥𝗡𝗜𝗡𝗚: LG Secretly Overhaul This OLED Feature on C3 & G3… Should You Buy C2 Instead?{ext}" + ) + + # Ensure it can actually open the file + with open(truncated_file_path, "w", encoding="utf-8"): + # Make sure the file is actually in the directory + dir_paths = list(Path(temp_dir).rglob("*")) + + assert len(dir_paths) == 1 + assert Path(truncated_file_path) == dir_paths[0]