Skip to content
Back to skills

Youtube Transcript

ASecurity

Extract a YouTube transcript with yt-dlp, preserve all substantive spoken content, and save polished markdown with an intuitive filename and clear structure (metadata, overview, readable body, optional timestamp reference).

  • 2 stars
  • 0 votes
  • 1 copy
  • 8 views
  • Added October 26, 2026
documentationgobashapi

Works with

  • claude code
  • api

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned October 26, 2026

npx -y skills add JoshuaShepherd/my-skills --skill youtube-transcript --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Youtube Transcript?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Youtube Transcript
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/joshuashepherd-youtube-transcript-my-skills/badge)](https://www.skillsdirectory.com/skills/joshuashepherd-youtube-transcript-my-skills)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: youtube-transcript
description: Extract a YouTube transcript with yt-dlp, preserve all substantive spoken content, and save polished markdown with an intuitive filename and clear structure (metadata, overview, readable body, optional timestamp reference).
user-invocable: true
allowed-tools: Read, Write, Bash, Glob, Grep
---

Extract and **publish** the transcript from a YouTube video: $ARGUMENTS

$ARGUMENTS should include:
- A YouTube URL (e.g. `https://www.youtube.com/watch?v=VIDEO_ID`, `https://youtu.be/VIDEO_ID`)
- Optionally: `--output DIR` — directory for the final `.md` file (default: `transcripts/`)
- Optionally: `--timestamps` — include a **Timestamped reference** section (default: include it when useful for long videos; omit only if user asks for plain prose only)
- Optionally: `--language LANG` — subtitle language code (default: `en`)

---

## Non-negotiable: content fidelity

The transcript is **source material**, not a summary. Every edit must preserve meaning and coverage.

1. **Do not remove substantive speech** — No paraphrasing that drops claims, examples, numbers, names, or definitions. No “condensing” that deletes sentences the speaker said.
2. **Safe cleanup only** — You may remove: VTT artifacts, duplicate **consecutive** lines from auto-captions, filler stutters only when they are exact repeats with no added meaning (`the the` → `the`), broken cue fragments that are **pure** duplicates of adjacent text, and obvious non-speech (`[Music]`, `[Applause]` — optional to keep in italics if they matter for context).
3. **Deduplication rule** — Collapse repeats **only** when the current line is identical (after trim) to the **immediately previous** line. Do not merge distinct sentences that happen to share a phrase.
4. **Verify before write** — After building the readable transcript, sanity-check against the raw cue list:
   - Rough **word count** of the cleaned body should be ≥ ~85–90% of the raw concatenated cue text (allowance for true duplicates and junk). If it drops far below, you removed too much — restore content.
   - If the video is long, spot-check **opening, middle, and closing** segments against the VTT: no missing paragraphs or skipped timestamp ranges.
5. **If unsure, keep the text** — When cleaning is ambiguous, preserve the longer/original wording.

---

## Before starting

1. **Parse the video ID** from the provided URL. Accept:
   - `youtube.com/watch?v=VIDEO_ID`
   - `youtu.be/VIDEO_ID`
   - `youtube.com/embed/VIDEO_ID`
   - `youtube.com/v/VIDEO_ID`
   - Bare video ID (11 characters, alphanumeric + `-` / `_`)
   - If none match, ask for a valid link.

2. **Check for `yt-dlp`**: `which yt-dlp`. If missing:
   ```
   yt-dlp is required. Install: pip install yt-dlp
   ```

3. **Ensure output directory exists** — Create `transcripts/` or the user’s `--output` directory.

---

## Step 1 — Fetch video metadata

```bash
yt-dlp --print title --print id --print uploader --print channel_id --print duration_string --print upload_date --no-download "VIDEO_URL"
```

Capture: **title**, **id**, **uploader** (channel name), **duration**, **upload_date**. Use these for the file header and filename.

---

## Step 2 — Download subtitles

Try in order until a `.vtt` exists:

### 2a — Manual subtitles
```bash
yt-dlp --write-subs --sub-lang LANG --skip-download --sub-format vtt -o "transcripts/%(id)s" "VIDEO_URL"
```

### 2b — Auto-generated (fallback)
```bash
yt-dlp --write-auto-subs --sub-lang LANG --skip-download --sub-format vtt -o "transcripts/%(id)s" "VIDEO_URL"
```

If neither produces `.vtt`:
```
No subtitles available (language: LANG). Captions may be disabled; try --language with another code.
```

Use a **60s timeout** on these commands; report timeout if it fails.

---

## Step 3 — Parse VTT (preserve content)

Read the `.vtt` and build an internal **cue list** before any prose shaping:

1. Strip `WEBVTT`, `Kind:`, `Language:`, cue IDs, positioning (`<c>`, `align:`, `position:`).
2. Strip HTML tags (`<b>`, `<i>`, `<u>`, `<font>`, etc.); keep the inner text.
3. Parse timestamps `HH:MM:SS.mmm --> HH:MM:SS.mmm` and attach text lines to each cue.
4. **Deduplicate** only consecutive identical lines (auto-subs).
5. Keep the **full cue sequence** in memory for verification and for the timestamped section.

**Paragraph breaks (readable body):** Join cues into paragraphs where:
- There is a **gap ≥ 3 seconds** between cues, or
- A clear **topic shift** (new section, rhetorical reset) without losing intervening sentences.

Do **not** drop cues to “improve flow.”

---

## Step 4 — Intuitive filename

Save as **kebab-case** from the video title, ASCII-only, lowercase, with a **short id suffix** for stability and deduplication.

**Pattern:** `{slug-from-title}-{video_id}.md`

- **Slug rules:** Lowercase; replace spaces and underscores with `-`; strip punctuation except hyphens; collapse multiple hyphens; max ~60 characters from the title portion (trim whole words if needed); keep the full 11-char `video_id` at the end.
- **Examples:**
  - Title “AutoResearch with Claude Code” + id `abc123xyz01` → `autoresearch-with-claude-code-abc123xyz01.md`
  - If the user’s project already uses a folder like `lab/youtube/ai/`, place the file there when they specify `--output lab/youtube/ai`.

**Never** use only the raw video id as the filename unless the user explicitly requests it.

---

## Step 5 — Markdown structure (organization)

Write one `.md` file with this **section order**. Adapt headings to the video (e.g. add `## Part 1 — …` only when the talk has clear movements), but keep the core blocks:

```markdown
---
title: "FULL VIDEO TITLE"
video_id: VIDEO_ID
url: https://www.youtube.com/watch?v=VIDEO_ID
channel: "CHANNEL / UPLOADER NAME"
duration: DURATION
uploaded: UPLOAD_DATE
extracted: ISO_DATE
subtitle_type: manual | auto-generated
language: LANG
source: youtube-transcript skill
---

# FULL VIDEO TITLE

Brief one- to three-sentence **overview** of what the video covers (accurate to the transcript; no invented claims).

## Key topics

- Bullet list of 3–8 themes **actually discussed** in the transcript (scan headings/cues; do not hallucinate).

## Transcript

Readable, **paragraph-structured** transcript. Use `###` subheadings only when the speaker clearly changes major topic or after long gaps (optional but improves scanability).

[All substantive spoken content belongs here — this is the canonical body.]

## Timestamped reference

Only if `--timestamps` is on, or by default for videos longer than ~15 minutes, or when the user will need to jump to moments:

Use one block per cue or group adjacent cues in the same minute:

`[MM:SS] Text...`

For videos under ~15 minutes with no timestamp flag, you may omit this section if the user asked for minimal output; otherwise include it.

## Chapters (optional)

If YouTube chapter titles are present in the description **or** obvious in the speech, add a small table: `| Start | Chapter |` with links `https://www.youtube.com/watch?v=VIDEO_ID&t=Xs`.

```

**Formatting habits:**
- Escape YAML double quotes in `title` / `channel` if needed.
- Use proper markdown lists and `###` for intra-transcript sections sparingly.
- No wall-of-giant-paragraph: break every ~4–8 sentences for readability **without** deleting sentences.

---

## Step 6 — Clean up

Delete the intermediate `.vtt` (and any `.part` files) after the markdown is written and verified.

---

## Output message to user

After saving:

```
## Transcript saved

- **Video**: TITLE
- **Channel**: UPLOADER
- **Duration**: DURATION
- **Subtitles**: manual | auto-generated
- **Approx. words** (body): N
- **Fidelity check**: raw cue words ≈ M; retained ratio OK / note if borderline
- **File**: path/to/slug-title-VIDEO_ID.md
```

---

## Error handling

- **Invalid URL** — Ask for a valid YouTube link.
- **yt-dlp missing** — Install instructions; stop.
- **No subtitles** — Clear message; suggest `--language`.
- **Network / timeout** — Report; suggest retry.
- **Language mismatch** — Try `en`, `en-US`, or list available tracks via `yt-dlp --list-subs` if needed.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.