""" Сервис для получения URL изображений разных размеров. Используется в шаблонах и представлениях для удобного доступа к разным версиям. """ import os from django.conf import settings from django.core.files.storage import default_storage class ImageService: """ Сервис для работы с изображениями разных размеров. Динамически строит URL на основе пути к оригинальному файлу. """ @staticmethod def _get_config(): """Получить конфигурацию из settings""" return getattr(settings, 'IMAGE_PROCESSING_CONFIG', {}) @staticmethod def _get_size_folders(): """Получить папки для разных размеров из конфигурации""" config = ImageService._get_config() return config.get('folders', { 'thumbnail': 'thumbnails', 'medium': 'medium', 'large': 'large', 'original': 'originals', }) @staticmethod def _get_format_config(size_key): """Получить конфигурацию формата для заданного типа изображения""" config = ImageService._get_config() formats = config.get('formats', {}) return formats.get(size_key, {'format': 'JPEG', 'quality': 90}) @staticmethod def _get_file_extension(size_key): """Получить расширение файла для заданного типа изображения""" format_config = ImageService._get_format_config(size_key) image_format = format_config.get('format', 'JPEG') ext_map = { 'JPEG': 'jpg', 'WEBP': 'webp', 'PNG': 'png', } return ext_map.get(image_format, 'jpg') @staticmethod def get_url(original_image_path, size='medium'): """ Получает URL изображения нужного размера. Работает с новой структурой: - products///original.jpg - products///large.webp - products///medium.webp - products///thumb.webp Args: original_image_path: Путь к оригинальному файлу (из models.image) Обычно это путь к файлу 'original' Пример: products/123/456/original.jpg size: Размер ('original', 'large', 'medium', 'thumbnail') По умолчанию 'medium' Returns: str: URL изображения или пустая строка если нет файла """ if not original_image_path: return '' try: # Работаем с новой структурой: products///original.jpg path_str = str(original_image_path) parts = path_str.split('/') if len(parts) >= 3: # Извлекаем base_path, entity_id, photo_id из пути base_path = parts[0] # products, kits, categories entity_id = parts[1] # ID сущности photo_id = parts[2] # ID фото # Определяем размер в имени файла filename = parts[-1] if parts else os.path.basename(path_str) # Проверяем, является ли это новой структурой if filename in ['original.jpg', 'large.webp', 'medium.webp', 'thumb.webp']: # Это новая структура, заменяем только размер ext_map = { 'original': 'jpg', 'large': 'webp', 'medium': 'webp', 'thumbnail': 'webp', } target_ext = ext_map.get(size, 'jpg') # Переименовываем thumbnail в thumb final_size_name = 'thumb' if size == 'thumbnail' else size # Создаем путь в новой структуре new_path = f"{base_path}/{entity_id}/{photo_id}/{final_size_name}.{target_ext}" # Проверяем существование файла if default_storage.exists(new_path): return f"{settings.MEDIA_URL}{new_path}" # Если файл не найден, пробуем с другим расширением # Определяем расширение из конфигурации format_config = ImageService._get_format_config(size) image_format = format_config.get('format', 'JPEG') ext_map_config = { 'JPEG': 'jpg', 'WEBP': 'webp', 'PNG': 'png', } target_ext = ext_map_config.get(image_format, 'jpg') final_size_name = 'thumb' if size == 'thumbnail' else size fallback_path = f"{base_path}/{entity_id}/{photo_id}/{final_size_name}.{target_ext}" if default_storage.exists(fallback_path): return f"{settings.MEDIA_URL}{fallback_path}" return f"{settings.MEDIA_URL}{path_str}" # Старая структура для совместимости filename = os.path.basename(path_str) # Определяем базовый путь (products, kits, categories) if len(parts) > 0: base_path = parts[0] else: base_path = 'products' # Проверяем старый формат имени файла с расширением # Поддерживаем jpg, webp, png расширения if filename.endswith(('.jpg', '.webp', '.png')): # Определяем расширение файла file_ext = os.path.splitext(filename)[1] # .jpg, .webp и т.д. filename_without_ext = filename[:-(len(file_ext))] # Имя без расширения # Разделяем по последнему _ для получения base_filename и size_key parts_of_name = filename_without_ext.rsplit('_', 1) if len(parts_of_name) == 2: base_filename, file_size_key = parts_of_name # Это старый формат с явным указанием размера в имени # Получаем расширение для целевого размера target_ext = ImageService._get_file_extension(size) # Строим папку size_folders = ImageService._get_size_folders() folder = size_folders.get(size, 'medium') # Сначала пытаемся с правильным расширением из конфигурации filename_new = f"{base_filename}_{size}.{target_ext}" new_path_primary = f"{base_path}/{folder}/{filename_new}" # Если файл существует - возвращаем его if default_storage.exists(new_path_primary): return f"{settings.MEDIA_URL}{new_path_primary}" # Иначе пробуем старый формат (все .jpg) для совместимости filename_fallback = f"{base_filename}_{size}.jpg" new_path_fallback = f"{base_path}/{folder}/{filename_fallback}" if default_storage.exists(new_path_fallback): return f"{settings.MEDIA_URL}{new_path_fallback}" # Если ничего не найдено, возвращаем путь с новым расширением (браузер покажет ошибку) return f"{settings.MEDIA_URL}{new_path_primary}" # Строим новый путь (для старых файлов без новой структуры) size_folders = ImageService._get_size_folders() folder = size_folders.get(size, 'medium') new_path = f"{base_path}/{folder}/{filename}" # Возвращаем URL return f"{settings.MEDIA_URL}{new_path}" except Exception: return '' @staticmethod def get_thumbnail_url(original_image_path): """Получить URL миниатюры (150x150)""" return ImageService.get_url(original_image_path, 'thumbnail') @staticmethod def get_medium_url(original_image_path): """Получить URL среднего размера (400x400)""" return ImageService.get_url(original_image_path, 'medium') @staticmethod def get_large_url(original_image_path): """Получить URL большого размера (800x800)""" return ImageService.get_url(original_image_path, 'large') @staticmethod def get_original_url(original_image_path): """Получить URL оригинального изображения""" return ImageService.get_url(original_image_path, 'original') @staticmethod def get_all_urls(original_image_path): """ Получить все версии изображения. Returns: dict: {'original': url, 'thumbnail': url, 'medium': url, 'large': url} """ return { 'original': ImageService.get_original_url(original_image_path), 'thumbnail': ImageService.get_thumbnail_url(original_image_path), 'medium': ImageService.get_medium_url(original_image_path), 'large': ImageService.get_large_url(original_image_path), }