Edit, trim, concat, convert, compress, or otherwise transform video files from the terminal using ffmpeg. Use when [OWNER] says "video-editor", "edit video", "cut video", "ffmpeg edit", "render video", "trim video", "concat video", "compress video", "extract audio", "burn subtitles", "make a gif from video", or any other one-off video task. Picks the correct ffmpeg recipe from a 20-pattern library, executes it, verifies the output with ffprobe, and stores the result in the right `assets/` folder.
Installation
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Edit, trim, concat, convert, compress, or otherwise transform video files from the terminal using ffmpeg. Use when [OWNER] says "video-editor", "edit video", "cut video", "ffmpeg edit", "render video", "trim video", "concat video", "compress video", "extract audio", "burn subtitles", "make a gif from video", or any other one-off video task. Picks the correct ffmpeg recipe from a 20-pattern library, executes it, verifies the output with ffprobe, and stores the result in the right `assets/` folder.
argument-hint
describe the operation: e.g. 'trim input.mp4 from 00:10 to 00:30', 'concat clips/*.mp4', 'extract audio from talk.mkv', 'compress demo.mp4 under 25MB'
Standard ffmpeg recipe library + execution harness for the terminal. Created after the 2026-05-29 CKIS demo reel needed three precise cuts (00:16–01:03 + 01:20–01:40 + 03:10–03:43) concatenated into one web-friendly MP4 — the multi-clip filter_complex pattern was rebuilt from scratch in chat instead of being a one-line skill invocation. This skill captures the 20 most common terminal video operations so the next edit takes seconds, not a debugging session.
━━━
Scope
This skill handles single-machine, single-pass video edits using the local ffmpeg binary. It covers:
Trimming, cutting, and multi-clip highlight reels
Concatenation (with and without re-encoding)
Format conversion, resizing, framerate changes
Audio: extract, replace, mute, normalize
Speed changes (slow-mo, time-lapse)
Overlays: text, watermarks, subtitles (burn-in and soft)
Two-pass encoding for strict size targets
CRF quality tuning
GIF creation, thumbnail extraction
Vertical crop for social platforms (9:16)
Do NOT touch:
.obsidian/ folder
Source videos in user-protected locations (~/Videos/, ~/Documents/) without explicit confirmation
Existing rendered outputs without backup if they took >5 minutes to produce
Files larger than 5 GB without confirming disk space first
━━━
Pre-conditions
Before running, verify:
ffmpeg and ffprobe are installed (command -v ffmpeg)
Input file(s) exist and are readable
Output directory exists or can be created (default: assets/<type>/)
Disk has at least 2× the input size free (re-encoding can double temporarily)
Decision tree — pick re-encode vs stream copy:
Stream copy (-c copy, fast, lossless) when: changing container only (MKV→MP4), trimming on keyframe boundaries, concatenating same-codec files, swapping audio without changing video
Output — absolute path, default to assets/<type>/<descriptive-name>.<ext>
Constraints — size limit, target resolution, quality preference, time range
If any required piece is ambiguous, ask [OWNER] before running ffmpeg.
━━━
Phase 2 — Select the Recipe
Match the parsed task to one of the 20 recipes below. If two recipes apply, prefer the simpler one (stream copy over re-encode, single-pass over two-pass).
━━━
Recipe Library
1 · Highlight reel — multi-clip filter_complex
When: Multiple non-contiguous segments from one source file → single output.
setpts=PTS-STARTPTS resets each segment's timeline to 0 so concat sees clean inputs. concat=n=N:v=1:a=1 joins N video+audio pairs. Add/remove [vN][aN] blocks symmetrically and update n=.
Reference: scripts/edit-ckis-demo-video.sh.
━━━
2 · Simple trim — single clip, single span
When: Cut one continuous segment from a longer source.
# Fast trim on keyframe (no re-encode, may snap to nearest keyframe)
ffmpeg -y -ss 00:00:10 -to 00:00:30 -i "$INPUT" -c copy "$OUTPUT"# Precise trim (re-encode for exact frame accuracy)
ffmpeg -y -i "$INPUT" -ss 00:00:10 -to 00:00:30 \
-c:v libx264 -crf 20 -preset medium -c:a aac -b:a 128k "$OUTPUT"
Put -ss/-tobefore-i for fast seek (decoder skips upstream). Put them after-i for frame-accurate seek (decoder reads through). Use HH:MM:SS.ms or raw seconds.
━━━
3 · Concat without re-encoding — same-codec files
When: Joining files that share codec, resolution, framerate, audio params.
-preset trades encode speed for compression efficiency: ultrafast < superfast < veryfast < faster < fast < medium (default) < slow < slower < veryslow. Each step roughly doubles encode time for ~5% smaller files at same CRF.
-pix_fmt yuv420p is mandatory for browser/QuickTime playback (some screen captures are yuv444p and silently fail to play). -movflags +faststart moves the moov atom to the file head for instant streaming.
━━━
19 · Vertical crop for social — 9:16 from 16:9
When: Repurpose horizontal content for TikTok / Reels / Shorts.
# Center crop to 9:16 (assumes 1920x1080 → 608x1080)
ffmpeg -y -i "$INPUT" -vf "crop=ih*9/16:ih" \
-c:v libx264 -crf 22 -preset medium -c:a copy "$OUTPUT"# Blurred background, original centered (full-frame 1080x1920 with cinematic look)
ffmpeg -y -i "$INPUT" -filter_complex "
[0:v]scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920,boxblur=20:5[bg];
[0:v]scale=1080:-2[fg];
[bg][fg]overlay=(W-w)/2:(H-h)/2
" -c:v libx264 -crf 22 -preset medium -c:a copy "$OUTPUT"
The second pattern (blurred background) is the "Instagram standard" look — original aspect preserved with brand-color/blurred fill.
━━━
20 · Normalize audio levels — loudnorm
When: Multiple clips need consistent loudness (broadcast / podcast standard).
Duration matches expected (within ±0.5 s for trim/concat operations)
Video codec / resolution matches request
Audio codec present (or correctly absent for -an cases)
File size is reasonable (flag if 10× expected — likely a config error)
For two-pass / size-targeted encodes, verify size is at or below the requested ceiling.
━━━
Phase 5 — Store Output
Default destinations by output type:
Output type
Default folder
Demo / highlight reel
assets/demo/
Screen recording
assets/recordings/
Thumbnail / poster
assets/thumbnails/
GIF
assets/gifs/
Extracted audio
assets/audio/
Subtitled version
assets/subtitled/
Social cut (9:16)
assets/social/
Filename convention: <topic>-<operation>-<YYYY-MM-DD>.<ext> (e.g. ckis-demo-cut-2026-05-29.mp4). If [OWNER] provides a name, use it verbatim.
If the operation was a one-off command (not a saved script), no further action. If the operation will recur (e.g. weekly highlight reels), offer to save it as a script under scripts/.
━━━
Report Format
━━━ Video Edit — {YYYY-MM-DD HH:MM} ━━━
Operation : {recipe name}
Input : {path} ({size}, {duration}, {codec})
Output : {path} ({size}, {duration}, {codec})
Recipe : #{N} — {title}
Encode : {CRF N preset X} or {two-pass ${kbps}k} or {stream copy}
Duration : ffmpeg ran in {Ns}
Verify:
- Duration delta: {±s vs expected}
- Codec: {OK | MISMATCH}
- Size: {actual vs target}
- Playback: {OK based on ffprobe}
Stored at: {final path}
━━━
Examples
Example 1 — [OWNER] says "cut video $HOME/Videos/talk.mkv from 5:30 to 6:45":
Phase 5: Output at assets/demo/ckis-demo-cut.mp4. Offers to save as scripts/edit-ckis-demo-video.sh for re-runs.
━━━
Troubleshooting
ffmpeg not found → install: sudo apt install ffmpeg (Debian/Ubuntu), brew install ffmpeg (macOS), winget install ffmpeg (Windows). Verify with ffmpeg -version.
Output plays but has no audio → input might be variable-framerate or have multiple audio tracks. Run ffprobe -i "$INPUT" to inspect streams, then use -map 0:v:0 -map 0:a:0 to explicitly select stream indices.
"Too many packets buffered for output stream" → mismatched stream timing. Add -async 1 or -vsync 1 to force sync. For severe drift, re-encode with -r 30 to normalize framerate.
Filter syntax errors with quotes → in filter_complex, prefer single quotes for the outer wrapper and double quotes inside. Escape commas in filter args with \,. For multi-line filters, ensure no spaces around ; separators.
Two-pass produces wrong size → check that pass 1 log file (/tmp/ff2pass-0.log) was created and is non-empty before pass 2. Pass 1 failure silently makes pass 2 fall back to single-pass behavior.
Output won't play in browser → likely missing -pix_fmt yuv420p (most browsers reject yuv444p) or -movflags +faststart (browser can't start streaming until moov atom is read). Always include both for web output.
libx265 output massive → x265 with default settings is verbose. Add -preset slow -x265-params log-level=error and lower CRF by 6 vs your x264 target.
Concat without re-encode fails → input files must share codec, resolution, framerate, pixel format, audio codec, audio sample rate. Check all with ffprobe. If any differ, use Recipe 1 (filter_complex concat) which re-encodes.
Drawtext "Cannot find a valid font" → install the font or specify full path with fontfile=. Common Linux fonts at /usr/share/fonts/truetype/dejavu/.
━━━
QA Checklist
The skill run is complete when:
Operation type identified and confirmed with [OWNER] (if ambiguous)
Correct recipe selected from library (or hybrid built from documented patterns)
ffmpeg command executed with exit code 0
Output file exists at expected path
ffprobe verification passed (duration, codec, size all within tolerance)
No source file modified or overwritten
No .obsidian/ modifications
Report delivered with input/output stats + verify results
If operation is recurring, offered to save as a script under scripts/