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

4.4 KiB

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.