Skip to main content

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:

  1. 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.
  2. 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).
  • --size defaults to the first video clip's resolution, falling back to 1280×720.
  • --audio lays 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:

  1. Create the project / reserve the name open-montage on PyPI, and generate an API token (PyPI → Account settings → API tokens).
  2. 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

open_montage-0.3.0.tar.gz (15.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

open_montage-0.3.0-py3-none-any.whl (13.3 kB view details)

Uploaded Python 3

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

Hashes for open_montage-0.3.0.tar.gz
Algorithm Hash digest
SHA256 0dd1199e926c72d244a975d33ce9d4169f9e91e6463a145f957905376cbd8730
MD5 042b7ec0db087dff186bb11b1125f48f
BLAKE2b-256 1ed69c7dd3ea8712b926398b73a7c8df2cd1052d625f7f3ccda8c0ae20c55a21

See more details on using hashes here.

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

Hashes for open_montage-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c69f73e400c377846b97fca63b1180e03c5b294264f02effef646a2fa614ff12
MD5 9fdad1925ee159fd03a4e07b1abbd363
BLAKE2b-256 2d822c38b594a0789a39dfa214516bf81ce08e96203386fd87a4ac426d652ea9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page