Start av-club: what it is, and a verified installer

Media processing pulled out of the website that first needed it. ffmpeg,
ImageMagick, libvips and exiftool are a real installation rather than an npm
install, and encoder settings get revisited as browser support moves — neither of
which belongs on the same clock as page templates and database schemas.

No processing code yet. What is here is the tooling requirement, an installer, and
docs/PIPELINE.md writing down what has NOT been decided — how a consumer talks to
this, whether processing blocks an upload, where output goes — so those get chosen
deliberately instead of by whatever gets written first.

verify.sh checks by running: it encodes a video and probes the result against what
ffmpeg was told to make, loads sharp under Bun specifically, and asserts a
re-encode still strips GPS. That last one guards a real leak — if a future sharp
changes its default, this fails a check rather than quietly publishing the
coordinates of a client's living room. Confirmed non-vacuous by hiding ffmpeg and
watching it exit 1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Pastilhas
2026-08-20 02:18:14 +01:00
co-authored by Claude Opus 5
commit 81c8e17a8e
9 changed files with 596 additions and 0 deletions
+88
View File
@@ -0,0 +1,88 @@
# Tooling
What is installed to process images and video, what each piece is for, and the failures that look
like something else.
## Bringing a machine up
```bash
./scripts/install.sh
```
Verified on Ubuntu 24.04. What it does, if you would rather do it by hand:
```bash
sudo apt-get install -y ffmpeg libimage-exiftool-perl imagemagick libvips-tools webp libavif-bin
bun install
./scripts/verify.sh
```
## What each is for
| Tool | Verified at | Used for |
|---|---|---|
| `ffmpeg` / `ffprobe` | 6.1.1 | Video and audio. Transcoding, extracting poster frames, and probing a file for duration, codec and dimensions rather than trusting what a browser claimed about it. |
| `sharp` (bun dep) | 0.35.3, libvips 8.18.3 | **The image path the code actually calls.** In-process resize and re-encode to WebP/AVIF/JPEG, libvips-backed, so no process spawn per image. |
| `exiftool` | 12.76 | Reading and stripping metadata. Client photos routinely carry GPS coordinates and camera serial numbers. |
| `imagemagick` | 6.9.12 (`convert`, `identify`) | Inspecting a file by hand when something looks wrong, and formats sharp does not cover. |
| `libvips-tools` | 8.15.1 (`vipsthumbnail`) | The CLI side of the same library sharp uses — for comparing behaviour outside the app. |
| `webp` | 1.3.2 (`cwebp`, `dwebp`) | WebP at the command line. |
| `libavif-bin` | 1.0.4 (`avifenc`, `avifdec`) | AVIF at the command line. |
The command-line encoders are for inspection and one-off work. Processing goes through `sharp` and
`ffmpeg`.
## Two things that bite
Both of these fail in a way that looks like a broken installation and is not.
### There is no `magick` command
Ubuntu 24.04 ships ImageMagick **6**, which provides `convert` and `identify`. The single `magick`
entrypoint is ImageMagick **7**. A script written against `magick` fails with "command not found" on
a machine that has ImageMagick fully installed and working.
Use `convert` and `identify`, or install IM7 deliberately and write down that you did.
### `sharp` must be resolved from the repo
Run from anywhere else — `bun -e` in `/tmp`, a script executed from another directory — Bun resolves
`sharp` from its **global install cache** and lands on the `wasm32` build, which is missing
`@emnapi/runtime` and dies with:
```
TypeError: sharp.libvipsVersion is not a function
```
The native `linux-x64` binary is in this repo's `node_modules`; the process just has to be able to
see it. Anything using sharp runs with the repo as its working directory. `scripts/verify.sh` does
exactly this, and the reason is commented at the call site.
## Metadata is stripped by default, and that is load-bearing
A `sharp` re-encode drops EXIF unless `.withMetadata()` is called explicitly.
Measured on a JPEG carrying injected GPS and camera tags: **34 tags before, 19 after**, and the 19
survivors are filesystem and image-structure facts only — `ImageWidth`, `MIMEType`, `FileSize` and
similar. GPS latitude and longitude, `Make` and `Model` were all gone.
So the default is the safe one, and **`.withMetadata()` is the call that publishes a client's
location.** Do not add it without a reason. `scripts/verify.sh` asserts this on every run, so if a
future sharp version changes the default, it fails a check rather than quietly publishing
coordinates.
## Behaviour worth remembering
**`sharp(...).resize(400)` enlarges a smaller source.** A 300×300 input comes out 400×400 and soft.
Anywhere a derivative size is requested will want `withoutEnlargement: true`, or small uploads get
upscaled into blur that nobody asked for.
## Verifying
```bash
./scripts/verify.sh
```
Every check runs the tool and compares the result against what was asked for — a version string
proves a binary exists, not that its encoders are present or that a native module loads. The script
exits non-zero on failure, so it is usable in CI.