Claude Code skill: convert screen recordings & chat-history scrolls into PDFs. --capture-all captures every message (一条不差); --auto for key frames. ffmpeg + Pillow + img2pdf.
SaferSkills independently audited screen-recording-to-pdf (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Turn a screen recording (or any video) into a multi-page PDF. Pick the mode by what the recording IS:
scrolled through, a feed) where every item must be captured -> use `--capture-all`. It measures the scroll and keeps a frame each half-screen so nothing scrolls past uncaught.
moments that change -> use `--auto` (or manual --scene-threshold / --max-frames).
Two scripts, pipelined:
video.mp4 --extract_frames.py--> frame_*.jpg --frames_to_pdf.py--> output.pdfextract_frames.py is adapted from mugnimaestra/video-frames-skill (see references/video-frames-source.md). Local additions: an ffmpeg/ffprobe resolution layer (no system ffmpeg needed), a fix to the scene-detection filter for ffmpeg >= 7, --auto (smart key-frame + pause de-dup), and --capture-all (scroll-aware complete capture).
python -c "import imageio_ffmpeg, PIL, numpy; print('core OK')"
python -c "import img2pdf; print('img2pdf OK (lossless)')" || echo "optional"Install anything missing (one line, no admin / no system ffmpeg):
pip install imageio-ffmpeg Pillow numpy img2pdfnumpy is required only for --capture-all (scroll matching). The other modes work without it.
Chat-history / scrolling recording (capture every message):
python scripts/extract_frames.py chat.mp4 --capture-all \
| python scripts/frames_to_pdf.py --json - -o chat.pdfDemo / tutorial (key frames only):
python scripts/extract_frames.py demo.mp4 --auto \
| python scripts/frames_to_pdf.py --json - -o demo.pdf"scrolling chat" vs "demo", ask the user, or default to --capture-all when completeness matters.
extract_frames.py -> JSON to stdout (frames,output_dir, resolution, summary, plus strategy/capture/dedup).
frames_to_pdf.py (frames dir or piped JSON).--capture-all, mention thecapture.kept count and that adjacent pages overlap (~50%+) so every message is on multiple pages.
| Mode | When to use | Example |
|---|---|---|
| `--capture-all` ⭐ | Scrolling / continuous content -- chat-history recording, long-page scroll, feed. Captures EVERY message (no skips). Measures scroll, keeps a frame each half-screen. | --capture-all |
| `--auto` ⭐ | Demos / tutorials / click-throughs -- wants only the moments that change; de-duplicates pauses. | --auto |
| `--scene-threshold T` | Manual scene detection (distinct screen changes). | --scene-threshold 0.4 |
| `--max-frames N` | Predictable page count, smooth/gradual content. | --max-frames 30 |
| `--fps N` | Fixed-rate sampling. Rarely best. | --fps 1 |
All modes are mutually exclusive at the CLI (--capture-all overrides the others).
--capture-all tuning| Option | Default | Meaning |
|---|---|---|
--sample-fps FPS | 10.0 | Dense sampling rate. Must be high enough that no message scrolls all the way past between samples. Raise for fast scrolls. |
--overlap FRAC | 0.5 | Keep a frame each time content scrolls by this fraction of the frame height. Lower = more pages / safer; higher = fewer pages. 0.5 = adjacent pages overlap ~50%. |
How it guarantees completeness: it densely samples (default 10fps), measures the vertical scroll between consecutive frames, and only advances to a new page once the content has scrolled by overlap of the screen. Adjacent pages therefore always overlap, so any message that was ever on screen appears on at least one (usually several) pages. For a 133s phone chat scroll this yields ~50 pages with ~79% adjacent overlap. If you see gaps, lower --overlap (e.g. 0.35) or raise --sample-fps.
--auto tuning| Option | Default | Meaning |
|---|---|---|
--target-density SECS | 2.0 | Target seconds between kept frames (auto-raised for long videos). |
--dedup-threshold DIFF | 6.0 | Grayscale mean-abs-diff below which two frames are duplicates (higher = keep more). |
| Preset | Max dim | JPEG q | Best for |
|---|---|---|---|
balanced | 1024px | 3 | General (default) |
detailed | 1568px | 2 | UI detail, small text, color fidelity |
ocr | 1568px | 1 (gray+sharpen) | Text-heavy -- loses color |
frames_to_pdf.py)| Option | Default | Meaning |
|---|---|---|
<frames_dir> | -- | Directory of frame images |
--json FILE | -- | Read frame list from extract JSON (- = stdin) |
-o / --output | required | Output PDF path |
--page | none | a4/a4l/letter/letterl/none (none = native pixel size) |
--margin | 0 | Page margin in points (needs --page) |
--backend | auto | img2pdf (lossless JPEG embed) / pillow / auto |
--auto /--max-frames) -- those skip frames and miss messages that scroll past. Use `--capture-all` instead. If still gappy, lower --overlap 0.35 and/or raise --sample-fps 15.
--overlap (e.g. 0.65 =fewer pages, less overlap) -- but stay below ~0.8 or you risk gaps on fast scrolls.
pip install numpy.--capture-all (chat) or --auto (it falls back to fixed-rate).
pip install imageio-ffmpeg (bundled, no admin) orwinget install Gyan.FFmpeg.
--preset ocr (grayscale, sharpened) or `--preset detailed--max-dimension 1920`.
pip install img2pdf.scripts/extract_frames.py -- video -> JPEG frames. Modes: --capture-all(scroll-aware complete capture), --auto (smart key-frames + pause dedup), --scene-threshold/--max-frames/--fps (manual). Prints JSON.
scripts/frames_to_pdf.py -- frames -> one PDF (img2pdf lossless embed, or Pillow).references/video-frames-source.md, references/llm-image-specs.md -- upstream refs.references/THIRD-PARTY-LICENSES.md -- full third-party license texts.LICENSE -- Mixed license (WDenlie Commercial-Authorization for original work + MIT for upstream-derived).This skill uses a mixed license (see LICENSE for the full text):
frames_to_pdf.py, the --capture-all / --auto /pause-de-dup / ffmpeg-resolution additions in extract_frames.py, and SKILL.md — is © 2026 WDenlie under the PolyForm Noncommercial License 1.0.0: free for personal / academic / non-commercial use with attribution; commercial use requires a separate license. To license commercially, contact WDenlie via WeChat (ID: WDenlie).
extract_frames.py, plus references/video-frames-source.md and references/llm-image-specs.md — is adapted/copied from mugnimaestra/video-frames-skill and remains under its MIT License (MIT permits commercial use of those portions; this cannot be revoked).
No binaries, no vendored libraries are shipped; deps are pip-installed by the user and ffmpeg is invoked only as an external process.
img2pdf (LGPL-3.0-or-later),imageio-ffmpeg (BSD-2-Clause), Pillow (PIL Software License), numpy (BSD-3-Clause, only for --capture-all).
--enable-gpl) build when fetched by imageio-ffmpeg,invoked only as a separate subprocess -- the skill does not link, bundle, or redistribute it, so GPL copyleft does not reach this skill's code. For commercial distribution prefer an LGPL-only ffmpeg build (configure without --enable-gpl/x264/x265) -- this skill only decodes video and encodes JPEG.
Full third-party license texts: references/THIRD-PARTY-LICENSES.md.
Not legal advice. Engineering-level license guidance; have counsel review before commercial distribution.
Claude, OpenAI, and Gemini are trademarks of their respective owners; this skill is independent and not affiliated with or endorsed by them.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.