Новый ролик: от дубля до мастера
Перед стартом: harness.config.json → configured: true (иначе — скилл setup), bash video/scripts/doctor.sh без MISSING. Прочитай brand-kit.md, editing-taste.md, «Профиль вкуса» в exemplars.md, anti-patterns.md, delivery-specs.md. Стиль ролика — только из профиля пользователя: есть ли постер, карточки, стикеры, б-ролл, звук, сколько графики, какой темп. Чего в профиле нет — спроси, не решай сам. Отправная точка кода — текущий video/src/.
Если video/public/footage.mp4 уже есть от прошлого ролика и он не заархивирован в edits/ — сначала заархивируй (шаг 11), потом начинай.
1. Осмотр дубля
ffprobe -v error -show_entries format=duration:stream=width,height,r_frame_rate:stream_side_data=rotation -of default=nw=1 takes/1.MOV
Ориентацию смотри по rotation, не по width (айфон пишет вертикаль как 1920x1080 + флаг). Посмотри 3–4 кадра (ffmpeg -ss N -i … -frames:v 1 out/peek-N.jpg). Не стабилизируй, если снято со штатива — vidstab добавит дрожь. Только если явно с рук: стабилизируй ИСХОДНИК (vidstabdetect=shakiness=6:accuracy=15 → vidstabtransform=smoothing=25:optzoom=1:interpol=bicubic, -c:a copy; нужен ffmpeg с libvidstab) и дальше работай с ним.
2. Дубли и фальш-старты — ДО нарезки
cd video && python3 scripts/chunks.py ../takes/1.MOV
Цельная расшифровка прячет повторы, а зритель ловит их сразу. Найди: соседние куски с одной фразой (дубль — оставь ПОСЛЕДНИЙ), куски в 1–2 слова, которые следующий кусок начинает заново (фальш-старт). Покажи пользователю список того, что вырежешь, с таймкодами — и получи «да». Границы — внутри пауз.
3. Нарезка
cp public/captions.json out/captions.backup.json 2>/dev/null # cut.py перезаписывает расшифровку
DROP="12.40-15.80" python3 scripts/cut.py ../takes/1.MOV
Темп берётся из cut в профиле (подобран по референсам). Скрипт печатает DURATION, кадры склеек и проверку «склейка на речи». Склеек мало для темпа, который хочет пользователь, — передай аргументы ниже профиля, например … 0.4 0.2 (пол ~0.35 / 0.2); много — выше. Нет склеек вообще — шумная комната: QUIET=-46. Быстрый говорун/вялый темп — SPEED=1.1. Склейка на речи — сдвинь DROP или параметры, не оставляй.
4. Матинг — запусти СРАЗУ (если style.poster.enabled)
TITLE_OUT = первая склейка из вывода cut.py (постер уходит на ней), обычно 4–7 с. Матинг — самая долгая часть (~8 с/кадр на CPU, 2 воркера ≈ 6 кадров/мин), запусти в фоне и пиши код, пока он идёт:
python3 scripts/matte.py <TITLE_OUT> 2 > out/matte.log 2>&1 # в фоне (run_in_background)
Не рендери стиллы, пока он идёт, — они его душат. Ждать окончания — через ps -axo command | grep "[m]atte.py", не через pgrep. Скрипт пропускает готовые кадры — после перезапуска просто запусти снова.
5. Расшифровка
Открой public/captions.json (w, s, e в секундах, em):
- исправь ослышки: имена, бренды, термины, англицизмы; акронимы — как пишутся (
AI,ROI); whisper любит «Спасибо за просмотр» / «Thank you.» в тишине хвоста — удали; em: 2(еслиstyle.captions.highlight—sizeилиboth) и"accent": 1(еслиcolorилиboth) — по СМЫСЛУ, в том количестве и на тех словах, что описаны в профиле (brand-kit.md§2, §4). Не описано — спроси, какие слова выделять.
6. Бит-лист
Сам или субагентом script-beats (при длинном ролике) напиши video/SCRIPT.md:
- постер — только если
style.poster.enabled: строки словами и тоном пользователя, акцент и кикер — как в профиле; - карточки — если
style.cardsнеnone, с плотностью изediting-taste.md§3 (число →count; шаг →num+head; перечисление →head+linesилиrowс иконками на словах);from= первое слово фразы + 4 кадра; - стикеры
FLOATS— только еслиstyle.stickers: подпись одним словом, компактная пиктограмма (ART_NAMES); - карточка
pain: true— только если пользователь хочет «так не надо».
Перед кодом покажи пользователю бит-лист коротко (что и где появится) — его ролик, его решения.
7. Код
src/Composition.tsx:DURATION,TITLE_OUT,KICKER,TITLE,BEATS,FLOATS;Captions from= начало второй фразы, если есть постер, иначе 0.src/ui/Footage.tsx: таблицаBEATSстрого по склейкам из cut.py, в полную силу (крупный план 1.16–1.21×, обычный 1.06–1.09×) —style.motion.punchсам ослабит или выключит. Где ставить крупный — поediting-taste.md§4; после крупного — обратно шире, иначе крупность «залипает»; если постер — на первой склейке.- Новый компонент — только если ни карточка, ни стикер, ни
Photo/Clip/Tagне могут это выразить (тогда —scene-builder). - Б-ролл из Higgsfield — если подключён и профиль его хочет (
brand-kit.md§5): показывай через<Clip>(заранее обрежь и заглуши:ffmpeg -an -t <сек>), проверь текст/лица/руки на кадрах генерации до вставки. Сначала спроси пользователя и назови цену. - Звук — только если
style.sfx: недостающие роли (card,count,end) — субагентомaudio-agentпо описанию из профиля. npm run lint— чисто.
8. Кадры на согласование (после матинга)
Отрендерь по стиллу на каждую карточку и стикер в полностью собранный момент, постер в середине жизни (последняя строка читается над волосами), несколько фраз субтитров, ПОСЛЕДНИЙ кадр:
npx remotion still MyComp out/key/f<N>.png --frame=<N> --scale=0.5 --timeout=120000
Склей в лист по 4 в ряд с подписями-номерами (PIL; шрифт с кириллицей, например /System/Library/Fonts/Supplemental/Arial Bold.ttf), покажи пользователю (SendUserFile). Мастер — только после его «ок». Лист — минуты, мастер — 10–20 минут.
Проверь сам до показа: ключевой текст вне нижних 15% / верхних 8%; ничего не налезает; стикер не пересекается с карточкой; последний кадр не пустой и не оборванный; всё как в профиле пользователя и ничего сверх.
9. Мастер
npm run master # в фоне; ждать процесс, не появление файла
Скрипт сам проверяет вырезки, считает кадры и делает out/preview.mp4. Проверь громкость: ffmpeg -i out/final-master.mp4 -af ebur128 -f null - 2>&1 | grep -A3 Summary ≈ цель из audio.lufs. Отправь пользователю превью.
Правка после мастера («вырежи вот эту фразу») — режь сам мастер (ffmpeg trim+concat по тишине), не перерезай дубль и не перерендеривай всё; назови ETA заранее. Проверь стык: вырежи 6 с вокруг и прогони whisper — должны сходиться ровно две нужные фразы.
10. Обложка
Спроси, какая обложка нужна, если в профиле не сказано: кадр с заголовком (Cover), постер ролика (PosterCover) или своя. Кадр выбирай вместе с пользователем: предложи 3–4 резких кадра с открытыми глазами (ffmpeg -ss <сек> -i takes/1.MOV -frames:v 1 -q:v 2 out/cover-N.jpg).
- Выбранный кадр →
public/cover-bg.jpg. rembg i -m birefnet-portrait public/cover-bg.jpg out/cover-matte.pngpython3 scripts/cover-sticker.py public/cover-bg.jpg out/cover-matte.png public/cover-cutout.png 20- В
Root.tsx→CoverdefaultProps.lines: заголовок словами пользователя,accent/em— как он хочет. Размеры подгоняются сами — не правь кегли. npx remotion still Cover out/cover.png --timeout=120000— покажи.
Альтернатива: PosterCover — постер ролика как обложка (кадр + его вырезка в poster-bg.jpg / poster-cutout.png).
11. Архив
edits/<N>/ (в корне, не в video/): Composition.tsx, Footage.tsx, captions.json, SCRIPT.md, cover.png, final-master.mp4, preview.mp4. Если пользователь хочет — дальше скилл publish.