diff --git a/.forgejo/workflows/deploy.yaml b/.forgejo/workflows/deploy.yaml index 1a4d160..224e369 100644 --- a/.forgejo/workflows/deploy.yaml +++ b/.forgejo/workflows/deploy.yaml @@ -97,10 +97,16 @@ jobs: # loads is third-party — which is what keeps the privacy notice's # "everything comes from catcrafts.net" true. # - # Content-addressed and incremental: a file already on the media mount is - # never downloaded again. Writes straight into the mount so the copies - # persist across deploys — they are NOT always reproducible, because a - # source instance deleting a file leaves ours as the only one. + # Content-addressed: the name is the hash of the bytes. Note what that + # does NOT mean — a third-party file is re-fetched on every build, because + # the name cannot be known until the bytes are in hand; only the write is + # skipped when the hash is already on the mount. Media we host ourselves + # (tools/publish-media.sh) is the exception that is genuinely incremental: + # fetch-media.sh adopts an own-origin URL without any request at all. + # + # Writes straight into the mount so the copies persist across deploys — + # they are NOT always reproducible, because a source instance deleting a + # file leaves ours as the only one. run: | set -eu if [ -d /deploy-app ]; then diff --git a/deploy/README.md b/deploy/README.md index 71a96c8..c5117ee 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -118,8 +118,9 @@ Only listed communities are fetched, so joining a new one does not silently publish it to the site — add a line first. ```sh -tools/fetch-posts.sh # writes content/posts.json -tools/fetch-media.sh # mirrors the media, rewrites posts.json to /media/... paths +tools/publish-media.sh FILE # BEFORE posting: uploads to /media, prints the URL +tools/fetch-posts.sh # writes content/posts.json +tools/fetch-media.sh # mirrors the media, rewrites posts.json to /media/... paths ``` Order matters: the second reads what the first wrote. CI runs both before the @@ -127,6 +128,47 @@ build. Neither fails the build on a network error — a fediverse outage leaves previous `posts.json` in place, and a single failed download leaves that one entry pointing at its original URL rather than losing the post. +### Publish the media first, then post it + +**The recommended flow is to put a recording on catcrafts.net before writing the +post, and use that URL as the post's link.** Run `tools/publish-media.sh +recording.mp4`; it transcodes, uploads to the media mount under its content hash, +and prints a `https://catcrafts.net/media/.mp4` URL to paste into the post. + +`fetch-media.sh` recognises its own origin and **adopts** such a URL: it rewrites +it to `/media/` and probes the local file for dimensions, downloading +nothing. That is not just an optimisation — it removes the whole class of build +failure where the media step depends on a third party. A 167 MB recording on a +file host once hit `MAX_BYTES` (64 MB), so the entry kept its original URL, and +the deploy then failed the e2e "media origin" check on a file that was sitting on +our own disk the entire time. + +The transcode matters as much as the hosting. Phone recordings are wildly +oversized for what they show — that same 167 MB clip was 78 s of a dark room at +17 Mbps, and denoising into AV1 gives the same picture in 15 MB. It also **bakes +in the rotation**: phones record landscape and attach a display matrix, so an +untouched file reports 1920x1080 while playing portrait, and the `width`/`height` +attributes then reserve exactly the wrong box. (`fetch-media.sh` swaps the +dimensions when it sees a quarter-turn matrix, so a straight-from-phone mirror is +correct too — but transcoding means nothing downstream has to know.) + +Two things to know about publishing AV1: + +* Nothing emits a fallback encoding and `