Zum Inhalt

scripts/shoot-appstore-stills.sh

#!/usr/bin/env bash
# Captures clean, chrome-free App Store stills: each of the 7 curated presets at peak
# density, per device size. Uses the DEBUG hooks PHYSARUM_BOOT_PRESET=<index> (boot a
# specific filled slot) + PHYSARUM_HIDE_UI=1 (launch chrome-free — reuses fullscreen, so
# topbar, panels, status bar + home indicator are all hidden). tvOS ignores HIDE_UI (its
# ambient view is already chrome-free without remote interaction).
#
# Output: docs/appstore/screenshots/<device>/<NN>-<name>.png
# Usage:  scripts/shoot-appstore-stills.sh [device-label ...]   (default: all three)
#         labels: iphone-6.9  ipad-13  appletv
# Requires: Xcode + simulators (6.9" iPhone Pro Max, 13" iPad Pro, Apple TV 4K).
# Run ONLY when no other xcodebuild is active (concurrent builds on the same project race).
set -euo pipefail
cd "$(dirname "$0")/.."

SCHEME="Physarum"
PROJ="apple/PhysarumApp/Physarum.xcodeproj"
BUNDLE_ID="de.jkaindl.physarum"
OUT="docs/appstore/screenshots"
PRESETS=(aurora terminal-haze neon-waves solar-veil dancing-ink mandala mystic)
PEAK=15   # seconds to let each preset's pattern reach full density before the shot
SAT_MIN=2 # min SATAVG: a vibrant preset is ≫2; the grey tvOS home screen is ~0.2 (relaunch
          # sometimes fails to foreground the app on the tvOS simulator → re-shoot)

# Integer SATAVG (average chroma) of a PNG, 0 if unreadable. Discriminates a vibrant preset
# from the near-grey home/Settings screen the tvOS simulator shows when a launch misses.
satavg() {
  local s
  s=$(ffmpeg -i "$1" -vf "signalstats,metadata=print" -f null - 2>&1 \
        | grep -oE "signalstats.SATAVG=[0-9.]+" | head -1 | cut -d= -f2)
  s=${s%%.*}; echo "${s:-0}"
}

( cd apple/PhysarumApp && xcodegen generate )

shoot_device() {
  local sim="$1" dir="$2" sdk="$3"
  echo "==> $dir"
  local udid
  udid=$(xcrun simctl list devices available | grep -E "$sim" | grep -oiE '[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}' | head -1 || true)
  [ -n "$udid" ] || { echo "skip: no simulator matching /$sim/"; return 0; }
  xcrun simctl boot "$udid" 2>/dev/null || true
  xcrun simctl bootstatus "$udid" -b
  local dd="${TMPDIR:-/tmp}/physarum-stills-$dir"
  xcodebuild -project "$PROJ" -scheme "$SCHEME" -destination "id=$udid" -derivedDataPath "$dd" build >/dev/null
  local app
  app=$(find "$dd/Build/Products" -name 'Physarum.app' -path "*$sdk*" | head -1)
  [ -n "$app" ] || { echo "skip: no .app built for $dir"; return 0; }
  xcrun simctl install "$udid" "$app"
  mkdir -p "$OUT/$dir"
  local i name out attempt sat
  for i in $(seq 0 6); do
    name="${PRESETS[$i]}"
    out="$OUT/$dir/$(printf '%02d' $((i + 1)))-$name.png"
    printf '    [%d/7] %s …\n' "$((i + 1))" "$name"
    # Re-run friendly: keep an already-valid shot, only (re)capture missing/broken ones.
    if [ -f "$out" ] && [ "$(satavg "$out")" -ge "$SAT_MIN" ]; then
      echo "        ✓ valid shot exists — skip"; continue
    fi
    attempt=0
    while :; do
      attempt=$((attempt + 1))
      xcrun simctl terminate "$udid" "$BUNDLE_ID" >/dev/null 2>&1 || true
      sleep 1
      SIMCTL_CHILD_PHYSARUM_BOOT_PRESET="$i" SIMCTL_CHILD_PHYSARUM_HIDE_UI=1 \
        xcrun simctl launch "$udid" "$BUNDLE_ID" >/dev/null 2>&1 || true
      sleep "$PEAK"
      xcrun simctl io "$udid" screenshot "$out" >/dev/null 2>&1 || true
      sat=$(satavg "$out")
      if [ "$sat" -ge "$SAT_MIN" ]; then echo "        ✓ sat=$sat (try $attempt)"; break; fi
      if [ "$attempt" -ge 5 ]; then echo "        ⚠ low sat=$sat after $attempt tries — keeping"; break; fi
      echo "        retry (sat=$sat — app not foreground?)"
    done
  done
  echo "    wrote 7 stills to $OUT/$dir/"
}

DEVICES="${*:-iphone-6.9 ipad-13 appletv}"
for d in $DEVICES; do
  case "$d" in
    iphone-6.9) shoot_device "iPhone 1[5-9] Pro Max" "iphone-6.9" "iphonesimulator" ;;
    ipad-13)    shoot_device "iPad Pro 13-inch"      "ipad-13"    "iphonesimulator" ;;
    appletv)    shoot_device "Apple TV 4K"           "appletv"    "appletvsimulator" ;;
    *) echo "unknown device label: $d (use iphone-6.9 | ipad-13 | appletv)" >&2 ;;
  esac
done

echo "Done. Review $OUT/*/"