parse-youtube
Extract a transcript, timestamps, metadata and chapters from a public YouTube URL. Use when someone drops a YouTube link and wants the gist, a quotable moment, or the text to feed into something else.
SKILL.mdparse-youtube/skills/parse-youtube/SKILL.md
GitHubSkill: parse-youtube
Extract transcripts, timestamps, metadata and chapters from any public YouTube video.
When to use
- Someone pastes a YouTube URL and asks a question about it
- A draft needs a quote or a claim sourced from a talk
- You need to find a specific moment inside a long video without watching it
- Pre-step before indexing video content into a vector store
CLI contract
python3 "$PARSE_YOUTUBE_SCRIPT" <url_or_video_id> [--lang en] [--format json|markdown|both] [--no-metadata]
$PARSE_YOUTUBE_SCRIPT is exported by the plugin's env contract and points at
scripts/parse-youtube.py inside the plugin directory. Standalone, call the
script by path.
Arguments:
url_or_video_id- any YouTube URL shape (youtube.com/watch?v=,youtu.be/,/embed/, or a raw 11-character ID)--lang- preferred language code, comma-separated for priority order. Overrides the configureddefault_language. The configuredfallback_languagesare always appended after it.--format-json(default),markdown, orboth--no-metadata- skip the yt-dlp call for a faster transcript-only run
JSON output schema:
{
"video_id": "dQw4w9WgXcQ",
"title": "...",
"channel": "...",
"duration_seconds": 213,
"language": "en",
"description": "... (truncated to description_max_chars)",
"chapters": [{"title": "...", "start": 0}],
"transcript": [{"start": 0.0, "duration": 2.5, "text": "..."}],
"full_text": "...",
"markdown": "..."
}
markdown is present only when --format is markdown or both.
Markdown output: # Title, then channel and duration, a chapters section if
the video has one, the description, and the transcript with bold [M:SS]
timestamp anchors on every line. Drops straight into a draft.
Configuration
| Key | Default | What it controls |
|---|---|---|
default_language |
en |
Caption language tried first |
fallback_languages |
["en"] |
Languages tried after the preferred one. Set this to the languages your install actually reads. |
metadata |
true |
Whether to spend the yt-dlp call on title/channel/chapters |
metadata_timeout_seconds |
30 |
When yt-dlp is abandoned. Metadata goes null; the transcript is unaffected. |
description_max_chars |
1000 |
Description truncation. Descriptions are often link dumps. |
Examples
# JSON with full transcript
python3 "$PARSE_YOUTUBE_SCRIPT" "https://youtu.be/dQw4w9WgXcQ"
# Markdown, ready to paste into a draft
python3 "$PARSE_YOUTUBE_SCRIPT" "https://youtu.be/dQw4w9WgXcQ" --format markdown
# Prefer the Portuguese caption track, fall back to whatever is configured
python3 "$PARSE_YOUTUBE_SCRIPT" "https://youtu.be/VIDEOID" --lang pt
# Transcript only, no metadata round-trip
python3 "$PARSE_YOUTUBE_SCRIPT" "https://youtu.be/VIDEOID" --no-metadata
Limitations
- The video must have captions, auto-generated or manual. Captions-disabled videos error out.
- Metadata adds roughly 5 seconds. On failure or timeout the metadata fields come back null and the transcript still succeeds.
- Age-restricted, private, and members-only videos fail. There is no authenticated path here on purpose.
- YouTube changes its player often enough that yt-dlp needs periodic updating. A stale yt-dlp shows up as null metadata, not as an error.
Downstream: vector indexing
The transcript array is already the right shape for chunked embedding. Each
segment becomes one chunk: start is the seek offset to link back to, text is
the embedding input, video_id is the source reference. Segments are short, so
most installs will want to merge adjacent ones up to a token budget before
embedding rather than indexing each line.