Files
av-club/docs/PIPELINE.md
T
PastilhasandClaude Opus 5 81c8e17a8e 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>
2026-08-20 02:18:14 +01:00

102 lines
4.4 KiB
Markdown

# The pipeline
What av-club is meant to do, and — just as importantly — what has not been decided yet.
**None of this is built.** This document exists so the decisions get made deliberately rather than
being settled by whatever gets written first. Where something is open, it says so.
## The problem
Someone uploads a file. It might be:
- a 12 MB photo straight off a phone, 4000px wide, in HEIC
- a 4K video in a codec Safari will not play
- a JPEG carrying the GPS coordinates of the room it was taken in
- a file named `photo.jpg` that is actually a PNG, or a PDF, or nothing at all
- something enormous, uploaded by accident
What a page needs is none of those: a few sizes in a modern format, a poster frame for video,
dimensions known ahead of time so layout does not shift, and nothing embedded that the person who
uploaded it did not mean to publish.
## What it should do
### 1. Identify, do not trust
Establish what a file **actually is** by inspecting it, never from its extension or the
`Content-Type` a browser attached. A rejected upload should say something a non-technical person can
act on, not a MIME type.
### 2. Strip what should not travel
GPS coordinates, camera serial numbers, timestamps, editing history. `sharp` drops these by default
on re-encode — see `docs/TOOLING.md`. Video metadata needs the same treatment and is not free.
### 3. Probe, and keep the numbers
Width, height, duration, codec. These get stored, because knowing an image's aspect ratio before it
loads is what lets a page reserve the space and not shift as it arrives.
### 4. Derive
Images: a small number of widths, in a modern format, never upscaled past the original.
Video: something every browser plays, plus a poster frame.
### 5. Name outputs so they can be cached forever
Content-addressed or otherwise unique, so a derivative can be served `immutable` honestly and
replacing an image never serves a stale one from a cache.
## What is NOT decided
These are real forks. Picking one by accident is how a project ends up with an answer nobody chose.
### How a consumer talks to this
| Option | Consequence |
|---|---|
| **A library** the consumer imports | Simplest. Processing happens in the web server's process — a big video transcode blocks a worker and competes for CPU with page rendering. |
| **A CLI** the consumer shells out to | Clean isolation, no shared runtime. Every call pays process startup, and errors arrive as exit codes and stderr rather than as values. |
| **A service** the consumer calls | Scales and isolates properly. It is another thing to deploy, monitor and secure, which is real work for a small site. |
### Whether processing is synchronous
Does an admin wait for a 4K video to transcode while staring at a spinner, or does the upload return
immediately and the derivative appear later?
Asynchronous is better for them and means a job queue, a way to show "still processing", and a
decision about what a page renders in the meantime. Synchronous is far simpler and is fine for
images; it is not fine for video.
**These two questions are related** — a queue implies something long-lived, which pushes toward a
service.
### Where output goes
Not decided, and it interacts with the consumer's deployment: whether a deploy replaces the working
directory determines whether generated files can live inside it at all. This is currently an open
question in `fofos/platform` too.
### The derivative ladder
Which widths, which formats, what quality. Deliberately unanswered — it should follow from a real
design, and guessing produces sizes nobody uses and gaps where one was needed.
### Externally hosted media
A consumer may store a URL to a video hosted elsewhere rather than a file. Whether av-club has
anything to say about those — probing them, fetching poster frames, or ignoring them entirely — is
not decided.
## Principles worth keeping
**The original is never destroyed.** Derivatives are derivatives. A quality setting judged wrong in
six months should be re-runnable, and it cannot be if the source was thrown away.
**Rejections explain themselves.** The person uploading is not a developer. "That file is 180 MB;
the limit is 50 MB" is usable. "Unsupported media type" is not.
**Verify by running, not by asserting.** `scripts/verify.sh` is the existing example: it encodes a
video and reads it back rather than checking a version string. The same standard applies to the
processing code.