Video clipper
Repurposes long-form video (podcasts, interviews, talks) into short-form vertical clips for Instagram Reels, TikTok, and YouTube Shorts.
How to use it
- Hit Copy SKILL.md — or use the Claude Code line below to get every file.
- Claude: ⋯ → Download .md, then Customize → Skills → Add → Upload skill.
ChatGPT: make a Project and paste it into Instructions.
Neither? Paste it at the top of a new chat — it works for that chat. - Describe your job in plain words. The AI follows the skill from there.
npx degit gooseworks-ai/goose-skills/skills/design/packs/video-production/video-clipper#main ~/.claude/skills/video-clipperFor one project only, change the path to .claude/skills/video-clipper. This skill also uses transcript.json, transcript.txt, r.json, captions.md, summary.md — copying SKILL.md alone won't be enough. See the folder on GitHub.
Not working?
- Check which app you pasted it into — the steps above name the right one.
- Some skills need the paid tier of Claude or ChatGPT.
Paste into Claude, ChatGPT or Cursor.
Show the full text371 lines
Video Clipper
Takes a long-form video and produces ready-to-post short-form vertical clips with speaker-tracked framing and professional animated captions. Works with podcasts, interviews, talks, and any talking-head content.
Requirements
- FFmpeg installed and available in PATH (
brew install ffmpegon macOS,apt install ffmpegon Linux) - Python 3 with
openai-whisperandrequestspackages (pip install openai-whisper requests). Note:openai-whisperinstalls PyTorch (~2GB download). This skill usesopenai-whisperinstead of the lighterwhisper-cppbecause it provides word-level timestamps needed for accurate viral moment scoring. - yt-dlp installed (for YouTube/URL downloads) —
brew install yt-dlpon macOS,pip install yt-dlpon Linux - API Keys in
.envfile (project root or any parent directory):KLAP_API_KEY— from klap.app (reframing with speaker tracking)CAPTIONS_AI_API_KEY— from captions.ai / platform.mirage.app (animated captions)
Before starting: Verify that FFmpeg, yt-dlp, and the Python packages are installed. If any are missing, instruct the user to install them before proceeding.
Cost Per Clip
| Step | Cost |
|---|---|
| Whisper (transcription) | Free (local) |
| FFmpeg (clip extraction) | Free (local) |
| Klap (reframing) | ~$1.50-2.50/clip depending on plan |
| Captions.ai (captions) | ~$0.15/min of output |
| Total per clip | ~$2-3 |
Input
The user provides:
Video source (required) — one of:
- Local file path — e.g.
/path/to/podcast.mp4 - YouTube URL — e.g.
https://www.youtube.com/watch?v=... - Any public video URL — direct link to MP4
- Local file path — e.g.
Moment selection mode (ask the user):
- Automatic — Claude picks the best moments
- Manual — user provides specific timestamps
- Hybrid — Claude proposes moments, user approves/adjusts before processing
Number of clips (optional) — default 3-5. Depends on video length and content density.
Caption template (optional) — Captions.ai template ID. Default:
ctpl_DxflLOnuKkb198FNdI9E(Heat). List available templates via the API if user wants to browse.Target clip duration (optional) — default 15-60 seconds. User can specify a range.
Pipeline
Step 1: Get the Video
Based on input type:
Local file:
# Verify it exists and get duration
ffprobe -v quiet -print_format json -show_format "video.mp4"
YouTube URL:
yt-dlp -f "bestvideo[height<=720]+bestaudio/best[height<=720]" --merge-output-format mp4 -o "<workdir>/source.mp4" "<URL>"
Other URL:
curl -L -o "<workdir>/source.mp4" "<URL>"
Step 2: Transcribe with Whisper
import whisper
model = whisper.load_model("base")
result = model.transcribe("source.mp4", language="en", word_timestamps=True)
Save both:
transcript.json— full result with word-level timestamps (needed for Step 3)transcript.txt— readable version with timestamps per segment (for Claude to analyze)
Step 3: Identify Best Moments (Viral Scoring)
This is the key intelligence step. Claude reads the full transcript and identifies potential clip moments.
Step 3a: Segment the transcript into candidate moments
Scan the transcript for self-contained 15-60 second windows. Look for natural start/end points (topic changes, pauses, complete thoughts).
Step 3b: Score each candidate moment on this rubric
For each candidate, score 1-10 on these five criteria:
| Criteria | What to look for | Score guide |
|---|---|---|
| Hook Strength | Does the first sentence grab attention? Is it a surprising claim, provocative question, or bold statement? | 10 = "wait, what?" reaction. 1 = generic setup |
| Quotability | Contains a memorable one-liner that people would screenshot or share? | 10 = tweet-worthy standalone quote. 1 = no standalone phrases |
| Emotional Intensity | Does the speaker show passion, humor, anger, vulnerability, or conviction? | 10 = genuine emotion. 1 = monotone/flat delivery |
| Self-Containedness | Does it make complete sense without watching the rest of the video? | 10 = fully standalone. 1 = needs prior context |
| Surprise/Controversy | Does it challenge conventional wisdom, reveal something unexpected, or take a hot take? | 10 = counterintuitive insight. 1 = commonly known information |
Total score = sum of all five (max 50).
Step 3c: Rank and select top N moments
- Sort by total score descending
- Select top N (user-specified or default 3-5)
- Ensure selected moments don't overlap
- Prefer variety in topics/angles — don't pick 3 clips about the same point
Step 3d: Present to user for approval
For each selected moment, show:
- Timestamp range (start - end)
- Duration
- Transcript excerpt (first 2-3 lines)
- Score breakdown (hook/quotability/emotion/self-contained/surprise)
- Total score
- Suggested hook text for the clip
Wait for user approval. User can:
- Approve all
- Remove specific clips
- Add their own timestamps
- Adjust start/end times
- Request more options
Do NOT proceed to Step 4 until user approves.
Step 4: Extract Raw Clips
For each approved moment, extract with FFmpeg:
ffmpeg -y -ss <start> -to <end> -i source.mp4 -c copy clip<N>-raw.mp4
Step 5: Reframe with Klap
Upload each raw clip to Klap for AI-powered speaker-tracked reframing to 9:16.
API: Klap
- Endpoint:
POST https://api.klap.app/v2/tasks/video-to-video - Auth:
Authorization: Bearer <KLAP_API_KEY>
Submit each clip:
import requests
headers = {
"Authorization": f"Bearer {klap_key}",
}
# Direct file upload
with open("clip-raw.mp4", "rb") as f:
r = requests.post(
"https://api.klap.app/v2/tasks/video-to-video",
headers=headers,
files={"video": f},
data={
"language": "en",
"editing_options": '{"captions":false,"reframe":true,"emojis":false,"intro_title":false}',
"dimensions": '{"width":1080,"height":1920}'
}
)
task_id = r.json()["id"]
output_id = r.json().get("output_id")
Poll until ready:
# Poll every 30 seconds
r = requests.get(f"https://api.klap.app/v2/tasks/{task_id}", headers=headers)
status = r.json()["status"] # "processing" or "ready"
output_id = r.json()["output_id"] # project ID when ready
Export the reframed video:
# Request export
r = requests.post(
f"https://api.klap.app/v2/projects/{output_id}/exports",
headers=headers,
json={}
)
export_id = r.json()["id"]
# Poll export every 15 seconds
r = requests.get(
f"https://api.klap.app/v2/projects/{output_id}/exports/{export_id}",
headers=headers
)
# When status != "processing", download from src_url
download_url = r.json()["src_url"]
Klap handles:
- Face detection and tracking
- Active speaker detection (for multi-person videos)
- Smooth 16:9 → 9:16 reframing
- Dynamic cropping that follows the speaker
Step 6: Add Animated Captions with Captions.ai
Upload each reframed clip to Captions.ai for professional animated captions.
API: Captions.ai (Mirage)
- Endpoint:
POST https://api.mirage.app/v1/videos/captions - Auth:
x-api-key: <CAPTIONS_AI_API_KEY>
Submit each clip:
headers = {"x-api-key": captions_key}
with open("clip-reframed.mp4", "rb") as f:
r = requests.post(
"https://api.mirage.app/v1/videos/captions",
headers=headers,
files={"video": f},
data={"caption_template_id": "ctpl_DxflLOnuKkb198FNdI9E"}
)
video_id = r.json()["video_id"]
Poll until complete:
# Poll every 10 seconds
r = requests.get(f"https://api.mirage.app/v1/videos/{video_id}", headers=headers)
status = r.json()["status"] # QUEUED → PROCESSING → COMPLETE or FAILED
Download the captioned video:
r = requests.get(
f"https://api.mirage.app/v1/videos/{video_id}/content",
headers=headers,
allow_redirects=True
)
with open("clip-FINAL.mp4", "wb") as f:
f.write(r.content)
Video requirements for Captions.ai:
- Aspect ratio: 9:16 (Klap's output satisfies this)
- Max file size: 50 MB
- Max duration: 5 minutes
- Formats: MP4, MOV
Available caption templates (fetch full list via GET https://api.mirage.app/v1/videos/captions/templates):
Some popular templates:
| Template | ID |
|---|---|
| Heat (default) | ctpl_DxflLOnuKkb198FNdI9E |
| Buzz | ctpl_yvE0ZnYzEj6ClCD2ee1f |
| Medusa | ctpl_yNnJyDLSH5oIouKdjQx2 |
| Drive | ctpl_wR9PXfmxW1DFxEUuATFg |
| Magazine | ctpl_vrs1M2VrxvzQWNRypRvh |
| Energy | ctpl_oofP3mxbx8CaEPNYqnKD |
| Sirius | ctpl_miZu2nLWyP7X8oEAAHcM |
| Milky Way | ctpl_jcTmJGX77Uwz2AqLOX4S |
Step 7: Generate Platform Captions
For each final clip, Claude writes platform-specific captions:
Instagram Reel:
- Hook line (first sentence people see)
- 2-3 sentences of context
- CTA (save, share, follow)
- 20-30 relevant hashtags
- Tone: professional but conversational
TikTok:
- Short, punchy caption (1-2 lines max)
- 5-8 hashtags
- Tone: casual, direct
YouTube Short:
- Title (under 60 characters, curiosity-driven)
- Description (2-3 sentences)
- Tags
LinkedIn (if applicable):
- Longer caption (3-5 sentences with a takeaway)
- Tone: professional, insight-driven
Step 8: Output
Save everything to the output directory:
<output-dir>/
clip1-FINAL.mp4 # Ready-to-post clip
clip2-FINAL.mp4
clip3-FINAL.mp4
captions.md # All platform captions for each clip
summary.md # Overview: source video, clips made, scores, costs
Output specs:
- Format: MP4 (H.264)
- Resolution: 1080×1920 (9:16)
- Duration: 15-60 seconds per clip
- Audio: AAC
Workflow Summary
User provides video
↓
[ASK] "Do you want me to pick the best moments, or do you have specific timestamps?"
↓
Whisper transcribes locally (free)
↓
Claude scores moments on viral rubric (hook, quotability, emotion, self-contained, surprise)
↓
[ASK] "Here are the top N moments with scores. Approve, adjust, or add your own?"
↓
FFmpeg extracts raw clips (free)
↓
Klap reframes to 9:16 with speaker tracking (~$2/clip)
↓
Captions.ai adds animated captions (~$0.15/clip)
↓
Claude writes platform-specific captions
↓
Output: final clips + captions, ready to post
Known Limitations
- yt-dlp may fail on some YouTube videos due to YouTube's evolving download restrictions. Install via
brew install yt-dlpand keep updated. If download fails, user should download the video manually and provide the local file path. - Klap credit costs can add up at scale. Each clip costs ~76 credits (44 processing + 32 generation). Monitor credit balance before batch processing.
- Captions.ai requires 9:16 input — always run Klap before Captions.ai, never the other way around.
- Whisper base model is fast but may have transcription errors on technical terms, accents, or overlapping speech. Use
whisper.load_model("medium")for better accuracy at the cost of slower transcription. - Viral scoring is heuristic — Claude's scoring is based on content patterns, not engagement data. Scores indicate relative quality within a video, not absolute viral potential.
- Max 5 minutes per clip for Captions.ai, and 50MB file size limit. Klap has plan-based limits on video length (45 min to 3 hours depending on plan).
- Processing time — Klap takes 2-5 minutes per clip, Captions.ai takes 1-2 minutes. A batch of 5 clips takes roughly 15-25 minutes total.
Environment Variables
Add these to your .env file:
KLAP_API_KEY=kak_xxxxx
CAPTIONS_AI_API_KEY=sk-xxxxx
No other API keys or local dependencies required. Whisper model downloads automatically on first run.
| 1 | |
| 2 | name video-clipper |
| 3 | description Repurposes long-form video (podcasts, interviews, talks) into short-form vertical clips for Instagram Reels, TikTok, and YouTube Shorts. Handles transcription, moment selection, clip extraction, speaker-tracked reframing (16:9 to 9:16), and animated captions. |
| 4 | user-invocable true |
| 5 | allowed-tools Bash, Read, Write, Edit, Grep, Glob, WebSearch, WebFetch |
| 6 | argument-hint [video-file-path-or-url] |
| 7 | |
| 8 | |
| 9 | # Video Clipper |
| 10 | |
| 11 | Takes a long-form video and produces ready-to-post short-form vertical clips with speaker-tracked framing and professional animated captions. Works with podcasts, interviews, talks, and any talking-head content. |
| 12 | |
| 13 | |
| 14 | |
| 15 | ## Requirements |
| 16 | |
| 17 | **FFmpeg** installed and available in PATH (`brew install ffmpeg` on macOS, `apt install ffmpeg` on Linux) |
| 18 | **Python 3** with `openai-whisper` and `requests` packages (`pip install openai-whisper requests`). **Note:** `openai-whisper` installs PyTorch (~2GB download). This skill uses `openai-whisper` instead of the lighter `whisper-cpp` because it provides word-level timestamps needed for accurate viral moment scoring. |
| 19 | **yt-dlp** installed (for YouTube/URL downloads) — `brew install yt-dlp` on macOS, `pip install yt-dlp` on Linux |
| 20 | **API Keys** in `.env` file (project root or any parent directory): |
| 21 | `KLAP_API_KEY` — from [klap.app] (reframing with speaker tracking) |
| 22 | `CAPTIONS_AI_API_KEY` — from [captions.ai] / [platform.mirage.app] (animated captions) |
| 23 | |
| 24 | **Before starting:** Verify that FFmpeg, yt-dlp, and the Python packages are installed. If any are missing, instruct the user to install them before proceeding. |
| 25 | |
| 26 | ### Cost Per Clip |
| 27 | |
| 28 | | Step | Cost | |
| 29 | |---|---| |
| 30 | | Whisper (transcription) | Free (local) | |
| 31 | | FFmpeg (clip extraction) | Free (local) | |
| 32 | | Klap (reframing) | ~$1.50-2.50/clip depending on plan | |
| 33 | | Captions.ai (captions) | ~$0.15/min of output | |
| 34 | | **Total per clip** | **~$2-3** | |
| 35 | |
| 36 | |
| 37 | |
| 38 | ## Input |
| 39 | |
| 40 | The user provides: |
| 41 | |
| 42 | **Video source** (required) — one of: |
| 43 | **Local file path** — e.g. `/path/to/podcast.mp4` |
| 44 | **YouTube URL** — e.g. `https://www.youtube.com/watch?v=...` |
| 45 | **Any public video URL** — direct link to MP4 |
| 46 | |
| 47 | **Moment selection mode** (ask the user): |
| 48 | **Automatic** — Claude picks the best moments |
| 49 | **Manual** — user provides specific timestamps |
| 50 | **Hybrid** — Claude proposes moments, user approves/adjusts before processing |
| 51 | |
| 52 | **Number of clips** (optional) — default 3-5. Depends on video length and content density. |
| 53 | |
| 54 | **Caption template** (optional) — Captions.ai template ID. Default: `ctpl_DxflLOnuKkb198FNdI9E` (Heat). List available templates via the API if user wants to browse. |
| 55 | |
| 56 | **Target clip duration** (optional) — default 15-60 seconds. User can specify a range. |
| 57 | |
| 58 | |
| 59 | |
| 60 | ## Pipeline |
| 61 | |
| 62 | ### Step 1: Get the Video |
| 63 | |
| 64 | Based on input type: |
| 65 | |
| 66 | **Local file:** |
| 67 | |
| 68 | # Verify it exists and get duration |
| 69 | ffprobe -v quiet -print_format json -show_format "video.mp4" |
| 70 | |
| 71 | |
| 72 | **YouTube URL:** |
| 73 | |
| 74 | yt-dlp -f "bestvideo[height<=720]+bestaudio/best[height<=720]" --merge-output-format mp4 -o "<workdir>/source.mp4" "<URL>" |
| 75 | |
| 76 | |
| 77 | **Other URL:** |
| 78 | |
| 79 | curl -L -o "<workdir>/source.mp4" "<URL>" |
| 80 | |
| 81 | |
| 82 | ### Step 2: Transcribe with Whisper |
| 83 | |
| 84 | |
| 85 | import whisper |
| 86 | |
| 87 | model = whisper.load_model("base") |
| 88 | result = model.transcribe("source.mp4", language="en", word_timestamps=True) |
| 89 | |
| 90 | |
| 91 | Save both: |
| 92 | `transcript.json` — full result with word-level timestamps (needed for Step 3) |
| 93 | `transcript.txt` — readable version with timestamps per segment (for Claude to analyze) |
| 94 | |
| 95 | ### Step 3: Identify Best Moments (Viral Scoring) |
| 96 | |
| 97 | This is the key intelligence step. Claude reads the full transcript and identifies potential clip moments. |
| 98 | |
| 99 | **Step 3a: Segment the transcript into candidate moments** |
| 100 | |
| 101 | Scan the transcript for self-contained 15-60 second windows. Look for natural start/end points (topic changes, pauses, complete thoughts). |
| 102 | |
| 103 | **Step 3b: Score each candidate moment on this rubric** |
| 104 | |
| 105 | For each candidate, score 1-10 on these five criteria: |
| 106 | |
| 107 | | Criteria | What to look for | Score guide | |
| 108 | |---|---|---| |
| 109 | | **Hook Strength** | Does the first sentence grab attention? Is it a surprising claim, provocative question, or bold statement? | 10 = "wait, what?" reaction. 1 = generic setup | |
| 110 | | **Quotability** | Contains a memorable one-liner that people would screenshot or share? | 10 = tweet-worthy standalone quote. 1 = no standalone phrases | |
| 111 | | **Emotional Intensity** | Does the speaker show passion, humor, anger, vulnerability, or conviction? | 10 = genuine emotion. 1 = monotone/flat delivery | |
| 112 | | **Self-Containedness** | Does it make complete sense without watching the rest of the video? | 10 = fully standalone. 1 = needs prior context | |
| 113 | | **Surprise/Controversy** | Does it challenge conventional wisdom, reveal something unexpected, or take a hot take? | 10 = counterintuitive insight. 1 = commonly known information | |
| 114 | |
| 115 | **Total score = sum of all five (max 50).** |
| 116 | |
| 117 | **Step 3c: Rank and select top N moments** |
| 118 | |
| 119 | Sort by total score descending |
| 120 | Select top N (user-specified or default 3-5) |
| 121 | Ensure selected moments don't overlap |
| 122 | Prefer variety in topics/angles — don't pick 3 clips about the same point |
| 123 | |
| 124 | **Step 3d: Present to user for approval** |
| 125 | |
| 126 | For each selected moment, show: |
| 127 | Timestamp range (start - end) |
| 128 | Duration |
| 129 | Transcript excerpt (first 2-3 lines) |
| 130 | Score breakdown (hook/quotability/emotion/self-contained/surprise) |
| 131 | Total score |
| 132 | Suggested hook text for the clip |
| 133 | |
| 134 | **Wait for user approval.** User can: |
| 135 | Approve all |
| 136 | Remove specific clips |
| 137 | Add their own timestamps |
| 138 | Adjust start/end times |
| 139 | Request more options |
| 140 | |
| 141 | **Do NOT proceed to Step 4 until user approves.** |
| 142 | |
| 143 | ### Step 4: Extract Raw Clips |
| 144 | |
| 145 | For each approved moment, extract with FFmpeg: |
| 146 | |
| 147 | |
| 148 | ffmpeg -y -ss <start> -to <end> -i source.mp4 -c copy clip<N>-raw.mp4 |
| 149 | |
| 150 | |
| 151 | ### Step 5: Reframe with Klap |
| 152 | |
| 153 | Upload each raw clip to Klap for AI-powered speaker-tracked reframing to 9:16. |
| 154 | |
| 155 | **API: Klap** |
| 156 | Endpoint: `POST https://api.klap.app/v2/tasks/video-to-video` |
| 157 | Auth: `Authorization: Bearer <KLAP_API_KEY>` |
| 158 | |
| 159 | **Submit each clip:** |
| 160 | |
| 161 | |
| 162 | import requests |
| 163 | |
| 164 | headers = { |
| 165 | "Authorization": f"Bearer {klap_key}", |
| 166 | } |
| 167 | |
| 168 | # Direct file upload |
| 169 | with open("clip-raw.mp4", "rb") as f: |
| 170 | r = requests.post( |
| 171 | "https://api.klap.app/v2/tasks/video-to-video", |
| 172 | headers=headers, |
| 173 | files={"video": f}, |
| 174 | data={ |
| 175 | "language": "en", |
| 176 | "editing_options": '{"captions":false,"reframe":true,"emojis":false,"intro_title":false}', |
| 177 | "dimensions": '{"width":1080,"height":1920}' |
| 178 | } |
| 179 | ) |
| 180 | task_id = r.json()["id"] |
| 181 | output_id = r.json().get("output_id") |
| 182 | |
| 183 | |
| 184 | **Poll until ready:** |
| 185 | |
| 186 | # Poll every 30 seconds |
| 187 | r = requests.get(f"https://api.klap.app/v2/tasks/{task_id}", headers=headers) |
| 188 | status = r.json()["status"] # "processing" or "ready" |
| 189 | output_id = r.json()["output_id"] # project ID when ready |
| 190 | |
| 191 | |
| 192 | **Export the reframed video:** |
| 193 | |
| 194 | # Request export |
| 195 | r = requests.post( |
| 196 | f"https://api.klap.app/v2/projects/{output_id}/exports", |
| 197 | headers=headers, |
| 198 | json={} |
| 199 | ) |
| 200 | export_id = r.json()["id"] |
| 201 | |
| 202 | # Poll export every 15 seconds |
| 203 | r = requests.get( |
| 204 | f"https://api.klap.app/v2/projects/{output_id}/exports/{export_id}", |
| 205 | headers=headers |
| 206 | ) |
| 207 | # When status != "processing", download from src_url |
| 208 | download_url = r.json()["src_url"] |
| 209 | |
| 210 | |
| 211 | **Klap handles:** |
| 212 | Face detection and tracking |
| 213 | Active speaker detection (for multi-person videos) |
| 214 | Smooth 16:9 → 9:16 reframing |
| 215 | Dynamic cropping that follows the speaker |
| 216 | |
| 217 | ### Step 6: Add Animated Captions with Captions.ai |
| 218 | |
| 219 | Upload each reframed clip to Captions.ai for professional animated captions. |
| 220 | |
| 221 | **API: Captions.ai (Mirage)** |
| 222 | Endpoint: `POST https://api.mirage.app/v1/videos/captions` |
| 223 | Auth: `x-api-key: <CAPTIONS_AI_API_KEY>` |
| 224 | |
| 225 | **Submit each clip:** |
| 226 | |
| 227 | headers = {"x-api-key": captions_key} |
| 228 | |
| 229 | with open("clip-reframed.mp4", "rb") as f: |
| 230 | r = requests.post( |
| 231 | "https://api.mirage.app/v1/videos/captions", |
| 232 | headers=headers, |
| 233 | files={"video": f}, |
| 234 | data={"caption_template_id": "ctpl_DxflLOnuKkb198FNdI9E"} |
| 235 | ) |
| 236 | video_id = r.json()["video_id"] |
| 237 | |
| 238 | |
| 239 | **Poll until complete:** |
| 240 | |
| 241 | # Poll every 10 seconds |
| 242 | r = requests.get(f"https://api.mirage.app/v1/videos/{video_id}", headers=headers) |
| 243 | status = r.json()["status"] # QUEUED → PROCESSING → COMPLETE or FAILED |
| 244 | |
| 245 | |
| 246 | **Download the captioned video:** |
| 247 | |
| 248 | r = requests.get( |
| 249 | f"https://api.mirage.app/v1/videos/{video_id}/content", |
| 250 | headers=headers, |
| 251 | allow_redirects=True |
| 252 | ) |
| 253 | with open("clip-FINAL.mp4", "wb") as f: |
| 254 | f.write(r.content) |
| 255 | |
| 256 | |
| 257 | **Video requirements for Captions.ai:** |
| 258 | Aspect ratio: 9:16 (Klap's output satisfies this) |
| 259 | Max file size: 50 MB |
| 260 | Max duration: 5 minutes |
| 261 | Formats: MP4, MOV |
| 262 | |
| 263 | **Available caption templates** (fetch full list via `GET https://api.mirage.app/v1/videos/captions/templates`): |
| 264 | |
| 265 | Some popular templates: |
| 266 | | Template | ID | |
| 267 | |---|---| |
| 268 | | Heat (default) | `ctpl_DxflLOnuKkb198FNdI9E` | |
| 269 | | Buzz | `ctpl_yvE0ZnYzEj6ClCD2ee1f` | |
| 270 | | Medusa | `ctpl_yNnJyDLSH5oIouKdjQx2` | |
| 271 | | Drive | `ctpl_wR9PXfmxW1DFxEUuATFg` | |
| 272 | | Magazine | `ctpl_vrs1M2VrxvzQWNRypRvh` | |
| 273 | | Energy | `ctpl_oofP3mxbx8CaEPNYqnKD` | |
| 274 | | Sirius | `ctpl_miZu2nLWyP7X8oEAAHcM` | |
| 275 | | Milky Way | `ctpl_jcTmJGX77Uwz2AqLOX4S` | |
| 276 | |
| 277 | ### Step 7: Generate Platform Captions |
| 278 | |
| 279 | For each final clip, Claude writes platform-specific captions: |
| 280 | |
| 281 | **Instagram Reel:** |
| 282 | Hook line (first sentence people see) |
| 283 | 2-3 sentences of context |
| 284 | CTA (save, share, follow) |
| 285 | 20-30 relevant hashtags |
| 286 | Tone: professional but conversational |
| 287 | |
| 288 | **TikTok:** |
| 289 | Short, punchy caption (1-2 lines max) |
| 290 | 5-8 hashtags |
| 291 | Tone: casual, direct |
| 292 | |
| 293 | **YouTube Short:** |
| 294 | Title (under 60 characters, curiosity-driven) |
| 295 | Description (2-3 sentences) |
| 296 | Tags |
| 297 | |
| 298 | **LinkedIn (if applicable):** |
| 299 | Longer caption (3-5 sentences with a takeaway) |
| 300 | Tone: professional, insight-driven |
| 301 | |
| 302 | ### Step 8: Output |
| 303 | |
| 304 | Save everything to the output directory: |
| 305 | |
| 306 | |
| 307 | <output-dir>/ |
| 308 | clip1-FINAL.mp4 # Ready-to-post clip |
| 309 | clip2-FINAL.mp4 |
| 310 | clip3-FINAL.mp4 |
| 311 | captions.md # All platform captions for each clip |
| 312 | summary.md # Overview: source video, clips made, scores, costs |
| 313 | |
| 314 | |
| 315 | **Output specs:** |
| 316 | Format: MP4 (H.264) |
| 317 | Resolution: 1080×1920 (9:16) |
| 318 | Duration: 15-60 seconds per clip |
| 319 | Audio: AAC |
| 320 | |
| 321 | |
| 322 | |
| 323 | ## Workflow Summary |
| 324 | |
| 325 | |
| 326 | User provides video |
| 327 | ↓ |
| 328 | [ASK] "Do you want me to pick the best moments, or do you have specific timestamps?" |
| 329 | ↓ |
| 330 | Whisper transcribes locally (free) |
| 331 | ↓ |
| 332 | Claude scores moments on viral rubric (hook, quotability, emotion, self-contained, surprise) |
| 333 | ↓ |
| 334 | [ASK] "Here are the top N moments with scores. Approve, adjust, or add your own?" |
| 335 | ↓ |
| 336 | FFmpeg extracts raw clips (free) |
| 337 | ↓ |
| 338 | Klap reframes to 9:16 with speaker tracking (~$2/clip) |
| 339 | ↓ |
| 340 | Captions.ai adds animated captions (~$0.15/clip) |
| 341 | ↓ |
| 342 | Claude writes platform-specific captions |
| 343 | ↓ |
| 344 | Output: final clips + captions, ready to post |
| 345 | |
| 346 | |
| 347 | |
| 348 | |
| 349 | ## Known Limitations |
| 350 | |
| 351 | **yt-dlp may fail on some YouTube videos** due to YouTube's evolving download restrictions. Install via `brew install yt-dlp` and keep updated. If download fails, user should download the video manually and provide the local file path. |
| 352 | **Klap credit costs** can add up at scale. Each clip costs ~76 credits (44 processing + 32 generation). Monitor credit balance before batch processing. |
| 353 | **Captions.ai requires 9:16 input** — always run Klap before Captions.ai, never the other way around. |
| 354 | **Whisper base model** is fast but may have transcription errors on technical terms, accents, or overlapping speech. Use `whisper.load_model("medium")` for better accuracy at the cost of slower transcription. |
| 355 | **Viral scoring is heuristic** — Claude's scoring is based on content patterns, not engagement data. Scores indicate relative quality within a video, not absolute viral potential. |
| 356 | **Max 5 minutes per clip** for Captions.ai, and 50MB file size limit. Klap has plan-based limits on video length (45 min to 3 hours depending on plan). |
| 357 | **Processing time** — Klap takes 2-5 minutes per clip, Captions.ai takes 1-2 minutes. A batch of 5 clips takes roughly 15-25 minutes total. |
| 358 | |
| 359 | |
| 360 | |
| 361 | ## Environment Variables |
| 362 | |
| 363 | Add these to your `.env` file: |
| 364 | |
| 365 | |
| 366 | KLAP_API_KEY=kak_xxxxx |
| 367 | CAPTIONS_AI_API_KEY=sk-xxxxx |
| 368 | |
| 369 | |
| 370 | No other API keys or local dependencies required. Whisper model downloads automatically on first run. |
| 371 |