Youtube content
Skill furkangonel/cowrangler/bundled_skills/media/youtube-content
Autonomous terminal AI agent for workflows and feasible project procedures. Co-Worker Co-Wrangler π
npx -y skills add furkangonel/cowrangler --skill youtube-contentAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
YouTube Data API β search, video details, channel info, transcripts.
SKILL.md
9.5 KB, ~2.6k tokens by cl100k_base, as published. Nobody here has run it
YouTube Content SOP
Search YouTube, fetch video and channel metadata, extract transcripts, and paginate through results via the YouTube Data API v3.
When to Use
- User wants to search YouTube for videos or channels
- User wants to get video details (duration, view count, tags, description)
- User wants channel statistics or upload history
- User wants to extract a video transcript or subtitles
- User wants to paginate through a large result set
Part 1 β Setup
1. Get an API Key
- Go to https://console.cloud.google.com
- Create a project (or select an existing one)
- Enable YouTube Data API v3 under APIs & Services β Library
- Go to APIs & Services β Credentials β Create Credentials β API key
- (Recommended) Restrict the key to YouTube Data API v3 and your IP
2. Environment Variable
export YOUTUBE_API_KEY="AIzaSy..."
Or in ~/.cowrangler/credentials.env:
YOUTUBE_API_KEY=AIzaSy...
3. Shell Helper
YOUTUBE_API_KEY="${YOUTUBE_API_KEY:-$(grep '^YOUTUBE_API_KEY=' ~/.cowrangler/credentials.env 2>/dev/null | cut -d= -f2 | tr -d '\n\r')}"
YT_BASE="https://www.googleapis.com/youtube/v3"
yt_get() {
# $1 = endpoint path + params (already URL-encoded)
curl -s "${YT_BASE}/$1&key=${YOUTUBE_API_KEY}"
}
Part 2 β Search
Search Videos
QUERY=$(python3 -c "import urllib.parse; print(urllib.parse.quote('python async tutorial'))")
yt_get "search?part=snippet&type=video&q=${QUERY}&maxResults=10&order=relevance" \
| python3 -c "
import sys, json
data = json.load(sys.stdin)
for item in data.get('items', []):
vid_id = item['id']['videoId']
title = item['snippet']['title']
channel = item['snippet']['channelTitle']
published = item['snippet']['publishedAt'][:10]
print(f'{vid_id} [{published}] {channel:30s} {title[:60]}')
print()
print('nextPageToken:', data.get('nextPageToken', '(none)'))
"
Search Channels
QUERY=$(python3 -c "import urllib.parse; print(urllib.parse.quote('machine learning'))")
yt_get "search?part=snippet&type=channel&q=${QUERY}&maxResults=5" \
| python3 -c "
import sys, json
data = json.load(sys.stdin)
for item in data.get('items', []):
ch_id = item['id']['channelId']
title = item['snippet']['channelTitle']
desc = item['snippet']['description'][:80]
print(f'{ch_id} {title:30s} {desc}')
"
Part 3 β Video Details
Get Video Metadata
VIDEO_ID="dQw4w9WgXcQ"
yt_get "videos?part=snippet,contentDetails,statistics&id=${VIDEO_ID}" \
| python3 -c "
import sys, json, re
data = json.load(sys.stdin)
v = data['items'][0]
snip = v['snippet']
cd = v['contentDetails']
stat = v['statistics']
# Parse ISO 8601 duration (e.g. PT4M13S β 4m13s)
dur = cd['duration']
m = re.match(r'PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?', dur)
h, mi, s = (int(m.group(i) or 0) for i in (1, 2, 3))
duration_str = f'{h}h{mi}m{s}s' if h else f'{mi}m{s}s'
print('Title: ', snip['title'])
print('Channel: ', snip['channelTitle'])
print('Published: ', snip['publishedAt'][:10])
print('Duration: ', duration_str)
print('Views: ', int(stat.get('viewCount', 0)):,)
print('Likes: ', int(stat.get('likeCount', 0)):,)
print('Comments: ', int(stat.get('commentCount', 0)):,)
print('Tags: ', ', '.join(snip.get('tags', [])[:8]))
print('Description:', snip['description'][:200])
"
Batch Multiple Videos (up to 50 IDs per request)
IDS="id1,id2,id3,id4,id5"
yt_get "videos?part=snippet,statistics&id=${IDS}" \
| python3 -c "
import sys, json
data = json.load(sys.stdin)
print('id,title,views,likes')
for v in data['items']:
stat = v['statistics']
title = v['snippet']['title'].replace(',', ' ')
views = stat.get('viewCount', 0)
likes = stat.get('likeCount', 0)
print(f\"{v['id']},{title},{views},{likes}\")
"
Part 4 β Channel Info
Channel Statistics
CHANNEL_ID="UCBcRF18a7Qf58cCRy5xuWwQ"
yt_get "channels?part=snippet,statistics,contentDetails&id=${CHANNEL_ID}" \
| python3 -c "
import sys, json
ch = json.load(sys.stdin)['items'][0]
snip = ch['snippet']
stat = ch['statistics']
cd = ch['contentDetails']
print('Name: ', snip['title'])
print('Description: ', snip['description'][:150])
print('Subscribers: ', int(stat.get('subscriberCount', 0)):,)
print('Total views: ', int(stat.get('viewCount', 0)):,)
print('Video count: ', stat.get('videoCount', '?'))
print('Uploads playlist:', cd['relatedPlaylists']['uploads'])
"
Get Channel ID from Username / Handle
HANDLE="mkbhd"
yt_get "search?part=snippet&type=channel&q=${HANDLE}&maxResults=1" \
| python3 -c "
import sys, json
items = json.load(sys.stdin).get('items', [])
if items:
print('Channel ID:', items[0]['id']['channelId'])
"
List Recent Uploads from a Channel
# First get the uploads playlist ID (from contentDetails above)
UPLOADS_PLAYLIST="UUBcRF18a7Qf58cCRy5xuWwQ"
yt_get "playlistItems?part=snippet,contentDetails&playlistId=${UPLOADS_PLAYLIST}&maxResults=20" \
| python3 -c "
import sys, json
data = json.load(sys.stdin)
for item in data['items']:
vid_id = item['contentDetails']['videoId']
title = item['snippet']['title']
published = item['contentDetails']['videoPublishedAt'][:10]
print(f'{vid_id} {published} {title}')
"
Part 5 β Pagination
All list endpoints return a nextPageToken when more results exist. Use it to walk through all results.
#!/usr/bin/env python3
"""List all videos from an uploads playlist."""
import os, json, urllib.request, urllib.parse
API_KEY = os.environ["YOUTUBE_API_KEY"]
PLAYLIST_ID = "UUBcRF18a7Qf58cCRy5xuWwQ"
BASE = "https://www.googleapis.com/youtube/v3"
def fetch_page(playlist_id, page_token=None):
params = {
"part": "snippet,contentDetails",
"playlistId": playlist_id,
"maxResults": 50,
"key": API_KEY,
}
if page_token:
params["pageToken"] = page_token
url = f"{BASE}/playlistItems?{urllib.parse.urlencode(params)}"
with urllib.request.urlopen(url) as r:
return json.load(r)
all_videos = []
next_token = None
while True:
data = fetch_page(PLAYLIST_ID, next_token)
for item in data["items"]:
all_videos.append({
"id": item["contentDetails"]["videoId"],
"title": item["snippet"]["title"],
"published": item["contentDetails"].get("videoPublishedAt", "")[:10],
})
next_token = data.get("nextPageToken")
if not next_token:
break
print(f"Total videos fetched: {len(all_videos)}")
for v in all_videos[:10]:
print(f" {v['id']} {v['published']} {v['title']}")
Part 6 β Transcript Extraction
The YouTube Data API does not provide transcripts. Use the youtube-transcript-api Python library.
Install
pip install youtube-transcript-api
Fetch Transcript
from youtube_transcript_api import YouTubeTranscriptApi, TranscriptsDisabled, NoTranscriptFound
VIDEO_ID = "dQw4w9WgXcQ"
try:
# Fetch default transcript
transcript = YouTubeTranscriptApi.get_transcript(VIDEO_ID)
full_text = " ".join(entry["text"] for entry in transcript)
print(full_text[:500])
except TranscriptsDisabled:
print("Transcripts are disabled for this video.")
except NoTranscriptFound:
print("No transcript available in any language.")
List Available Transcripts
from youtube_transcript_api import YouTubeTranscriptApi
VIDEO_ID = "dQw4w9WgXcQ"
transcript_list = YouTubeTranscriptApi.list_transcripts(VIDEO_ID)
for t in transcript_list:
print(f" lang={t.language_code} generated={t.is_generated} translatable={t.is_translatable}")
Fetch in a Specific Language
transcript = YouTubeTranscriptApi.get_transcript(VIDEO_ID, languages=["tr", "en"])
Timed Transcript (for subtitle alignment)
transcript = YouTubeTranscriptApi.get_transcript(VIDEO_ID)
for entry in transcript[:20]:
start = entry["start"]
dur = entry["duration"]
text = entry["text"]
mins, secs = divmod(int(start), 60)
print(f"[{mins:02d}:{secs:02d}] {text}")
Part 7 β Quota Awareness
YouTube Data API v3 has a daily quota of 10,000 units per project (free tier).
| Operation | Cost (units) |
|---|---|
search.list | 100 |
videos.list | 1 |
channels.list | 1 |
playlistItems.list | 1 |
captions.list | 50 |
Tips to stay within quota:
- Prefer
videos.list?id=id1,id2,...(batch up to 50) over per-video calls. - Cache results locally; avoid repeated searches for the same query.
search.listis expensive β use playlist crawl instead when you have a channel's upload playlist ID.- Monitor usage at https://console.cloud.google.com/apis/dashboard.
Checklist
- YouTube Data API v3 enabled in Google Cloud Console
-
YOUTUBE_API_KEYset and restricted to YouTube Data API v3 - Using batch
videos.list?id=...for multi-video metadata (not looping single calls) - Pagination handled with
nextPageTokenfor result sets >maxResults - Quota usage monitored β
search.listcosts 100 units per call -
youtube-transcript-apiinstalled for transcript extraction (not part of the official API)
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.
Gives 0 of the 12 instructions most social media skills give in ~2.6k tokens
Counted across 489 of the 492 authors here whose files we hold, read 2026-08-07
- Adapt formats and tone to each platformin 26 of 489, across 14 files
- Build content around three to five pillarsin 25 of 489, across 13 files
- Read product marketing context before asking questionsin 23 of 489, across 13 files
- Respond to all comments on your postsin 21 of 489, across 9 files
- Use the output flag to specify an output directoryin 14 of 489, across 4 files
- Generate output logo images with white backgroundin 13 of 489, across 4 files
- Fix failing generation scripts directlyin 13 of 489, across 4 files
- Ask user about HTML preview after logo generationin 12 of 489, across 3 files
- Run the download script with a URLin 12 of 489, across 3 files
- Implement exponential backoff for 429 responsesin 12 of 489, across 3 files
- Write the hook firstin 12 of 489, across 7 files
- Include a single clear call to actionin 12 of 489, across 9 files
Said here and by no other author read
- use the search endpoint for videos or channels
- use playlist crawl instead of search.list when possible
- cache api results locally to avoid repeated calls
- handle TranscriptsDisabled and NoTranscriptFound exceptions
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.