Skill #1 · Pipeline coordinator

video-orchestrator

Дирижёр всего видео-конвейера. Получает запрос «сделай 3 ролика для бас дент» и гоняет цепочку copywriter → voiceover → director → captions → music → reviewer. Резолвит проект из команды, поднимает preflight, координирует sub-скиллы и отдаёт готовые .mp4.

Путь
~/.claude/skills/video-orchestrator/
Триггеры
сделай ролики, запусти производство, produce videos, make ads, запусти конвейер, pipeline, полный цикл, видео для таргета, нужны ролики, сделай креативы
Зависит от
все sub-скиллы (copywriter / voiceover / director / captions / reviewer), ELEVENLABS_API_KEY, ffmpeg/ffprobe
Пэйрится с
project-setup (через check_project.py), все sub-скиллы пайплайна

Что делает простыми словами

Это top-level дирижёр. Юзер говорит «сделай 3 ролика для имплантов» — а orchestrator под капотом:

  1. Определяет, какой проект имеется в виду (bas-dent, fitzone, apparatus, etc.)
  2. Читает project.yaml этого проекта → знает язык, ниши, голос-настройки, бренд
  3. Прогоняет preflight check (есть ли клипы, голоса, ключи)
  4. Запускает copywriter N раз — получает N скриптов
  5. Запускает voiceover на каждый текст — получает N mp3
  6. Прогоняет audio_process.py — убирает паузы, нормализует громкость
  7. Запускает director — собирает по 2-3 варианта на каждый ролик
  8. Накладывает hook + субтитры (captions через render_final.py)
  9. Накладывает фоновую музыку (overlay_music.py) — отдельным шагом
  10. Прогоняет reviewer для QA
  11. Выдаёт юзеру список финальных .mp4

Без orchestrator пришлось бы каждый скилл вызывать вручную и держать в голове, где какой файл лежит, какая длительность и какие настройки. С ним — одна команда даёт готовые ролики.

Каждый шаг — отдельный скилл с собственными скриптами. Orchestrator не дублирует логику. Он знает порядок и параметры, дёргает чужие scripts/, передаёт пути и фолбэчит при ошибках.

Два пайплайна (по формату ролика)

Voiceover (дефолтный)

copywriter → voiceover (ElevenLabs) → audio_process → director (assemble, БЕЗ музыки) → render_final (hook + captions) → overlay_music → reviewer

Длительность диктует озвучка (15-60 сек). Музыка — последний шаг, отдельно от assemble.

Textover

copywriter (textover_hook) → director (clips + музыка) → remotion (text overlay) → reviewer

Фиксированные 15 сек, без озвучки, без Whisper, без captions. Текст рендерится анимацией поверх видео через Remotion.

Project Resolution

Каждый запуск должен знать «к какому проекту относится команда». Порядок резолва:

  1. Явно в команде: «сделай 3 видео для бас дент» → slug bas-dent → читаем video-projects/bas-dent/project.yaml
  2. Единственный проект: если в ~/video-projects/ только одна папка с project.yaml — используем её автоматически
  3. Неоднозначно: если проектов несколько и юзер не указал → спрашиваем
После резолва orchestrator передаёт sub-скиллам конкретные параметры из yaml: slug → scoping в БД, language → captions/voiceover, service_directions → copywriter, veo.* → clipgen, voiceover.* → voiceover, captions.* → captions, remotion.brand.* → motionfx, director.* → director.
director.db лежит ОБЩАЯ на все проекты — в ~/video-projects/director.db, не внутри папки проекта. Изоляция через junction-таблицы.

Два режима работы

РежимПоведениеКогда выбирать
auto (default) Гонит весь pipeline без остановок. Юзер ждёт и получает финал. Когда юзер доверяет системе и хочет скорости. Команда: «сделай 3 ролика, автомат»
semi-auto Останавливается на чекпоинтах, ждёт approval/edit Когда нужен творческий контроль. Команда: «покажи тексты перед озвучкой»

Чекпоинты semi-auto

  1. После генерации текстов — показать скрипты, ждать approval/edits
  2. После voiceover — отчитаться о длительности, спросить «ок?»
  3. После montage plan — показать какие клипы под какой сегмент идут, до сборки
  4. После выбора caption-стиля — какой preset рендерим (neon/marker/cyber/...)
  5. Перед overlay_music — какой трек и какая громкость
  6. После reviewer — показать оценки, спросить нужны ли правки

Preflight: check_project.py

