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.
التثبيت
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
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/