image library
created using VS Codey Copilot Agent with Claude Sonnet 4
This commit is contained in:
parent
bdf06863d2
commit
52dbe89582
14 changed files with 1366 additions and 3 deletions
243
hub/services/utils/image_library.py
Normal file
243
hub/services/utils/image_library.py
Normal file
|
@ -0,0 +1,243 @@
|
|||
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
|
Loading…
Add table
Add a link
Reference in a new issue