shorts-forge
Purpose
Turn a plain-text script into a short-form vertical video (9:16, 1080×1920)
using locally installed ffmpeg. The pipeline covers:
- Script validation and scene segmentation
- Optional voice-over prompt generation for TTS tools (ElevenLabs, piper, etc.)
- Background clip selection from a local asset library
- ffmpeg assembly with text-overlay burn-in
- MP4 output to
$OUTPUT_DIR
No cloud rendering required. CUDA acceleration is used automatically when
CUDA_VISIBLE_DEVICES is set and ffmpeg was compiled with h264_nvenc.
Runbook
Pre-flight — verify
ffmpegandpython3are available.
CheckOUTPUT_DIRis set and writable.
Optionally setASSET_DIR(default:~/.shorts-forge/assets/) for background clips and music. Populate it with royalty-free.mp4clips before first use.Validate script — run
scripts/validate-script.sh <script.txt>:- Checks total word count (target: 60–90 words for a 30–45 s short)
- Splits into scenes at blank lines; warns if any scene > 25 words
- Emits
script.jsonwith[{scene_id, text, duration_hint_s}]
Generate voice-over prompt — the validate script prints a ready-to-paste TTS prompt for each scene. Paste into your TTS tool and save output as
voiceover.mp3(optional but strongly recommended for viewer retention).Render — run
scripts/render.sh <script.json> [voiceover.mp3]:- Picks a background clip from
ASSET_DIRrandomly (or uses--bg-clip) - Burns in subtitle text per scene with
drawtextfilter - Adds
voiceover.mp3if provided, otherwise leaves silent - Encodes with
h264_nvenc(GPU) orlibx264(CPU fallback) - Output:
$OUTPUT_DIR/short_<timestamp>.mp4
- Picks a background clip from
Review output — open the MP4 before publishing. The script does not auto-upload to any platform.
Stop conditions
- Abort if
ffmpegis not inPATH. - Abort if
OUTPUT_DIRis unset or not writable. - Abort if the script exceeds 200 words — likely not a short-form script.
- Abort if
ASSET_DIRhas no.mp4clips and no--bg-clipoverride is given. - Never auto-upload to YouTube, TikTok, or Instagram — output is local only.
- Do not process scripts with URLs or shell-expansion characters unless explicitly reviewed.
Output format
Script validation (script.json)
[
{"scene_id": 1, "text": "Ever wondered how caffeine actually works?", "duration_hint_s": 4},
{"scene_id": 2, "text": "It blocks adenosine receptors, tricking your brain into staying alert.", "duration_hint_s": 6}
]
Render summary (stdout)
🎬 shorts-forge render complete
Output : /home/user/shorts/short_20260422_143012.mp4
Duration : 38 s (target: 30–45 s ✓)
Resolution: 1080×1920 @ 30fps
Encoder : h264_nvenc (GPU)
Scenes : 5
Audio : voiceover.mp3 mixed
Example invocations
scripts/validate-script.sh myscript.txt— validate and segment scriptscripts/render.sh script.json voiceover.mp3— render to MP4scripts/render.sh script.json --bg-clip nature.mp4— specify background- "Validate my script and tell me if it's the right length."
- "Render a short from script.json using my voiceover file."