quick_search/packaging/capture.sh
= dc9580a659
Some checks failed
CI / linux (push) Has been cancelled
CI / release (push) Has been skipped
CI / windows-cross (push) Successful in 2m8s
Added AppImage to CI
2026-08-07 20:44:07 -04:00

131 lines
5.9 KiB
Bash

#!/usr/bin/env bash
# Regenerate the website capture assets: search.webm, manage-indexing.webm,
# duplicates.png, query-highlight.png — all landing in packaging/captures/
# (gitignored). Needs a graphical session (X11 or Wayland — screenshots and
# video frames are read back from the app's own framebuffer, so the display
# server does not matter) and ffmpeg with libx264rgb and libvpx-vp9.
#
# The app is built with the `capture` feature and drives itself through
# packaging/capture-scenario.txt (see crates/quicksearch-gui/src/capture.rs
# for the command grammar). It runs against a throwaway index of this
# repository plus ~/.cargo/registry/src, under scratch XDG dirs — the real
# ~/.config/quicksearch and index are never touched.
#
# The scratch dirs live OUTSIDE both index roots on purpose: the demo config
# has an empty ignore list, so a scratch index database inside an indexed
# tree would be indexed and watched by the very run that writes it.
set -euo pipefail
here="$(cd "$(dirname "$0")" && pwd)"
repo="$(dirname "$here")"
out="$here/captures"
work="${TMPDIR:-/tmp}/quicksearch-capture"
# --- preflight --------------------------------------------------------------
if [ -z "${DISPLAY:-}" ] && [ -z "${WAYLAND_DISPLAY:-}" ]; then
echo "capture needs a graphical session (neither DISPLAY nor" \
"WAYLAND_DISPLAY is set)" >&2
exit 1
fi
for tool in ffmpeg ffprobe cargo; do
command -v "$tool" >/dev/null || { echo "missing tool: $tool" >&2; exit 1; }
done
# The encoder list is captured first: `grep -q` closing the pipe early would
# make ffmpeg exit on SIGPIPE, which pipefail reports as failure.
encoders="$(ffmpeg -hide_banner -encoders 2>/dev/null)"
for enc in libx264rgb libvpx-vp9; do
grep -q "$enc" <<< "$encoders" \
|| { echo "ffmpeg lacks the $enc encoder" >&2; exit 1; }
done
# A config.toml beside the binary would silently override the scratch XDG
# config entirely (portable mode) — refuse to run with one present.
if [ -e "$repo/target/release/config.toml" ]; then
echo "remove $repo/target/release/config.toml first: portable mode would" \
"override the capture config" >&2
exit 1
fi
registry="$HOME/.cargo/registry/src"
[ -d "$registry" ] || { echo "missing demo index root: $registry" >&2; exit 1; }
# --- build + parser tests ---------------------------------------------------
cargo test --manifest-path "$repo/Cargo.toml" --release --locked \
-p quicksearch-gui --features capture capture::
cargo build --manifest-path "$repo/Cargo.toml" --release --locked \
-p quicksearch-gui --features capture
# --- scratch environment ----------------------------------------------------
# Fresh every run: no stale index, no schema-mismatch prompt, and a fresh
# app.ron so the window opens at its default 1000x700 geometry.
rm -rf "$work"
mkdir -p "$work/config/quicksearch" "$work/data" "$work/tmp" "$out"
cat > "$work/config/quicksearch/config.toml" <<EOF
# Generated by packaging/capture.sh — the demo config for capture runs.
[paths]
indexing_paths = ["$repo", "$registry"]
[indexing]
auto_index = true
include_hidden = true
ignore_patterns = []
# 1.5x zoom + proportionally larger windows (set in the scenario) render the
# same layout at ~1.5x the pixel density, for crisper website assets.
[ui]
scale = 1.25
EOF
# --- run the scripted app ---------------------------------------------------
# The outer timeout catches a wedged run; the driver's own hard timeouts
# should fire well before it.
echo "Running the capture scenario (the initial index build takes minutes)..."
env XDG_CONFIG_HOME="$work/config" \
XDG_DATA_HOME="$work/data" \
QS_CAPTURE_SCRIPT="$here/capture-scenario.txt" \
QS_CAPTURE_OUT="$work/tmp" \
timeout 2400 "$repo/target/release/quicksearch" \
|| { code=$?; echo "capture run failed (exit $code; 2=script parse," \
"3=wait timeout, 4=io/ffmpeg, 124=hung)" >&2; exit 1; }
# --- transcode the lossless intermediates to VP9 webm -----------------------
# The recording itself is lossless, so webm quality is decided entirely here.
# QS_WEBM_CRF is the knob: constant-quality factor, 0 (best) to 63; UI text
# stays crisp around 24-32, and each step of ~6 roughly halves/doubles the
# file size. QS_WEBM_CPU trades encode time for compression efficiency
# (0 slowest/best to 5 fastest).
crf="${QS_WEBM_CRF:-26}"
cpu="${QS_WEBM_CPU:-0}"
for clip in manage-indexing search; do
[ -s "$work/tmp/$clip.cap.mkv" ] \
|| { echo "missing recording: $clip.cap.mkv" >&2; exit 1; }
# The crop drops at most one row/column: yuv420p needs even dimensions,
# and fractional display scaling can make the window an odd size.
ffmpeg -y -hide_banner -loglevel warning -i "$work/tmp/$clip.cap.mkv" \
-vf "crop=trunc(iw/2)*2:trunc(ih/2)*2" \
-c:v libvpx-vp9 -b:v 0 -crf "$crf" -deadline good -cpu-used "$cpu" \
-row-mt 1 -pix_fmt yuv420p -an "$out/$clip.webm"
done
mv "$work/tmp/query-highlight.png" "$work/tmp/duplicates.png" "$out/"
# --- verify -----------------------------------------------------------------
fail=0
for f in "$out/search.webm" "$out/manage-indexing.webm"; do
codec=$(ffprobe -v error -select_streams v:0 \
-show_entries stream=codec_name -of csv=p=0 "$f")
dur=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$f")
[ "$codec" = vp9 ] || { echo "FAIL: $f codec=$codec" >&2; fail=1; }
awk -v d="$dur" 'BEGIN{exit !(d>=4 && d<=60)}' \
|| { echo "FAIL: $f duration=${dur}s (expected 4-60s)" >&2; fail=1; }
echo "OK: $f (vp9, ${dur}s)"
done
for f in "$out/duplicates.png" "$out/query-highlight.png"; do
dims=$(ffprobe -v error -select_streams v:0 \
-show_entries stream=width,height -of csv=p=0 "$f")
echo "OK: $f (${dims})"
done
[ "$fail" -eq 0 ]
# $work is kept for post-mortems (app stderr is on this terminal; the scratch
# index and raw .cap.mkv files live there) and recreated fresh next run.
echo
echo "Assets:"
ls -l "$out"/*.webm "$out"/*.png