127 lines
4.9 KiB
Python
127 lines
4.9 KiB
Python
# modules/image_downloader.py
|
||
"""
|
||
Модуль для загрузки изображений
|
||
"""
|
||
|
||
import os
|
||
import requests
|
||
import hashlib
|
||
import logging
|
||
from pathlib import Path
|
||
from urllib.parse import urlparse
|
||
from PIL import Image
|
||
import mimetypes
|
||
|
||
|
||
class ImageDownloader:
|
||
"""Загрузчик изображений"""
|
||
|
||
def __init__(self, config):
|
||
self.config = config
|
||
self.logger = logging.getLogger(__name__)
|
||
|
||
self.download_path = Path(config.get('images.download_path', 'images'))
|
||
self.max_size_mb = config.get('images.max_size_mb', 10)
|
||
self.allowed_formats = config.get('images.allowed_formats', ['jpg', 'jpeg', 'png', 'webp'])
|
||
self.quality = config.get('images.quality', 85)
|
||
|
||
# Создаём директорию для изображений
|
||
self.download_path.mkdir(parents=True, exist_ok=True)
|
||
|
||
def download_product_images(self, image_urls, product_id):
|
||
"""Загружает все изображения товара"""
|
||
local_images = []
|
||
|
||
# Создаём папку для товара
|
||
product_dir = self.download_path / str(product_id)
|
||
product_dir.mkdir(exist_ok=True)
|
||
|
||
for i, url in enumerate(image_urls):
|
||
try:
|
||
local_path = self._download_image(url, product_dir, f"img_{i}")
|
||
if local_path:
|
||
local_images.append(str(local_path))
|
||
except Exception as e:
|
||
self.logger.error(f"Failed to download image {url}: {e}")
|
||
|
||
return local_images
|
||
|
||
def _download_image(self, url, save_dir, filename_prefix):
|
||
"""Загружает одно изображение"""
|
||
try:
|
||
# Получаем изображение
|
||
response = requests.get(url, timeout=30, stream=True)
|
||
response.raise_for_status()
|
||
|
||
# Проверяем размер
|
||
content_length = response.headers.get('content-length')
|
||
if content_length and int(content_length) > self.max_size_mb * 1024 * 1024:
|
||
self.logger.warning(f"Image too large: {url}")
|
||
return None
|
||
|
||
# Определяем формат
|
||
content_type = response.headers.get('content-type', '')
|
||
extension = mimetypes.guess_extension(content_type)
|
||
|
||
if not extension:
|
||
# Пытаемся определить по URL
|
||
parsed_url = urlparse(url)
|
||
path_ext = Path(parsed_url.path).suffix.lower()
|
||
if path_ext in ['.jpg', '.jpeg', '.png', '.webp']:
|
||
extension = path_ext
|
||
else:
|
||
extension = '.jpg' # По умолчанию
|
||
|
||
# Проверяем разрешённые форматы
|
||
format_name = extension[1:].lower()
|
||
if format_name not in self.allowed_formats:
|
||
self.logger.warning(f"Unsupported format {format_name}: {url}")
|
||
return None
|
||
|
||
# Генерируем имя файла
|
||
url_hash = hashlib.md5(url.encode()).hexdigest()[:8]
|
||
filename = f"{filename_prefix}_{url_hash}{extension}"
|
||
filepath = save_dir / filename
|
||
|
||
# Проверяем, не скачан ли уже файл
|
||
if filepath.exists():
|
||
return filepath
|
||
|
||
# Сохраняем изображение
|
||
with open(filepath, 'wb') as f:
|
||
for chunk in response.iter_content(chunk_size=8192):
|
||
f.write(chunk)
|
||
|
||
# Оптимизируем изображение
|
||
self._optimize_image(filepath)
|
||
|
||
self.logger.debug(f"Downloaded image: {filepath}")
|
||
return filepath
|
||
|
||
except Exception as e:
|
||
self.logger.error(f"Error downloading image {url}: {e}")
|
||
return None
|
||
|
||
def _optimize_image(self, filepath):
|
||
"""Оптимизирует изображение"""
|
||
try:
|
||
with Image.open(filepath) as img:
|
||
# Конвертируем в RGB если необходимо
|
||
if img.mode in ('RGBA', 'LA', 'P'):
|
||
background = Image.new('RGB', img.size, (255, 255, 255))
|
||
if img.mode == 'P':
|
||
img = img.convert('RGBA')
|
||
background.paste(img, mask=img.split()[-1] if img.mode == 'RGBA' else None)
|
||
img = background
|
||
|
||
# Ограничиваем размер
|
||
max_size = (1200, 1200)
|
||
if img.size[0] > max_size[0] or img.size[1] > max_size[1]:
|
||
img.thumbnail(max_size, Image.Resampling.LANCZOS)
|
||
|
||
# Сохраняем с оптимизацией
|
||
img.save(filepath, 'JPEG', quality=self.quality, optimize=True)
|
||
|
||
except Exception as e:
|
||
self.logger.error(f"Error optimizing image {filepath}: {e}")
|