From 1133aeacebb46f5145b433f3d0c3b641d8ecf3b3 Mon Sep 17 00:00:00 2001 From: H1ghSyst3m <60105043+H1ghSyst3m@users.noreply.github.com> Date: Sun, 31 May 2026 17:30:09 +0200 Subject: [PATCH 1/2] fix: unify DIM package and support filename generation --- app.py | 12 ++------- naming_utils.py | 39 ++++++++++++++++++++++++++++ packaging_utils.py | 24 +++++++++--------- tests/test_naming_utils.py | 52 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+), 22 deletions(-) create mode 100644 naming_utils.py create mode 100644 tests/test_naming_utils.py diff --git a/app.py b/app.py index 62404ac..05cc6ed 100644 --- a/app.py +++ b/app.py @@ -5,7 +5,6 @@ import zipfile import stat import uuid -import re import patoolib import ctypes import shiboken6 @@ -54,6 +53,7 @@ FileExplorer, BuildListWidget ) from packaging_utils import PackagingWorker, PackageSpec, BatchPackagingWorker +from naming_utils import build_dim_zip_filename from extraction_utils import ( ContentExtractionWorker, MultiBuildExtractionWorker, classify_archives, detect_heuristic_ordering @@ -781,15 +781,7 @@ def build_zip_filename(self) -> str: part_val = self.product_part_input.value() name_raw = self.product_name_input.text() or "Package" - prefix_clean = re.sub(r'[^A-Za-z0-9]+', '', str(prefix_raw)).upper() or "IM" - try: - sku_formatted = f"{int(str(sku_raw)):08d}" - except ValueError: - sku_formatted = (str(sku_raw) or "").zfill(8) if sku_raw else "00000000" - part_str = f"{int(part_val):02d}" - sanitized_name = re.sub(r'[^A-Za-z0-9._-]+', '_', str(name_raw)).strip('_') or "Package" - - return f"{prefix_clean}{sku_formatted}-{part_str}_{sanitized_name}.zip" + return build_dim_zip_filename(prefix_raw, sku_raw, part_val, name_raw) def updateZipPreview(self): try: diff --git a/naming_utils.py b/naming_utils.py new file mode 100644 index 0000000..3f041f5 --- /dev/null +++ b/naming_utils.py @@ -0,0 +1,39 @@ +import re + + +def sanitize_dim_zip_product_name(product_name: str, fallback: str = "Package") -> str: + sanitized = re.sub(r'[^A-Za-z0-9]+', '', str(product_name)) + return sanitized or fallback + + +def sanitize_support_filename_segment(value: str, fallback: str = "") -> str: + sanitized = re.sub(r'[^A-Za-z0-9_-]+', '_', str(value)).strip('_') + return sanitized or fallback + + +def format_dim_sku(sku: str) -> str: + try: + return f"{int(str(sku)):08d}" + except ValueError: + return str(sku).zfill(8) + + +def build_dim_zip_filename( + prefix: str, + sku: str, + product_part: int, + product_name: str, + fallback_prefix: str = "IM", +) -> str: + prefix_clean = re.sub(r'[^A-Za-z0-9]+', '', str(prefix)).upper() or fallback_prefix + sku_formatted = format_dim_sku(sku) + part_str = f"{int(product_part):02d}" + name_segment = sanitize_dim_zip_product_name(product_name) + return f"{prefix_clean}{sku_formatted}-{part_str}_{name_segment}.zip" + + +def build_support_cover_filename(store: str, sku: str, product_name: str) -> str: + store_segment = sanitize_support_filename_segment(store) + product_segment = sanitize_support_filename_segment(product_name, "Package") + sku_segment = str(sku).strip() + return f"{store_segment}_{sku_segment}_{product_segment}.jpg" diff --git a/packaging_utils.py b/packaging_utils.py index 6a6958e..7223407 100644 --- a/packaging_utils.py +++ b/packaging_utils.py @@ -12,6 +12,7 @@ from typing import Callable, Dict, Optional, Any from logger_utils import get_logger +from naming_utils import build_dim_zip_filename, build_support_cover_filename from utils import calculate_total_size, find_7z_executable, suppress_cmd_window log = get_logger(__name__) @@ -133,9 +134,11 @@ def _process_and_paste_image(self) -> bool: self.log.info("Attempting to generate Product cover from: %s", self.spec.image_path) try: - sanitized_product_name = re.sub(r'[^A-Za-z0-9._-]+', '_', self.spec.product_name).strip('_') - store_formatted = re.sub(r'[^A-Za-z0-9._-]+', '_', self.spec.store).strip('_') - new_image_name = f"{store_formatted}_{self.spec.sku}_{sanitized_product_name}.jpg" + new_image_name = build_support_cover_filename( + self.spec.store, + self.spec.sku, + self.spec.product_name, + ) target_dir = os.path.join(self.spec.content_dir, "Runtime", "Support") os.makedirs(target_dir, exist_ok=True) @@ -196,15 +199,12 @@ def _create_supplement(self) -> bool: return False def _build_zip_name(self) -> str: - prefix_clean = re.sub(r'[^A-Za-z0-9]+', '', str(self.spec.prefix)).upper() - try: - sku_formatted = f"{int(str(self.spec.sku)):08d}" - except ValueError: - sku_formatted = str(self.spec.sku).zfill(8) - - part_str = f"{int(self.spec.product_part):02d}" - sanitized_name = re.sub(r'[^A-Za-z0-9._-]+', '_', str(self.spec.product_name)).strip('_') - return f"{prefix_clean}{sku_formatted}-{part_str}_{sanitized_name}.zip" + return build_dim_zip_filename( + self.spec.prefix, + self.spec.sku, + self.spec.product_part, + self.spec.product_name, + ) def _zip_package(self, progress_callback: Callable[[int], None]) -> bool: zip_name = self._build_zip_name() diff --git a/tests/test_naming_utils.py b/tests/test_naming_utils.py new file mode 100644 index 0000000..626e1b4 --- /dev/null +++ b/tests/test_naming_utils.py @@ -0,0 +1,52 @@ +import unittest + +from naming_utils import ( + build_dim_zip_filename, + build_support_cover_filename, + sanitize_dim_zip_product_name, + sanitize_support_filename_segment, +) + + +class NamingUtilsTests(unittest.TestCase): + def test_dim_zip_product_segment_allows_only_letters_and_digits(self): + self.assertEqual( + sanitize_dim_zip_product_name("X Fashion - Series_3 for G8-8.1 Females"), + "XFashionSeries3forG881Females", + ) + + def test_dim_zip_filename_pads_sku_and_part(self): + self.assertEqual( + build_dim_zip_filename("RE", "70127", 1, "Bull Boxers G8M G8.1M G9"), + "RE00070127-01_BullBoxersG8MG81MG9.zip", + ) + + def test_support_segment_replaces_dot_and_preserves_hyphen_and_underscore(self): + self.assertEqual( + sanitize_support_filename_segment("Bull_Boxers G8-8.1"), + "Bull_Boxers_G8-8_1", + ) + + def test_renderotica_support_cover_filename_matches_dim_support_basename(self): + self.assertEqual( + build_support_cover_filename( + "Renderotica", + "70127", + "Bull Boxers G8M G8.1M G9", + ), + "Renderotica_70127_Bull_Boxers_G8M_G8_1M_G9.jpg", + ) + + def test_daz_support_cover_filename_matches_observed_dim_support_basename(self): + self.assertEqual( + build_support_cover_filename( + "DAZ 3D", + "163838", + "X Fashion - Series 3 for G8-8.1 Females", + ), + "DAZ_3D_163838_X_Fashion_-_Series_3_for_G8-8_1_Females.jpg", + ) + + +if __name__ == "__main__": + unittest.main() From 399d02a9a847e07caa2b218bb83c592eeba98329 Mon Sep 17 00:00:00 2001 From: H1ghSyst3m <60105043+H1ghSyst3m@users.noreply.github.com> Date: Sun, 31 May 2026 17:41:03 +0200 Subject: [PATCH 2/2] docs: move naming fixes to unreleased changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fdc894..d09783e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Fixed +- DIM package ZIP names now use DIM-compatible product name segments so archives with spaces, underscores, hyphens, or dots in the product name are recognized correctly. +- Runtime/Support cover image names now normalize dots the same way DIM support metadata does, preventing missing Smart Content covers for products such as `G8.1`. + ## v2.0.1 ### Fixed