# Download Online Media

> Download videos, audio, and images from online platforms (X/Twitter, YouTube, Reddit, Instagram, TikTok, etc.) using yt-dlp. Use when the user provides a URL to a social-media post, streaming video, or online clip and asks to save, download, fetch, or archive it. Triggers on phrases like "download this video", "save this clip", "받기", "다운받아줘", or any URL pointing to x.com, twitter.com, youtube.com, reddit.com, instagram.com, tiktok.com, vimeo.com, etc.

- Skill: `wcpaka-lgtm/download-online-media` (Agent Skill)
- Install (CLI): `npx skillmds@latest add wcpaka-lgtm/download-online-media`
- Raw SKILL.md: https://api.skillmd.com/api/skills/wcpaka-lgtm/download-online-media/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: wcpaka-lgtm (https://skillmd.com/u/wcpaka-lgtm)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/wcpaka-lgtm/download-online-media

---


# Download Online Media

## When to use
- User provides a URL to a video/audio/image on a social platform and wants to save it locally
- Need to archive a clip from X, YouTube, Reddit, Instagram, TikTok, etc.
- Want to extract just the audio (as MP3) from a video

## Tool
- **`yt-dlp`** — actively maintained fork of youtube-dl, supports 1000+ sites. Usually pre-installed (check with `which yt-dlp` or `yt-dlp --version`).
- **`ffmpeg`** — required for merging HLS streams. X/Twitter videos are HLS with separate video + audio manifests; yt-dlp downloads both and merges via ffmpeg. Verify with `which ffmpeg` before starting.