Главный скрипт скилла — pre-flight check, который запускается ДО любого pipeline-run'а. Он проверяет:

Что проверяетЗачем
project.yaml существует и валиденБез него ничего не запустится
Проект зарегистрирован в projectsИначе junction-таблицы не будут работать
Brief сохранёнCopywriter без брифа пишет в пустоту
Минимум 5-10 клипов в библиотекеDirector не сможет собрать ролик без материала
Минимум 1 голос в пулеVoiceover нужен пул для rotation
ELEVENLABS_API_KEY в envБез ключа ElevenLabs упадёт
ffmpeg + ffprobe в PATHВсе сборки идут через них
python ~/.claude/skills/video-orchestrator/scripts/check_project.py ~/video-projects/bas-dent

Возвращает структурированный отчёт «что готово / что недостаёт». Если чего-то нет — orchestrator говорит юзеру, какой скилл нужен («запусти clipgen для клипов», «добавь голоса через voiceover add-voice»).

GOOGLE_API_KEY не нужен для core pipeline. Он требуется только если ты ещё и НОВЫЕ клипы генеришь через Veo (clipgen). Для «текст → аудио → сборка» хватает одного ELEVENLABS_API_KEY.

Команды основных шагов

Тексты (Copywriter)

python ~/.claude/skills/video-copywriter/scripts/db.py get-brief <project-dir>
python ~/.claude/skills/video-copywriter/scripts/db.py list-transcriptions <project-dir> --json
python ~/.claude/skills/video-copywriter/scripts/db.py list-texts <project-dir> --json --limit 10

# Сохранить сгенерированный текст:
python ~/.claude/skills/video-copywriter/scripts/db.py save-text <project-dir> \
  --type storytelling --for-video --text "<текст>"

Озвучка (Voiceover)

python ~/.claude/skills/video-voiceover/scripts/db.py next-voice <project-dir>

python ~/.claude/skills/video-voiceover/scripts/tts.py \
  --project-dir <project-dir> \
  --text "<текст>" \
  --voice-id <voice-id>

python ~/.claude/skills/video-voiceover/scripts/audio_process.py \
  --input voiceover.mp3 --output voiceover_processed.mp3

Сборка (Director)

python ~/.claude/skills/video-director/scripts/probe_audio.py voiceover.mp3
python ~/.claude/skills/video-director/scripts/db.py list-clips <project-dir> --json

# Сборка БЕЗ музыки (для voiceover pipeline!):
python ~/.claude/skills/video-director/scripts/assemble.py \
  --project-dir <project-dir> \
  --voiceover voiceover_processed.mp3 \
  --montage-plan '[...]' \
  --variant-name "video1_a" \
  --transition fade --transition-duration 0.5 \
  --sfx-transition <project-dir>/sfx/whoosh.mp3

Hook + Captions (render_final.py)

# Одним проходом Remotion — hook анимация на первой сцене + караоке-субтитры:
python ~/.claude/skills/video-captions/scripts/render_final.py \
  --video assembled.mp4 \
  --text "<полный текст озвучки>" \
  --voiceover voiceover_processed.mp3 \
  --preset neon \
  --caption-style karaoke \
  --hook-duration <sec первого предложения> \
  --sfx-file ~/video-projects/sfx/text/pop_bright.mp3 \
  --project-dir <project-dir> \
  --language ru \
  --model small

Music overlay (последний шаг)

python ~/.claude/skills/video-director/scripts/overlay_music.py \
  --video video_final.mp4 \
  --music <project-dir>/music/dramatic.mp3 \
  --volume 0.20 \
  --fade-in 2.0 --fade-out 2.5

Reviewer (опц. но рекомендуется)

python ~/.claude/skills/video-reviewer/scripts/review_video.py \
  --video captioned.mp4 --frames 15

Hook duration — критичная фишка

Per project memory: hook + первая сцена = длительность ВСЕГО первого предложения. Измерять Whisper'ом ДО сборки, второй клип идёт только после.

Замер первого предложения:

python ~/.claude/skills/video-captions/scripts/transcribe_words.py \
  --audio voiceover.mp3 --language ru --text "<текст>" --json \
  | jq -r '.words | map(select(.word | test("[.!?…]$"))) | .[0].end'

# → 4.3   ← это hook_duration; первый сегмент trim_end тот же

Whisper-кэш ключуется по sha1(audio path + mtime + size + model + lang + text hash) — повторный вызов на тот же mp3 моментален и бесплатен.

