Video Repurposing
SkillMediaTurn long-form video into platform-ready clips - LinkedIn posts, TikTok/Reels, teasers, highlight reels - with word-level captions, using FFmpeg and Remotion. USE THIS SKILL WHEN the user wants to repurpose a YouTube video or any long video into social clips, mentions video, clip, footage, mp4, mov, or any video format, wants to cut, stitch, combine, or edit video files, needs captions, subtitles, or TikTok-style word highlighting, wants a teaser, trailer, or highlight reel, asks about video transcription or whisper, mentions FFmpeg, ffprobe, or Remotion errors, or has .mp4/.captions.json files or video folders. Runs a mandatory intake, proposes an edit plan with exact timestamps, previews in Remotion Studio, and renders only on approval.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Video Repurposing skill
What this skill tells your AI
The instructions your AI receives, as published by naveedharri/benai-skills in shared-skills/video-repurposing/SKILL.md and read by ahel’s review.
Turns long-form video into platform-ready clips with captions. FFmpeg for fast CLI operations, Remotion for styled compositions you can preview before rendering. Battle-tested on a real LinkedIn publishing pipeline; the rules below exist because breaking them produced visibly worse clips.
Rules (Always Enforce)
Opening Clip (CRITICAL):
- The first clip is the hook - it must grab attention immediately
- Must start at the start of a complete sentence or idea with an explicit subject
- NEVER start with joining words ("But", "And", "So", "Now", "Then", "However")
- NEVER start with pronouns without context ("It", "This", "That", "They", "These", "There")
- NEVER start with filler, hedging, or weak openers ("I think", "You know", "Basically")
- Review the full transcript and deliberately select the strongest possible opening - a hook, an insight, a bold statement, or a compelling question
Closing Clip (CRITICAL):
- Must end at the natural conclusion of a thought - never trailing off, never a hard stop mid-idea
- Should leave the viewer with a takeaway, a call-to-action, or a sense of closure
- If the last word sounds like it leads into something else, keep going until it resolves
- Good endings: a summary statement, a strong opinion, a forward-looking insight
- Bad endings: trailing off, abrupt cuts, filler, unrelated tangents
Clean Cut Points (every cut, no exceptions):
- Cuts happen at a natural pause in the content - never mid-sentence, never mid-word
- Respect the idea, not just the words: a cut is clean when the speaker just finished a complete thought and there is a natural breath after the last word
- Every clip must be a complete, standalone thought with an explicit subject
- The stitched video must tell a coherent story, not feel like random fragments
Editing modes - pick ONE in intake:
- Hard-cut mode (DEFAULT for social clips: LinkedIn, TikTok, Reels). No transitions. Ever. No fades on video or audio, no crossfades, no dissolves. Clip ends on one frame, the next begins on the very next frame. Audio starts and stops at full volume. Clean hard cuts read as confident and native on social feeds; fades read as template-ware. Build with plain
<Sequence>blocks +premountFor={60}(see Composition Rules below). Do not useTransitionSeries- it has a null-props bug that crashes Studio in current Remotion versions. - Crossfade mode (optional, for trailers/teasers/montages when the user explicitly wants blended transitions). NEVER allow overlapping audio noise: cosine curves only (audio fade-out 1.5s, fade-in 1.2s, visual crossfade 1s), no fade-in on the first clip, no fade-out on the last. See references/transitions.md for the
createVolumeFnpattern and the TransitionSeries bug workaround.
Transcription-first timestamps (CRITICAL - the single source of truth):
- All clip boundaries come from whisper word-level timestamps, never from scrubbing or guessing
startMs/endMs= whisper timestamps of the first and last spoken words → these define the captions- Audio/video cut points DERIVE from those:
audioEndMs = endMs + audioBufferMsper clip. Whisper timestamps run ~200-700ms ahead of actual speech, so without the buffer the last word gets clipped - Punctuation tokens (".", ",") have timestamps but are NOT spoken - never use them as
endMs - Captions are filtered by the raw transcript boundaries, NOT the buffered audio end, so words from the next sentence never flash on screen during the buffer zone
- Always verify by ear in Studio: the last word resolves fully, the next sentence doesn't bleed in
- Full pattern with code in references/transcription.md
Source video prep (before anything else):
- Check the codec with ffprobe. AV1 is too slow to decode in Remotion Studio (YouTube often serves AV1 by default) - always transcode to H.264
-movflags +faststartis mandatory: without it the moov atom sits at the end of the file and Remotion reads the whole file before it can seek, making Studio crawl. Already-H.264 files remux losslessly:ffmpeg -i in.mp4 -c copy -movflags +faststart out.mp4- Videos go DIRECTLY into
public/with no spaces in filenames
Workflow:
- ALWAYS complete Phase 2 (Intake) before making any edits - never skip clarification questions
- ALWAYS propose an edit plan with exact timestamps and get user approval before executing
- ALWAYS preview in Remotion Studio before rendering
- ALWAYS run QA checks before showing the preview (see references/qa-testing.md)
- NEVER render automatically - only when the user explicitly approves
Brand kit (one-time, per user)
All styled output (captions, title cards, graphics) uses the user's brand, captured once in intake and reused forever:
| Setting | What to ask | Default if they have no preference |
|---|---|---|
| Colors | brand dark, background/light, 1-2 accents | dark #0A0A14, light #F5F2EA, accent #B8E0B0 |
| Fonts | heading font (Google Fonts name) | Inter (via @remotion/google-fonts) |
| Caption style | frosted pill (default) or bold pill | frosted |
| Background rule | never pure #000 black - use the brand dark | applies to everyone |
Store the answers in the Remotion project (a BRAND constant or src/lib/brand.ts) so every composition reads from one place. All code in the references uses example values - swap in the user's kit.
Workflow: 5 Phases
Phase 1: SETUP ──→ Phase 2: INTAKE ──→ Phase 3: EDIT PATH ──→ Phase 4: ITERATE ──→ Phase 5: RENDER
(one-time) (mandatory) (copy-first OR (feedback loop) (user approves)
video-first)
Phase 1: SETUP (one-time)
If a Remotion project already exists, skip to Phase 2.
- Check prerequisites:
which ffmpeg || brew install ffmpeg; Node 18+ - Create the Remotion project per references/remotion-setup.md (Remotion
^4.0.242, React^18.x). Complete working code: references/reference-implementation.md - Set up whisper: copy
scripts/setup-whisper.tsandscripts/transcribe.tsfrom THIS SKILL'sscripts/folder into the project'sscripts/, then runnpx ts-node scripts/setup-whisper.ts(installs whisper.cpp + base.en model into.whisper/, GPU-accelerated)
Phase 2: INTAKE (mandatory - never skip)
Ask before any editing. Do not proceed without clear answers.
Intent & Goal: What's the goal (LinkedIn clip, teaser/trailer, highlight reel, full edit, another platform)? Who is the audience? Video Source: Where is the video (YouTube URL, local file, folder of clips)? Existing copy/script, or discover from the video? Format & Duration: Target duration? Output format (16:9, 9:16, 1:1)? Polish level? Editing mode: Hard cuts (default for social) or crossfades (trailers/montages)? Brand kit: Collect it if this is the first session (table above); otherwise reuse. Content: Specific moments to include or exclude? Reveal the ending or create curiosity?
Then route: has copy → Phase 3A; no copy → Phase 3B.
Phase 3A: COPY-FIRST (user has a script)
- Download/copy video into
public/(prep rules above: H.264 + faststart, no spaces) - Analyze with ffprobe (references/video-analysis.md)
- Transcribe:
npx ts-node scripts/transcribe.ts(references/transcription.md) - Map script sections to transcript timestamps
- Propose cuts aligned to the copy - exact timestamps, the spoken words at each boundary, and reasoning
- User approves before any editing
Phase 3B: VIDEO-FIRST (no script - discover the story)
1-3. Same prep as 3A 4. Analyze the transcript: hooks, insights, quotable complete thoughts 5. Suggest narrative structure per the goal; create copy/captions from selected segments 6. Propose the edit plan - clips, timestamps, spoken words at boundaries, reasoning 7. User approves before any editing
Phase 4: PROPOSE, PREVIEW & ITERATE (loop)
- Present edit plan → get approval
- Build/update the Remotion composition (conventions below)
- Run QA (references/qa-testing.md)
- Preview in Studio (
npm run dev) - show the user only after QA passes - Feedback → refine → loop. Users commonly adjust: clip selection, caption style, mode, duration, format
references/intelligent-editing.md has per-task-type questions and quality checks.
Phase 5: RENDER
Only on explicit approval. Social-quality render:
npx remotion render CompositionName out/video.mp4 --video-bitrate 50M --audio-bitrate 320k
Composition Rules
- One composition per project video, in
src/compositions/<platform>/ - Clip config lives in a clearly marked
CONFIGblock at the top - the only place to edit. Document every timestamp with the actual spoken words as a comment - Export duration as a constant; register every composition in
src/Root.tsx
The hard-cut pattern (default):
const CLIPS = [
{
label: 'Hook',
startMs: 148_920, // "The reason most automations fail..." (whisper timestamp of first word)
endMs: 238_340, // "...and that's what makes it work." (whisper timestamp of last spoken word)
audioBufferMs: 200, // per-clip: small when the next sentence starts fast, up to 700 when there's a gap
},
] as const;
const clips = CLIPS.map((c) => {
const audioEndMs = c.endMs + c.audioBufferMs;
return {...c, audioEndMs, startFrame: msToFrames(c.startMs), endFrame: msToFrames(audioEndMs),
duration: msToFrames(audioEndMs - c.startMs)};
});
// premountFor={60} is MANDATORY on every Sequence - it preloads the next clip 2s early.
// Without it OffthreadVideo hasn't decoded the next clip's first frame and you get a black flash at the cut.
<Sequence from={0} durationInFrames={clips[0].duration} premountFor={60}>
<OffthreadVideo src={staticFile('source_h264.mp4')} startFrom={clips[0].startFrame} endAt={clips[0].endFrame} />
</Sequence>
Total duration = sum of clip durations. No overlap, no subtraction.
Captions: never import the captions JSON (bloats the bundle, kills hot reload) - fetch(staticFile(...)) with delayRender/continueRender, pre-filtered to clip ranges. Full pattern + the frosted-pill recipe: references/captions.md.
Platform specs
| Platform | Resolution | FPS | Max duration | Notes |
|---|---|---|---|---|
| 1920x1080 (16:9) | 30 | 10 min | H.264 MP4; native-feeling hard cuts | |
| TikTok / Reels / Shorts | 1080x1920 (9:16) | 30 | 60-90s | word-highlight captions carry sound-off viewing |
| Square feed | 1080x1080 (1:1) | 30 | - |
Tool Selection
| Task | FFmpeg | Remotion |
|---|---|---|
| Stitching | Same codec, no styling | Anything styled or previewed |
| Captions | SRT burn-in | TikTok-style word highlighting |
| Teasers | Quick cuts | Text overlays, branded elements |
FFmpeg = fast CLI, batch, format conversion. Remotion = styled content, preview-before-render, React.
Reference Files
Read as needed:
| File | Contents |
|---|---|
| references/intelligent-editing.md | Editing workflow, questions by task type, quality checks |
| references/video-analysis.md | ffprobe analysis |
| references/transcription.md | Whisper transcription + the timestamp-drift/audioBufferMs pattern |
| references/qa-testing.md | QA tests before user preview |
| references/stitching.md | Combining clips (hard-cut default) |
| references/transitions.md | Crossfade mode + TransitionSeries bug workaround |
| references/captions.md | Word-level captions, frosted pill style, async loading |
| references/teasers.md | 30-second teasers/trailers |
| references/title-cards.md | Chapter headers, bumpers |
| references/graphics-generation.md | Thumbnails, overlays, social graphics |
| references/ffmpeg-basics.md | FFmpeg patterns, faststart/moov, AV1 handling |
| references/remotion-setup.md | Project setup |
| references/remotion-tips.md | Animations, timing, springs |
| references/reference-implementation.md | Complete working project code |
Key Lessons (field-tested)
- Transcription:
@remotion/install-whisper-cpp, NOT Python whisper (10x faster, Metal GPU).base.enfor speed,medium.enfor final quality.whisperCppVersion: '1.5.5'andtokenLevelTimestamps: trueare required - Whisper drift: timestamps run 200-700ms ahead of audio - per-clip
audioBufferMsalways, verify by ear - Black flash between clips = missing
premountFor={60} - Studio loading slowly = moov atom at the end of the file - remux with
+faststart - Studio crash on transitions =
TransitionSeriesnull-props bug - use plainSequencehard cuts - Teasers: 3-4 clips totaling 25-30s; 1.3x playback for energy (1.5x is too fast); first teaser clip must NOT overlap the intro start
- Captions: pill container behind text beats stroke/shadow for legibility on any footage; same colors on all clips so contrast normalizes
Signals
- GitHub stars
- 61
- Forks
- 26
- Last commit
- Sep 2026
ahel review
K1binfo
installs-packagesK6low
bundled executables the agent is told to runK1binfo
installs-packages (in references/ffmpeg-basics.md)K1binfo
installs-packages (in references/graphics-generation.md)K1binfo
installs-packages (in references/reference-implementation.md)K1binfo
installs-packages (in references/remotion-setup.md)K1binfo
installs-packages (in references/teasers.md)K1binfo
installs-packages (in references/transcription.md)
Automated review, not a security audit. Ruleset v1+k2.
Advanced
- Catalog kind
- skill
- Gateway key
video-repurposing- Source
- github.com/naveedharri/benai-skills