import io import logging import sys from pathlib import Path from typing import Optional from PIL import Image logger = logging.getLogger(__name__) DEBUG_PREFIX = "[image-compression]" def _load_imgcompress(): try: from imgcompress import ( # type: ignore CompressionMode, CompressionSettings, ImageCompressor, ) print(f"{DEBUG_PREFIX} imported imgcompress from installed environment") return ImageCompressor, CompressionSettings, CompressionMode except ImportError as exc: print(f"{DEBUG_PREFIX} installed import failed: {exc}") repo_root = Path(__file__).resolve().parents[2] src_path = repo_root / "image-compressor" / "src" if src_path.exists(): sys.path.insert(0, str(src_path)) print(f"{DEBUG_PREFIX} trying local source path: {src_path}") from imgcompress import ( # type: ignore CompressionMode, CompressionSettings, ImageCompressor, ) print(f"{DEBUG_PREFIX} imported imgcompress from local source") return ImageCompressor, CompressionSettings, CompressionMode raise def is_compressible_image(filename: str, content_type: Optional[str] = None) -> bool: name = (filename or "").lower() ctype = (content_type or "").lower() if name.endswith(".gif") or name.endswith(".svg"): return False if ctype in {"image/gif", "image/svg+xml"}: return False compressible_exts = (".jpg", ".jpeg", ".png", ".webp", ".bmp", ".tif", ".tiff", ".avif") return name.endswith(compressible_exts) or ctype.startswith("image/") def compress_image_bytes(data: bytes, filename: str) -> bytes: if not data: return data print(f"{DEBUG_PREFIX} compress_image_bytes start file={filename} size={len(data)}") ImageCompressor, CompressionSettings, CompressionMode = _load_imgcompress() settings = CompressionSettings( mode=CompressionMode.VISUALLY_LOSSLESS, allow_format_switch=False, strip_metadata=False, png_use_zopfli=False, ) compressor = ImageCompressor(settings=settings) result = compressor.compress_bytes(data, Path(filename)) final_bytes = result.payload or data print( f"{DEBUG_PREFIX} compress_image_bytes done file={filename} " f"original={len(data)} final={len(final_bytes)} skipped={getattr(result, 'skipped', False)} " f"note={getattr(result, 'note', '')}" ) return final_bytes def maybe_compress_uploaded_file(file_obj): filename = getattr(file_obj, "name", "upload") content_type = getattr(file_obj, "content_type", None) print( f"{DEBUG_PREFIX} received upload file={filename} " f"content_type={content_type} size={getattr(file_obj, 'size', 'unknown')}" ) if not is_compressible_image(filename, content_type): print(f"{DEBUG_PREFIX} skip non-compressible file={filename}") if hasattr(file_obj, "seek"): file_obj.seek(0) return None try: raw_bytes = file_obj.read() if hasattr(file_obj, "seek"): file_obj.seek(0) if not raw_bytes: print(f"{DEBUG_PREFIX} skip empty payload file={filename}") return None with Image.open(io.BytesIO(raw_bytes)) as img: img.verify() compressed = compress_image_bytes(raw_bytes, filename) if compressed and len(compressed) <= len(raw_bytes): print( f"{DEBUG_PREFIX} accepted file={filename} " f"original={len(raw_bytes)} compressed={len(compressed)}" ) return compressed print( f"{DEBUG_PREFIX} keeping original file={filename} " f"original={len(raw_bytes)} candidate={len(compressed) if compressed else 'none'}" ) return raw_bytes except Exception as exc: print(f"{DEBUG_PREFIX} failed file={filename} error={exc}") logger.warning("Image compression skipped for %s: %s", filename, exc) if hasattr(file_obj, "seek"): file_obj.seek(0) return None