PodClipper
Give it a podcast. Get vertical reels with captions.
PodClipper transcribes your video, picks and reviews useful moments, follows the speakers in a 9:16 crop, and exports captioned MP4s. One command runs the whole podcast-to-reels flow.
podclipper "podcast.mp4"
Video and audio processing run locally. One configured LLM endpoint handles clip selection, title review, caption correction, and content scoring.
Get started
1. Install
Use Python 3.10–3.12 and install FFmpeg (which includes ffprobe):
# macOS
brew install ffmpeg
# Ubuntu / Debian
sudo apt install ffmpeg
Install from this repository:
git clone https://github.com/LoukikNaik/PodClipper.git
cd PodClipper
python3 -m venv .venv
source .venv/bin/activate
pip install -e .
After the first PyPI release is published, you can install without cloning:
# macOS: install the system tools and a supported Python
brew install ffmpeg pipx python@3.12
pipx ensurepath
pipx install --python python3.12 podclipper
Reopen your terminal if pipx ensurepath asks you to. Pipx manages PodClipper's
virtual environment for you. Upgrade later with pipx upgrade podclipper.
2. Set your API key once
podclipper init
This creates a small podclipper.yaml and a .env file. Existing files are kept.
The starter config uses the OpenAI endpoint; put your key in .env:
LLM_API_KEY=your-api-key
The same LLM_API_KEY name works for every supported host. Already using another
OpenAI-compatible endpoint? Set its endpoint and model in .env or
podclipper.yaml. See LLM configuration.
3. Make reels
podclipper "path/to/podcast.mp4"
That's the full flow. Podcast mode, vertical cropping, and classic captions
are already enabled. The CLI automatically reads podclipper.yaml and .env
from your working directory. No -c or extra mode flags are needed.
The first run downloads the local transcription and detection models. Those models are reused on later runs. Processing can take a while, especially on CPU.
Finished clips appear in a new folder:
outputs/<timestamp>/
reel_01_<title>.mp4
reel_01_<title>.txt
reel_02_<title>.mp4
reel_02_<title>.txt
MP4s are vertical 1080 × 1920 with audio and burned-in captions. Text sidecars contain source timestamps, selection reasons, and review scores. The CLI prints the saved file paths when it finishes. It aims for the configured clip count; it may export fewer when candidates are rejected or processing fails.
Everyday commands
# Run everything, asking for three clips
podclipper podcast.mp4 --clips 3
# Use pop captions instead of classic karaoke captions
podclipper podcast.mp4 --captions pop
# Choose an output location
podclipper podcast.mp4 --output-dir outputs/my-podcast
Want to review the picks before exporting?
podclipper podcast.mp4 --clips 3 --select-only
Read outputs/<timestamp>/selection.md for titles, source timestamps, full
transcript excerpts, and the opening and ending of each clip. No reels are
rendered yet. A matching selection.json is saved alongside it.
Then export those exact picks:
podclipper podcast.mp4 --selection outputs/<timestamp>/selection.json
Use the same source video. Saved titles, order, and cut boundaries are preserved; per-clip transcription, cropping, captions, and evaluation still run normally.
Small config, sensible defaults
The starter config contains only the settings most people need:
llm:
endpoint: https://api.openai.com/v1
model: gpt-5-mini
content:
clips: 8
output:
captions: classic
Change content.clips to ask for fewer or more clips. Captions can be classic,
pop, or off. Optional settings include content.duration: [15, 90] (seconds)
and output.directory: outputs.
Omitted settings inherit the packaged defaults. You can still override internal settings without copying the whole default config—for example:
crop:
stacked_transition_frames: 24
Config loading order is packaged defaults → explicit -c file or local
podclipper.yaml → environment overrides → CLI flags. Nested mappings merge; lists and scalar values
replace defaults. Explicit internal settings take precedence over their public
shortcuts. Unknown settings and invalid value types produce an error.
# See the effective config, including inherited defaults
podclipper config --resolved
# Use a different config (and load its sibling .env as a fallback)
podclipper podcast.mp4 -c another-config.yaml
LLM_ENDPOINT and LLM_MODEL in your environment override YAML settings.
Blank endpoint/model variables leave the YAML values in place.
Shell environment variables take precedence over .env; the working-directory
.env takes precedence over a config file's sibling .env. Keep API keys in
.env or your shell, rather than the YAML file.
LLM configuration
Every LLM stage uses the same OpenAI-compatible endpoint through LiteLLM. There is no Claude CLI dependency or reasoning-effort override.
Use one host, one model, and one key. For example, our OpenRouter setup can be
configured entirely in .env:
LLM_ENDPOINT=https://openrouter.ai/api/v1
LLM_MODEL=z-ai/glm-5.3-flash
LLM_API_KEY=your-api-key
Or keep the endpoint and model in YAML and only LLM_API_KEY in .env:
llm:
endpoint: https://openrouter.ai/api/v1
model: z-ai/glm-5.3-flash
There are no provider-specific key variables or api_key_env setting. Existing
setups should rename their API key variable to LLM_API_KEY and remove
llm.api_key_env from YAML. Optional provider routing:
llm:
routing:
order: [deepinfra]
allow_fallbacks: true
This prefers DeepInfra while allowing other hosts. only: [deepinfra] restricts
requests to that host. Model and provider availability depend on your endpoint.
LLM calls default to llm.max_tokens: 32768 (32k). Incomplete responses are
never accepted. The retry ceiling also defaults to 32k, so a truncated response
at that limit fails. To allow one larger retry, raise llm.retry_max_tokens
above max_tokens; the retry uses up to twice the requested budget, capped by
that ceiling. A retry can increase API cost, and your endpoint must support the
larger budget. These settings are optional overrides; they don't need to appear
in the minimal config.
What happens under the hood
- Validate the video and extract audio.
- Transcribe the podcast locally with Whisper.
- Ask the LLM for clips, refine their boundaries, and review each cut and title.
- For each accepted clip, detect speakers and transcribe again for caption timing.
- Correct caption words, crop to 9:16, and burn in captions and the title.
- Export the MP4 and a content scorecard.
Selection checks for an understandable opening, a complete ending, supported
titles, valid duration, and overlapping clips. Overlong titles get one shortening
retry. Review decisions are saved in the analysis cache's 04_final.json.
Caption correction can join wrongly split words or split wrongly joined words. Original word indices stay attached; split timing is estimated within the original word span. Invalid corrections preserve the original transcript.
Troubleshooting and advanced options
- Missing API key or FFmpeg: the CLI checks these before transcription and tells you what to set or install. This checks key presence, not whether the endpoint will accept the key or whether your account has credit.
- Config error: fix the named setting in
podclipper.yaml. Usepodclipper config --resolvedto see inherited defaults. - Need details about a failure? Add
--verbose. - First run is slow: model downloads and local video processing take time.
- Rerunning: intermediate audio, transcripts, and clips are cached in
.cache/. Stages may still run again; this isn't a resume from the exact point of failure. Use--no-cacheto rebuild intermediates.
podclipper --help # everyday podcast options
podclipper --help-all # all options, including advanced and other modes
The default transcription engine is faster-whisper (CPU/CUDA), using base
for initial transcription and large-v3 per clip. Apple Silicon users can opt
into --whisper-engine mlx; it falls back to faster-whisper if unavailable.
Whisper, YOLO, and MediaPipe models download automatically on first use.
Trailer and comedy options remain available under advanced help. The main onboarding and examples above focus on podcast reels.
Full defaults: src/podclipper/config/default.yaml. Developer notes: CLAUDE.md. Local crop iteration:
python dev/regen_crops.py .cache/<video_stem>-<hash> outputs/regen_run sidecar_dir/
Current limitations
Review exports before posting. Clip selection uses transcript text; it can miss visual moments or choose a cut that needs more context. Content scores aren't an audiovisual quality check or an independent fact check.
The latest full run also exposed occasional duplicate speaker panels and titles covering faces. Those crop/layout issues are still outstanding.
Demo reels
Three reels generated end-to-end from a single ~60-minute podcast episode. Previews loop silently below — click any tile to open the full MP4 with audio.
|
"Confidence Is an Output, Not Input" ▶ play with audio |
"Make Anxiety Sit in the Back Seat" ▶ play with audio |
"How Hanuman Knew It Was Sita" ▶ play with audio |
For full-screen playback with audio inline, visit the live demo site.
Project
The Python package is in src/podclipper/; local development scripts are in
dev/. The demo site lives in landing/ and deploys to GitHub Pages separately.
License
PodClipper is licensed under AGPL-3.0-only. See LICENSE for the full terms and NOTICE for copyright and third-party attribution.
Person detection uses Ultralytics YOLO under its AGPL license. Other dependencies and downloaded models retain their own licenses. The project source is available in this repository.
Publishing releases
See the release guide for one-time PyPI setup and the exact
commands. A new vX.Y.Z tag publishes version X.Y.Z automatically; there is
no version number to edit in the code. Existing published versions and tags
must not be reused for new fixes.
Metadata
Release files for podclipper 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| podclipper-0.1.0.tar.gz | 106.0 kB | With provenance | Details for podclipper-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| podclipper-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for podclipper-0.1.0-py3-none-any.whl |
Total release size: 219.0 kB
Release files / podclipper-0.1.0.tar.gz
| Download URL | podclipper-0.1.0.tar.gz |
|---|---|
| Size | 106.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9b1d7be6a2dc86ba6af5ffa9a67e1d0e30cd9ad82cfdcd8858556ae0b8da65f5
|
|
BLAKE2b-256 checksum How to use checksums |
4d31f2957d82cb093e4744517f642c051c5bb7138e0a846178b0704adf042fa4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.
Transparency logRelease files / podclipper-0.1.0-py3-none-any.whl
| Download URL | podclipper-0.1.0-py3-none-any.whl |
|---|---|
| Size | 113.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9d75f20db44dd49102c89ff027148e14e8e609b4a4eb29a93ff7824e05a80648
|
|
BLAKE2b-256 checksum How to use checksums |
a2d4173e7a81438e2e2aa4f6f6ceca7e4d64073a76debb2ae8b51f831ad0b3eb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.
Transparency logSecurity for podclipper 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for podclipper-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.