Skip to main content

Turn a long interview into publish-ready vertical clips: cut on sentence boundaries, reframed to 9:16, karaoke subtitles burned in.

Project description

🎬 shortsmaker

A long interview goes in. Publish-ready vertical clips come out.

Cut on sentence boundaries · reframed to 9:16 by body tracking · karaoke subtitles burned in

PyPI Python 3.12+ License: MIT

uvx shortsmaker run interview.mp4

A 15-minute extract of a podcast becomes four or five clips you could post, in about five minutes, on a laptop with no GPU. French by default (-l fr); also English, Spanish, German, Italian, Portuguese and Dutch.

Watch an example clip — 21 seconds, straight out of shortsmaker run, not retouched. It shows the three things the pipeline exists to get right: the clip opens and closes on a whole sentence, the crop holds on the speaker and jumps only where the show's own editor cut, and the caption highlights each word as it is spoken.


Install

Needs Python 3.12+, uv, and FFmpeg built with libass (the distro package is fine — apt install ffmpeg).

uvx shortsmaker run interview.mp4     # run it without installing anything
uv tool install shortsmaker           # or install the command for good

The font and the pose model are vendored inside the package: nothing else to download.

API keys

Key For
OPENAI_API_KEY Whisper transcription
GEMINI_API_KEY Choosing the segments

These are the only two stages that cost money, and they cache. Everything else runs locally and free. No keys to hand? --mock fakes both APIs with realistic data, so the whole pipeline runs offline and free — useful for trying it out, and for CI.

shortsmaker run interview.mp4 --mock

Use

shortsmaker run interview.mp4              # everything → clips/
shortsmaker run interview.mp4 -n 3         # keep only the 3 best passages
shortsmaker run interview.mp4 -f 1:1 -l en # square, English
Default
-f --format 9:16 9:16 TikTok/Reels/Shorts · 4:5 Instagram · 1:1 LinkedIn/X · 16:9 keep the original shape (no crop, tracking skipped)
-l --language fr The language spoken — and the one titles and summaries are written in.
-n --clips all Keep only the N best-scoring passages.
--min-score none Keep only passages scoring at least this. Combines with -n.
-j --jobs auto Clips processed at once.
-o --out clips/ Where the finished clips land.
-v --verbose off Show the ffmpeg commands and the raw API traffic.
--mock off Fake the APIs. No key, no cost.

--force has no short flag on purpose: it re-runs the paid stages, so it should cost a moment's thought.

Or one stage at a time. Only the first two cost money, so you can re-run the last ones as often as you like while tuning the look:

shortsmaker transcribe interview.mp4        # Whisper   → transcript.json   (the clock)
shortsmaker analyze    interview.mp4        # Gemini    → analysis.json     (paid, kept whole)
shortsmaker show       interview.mp4        # print what was found, with scores
shortsmaker reframe    interview.mp4        # MediaPipe → the crop plan
shortsmaker render     interview.mp4        # ffmpeg    → clips/

shortsmaker render interview.mp4 --size 96 --accent "#00E5FF"   # restyle, free
shortsmaker analyze interview.mp4 --min-score 0.8               # change your mind, free

Choosing which passages to keep costs nothing. analyze writes everything it found and never narrows it; -n and --min-score select from that, without a second API call.

shortsmaker --help on anything — the defaults carry their reasoning.

As a library

Importing it does not turn it into a CLI: nothing writes to stdout, and the stages log through the standard logging module, silent until your application adds a handler.

from shortsmaker import Paths, Settings
from shortsmaker.stages import analyze, transcribe

transcript = transcribe.transcribe(paths, Settings())
segments = analyze.analyze(paths, transcript, Settings(), clips=3)

See the package docstring for the full loop.


How it works, and why

SPEC.md — the flowchart, and the reasoning stage by stage. Four spikes were built and measured before any of this was written, and nearly every rule in the code replaced one that made the clips worse. The three decisions worth knowing:

Gemini decides what to cut. Whisper decides when. Gemini picks the right moments but cannot place them in time — its timestamps drift by ~2 s, and one run answered in MM.SS while the prompt demanded seconds, producing five 1-second clips that passed every check in silence. So it is never asked for timestamps: it quotes the words, and the quote is looked up in Whisper's word-level transcript. Bounds land on real word boundaries by construction.

Gemini gets the audio, never the video. The source is a talking head. The frames carry nothing the audio doesn't, but they dominate the token bill. What decides whether a Short lands — a laugh, the pause before a punchline — is in the audio, and Gemini hears it natively.

The reframe tracks bodies, and never guesses who is speaking. Face detection missed the guest on a wide shot (in profile, head down) and hallucinated a box on the background. Bodies don't vanish in profile. And when two people are in frame, we show both rather than pick one.

Performance

Measured on a 16-core CPU, no GPU: reframe 0.71× realtime, render 0.45×, so local processing runs at about 1.2× realtime. Transcription and analysis are network-bound — roughly 90 seconds for a 15-minute video.


Development

uv sync                                     # runtime deps + pytest
uv run pytest                               # tests

uvx ruff@0.15.21 check .                    # lint
uvx ty@0.0.58 check                         # types
uvx bandit@1.9.4 -c pyproject.toml -r src   # security

These are, verbatim, the four commands CI runs — see ci.yml. Green here means green there. The conventions they enforce, and the traps worth knowing before changing anything, are in CLAUDE.md.

To cut a release, see PUBLISHING.md. The short version: push a tag.


Licence and credits

The code is MIT — see LICENSE.

The example clip in docs/ is not. It is a short excerpt of an interview with Bernard Arnault from the show Legend (Guillaume Pley), reproduced solely to illustrate what the tool outputs. All rights remain with its owners; it is not covered by this project's licence and is not redistributable as part of it.

The vendored assets carry their own licences: Lato (Łukasz Dziedzic, SIL Open Font License 1.1) and MediaPipe's pose_landmarker (Google, Apache 2.0).

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

shortsmaker-0.1.1.tar.gz (24.1 MB view details)

Uploaded Source

Built Distribution

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

shortsmaker-0.1.1-py3-none-any.whl (5.3 MB view details)

Uploaded Python 3

File details

Details for the file shortsmaker-0.1.1.tar.gz.

File metadata

  • Download URL: shortsmaker-0.1.1.tar.gz
  • Upload date:
  • Size: 24.1 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for shortsmaker-0.1.1.tar.gz
Algorithm Hash digest
SHA256 c5ae59d93737c02446a16cea79cf0ce8777fd9f7581c6dbe710374e16e6b76f3
MD5 eb04ada0666535cec1320d1d830cd57d
BLAKE2b-256 a9882815663335d5e089948fdb16d00e54a1a0839b3463f1e21731bab1d0ef3c

See more details on using hashes here.

Provenance

The following attestation bundles were made for shortsmaker-0.1.1.tar.gz:

Publisher: publish.yml on pirocheto/shortsmaker

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file shortsmaker-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: shortsmaker-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 5.3 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for shortsmaker-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 7ce017ea6763ee201047c36d22ee2b3d798e1827c69683fe0a9ea6a8d6a218bb
MD5 a9728d4051c491651f2bea9647617412
BLAKE2b-256 8572b5d884273d2a76548272a2e38ca1d0d1f4c1eba868fc2de533ae36a10ad9

See more details on using hashes here.

Provenance

The following attestation bundles were made for shortsmaker-0.1.1-py3-none-any.whl:

Publisher: publish.yml on pirocheto/shortsmaker

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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