platform/x-video-grab/SKILL.md

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

  1. 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.
  2. Ask which view to scroll if it isn't obvious. A raw timeline pulls in ads and reposts; x.com/i/bookmarks or a profile's media tab is usually what they actually want.
  3. 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).

  1. 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 --interval before blaming the selectors.
  • .seen in 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_JS selector in the script — verify it by hand first with cdp_eval.py <ws_url> '<expr>' from the chrome-cdp skill 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-index skill rather than importing the files straight into an NLE — it owns indexing and clip selection for both new and already-indexed corpora.