Partial runs

Orchestrator умеет запускать только часть pipeline:

ЗапросЧто запускается
«только тексты»copywriter → стоп
«только озвучки»(тексты есть) → voiceover → стоп
«только сборка»(озвучки есть) → director → стоп
«только субтитры»(видео есть) → captions → стоп
«тексты + озвучки»copywriter → voiceover → стоп
«сборка + субтитры»director → captions → стоп
«с новыми клипами»clipgen → full pipeline

Внутренности

Скрипты

ФайлЧто делает
scripts/check_project.pyPre-flight check: project.yaml, DB registration, clips count, voices, API keys, ffmpeg
scripts/api_keys.pyРезолвит API-ключи из project/api_keys.db или env (общий между скиллами)
scripts/schema.pySchema source-of-truth (v8). Копия одинакова во всех скиллах

Таблицы БД, которые трогает (опосредованно через sub-скиллы)

ТаблицаЧерез какой скилл
projectsRead (для резолва)
briefcopywriter
generated_textscopywriter
voiceover_historyvoiceover
clips / video_clipsdirector
videosdirector

Конфиг из project.yaml

Orchestrator сам ничего из yaml кроме project resolution не использует — но передаёт куски всем sub-скиллам. Ключевые блоки, на которые он опирается:

language: "ru"                    # → captions, voiceover
service_directions: [...]         # → copywriter (creative_ref пути)

veo:                              # → clipgen
  demographics: ...
voiceover:                        # → voiceover
  model: "eleven_multilingual_v2"
  speed: 1.2
captions:                         # → captions
  style: "karaoke"
remotion:                         # → motionfx
  brand: {...}
director:                         # → director
  default_format: "9:16"
  music_ducking: true
final_output_dir: "..."           # → копия финала за пределы проекта

Output / Delivery

В конце orchestrator выдаёт summary со списком всех файлов:

Production complete:

Video 1 (storytelling, 18s):
  - variant_a: /path/to/video1_variant_a.mp4
  - variant_b: /path/to/video1_variant_b.mp4

Video 2 (direct_offer, 12s):
  - variant_a: /path/to/video2_variant_a.mp4
  - variant_b: /path/to/video2_variant_b.mp4

Total: 4 video files
Voices used: Rachel (1x), Bella (1x)
Clips used: #3, #5, #7, #8, #12, #15

Gotchas и tips

НИКОГДА не передавай --music в assemble.py в voiceover-пайплайне. Sidechain ducking в assemble делает музыку неслышной поверх нормализованного голоса. В voiceover-pipeline музыка ВСЕГДА накладывается отдельно через overlay_music.py после captions. --music в assemble оставлено только для textover (без голоса — нечем ducking-ать).
Audio post-processing ОБЯЗАТЕЛЕН. Сырое ElevenLabs-аудио имеет долгие паузы и непостоянную громкость. audio_process.py убирает их (~5 сек экономии на 30-сек ролике), нормализует до -16 LUFS, чистит низы/верха EQ. Использовать processed-файл для всех последующих шагов, не оригинал.
Hook duration измерять ДО сборки. Whisper-замер первого предложения = первая сцена в монтаже. Если зашить произвольное значение (например 3 сек) — hook обрежется посредине фразы, и анимация text-overlay не сойдётся со звуком.
render_final.py = два шага в одном. Раньше hook overlay и captions рендерились двумя проходами Remotion. Теперь — один pass, плюс общий Whisper-кэш. В 2 раза быстрее, дешевле, и одна транскрипция переиспользуется между запусками.
Legacy fallback. Старые hook_overlay.py и remotion_captions.py работают standalone — полезно для отладки hook-пресета без перерендера captions. Orchestrator же использует render_final.py по дефолту.
Skill paths резолвятся как siblings. Все скиллы лежат в одной папке skills/. Если orchestrator в /path/to/skills/video-orchestrator, то copywriter — в /path/to/skills/video-copywriter. Относительно своего пути.

Место в пайплайне

project-setup → clipgen / analyzer → orchestrator → copywriter → voiceover → director → captions → music → reviewer

Orchestrator = «зонтик» над всеми остальными. Юзер видит его как точку входа в production. Под капотом — координация и preflight.

Когда использовать orchestrator, а когда — sub-скиллы напрямую: orchestrator — для production-batch'ей («сделай 3 ролика»). Sub-скиллы напрямую — для ручных правок («перегенери только captions», «замени hook»).