Утилита, которая собирает вертикальное видео (1080×1920) из набора изображений. Есть GUI на Tkinter с превью и ручной подстройкой crop-offsets (для cover), а также CLI на Click для пакетных прогонов.
- Вход: набор изображений (jpg/jpeg/png/webp)
- Выход:
.mp4 (H.264 + AAC) - Режимы вписывания:
cover— заполняем кадр, лишнее обрезаем (в GUI доступныoffset_x/offset_y)fit— вписываем целиком + фон (однотонный или “fancy” размытие)
- Движение:
none— статикаkenburns— плавный zoom/pan
- Переходы:
crossfadeмежду слайдами - Аудио (опционально):
trimилиloopдо длины видео - Длительность:
sec_per(длина кадра)- или
total_duration(общая длина —sec_perпересчитывается)
python -m venv .venv
source .venv/bin/activate # macOS/Linux
# .venv\Scripts\activate # Windowspip install -r requirements.txt✅ macOS: проверено
brew install ffmpegsudo apt-get update && sudo apt-get install -y ffmpegchoco install ffmpeg⸻
python -m vv.guiЧто есть в GUI:
- выбор изображений (несколько файлов)
- опциональное аудио
- fit/cover
- длительность: либо per-frame, либо total
- transitions
- kenburns (чекбокс “Зум/сдвиг кадра”)
- справа — превью 9:16, стрелки навигации по hover
- в cover появляются слайдеры Смещение X/Y (сохраняются per-image в crop_offsets)
⸻
python -m vv.cli -i path/to/img1.jpg -i path/to/img2.png -o output/video.mp4Примеры
- Простой рендер пачки
python -m vv.cli \
-i images/ \
-o output/video.mp4 \
--sec-per 4 \
--fps 30 \
--fit-mode cover- Общая длина ролика (sec_per будет пересчитан)
python -m vv.cli \
-i images/ \
-o output/video.mp4 \
--total 60 \
--transitions- Fit + fancy background
python -m vv.cli \
-i images/ \
-o output/video.mp4 \
--fit-mode fit \
--fancy-bg- С музыкой + loop
python -m vv.cli \
-i images/ \
-a music.mp3 \
-o output/video.mp4 \
--audio-adjust loopПосмотреть полный help
python -m vv.cli --help⸻
Запуск всех тестов:
pytest⸻
- Видео пишется через libx264, аудио — aac.
- transitions=True уменьшает “эффективную” длительность каждого кадра из-за overlap (это учтено через sec_per_for_total(...) и fade_for(...)).
- В GUI offset_x/offset_y имеет смысл только в cover. В fit offsets скрываются и не применяются.
⸻
- vv/pipeline.py — сборка клипов, переходы, аудио, рендер
- vv/gui.py — Tkinter GUI, превью, offsets
- vv/cli.py — Click CLI
- vv/image.py — fit_to_canvas(...)
- vv/audio.py — prepare_audio(...)
- vv/duration.py — расчеты длительностей/фейдов
- tests/ — pytest