Привет, Хабр! 👋

В этой статье я хочу поделиться опытом разработки пет-проекта, который превратился в полноценный инструмент для автоматической генерации коротких видео (Shorts, Reels, TikTok). Идея проста: на входе — тема (например, "История Римской Империи за 1 минуту"), на выходе — готовый видеоролик с озвучкой, субтитрами и сгенерированным видеорядом.

Но вместо того, чтобы накидать "спагетти-код" в одном файле main.py, я решил подойти к задаче как инженер и построить систему на принципах Clean Architecture.

🎯 Зачем?

Генерация видео — это сложный пайплайн:

1.  Написать сценарий.

2.  Придумать визуальный стиль.

3.  Сгенерировать картинки (Midjourney, Flux).

4.  Оживить картинки в видео (Kling, Runway, Sora).

5.  Озвучить текст (TTS).

6.  Собрать всё вместе с субтитрами.

API меняются, модели выходят новые каждую неделю. Сегодня лучший визуал у Flux, завтра у Midjourney v7. Сегодня видео делаем в Runway, завтра в Kling. Жесткая привязка к конкретным API убила бы проект через месяц.

Поэтому Clean Architecture здесь не роскошь, а необходимость.

🏗 Архитектура

Проект разбит на слои, следуя классической "луковой" архитектуре:

1.  Domain (Entities): Pydantic-модели, описывающие суть (VideoScript, Scene, Character). Они ничего не знают о внешнем мире.

2.  Interfaces: Абстрактные классы (VideoGenerator, ScriptGenerator). Контракты, которые должны соблюдать внешние сервисы.

3.  Services (Use Cases): Бизнес-логика. Здесь живут "Агенты": Сценарист, Арт-директор, Режиссер монтажа.

4.  Infrastructure: Реализации интерфейсов (API клиентов Comet, Yandex, OpenAI и т.д.).

Стек технологий

*   Python 3.11+

*   Pydantic: Для строгой типизации данных между слоями.

*   Asyncio: Генерация контента — долгий процесс, все запросы к API дол��ны быть асинхронными.

*   LangChain: Для удобной работы с LLM.

*   MoviePy: Для финальной сборки видео.

🤖 Команда Агентов

Вместо одного "умного" промта, который делает всё, я разделил обязанности между специализированными агентами.

1. Screenwriter (Сценарист)

Его задача — только текст. Он не думает о том, как это будет выглядеть. Он выдает JSON со сценами, диалогами и таймингами.

class Scene(BaseModel):

    scene_number: int

    narration: str  # Текст для озвучки

    duration_seconds: int = 5

    # Визуальные поля пока пустые

    image_prompt: str | None = None

    video_prompt: str | None = None

2. Art Director (Арт-директор)

Вот здесь начинается магия. Арт-директор получает сухой сценарий и превращает его в визуальное описание.

Недавно я разделил логику так, что Арт-директор может генерировать промты с нуля, если Сценарист их не предоставил.

Он использует специальные промты для LLM, чтобы добавить "кинематографичности": cinematic lighting, 8k, dramatic atmosphere.

# app/services/art_director.py

async def enhance_script(self, script: VideoScript) -> VideoScript:

    """Арт-директор проходит по всем сценам и создает визуальный стиль."""

    for scene in script.scenes:

        if not scene.image_prompt:

             # Если сценарист не дал визуал, придумываем сами

            scene.image_prompt = await self._generate_scene_visuals(scene)

        else:

             # Иначе улучшаем то, что есть

            scene.image_prompt = await self._enhance_prompt(scene.image_prompt)

    return scriptt

3. Motion Director (Постановщик движения)

Статичная картинка — это скучно. Motion Director добавляет инструкции для видео-генератора.

*   "Camera zoom in"

*   "Character turns head left"

*   "Wind blowing through hair"

Он использует шаблон motion_director.txt, который заставляет LLM выдавать строгие механические инструкции, понятные нейросетям типа Kling или Runway.

## 🚀 Pipeline (Оркестратор)

Все это связывается в классе VideoPipeline. Это фасад, который управляет потоком данных.

# app/services/pipeline.py

async def generate_video(self, topic: str):

    # 1. Сценарий

    script = await self.script_gen.generate_script(topic)

    

    # 2. Визуальный стиль

    script = await self.art_director.enhance_script(script)

    

    # 3. Движение

    script = await self.motion_director.enhance_script(script)

    

    # 4. Параллельная генерация ассетов

    await asyncio.gather(

        self.generate_images(script),

        self.generate_voiceovers(script)

    )

    

    # 5. Оживление (Image-to-Video)

    await self.generate_videos(script)

    

    # 6. Монтаж

    final_video = await self.video_editor.edit_video(script)

    return final_video

Интересный момент: мы используем asyncio.gather для параллельной генерации. Пока генерируются картинки для 10 сцен, параллельно может идти озвучка. Это экономит кучу времени.

🔌 Адаптеры и Интерфейсы

Благодаря интерфейсам, я могу менять провайдеров видео "на лету".

class VideoGenerator(ABC):

    @abstractmethod

    async def generate_video(self, image_path: str, prompt: str) -> str:

        pass

У меня есть CometVideoGenerator (наш внутренний сервис), KieVideoGenerator (для Grok), и я легко могу добавить RunwayVideoGenerator.

В dependencies.py я просто проверяю конфиг и подставляю нужную реализацию:

if "grok" in settings.VIDEO_GENERATION_MODEL:

    video_gen = KieVideoGenerator(...)

else:

    video_gen = CometVideoGenerator(...)

🛠 CLI и новые фичи

Недавно добавил возможность генерировать видео только по картинке, игнорируя текстовые промты. Это полезно, когда нейросеть (например, Kling) лучше понимает контекст из самой картинки, а текст её только сбивает.

В cli.py добавил флаг --video-image-only, который прокидывается в пайплайн:

# В пайплайне

if not self.use_video_prompt:

    prompt = None  # Генератор получит только image_path

📈 Результат

В итоге получилась гибкая система. Я могу:

1.  Поменять LLM для сценария (OpenAI -> Mistral -> Local Llama).

2.  Поменять генератор картинок (Midjourney -> Flux).

3.  Поменять генератор видео.

И всё это без переписывания бизнес-логики.

Что дальше?

*   Веб-интерфейс (FastAPI + React).

*   Интерактивный режим (правка сценария человеком перед генерацией).

*   Поддержка вертикального/горизонтального форматов на уровне кропа.

Ссылка на проект: GitHub

Пишите в комментариях, какие инструменты для AI видео используете вы!