feat: video support on postcards (upload + inline playback) #7
Reference in New Issue
Block a user
No description provided.
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
Add video as a media type on postcards, alongside the existing images. m wants to attach a video to a card and have it play on the rendered postcard.
MVP scope (keep it tasteful + simple, like the image flow)
postcardsbucket, e.g. avideo/key prefix), same pattern as images. Store apostcard_videosrow (or extendpostcard_images/a media table) — mirror the existing ordered-media model.<video controls playsinline>on the card, mobile-first. Works in the gallery layout and (sensibly) the flip-card — pick the cleanest interaction; don't let video controls fight the swipe/flip/lightbox gestures already in place.mp4(H.264/AAC — broadest mobile support);webmoptionally. Validate type + size before upload, like images.Key constraint — file size / storage
Videos are large. The single most important decision:
MAX_UPLOAD_BYTES).Constraints (carry over from the app)
noindexon card pages.postgresrole via the pooler (NOT supabase_admin) — see #1/#2/#3 history, or the deploy crash-loops.DoD
/c/{token}card plays it inline on mobile + desktop.✅ Video support implemented & e2e-verified
Commit:
340dd7e491Branch
mai/hermes/issue-7-feat-video(otto-head merges + deploys).What shipped
postcardsbucket under a<token>/video/key prefix; public URL derived at render time (so a BASE_URL / backend swap needs no data migration). Mirrors the image flow.004_videos.sqladdspostcards.postcard_videos— mirrorspostcard_imagesexactly (storage_key/content_type/position,ON DELETE CASCADE). Additive + idempotent; a freshCREATEruns as thepostgresrole via the pooler, so the v1 ownership-crash class (#1/#2/#3) can't recur. Verified: the migration ran cleanly against the live shared schema during e2e and does not break the currently-running old binary (extra unused table).<video controls playsinline preload="metadata">on the card, mobile-first. In gallery and single it sits above the images; on the flip card it renders in its own block above the scene so its native controls never fight the swipe / flip / lightbox gestures already in place. The lightbox only wires.postcard img, so videos are untouched.ftypbox / webm EBML header) rather than the client-suppliedContent-Type(which doesn't reliably recognise webm). Friendly German errors for wrong type and oversize.Key constraint — storage size limit (verified, not assumed)
Probed the shared msupabase Storage instance directly: the
postcardsbucket hasfile_size_limit: null(inherits the global), and the global ceiling is exactly 50 MiB —52428800bytes uploads OK,52428801→HTTP 413 "object exceeded the maximum allowed size". SoMAX_VIDEO_UPLOAD_BYTESdefaults to 50 MiB (= the real ceiling). Going higher would need the instance's global storage limit (and any proxy body limits in front ofsupa.flexsiebels.de) raised first. No server-side transcoding (out of MVP); a poster-frame is noted as a future nicety.New env (documented in
.env.example)MAX_VIDEO_UPLOAD_BYTES(default52428800= 50 MiB)MAX_VIDEOS(default1)e2e (real production path — shared DB + Supabase Storage)
src+type="video/mp4"; the stored object served byte-identical withContent-Type: video/mp4,ffprobeconfirmed a playable h264+aac mp4. ✔️type="video/webm". ✔️HTTP 400+ „Nur MP4- oder WebM-Videos werden unterstützt.“ ✔️postcard_videosrows remain.Quality
gofmtclean,go vet,go build,go test ./...all green. New tests:detectVideoTypemagic-byte sniffing, per-layout<video>render (and absence when no video), compose video field, config defaults.noindex, nothing hardcodes the domain. Coexists with single/gallery/flip, broadcast mode, custom Shlink slugs, image lightbox.Deploy note for otto-head: ensure the reverse proxy in front of
post.msbls.de(Traefik) allows request bodies up to ~131 MiB (8×10 MiB images + 50 MiB video + slack) so large uploads aren't truncated at the edge.✅ Video live on https://post.msbls.de
Merged
340dd7e(merge158f856), deployed. Compose now has an optional Video (MP4/WebM, bis 50 MB) field; the rendered card plays it inline via HTML5<video>, mobile-first, alongside images/layouts.Verified live e2e: uploaded a test mp4 → stored at
…/postcards/{token}/video/0.mp4in Supabase Storage, card renders the<video>player, object serves 200video/mp4. Test data cleaned. Migration 004 (postcard_videos, owner-safe) applied cleanly. go build/vet/test green.Note: Traefik streams request bodies (no buffering cap on post.msbls.de), so the larger upload works — verified. Not closing — m closes.