2026-10-28 20:24:49 +00:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
# Turns any fly-through clip into the WebP frame sequences the scroll scene scrubs through.
|
2026-10-28 21:30:16 +00:00
|
|
|
# desktop: 16:9 1280x720 (landscape screens)
|
|
|
|
|
# tablet: centre-cropped 3:4 810x1080 (portrait tablets)
|
|
|
|
|
# mobile: centre-cropped 9:16 608x1080 (portrait phones)
|
|
|
|
|
# Portrait crops use the full height of a 1080p source, so they're as sharp as the footage allows.
|
2026-10-28 20:24:49 +00:00
|
|
|
#
|
|
|
|
|
# Usage: scripts/make-frames.sh <video.mp4> [frame-count=120]
|
|
|
|
|
# Then commit public/frames/**. The site reads public/frames/manifest.json.
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
IN=${1:?video file}
|
|
|
|
|
COUNT=${2:-120}
|
|
|
|
|
OUT="$(cd "$(dirname "$0")/.." && pwd)/public/frames"
|
|
|
|
|
|
|
|
|
|
DUR=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$IN")
|
|
|
|
|
FPS=$(echo "$COUNT / $DUR" | bc -l)
|
|
|
|
|
|
2026-10-28 21:30:16 +00:00
|
|
|
SETS="desktop tablet mobile"
|
|
|
|
|
for s in $SETS; do
|
|
|
|
|
mkdir -p "$OUT/$s"
|
|
|
|
|
find "$OUT/$s" -name '*.webp' -delete
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
filter() {
|
|
|
|
|
case $1 in
|
|
|
|
|
desktop) echo "scale=1280:720:force_original_aspect_ratio=increase,crop=1280:720" ;;
|
|
|
|
|
tablet) echo "scale=-2:1080,crop=810:1080" ;;
|
|
|
|
|
mobile) echo "scale=-2:1080,crop=608:1080" ;;
|
|
|
|
|
esac
|
|
|
|
|
}
|
|
|
|
|
quality() {
|
|
|
|
|
case $1 in
|
|
|
|
|
desktop) echo 60 ;;
|
|
|
|
|
*) echo 66 ;;
|
|
|
|
|
esac
|
|
|
|
|
}
|
2026-10-28 20:24:49 +00:00
|
|
|
|
|
|
|
|
TMP=$(mktemp -d)
|
2026-10-28 21:30:16 +00:00
|
|
|
for s in $SETS; do
|
|
|
|
|
mkdir -p "$TMP/$s"
|
|
|
|
|
ffmpeg -loglevel error -y -i "$IN" -vf "fps=$FPS,$(filter $s)" -frames:v "$COUNT" "$TMP/$s/%04d.png"
|
|
|
|
|
# cwebp (brew install webp), encoded in parallel
|
|
|
|
|
ls "$TMP/$s"/*.png | xargs -P 8 -I{} sh -c 'f={}; cwebp -quiet -q '"$(quality $s)"' -m 6 "$f" -o "'"$OUT/$s"'/$(basename "$f" .png).webp"'
|
|
|
|
|
done
|
2026-10-28 20:24:49 +00:00
|
|
|
rm -r "$TMP"
|
|
|
|
|
|
|
|
|
|
N=$(ls "$OUT/desktop" | wc -l | tr -d ' ')
|
2026-10-28 21:30:16 +00:00
|
|
|
printf '{ "count": %s, "desktop": "/frames/desktop/", "tablet": "/frames/tablet/", "mobile": "/frames/mobile/" }\n' "$N" > "$OUT/manifest.json"
|
|
|
|
|
echo "wrote $N frames -> $OUT"
|
|
|
|
|
for s in $SETS; do echo " $s: $(du -sh "$OUT/$s" | cut -f1)"; done
|