youtube_transcript
Fetch the text transcript of a YouTube video from its URL.
Downloads: 10 · ID: 61fb7c786b5b3cc94b000000
Fetch the text transcript of a YouTube video from its URL.
Downloads: 10 · ID: 61fb7c786b5b3cc94b000000
<!-- FILE: youtube_transcript_skill.md -->
# YouTube Transcript Skill
Fetches text transcripts / summaries from YouTube videos (regular URLs, shorts, youtu.be, embed, etc.).
## Method 1 — Primary: youtube_transcript_api (preferred)
Fetches the actual caption transcript via `youtube_transcript_api`. Outputs clean plain text to stdout, optionally with SRT-style timestamps.
```
uv run fetch_transcript.py <youtube_url> [options]
```
| Arg | Default | Desc |
|-----|---------|------|
| url | required | YouTube URL (positional or pipe via stdin) |
| `--lang` | `en` | Language code(s), comma-separated, tried in order (e.g. `en,es,ja`) |
| `--timestamps` / `-t` | off | Include `[HH:MM:SS,mmm -> HH:MM:SS,mmm]` timestamps per caption segment |
**URL formats supported:** `youtube.com/watch?v=…`, `youtu.be/…`, `youtube.com/shorts/…`, `youtube.com/embed/…`, `youtube.com/v/…`, and various subdomain variants.
## Method 2 — Fallback: youtubesummary.com (when IP is blocked)
If Method 1 fails with an IP-block error (YouTube blocking requests from this host), fall back to fetching a human-readable summary from **youtubesummary.com**. The summary is AI-generated and less detailed than a raw transcript, but covers key points, numbers, and context.
1. Extract the video ID from the URL (e.g. `dQw4w9WgXcQ` from `https://www.youtube.com/watch?v=dQw4w9WgXcQ`)
2. Fetch `https://youtubesummary.com/summary/<VIDEO_ID>` using the `fetch_url` tool
3. Read the summary content and present it to the user
If youtubesummary.com also fails, do a web search for the video title/description info to give the user a rough idea of what the video covers.
## Output (Method 1 — default, no `-t`):
```
Hello and welcome to this video Today we're going to talk about...
```
## Output (Method 1 — with `-t`):
```
[00:00:00,000 -> 00:00:05,280] Hello and welcome to this video
[00:00:05,280 -> 00:00:08,640] Today we're going to talk about...
```
## Examples
```
uv run fetch_transcript.py "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
uv run fetch_transcript.py "https://youtu.be/dQw4w9WgXcQ" --timestamps
uv run fetch_transcript.py "https://www.youtube.com/shorts/abc123def45"
echo "https://youtu.be/dQw4w9WgXcQ" | uv run fetch_transcript.py
uv run fetch_transcript.py "https://youtu.be/dQw4w9WgXcQ" --lang en,es
```
Deps: `youtube-transcript-api` (auto-installed by `uv` on first run).
<!-- FILE: fetch_transcript.py -->
#!/usr/bin/env -S uv run
# /// script
# requires-python = ">=3.11"
# dependencies = ["youtube-transcript-api"]
# ///
"""Fetch a YouTube video transcript via youtube_transcript_api.
Accepts a YouTube URL (regular, shorts, youtu.be, embed, etc.) as argument
or via stdin pipe. Prints transcript text to stdout, optionally with timestamps.
"""
import argparse, re, subprocess, sys
def _ensure_uv():
"""Check uv is available. Exit with clear instructions if not."""
try:
subprocess.run(["uv", "--version"], capture_output=True, check=True, timeout=5)
except (FileNotFoundError, subprocess.CalledProcessError):
print("ERROR: 'uv' (https://docs.astral.sh/uv/) is required.", file=sys.stderr)
print(file=sys.stderr)
print("Install it:", file=sys.stderr)
print(" curl -LsSf https://astral.sh/uv/install.sh | sh", file=sys.stderr)
print(file=sys.stderr)
print("Or via pip: pip install uv", file=sys.stderr)
sys.exit(1)
except subprocess.TimeoutExpired:
pass
_ensure_uv()
from youtube_transcript_api import YouTubeTranscriptApi
def extract_video_id(url):
"""Extract video ID from various YouTube URL formats."""
# Patterns in priority order.
patterns = [
r'(?:youtube\.com/shorts/)([\w-]{11})',
r'(?:youtu\.be/)([\w-]{11})',
r'(?:youtube\.com/watch\?v=)([\w-]{11})',
r'(?:youtube\.com/embed/)([\w-]{11})',
r'(?:youtube\.com/v/)([\w-]{11})',
]
for pat in patterns:
m = re.search(pat, url)
if m:
return m.group(1)
# Fallback: grab any 11-char word chunk after v= or /
m = re.search(r'(?:v=|/)([\w-]{11})', url)
if m:
return m.group(1)
return None
def format_timestamp(seconds):
"""Convert seconds to SRT-style timestamp: HH:MM:SS,mmm"""
h = int(seconds // 3600)
m = int((seconds % 3600) // 60)
s = int(seconds % 60)
ms = int((seconds - int(seconds)) * 1000)
return f"{h:02d}:{m:02d}:{s:02d},{ms:03d}"
def main():
p = argparse.ArgumentParser(description="Fetch YouTube video transcript")
p.add_argument("url", nargs="?", default=None,
help="YouTube video URL (or pipe via stdin)")
p.add_argument("--lang", default="en",
help="Language code(s) — comma-separated, tried in order (default: en)")
p.add_argument("--timestamps", "-t", action="store_true",
help="Include timestamps in output")
a = p.parse_args()
url = a.url
if not url and not sys.stdin.isatty():
url = sys.stdin.read().strip()
if not url:
print("ERROR: no URL provided. Usage: python fetch_transcript.py <youtube_url>", file=sys.stderr)
sys.exit(1)
video_id = extract_video_id(url)
if not video_id:
print("ERROR: could not extract video ID from URL", file=sys.stderr)
sys.exit(1)
languages = [lang.strip() for lang in a.lang.split(",") if lang.strip()]
if not languages:
languages = ["en"]
try:
transcript = YouTubeTranscriptApi().fetch(video_id, languages=languages)
except Exception as e:
print(f"ERROR: failed to fetch transcript: {e}", file=sys.stderr)
sys.exit(1)
if not transcript:
print("ERROR: no transcript entries found", file=sys.stderr)
sys.exit(1)
if a.timestamps:
lines = []
for entry in transcript:
ts = format_timestamp(entry.start)
dur = format_timestamp(entry.duration)
lines.append(f"[{ts} -> {dur}] {entry.text}")
print("\n".join(lines))
else:
text = " ".join(entry.text for entry in transcript)
print(text)
if __name__ == "__main__":
main()