finder-adjust-thumbnails
Set the Finder thumbnail of your videos to the frame at an offset you choose, instead of whatever QuickLook picked.
How it works, and what that means
macOS gives you no way to tell QuickLook which frame to use as a video's poster
frame. What it does give you is a custom file icon — an image stored in the file's
resource fork and flagged in com.apple.FinderInfo — which Finder shows in place of
the generated thumbnail.
So this tool grabs the frame you asked for and installs it as the file's icon. Three consequences worth knowing up front:
- It is reversible.
--clearremoves the custom icon and Finder goes back to its own thumbnail. - It does not travel. The icon lives in the resource fork, so it survives copies and moves within macOS but is lost through zip archives and most cloud sync.
- Icons are square, frames are not. macOS stretches whatever it is given to fill a square, which would squash a 16:9 frame. The frame is therefore centred at its own proportions with the surrounding space left fully transparent — so what you see in Finder is the frame at its true shape, with no letterbox bars.
The video file's own content is never modified.
Install
Requires macOS and Python 3.12+:
uv tool install .
ffmpeg is optional — see below.
Use
# every video in a directory, one quarter of the way in
finder-adjust-thumbnails ~/Movies --offset 25%
# a fixed timestamp, recursively
finder-adjust-thumbnails ~/Movies --recursive --offset 1:30
# a single file
finder-adjust-thumbnails ~/Movies/holiday.mp4 --offset 10
# see what would happen, change nothing
finder-adjust-thumbnails ~/Movies --offset 25% --dry-run
# put the default thumbnails back
finder-adjust-thumbnails ~/Movies --clear
Offsets
| Form | Example | Meaning |
|---|---|---|
| seconds | 10, 90s, 2.5 |
that many seconds in |
| clock time | 1:30, 1:00:30 |
mm:ss or hh:mm:ss |
| percentage | 25% |
that far through the video |
An offset past the end of a video is clamped to just inside it, so one command works across clips of very different lengths.
Options
| Option | Default | |
|---|---|---|
--offset, -o |
— | where to take the frame from |
--clear |
remove custom icons instead | |
--recursive, -r |
off | descend into subdirectories |
--ext |
avi,m4v,mkv,mov,mp4,webm,wmv |
extensions to consider in a directory |
--icon-size |
512 |
pixel size of the square icon |
--dry-run |
off | report without changing anything |
--jobs, -j |
up to 8 | videos to decode in parallel |
A file named explicitly is always processed, whatever its extension.
Exit status is 0 on success, 1 if any file failed, 2 for bad arguments.
Which decoder runs
macOS decodes video itself through AVFoundation, so that is tried first: no external binary, no subprocess, and a video's preferred track transform is honoured — footage shot in portrait comes out upright rather than on its side.
ffmpeg is the last resort, used only for the formats QuickTime never learned: Matroska, WebM and WMV. If you have none of those, you never need it. When the fallback is used the report says so:
updated holiday.mp4 (frame at 1.50s)
updated archive.mkv (frame at 1.50s, via ffmpeg)
If a file needs ffmpeg and it is not installed, only that file fails — everything
macOS can read is still done — and the message tells you to brew install ffmpeg.
Disk space
Each icon is stored in its file's resource fork, so it does cost real bytes. Measured on a detailed 1080p frame:
--icon-size |
per file |
|---|---|
| 256 | ~20 KB |
| 512 (default) | ~44 KB |
| 1024 | ~100 KB |
Finder's icon slider tops out at 512pt, so the default covers every view without
storing pixels nobody sees. Drop to --icon-size 256 for a large library, raise it if
you live in huge-icon view. (For reference, an unbounded full-resolution frame costs
about 180 KB a file.)
If Finder keeps showing the old thumbnail
Finder caches aggressively. The tool touches the containing directory to prod it; if
a stale thumbnail persists, killall Finder.
Development
uv sync
uv run pre-commit install # ruff check, ruff format and hygiene hooks on every commit
uv run pytest
Linting and formatting are both ruff, run through pre-commit; CI runs the same hooks,
so pre-commit run --all-files is the whole check locally.
Tests run against real videos generated by ffmpeg and assert on the real
com.apple.FinderInfo flag, the real stored icon pixels, and real resource fork
sizes — nothing that matters here is mocked. Proportions are checked by putting a
known square in the frame and measuring whether it is still square in the icon.
Two things to know before changing this code:
- AppKit's
setIcon:forFile:takes an internal IconServices lock and deadlocks when called from several threads at once. Frame extraction is parallel; icon writing is deliberately serial, andtest_icon_writes_never_happen_on_a_worker_threadholds that line. - PyObjC has no type metadata for the
CGImageRefAVFoundation returns, so it arrives as an untyped pointer and is passed straight intoNSBitmapImageRep. It works, but it is the fragile seam;tests/test_backends.pypins it.
Release files for finder-adjust-thumbnails 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| finder_adjust_thumbnails-0.1.0.tar.gz | 37.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| finder_adjust_thumbnails-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 54.5 kB
Release files / finder_adjust_thumbnails-0.1.0.tar.gz
| Download URL | finder_adjust_thumbnails-0.1.0.tar.gz |
|---|---|
| Size | 37.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c26926f93066b46fe5c8bbf5a0125b89f7ce05b5321f7a9ec4a8b242ecdc39ec
|
|
BLAKE2b-256 checksum How to use checksums |
ce929b8caeae40443f5a326f110b44bb12a04f04e5f33a9227a8f40b06c633d6
|
| 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 Sep 15, 2026.
Transparency logRelease files / finder_adjust_thumbnails-0.1.0-py3-none-any.whl
| Download URL | finder_adjust_thumbnails-0.1.0-py3-none-any.whl |
|---|---|
| Size | 17.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fe6034368c6374ce71f3ac92ce2e477e831341366e97fc66c878f48829e9a1a0
|
|
BLAKE2b-256 checksum How to use checksums |
d6914de99d3ffcd462df2aecd1203b0acbc0e6b8db291a520de33b03fee92f0e
|
| 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 Sep 15, 2026.
Transparency log