Describe media montages and render them into real movies.
Project description
Open Montage
A Python toolkit to describe media montages and render them into real movies.
Open Montage has two layers:
- A tiny, dependency-free model — build a montage out of clips and read back its timeline (the absolute start/end time of every clip), serialize it to JSON.
- A video renderer — turn that montage into an actual MP4: still images are held for their duration, video clips are trimmed and stitched together, with an optional audio track. Rendering uses MoviePy, which ships its own ffmpeg, so no system ffmpeg install is needed.
v0.3.0, part of the BAS-More software library.
Install
pip install -e ".[render]" # model + video renderer
pip install -e ".[dev,render]" # everything, incl. test/lint tools
pip install -e . # model + CLI only (build/info), no rendering deps
The core model has no third-party dependencies; only the renderer pulls in MoviePy + ffmpeg.
Make a movie
# 1. Describe the montage (3 sources, 1.5s each)
open-montage build scene1.png clip.mp4 scene2.jpg --duration 1.5 --title "Reel" -o reel.json
# 2. Render it to a video file
open-montage render reel.json -o reel.mp4 --fps 24 --size 1280x720 --audio music.mp3
# Wrote 3 clip(s) → reel.mp4 (4.5s)
# ...with effects: letterbox, crossfades, slow zoom, a title card and captions
open-montage render reel.json -o reel.mp4 \
--size 1920x1080 --fit contain --transition 0.5 \
--ken-burns --title "Holiday" --captions
- Images (
.png .jpg .jpeg .bmp .gif .webp .tif) are shown as stills for their duration. - Videos (
.mp4 .mov .avi .mkv .webm .m4v .mpg) are trimmed to their duration (or used whole if shorter). --sizedefaults to the first video clip's resolution, falling back to 1280×720.--audiolays a track under the whole montage, trimmed to its length.
Effects
| Flag | What it does |
|---|---|
--fit contain|cover|stretch |
Fit clips to the frame: letterbox (default), fill-and-crop, or stretch. |
--transition SECONDS |
Crossfade between consecutive clips. Must be shorter than the shortest clip. |
--ken-burns |
Slow centered zoom on still images. |
--title TEXT / --title-duration S |
Show a title card before the montage. |
--captions |
Overlay each clip's label as a caption along the bottom. |
From Python:
from open_montage import Clip, Montage, render_montage
montage = (
Montage(title="Reel")
.add(Clip("scene1.png", duration=1.5, label="Sunrise"))
.add(Clip("clip.mp4", duration=3.0, label="Harbour"))
)
render_montage(
montage, "reel.mp4",
fps=24, size=(1920, 1080),
fit="contain", transition=0.5, ken_burns=True,
title="Holiday", captions=True, audio="music.mp3",
)
Model & inspection (no rendering deps)
from open_montage import Clip, Montage
montage = (
Montage(title="Holiday")
.add(Clip("beach.jpg", duration=2.0, label="Beach"))
.add(Clip("sunset.mp4", duration=4.0))
)
print(montage.total_duration) # 6.0
for segment in montage.timeline():
print(segment.clip.source, segment.start, "->", segment.end)
montage.to_json() # serialize to a JSON document
Montage.from_json(text) # ...and back again
open-montage info reel.json
# Reel — 3 clip(s), 4.5s total
# 1. scene1.png [0s → 1.5s]
# ...
You can also run the CLI as a module: python -m open_montage ....
Project layout
src/open_montage/
__init__.py # public API (Clip, Montage, Segment, render_montage, ...)
montage.py # pure domain model — no I/O, no dependencies
render.py # video rendering via MoviePy/ffmpeg (optional [render] extra)
cli.py # argparse command-line interface
__main__.py # `python -m open_montage`
tests/ # pytest suite
Development
pip install -e ".[dev,render]"
ruff check .
pytest
Render tests that encode video are skipped automatically if the render extra
isn't installed.
Publishing
Releases are built and published to PyPI by
.github/workflows/release.yml when a v* tag
is pushed:
git tag v0.3.0
git push origin v0.3.0
The workflow builds an sdist + wheel, runs twine check, and uploads to PyPI.
Authentication uses a PyPI API token, configured once:
- Create the project / reserve the name
open-montageon PyPI, and generate an API token (PyPI → Account settings → API tokens). - Add it to this repo as a secret named
PYPI_API_TOKEN(Settings → Secrets and variables → Actions → New repository secret).
Then publishing happens automatically on the next v* tag. (Prefer keyless
Trusted Publishing instead? Drop the
with: password: line from the publish step and configure a Trusted Publisher
for this repo + release.yml.)
Build locally to sanity-check before tagging:
python -m pip install build twine
python -m build && python -m twine check dist/*
License
MIT © BAS-More
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file open_montage-0.3.0.tar.gz.
File metadata
- Download URL: open_montage-0.3.0.tar.gz
- Upload date:
- Size: 15.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0dd1199e926c72d244a975d33ce9d4169f9e91e6463a145f957905376cbd8730
|
|
| MD5 |
042b7ec0db087dff186bb11b1125f48f
|
|
| BLAKE2b-256 |
1ed69c7dd3ea8712b926398b73a7c8df2cd1052d625f7f3ccda8c0ae20c55a21
|
File details
Details for the file open_montage-0.3.0-py3-none-any.whl.
File metadata
- Download URL: open_montage-0.3.0-py3-none-any.whl
- Upload date:
- Size: 13.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c69f73e400c377846b97fca63b1180e03c5b294264f02effef646a2fa614ff12
|
|
| MD5 |
9fdad1925ee159fd03a4e07b1abbd363
|
|
| BLAKE2b-256 |
2d822c38b594a0789a39dfa214516bf81ce08e96203386fd87a4ac426d652ea9
|