Files
Motionity/Dockerfile
T
kawaandClaude Opus 5 9a4d14613f feat: replace the archive.org asm.js encoder with vendored ffmpeg.wasm
MP4/GIF export used to importScripts() an 18.5 MB asm.js ffmpeg build from
https://archive.org/download/ffmpeg_asm/ffmpeg_asm.js: no integrity check, no
pinning, executed in the page, and unavailable offline. vendor.mjs now copies
ffmpeg.wasm out of node_modules, where package-lock.json pins it by hash, and
no CDN fallback is left anywhere in the app.

@ffmpeg/core-st is the single-threaded core, chosen deliberately: the default
@ffmpeg/core is built with pthreads and needs SharedArrayBuffer, which requires
COOP/COEP isolation, which would break the Pixabay, Unsplash and Google Fonts
requests. That core also forces two things worth knowing:

- mainName: 'main' is mandatory. The loader defaults to proxy_main, which only
  the multi-threaded build exports, so load() compiles all 23 MB and then aborts.
- Its main() calls exit(), so an instance survives exactly one command. Reusing
  one dies with "Program terminated with exit(0)", so convertStreams builds and
  tears one down per conversion (~110 ms, and the 23 MB heap comes back in
  between). The teardown also runs on failure: an interrupted run otherwise
  leaves the loader's "running" flag set and wedges every later conversion until
  a page reload.

MP4 encodes with libx264 -crf 23 -pix_fmt yuv420p plus AAC rather than
mpeg4 -b:v 6400k. Same core, better quality per byte, and yuv420p is what makes
it play in Safari and QuickTime.

The two @ffmpeg packages are dependencies, not devDependencies, so the Docker
vendor stage can npm ci --omit=dev without pulling in electron; build.files
excludes them from the asar since src/vendor/ffmpeg/ already carries the copies
the app loads. WITH_FFMPEG=0 now means MP4/GIF export is unavailable and says
so, rather than silently fetching an encoder at run time.

Also deletes src/js/libraries/ffmpeg.min.js, an unreferenced ffmpeg.wasm loader
stub that would have fetched its core from unpkg, and prunes the stale
src/vendor/ffmpeg_asm.js from existing checkouts — src/vendor/ is packaged
whole, so it would have shipped 18.5 MB of dead weight in every installer.

Verified in Chromium against a real MediaRecorder WebM: core loads with
crossOriginIsolated false, MP4 24 KB decoding to 320x240 / 2.00 s, GIF 138 KB,
the two back to back, and the missing-core path reporting correctly.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 15:41:40 +02:00

55 lines
1.9 KiB
Docker

# syntax=docker/dockerfile:1
#
# Motionity is a static app, so the runtime image is a static file server and
# nothing else: no Node, no shell tooling, ~8 MB of base image.
#
# Build: docker build -t motionity:latest .
# Run: docker run --rm -p 8080:8080 motionity:latest
#
# WITH_FFMPEG=0 drops the 23 MB ffmpeg.wasm core from the image and disables
# MP4/GIF export, which then reports itself as unavailable. It no longer falls
# back to downloading the encoder at run time: the old asm.js build came from a
# public archive.org mirror with no integrity check. Everything else, WEBM
# export included, is unaffected.
FROM node:22-alpine AS vendor
ARG WITH_FFMPEG=1
WORKDIR /app
# ffmpeg.wasm is a runtime dependency, so --omit=dev pulls it in without
# dragging electron and electron-builder (~400 MB) into the build.
COPY package.json package-lock.json ./
RUN npm ci --omit=dev --no-audit --no-fund
COPY scripts/vendor.mjs scripts/
COPY src/index.html src/
RUN if [ "$WITH_FFMPEG" = "1" ]; then \
node scripts/vendor.mjs; \
else \
node scripts/vendor.mjs --skip-ffmpeg; \
fi
FROM ghcr.io/static-web-server/static-web-server:2-alpine
COPY --chown=65534:65534 src/ /public/
COPY --from=vendor --chown=65534:65534 /app/src/vendor/ /public/vendor/
ENV SERVER_ROOT=/public \
SERVER_HOST=0.0.0.0 \
SERVER_PORT=8080 \
SERVER_COMPRESSION=true \
SERVER_COMPRESSION_LEVEL=default \
SERVER_CACHE_CONTROL_HEADERS=true \
SERVER_LOG_LEVEL=warn \
SERVER_SECURITY_HEADERS=false \
SERVER_HEALTH=true
USER 65534:65534
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \
CMD wget -q --spider http://127.0.0.1:8080/health || exit 1
# NOTE: browsers expose WebCodecs (the fast exporter) and IndexedDB (project
# saving) only in a secure context. Reaching this container over plain http://
# from another machine disables both. Publish it behind TLS, or keep access on
# http://localhost.