edit-video — конвеєр монтажу вертикальних роликів
SKILL_DIR — «Base directory for this skill», показаний при завантаженні скіла. Усі скрипти лежать у SKILL_DIR/scripts.
Проєкт — папка з відео користувача (поточна робоча папка). У ній створюються work/<назва>/, out/, assets/.
Вимоги до результату (структура, темп, стиль, правила b-roll, звук, чекліст) описані в SKILL_DIR/docs/TZ-video.md.
Прочитай його перед кроком 2 і виконуй.
Крок 0. Середовище (раз на сесію, ОБОВ'ЯЗКОВО)
node "SKILL_DIR/scripts/setup.mjs"
Скрипт перевіряє й ставить: ffmpeg ≥ 7 (з libass), whisper-cli, модель whisper large-v3-turbo (~570 МБ), модель RVM (15 МБ), npm-пакети (Remotion, onnxruntime-node, ~300 МБ), Chrome Headless Shell для Remotion, звук «вжух», шрифт. Повторний запуск нічого не перекачує. Запускай у фоні, бо перший раз триває довго. Наприкінці друкує таблицю ✅/❌; поки є ❌, далі не йди.
- Немає Node.js (
nodeне знайдено або версія < 20): Windows →winget install OpenJS.NodeJS.LTS, macOS →brew install node. Після встановлення відкрий нову консоль (PATH) і повтори крок 0. - macOS без Homebrew: попроси користувача самого виконати інсталятор з https://brew.sh (потрібен пароль sudo).
- onnxruntime ❌ на Windows: setup сам копіює новіші
msvcp140*.dllу папку onnxruntime, а якщо не допомогло — ставитьMicrosoft.VCRedist.2015+.x64. Попередь користувача, що може з'явитися вікно UAC. - Користувачу коротко скажи, що встановлено і скільки це займе місця.
Два формати запису
- Говорюща голова — одне відео (камера/телефон зі звуком).
prep <відео>. - Екран + голова + мікрофон — три файли в одній папці, наприклад
… Head.mp4,… Screen.mp4,… Sound.wav.prep <папка>сам знайде їх за назвами (Head/голова/cam, Screen/екран, Sound/audio/mic/звук), або явно:prep <head.mp4> --audio <sound.wav> --screen <screen.mp4>. Файли записані неодночасно, тож prep сам синхронізує їх за звуком (крос-кореляція, точність ~1 мс) і пишеwork/<назва>/sync.json:час_у_файлі = t_мікрофона + зсув. Головна шкала часу (склейки, субтитри,at) — мікрофон, розшифровка теж з нього. Компоновка —"layout": "split": екран зверху (1080×960), камера знизу, жовта лінія на стику.
Крок 1. Розшифровка
node "SKILL_DIR/scripts/vid.mjs" prep <відео | папка> [uk|ru|en|auto] [--audio f] [--screen f]
Запускай у фоні, на слабкому CPU це кілька хвилин. Результат у <проєкт>/work/<назва>/: probe.json, transcript.json
(слова з таймкодами), silences.json (паузи −30 дБ ≥ 0.2 с), а в консолі — текст фразами з таймкодами.
Терміни, які whisper розпізнав з помилками («джавоскріп» → JavaScript, «гід» → Git), виправ прямо в transcript.json, поле w.
Крок 2. План монтажу → work/<назва>/edit.json
Прочитай розшифровку, silences.json і подивись 1–2 кадри відео (ffmpeg -ss T -i відео -frames:v 1 x.png),
щоб зрозуміти, де обличчя і що стоїть перед спікером. Далі за ТЗ:
- Склейки: паузи > 0.35 с, «еее/ммм», фальстарти. З дублів лишай останній. Межі ставь на ~0.08 с
всередину паузи з кожного боку.
zoomчергуй 1.0 / 1.12. - Хук: титул 2–5 слів (питання або обіцянка), перше слово без паузи.
- Плашки: 3–5 ключових думок,
*слово*підсвічується. - B-roll «за спиною» (
broll): 3–4 епізоди на словах, які він ілюструє. Картинки клади в<проєкт>/assets/covers/: обкладинки робіт користувача (якщо підключено MCP devguide, бериcoverзdevguide_catalog_list) або його скріншоти. Предмет перед спікером (мікрофон, чашка) задай вavoid, інакше картка його перекриє. - Стокові вставки (
cutaways): 2–3 по 3–4 с на фразах із конкретним образом. Бери з Mixkit (див. «Ассети»). - Музика:
assets/music/*.mp3,volume0.3–0.4. - Інтервали: між b-roll, вставками й плашкою ≥ 0.5 с;
fly-b-roll і плашка не одночасно. - Формат 2 (спліт): перед вибором
focusвитягни кадр екрана в середині кожного сегмента (ffmpeg -ss <t + sync.screenOffset> -i screen.mp4 -frames:v 1 -vf scale=480:-2 sN.png, склей у контакт-лист) і наведиfocus: [x, y, w](пікселі запису екрана, висота = w / 1.125) на те, про що йде мова: вікно гри, код, кнопку, заголовок.w800–1215: менше — крупніше, але текст має лишатися цілим. Де запис уже наближений самою програмою запису, бери повну висоту (w1215). Наближення міняй на склейках.headX— де обличчя на камері (0…1). B-roll у спліті вимкнено, плашки стоять угорі екрана, субтитри — на стику. Покажи користувачу таблицю «час → текст, що лишається» + титул/плашки/b-roll/вставки/музику й дочекайся «ок».
Крок 3. Прев'ю
node "SKILL_DIR/scripts/vid.mjs" preview <проєкт>/work/<назва>/edit.json
Запускай у фоні. Вийде preview.mp4 (540×960) і frame_*.png: перші кадри знято на піках b-roll і вставок.
Перший запуск рахує маску людини (RVM на CPU ≈ 1 кадр/с, лише на відрізках b-roll) і кліпи Remotion.
Далі обидва беруться з кешу (matte_<hash>.mkv, broll_<hash>.mov), поки не зміняться склейки чи props.
Переглянь кадри сам (Read) за чеклістом ТЗ: голова перед картками, без ореолу, обличчя й avoid-зона
не перекриті, субтитри не нижче 22% кадру, плашки не на обличчі. Склади кадри в один контакт-лист
(ffmpeg hstack) і покажи користувачу разом із прев'ю.
Перевірити один лейаут без повного рендеру:
node SKILL_DIR/node_modules/@remotion/cli/remotion-cli.js still SKILL_DIR/motion/index.ts Broll x.png --props=p.json --frame=40 --public-dir=<проєкт>/assets
Крок 4. Фінал
node "SKILL_DIR/scripts/vid.mjs" render <проєкт>/work/<назва>/edit.json
Вийде <проєкт>/out/<назва>_final.mp4 (1080×1920, H.264 + AAC). Скрипт друкує LUFS і true peak (ціль ≈ −14, пік ≤ −1).
Перевір ffprobe-ом формат і тривалість, витягни 3–4 кадри. Нагадай користувачу послухати ролик:
звук перевірено лише вимірами.
Схема edit.json
Усі часи — у секундах вихідного відео; скрипт сам перераховує їх після вирізання (елементи у вирізаних шматках відкидає).
{
"source": "VID_xxx.mp4",
"site": "devguide.com.ua",
"avoid": [760, 800, 320, 1120],
"segments": [{"start": 28.13, "end": 29.49, "zoom": 1.0}, {"start": 29.81, "end": 34.36, "zoom": 1.12}],
"title": {"text": "Що питатимуть на співбесіді?", "dur": 2.6},
"callouts": [{"at": 39.0, "dur": 3.4, "text": "Не пише кожен рядок — *працює з AI*"}],
"broll": [{"at": 34.7, "dur": 2.8, "layout": "fan", "images": ["ai-site.webp", "site-3-days.webp"]}],
"cutaways": [{"at": 31.0, "dur": 3.0, "file": "assets/stock/handshake.mp4", "from": 1.0, "cropX": 0.5}],
"music": {"file": "assets/music/track.mp3", "volume": 0.4},
"style": {"subY": 520, "calloutY": 330, "titleY": 360},
"cropX": 0.5
}
Формат 2 додає поля (решта — як вище; music.file може бути ../assets/..., якщо музика спільна для кількох папок):
{
"source": "День 5 - Відео 1 Head.mp4",
"audio": "День 5 - Відео 1 Sound.wav",
"screen": "День 5 - Відео 1 Screen.mp4",
"layout": "split",
"headX": 0.77,
"segments": [{"start": 6.05, "end": 9.30, "zoom": 1.0, "focus": [346, 0, 1215]},
{"start": 25.52, "end": 27.82, "zoom": 1.12, "focus": [230, 40, 880]}],
"sync": {"head": 22.094, "screenOffset": 2.024}
}
-
face: [x, y]— центр обличчя на кадрі камери (частки 0…1, визнач за кадром). Обличчя стає по центру нижньої половини, на ~38% її висоти.headZoom(типово 1.25 ≈ по 10% з боків) — наскільки крупно;zoomсегмента множиться на нього. -
bgBlur(типово true у спліті): фон за спікером розмивається й трохи темніє, людина лишається різкою (маска RVM 384×342 @15 fps на весь ролик, рахується раз і кешується якheadmatte_<hash>.mkv). -
syncнеобов'язковий: за замовчуванням береться зsync.json. Вписуй вручну, лише якщо губи явно не збігаються. -
layout: "head"при наявномуscreen— лише камера на весь кадр (звук з мікрофона), b-roll знову доступний. -
Екран коротший за мовлення → останній кадр тримається; камера має покривати весь діапазон склейок.
-
captions:{"style": "clean" | "classic", "emphasis": ["годин", "сайт"], "accent": "#FFD200"}.clean(типово) — Montserrat ExtraBold, малі літери, слова з'являються по одному з «попом», м'яка тінь;emphasis— початки ключових слів (акцентний колір і +12%).classic— старий стиль: ВЕЛИКІ, жовте поточне слово. -
Титул і плашки — кінетичний текст Remotion (
motion/Kinetic.tsx):"підводка *акцент*"→ дрібний білий рядок і великий акцентний, слова влітають із розмиттям.textStyle: "classic"повертає старі ASS-плашки. Пиши коротко: акцент ≤ 3–4 слова, інакше він не влізе у 2 рядки. -
site— підпис в адресному рядку карток b-roll (порожньо — без нього). -
avoid—[x, y, w, h]у кадрі 1080×1920: зона предмета перед спікером, куди картки не заходять. -
broll.layout:fan(2–3 картки виринають з-за голови) |fly(3 картки залітають з країв, паралакс) |wall(3D-стіна з усіх робіт, для «хто я» / CTA).images— файли зassets/covers/. -
cutaways:file— стоковий кліп,from— секунда початку в ньому,cropX— куди наводити кроп (0…1). Звук переходу береться зassets/sfx/whoosh.mp3проєкту, а якщо його немає — зіSKILL_DIR/tools/sfx/whoosh.mp3. -
style: пікселі кадру 1080×1920 (subY— від низу до субтитрів, інші — центр по Y).cropX— кроп горизонтального відео. -
icons(застаріле, але працює):[{"at","dur","file","pos","size"}]— PNG-іконки. Краще b-roll.
Ассети: де брати й що можна
- Стокові відео, музика, звуки — Mixkit. Пряме CDN:
assets.mixkit.co/videos/<id>/<id>-1080.mp4(або-2160),assets.mixkit.co/music/<id>/<id>.mp3,assets.mixkit.co/active_storage/sfx/<id>/<id>-preview.mp3. Можна лише Free License: у HTML сторінки кліпу JSON-LD має містити"copyrightNotice":"Free". Кліпи зMixkit Restricted Licenseне бери. ID шукай на сторінкахmixkit.co/free-stock-video/<тема>/. Роби небагато запитів: сайт вмикає Cloudflare-перевірку, і її не можна обходити (тоді спробуй пізніше або попроси користувача). Горизонтальні кліпи бери в 4K/1080p, бо кроп у 9:16 бере ~1/3 ширини. - Музика також з Pixabay Music, але лише треки без позначки «Content ID Registered». Скачати
автоматично не вийде (403 / браузер не зберігає файл), тож дай користувачу посилання, щоб поклав файл у
assets/music/. - Медіа не коміть у репозиторій скіла: ліцензії стоків забороняють перепоширювати файли окремо.
Troubleshooting
| Симптом | Що робити |
|---|---|
ERR_DLOPEN_FAILED onnxruntime (Windows) | node SKILL_DIR/scripts/setup.mjs — сам покладе DLL або поставить VC++ Redistributable |
| Маска дуже довго | Норма для CPU (~1 кадр/с). Рахується лише на відрізках b-roll і кешується. Менше b-roll → швидше |
| Перші слова фрази зникли із субтитрів | whisper склеїв їх у паузу; retime у скрипті це виправляє — перевір межі сегментів |
| Квадрати замість літер | Немає шрифту: Windows — Segoe UI Black (системний), macOS — SKILL_DIR/fonts/Montserrat-Black.ttf |
| Картка перекрила мікрофон | Задай avoid |
| Музики не чути | volume 0.4–0.5; під голосом вона має бути на ~12–15 дБ тихіше (див. ТЗ §9) |
ffmpeg … -/filter_complex невідомий | ffmpeg < 7, онови (setup показує версію) |