Metadata-Version: 2.5
Name: screencap
Version: 0.1.0
Summary: Fuzzy-find a subtitle line and grab the sharpest frame (or clip) with the caption burned in.
Project-URL: Homepage, https://github.com/omarish/screencap
Project-URL: Repository, https://github.com/omarish/screencap
Author-email: Omar Bohsali <omar.bohsali@gmail.com>
Requires-Python: >=3.9
Requires-Dist: numpy
Requires-Dist: pillow
Description-Content-Type: text/markdown

# screencap

Fuzzy-find a subtitle line and grab the sharpest frame for it, with the
subtitle burned in.

Point it at a directory holding **one** video and **one** subtitle file. A
`fzf` typeahead opens; fuzzy-match the line you want; `screencap` finds the
least-blurry frame within that line's time window, draws the subtitle onto it,
and writes a PNG next to the source.

```
screencap ~/Movies/some-film
```

<video src="https://files.omarish.com/perm/screencap/screencap-demo.mp4" controls playsinline></video>

## Why it's not blurry

Within the matched subtitle's time window it samples every frame, scores each by
**variance of the Laplacian** (a standard focus metric — higher = sharper), and
picks the sharpest one. So you get a crisp frame instead of whatever happened to
be under the playhead.

## Usage

```
screencap [DIR]                 # interactive fzf picker (default DIR: .)
screencap DIR --query "text"    # non-interactive: best-matching line
screencap DIR --index 42        # non-interactive: the Nth cue (1-based)

Options:
  --clip           make a short mp4 clip (audio + burned-in captions)
  --to N           clip mode: end cue index (1-based); default = start cue
  --to-query TEXT  clip mode: end cue by best text match
  --video PATH     explicit video file (skip auto-discovery)
  --subs PATH      explicit subtitle file (skip auto-discovery)
  --pad SECONDS    lead-in/out (clip) or search-window widening (default 0.15)
  --no-subs        don't burn the subtitle in
  --styled         screenshot only: authentic styling via ffmpeg's libass
                   filter (needs a libass-enabled ffmpeg; see below)
  --font PATH      .ttf/.ttc for the Pillow caption (default: Arial/Helvetica)
  -o, --out PATH   output file (default: <timestamp>_<slug>.png/.mp4 in DIR)
  -v, --verbose    show what it's doing
```

In the picker: type to fuzzy-match, arrow keys to move (the preview pane shows
surrounding lines + the timecode), **Enter** to grab the frame.

## Clips

`--clip` produces a short `.mp4` (h264 + aac) with the audio and the subtitles
burned in — the captions change over the clip, each line appearing for its own
window:

```
screencap DIR --clip                                   # Tab-mark first & last line, Enter
screencap DIR --clip --query "you dig a"                # single-line clip
screencap DIR --clip --query "you dig a" --to-query "but you dig 12"   # a span
```

Interactively, **Tab**-mark the first and last line of the exchange (or just
one line for a single-line clip), then **Enter**. Every subtitle line inside the
span is burned in, and `--pad` adds a little lead-in/out. Captions are
composited with ffmpeg's `overlay` filter, so clips work on any ffmpeg — no
libass required.

## Requirements

- [`uv`](https://docs.astral.sh/uv/) — runs the script and manages its Python
  deps (Pillow, numpy) automatically via the inline script header.
- `ffmpeg` / `ffprobe` — frame extraction. **No libass/freetype build required**:
  the caption is drawn in Python, so any ffmpeg works.
- `fzf` — the fuzzy picker (only for interactive mode).

Supported subtitles: `.srt`, `.vtt`, `.ass`/`.ssa`.

## Install

```
uvx --from git+https://github.com/omarish/screencap screencap
```

Needs [`uv`](https://docs.astral.sh/uv/), plus `ffmpeg` / `ffprobe` and `fzf` (interactive mode only).

To put it on your `PATH`:

```
uv tool install git+https://github.com/omarish/screencap
```

## Notes / limitations

- By default the caption is drawn by Pillow: a clean centered
  white-with-black-outline caption, using the exact text of the matched line
  (no subtitle-timing guesswork). It does **not** reproduce `.ass` styling.
- `--styled` instead renders the real styling via ffmpeg's libass `subtitles`
  filter — but that needs an ffmpeg built with libass (the stock Homebrew
  `ffmpeg` is not; `brew install homebrew-ffmpeg/ffmpeg/ffmpeg` is, though it
  builds from source). For plain `.srt` (which carries no styling) the two look
  identical, so `--styled` only matters for styled `.ass`/`.ssa` subtitles.
- Assumes exactly one video and one subtitle file per directory; if there are
  several it stops and asks you to pass `--video` / `--subs`.
