import io from pathlib import Path from PIL import Image, ImageOps from .base import ImageAttachment INLINE_IMAGE_MAX_BYTES = 2_000_000 PREVIEW_TARGET_BYTES = 1_900_000 WECOM_FILE_MAX_BYTES = 20_000_000 TELEGRAM_PHOTO_MAX_BYTES = 10_000_000 TELEGRAM_FILE_MAX_BYTES = 50_000_000 def compressed_preview(image: ImageAttachment) -> ImageAttachment: if len(image.data) <= INLINE_IMAGE_MAX_BYTES: return image with Image.open(io.BytesIO(image.data)) as source: source.load() working = ImageOps.exif_transpose(source) if working.mode in {"RGBA", "LA"}: background = Image.new("RGB", working.size, "white") alpha = working.getchannel("A") background.paste(working.convert("RGB"), mask=alpha) working = background elif working.mode != "RGB": working = working.convert("RGB") current = working while True: for quality in (90, 82, 74, 66, 58, 50, 42, 34): output = io.BytesIO() current.save( output, format="JPEG", quality=quality, optimize=True, progressive=True, ) data = output.getvalue() if len(data) <= PREVIEW_TARGET_BYTES: name = f"{Path(image.filename).stem}-preview.jpg" return ImageAttachment(name, "image/jpeg", data) width, height = current.size if min(width, height) <= 128: raise RuntimeError("image cannot be compressed below preview limit") current = current.resize( (max(1, int(width * 0.8)), max(1, int(height * 0.8))), Image.Resampling.LANCZOS, )