Skill: bstokes0971/save-image (version 1.0.0)

--- name: save-image description: Download images, GIFs, and media from the web correctly — with proper browser headers, Referer spoofing, and CDN-aware two-step scraping. Use when any model needs to fetch an image, GIF, meme, or media file from a URL. Prevents the common failure modes of bare curl (no headers, guessed URLs, CDN blocks). Covers the full decision tree: GIF/meme → gifgrep, video/social → yt-dlp, CDN-protected image → scrape-then-fetch, plain image → curl with headers. Trigger on any request involving "download image", "save image", "grab a meme", "fetch a GIF", "get this image", or any media download from a URL. --- # save-image Never guess at direct image URLs. Never use bare curl with no headers. Always pick the right tool. ## Decision Tree ``` What are you downloading? ├── GIF or meme search (no specific URL) → gifgrep ├── Video or social media (YouTube, TikTok, Twitter/X, Instagram, Reddit) → yt-dlp ├── Image URL from a CDN-protected site (Imgur, KnowYourMeme, Reddit, etc.) → two-step scrape └── Plain image URL (direct .jpg/.png/.gif link, no CDN) → curl with headers ``` See [references/tools.md](references/tools.md) for tool-specific flags and examples. ## Rule 1: Always send browser headers Never do bare `curl <url>`. CDNs check `Referer` and `User-Agent` and block bot-looking requests. Minimum headers for any curl image fetch: ```bash curl -s "<url>" \ -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36" \ -H "Referer: <origin-of-the-page-hosting-the-image>" \ -H "Accept: image/webp,image/apng,image/*,*/*;q=0.8" \ -o <output-file> ``` Use `scripts/fetch-image.sh` for a ready-made wrapper. ## Rule 2: Two-step scrape for CDN images If you don't have a direct image URL (or a guessed URL fails), scrape the page first: ```bash # Step 1: Scrape the page for real CDN image URLs curl -s "<page-url>" \ -H "User-Agent: Mozilla/5.0 ..." \ | grep -oE 'https://[a-z0-9._-]+\.(com|net|org)/[^"]+\.(jpg|jpeg|png|gif|webp)' \ | head -10 # Step 2: Download the URL you want with Referer set to the page curl -s "<image-url>" \ -H "Referer: <page-url>" \ -H "User-Agent: Mozilla/5.0 ..." \ -o <output-file> ``` ## Rule 3: Verify the download Always confirm you got an actual image, not an HTML error page: ```bash file <output-file> # Should say: JPEG image data / PNG image data / GIF image data # NOT: HTML document / ASCII text ``` ## Quick Examples **GIF search (no URL):** ```bash gifgrep "distracted boyfriend" --download --max 1 ``` **KnowYourMeme / CDN-protected image:** ```bash # Scrape page → get URL → fetch with Referer curl -s "https://knowyourmeme.com/memes/doge" -H "User-Agent: Mozilla/5.0..." \ | grep -oE 'https://i\.kym-cdn\.com/[^"]+\.(jpg|png|gif)' | head -3 # Then fetch the URL with Referer: https://knowyourmeme.com/ ``` **Direct image URL:** ```bash bash scripts/fetch-image.sh "https://example.com/image.jpg" ~/Downloads/image.jpg "https://example.com" ``` **Video/social media:** ```bash yt-dlp "<url>" -o ~/Downloads/%(title)s.%(ext)s ```

path: references/tools.md
content: # Tool Reference

## gifgrep — GIF and meme search

Use when searching by keyword (no specific URL). Handles Tenor/Giphy, proper headers, downloads cleanly.

```bash
# Search and print URLs
gifgrep "doge" --max 5 --format url

# Download first result
gifgrep "distracted boyfriend" --download --max 1

# JSON output (id, title, url, dimensions)
gifgrep search --json "this is fine" | jq '.[0]'

# Interactive TUI browser
gifgrep tui "muppets"

# Extract a still frame from a downloaded GIF
gifgrep still ./clip.gif --at 1.5s -o still.png

# Generate a sprite sheet (9 frames, 3-col grid)
gifgrep sheet ./clip.gif --frames 9 --cols 3 -o sheet.png
```

Providers: `--source auto|tenor|giphy` (GIPHY_API_KEY required for Giphy)

