youtube-harvester
Extract transcripts and metadata from YouTube videos
What this skill does
# YouTube Harvester Skill
> Extract and ingest YouTube video transcripts into RAG with proper chunking and metadata.
## Overview
YouTube is a rich source of tutorials, lectures, and explanations. This skill covers:
- Transcript extraction (manual and auto-generated)
- Timestamp-aware chunking
- Playlist and channel harvesting
- Metadata enrichment
## Prerequisites
```bash
# Install yt-dlp for video metadata and subtitles
pip install yt-dlp
# Install youtube-transcript-api for cleaner transcript access
pip install youtube-transcript-api
# Optional: for audio transcription fallback
pip install openai-whisper
```
## Extraction Methods
### Method 1: youtube-transcript-api (Recommended)
Best for clean transcript text with timestamps.
```python
#!/usr/bin/env python3
"""Extract YouTube transcripts using youtube-transcript-api."""
from youtube_transcript_api import YouTubeTranscriptApi
from youtube_transcript_api.formatters import TextFormatter
import json
import re
from typing import Dict, List, Optional
from datetime import datetime
def extract_video_id(url: str) -> str:
"""Extract video ID from various YouTube URL formats."""
patterns = [
r'(?:v=|/v/|youtu\.be/)([a-zA-Z0-9_-]{11})',
r'(?:embed/)([a-zA-Z0-9_-]{11})',
]
for pattern in patterns:
match = re.search(pattern, url)
if match:
return match.group(1)
raise ValueError(f"Could not extract video ID from: {url}")
def get_transcript(video_id: str, languages: List[str] = ['en']) -> List[Dict]:
"""
Fetch transcript for a video.
Args:
video_id: YouTube video ID
languages: Preferred languages in order
Returns:
List of transcript segments with text, start, duration
"""
try:
# Try to get manual captions first
transcript_list = YouTubeTranscriptApi.list_transcripts(video_id)
try:
transcript = transcript_list.find_manually_created_transcript(languages)
except:
# Fall back to auto-generated
transcript = transcript_list.find_generated_transcript(languages)
return transcript.fetch()
except Exception as e:
print(f"Error fetching transcript: {e}")
return []
def format_timestamp(seconds: float) -> str:
"""Convert seconds to HH:MM:SS format."""
hours = int(seconds // 3600)
minutes = int((seconds % 3600) // 60)
secs = int(seconds % 60)
if hours > 0:
return f"{hours:02d}:{minutes:02d}:{secs:02d}"
return f"{minutes:02d}:{secs:02d}"
def get_video_metadata(video_id: str) -> Dict:
"""Get video metadata using yt-dlp."""
import subprocess
import json
result = subprocess.run(
['yt-dlp', '--dump-json', '--no-download', f'https://youtube.com/watch?v={video_id}'],
capture_output=True,
text=True
)
if result.returncode == 0:
data = json.loads(result.stdout)
return {
'title': data.get('title'),
'channel': data.get('channel'),
'channel_id': data.get('channel_id'),
'upload_date': data.get('upload_date'),
'duration': data.get('duration'),
'view_count': data.get('view_count'),
'description': data.get('description', '')[:500], # Truncate
'tags': data.get('tags', [])[:10], # Limit tags
}
return {}
```
### Method 2: yt-dlp Subtitles
Better for batch processing and when API limits are hit.
```bash
#!/bin/bash
# Extract subtitles using yt-dlp
VIDEO_URL="$1"
OUTPUT_DIR="${2:-.}"
# Download auto-generated subtitles
yt-dlp \
--write-auto-sub \
--sub-lang en \
--sub-format vtt \
--skip-download \
--output "$OUTPUT_DIR/%(title)s.%(ext)s" \
"$VIDEO_URL"
# Convert VTT to plain text
for vtt in "$OUTPUT_DIR"/*.vtt; do
# Remove VTT formatting, keep just text
sed -e '/^WEBVTT/d' \
-e '/^Kind:/d' \
-e '/^Language:/d' \
-e '/^[0-9][0-9]:[0-9][0-9]/d' \
-e '/-->/d' \
-e 's/<[^>]*>//g' \
-e '/^$/d' \
"$vtt" > "${vtt%.vtt}.txt"
done
```
## Chunking Strategies
### Strategy 1: Time-Based Chunks
Split transcript into fixed time intervals.
```python
def chunk_by_time(
transcript: List[Dict],
chunk_duration: int = 300 # 5 minutes
) -> List[Dict]:
"""
Chunk transcript by time intervals.
Args:
transcript: List of transcript segments
chunk_duration: Seconds per chunk
"""
chunks = []
current_chunk = {
'text': '',
'start': 0,
'end': 0,
'segments': []
}
for segment in transcript:
segment_start = segment['start']
# Check if we need to start a new chunk
if segment_start >= current_chunk['start'] + chunk_duration:
if current_chunk['text']:
chunks.append(current_chunk)
current_chunk = {
'text': '',
'start': segment_start,
'end': segment_start,
'segments': []
}
current_chunk['text'] += ' ' + segment['text']
current_chunk['end'] = segment['start'] + segment.get('duration', 0)
current_chunk['segments'].append(segment)
# Don't forget the last chunk
if current_chunk['text']:
chunks.append(current_chunk)
return chunks
```
### Strategy 2: Topic-Based Chunks
Split when topic appears to change (silence gaps or topic markers).
```python
def chunk_by_topic(
transcript: List[Dict],
gap_threshold: float = 5.0, # Seconds of silence indicating topic change
min_chunk_size: int = 100 # Minimum words per chunk
) -> List[Dict]:
"""
Chunk transcript by topic boundaries.
Uses gaps in speech and sentence boundaries to identify topic changes.
"""
chunks = []
current_chunk = {
'text': '',
'start': 0,
'end': 0,
'word_count': 0
}
prev_end = 0
for segment in transcript:
segment_start = segment['start']
gap = segment_start - prev_end
word_count = len(segment['text'].split())
# Check for topic boundary
is_boundary = (
gap > gap_threshold and
current_chunk['word_count'] >= min_chunk_size
)
if is_boundary:
if current_chunk['text']:
chunks.append(current_chunk)
current_chunk = {
'text': '',
'start': segment_start,
'end': segment_start,
'word_count': 0
}
current_chunk['text'] += ' ' + segment['text']
current_chunk['end'] = segment_start + segment.get('duration', 0)
current_chunk['word_count'] += word_count
prev_end = current_chunk['end']
if current_chunk['text']:
chunks.append(current_chunk)
return chunks
```
### Strategy 3: Semantic Chunks
Use embeddings to find natural topic boundaries.
```python
def chunk_by_semantics(
transcript: List[Dict],
similarity_threshold: float = 0.7,
window_size: int = 5
) -> List[Dict]:
"""
Chunk based on semantic similarity between segments.
Groups semantically similar consecutive segments together.
"""
from sentence_transformers import SentenceTransformer
import numpy as np
model = SentenceTransformer('all-MiniLM-L6-v2')
# Combine segments into windows for more stable embeddings
windows = []
for i in range(0, len(transcript), window_size):
window_text = ' '.join(
s['text'] for s in transcript[i:i+window_size]
)
windows.append({
'text': window_text,
'start': transcript[i]['start'],
'end': transcript[min(i+window_size-1, len(transcript)-1)]['start'],
'segments': transcript[i:i+window_size]
})
# Get embeddings
embeddings = model.encode([w['text'] for w in windows])
# Find boRelated in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.