mirror of
https://github.com/screentinker/screentinker.git
synced 2026-08-14 14:23:14 -06:00
Four loose files that must all land intact, in the right place, is a poor way to
hand someone a player. autorun.zip is one file: drop it on the root of a
player's storage, power-cycle, and autozip.brs unpacks it in place and reboots
into the player. A half-copied set of loose files boots into something broken; a
half-copied zip simply fails to extract and leaves the player as it was.
Two rules the format imposes, both of which fail SILENTLY when broken, so the
build script asserts them instead of trusting them:
- the archive must expand to files at its root, with no wrapper directory. A
player extracts to the storage root, so a nested folder puts autorun.brs
somewhere the player never looks and the card appears to do nothing.
- autorun.brs must not sit next to autorun.zip on the storage root; its
presence stops the zip being processed at all.
autozip.brs renames the archive to autorun.zip.done after a successful extract,
which is what makes it idempotent — without that the player extracts, reboots,
extracts, reboots, a loop indistinguishable from a hardware fault. A FAILED
extract deliberately does not rename, so a truncated copy gets retried once
someone replaces it rather than being skipped forever.
It is volume-aware for the same reason autorun.brs is: a player may be booting
from internal flash because its card interface is dead, and extracting to "SD:/"
on such a unit writes to a volume that does not exist.
--server rewrites screentinker.json in the staging copy so a batch can be imaged
for a specific instance without hand-editing anything.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uaeo9MvzKoyXuN6ZsbhtkL
79 lines
3.1 KiB
Bash
Executable file
79 lines
3.1 KiB
Bash
Executable file
#!/bin/bash
|
|
# Build brightsign/autorun.zip — the single-file installer for a BrightSign player.
|
|
#
|
|
# scripts/build-autorun-zip.sh [--server https://your-server] [-o path/to/autorun.zip]
|
|
#
|
|
# Drop the resulting autorun.zip on the root of a player's storage (microSD, USB, or internal
|
|
# flash over SFTP) and power-cycle. autozip.brs unpacks it in place, marks it done, and reboots
|
|
# into the player. One file to distribute instead of four that must all land intact.
|
|
#
|
|
# ⚠️ The zip must expand to files AT ITS ROOT — no wrapper directory. A player extracts to the
|
|
# storage root, so a nested folder puts autorun.brs somewhere the player never looks and the
|
|
# card silently does nothing. That is why this zips from *inside* the staging directory.
|
|
#
|
|
# ⚠️ autorun.brs must NOT sit next to autorun.zip on the storage root: its presence stops the zip
|
|
# being processed at all. It belongs inside, which is where this puts it.
|
|
set -euo pipefail
|
|
cd "$(dirname "$0")/.."
|
|
|
|
SERVER=""
|
|
OUT="brightsign/autorun.zip"
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--server) SERVER="${2:-}"; shift 2 ;;
|
|
-o|--out) OUT="${2:-}"; shift 2 ;;
|
|
-h|--help) sed -n '2,12p' "$0"; exit 0 ;;
|
|
*) echo "unknown argument: $1" >&2; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
command -v zip >/dev/null || { echo "ERROR: 'zip' is not installed." >&2; exit 1; }
|
|
|
|
STAGE="$(mktemp -d)"
|
|
trap 'rm -rf "$STAGE"' EXIT
|
|
|
|
# The payload. autozip.brs must be here too: it is what the NEXT player to receive this archive
|
|
# runs, and it has to survive being extracted alongside everything else.
|
|
cp brightsign/autozip.brs "$STAGE/"
|
|
cp brightsign/autorun.brs "$STAGE/"
|
|
cp brightsign/offline.html "$STAGE/"
|
|
cp brightsign/screentinker.json "$STAGE/"
|
|
|
|
# Point a batch at a specific server without hand-editing each card.
|
|
if [ -n "$SERVER" ]; then
|
|
python3 - "$STAGE/screentinker.json" "$SERVER" <<'PY'
|
|
import json, sys
|
|
path, server = sys.argv[1], sys.argv[2]
|
|
cfg = json.load(open(path))
|
|
cfg['server_url'] = server
|
|
json.dump(cfg, open(path, 'w'), indent=2)
|
|
PY
|
|
echo " server_url set to $SERVER"
|
|
fi
|
|
|
|
mkdir -p "$(dirname "$OUT")"
|
|
rm -f "$OUT"
|
|
ABS_OUT="$(cd "$(dirname "$OUT")" && pwd)/$(basename "$OUT")"
|
|
|
|
# -j would flatten any directories we add later; instead cd in and zip '.' so the archive root IS
|
|
# the staging root, and future subdirectories keep their structure.
|
|
( cd "$STAGE" && zip -q -r -X "$ABS_OUT" . )
|
|
|
|
echo " built $OUT"
|
|
unzip -l "$OUT" | sed 's/^/ /'
|
|
|
|
# Prove the root-level invariant rather than trusting it: this is the one mistake that makes a
|
|
# card look blank to the player, and it is invisible until hardware refuses to boot.
|
|
if unzip -l "$OUT" | awk 'NR>3 && $4 ~ /\// && $4 !~ /^[^\/]+$/ {print $4}' | grep -qE '^[^/]+/'; then
|
|
echo " NOTE: archive contains directories — verify they are intended subdirectories, not a wrapper."
|
|
fi
|
|
if ! unzip -l "$OUT" | grep -qE ' autorun\.brs$'; then
|
|
echo "ERROR: autorun.brs is not at the archive root — the player would never find it." >&2
|
|
exit 1
|
|
fi
|
|
if ! unzip -l "$OUT" | grep -qE ' autozip\.brs$'; then
|
|
echo "ERROR: autozip.brs is missing — nothing would unpack this archive." >&2
|
|
exit 1
|
|
fi
|
|
echo " root-level layout verified"
|