ГлавнаяБлогVibeClip: как я создал open-source редактор видео с ИИ
Python

VibeClip: как я создал open-source редактор видео с ИИ

Узнайте, как создать open-source редактор видео с ИИ на Python. VibeClip режет длинные видео в shorts с субтитрами через чат. Попробуйте сами!

Al
Редакция Algolitalgolit.ru
12 мин чтения13 июня 2026 г.

Зачем нужен VibeClip — редактор видео на основе ИИ

Представьте: у вас папка с длинными видео — записи подкастов, лекции, скринкасты. Вы хотите превратить их в короткие вертикальные клипы с субтитрами за минуты, а не часы. Существующие инструменты либо заставляют вас вручную таскать клипы по таймлайну, либо требуют загружать сырые файлы в облако и платить за каждый экспорт. Я столкнулся с этой проблемой и решил её, создав VibeClip — open-source инструмент, который позволяет редактировать видео, просто описывая желаемые изменения в чате. В этой статье я расскажу, как он устроен, и покажу ключевые архитектурные решения на Python.

Архитектура VibeClip: три ключевых компонента

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

1. Транскрибация: faster-whisper

Всё начинается с точной покадровой транскрипции. Мы используем faster-whisper — локальную модель, которая не требует API-ключа и выдаёт временные метки для каждого слова. Эти метки — основа для синхронизации субтитров и обнаружения тишины.

from faster_whisper import WhisperModel

# Загружаем модель (можно выбрать размер: tiny, base, small, medium, large)
model = WhisperModel("base", device="cpu", compute_type="int8")

# Транскрибируем видеофайл
segments, info = model.transcribe("input_video.mp4", beam_size=5)

# Выводим текст с временными метками
for segment in segments:
    print(f"[{segment.start:.2f}s - {segment.end:.2f}s] {segment.text}")
    for word in segment.words:
        print(f"  Слово: {word.word}, время: {word.start:.2f}s - {word.end:.2f}s")

Этот код запускается один раз для каждого видео. Результат — список слов с метками, который мы сохраняем в JSON. На его основе LLM будет принимать решения о нарезке.

2. «Мозг»: LLM с вашим ключом

Единственный компонент, который обращается к сети, — это языковая модель. Вы подключаете свой ключ (OpenAI, Gemini, Claude, DeepSeek или локальный Ollama). LLM выполняет две задачи:

  • Оценка сильных моментов: анализирует транскрипт на основе критериев «зацепка», «поток», «ценность» (не просто ключевые слова).
  • Преобразование запросов: ваш запрос «сделай более динамичным» превращается в последовательность конкретных действий (обрезать тишину, увеличить субтитры, изменить цвет).

Пример вызова LLM через OpenAI API:

import openai

# Устанавливаем ключ из переменной окружения
openai.api_key = os.getenv("OPENAI_API_KEY")

def analyze_transcript(transcript_json):
    # Отправляем транскрипт модели для оценки
    response = openai.ChatCompletion.create(
        model="gpt-4",
        messages=[
            {"role": "system", "content": "Ты — редактор видео. Оценивай транскрипт на силу моментов (1-10). Выдай JSON: [{'start': float, 'end': float, 'score': int, 'reason': str}]."},
            {"role": "user", "content": f"Транскрипт: {transcript_json}"}
        ],
        response_format={"type": "json_object"}
    )
    return response.choices[0].message.content

# Пример вызова
scored_moments = analyze_transcript(transcript_data)
print(scored_moments)

Важно: мы отправляем только текст транскрипта, а не само видео. Это сохраняет приватность.

3. Рендеринг: ffmpeg

Все операции — нарезка, переформатирование в 9:16, наложение субтитров, зум и изменение громкости — выполняются через ffmpeg локально. Никаких облачных ферм рендеринга.

import subprocess
import json

def render_clip(input_file, start, end, output_file, captions):
    # Формируем команду ffmpeg для нарезки и добавления субтитров
    cmd = [
        "ffmpeg",
        "-i", input_file,
        "-ss", str(start),
        "-to", str(end),
        "-vf", f"crop=ih*9/16:ih,scale=1080:1920,drawtext=text='{captions}':fontsize=24:fontcolor=white:x=(w-text_w)/2:y=h-th-100",
        "-c:a", "aac",
        output_file
    ]
    subprocess.run(cmd, check=True)

# Пример: клип с 10 по 20 секунду с субтитрами
render_clip("input.mp4", 10, 20, "clip_1.mp4", "Привет, это тестовый клип")

Чтобы избежать повторной обработки при отмене, мы используем кэширование промежуточных результатов. Каждое изменение — это снимок состояния, который можно откатить.

Что было сложнее, чем ожидалось

Синхронизация субтитров после удаления тишины

Метки времени от Whisper точны, но не идеальны. Когда мы вырезаем тишину, оригинальные временные метки сбиваются. Решение: сделать транскрипт единственным источником истины, а видео — производным от него. То есть мы сначала определяем, какие слова оставить, а потом генерируем видео, которое точно соответствует этим словам.

def align_captions_after_silence_removal(original_words, silence_intervals):
    """Корректируем временные метки слов после удаления тишины."""
    adjusted_words = []
    total_silence_removed = 0
    for word in original_words:
        # Вычитаем всю тишину, которая была до этого слова
        for interval in silence_intervals:
            if interval['end'] <= word['start']:
                total_silence_removed += interval['end'] - interval['start']
        adjusted_words.append({
            'text': word['text'],
            'start': word['start'] - total_silence_removed,
            'end': word['end'] - total_silence_removed
        })
    return adjusted_words

Поддержка разных провайдеров LLM

Проблема: OpenAI использует response_format={"type": "json_object"}, но другие провайдеры (например, Ollama) игнорируют этот параметр или выдают ошибку. Решение: отправлять параметр только для поддерживаемых провайдеров и толерантно парсить JSON из ответа, даже если он обёрнут в markdown-блок.

def parse_llm_response(response_text):
    """Извлекаем JSON из ответа LLM, даже если он обёрнут в ```json ... ```."""
    import re
    # Ищем блок кода с JSON
    match = re.search(r'```(?:json)?\n(.*?)\n```', response_text, re.DOTALL)
    if match:
        json_str = match.group(1)
    else:
        json_str = response_text
    return json.loads(json_str)

Практический вывод: что делать прямо сейчас

VibeClip — это рабочий open-source инструмент, который вы можете запустить одной командой docker compose up или через uv/pip. Вам нужен только ключ от LLM. Попробуйте его для своих видео:

  • Скачайте репозиторий: github.com/oktaydbk54/vibeclip
  • Установите зависимости: pip install -r requirements.txt
  • Настройте .env с вашим API-ключом
  • Запустите: python main.py

Если вы создаёте короткий контент или интересуетесь AI-инструментами, это отличная возможность внести свой вклад. Присылайте PR и issues — проект ещё сырой, и ваша обратная связь очень важна.

#open-source#видеоредактор#искусственный интеллект#Python#ffmpeg
Al
Редакция Algolit

Пишем про алгоритмы, подготовку к собеседованиям и карьеру в IT — так, чтобы было понятно и полезно.

Хочешь закрепить знания на практике?

Решай задачи на Algolit — интерактивная платформа для обучения

Начать бесплатно →
VibeClip: как я создал open-source редактор видео с ИИ | Algolit