Decided to keep python based, added S3 support
This commit is contained in:
parent
c7586d1aa1
commit
c752138fb0
9 changed files with 293 additions and 37 deletions
95
image_processor.py
Normal file
95
image_processor.py
Normal file
|
@ -0,0 +1,95 @@
|
|||
import os
|
||||
from PIL import Image
|
||||
from wand.image import Image as WandImage
|
||||
import io
|
||||
from typing import BinaryIO, Tuple, Optional
|
||||
|
||||
class ImageProcessor:
|
||||
@staticmethod
|
||||
def is_animated_gif(file_storage) -> bool:
|
||||
"""Check if the image is an animated GIF"""
|
||||
try:
|
||||
# Save current position
|
||||
pos = file_storage.tell()
|
||||
# Go to beginning
|
||||
file_storage.seek(0)
|
||||
|
||||
with Image.open(file_storage) as img:
|
||||
try:
|
||||
img.seek(1) # Try to move to the second frame
|
||||
is_animated = True
|
||||
except EOFError:
|
||||
is_animated = False
|
||||
|
||||
# Restore position
|
||||
file_storage.seek(pos)
|
||||
return is_animated
|
||||
except Exception:
|
||||
# Restore position in case of error
|
||||
file_storage.seek(pos)
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def convert_to_webp(file_storage, quality: int = 90) -> Tuple[BinaryIO, str]:
|
||||
"""
|
||||
Convert an image to WebP format.
|
||||
Returns a tuple of (file_object, extension)
|
||||
"""
|
||||
# Save current position
|
||||
pos = file_storage.tell()
|
||||
# Go to beginning
|
||||
file_storage.seek(0)
|
||||
|
||||
try:
|
||||
# Check if it's an animated GIF
|
||||
if ImageProcessor.is_animated_gif(file_storage):
|
||||
# Convert animated GIF to animated WebP
|
||||
file_storage.seek(0)
|
||||
with WandImage(file=file_storage) as img:
|
||||
# Configure WebP animation settings
|
||||
img.format = 'WEBP'
|
||||
|
||||
# Higher quality settings for animation
|
||||
img.options['webp:lossless'] = 'true' # Use lossless for animations
|
||||
img.options['webp:method'] = '6' # Best compression method
|
||||
img.options['webp:image-hint'] = 'graph' # Better for animations
|
||||
img.options['webp:minimize-size'] = 'false' # Prioritize quality
|
||||
|
||||
# Animation specific settings
|
||||
img.options['webp:animation-type'] = 'default'
|
||||
img.options['webp:loop'] = '0' # Infinite loop
|
||||
|
||||
# Save with high quality
|
||||
webp_bytes = io.BytesIO(img.make_blob(format='webp'))
|
||||
webp_bytes.seek(0)
|
||||
return webp_bytes, '.webp'
|
||||
else:
|
||||
# Handle static images
|
||||
file_storage.seek(0)
|
||||
with Image.open(file_storage) as img:
|
||||
# Convert RGBA to RGB if necessary
|
||||
if img.mode in ('RGBA', 'LA'):
|
||||
background = Image.new('RGB', img.size, (255, 255, 255))
|
||||
background.paste(img, mask=img.getchannel('A'))
|
||||
img = background
|
||||
elif img.mode != 'RGB':
|
||||
img = img.convert('RGB')
|
||||
|
||||
# Save as WebP with high quality
|
||||
output = io.BytesIO()
|
||||
img.save(output,
|
||||
format='WEBP',
|
||||
quality=quality, # Higher quality
|
||||
method=6, # Best compression method
|
||||
lossless=False, # Use lossy for static images
|
||||
exact=True) # Preserve color exactness
|
||||
output.seek(0)
|
||||
return output, '.webp'
|
||||
finally:
|
||||
# Restore original position
|
||||
file_storage.seek(pos)
|
||||
|
||||
@staticmethod
|
||||
def process_featured_image(file_storage) -> Tuple[BinaryIO, str]:
|
||||
"""Process featured image, converting to WebP format"""
|
||||
return ImageProcessor.convert_to_webp(file_storage, quality=90)
|
Loading…
Add table
Add a link
Reference in a new issue