Skip to main content

README.md

# Dims

Probe **remote image dimensions** (width × height) from just a URL —
without downloading the image.

Image formats keep their dimensions in the file header, so `Dims`
fetches only the **first ~128 KB via an HTTP Range request**, parses the
JPEG / PNG / WebP / GIF header, and discards the bytes. Milliseconds and
a few KB per image instead of megabytes.

```elixir
Dims.probe("https://cdn.example.com/page1.jpg")
#=> %{width: 800, height: 8000}

Dims.probe_all(urls)
#=> [%{url: ..., width: 800, height: 1200}, ...]   # input order preserved
```

Useful anywhere you deal with images you don't host: reserving layout
space (`aspect-ratio`, `<img width height>`) before anything loads,
link previews, gallery imports, comic/manga readers deciding page
layouts, CMS ingestion.

---

## Install

```elixir
def deps do
  [
    {:dims, "~> 0.1"}
  ]
end
```

## Batch probing, built for the real world

* **Parallel** — `probe_all/2` sweeps the list with bounded concurrency
  (default 8), preserving order.
* **Median backfill** — a probe that fails or times out gets the median
  dimensions of its successful siblings, not a constant fallback. Image
  sets (chapter pages, galleries) are near-uniform, so the estimate is
  close — and relative layouts built from the results never explode.
* **Sampling** — `probe_sampled/2` probes ~20 evenly-distributed URLs
  of a huge list and median-fills the rest: accuracy within a percent
  or two for uniform sets at ~25× less traffic. `probe_auto/2` switches
  between full and sampled by list length (default threshold 80).
* **Caching** — results cache in ETS with a 30-day TTL (a URL's bytes
  don't change, so its dimensions don't either). Bypass per call with
  `cache: false`.

## Options

```elixir
Dims.probe(url,
  headers: [{"referer", "https://source.example/"}],  # CDNs that check referers
  headers_fun: &MyApp.headers_for/1,                  # per-URL headers
  probe_bytes: 131_071,
  receive_timeout: 8_000,
  cache: true,
  cache_ttl: :timer.hours(24 * 30)
)
```

Batch calls also take `:max_concurrency`, `:sample_size`, and
`:full_threshold`.

## Parsing bytes you already have

`Dims.Parser.parse/1` is the pure header parser — hand it the leading
bytes of an upload or a cached chunk and get `%{width:, height:}` back
with no I/O.

## Formats

JPEG (all SOFn markers, so progressive too), PNG, WebP (VP8 / VP8L /
VP8X), GIF (87a/89a).

## License

MIT