video-screencast
Монтажёр для сырых записей с экрана и talking-head. Авто-детект тишины, дедупликация дублей по канонический сценарию, voice cleanup для реального микрофона, паддинг до 9:16. Для Screen Studio / Loom / телефонных съёмок где юзер перезаписывает фразы пока не понравится.
Что делает простыми словами
Это монтажёр для случая «я записал себя на телефон / Screen Studio, переписал несколько раз каждое предложение, и хочу чтобы оставили только финальные дубли без пауз». Алгоритм:
- Извлекает аудио из сырого
.mp4→ моно WAV 16kHz - silencedetect через FFmpeg (порог -32dB, мин 0.5с тишины) → нарезка на speech-сегменты
- На каждом сегменте отдельно гонит Whisper (с
condition_on_previous_text=Falseчтобы предыдущая транскрипция не влияла) - Каждый сегмент фаззи-мэтчит на предложения из канонического сценария (word overlap)
- Если на одно предложение несколько дублей — оставляет последний (по позиции во времени)
- Формирует cut plan JSON: какие сегменты
kept: true - FFmpeg trim + concat по cut plan + voice cleanup chain → cleaned.mp4
- Опционально: pad до 1080×1920 если запись не 9:16 (Screen Studio часто экспортирует 864×1080 = 4:5)
0.08 вместо дефолтного 0.20 для TTS.
Пайплайн
↓ extract audio (mono 16k WAV)
↓ ffmpeg silencedetect (-32dB, 0.5s) → speech segments
↓ Whisper small per-segment
↓ match segments → canonical sentences (fuzzy word overlap)
↓ keep LAST segment per (sentence, position) → cut plan JSON
↓ ffmpeg trim+concat with voice cleanup chain → cleaned.mp4
↓ optional: pad to 1080×1920 for 9:16
↓ optional: video-captions (Remotion) → captioned.mp4
↓ optional: video-director overlay_music.py → final.mp4
Voice cleanup chain
Стандартная цепочка фильтров для реального микрофона (НЕ для ElevenLabs TTS — там аудио уже чистое):
highpass=f=80, afftdn=nf=-25, acompressor=threshold=-18dB:ratio=3:attack=20:release=200:makeup=2, loudnorm=I=-16:TP=-1.5:LRA=11
| Фильтр | Что делает |
|---|---|
highpass=f=80 |
Срезает суб-бас румбл (кондиционер, удары по столу, вибрация стойки микрофона) — всё что ниже 80Hz |
afftdn=nf=-25 |
FFT-based адаптивный denoise — снимает фоновый шипяще-шумовой ковёр (room tone, мелкий шум) |
acompressor (-18dB / 3:1, makeup +2dB) |
Выравнивает громкие и тихие места речи. Юзер шепчет — поднимает, орёт — придавливает. Makeup +2dB поднимает общий уровень обратно. |
loudnorm I=-16 |
Нормализует до broadcast/podcast стандарта: -16 LUFS integrated, true peak -1.5, LRA 11. Это та громкость которую ждут Reels/TikTok/Spotify. |
Скрипты
| Файл | Что делает |
|---|---|
scripts/detect_takes.py |
Audio → silence detection → per-segment Whisper → cut plan JSON. Мэтчит на canonical text, помечает дубли. Главный «думающий» скрипт. |
scripts/cut_and_clean.py |
Cut plan JSON + raw video → cleaned mp4. Trim+concat по плану, прогоняет audio через voice cleanup chain, опционально pad до 1080×1920. |
scripts/voice_cleanup_chain.py |
Reusable: возвращает -af filter-строку для real-mic voice cleanup. Можно импортировать в другие скрипты. |
scripts/render_pipeline.sh |
End-to-end driver: detect → review JSON в $EDITOR → cut+clean → captions → music. Запускает весь маршрут одной командой. |
Команды
Шаг 1: детект дублей + cut plan
python ~/.claude/skills/video-screencast/scripts/detect_takes.py \ --video raw_recording.mp4 \ --canonical-text "$(cat script.txt)" \ --whisper-model small \ --silence-threshold -32dB \ --silence-min 0.5 \ --min-segment 0.5 \ --output cut_plan.json
Шаг 2: ручной review (рекомендуется для сложных записей)
# Открывает cut_plan.json в $EDITOR # Юзер вручную флипает "kept": true/false для проблемных сегментов $EDITOR cut_plan.json
Шаг 3: cut + clean + pad
python ~/.claude/skills/video-screencast/scripts/cut_and_clean.py \ --cut-plan cut_plan.json \ --output cleaned.mp4 \ --voice-cleanup \ --pad-to 1080x1920
End-to-end (всё одной командой)
bash ~/.claude/skills/video-screencast/scripts/render_pipeline.sh \ --video raw_recording.mp4 \ --canonical-text script.txt \ --music ~/video-projects/bas_dent/music/calm_piano.mp3 \ --project-dir ~/video-projects/bas_dent \ --output final.mp4
Captions integration
После cut_and_clean идёт video-captions. Для talking-head дефолтный вариант — monochrome scale-pop (анимация размером, без вспышки цвета):
python ~/.claude/skills/video-captions/scripts/remotion_captions.py \ --video cleaned.mp4 \ --text "$(cat canonical.txt)" \ --style karaoke \ --highlight-color "#FFFFFF" \ --font-size 64 \ --project-dir ~/video-projects/bas_dent \ --output captioned.mp4
wordAnimation: scale-pop. Если передать --highlight-color "#FFFFFF", активное слово окрашивается в тот же цвет что и неактивный текст — остаётся только пульсация размера. Без жёлтой/cyan вспышки. Это «спокойный» вариант для личного бренда и talking-head.
Music overlay
Для финального микса — video-director overlay_music.py. Для talking-head дефолтная громкость 0.20 слишком громко над живым голосом — используем 0.08:
python ~/.claude/skills/video-director/scripts/overlay_music.py \ --video captioned.mp4 \ --music ~/video-projects/bas_dent/music/track.mp3 \ --volume 0.08 \ --output final.mp4
| Контекст | Volume | Почему |
|---|---|---|
| ElevenLabs TTS (основной пайплайн) | 0.20 | TTS-голос уже громкий и чистый, музыка может быть громче |
| Real-mic talking-head (screencast) | 0.08 | Живой голос имеет динамику, музыка должна оставлять headroom |
Aspect ratio padding
Screen Studio часто экспортирует не стандартный aspect (например 864×1080 = 4:5). Для downstream Remotion captions требуется 1080×1920, поэтому cut_and_clean.py --pad-to 1080x1920:
- Скейлит видео по ширине до 1080
- Паддит сверху и снизу чёрным до 1920 высоты
- На выходе чистый 9:16 готовый для captions
Detection tuning
detect_takes.py дефолты подобраны для «нормального» голоса в комнатных условиях. Подкручивать когда:
| Параметр | Default | Когда менять |
|---|---|---|
--silence-threshold |
-32dB |
Тихий голос → -38dB. Очень экспрессивная запись (смех, вздохи) → -28dB. |
--silence-min |
0.5s |
Минимальная длительность тишины чтобы считать за break. Меньше — больше мелких разрывов. |
--min-segment |
0.5s |
Минимальный speech-блип. Дропает клики мыши, вдохи, причмокивания. |
--whisper-model |
small |
Для коротких клипов — быстро и точно. medium если канонический сценарий длинный и сложный. |
-20dB) → тишины не детектятся, дубли сливаются. Слишком тихий (-45dB) → ложные срабатывания speech, оставит шум как «сегмент».
Cut Plan JSON формат
{
"source": "/path/to/raw.mp4",
"canonical_text": "...",
"voice_cleanup": true,
"segments": [
{"start": 46.30, "end": 49.85, "sentence_idx": 0, "take": 3, "kept": true, "text": "..."},
{"start": 21.47, "end": 24.71, "sentence_idx": 0, "take": 1, "kept": false, "text": "..."},
{"start": 30.10, "end": 33.42, "sentence_idx": 0, "take": 2, "kept": false, "text": "..."},
{"start": 52.00, "end": 56.20, "sentence_idx": 1, "take": 1, "kept": true, "text": "..."}
]
}
Для предложения sentence_idx: 0 было 3 take'а — оставлен только последний (take: 3, по времени 46.30-49.85). Остальные помечены kept: false.
--review в detect_takes.py открывает план в $EDITOR чтобы юзер вручную поправил kept: true/false перед рендером. Рекомендуется для нетривиальных записей (раздробленные дубли, частичные перезаписи). Дёшево по времени, спасает от плохих автоматических решений.
Когда использовать
- Юзер экспортировал
.mp4из Screen Studio / Loom / телефонной камеры и перезаписывал предложения по несколько раз - Есть канонический текст сценария — запись следует ему с ошибками и рестартами
- Юзер говорит: «убери паузы», «оставь последние дубли», «обработай запись», «смонтируй из записи»
Input contract:
- Raw
.mp4(любой формат, любой aspect) - Canonical script text (тот текст что юзер хотел озвучить)
Output contract:
- Чистый
.mp4где удалены тишины и более ранние дубли - Voice обработан: highpass 80Hz → afftdn → compressor → loudnorm -16 LUFS / TP -1.5
- Опционально паддинг до 1080×1920 для downstream captions
Gotchas и tips
--min-segment слишком маленький, в плане могут оказаться сегменты которые Whisper не смог распознать (вернул пустой текст). detect_takes.py их дропает автоматически, но если что-то идёт не так — глянуть JSON руками.
-19 LUFS. Для broadcast TV — -23 LUFS. Менять через loudnorm=I=-19 в voice_cleanup_chain.py.
Место в пайплайне
Альтернативный трек к основному пайплайну. Заменяет copywriter + voiceover + director — юзер сам пишет себя на камеру со своим скриптом и микрофоном.
Финальный mp4 проходит те же captions и music-overlay шаги что и TTS-вариант, но с другими параметрами (monochrome scale-pop captions, volume 0.08 для музыки). По желанию можно прогнать через video-reviewer для финального QA.