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.
This commit is contained in:
@@ -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>{level}</level>|<green>{file}:{line}:{function}</green>| {message}"
|
||||
)
|
||||
def setup_logger() -> None:
|
||||
"""Configure the logger."""
|
||||
logger.remove()
|
||||
logger.add(
|
||||
sys.stdout,
|
||||
colorize=True,
|
||||
format="<level>{level}</level>|<green>{file}:{line}:{function}</green>| {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",
|
||||
|
||||
Reference in New Issue
Block a user