first commit
This commit is contained in:
126
modules/image_downloader.py
Normal file
126
modules/image_downloader.py
Normal file
@@ -0,0 +1,126 @@
|
||||
# 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}")
|
||||
Reference in New Issue
Block a user