Skip to main content

webpthumb

WebP thumbnails for images and PDFs. One command covers a one-shot convert, an HTTP endpoint, and a directory watcher.

PDFs are rendered by pdfthumb (PyMuPDF). Images are resized and encoded by image-to-webp (Pillow). The watcher uses watchdog. A thumbnail is a single still frame: the first frame of an animated GIF or APNG, or the requested PDF page.

Images are only shrunk, to fit the requested width (and height, when you set one). PDFs are rendered at that width. The defaults match pdfthumb: 256 pixels wide, WebP quality 80, page 1.

Install

Python 3.9 or newer. install.sh prefers python3.12 when it is on PATH.

./install.sh            # .venv, editable install, link ~/.local/bin/webpthumb
./install.sh --dev      # also install pytest
./install.sh --uninstall

Override the link location with BIN_DIR (default ~/.local/bin). The script uses sudo only when that directory is not writable.

The default install reads the formats Pillow ships with (PNG, JPEG, GIF, BMP, TIFF, WebP, ICO, and others), HEIF (.heic, .heif, .hif, and AVIF, via pillow-heif), and PDF. Camera RAW and SVG need the optional extras of image-to-webp (pip install "image-to-webp[raw]" or [svg]). SVG also needs the system cairo library. RAW and SVG are not in the Docker image.

CLI

webpthumb convert photo.png doc.pdf -o ./thumbnails
webpthumb convert ./inbox -r -o ./thumbnails -w 512 -q 80
webpthumb convert doc.pdf -p all          # doc_p1.webp, doc_p2.webp, ...
webpthumb convert doc.pdf --height 300    # fit inside 256x300
webpthumb convert doc.pdf --crop-top      # PDF only: top square
webpthumb convert doc.pdf -c 0.5          # PDF only: top band, 256x128

webpthumb serve --host 127.0.0.1 --port 8080

webpthumb watch ./inbox -o ./thumbnails -r
webpthumb watch ./inbox -o ./thumbnails -r --background
webpthumb start ./inbox -o ./thumbnails -r
webpthumb status
webpthumb stop

convert sends PDFs through pdfthumb.generate_thumbnails and images through image-to-webp. Output names are <name>.webp for an image or for PDF page 1. Any other page spec uses <name>_p<N>.webp. Existing files are skipped unless you pass --overwrite.

Exit codes: 0 when every file succeeded or was skipped, 1 when a file failed, 2 for bad arguments. status exits 1 when the background watcher is not running. stop exits 0 when it is already stopped.

HTTP

GET /health returns {"status":"ok"}.

POST /v1/thumbnail takes one multipart field named file and returns image/webp. Query parameters: width, height, quality, page, crop_top. crop_top applies only to PDFs. Interactive docs are at /docs.

curl -sS -D- --output thumb.webp \
  -F "file=@photo.png" \
  "http://127.0.0.1:8080/v1/thumbnail?width=256&quality=80"

A file that is not an image or a PDF, or a PDF that cannot be read, returns 400. An upload larger than WEBTHUMB_MAX_UPLOAD_MB (default 32) returns 413. The endpoint reads only the upload. It does not accept a filesystem path.

Watch

webpthumb watch thumbnails files already in the directory, then stays running until SIGINT or SIGTERM. New and updated files are picked up with watchdog. The watcher waits until a file's size stops changing (about half a second) so a copy in progress is not read early.

webpthumb watch --background and webpthumb start do that work in the background and return. webpthumb status prints whether that watcher is running. webpthumb stop sends SIGTERM and waits for it to exit. A second start is refused while the first is still running. The pid, the directories, and the log live under WEBTHUMB_RUN_DIR ($XDG_STATE_HOME/webpthumb, or ~/.local/state/webpthumb). A foreground watch is not tracked. Keep watch in the foreground when it is the main process of a container.

Watched extensions: .png, .jpg, .jpeg, .gif, .bmp, .tif, .tiff, .webp, .ico, .heic, .heics, .heif, .heifs, .hif, .avif, .avifs, .pdf. Hidden files and *.part files are ignored. The output directory must not sit inside the input directory. With -r, the output tree mirrors the input tree.

A thumbnail is rewritten when the source is newer than the WebP, or when --overwrite is set.

webpthumb serve starts the same watcher in the background when both WEBTHUMB_WATCH and WEBTHUMB_OUTPUT are set.

Environment

Used when the matching flag or query parameter is omitted.

Variable Default Used by
WEBTHUMB_WIDTH 256 convert, serve, watch
WEBTHUMB_HEIGHT unset convert, serve, watch
WEBTHUMB_QUALITY 80 convert, serve, watch
WEBTHUMB_PAGES 1 convert, watch
WEBTHUMB_CROP_TOP unset convert, serve, watch
WEBTHUMB_OVERWRITE off convert, watch (1, true, yes, on)
WEBTHUMB_RECURSIVE off convert, watch
WEBTHUMB_JOBS CPU count convert
WEBTHUMB_OUTPUT ./thumbnails for convert convert, watch, serve
WEBTHUMB_WATCH unset serve, with WEBTHUMB_OUTPUT
WEBTHUMB_HOST 127.0.0.1 serve (the image sets 0.0.0.0)
WEBTHUMB_PORT 8080 serve
WEBTHUMB_MAX_UPLOAD_MB 32 serve
WEBTHUMB_RUN_DIR ~/.local/state/webpthumb start, stop, status

Docker

The image is python:3.12-slim plus this package. PyMuPDF carries its own MuPDF build, so poppler and ImageMagick are not installed.

docker build -t webpthumb .
docker run --rm -p 8080:8080 webpthumb

# API and watcher together
docker compose up --build

# watcher only
docker run --rm -v "$PWD/in:/data/in" -v "$PWD/out:/data/out" \
  webpthumb watch /data/in -o /data/out -r

docker compose up publishes port 8080 and mounts ./in and ./out. Drop a PNG, HEIC, or PDF into ./in and the thumbnail appears in ./out.

Tests

pytest

Library

from webpthumb import ThumbOptions, make_thumbnail, write_thumbnail

webp = make_thumbnail(open("doc.pdf", "rb").read(), "doc.pdf", ThumbOptions(width=256))
paths = write_thumbnail("photo.png", "thumbnails", ThumbOptions(width=256, quality=80))

Metadata

Release files for webpthumb 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for webpthumb 0.1.0
File Size Uploaded
webpthumb-0.1.0.tar.gz 22.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for webpthumb 0.1.0
File Interpreter ABI Platform
webpthumb-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 41.5 kB

Release files / webpthumb-0.1.0.tar.gz

Download URL webpthumb-0.1.0.tar.gz
Size 22.5 kB
Tags Source
SHA-256 checksum
How to use checksums
fe1e89269bdc0ecc6626fbe57ef26c4fce8d06577b4f6f386d6394977b94f0be
BLAKE2b-256 checksum
How to use checksums
d5f5144249d5e2d1fe40674157d17b03271b20225ab7f8c6728bad57b7037b7c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 10, 2026.

Transparency log

Release files / webpthumb-0.1.0-py3-none-any.whl

Download URL webpthumb-0.1.0-py3-none-any.whl
Size 19.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2be8bf571a5cc05bd0b38b6472638f74c4adc8fa0dd56ae6e547fa5eafea9b56
BLAKE2b-256 checksum
How to use checksums
3b0f67c4140f51fb74341a650e65246e01e77f3cb724e6557c806f01cd61e9fd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 10, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page