@@ -1114,11 +1114,16 @@ msgstr "Не найдено моделей по этому ID" msgid "Voice not found." msgstr "Голос не найден." -#: ml_model/services/FileService.py:271 +#: ml_model/services/FileService.py:293 +#, python-format +msgid "The short side of the image must be at least %(min_side)d pixels" +msgstr "Короткая сторона изображения должна быть не менее %(min_side)d пикселей" + +#: ml_model/services/FileService.py:302 msgid "Image is too wide or tall" msgstr "Изображение слишком широкое или высокое" -#: ml_model/services/FileService.py:279 +#: ml_model/services/FileService.py:310 #, python-format msgid "The attached video must be at most %(max_seconds)d seconds" msgstr "Прикреплённое видео должно быть не длиннее %(max_seconds)d секунд" @@ -274,12 +274,23 @@ class MediaFileValidator: class ImageFileValidator(MediaFileValidator): @staticmethod - def validate_dimensions(w: int | None, h: int | None, *, max_pixels: int | None) -> None: + def validate_dimensions( + w: int | None, + h: int | None, + *, + max_pixels: int | None = None, + min_side: int | None = None, + ) -> None: if not (w and h): raise CorruptedFileError if max_pixels: if w * h > max_pixels: raise ImageTooLargeError(max_pixels) + if min_side and min(w, h) < min_side: + raise InvalidParameterError( + _('The short side of the image must be at least %(min_side)d pixels') + % {'min_side': min_side} + ) @staticmethod def validate_aspect_ratio(w: int | None, h: int | None, *, min_ratio: float, max_ratio: float) -> None: @@ -1,16 +1,15 @@ -import base64 import time from datetime import timedelta from decimal import Decimal from io import BytesIO from typing import Any -import filetype import requests from django.core.files import File from messages.models import Message from ml_model.exceptions import ModelVersionNotAvailable +from ml_model.services.FileService import ImageFileProcessor, ImageFileValidator from ml_model.services.base import SimpleService from ml_model.tasks import replicate_run @@ -60,12 +59,13 @@ class Hailuo(SimpleService): 'duration': 6, **input_message.info, } - if input_message.file: - kind = filetype.guess(input_message.file.read(20)) - mime = kind.mime if kind else 'application/octet-stream' - input_message.file.seek(0) - image = f'data:{mime};base64,{base64.b64encode(input_message.file.read()).decode("utf-8")}' - input_message.file.close() + if image := input_message.file: + image_processor = ImageFileProcessor(image) + kind = image_processor.get_kind(image_processor.get_bytes(20)) + ImageFileValidator.validate_kind(kind, image_processor.EXTENSIONS) + width, height = image_processor.get_dimensions() + ImageFileValidator.validate_dimensions(width, height, min_side=300) + image.close() callback_data.update({'first_frame_image': image}) start_time = time.time() video = replicate_run(f'minimax/{version}', callback_data) @@ -127,6 +127,9 @@ def replicate_run(callback_url: str, payload: dict[str, Any]): if isinstance(image := payload.get('image'), FieldFile): payload['image'], temporary_file = ImageInputService.prepare_for_replicate(image) + if isinstance(image := payload.get('first_frame_image'), FieldFile): + payload['first_frame_image'], temporary_file = ImageInputService.prepare_for_replicate(image) + output = replicate_client.run( ref=callback_url, input=payload,