import logging import os import pprint from pelican import signals from PIL import Image log = logging.getLogger(__name__) THUMBNAIL_MAX_SIZE = 448 def generator_finalized(generator): """ Generates thumbnails for images specified in article metadata. """ output_path = generator.settings['OUTPUT_PATH'] content_path = generator.settings['PATH'] media_path = os.path.join(content_path, 'media') thumb_dir = os.path.join(output_path, 'media', 'thumbnails') if not os.path.exists(thumb_dir): try: os.makedirs(thumb_dir) log.info(f"Created thumbnail directory: {thumb_dir}") except OSError as e: log.error(f"Could not create thumbnail directory {thumb_dir}: {e}") return for article in generator.articles: if hasattr(article, 'image'): image_path_rel_to_content = article.image # image_path_rel_to_content is often like 'media/imagename.jpg' # or just 'imagename.jpg' if it's directly in 'content/media/' # and STATIC_PATHS includes 'media'. # We assume article.image is a path relative to the 'content' folder. source_image_full_path = os.path.join(media_path, image_path_rel_to_content) if not os.path.exists(source_image_full_path): log.warning(f"Source image not found for article '{article.slug}': {source_image_full_path}") continue image_filename = os.path.basename(image_path_rel_to_content) thumb_path = os.path.join(thumb_dir, image_filename) _, ext = os.path.splitext(image_filename) ext_lower = ext.lower() if ext_lower not in ['.jpg', '.jpeg', '.png']: log.info(f"Skipping non-JPG/PNG image for article '{article.slug}': {image_filename}") continue try: log.debug(f"Processing image: {source_image_full_path}") img = Image.open(source_image_full_path) # Preserve original format, handle potential conversion issues for some modes original_format = img.format if img.mode == 'P' and 'transparency' in img.info: # Palette mode with transparency img = img.convert('RGBA') elif img.mode not in ('RGB', 'RGBA', 'L'): # L is grayscale log.info(f"Converting image {image_filename} from mode {img.mode} to RGB for thumbnailing.") img = img.convert('RGB') img.thumbnail((THUMBNAIL_MAX_SIZE, THUMBNAIL_MAX_SIZE)) save_kwargs = {} if original_format: save_kwargs['format'] = original_format if original_format == 'JPEG': save_kwargs['quality'] = 95 # Adjust quality for JPEGs save_kwargs['optimize'] = True elif original_format == 'PNG': save_kwargs['optimize'] = True img.save(thumb_path, **save_kwargs) log.info(f"Generated thumbnail for '{article.slug}': {thumb_path}") # Optionally, add thumbnail URL to article metadata if needed by templates # This depends on how SITEURL and paths are structured. # For now, just creating the file. # article.thumbnail_url = f"{generator.settings.get('SITEURL', '')}/media/thumbs/{image_filename}" except FileNotFoundError: log.error(f"Image file not found: {source_image_full_path}") except IOError as e: log.error(f"Could not open or process image {source_image_full_path}: {e}") except Exception as e: log.error(f"An unexpected error occurred while processing {source_image_full_path}: {e}") def register(): signals.article_generator_finalized.connect(generator_finalized)