
Transcribe
- 37 installs
- 44 repo stars
- Updated July 8, 2026
- aviz85/claude-skills-library
transcribe is a Claude skill that generates SRT subtitle files from audio or video using ElevenLabs Scribe v2.
About
transcribe generates SRT subtitle files from audio or video using ElevenLabs Scribe v2 via a bundled TypeScript script. A developer uses it to create subtitles or captions, with options for language, max words, duration, and characters per subtitle entry. It can also emit raw transcript JSON with word-level timestamps.
- Transcribes audio/video to SRT subtitles using ElevenLabs Scribe v2
- Auto-detects language or accepts a language code; tunable words, duration, chars per subtitle
- Optional word-level timestamp JSON output
Transcribe by the numbers
- 37 all-time installs (skills.sh)
- Ranked #940 of 1,337 Generative Media skills by installs in the Skillselion catalog
- Data as of Jul 28, 2026 (Skillselion catalog sync)
transcribe capabilities & compatibility
Requires an ElevenLabs API key stored in scripts/.env; ships with setup_complete false and a SETUP.md.
- Capabilities
- translate video · speech generator
- Use cases
- transcription · translation
- Pricing
- Bring your own API key
What transcribe says it does
Generate SRT subtitle files from audio/video using ElevenLabs Scribe v2.
`.json` file (optional) - Raw transcript with word-level timestamps
API key stored in `scripts/.env`:
npx skills add https://github.com/aviz85/claude-skills-library --skill transcribeAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 37 |
|---|---|
| repo stars | ★ 44 |
| Last updated | July 8, 2026 |
| Repository | aviz85/claude-skills-library ↗ |
What it does
Generate SRT subtitles or captions from an audio or video file using ElevenLabs Scribe v2.
Who is it for?
Producing SRT subtitles or captions from audio or video, with per-entry length control.
Skip if: Generating speech from text, which is the reverse text-to-speech task.
When should I use this skill?
You need transcription, subtitles, captions, or SRT generation from audio or video.
What you get
An SRT subtitle file, with optional word-level timestamp JSON.
- SRT subtitle file
- Optional word-level transcript JSON
By the numbers
- Default 5 max words, 3.0s max duration, 70 max chars per subtitle
Files
Transcribe
First time? Ifsetup_complete: falseabove, run./SETUP.mdfirst, then setsetup_complete: true.
Generate SRT subtitle files from audio/video using ElevenLabs Scribe v2.
Quick Start
cd ~/.claude/skills/transcribe/scripts
# Basic transcription (auto-detect language)
npx ts-node transcribe.ts -i /path/to/video.mp4 -o /path/to/output.srt
# Specify language
npx ts-node transcribe.ts -i /path/to/video.mp4 -o /path/to/output.srt -l en
# Custom subtitle length (max words per entry)
npx ts-node transcribe.ts -i /path/to/video.mp4 -o /path/to/output.srt --max-words 6
# Custom max duration per subtitle
npx ts-node transcribe.ts -i /path/to/video.mp4 -o /path/to/output.srt --max-duration 4.0Options
| Option | Short | Default | Description |
|---|---|---|---|
--input | -i | (required) | Input audio/video file |
--output | -o | (required) | Output SRT file path |
--language | -l | auto | Language code (en, he, ar, etc.) |
--max-words | 5 | Max words per subtitle entry | |
--max-duration | 3.0 | Max seconds per subtitle entry | |
--max-chars | 70 | Max characters per subtitle entry | |
--timing-offset | 0.25 | Timing offset in seconds | |
--json | false | Also output raw transcript JSON |
Language Codes
en- Englishhe- Hebrewar- Arabices- Spanishfr- Frenchde- Germanru- Russianzh- Chineseja- Japanese- (or omit for auto-detection)
Output
The script generates: 1. .srt file - Standard subtitle file 2. .json file (optional) - Raw transcript with word-level timestamps
Environment
API key stored in scripts/.env:
ELEVENLABS_API_KEY=your_key_here{
"name": "transcribe-skill",
"version": "1.0.0",
"description": "Transcribe audio/video to SRT using ElevenLabs Scribe v2",
"main": "transcribe.ts",
"scripts": {
"transcribe": "npx ts-node transcribe.ts"
},
"dependencies": {
"commander": "^11.0.0",
"dotenv": "^16.0.0"
},
"devDependencies": {
"@types/node": "^20.0.0",
"ts-node": "^10.9.0",
"typescript": "^5.0.0"
}
}
#!/usr/bin/env npx ts-node
/**
* Transcribe audio/video to SRT using ElevenLabs Scribe v2
*/
import * as fs from 'fs';
import * as path from 'path';
import * as dotenv from 'dotenv';
import { program } from 'commander';
dotenv.config();
const ELEVENLABS_API_KEY = process.env.ELEVENLABS_API_KEY;
const ELEVENLABS_BASE_URL = 'https://api.elevenlabs.io';
interface Word {
word: string;
start: number;
end: number;
}
interface TranscriptResult {
text: string;
words: Word[];
duration: number;
language: string;
}
interface SubtitleEntry {
index: number;
start: number;
end: number;
text: string;
}
function secondsToSrtTime(seconds: number): string {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = Math.floor(seconds % 60);
const millis = Math.floor((seconds % 1) * 1000);
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')},${millis.toString().padStart(3, '0')}`;
}
async function transcribe(inputPath: string, language?: string): Promise<TranscriptResult> {
if (!ELEVENLABS_API_KEY) {
throw new Error('ELEVENLABS_API_KEY not set in .env');
}
if (!fs.existsSync(inputPath)) {
throw new Error(`Input file not found: ${inputPath}`);
}
console.log(`Transcribing: ${path.basename(inputPath)}`);
console.log(`Language: ${language || 'auto-detect'}`);
const formData = new FormData();
const fileBuffer = fs.readFileSync(inputPath);
const blob = new Blob([fileBuffer]);
formData.append('file', blob, path.basename(inputPath));
formData.append('model_id', 'scribe_v2');
formData.append('tag_audio_events', 'false');
if (language) {
formData.append('language_code', language);
}
const response = await fetch(`${ELEVENLABS_BASE_URL}/v1/speech-to-text`, {
method: 'POST',
headers: {
'xi-api-key': ELEVENLABS_API_KEY,
},
body: formData,
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Transcription failed: ${response.status} - ${errorText}`);
}
const result = await response.json() as any;
const words: Word[] = (result.words || []).map((w: any) => ({
word: w.text || w.word || '',
start: w.start || 0,
end: w.end || 0,
}));
const duration = words.length > 0 ? Math.max(...words.map(w => w.end)) : 0;
console.log(`Done: ${words.length} words, ${duration.toFixed(1)}s`);
return {
text: result.text || '',
words,
duration,
language: result.language_code || language || 'unknown',
};
}
function generateSrt(
words: Word[],
options: {
maxWords: number;
maxDuration: number;
maxChars: number;
timingOffset: number;
}
): SubtitleEntry[] {
const entries: SubtitleEntry[] = [];
let currentWords: Word[] = [];
let currentStart: number | null = null;
for (const word of words) {
const wordText = word.word.trim();
if (!wordText) continue;
if (currentStart === null) {
currentStart = word.start;
}
const testText = [...currentWords.map(w => w.word), wordText].join(' ');
const entryDuration = word.end - currentStart;
const shouldBreak =
testText.length > options.maxChars ||
entryDuration > options.maxDuration ||
currentWords.length >= options.maxWords;
if (shouldBreak && currentWords.length > 0) {
const lastWord = currentWords[currentWords.length - 1];
const entryEnd = lastWord.end;
const text = currentWords.map(w => w.word).join(' ');
entries.push({
index: entries.length + 1,
start: currentStart + options.timingOffset,
end: entryEnd + options.timingOffset,
text,
});
currentWords = [word];
currentStart = word.start;
} else {
currentWords.push(word);
}
}
// Add final entry
if (currentWords.length > 0 && currentStart !== null) {
const lastWord = currentWords[currentWords.length - 1];
const text = currentWords.map(w => w.word).join(' ');
entries.push({
index: entries.length + 1,
start: currentStart + options.timingOffset,
end: lastWord.end + options.timingOffset,
text,
});
}
return entries;
}
function writeSrt(entries: SubtitleEntry[], outputPath: string): void {
const lines: string[] = [];
for (const entry of entries) {
lines.push(entry.index.toString());
lines.push(`${secondsToSrtTime(entry.start)} --> ${secondsToSrtTime(entry.end)}`);
lines.push(entry.text);
lines.push('');
}
fs.writeFileSync(outputPath, lines.join('\n'), 'utf-8');
console.log(`Written: ${outputPath} (${entries.length} entries)`);
}
async function main() {
program
.requiredOption('-i, --input <path>', 'Input audio/video file')
.requiredOption('-o, --output <path>', 'Output SRT file path')
.option('-l, --language <code>', 'Language code (en, he, ar, etc.)')
.option('--max-words <n>', 'Max words per subtitle', '5')
.option('--max-duration <s>', 'Max duration per subtitle in seconds', '3.0')
.option('--max-chars <n>', 'Max characters per subtitle', '70')
.option('--timing-offset <s>', 'Timing offset in seconds', '0.25')
.option('--json', 'Also output raw transcript JSON')
.parse();
const opts = program.opts();
try {
const transcript = await transcribe(opts.input, opts.language);
const entries = generateSrt(transcript.words, {
maxWords: parseInt(opts.maxWords),
maxDuration: parseFloat(opts.maxDuration),
maxChars: parseInt(opts.maxChars),
timingOffset: parseFloat(opts.timingOffset),
});
writeSrt(entries, opts.output);
if (opts.json) {
const jsonPath = opts.output.replace(/\.srt$/i, '_transcript.json');
fs.writeFileSync(jsonPath, JSON.stringify(transcript, null, 2), 'utf-8');
console.log(`Written: ${jsonPath}`);
}
console.log('\n✓ Transcription complete!');
} catch (error) {
console.error('Error:', (error as Error).message);
process.exit(1);
}
}
main();
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020"],
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"outDir": "./dist"
},
"include": ["*.ts"],
"exclude": ["node_modules"]
}
Transcribe - Setup Guide
Prerequisites
- Node.js installed
- ElevenLabs account with API access
1. Get ElevenLabs API Key
1. Go to elevenlabs.io 2. Sign up or log in 3. Go to Profile → API Key 4. Copy your API key
2. Configure Environment
cd ~/.claude/skills/transcribe/scripts
cp .env.example .envEdit .env:
ELEVENLABS_API_KEY=your_actual_api_key_here3. Install Dependencies
cd ~/.claude/skills/transcribe/scripts
npm install4. Test
# Test with a sample audio file
npx ts-node transcribe.ts --helpIf help appears, setup is complete!
5. Mark Setup Complete
Edit SKILL.md and change:
setup_complete: trueTroubleshooting
| Issue | Solution |
|---|---|
| Invalid API key | Check .env has correct key |
| File not found | Use absolute paths |
| Unsupported format | Convert to mp3/wav first |
Related skills
FAQ
Which engine does transcribe use?
ElevenLabs Scribe v2, with auto language detection or an explicit language code.
What outputs does it produce?
A standard .srt file and an optional .json file with word-level timestamps.