YouTube Transcript Downloader skill
Download YouTube video transcripts when user provides a YouTube URL or asks to download/get/fetch a transcript from YouTube.
by michalparkola·MIT license·GitHub ↗
★ 547 Stars on the repo·Checked
npx degit michalparkola/tapestry-skills-for-claude-code/youtube-transcript#main ~/.claude/skills/youtube-transcriptFiles of YouTube Transcript Downloader
Files 1 file
Show the full text416 lines
YouTube Transcript Downloader
This skill helps download transcripts (subtitles/captions) from YouTube videos using yt-dlp.
When to Use This Skill
Activate this skill when the user:
- Provides a YouTube URL and wants the transcript
- Asks to "download transcript from YouTube"
- Wants to "get captions" or "get subtitles" from a video
- Asks to "transcribe a YouTube video"
- Needs text content from a YouTube video
How It Works
Priority Order:
- Check if yt-dlp is installed - install if needed
- List available subtitles - see what's actually available
- Try manual subtitles first (
--write-sub) - highest quality - Fallback to auto-generated (
--write-auto-sub) - usually available - Last resort: Whisper transcription - if no subtitles exist (requires user confirmation)
- Confirm the download and show the user where the file is saved
- Optionally clean up the VTT format if the user wants plain text
Installation Check
IMPORTANT: Always check if yt-dlp is installed first:
which yt-dlp || command -v yt-dlp
If Not Installed
Attempt automatic installation based on the system:
macOS (Homebrew):
brew install yt-dlp
Linux (apt/Debian/Ubuntu):
sudo apt update && sudo apt install -y yt-dlp
Alternative (pip - works on all systems):
pip3 install yt-dlp
# or
python3 -m pip install yt-dlp
If installation fails: Inform the user they need to install yt-dlp manually and provide them with installation instructions from https://github.com/yt-dlp/yt-dlp#installation
Check Available Subtitles
ALWAYS do this first before attempting to download:
yt-dlp --list-subs "YOUTUBE_URL"
This shows what subtitle types are available without downloading anything. Look for:
- Manual subtitles (better quality)
- Auto-generated subtitles (usually available)
- Available languages
Download Strategy
Option 1: Manual Subtitles (Preferred)
Try this first - highest quality, human-created:
yt-dlp --write-sub --skip-download --output "OUTPUT_NAME" "YOUTUBE_URL"
Option 2: Auto-Generated Subtitles (Fallback)
If manual subtitles aren't available:
yt-dlp --write-auto-sub --skip-download --output "OUTPUT_NAME" "YOUTUBE_URL"
Both commands create a .vtt file (WebVTT subtitle format).
Option 3: Whisper Transcription (Last Resort)
ONLY use this if both manual and auto-generated subtitles are unavailable.
Step 1: Show File Size and Ask for Confirmation
# Get audio file size estimate
yt-dlp --print "%(filesize,filesize_approx)s" -f "bestaudio" "YOUTUBE_URL"
# Or get duration to estimate
yt-dlp --print "%(duration)s %(title)s" "YOUTUBE_URL"
IMPORTANT: Display the file size to the user and ask: "No subtitles are available. I can download the audio (approximately X MB) and transcribe it using Whisper. Would you like to proceed?"
Wait for user confirmation before continuing.
Step 2: Check for Whisper Installation
command -v whisper
If not installed, ask user: "Whisper is not installed. Install it with pip install openai-whisper (requires ~1-3GB for models)? This is a one-time installation."
Wait for user confirmation before installing.
Install if approved:
pip3 install openai-whisper
Step 3: Download Audio Only
yt-dlp -x --audio-format mp3 --output "audio_%(id)s.%(ext)s" "YOUTUBE_URL"
Step 4: Transcribe with Whisper
# Auto-detect language (recommended)
whisper audio_VIDEO_ID.mp3 --model base --output_format vtt
# Or specify language if known
whisper audio_VIDEO_ID.mp3 --model base --language en --output_format vtt
Model Options (stick to base for now):
tiny- fastest, least accurate (~1GB)base- good balance (~1GB) ← USE THISsmall- better accuracy (~2GB)medium- very good (~5GB)large- best accuracy (~10GB)
Step 5: Cleanup
After transcription completes, ask user: "Transcription complete! Would you like me to delete the audio file to save space?"
If yes:
rm audio_VIDEO_ID.mp3
Getting Video Information
Extract Video Title (for filename)
yt-dlp --print "%(title)s" "YOUTUBE_URL"
Use this to create meaningful filenames based on the video title. Clean the title for filesystem compatibility:
- Replace
/with- - Replace special characters that might cause issues
- Consider using sanitized version:
$(yt-dlp --print "%(title)s" "URL" | tr '/' '-' | tr ':' '-')
Post-Processing
Convert to Plain Text (Recommended)
YouTube's auto-generated VTT files contain duplicate lines because captions are shown progressively with overlapping timestamps. Always deduplicate when converting to plain text while preserving the original speaking order.
python3 -c "
import sys, re
seen = set()
with open('transcript.en.vtt', 'r') as f:
for line in f:
line = line.strip()
if line and not line.startswith('WEBVTT') and not line.startswith('Kind:') and not line.startswith('Language:') and '-->' not in line:
clean = re.sub('<[^>]*>', '', line)
clean = clean.replace('&', '&').replace('>', '>').replace('<', '<')
if clean and clean not in seen:
print(clean)
seen.add(clean)
" > transcript.txt
Complete Post-Processing with Video Title
# Get video title
VIDEO_TITLE=$(yt-dlp --print "%(title)s" "YOUTUBE_URL" | tr '/' '_' | tr ':' '-' | tr '?' '' | tr '"' '')
# Find the VTT file
VTT_FILE=$(ls *.vtt | head -n 1)
# Convert with deduplication
python3 -c "
import sys, re
seen = set()
with open('$VTT_FILE', 'r') as f:
for line in f:
line = line.strip()
if line and not line.startswith('WEBVTT') and not line.startswith('Kind:') and not line.startswith('Language:') and '-->' not in line:
clean = re.sub('<[^>]*>', '', line)
clean = clean.replace('&', '&').replace('>', '>').replace('<', '<')
if clean and clean not in seen:
print(clean)
seen.add(clean)
" > "${VIDEO_TITLE}.txt"
echo "✓ Saved to: ${VIDEO_TITLE}.txt"
# Clean up VTT file
rm "$VTT_FILE"
echo "✓ Cleaned up temporary VTT file"
Output Formats
- VTT format (
.vtt): Includes timestamps and formatting, good for video players - Plain text (
.txt): Just the text content, good for reading or analysis
Tips
- The filename will be
{output_name}.{language_code}.vtt(e.g.,transcript.en.vtt) - Most YouTube videos have auto-generated English subtitles
- Some videos may have multiple language options
- If auto-subtitles aren't available, try
--write-subinstead for manual subtitles
Complete Workflow Example
VIDEO_URL="https://www.youtube.com/watch?v=dQw4w9WgXcQ"
# Get video title for filename
VIDEO_TITLE=$(yt-dlp --print "%(title)s" "$VIDEO_URL" | tr '/' '_' | tr ':' '-' | tr '?' '' | tr '"' '')
OUTPUT_NAME="transcript_temp"
# ============================================
# STEP 1: Check if yt-dlp is installed
# ============================================
if ! command -v yt-dlp &> /dev/null; then
echo "yt-dlp not found, attempting to install..."
if command -v brew &> /dev/null; then
brew install yt-dlp
elif command -v apt &> /dev/null; then
sudo apt update && sudo apt install -y yt-dlp
else
pip3 install yt-dlp
fi
fi
# ============================================
# STEP 2: List available subtitles
# ============================================
echo "Checking available subtitles..."
yt-dlp --list-subs "$VIDEO_URL"
# ============================================
# STEP 3: Try manual subtitles first
# ============================================
echo "Attempting to download manual subtitles..."
if yt-dlp --write-sub --skip-download --output "$OUTPUT_NAME" "$VIDEO_URL" 2>/dev/null; then
echo "✓ Manual subtitles downloaded successfully!"
ls -lh ${OUTPUT_NAME}.*
else
# ============================================
# STEP 4: Fallback to auto-generated
# ============================================
echo "Manual subtitles not available. Trying auto-generated..."
if yt-dlp --write-auto-sub --skip-download --output "$OUTPUT_NAME" "$VIDEO_URL" 2>/dev/null; then
echo "✓ Auto-generated subtitles downloaded successfully!"
ls -lh ${OUTPUT_NAME}.*
else
# ============================================
# STEP 5: Last resort - Whisper transcription
# ============================================
echo "⚠ No subtitles available for this video."
# Get file size
FILE_SIZE=$(yt-dlp --print "%(filesize_approx)s" -f "bestaudio" "$VIDEO_URL")
DURATION=$(yt-dlp --print "%(duration)s" "$VIDEO_URL")
TITLE=$(yt-dlp --print "%(title)s" "$VIDEO_URL")
echo "Video: $TITLE"
echo "Duration: $((DURATION / 60)) minutes"
echo "Audio size: ~$((FILE_SIZE / 1024 / 1024)) MB"
echo ""
echo "Would you like to download and transcribe with Whisper? (y/n)"
read -r RESPONSE
if [[ "$RESPONSE" =~ ^[Yy]$ ]]; then
# Check for Whisper
if ! command -v whisper &> /dev/null; then
echo "Whisper not installed. Install now? (requires ~1-3GB) (y/n)"
read -r INSTALL_RESPONSE
if [[ "$INSTALL_RESPONSE" =~ ^[Yy]$ ]]; then
pip3 install openai-whisper
else
echo "Cannot proceed without Whisper. Exiting."
exit 1
fi
fi
# Download audio
echo "Downloading audio..."
yt-dlp -x --audio-format mp3 --output "audio_%(id)s.%(ext)s" "$VIDEO_URL"
# Get the actual audio filename
AUDIO_FILE=$(ls audio_*.mp3 | head -n 1)
# Transcribe
echo "Transcribing with Whisper (this may take a few minutes)..."
whisper "$AUDIO_FILE" --model base --output_format vtt
# Cleanup
echo "Transcription complete! Delete audio file? (y/n)"
read -r CLEANUP_RESPONSE
if [[ "$CLEANUP_RESPONSE" =~ ^[Yy]$ ]]; then
rm "$AUDIO_FILE"
echo "Audio file deleted."
fi
ls -lh *.vtt
else
echo "Transcription cancelled."
exit 0
fi
fi
fi
# ============================================
# STEP 6: Convert to readable plain text with deduplication
# ============================================
VTT_FILE=$(ls ${OUTPUT_NAME}*.vtt 2>/dev/null || ls *.vtt | head -n 1)
if [ -f "$VTT_FILE" ]; then
echo "Converting to readable format and removing duplicates..."
python3 -c "
import sys, re
seen = set()
with open('$VTT_FILE', 'r') as f:
for line in f:
line = line.strip()
if line and not line.startswith('WEBVTT') and not line.startswith('Kind:') and not line.startswith('Language:') and '-->' not in line:
clean = re.sub('<[^>]*>', '', line)
clean = clean.replace('&', '&').replace('>', '>').replace('<', '<')
if clean and clean not in seen:
print(clean)
seen.add(clean)
" > "${VIDEO_TITLE}.txt"
echo "✓ Saved to: ${VIDEO_TITLE}.txt"
# Clean up temporary VTT file
rm "$VTT_FILE"
echo "✓ Cleaned up temporary VTT file"
else
echo "⚠ No VTT file found to convert"
fi
echo "✓ Complete!"
Note: This complete workflow handles all scenarios with proper error checking and user prompts at each decision point.
Error Handling
Common Issues and Solutions:
1. yt-dlp not installed
- Attempt automatic installation based on system (Homebrew/apt/pip)
- If installation fails, provide manual installation link
- Verify installation before proceeding
2. No subtitles available
- List available subtitles first to confirm
- Try both
--write-suband--write-auto-sub - If both fail, offer Whisper transcription option
- Show file size and ask for user confirmation before downloading audio
3. Invalid or private video
- Check if URL is correct format:
https://www.youtube.com/watch?v=VIDEO_ID - Some videos may be private, age-restricted, or geo-blocked
- Inform user of the specific error from yt-dlp
4. Whisper installation fails
- May require system dependencies (ffmpeg, rust)
- Provide fallback: "Install manually with:
pip3 install openai-whisper" - Check available disk space (models require 1-10GB depending on size)
5. Download interrupted or failed
- Check internet connection
- Verify sufficient disk space
- Try again with
--no-check-certificateif SSL issues occur
6. Multiple subtitle languages
- By default, yt-dlp downloads all available languages
- Can specify with
--sub-langs enfor English only - List available with
--list-subsfirst
Best Practices:
- ✅ Always check what's available before attempting download (
--list-subs) - ✅ Verify success at each step before proceeding to next
- ✅ Ask user before large downloads (audio files, Whisper models)
- ✅ Clean up temporary files after processing
- ✅ Provide clear feedback about what's happening at each stage
- ✅ Handle errors gracefully with helpful messages
| 1 | |
| 2 | name youtube-transcript |
| 3 | description Download YouTube video transcripts when user provides a YouTube URL or asks to download/get/fetch a transcript from YouTube. Also use when user wants to transcribe or get captions/subtitles from a YouTube video. |
| 4 | allowed-tools Bash,Read,Write |
| 5 | |
| 6 | |
| 7 | # YouTube Transcript Downloader |
| 8 | |
| 9 | This skill helps download transcripts (subtitles/captions) from YouTube videos using yt-dlp. |
| 10 | |
| 11 | ## When to Use This Skill |
| 12 | |
| 13 | Activate this skill when the user: |
| 14 | Provides a YouTube URL and wants the transcript |
| 15 | Asks to "download transcript from YouTube" |
| 16 | Wants to "get captions" or "get subtitles" from a video |
| 17 | Asks to "transcribe a YouTube video" |
| 18 | Needs text content from a YouTube video |
| 19 | |
| 20 | ## How It Works |
| 21 | |
| 22 | ### Priority Order: |
| 23 | **Check if yt-dlp is installed** - install if needed |
| 24 | **List available subtitles** - see what's actually available |
| 25 | **Try manual subtitles first** (`--write-sub`) - highest quality |
| 26 | **Fallback to auto-generated** (`--write-auto-sub`) - usually available |
| 27 | **Last resort: Whisper transcription** - if no subtitles exist (requires user confirmation) |
| 28 | **Confirm the download** and show the user where the file is saved |
| 29 | **Optionally clean up** the VTT format if the user wants plain text |
| 30 | |
| 31 | ## Installation Check |
| 32 | |
| 33 | **IMPORTANT**: Always check if yt-dlp is installed first: |
| 34 | |
| 35 | |
| 36 | which yt-dlp || command -v yt-dlp |
| 37 | |
| 38 | |
| 39 | ### If Not Installed |
| 40 | |
| 41 | Attempt automatic installation based on the system: |
| 42 | |
| 43 | **macOS (Homebrew)**: |
| 44 | |
| 45 | brew install yt-dlp |
| 46 | |
| 47 | |
| 48 | **Linux (apt/Debian/Ubuntu)**: |
| 49 | |
| 50 | sudo apt update && sudo apt install -y yt-dlp |
| 51 | |
| 52 | |
| 53 | **Alternative (pip - works on all systems)**: |
| 54 | |
| 55 | pip3 install yt-dlp |
| 56 | # or |
| 57 | python3 -m pip install yt-dlp |
| 58 | |
| 59 | |
| 60 | **If installation fails**: Inform the user they need to install yt-dlp manually and provide them with installation instructions from https://github.com/yt-dlp/yt-dlp#installation |
| 61 | |
| 62 | ## Check Available Subtitles |
| 63 | |
| 64 | **ALWAYS do this first** before attempting to download: |
| 65 | |
| 66 | |
| 67 | yt-dlp --list-subs "YOUTUBE_URL" |
| 68 | |
| 69 | |
| 70 | This shows what subtitle types are available without downloading anything. Look for: |
| 71 | Manual subtitles (better quality) |
| 72 | Auto-generated subtitles (usually available) |
| 73 | Available languages |
| 74 | |
| 75 | ## Download Strategy |
| 76 | |
| 77 | ### Option 1: Manual Subtitles (Preferred) |
| 78 | |
| 79 | Try this first - highest quality, human-created: |
| 80 | |
| 81 | |
| 82 | yt-dlp --write-sub --skip-download --output "OUTPUT_NAME" "YOUTUBE_URL" |
| 83 | |
| 84 | |
| 85 | ### Option 2: Auto-Generated Subtitles (Fallback) |
| 86 | |
| 87 | If manual subtitles aren't available: |
| 88 | |
| 89 | |
| 90 | yt-dlp --write-auto-sub --skip-download --output "OUTPUT_NAME" "YOUTUBE_URL" |
| 91 | |
| 92 | |
| 93 | Both commands create a `.vtt` file (WebVTT subtitle format). |
| 94 | |
| 95 | ## Option 3: Whisper Transcription (Last Resort) |
| 96 | |
| 97 | **ONLY use this if both manual and auto-generated subtitles are unavailable.** |
| 98 | |
| 99 | ### Step 1: Show File Size and Ask for Confirmation |
| 100 | |
| 101 | |
| 102 | # Get audio file size estimate |
| 103 | yt-dlp --print "%(filesize,filesize_approx)s" -f "bestaudio" "YOUTUBE_URL" |
| 104 | |
| 105 | # Or get duration to estimate |
| 106 | yt-dlp --print "%(duration)s %(title)s" "YOUTUBE_URL" |
| 107 | |
| 108 | |
| 109 | **IMPORTANT**: Display the file size to the user and ask: "No subtitles are available. I can download the audio (approximately X MB) and transcribe it using Whisper. Would you like to proceed?" |
| 110 | |
| 111 | **Wait for user confirmation before continuing.** |
| 112 | |
| 113 | ### Step 2: Check for Whisper Installation |
| 114 | |
| 115 | |
| 116 | command -v whisper |
| 117 | |
| 118 | |
| 119 | If not installed, ask user: "Whisper is not installed. Install it with `pip install openai-whisper` (requires ~1-3GB for models)? This is a one-time installation." |
| 120 | |
| 121 | **Wait for user confirmation before installing.** |
| 122 | |
| 123 | Install if approved: |
| 124 | |
| 125 | pip3 install openai-whisper |
| 126 | |
| 127 | |
| 128 | ### Step 3: Download Audio Only |
| 129 | |
| 130 | |
| 131 | yt-dlp -x --audio-format mp3 --output "audio_%(id)s.%(ext)s" "YOUTUBE_URL" |
| 132 | |
| 133 | |
| 134 | ### Step 4: Transcribe with Whisper |
| 135 | |
| 136 | |
| 137 | # Auto-detect language (recommended) |
| 138 | whisper audio_VIDEO_ID.mp3 --model base --output_format vtt |
| 139 | |
| 140 | # Or specify language if known |
| 141 | whisper audio_VIDEO_ID.mp3 --model base --language en --output_format vtt |
| 142 | |
| 143 | |
| 144 | **Model Options** (stick to `base` for now): |
| 145 | `tiny` - fastest, least accurate (~1GB) |
| 146 | `base` - good balance (~1GB) ← **USE THIS** |
| 147 | `small` - better accuracy (~2GB) |
| 148 | `medium` - very good (~5GB) |
| 149 | `large` - best accuracy (~10GB) |
| 150 | |
| 151 | ### Step 5: Cleanup |
| 152 | |
| 153 | After transcription completes, ask user: "Transcription complete! Would you like me to delete the audio file to save space?" |
| 154 | |
| 155 | If yes: |
| 156 | |
| 157 | rm audio_VIDEO_ID.mp3 |
| 158 | |
| 159 | |
| 160 | ## Getting Video Information |
| 161 | |
| 162 | ### Extract Video Title (for filename) |
| 163 | |
| 164 | |
| 165 | yt-dlp --print "%(title)s" "YOUTUBE_URL" |
| 166 | |
| 167 | |
| 168 | Use this to create meaningful filenames based on the video title. Clean the title for filesystem compatibility: |
| 169 | Replace `/` with `-` |
| 170 | Replace special characters that might cause issues |
| 171 | Consider using sanitized version: `$(yt-dlp --print "%(title)s" "URL" | tr '/' '-' | tr ':' '-')` |
| 172 | |
| 173 | ## Post-Processing |
| 174 | |
| 175 | ### Convert to Plain Text (Recommended) |
| 176 | |
| 177 | YouTube's auto-generated VTT files contain **duplicate lines** because captions are shown progressively with overlapping timestamps. Always deduplicate when converting to plain text while preserving the original speaking order. |
| 178 | |
| 179 | |
| 180 | python3 -c " |
| 181 | import sys, re |
| 182 | seen = set() |
| 183 | with open('transcript.en.vtt', 'r') as f: |
| 184 | for line in f: |
| 185 | line = line.strip() |
| 186 | if line and not line.startswith('WEBVTT') and not line.startswith('Kind:') and not line.startswith('Language:') and '-->' not in line: |
| 187 | clean = re.sub('<[^>]*>', '', line) |
| 188 | clean = clean.replace('&', '&').replace('>', '>').replace('<', '<') |
| 189 | if clean and clean not in seen: |
| 190 | print(clean) |
| 191 | seen.add(clean) |
| 192 | " > transcript.txt |
| 193 | |
| 194 | |
| 195 | ### Complete Post-Processing with Video Title |
| 196 | |
| 197 | |
| 198 | # Get video title |
| 199 | VIDEO_TITLE=$(yt-dlp --print "%(title)s" "YOUTUBE_URL" | tr '/' '_' | tr ':' '-' | tr '?' '' | tr '"' '') |
| 200 | |
| 201 | # Find the VTT file |
| 202 | VTT_FILE=$(ls *.vtt | head -n 1) |
| 203 | |
| 204 | # Convert with deduplication |
| 205 | python3 -c " |
| 206 | import sys, re |
| 207 | seen = set() |
| 208 | with open('$VTT_FILE', 'r') as f: |
| 209 | for line in f: |
| 210 | line = line.strip() |
| 211 | if line and not line.startswith('WEBVTT') and not line.startswith('Kind:') and not line.startswith('Language:') and '-->' not in line: |
| 212 | clean = re.sub('<[^>]*>', '', line) |
| 213 | clean = clean.replace('&', '&').replace('>', '>').replace('<', '<') |
| 214 | if clean and clean not in seen: |
| 215 | print(clean) |
| 216 | seen.add(clean) |
| 217 | " > "${VIDEO_TITLE}.txt" |
| 218 | |
| 219 | echo "✓ Saved to: ${VIDEO_TITLE}.txt" |
| 220 | |
| 221 | # Clean up VTT file |
| 222 | rm "$VTT_FILE" |
| 223 | echo "✓ Cleaned up temporary VTT file" |
| 224 | |
| 225 | |
| 226 | ## Output Formats |
| 227 | |
| 228 | **VTT format** (`.vtt`): Includes timestamps and formatting, good for video players |
| 229 | **Plain text** (`.txt`): Just the text content, good for reading or analysis |
| 230 | |
| 231 | ## Tips |
| 232 | |
| 233 | The filename will be `{output_name}.{language_code}.vtt` (e.g., `transcript.en.vtt`) |
| 234 | Most YouTube videos have auto-generated English subtitles |
| 235 | Some videos may have multiple language options |
| 236 | If auto-subtitles aren't available, try `--write-sub` instead for manual subtitles |
| 237 | |
| 238 | ## Complete Workflow Example |
| 239 | |
| 240 | |
| 241 | VIDEO_URL="https://www.youtube.com/watch?v=dQw4w9WgXcQ" |
| 242 | |
| 243 | # Get video title for filename |
| 244 | VIDEO_TITLE=$(yt-dlp --print "%(title)s" "$VIDEO_URL" | tr '/' '_' | tr ':' '-' | tr '?' '' | tr '"' '') |
| 245 | OUTPUT_NAME="transcript_temp" |
| 246 | |
| 247 | # ============================================ |
| 248 | # STEP 1: Check if yt-dlp is installed |
| 249 | # ============================================ |
| 250 | if ! command -v yt-dlp &> /dev/null; then |
| 251 | echo "yt-dlp not found, attempting to install..." |
| 252 | if command -v brew &> /dev/null; then |
| 253 | brew install yt-dlp |
| 254 | elif command -v apt &> /dev/null; then |
| 255 | sudo apt update && sudo apt install -y yt-dlp |
| 256 | else |
| 257 | pip3 install yt-dlp |
| 258 | fi |
| 259 | fi |
| 260 | |
| 261 | # ============================================ |
| 262 | # STEP 2: List available subtitles |
| 263 | # ============================================ |
| 264 | echo "Checking available subtitles..." |
| 265 | yt-dlp --list-subs "$VIDEO_URL" |
| 266 | |
| 267 | # ============================================ |
| 268 | # STEP 3: Try manual subtitles first |
| 269 | # ============================================ |
| 270 | echo "Attempting to download manual subtitles..." |
| 271 | if yt-dlp --write-sub --skip-download --output "$OUTPUT_NAME" "$VIDEO_URL" 2>/dev/null; then |
| 272 | echo "✓ Manual subtitles downloaded successfully!" |
| 273 | ls -lh ${OUTPUT_NAME}.* |
| 274 | else |
| 275 | # ============================================ |
| 276 | # STEP 4: Fallback to auto-generated |
| 277 | # ============================================ |
| 278 | echo "Manual subtitles not available. Trying auto-generated..." |
| 279 | if yt-dlp --write-auto-sub --skip-download --output "$OUTPUT_NAME" "$VIDEO_URL" 2>/dev/null; then |
| 280 | echo "✓ Auto-generated subtitles downloaded successfully!" |
| 281 | ls -lh ${OUTPUT_NAME}.* |
| 282 | else |
| 283 | # ============================================ |
| 284 | # STEP 5: Last resort - Whisper transcription |
| 285 | # ============================================ |
| 286 | echo "⚠ No subtitles available for this video." |
| 287 | |
| 288 | # Get file size |
| 289 | FILE_SIZE=$(yt-dlp --print "%(filesize_approx)s" -f "bestaudio" "$VIDEO_URL") |
| 290 | DURATION=$(yt-dlp --print "%(duration)s" "$VIDEO_URL") |
| 291 | TITLE=$(yt-dlp --print "%(title)s" "$VIDEO_URL") |
| 292 | |
| 293 | echo "Video: $TITLE" |
| 294 | echo "Duration: $((DURATION / 60)) minutes" |
| 295 | echo "Audio size: ~$((FILE_SIZE / 1024 / 1024)) MB" |
| 296 | echo "" |
| 297 | echo "Would you like to download and transcribe with Whisper? (y/n)" |
| 298 | read -r RESPONSE |
| 299 | |
| 300 | if [[ "$RESPONSE" =~ ^[Yy]$ ]]; then |
| 301 | # Check for Whisper |
| 302 | if ! command -v whisper &> /dev/null; then |
| 303 | echo "Whisper not installed. Install now? (requires ~1-3GB) (y/n)" |
| 304 | read -r INSTALL_RESPONSE |
| 305 | if [[ "$INSTALL_RESPONSE" =~ ^[Yy]$ ]]; then |
| 306 | pip3 install openai-whisper |
| 307 | else |
| 308 | echo "Cannot proceed without Whisper. Exiting." |
| 309 | exit 1 |
| 310 | fi |
| 311 | fi |
| 312 | |
| 313 | # Download audio |
| 314 | echo "Downloading audio..." |
| 315 | yt-dlp -x --audio-format mp3 --output "audio_%(id)s.%(ext)s" "$VIDEO_URL" |
| 316 | |
| 317 | # Get the actual audio filename |
| 318 | AUDIO_FILE=$(ls audio_*.mp3 | head -n 1) |
| 319 | |
| 320 | # Transcribe |
| 321 | echo "Transcribing with Whisper (this may take a few minutes)..." |
| 322 | whisper "$AUDIO_FILE" --model base --output_format vtt |
| 323 | |
| 324 | # Cleanup |
| 325 | echo "Transcription complete! Delete audio file? (y/n)" |
| 326 | read -r CLEANUP_RESPONSE |
| 327 | if [[ "$CLEANUP_RESPONSE" =~ ^[Yy]$ ]]; then |
| 328 | rm "$AUDIO_FILE" |
| 329 | echo "Audio file deleted." |
| 330 | fi |
| 331 | |
| 332 | ls -lh *.vtt |
| 333 | else |
| 334 | echo "Transcription cancelled." |
| 335 | exit 0 |
| 336 | fi |
| 337 | fi |
| 338 | fi |
| 339 | |
| 340 | # ============================================ |
| 341 | # STEP 6: Convert to readable plain text with deduplication |
| 342 | # ============================================ |
| 343 | VTT_FILE=$(ls ${OUTPUT_NAME}*.vtt 2>/dev/null || ls *.vtt | head -n 1) |
| 344 | if [ -f "$VTT_FILE" ]; then |
| 345 | echo "Converting to readable format and removing duplicates..." |
| 346 | python3 -c " |
| 347 | import sys, re |
| 348 | seen = set() |
| 349 | with open('$VTT_FILE', 'r') as f: |
| 350 | for line in f: |
| 351 | line = line.strip() |
| 352 | if line and not line.startswith('WEBVTT') and not line.startswith('Kind:') and not line.startswith('Language:') and '-->' not in line: |
| 353 | clean = re.sub('<[^>]*>', '', line) |
| 354 | clean = clean.replace('&', '&').replace('>', '>').replace('<', '<') |
| 355 | if clean and clean not in seen: |
| 356 | print(clean) |
| 357 | seen.add(clean) |
| 358 | " > "${VIDEO_TITLE}.txt" |
| 359 | echo "✓ Saved to: ${VIDEO_TITLE}.txt" |
| 360 | |
| 361 | # Clean up temporary VTT file |
| 362 | rm "$VTT_FILE" |
| 363 | echo "✓ Cleaned up temporary VTT file" |
| 364 | else |
| 365 | echo "⚠ No VTT file found to convert" |
| 366 | fi |
| 367 | |
| 368 | echo "✓ Complete!" |
| 369 | |
| 370 | |
| 371 | **Note**: This complete workflow handles all scenarios with proper error checking and user prompts at each decision point. |
| 372 | |
| 373 | ## Error Handling |
| 374 | |
| 375 | ### Common Issues and Solutions: |
| 376 | |
| 377 | **1. yt-dlp not installed** |
| 378 | Attempt automatic installation based on system (Homebrew/apt/pip) |
| 379 | If installation fails, provide manual installation link |
| 380 | Verify installation before proceeding |
| 381 | |
| 382 | **2. No subtitles available** |
| 383 | List available subtitles first to confirm |
| 384 | Try both `--write-sub` and `--write-auto-sub` |
| 385 | If both fail, offer Whisper transcription option |
| 386 | Show file size and ask for user confirmation before downloading audio |
| 387 | |
| 388 | **3. Invalid or private video** |
| 389 | Check if URL is correct format: `https://www.youtube.com/watch?v=VIDEO_ID` |
| 390 | Some videos may be private, age-restricted, or geo-blocked |
| 391 | Inform user of the specific error from yt-dlp |
| 392 | |
| 393 | **4. Whisper installation fails** |
| 394 | May require system dependencies (ffmpeg, rust) |
| 395 | Provide fallback: "Install manually with: `pip3 install openai-whisper`" |
| 396 | Check available disk space (models require 1-10GB depending on size) |
| 397 | |
| 398 | **5. Download interrupted or failed** |
| 399 | Check internet connection |
| 400 | Verify sufficient disk space |
| 401 | Try again with `--no-check-certificate` if SSL issues occur |
| 402 | |
| 403 | **6. Multiple subtitle languages** |
| 404 | By default, yt-dlp downloads all available languages |
| 405 | Can specify with `--sub-langs en` for English only |
| 406 | List available with `--list-subs` first |
| 407 | |
| 408 | ### Best Practices: |
| 409 | |
| 410 | ✅ Always check what's available before attempting download (`--list-subs`) |
| 411 | ✅ Verify success at each step before proceeding to next |
| 412 | ✅ Ask user before large downloads (audio files, Whisper models) |
| 413 | ✅ Clean up temporary files after processing |
| 414 | ✅ Provide clear feedback about what's happening at each stage |
| 415 | ✅ Handle errors gracefully with helpful messages |
| 416 |
Discussion
Browse more free Claude skills or everything in Content creator.