## Basic usage
```bash
yt-dlp "<URL>"
```
Saves to the current directory with an auto-generated filename (typically includes the platform's post/video ID).

### Custom output path / filename
```bash
yt-dlp -o "<template>" "<URL>"
```
- `%(id)s` — platform's post/video ID
- `%(title)s` — video title (sanitized for filesystem)
- `%(uploader)s` — channel/author
- `%(ext)s` — file extension (mp4, webm, mp3, …)
- Example: `-o "%(uploader)s_%(title)s_%(id)s.%(ext)s"`

### Quality
Omit flags and let yt-dlp pick the best by default. Override with:
- `-f b` — best **pre-merged** single-file format
- `-f worst` — smallest file
- `-f "bestvideo+bestaudio"` — pick streams then merge (default behavior, no flag needed)

**⚠️ Do NOT use `-f best` on platforms with split HLS streams (X/Twitter, some Instagram reels, Reddit hosted video).** `-f best` selects a single pre-merged file. When the platform only exposes video and audio as **separate HLS manifests** (X is the canonical case), no pre-merged format exists and yt-dlp will pick a video-only stream — **the output has no audio**, even though the merge step "succeeds" and the file plays. yt-dlp prints a `WARNING: "-f best" selects the best pre-merged format which is often not the best option` to flag this.

Correct approach for these platforms: **omit `-f` entirely** and let yt-dlp pick `bestvideo+bestaudio`, download both, then merge via ffmpeg. Always run the ffprobe stream check in the "Always verify" section below — the only reliable way to catch the silent audio-loss is to confirm `codec_type=audio` exists in the output.

---

## ⚠️ Windows MSYS / git-bash pitfall (CRITICAL)

**On Windows under git-bash or MSYS, do NOT pass MSYS-style paths to `-o`.**

A path like `/c/Users/okya1/Videos/x_video.mp4` will be interpreted by yt-dlp as a **literal filename** (because of the leading slash), and a file named `\c\Users\okya1\Videos\x_video.mp4` will be created in your **current working directory** — not in `Videos/`. The download reports success, the exit code is 0, and the file "exists" somewhere, but it is at the wrong location with the wrong name. This is silent and easy to miss.

**Always use Windows-style paths with forward slashes:**
```bash
# CORRECT — Windows-style, leading drive letter, forward slashes
yt-dlp -o "C:/Users/okya1/Downloads/x_video_%(id)s.%(ext)s" "https://x.com/user/status/123"

# WRONG — MSYS-style, will create a file with the literal name in cwd
yt-dlp -o "/c/Users/okya1/Videos/x_video.mp4" "https://x.com/user/status/123"
```

If the user has a default Downloads folder, prefer it over Videos — easier to find, and the Windows shell opens it reliably.

---

## Always verify before reporting success

After `yt-dlp` exits 0, **do not assume the file is where you think it is.** Run a real existence check AND a stream check. This two-step verification is non-negotiable:

```bash
# 1. Existence + size (use Windows-style path with forward slashes)
ls -lh "C:/Users/okya1/Downloads/<filename>.mp4"

# 2. Validate it's a real video with audio streams (use ffprobe)
"<ffmpeg-bin>/ffprobe.exe" -v error -show_format -show_streams "<file>"
```

A valid merged file should report at least:
- one `codec_type=video` stream (h264 / hevc / av1 / vp9)
- one `codec_type=audio` stream (aac / mp3 / opus) ← **critical: missing audio = silent loss from `-f best` on split-HLS platforms (see Quality section)**
- a plausible `duration=` (in seconds)
- `TAG:handler_name=Twitter-vork muxer` (or similar) — confirms a real merge happened, not a stub

### Gotcha: `ls` and `os.path.exists` can disagree
In git-bash / MSYS, `ls -lh "/c/Users/okya1/Downloads/foo.mp4"` and `os.path.exists("C:\\Users\\okya1\\Downloads\\foo.mp4")` can give different answers for the SAME file when path semantics mix POSIX and Windows. A `search_files` (which uses ripgrep) and `ls` can both return the file, but a follow-up `os.path.exists` call with the same Windows-style path may still report False if the earlier `ls` was reading through a different path layer.

**Safe pattern — run BOTH:**
```python
import os
assert os.path.exists(r"C:\Users\okya1\Downloads\foo.mp4"), "file not where you think"
assert os.path.getsize(r"C:\Users\okya1\Downloads\foo.mp4") > 0
```
After every `yt-dlp` run, before claiming success. If `os.path.exists` returns False, fall back to `find` / `ls` in the cwd to discover where the file actually went (likely the MSYS-path pitfall produced a literal-named file there).

If `os.path.exists` (Python) or `ls` can't find the file you expected, search the actual cwd — you may have fallen into the MSYS-path pitfall above and produced a literal-named file there.

### Cleanup of stray files
If you did fall into the MSYS-path pitfall, the bogus file will be in whatever directory you were in when you ran yt-dlp. Find it with:
```bash
ls -la | grep -E '^.*'
```
and remove it (`rm`) before retrying with the correct path.

---

## Useful flags
- `-U` — update yt-dlp itself (run when a new site breaks or auth changes)
- `-k` — keep intermediate files (don't delete separate video/audio streams after merge)
- `--write-thumbnail` — also save the thumbnail
- `-x --audio-format mp3` — extract audio only as MP3
- `--write-auto-sub --sub-lang <code>` — download subtitles (e.g. `ko`, `en`, `ja`)
- `--cookies-from-browser chrome` — needed for region-locked / age-gated YouTube videos

---

## Common platform notes
- **X / Twitter** — videos are HLS; yt-dlp downloads video + audio manifests and merges. May need retry if X rate-limits (403).
- **YouTube** — works out of the box; some videos need browser cookies for age/region gates.
- **Reddit** — works, picks highest quality.
- **Instagram / TikTok** — occasionally need `yt-dlp -U` if format extraction fails.
- **Vimeo / direct MP4s** — usually single-file, no merge needed.

---

## Quick command template (Windows MSYS)
```bash
yt-dlp -o "C:/Users/okya1/Downloads/<name>_%(id)s.%(ext)s" "<URL>"
ls -lh "C:/Users/okya1/Downloads/<name>_*.mp4"
```