---

## yt-dlp — Video and social media

Use for YouTube, TikTok, Twitter/X, Instagram, Reddit, and 1000+ other sites.

```bash
# Download video (best quality)
yt-dlp "<url>" -o ~/Downloads/%(title)s.%(ext)s

# Extract audio only
yt-dlp -x --audio-format mp3 "<url>" -o ~/Downloads/%(title)s.%(ext)s

# List available formats
yt-dlp -F "<url>"

# Download specific format
yt-dlp -f 137+140 "<url>"

# Thumbnail only
yt-dlp --write-thumbnail --skip-download "<url>"
```

---

## curl with headers — Direct image URLs

Use for plain image URLs or CDN-protected images after scraping the real URL.

### Minimum viable headers
```bash
curl -s "<url>" \
  -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36" \
  -H "Referer: <page-url>" \
  -H "Accept: image/webp,image/apng,image/*,*/*;q=0.8" \
  -o <output-file> \
  -w "%{http_code} | %{size_download} bytes\n"
```

### Two-step scrape pattern
```bash
# 1. Get real CDN URLs from the page
curl -s "<page-url>" \
  -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 ..." \
  | grep -oE 'https://[a-z0-9._-]+\.(com|net|org)/[^"'\'']+\.(jpg|jpeg|png|gif|webp)' \
  | sort -u | head -10

# 2. Fetch the image with Referer pointing back to the page
curl -s "<cdn-image-url>" \
  -H "Referer: <page-url>" \
  -H "User-Agent: Mozilla/5.0 ..." \
  -o <output-file>
```

### Known CDN Referer requirements
| Site | CDN domain | Required Referer |
|------|-----------|-----------------|
| KnowYourMeme | i.kym-cdn.com | https://knowyourmeme.com/ |
| Imgur | i.imgur.com | https://imgur.com/ |
| Reddit media | preview.redd.it / i.redd.it | https://www.reddit.com/ |
| Giphy | media.giphy.com | https://giphy.com/ |
| Tenor | media.tenor.com | https://tenor.com/ |

---

## What won't work

- **Cloudflare-protected sites** — JS challenge required; curl can't solve it regardless of headers
- **Login-gated images** — Need session cookies; use browser automation instead
- **Signed S3/CDN URLs with expiry** — URL itself contains auth; must be scraped fresh each time


path: scripts/fetch-image.sh
content: #!/usr/bin/env bash
# fetch-image.sh — Download an image with proper browser headers
# Usage: fetch-image.sh <url> <output-file> [referer]
# Example: fetch-image.sh "https://i.kym-cdn.com/doge.jpg" ~/Downloads/doge.jpg "https://knowyourmeme.com/"

set -euo pipefail

URL="${1:-}"
OUTPUT="${2:-}"
REFERER="${3:-${URL}}"  # Default Referer to the URL itself if not provided

if [[ -z "$URL" || -z "$OUTPUT" ]]; then
  echo "Usage: fetch-image.sh <url> <output-file> [referer]" >&2
  exit 1
fi

UA="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"

echo "Fetching: $URL"
echo "Output:   $OUTPUT"
echo "Referer:  $REFERER"

HTTP_CODE=$(curl -s "$URL" \
  -H "User-Agent: $UA" \
  -H "Referer: $REFERER" \
  -H "Accept: image/webp,image/apng,image/*,*/*;q=0.8" \
  -H "Accept-Language: en-US,en;q=0.9" \
  -o "$OUTPUT" \
  -w "%{http_code}")

if [[ "$HTTP_CODE" != "200" ]]; then
  echo "ERROR: HTTP $HTTP_CODE — download failed" >&2
  exit 1
fi

SIZE=$(stat -f%z "$OUTPUT" 2>/dev/null || stat -c%s "$OUTPUT" 2>/dev/null || echo "?")
TYPE=$(file "$OUTPUT" | cut -d: -f2 | xargs)

echo "Done: HTTP $HTTP_CODE | $SIZE bytes | $TYPE"

# Warn if we got HTML instead of an image
if echo "$TYPE" | grep -qi "html\|text"; then
  echo "WARNING: Output looks like HTML, not an image. CDN may have blocked the request." >&2
  exit 1
fi


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)