name: x-video-grab description: >- Download videos from X (Twitter) posts as the user scrolls, by attaching to a debug-mode Chrome tab and feeding permalinks to yt-dlp. Use when asked to grab, save, archive, or bulk-download videos/GIFs from an X timeline, bookmarks, likes, a profile, or search results.
x-video-grab
Poll a live X tab for posts containing a video player, dedupe the permalinks,
and download each one with yt-dlp. Runs for as long as the user keeps
scrolling.
When to use this
- "download the videos as I scroll X", "save these X videos", "archive my X bookmarks"
- Any bulk grab off a logged-in X view where the URLs aren't known up front.
For a handful of known post URLs, skip this — just run yt-dlp <url> directly.
Prerequisites
yt-dlp (brew), and websocket-client for python3 (the chrome-cdp skill's
cdp_eval.py is imported directly, so its install note applies).
Chrome only exposes CDP when launched with the flag, and the user must launch it themselves — an already-open Chrome can't be upgraded. Have them run:
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--remote-debugging-port=9222 --user-data-dir="$HOME/.chrome-debug-cdp"
The separate --user-data-dir keeps their main session intact — which means
that profile starts logged out. They must log into X in that window, or
every download 404s. Cookies are then read from that same profile dir, which is
why --profile defaults to it.
Workflow
- Confirm a debug Chrome is up and on X:
curl -s http://127.0.0.1:9222/json | grep x.com. If not, give the user the launch command above and wait. - Ask which view to scroll if it isn't obvious. A raw timeline pulls in ads and
reposts;
x.com/i/bookmarksor a profile's media tab is usually what they actually want. - Run it in the background, then report the output dir and let it accrue:
~/.claude/skills/x-video-grab/x-video-watch.py -o ~/Downloads/x-videos
Flags: --port (9222), --interval (2s poll), --profile (cookie source).
- Tell the user to scroll. Each new post prints
+ <url>; downloads happen on a worker thread so a slow one never blocks discovery.
Notes
- Only what's in the DOM gets seen. X virtualizes the timeline and drops
offscreen posts, so scrolling faster than the poll interval loses items —
lower
--intervalbefore blaming the selectors. .seenin the output dir makes reruns cheap and prevents re-downloads; delete it to force a re-grab.- Post markup is X's, and it changes. If nothing is ever found, the fix is the
SCRAPE_JSselector in the script — verify it by hand first withcdp_eval.py <ws_url> '<expr>'from thechrome-cdpskill rather than editing blind. - Only downloads what the logged-in user can already view. It has no bearing on what they may republish.
- Grabbing clips as edit source? Hand the output dir to the
footage-indexskill rather than importing the files straight into an NLE — it owns indexing and clip selection for both new and already-indexed corpora.
