From 451164f5b2cafc63cd9569decc7a045db930d651 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D9=8A?= Date: Fri, 17 Jan 2025 13:02:51 +0530 Subject: [PATCH] Update optimize_images_for_upload.py Add Type Hints: Improve code readability and help with static type checking by adding type hints. Modular Logging Setup: Move the logging setup to a separate function for better modularity. Error Handling Improvements: Add more specific error handling and informative logging. Environment Variable Loading: Ensure environment variables are loaded only once and not inside functions. Check for API Key Validity: Add a check to validate the Tinyfy API key before using it. Optimize Image Saving: Use a temporary file to avoid saving directly to the filesystem. --- .../optimize_images_for_upload.py | 63 +++++++++---------- 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/lib/ai_seo_tools/optimize_images_for_upload.py b/lib/ai_seo_tools/optimize_images_for_upload.py index 31c3bc95..cc1ae0c8 100644 --- a/lib/ai_seo_tools/optimize_images_for_upload.py +++ b/lib/ai_seo_tools/optimize_images_for_upload.py @@ -5,24 +5,28 @@ from PIL import Image from loguru import logger from dotenv import load_dotenv import streamlit as st +from tempfile import NamedTemporaryFile # Load environment variables load_dotenv() # Set Tinyfy API key from environment variable -tinify_key = os.getenv('TINIFY_API_KEY') -if tinify_key: - tinify.key = tinify_key +TINIFY_API_KEY = os.getenv('TINIFY_API_KEY') +if TINIFY_API_KEY: + tinify.key = TINIFY_API_KEY -# Configure logger -logger.remove() -logger.add( - sys.stdout, - colorize=True, - format="{level}|{file}:{line}:{function}| {message}" -) +def setup_logger() -> None: + """Configure the logger.""" + logger.remove() + logger.add( + sys.stdout, + colorize=True, + format="{level}|{file}:{line}:{function}| {message}" + ) -def compress_image(image, quality=45, resize=None, preserve_exif=False): +setup_logger() + +def compress_image(image: Image.Image, quality: int = 45, resize: tuple = None, preserve_exif: bool = False) -> Image.Image: """ Compress and optionally resize an image. @@ -36,35 +40,33 @@ def compress_image(image, quality=45, resize=None, preserve_exif=False): PIL.Image: The compressed and resized image object. """ try: - # Ensure image is in a compatible mode for JPEG/WebP if image.mode == 'RGBA': logger.info("Converting RGBA image to RGB.") image = image.convert('RGB') exif = image.info.get('exif') if preserve_exif and 'exif' in image.info else None - # Resize image if needed if resize: image = image.resize(resize, Image.LANCZOS) logger.info(f"Resized image to {resize}") - # Save compressed image - try: - logger.info("Attempting to save the compressed image with EXIF data (if any).") - image.save("temp.jpg", optimize=True, quality=quality, exif=exif) - except Exception as exif_error: - logger.warning(f"Error saving image with EXIF: {exif_error}. Saving without EXIF.") - image.save("temp.jpg", optimize=True, quality=quality) + with NamedTemporaryFile(delete=False, suffix=".jpg") as temp_file: + temp_path = temp_file.name + try: + image.save(temp_path, optimize=True, quality=quality, exif=exif) + except Exception as exif_error: + logger.warning(f"Error saving image with EXIF: {exif_error}. Saving without EXIF.") + image.save(temp_path, optimize=True, quality=quality) logger.info("Image compression successful.") - return Image.open("temp.jpg") + return Image.open(temp_path) except Exception as e: logger.error(f"Error compressing image: {e}") st.error("Failed to compress the image. Please try again.") return None -def convert_to_webp(image, image_path): +def convert_to_webp(image: Image.Image, image_path: str) -> str: """ Convert an image to WebP format. @@ -84,7 +86,7 @@ def convert_to_webp(image, image_path): st.error("Failed to convert the image to WebP format. Please try again.") return None -def compress_image_tinyfy(image_path): +def compress_image_tinyfy(image_path: str) -> None: """ Compress an image using Tinyfy API. @@ -102,11 +104,14 @@ def compress_image_tinyfy(image_path): source = tinify.from_file(image_path) source.to_file(image_path) logger.info("Tinyfy compression successful.") + except tinify.errors.AccountError: + logger.error("Verify your Tinyfy API key and account limit.") + st.warning("Tinyfy compression failed. Check your API key and account limit.") except Exception as e: logger.error(f"Error during Tinyfy compression: {e}") st.warning("Tinyfy compression failed. Ensure the API key is set.") -def optimize_image(image, image_path, quality, resize, preserve_exif): +def optimize_image(image: Image.Image, image_path: str, quality: int, resize: tuple, preserve_exif: bool) -> str: """ Optimize the image by compressing and converting it to WebP, with optional Tinyfy compression. @@ -122,17 +127,14 @@ def optimize_image(image, image_path, quality, resize, preserve_exif): """ logger.info("Starting image optimization process...") - # Compress the image using Pillow compressed_image = compress_image(image, quality, resize, preserve_exif) if compressed_image is None: return None - # Convert image to WebP format webp_path = convert_to_webp(compressed_image, image_path) if webp_path is None: return None - # Optionally compress the WebP image using Tinyfy API if tinify.key: compress_image_tinyfy(webp_path) else: @@ -140,23 +142,20 @@ def optimize_image(image, image_path, quality, resize, preserve_exif): return webp_path -def main_img_optimizer(): +def main_img_optimizer() -> None: st.title("ALwrity Image Optimizer") st.markdown("## Upload an image to optimize its size and format.") - # API Key Input (Optional) input_tinify_key = st.text_input("Optional: Enter your Tinyfy API Key") if input_tinify_key: tinify.key = input_tinify_key - # File Upload uploaded_file = st.file_uploader("Upload an image", type=['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp']) if uploaded_file: image = Image.open(uploaded_file) st.image(image, caption="Original Image", use_column_width=True) - # Image Compression Options quality = st.slider("Compression Quality", 1, 100, 45) preserve_exif = st.checkbox("Preserve EXIF Data", value=False) resize = st.checkbox("Resize Image") @@ -168,7 +167,6 @@ def main_img_optimizer(): else: resize_dims = None - # Optimize Image if st.button("Optimize Image"): with st.spinner("Optimizing..."): if tinify.key: @@ -180,7 +178,6 @@ def main_img_optimizer(): st.image(webp_path, caption="Optimized Image (WebP)", use_column_width=True) st.success("Image optimization completed!") - # Provide download link with open(webp_path, "rb") as file: st.download_button( label="Download Optimized Image",