#!/usr/bin/env bash # fetch-ffmpeg.sh — populate ./ffmpeg/ with the pinned BtbN LGPL-shared FFmpeg build. # # WHY THIS EXISTS: the app used to download ffmpeg from GitHub on first run and execute # it. That is Windows Store policy 10.2.2 (dynamic code inclusion) and it already cost a # real failure — the pin aged out of BtbN's 14-day retention and the download 404'd on # the creator's first recording attempt (2026-09-01). FFmpeg is now BUNDLED, so nothing is # fetched at runtime. This script is the BUILD-TIME half of that: a fresh clone has no # ffmpeg/ folder, so it must be filled once before the first build. # # The binaries are NOT committed to git (135 MB of incompressible DLLs would tax every # commit on this 9p-bridged working tree). The URL + SHA-256 below are the audit trail # that the LGPL written-offer obligation actually needs, and they are reviewable in a diff. # # Usage: ./scripts/fetch-ffmpeg.sh # FFMPEG_ZIP=/path/to/already-downloaded.zip ./scripts/fetch-ffmpeg.sh # # Idempotent: re-running with ffmpeg/ already populated verifies and exits. Delete # ffmpeg/ to force a refetch. set -euo pipefail # Pinned BtbN lgpl-shared win64 build. A MONTH-END autobuild tag on purpose: BtbN keeps # ~14 dailies, but month-end builds for ~2 years. Do not "refresh" this to a daily — that # is the mistake that caused the 404. URL="https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2026-08-31-13-27/ffmpeg-N-126342-gf88b741dbf-win64-lgpl-shared.zip" SHA256="46ea6521ed3877689d2ff0bd03640ac2637104415f6237d42625d13408767146" # The files we ship. ffplay.exe (18 MB) is deliberately excluded — nothing in the app # calls it, and shipping an unused player would widen the package and the license surface # for nothing. WANT_EXE="ffmpeg.exe ffprobe.exe" ROOT="$(cd "$(dirname "$0")/.." && pwd)" DEST="$ROOT/ffmpeg" if [ -f "$DEST/ffmpeg.exe" ]; then echo "✓ ffmpeg/ already populated — nothing to do." echo " (delete $DEST to force a refetch)" exit 0 fi command -v python3 >/dev/null 2>&1 || { echo "❌ python3 is required to extract the zip (unzip is not installed in this WSL)." echo " Set FFMPEG_ZIP to a pre-downloaded archive, or install python3." exit 1 } ZIP="${FFMPEG_ZIP:-}" if [ -z "$ZIP" ]; then ZIP="$(mktemp -t ffmpeg-pin-XXXXXX.zip)" trap 'rm -f "$ZIP"' EXIT echo "══ Downloading the pinned FFmpeg build (~65 MB, one time) ══" curl -fsSL --retry 3 -o "$ZIP" "$URL" fi echo "══ Verifying SHA-256 ══" GOT="$(sha256sum "$ZIP" | cut -d' ' -f1)" if [ "$GOT" != "$SHA256" ]; then echo "❌ SHA-256 mismatch — refusing to extract." echo " expected $SHA256" echo " actual $GOT" exit 1 fi echo "✓ $GOT" echo "══ Extracting into ffmpeg/ ══" python3 - "$ZIP" "$DEST" "$WANT_EXE" <<'PY' import os, sys, zipfile archive_path, dest, want = sys.argv[1], sys.argv[2], set(sys.argv[3].split()) wanted_exe = {n for n in want} os.makedirs(dest, exist_ok=True) found = [] with zipfile.ZipFile(archive_path) as z: for name in z.namelist(): if '/bin/' not in name or name.endswith('/'): continue base = os.path.basename(name) if not (base.endswith('.dll') or base in wanted_exe): continue with z.open(name) as src, open(os.path.join(dest, base), 'wb') as out: out.write(src.read()) found.append(base) missing = wanted_exe - set(found) if missing: sys.exit(f"❌ archive is missing required binaries: {sorted(missing)}") print(f"✓ extracted {len(found)} files") for base in sorted(found): print(f" {base}") PY echo "" echo "✓ Bundled FFmpeg ready. The build copies ffmpeg/ → bin///ffmpeg/." echo " Nothing is downloaded at runtime any more (Store policy 10.2.2 satisfied)."