244 lines
6.6 KiB
Python
244 lines
6.6 KiB
Python
|
from django.core.files.base import ContentFile
|
||
|
from django.utils.text import slugify
|
||
|
from ..models.images import ImageLibrary
|
||
|
import os
|
||
|
|
||
|
try:
|
||
|
import requests
|
||
|
except ImportError:
|
||
|
requests = None
|
||
|
from PIL import Image as PILImage
|
||
|
|
||
|
|
||
|
def create_image_from_file(
|
||
|
file_path, name, description="", alt_text="", category="other", tags=""
|
||
|
):
|
||
|
"""
|
||
|
Create an ImageLibrary entry from a local file.
|
||
|
|
||
|
Args:
|
||
|
file_path: Path to the image file
|
||
|
name: Name for the image
|
||
|
description: Optional description
|
||
|
alt_text: Alternative text for accessibility
|
||
|
category: Image category
|
||
|
tags: Comma-separated tags
|
||
|
|
||
|
Returns:
|
||
|
ImageLibrary instance or None if failed
|
||
|
"""
|
||
|
try:
|
||
|
if not os.path.exists(file_path):
|
||
|
print(f"File not found: {file_path}")
|
||
|
return None
|
||
|
|
||
|
# Generate slug
|
||
|
slug = slugify(name)
|
||
|
|
||
|
# Check if image already exists
|
||
|
if ImageLibrary.objects.filter(slug=slug).exists():
|
||
|
print(f"Image with slug '{slug}' already exists")
|
||
|
return ImageLibrary.objects.get(slug=slug)
|
||
|
|
||
|
# Create image library entry
|
||
|
image_lib = ImageLibrary(
|
||
|
name=name,
|
||
|
slug=slug,
|
||
|
description=description,
|
||
|
alt_text=alt_text or name,
|
||
|
category=category,
|
||
|
tags=tags,
|
||
|
)
|
||
|
|
||
|
# Read and save the image file
|
||
|
with open(file_path, "rb") as f:
|
||
|
image_lib.image.save(
|
||
|
os.path.basename(file_path), ContentFile(f.read()), save=True
|
||
|
)
|
||
|
|
||
|
print(f"Created image library entry: {name}")
|
||
|
return image_lib
|
||
|
|
||
|
except Exception as e:
|
||
|
print(f"Error creating image library entry: {e}")
|
||
|
return None
|
||
|
|
||
|
|
||
|
def create_image_from_url(
|
||
|
url, name, description="", alt_text="", category="other", tags=""
|
||
|
):
|
||
|
"""
|
||
|
Create an ImageLibrary entry from a URL.
|
||
|
|
||
|
Args:
|
||
|
url: URL to the image
|
||
|
name: Name for the image
|
||
|
description: Optional description
|
||
|
alt_text: Alternative text for accessibility
|
||
|
category: Image category
|
||
|
tags: Comma-separated tags
|
||
|
|
||
|
Returns:
|
||
|
ImageLibrary instance or None if failed
|
||
|
"""
|
||
|
if requests is None:
|
||
|
print("requests library is not installed. Cannot download from URL.")
|
||
|
return None
|
||
|
|
||
|
try:
|
||
|
# Generate slug
|
||
|
slug = slugify(name)
|
||
|
|
||
|
# Check if image already exists
|
||
|
if ImageLibrary.objects.filter(slug=slug).exists():
|
||
|
print(f"Image with slug '{slug}' already exists")
|
||
|
return ImageLibrary.objects.get(slug=slug)
|
||
|
|
||
|
# Download the image
|
||
|
response = requests.get(url)
|
||
|
response.raise_for_status()
|
||
|
|
||
|
# Create image library entry
|
||
|
image_lib = ImageLibrary(
|
||
|
name=name,
|
||
|
slug=slug,
|
||
|
description=description,
|
||
|
alt_text=alt_text or name,
|
||
|
category=category,
|
||
|
tags=tags,
|
||
|
)
|
||
|
|
||
|
# Save the image
|
||
|
filename = url.split("/")[-1]
|
||
|
if "?" in filename:
|
||
|
filename = filename.split("?")[0]
|
||
|
|
||
|
image_lib.image.save(filename, ContentFile(response.content), save=True)
|
||
|
|
||
|
print(f"Created image library entry from URL: {name}")
|
||
|
return image_lib
|
||
|
|
||
|
except Exception as e:
|
||
|
print(f"Error creating image library entry from URL: {e}")
|
||
|
return None
|
||
|
|
||
|
|
||
|
def get_image_by_slug(slug):
|
||
|
"""
|
||
|
Get an image from the library by slug.
|
||
|
|
||
|
Args:
|
||
|
slug: Slug of the image
|
||
|
|
||
|
Returns:
|
||
|
ImageLibrary instance or None if not found
|
||
|
"""
|
||
|
try:
|
||
|
return ImageLibrary.objects.get(slug=slug)
|
||
|
except ImageLibrary.DoesNotExist:
|
||
|
return None
|
||
|
|
||
|
|
||
|
def get_images_by_category(category):
|
||
|
"""
|
||
|
Get all images from a specific category.
|
||
|
|
||
|
Args:
|
||
|
category: Category name
|
||
|
|
||
|
Returns:
|
||
|
QuerySet of ImageLibrary instances
|
||
|
"""
|
||
|
return ImageLibrary.objects.filter(category=category)
|
||
|
|
||
|
|
||
|
def get_images_by_tags(tags):
|
||
|
"""
|
||
|
Get images that contain any of the specified tags.
|
||
|
|
||
|
Args:
|
||
|
tags: List of tags or comma-separated string
|
||
|
|
||
|
Returns:
|
||
|
QuerySet of ImageLibrary instances
|
||
|
"""
|
||
|
if isinstance(tags, str):
|
||
|
tags = [tag.strip() for tag in tags.split(",")]
|
||
|
|
||
|
from django.db.models import Q
|
||
|
|
||
|
query = Q()
|
||
|
for tag in tags:
|
||
|
query |= Q(tags__icontains=tag)
|
||
|
|
||
|
return ImageLibrary.objects.filter(query).distinct()
|
||
|
|
||
|
|
||
|
def cleanup_unused_images():
|
||
|
"""
|
||
|
Find and optionally clean up unused images from the library.
|
||
|
|
||
|
Returns:
|
||
|
List of ImageLibrary instances with usage_count = 0
|
||
|
"""
|
||
|
unused_images = ImageLibrary.objects.filter(usage_count=0)
|
||
|
|
||
|
print(f"Found {unused_images.count()} unused images:")
|
||
|
for image in unused_images:
|
||
|
print(f" - {image.name} ({image.slug})")
|
||
|
|
||
|
return unused_images
|
||
|
|
||
|
|
||
|
def optimize_image(image_library_instance, max_width=1920, max_height=1080, quality=85):
|
||
|
"""
|
||
|
Optimize an image in the library by resizing and compressing.
|
||
|
|
||
|
Args:
|
||
|
image_library_instance: ImageLibrary instance
|
||
|
max_width: Maximum width in pixels
|
||
|
max_height: Maximum height in pixels
|
||
|
quality: JPEG quality (1-100)
|
||
|
|
||
|
Returns:
|
||
|
bool: True if optimization was successful
|
||
|
"""
|
||
|
try:
|
||
|
if not image_library_instance.image:
|
||
|
return False
|
||
|
|
||
|
# Open the image
|
||
|
with PILImage.open(image_library_instance.image.path) as img:
|
||
|
# Calculate new dimensions while maintaining aspect ratio
|
||
|
ratio = min(max_width / img.width, max_height / img.height)
|
||
|
|
||
|
if ratio < 1: # Only resize if image is larger than max dimensions
|
||
|
new_width = int(img.width * ratio)
|
||
|
new_height = int(img.height * ratio)
|
||
|
|
||
|
# Resize the image
|
||
|
img_resized = img.resize(
|
||
|
(new_width, new_height), PILImage.Resampling.LANCZOS
|
||
|
)
|
||
|
|
||
|
# Save the optimized image
|
||
|
img_resized.save(
|
||
|
image_library_instance.image.path,
|
||
|
format="JPEG",
|
||
|
quality=quality,
|
||
|
optimize=True,
|
||
|
)
|
||
|
|
||
|
# Update the image properties
|
||
|
image_library_instance._update_image_properties()
|
||
|
|
||
|
print(f"Optimized image: {image_library_instance.name}")
|
||
|
return True
|
||
|
else:
|
||
|
print(f"Image already optimal: {image_library_instance.name}")
|
||
|
return True
|
||
|
|
||
|
except Exception as e:
|
||
|
print(f"Error optimizing image {image_library_instance.name}: {e}")
|
||
|
return False
|