Skill #4 · Video assembly
video-director
AI-режиссёр: берёт готовую озвучку + clip-library проекта, собирает 2-3 варианта смонтированного ролика через FFmpeg. Ядро сборки видео-объявлений.
Что делает простыми словами
Это «монтажёр». Ему дают озвучку (аудио + текст) и библиотеку клипов проекта (база с описаниями каждого — что происходит, сколько секунд, какой mood). Он:
- Замеряет длину озвучки → понимает сколько должен длиться ролик
- Читает текст озвучки → понимает о чём говорится в каждый момент
- Подбирает клипы под смысл (проблема → клип проблемы, продукт → клип продукта)
- Режет на сегменты 2-5 секунд, чередует план/общий план для ритма
- Проверяет историю использования клипов (чтобы не использовать одни и те же в N роликах подряд)
- Собирает 2-3 разных варианта монтажа
- FFmpeg-командой склеивает + накладывает фоновую музыку (с ducking под голос)
- Результат: 2-3 готовых
.mp4
Subtitles НЕ накладывает — это делает отдельный video-captions на следующем шаге. Director выдаёт сырое видео с аудио, captions его дожимает.
Два режима работы
| Режим | Что внутри | Длина | Когда |
|---|---|---|---|
| voiceover (default) | Clips + ElevenLabs voice + музыка (приглушённая 0.20) + Whisper-субтитры | = длина озвучки (15-60 сек) | Классическое видео-объявление с голосом за кадром |
| textover | Clips + музыка (громче 0.30) + анимированный текст через Remotion | Фиксированные 15 секунд | Когда нужен текстовый hook без озвучки (молчаливый rant с типографикой) |
Принципы монтажа (зашиты в SKILL.md)
Hook (первые 2-3 сек) = всё. Зритель решает «смотреть или скроллить» в это окно. Берётся самый яркий клип: движение, лица, неожиданность, контраст.
Middle несёт сообщение. Клип должен соответствовать тому, что в этот момент говорит голос. Голос про проблему — показываем проблему. Голос про продукт — показываем продукт.
Ending (CTA, последние 2-4 сек) = product shot / лого / результат. Энергия нарастает к этому моменту.
Pacing: сегмент 2-5 сек. Никогда не держать клип дольше 5 сек — внимание падает. Quick cuts (2-3с) = энергия, более длинные holds (4-5с) = акцент.
Команды
Замерить озвучку
python ~/.claude/skills/video-director/scripts/probe_audio.py voiceover.mp3
# → {"duration_seconds": 28.4}
Получить клипы проекта
python ~/.claude/skills/video-director/scripts/db.py list-clips \ --project-dir ~/video-projects/apparatus # → JSON массив всех клипов проекта с description, duration, scene_type, mood
Проверить usage history (rotation)
python ~/.claude/skills/video-director/scripts/db.py recent-clips \ --project-dir ~/video-projects/apparatus --limit 3 # → клипы из последних 3 видео — стараться не использовать снова
Собрать вариант (voiceover-режим)
python ~/.claude/skills/video-director/scripts/assemble.py \
--project-dir ~/video-projects/apparatus \
--voiceover voiceover.mp3 \
--subtitle-text "Текст для субтитров" \
--montage-plan '[
{"clip_id": 12, "trim_start": 0, "trim_end": 3.0},
{"clip_id": 45, "trim_start": 1.5, "trim_end": 5.5},
{"clip_id": 8, "trim_start": 0, "trim_end": 4.0}
]' \
--variant-name "variant_a" \
--music background.mp3 \
--music-volume 0.20 \
--format voiceover
Собрать вариант (textover-режим)
python ~/.claude/skills/video-director/scripts/assemble.py \
--project-dir ~/video-projects/apparatus \
--montage-plan '[{"clip_id": 12, "trim_start": 0, "trim_end": 7.5}, ...]' \
--variant-name "textover_a" \
--format textover \
--music background.mp3 \
--music-volume 0.30 \
--duration 15
Внутренности
Скрипты
| Файл | Что делает |
|---|---|
scripts/probe_audio.py | Получает длину аудиофайла через ffprobe |
scripts/db.py | CRUD по clips + videos + video_clips (project-scoped через junction-таблицы) |
scripts/assemble.py | Главный воркхорс: принимает montage_plan JSON → строит FFmpeg-команду → cuts + concat + аудио-микс + ducking |
scripts/overlay_music.py | Накладывает фоновую музыку с ducking. Используется отдельно (например screencast вызывает с volume 0.08) |
scripts/api_keys.py | Резолвит API-ключи из project/api_keys.db или env |
scripts/schema.py | Общий schema-source-of-truth (v8) — копия одинакова во всех скиллах |
Таблицы БД, которые трогает
| Таблица | Действие | Зачем |
|---|---|---|
clips | Read | Берёт всю clip-library проекта (через project_clips junction) |
videos | Write | Регистрирует собранный ролик (path, voiceover_id, generated_text_id) |
video_clips | Write | Записывает «какие клипы пошли в этот ролик» — для rotation |
project_videos | Write | Junction: linking видео к проекту |
music | Read | Список доступных music-треков (через project_music) |
Конфиг из project.yaml
Director читает из ~/video-projects/<project>/project.yaml блок director:
director: default_format: voiceover # или textover default_aspect: "9:16" # output aspect segment_duration_range: [2, 5] # min/max длина сегмента music_volume_voiceover: 0.20 # дакинг под голос music_volume_textover: 0.30 # без голоса — громче transition: "none" # пока без переходов (hard cut) rotation_window: 3 # сколько последних видео учитывать в rotation
Output
Финальное видео ложится в:
~/video-projects/<project>/videos/<variant_name>.mp4
Если в project.yaml прописан final_output_dir (например для performante-ai-agency = ~/Desktop/prfmnt creo/) — копия туда же.
Gotchas и tips
Total duration MUST = voiceover duration. Если sumof(trim_end - trim_start) ≠ duration озвучки, FFmpeg либо обрежет хвост, либо растянет (= рассинхрон).
assemble.py валидирует это и кидает ошибку если расхождение > 0.3 сек.
Subtitles НЕ здесь. Не путать с video-captions — director выдаёт чистое видео + аудио. Captions = отдельный шаг.
Music ducking уже встроен в assemble.py (ffmpeg
sidechaincompress): музыка автоматически приглушается когда говорит голос. Не нужно делать руками.
Clip rotation важна для свежести. Если у проекта 30 клипов и каждый ролик использует 5-7 — за 6 роликов вся library задействована. Без rotation одни и те же клипы кочуют из ролика в ролик = реклама смотрится одинаковой = CTR падает.
Не вызывать без заполненной clip library. Если в
clips для проекта пусто — director не сможет собрать. Сначала clipgen (Veo генерация) или analyzer (импорт своих видео).
🎬 Примеры работы
Открой Showcase → final-textover — там 3 финальных textover-видео (T608/ETC5/T8) + 3 voiceover-видео для bas-dent с music ducking. Все примеры — реальные production-runs, embedded локально (работают офлайн).
Место в пайплайне
copywriter →
voiceover →
director →
captions →
reviewer
Director = центральный узел сборки. Входит:
Director = центральный узел сборки. Входит:
voiceover.mp3 + text + clips[]. Выходит: 2-3 variant_X.mp4 без субтитров.
Сверху над director обычно стоит video-orchestrator — он автоматически прогоняет всю цепочку (copywriter → voiceover → director → captions). Если хочется ручного контроля — вызывают director напрямую.