agentsclimarketplace

Youtube content

Skill furkangonel/cowrangler/bundled_skills/media/youtube-content

Autonomous terminal AI agent for workflows and feasible project procedures. Co-Worker Co-Wrangler πŸ™

Install
npx -y skills add furkangonel/cowrangler --skill youtube-content

Assembled 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

  1. Go to https://console.cloud.google.com
  2. Create a project (or select an existing one)
  3. Enable YouTube Data API v3 under APIs & Services β†’ Library
  4. Go to APIs & Services β†’ Credentials β†’ Create Credentials β†’ API key
  5. (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).

OperationCost (units)
search.list100
videos.list1
channels.list1
playlistItems.list1
captions.list50

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.list is 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_KEY set and restricted to YouTube Data API v3
  • Using batch videos.list?id=... for multi-video metadata (not looping single calls)
  • Pagination handled with nextPageToken for result sets > maxResults
  • Quota usage monitored β€” search.list costs 100 units per call
  • youtube-transcript-api installed 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.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.