Install
openclaw skills install @social-media-skills/auto-captions-skillAuto captions for videos, made on the user's own computer by their AI agent: transcribes speech with a time for every word (free local Whisper by default, or the user's own Groq or Deepgram key, or existing subtitles), proofreads names and numbers, burns creator-style captions into the video (8 presets: word highlight, bold, pill, reveal, two-line, clean subtitles, minimal, one word) in any shape (9:16, 4:5, 1:1, 16:9, crop or blurred background), writes SRT, VTT and a transcript, translates the captions into other languages with the same timing, shows a review sheet with each app's buttons drawn on, and posts or schedules the captioned videos to TikTok, Reels, Shorts, LinkedIn, Facebook, X, Threads and Bluesky through WoopSocial after a yes. Use when someone says "auto captions", "add captions", "add subtitles to my video", "caption generator", "subtitle generator", "burn in subtitles", "TikTok captions", "make an SRT", "translate my subtitles" or "caption this folder of videos". No watermark, no per-minute limits, no paid API needed. Standalone.
openclaw skills install @social-media-skills/auto-captions-skillMany people watch with the sound off; captions let them follow along. Good captions are accurate (names and numbers right), short enough to read at a glance, timed to the speech, placed clear of each app's buttons, and in a style that fits the account. Accessible captions also need a subtitle file and a transcript, because burned-in text can't be resized or read aloud.
This skill does all of it on the user's own computer, with no watermark and no per-minute limit. You (the agent) do the part tools get wrong: you proofread the transcript, choose what to stress, and translate.
Where things run: <skill> below is the folder this file is in. Run commands from the user's
own project folder and keep their files there: the job file, work/ (transcripts, fixes) and
captioned/ (results). Never write into <skill>: an update replaces it.
Five commitments:
Run python3 <skill>/scripts/check_setup.py (Windows: py <skill>\scripts\check_setup.py). It
needs Python 3.9+, Pillow and ffmpeg, plus one transcription route; it prints the install command
for anything missing. The free route is pip install faster-whisper (runs locally; the model
downloads once, about 0.5 GB for "small"). On Apple Silicon, pip install mlx-whisper is faster.
A Groq key (GROQ_API_KEY, about $0.04 per hour of audio) or a Deepgram key (DEEPGRAM_API_KEY,
labels speakers) is quicker for long videos; the audio is then sent to that service, so ask first.
In one message, ask only what you can't see:
post_to (for
example ["TIKTOK", "INSTAGRAM", "YOUTUBE"]). If they name places to post, also ask: "Should I
post or schedule them there when they're done, or just hand you the files?" A yes is a request
to post (below). "Just the files" means no posting talk until the end.vocab).Save the answers in captions.json (the job file):
{
"videos": [{"file": "clips/launch.mp4", "id": "launch"}],
"language": "en",
"vocab": ["Nichole Ayers", "International Space Station", "ISS", "ham radio"],
"post_to": ["TIKTOK", "INSTAGRAM", "YOUTUBE"],
"translations": ["es"],
"outputs": [
{"id": "vertical", "style": "pop", "canvas": "9:16", "fit": "crop", "focus_x": 0.55},
{"id": "linkedin", "style": "clean", "canvas": "4:5", "fit": "blur", "languages": ["en"]}
]
}
Every field is described in references/job-file.md. A folder of videos is just a longer
videos list; every script skips work that is already done, so a batch can stop and resume.
If the user asked to post (in their request or their answers; naming an app only sets the
shape and is not a request to post): check social_accounts_list now, not at the end. If
WoopSocial isn't connected, say it in one short message and carry on with the captions while they
set it up:
references/woopsocial.md).At Step 6, check social_accounts_list again instead of repeating this.
python3 <skill>/scripts/transcribe.py captions.json
It picks the best route available (the video's own .srt/.vtt next to it, a Groq or Deepgram key,
mlx-whisper, then faster-whisper) and writes work/<id>/transcript.txt (numbered lines) and
transcript.json (every word with its time). --model turbo is more accurate and slower; use it
for hard audio, accents or many names. Music-heavy or singing videos transcribe poorly: if the
user has a clean voice track, caption from that.
python3 <skill>/scripts/fix.py captions.json --report lists unsure words, near-misses of the
vocab names and split numbers.work/<id>/transcript.txt. Fix misheard words, names, numbers (write them
as digits: "17,500"), and missing punctuation, which decides where captions break.work/<id>/fixes.json (references/proofreading.md): corrected lines, whole-word
replacements, and emphasis, the 1 or 2 words per sentence that carry the point (numbers,
names, the payoff); styles colour them. Fillers (um, uh) are removed unless the user wants them.Don't change what someone said to make it sound better. Captions transcribe; they don't edit.
python3 <skill>/scripts/captions.py captions.json writes captioned/<id>/<id>.<lang>.srt,
.vtt and .txt (subtitle cues follow broadcast rules: 2 lines of 42 characters at most,
broken at punctuation).work/<id>/translate.<lang>.json (references/translation.md): every
cue's meaning in natural language, about as long as the original, names and numbers kept.
Then run captions.py again. Timing comes from the original, so it stays in sync.python3 <skill>/scripts/styles.py captions.json --at <a second with speech> puts every style
on one frame of their video (captioned/styles-<id>.jpg). Show it; let them pick. Styles are
in references/styles.md; a brand colour goes in style_overrides.python3 <skill>/scripts/render.py captions.json --preview writes
captioned/<id>/review-<id>.jpg: three moments per output and language, and for 9:16 the same
frame with TikTok's, Reels' and Shorts' buttons drawn on. Check that captions don't cover a
face, existing on-screen text (a name title, a slide) or the buttons; move them with
"position" (0.05 top to 0.95 bottom) per output or per video, and the crop with focus_x.python3 <skill>/scripts/render.py captions.json renders every video, output and language
to captioned/<id>/<id>.<output>.<lang>.mp4 (H.264, AAC, ready to post) with a cover frame,
and lists them in captioned/manifest.json. Expect one to three times the video's length in
render time per output, depending on the computer.After delivering, offer once, in one line, using the places from post_to (skip this if they
chose "just the files" in Step 1). For example: "Want post
texts and a schedule for these too? One a day on TikTok, Reels and Shorts from tomorrow. I can
schedule them for you, or you post by hand from the plan." If not, stop here.
On a yes:
post.json (references/posting.md) from what you already know: one account per place
and language from Step 1, the post text per video and language, the times they gave (or the
defaults, said as defaults), starting tomorrow. Leave out socialAccountId for accounts that
aren't connected yet. The post text is what search reads: say what the video is about in plain
words, 3 to 5 specific hashtags at most. TikTok's privacy level and YouTube's "made for kids"
are the user's answers; ask, never pick them.python3 <skill>/scripts/post_plan.py post.json checks every file against each platform
(length, size, frame rate, shape, text limits), plans one post a day per account at most, and
writes plan.md and requests/captions/. It works without WoopSocial. Show plan.md: it has
every file, text and time, and where to add each subtitle file, so they can post by hand from it.social_accounts_list; connect what's missing in one pass (references/woopsocial.md), then
run post_plan.py again with the IDs.references/woopsocial.md), save media.json
({"captioned/x/x.vertical.en.mp4": "<mediaId>"}), run post_plan.py post.json --media media.json.posts_validate each request, show plan.md, and wait for one clear yes for the batch. Then
posts_create each, and report what was scheduled and anything that failed, in plain words.plan.md lists where to add
each SRT (YouTube Studio, Facebook) after the post is live. X and LinkedIn take a subtitle file
only while uploading in their own app, so there the burned-in captions do the job.references/woopsocial.md, Results).scripts/check_setup.py: what's installed, what's missing, how to fix it.scripts/transcribe.py: words with times (faster-whisper, mlx-whisper, Groq, Deepgram, existing subtitles).scripts/fix.py: proofreading report; applies fixes.json with word times kept.scripts/captions.py: SRT, VTT, transcript; translation templates.scripts/styles.py: every style on a frame of the user's video.scripts/render.py: review sheet; burns captions in each output, shape and language.scripts/post_plan.py: platform checks, schedule, WoopSocial requests, plan.md.scripts/upload_parts.py: uploads a file to a WoopSocial upload session.styles/*.json: the 8 presets. fonts/: Poppins, Anton, Inter (SIL Open Font License).references/: job-file.md, proofreading.md, translation.md, styles.md, platforms.md, posting.md, woopsocial.md.examples/iss-radio/: two NASA clips captioned in English and Spanish, with every file the skill makes.