help quicky produce instagram reels and youtube shorts
80
93%
Does it follow best practices?
Impact
47%
1.95xAverage score across 1 eval scenario
Passed
No known issues
Manual recipes for when script defaults need overriding. All commands assume ffmpeg 6+.
color_transfer=arib-std-b67)iPhone default video since 13 Pro is HDR/HLG in Dolby Vision containers. Uploading HDR directly to TikTok/IG produces washed-out or blown results after their transcode — always tonemap to SDR:
-vf "zscale=t=linear:npl=100,tonemap=hable:desat=0,zscale=p=bt709:t=bt709:m=bt709:r=tv,format=yuv420p"If zscale is missing (ffmpeg built without libzimg): brew install ffmpeg includes it; fallback chain is tonemapx or -vf "colorspace=bt709:iall=bt2020:fast=1" (lower quality).
Flat log profile — footage looks gray/desaturated. Conversion options, best first:
normalize_clips.py --dlog-lut official.cube, or manually -vf "lut3d='official.cube'".--dlog-lut isn't given): assets/luts/conversion/dji-dlogm-to-rec709.cube, generated by scripts/make_luts.py from the published D-Log whitepaper math (exact D-Log→linear transfer + D-Gamut→Rec.709 matrix + ACES-fit filmic shoulder, 18% gray anchored at ~0.45 display). Accurate for D-Log; a close approximation for D-Log M (whose highlights clip earlier — the shoulder absorbs most of the difference).eq=contrast=1.35:saturation=1.4:gamma=0.92. Visibly inferior on skin tones.Note: DJI "Normal" color profile needs no treatment — only D-Log/D-Log M. Probe detection is heuristic (metadata tags); ask the user if unsure whether they shot log.
assets/luts/conversion/ — camera conversion, input must be D-Log(M). On already-Rec.709 footage (iPhone SDR, normalized mezzanine) it crushes blacks, blows highlights, and pushes colors out of gamut. This also means Resolve's LUT-browser thumbnails (Rec.709 stills) always preview conversion LUTs as blown-out neon — expected, not a defect; DJI's official LUT previews identically.assets/luts/looks/ — creative grades (reel-gritty, reel-punchy, reel-faded), Rec.709 in/out, safe on any footage: render_reel.py --lut, an ffmpeg lut3d node, or the user's NLE. In a Resolve workflow the correct stack is conversion LUT (log clips only) on node 1, look on node 2.make_luts.py)scripts/make_luts.py regenerates the bundled set (conversion + 3 looks), 33-point .cube, stdlib-only (no numpy; the colour-science library is the right tool for heavier color work, but overkill at this cube size). Look strengths are plain constants in the STYLES dict — tune and re-run.
To get an exact conversion and a branded look in one cube (one-node workflows), compose a look on top of the user's downloaded official DJI LUT (it's sampled trilinearly as the base transform; nothing of DJI's file is redistributed):
python3 scripts/make_luts.py --compose official_dlogm.cube --looks gritty --outdir work/luts
python3 scripts/normalize_clips.py work/clips.json --dlog-lut work/luts/dji-dlogm-to-rec709-gritty.cube(--combined does the same from the whitepaper base.) Caveat: baking a look into the mezzanine locks it in before Gate 1 — prefer the neutral conversion at normalize time and looks at render time (--grade/--lut), unless the user has already committed to a look.
Center-cut usually decapitates the subject on drone shots (subject often in lower third or off-center). Options:
crop=ih*9/16:ih:x_offset:0 — set pan per clip in clips.jsonzoompan=z='min(zoom+0.0008,1.15)':d=1:s=1080x1920:fps=30split[a][b];[a]scale=1080:1920,boxblur=20[bg];[b]scale=1080:-2[fg];[bg][fg]overlay=(W-w)/2:(H-h)/2A 40-minute talk or screen recording flagged by probe_clips.py should never go through normalization whole. Stream-copy the usable window out first (instant, no re-encode), keep raw/ untouched:
mkdir -p work/extracts
ffmpeg -ss 12:40 -to 13:25 -i "raw/long_event.mp4" -c copy work/extracts/long_event_moment.mp4-c copy cuts on keyframes so in/out may shift by up to a GOP (~1-2s); take a slightly wider window, then let the mezzanine re-encode trim precisely. Point START/END using the clip's contact sheet, add the extract to the probe run (probe_clips.py accepts any dir — re-probe with the extract in place of the original in clips.json).
iPhone and screen recordings are often VFR → causes A/V drift after cuts. The mezzanine step's fps=30 + re-encode fixes it. If drift persists: add -vsync cfr before output.
Phones store rotation as metadata, not rotated pixels. probe_clips.py already accounts for it (side_data rotation). If a clip renders sideways, add -noautorotate diagnosis: ffmpeg usually applies rotation automatically; problems typically mean double rotation — strip metadata with -metadata:s:v rotate=0 after transposing manually.
eq=contrast=1.08:saturation=1.12,
colorbalance=rs=-0.06:gs=-0.02:bs=0.08:rm=0.05:gm=0.01:bm=-0.05:rh=0.04:bh=-0.04,
curves=all='0/0.02 0.5/0.5 1/0.98',
vignette=PI/5:mode=backwardTuning: more teal in shadows → raise bs; warmer skin → raise rm; punchier → contrast 1.12. Keep saturation ≤1.15 to stay "clean but not sterile".
If the user grades one clip in another tool and wants the look everywhere:
ffmpeg -i reference_graded.mp4 -i same_clip_ungraded.mp4 -lavfi haldclutsrc=8,format=rgb24 ... — this workflow is fiddly; simpler to iterate the filter numbers above against a preview frame: ffmpeg -ss 3 -i clip.mp4 -vf "<grade>" -frames:v 1 test.jpg.
| Platform | Res | Max len | Video | Audio | Notes |
|---|---|---|---|---|---|
| TikTok | 1080x1920 | 10 min (287s via API) | H.264 high, <=~16 Mbps | AAC 192k, -14 LUFS | Aggressive recompress; CRF 19 is enough |
| IG Reels | 1080x1920 | 15 min | H.264 high | AAC 192k, -14 LUFS | IG recompresses hardest — feed CRF 18 |
| YT Shorts | 1080x1920 | 3 min | H.264 high, BT.709 tags explicit | AAC 192k, -14 LUFS | Wrong/missing color tags cause washed-out playback |
All three: yuv420p, +faststart, 30fps (60fps also fine if source supports it end-to-end).
ffprobe -show_streams.-safe 0 and absolute paths in list.txt.-ss before -i is fast but keyframe-snapped on stream copy; scripts re-encode segments so this shouldn't occur — if hand-rolling with -c copy, expect it.-colorspace bt709 -color_primaries bt709 -color_trc bt709.libx264 → h264_videotoolbox -b:v 12M for hardware encoding (~5-10x faster, slightly larger files, no CRF mode). Good for previews; keep libx264 CRF for masters if quality matters.loudnorm=I=-14 normalizes to platform standard; don't also normalize in the render step.No such filter: 'drawtext': Homebrew ffmpeg is built without libfreetype/drawtext. Don't build recipes on it — burn text via the subtitles filter (libass ships in the Homebrew build) with an .srt/.ass file, or leave text to the platform apps. (A --HEAD/custom-tap build has drawtext, but never assume it.)moov atom not found on a mezzanine/render: the file was read while still being written. Scripts write to .tmp.mp4 and rename on completion, so this means a job was killed mid-write (delete the .tmp and re-run) — or you're gating on file existence instead of process exit; wait for the process.