4bb64a8ca3
- ticker / QR ad overlay / break slide graphics (1920×1080) with all geometry derived from one shared module (src/shared/obs-geometry.mjs) - OBS scene-collection generator (scripts/generate-obs-scenes.mjs) and a read-only in-dashboard setup verifier, both driven by the same module - en/sr dashboard i18n selected via bundle config; configschema.json with neutral first-run defaults seeded from optional branding config - install.sh / systemd user service / update.sh for venue deployment - reference OBS scene collection + profile from a real venue as fixtures
167 lines
6.4 KiB
Bash
Executable File
167 lines
6.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# Bootstrap a complete NodeCG + nodecg-venue-gfx install on a venue machine.
|
||
#
|
||
# Creates a standalone NodeCG installation (the nodecg npm package extracted
|
||
# as the install root — the layout NodeCG itself calls "standalone"; running
|
||
# it as a node_modules dependency is still flagged experimental upstream),
|
||
# clones this bundle into bundles/, builds it, writes the config files, and
|
||
# generates an importable OBS scene collection.
|
||
#
|
||
# Usage:
|
||
# ./install.sh [--dir ~/venue-gfx] [--port 9090] [--obs-password SECRET]
|
||
# [--repo <git-url>] [--local <path-to-checkout>] [--yes]
|
||
#
|
||
# --local uses an existing checkout (copied, not cloned) instead of --repo;
|
||
# when run from inside a checkout, --local defaults to that checkout.
|
||
# --yes accepts all defaults without prompting.
|
||
|
||
set -euo pipefail
|
||
|
||
BUNDLE=nodecg-venue-gfx
|
||
DEFAULT_REPO="https://git.dvdrw.dev/kckljajicevo/nodecg-venue-gfx.git"
|
||
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
|
||
dir=""
|
||
port=""
|
||
obs_password=""
|
||
repo=""
|
||
local_src=""
|
||
assume_yes=0
|
||
|
||
while [[ $# -gt 0 ]]; do
|
||
case "$1" in
|
||
--dir) dir="$2"; shift 2 ;;
|
||
--port) port="$2"; shift 2 ;;
|
||
--obs-password) obs_password="$2"; shift 2 ;;
|
||
--repo) repo="$2"; shift 2 ;;
|
||
--local) local_src="$2"; shift 2 ;;
|
||
--yes|-y) assume_yes=1; shift ;;
|
||
--help|-h) sed -n '2,17p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
||
*) echo "unknown option: $1" >&2; exit 1 ;;
|
||
esac
|
||
done
|
||
|
||
# ── prerequisites ─────────────────────────────────────────────────────────
|
||
|
||
command -v node >/dev/null || { echo "node not found — install Node.js 20+" >&2; exit 1; }
|
||
command -v npm >/dev/null || { echo "npm not found" >&2; exit 1; }
|
||
|
||
node_major=$(node -p 'process.versions.node.split(".")[0]')
|
||
if (( node_major < 20 )); then
|
||
echo "Node.js $(node -v) is too old — need 20+" >&2
|
||
exit 1
|
||
fi
|
||
|
||
# When run from inside a checkout of the bundle, default to copying it.
|
||
if [[ -z $local_src && -z $repo && -f "$SCRIPT_DIR/../package.json" ]]; then
|
||
if node -p 'require(process.argv[1]).name' "$SCRIPT_DIR/../package.json" 2>/dev/null | grep -qx "$BUNDLE"; then
|
||
local_src="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||
fi
|
||
fi
|
||
|
||
ask() { # ask <prompt> <default-var-name> <default-value>
|
||
local prompt="$1" var="$2" def="$3" answer
|
||
if [[ -n ${!var} ]]; then return; fi
|
||
if (( assume_yes )); then printf -v "$var" '%s' "$def"; return; fi
|
||
read -r -p "$prompt [$def]: " answer
|
||
printf -v "$var" '%s' "${answer:-$def}"
|
||
}
|
||
|
||
ask "Install directory" dir "$HOME/venue-gfx"
|
||
ask "NodeCG port" port "9090"
|
||
ask "OBS websocket password (empty = auth disabled)" obs_password ""
|
||
if [[ -z $local_src ]]; then
|
||
ask "Bundle git URL" repo "$DEFAULT_REPO"
|
||
fi
|
||
|
||
dir="${dir/#\~/$HOME}"
|
||
echo
|
||
echo "Installing into $dir (NodeCG on port $port)"
|
||
|
||
# ── NodeCG itself (standalone layout: install root = the nodecg package) ──
|
||
|
||
mkdir -p "$dir"
|
||
cd "$dir"
|
||
|
||
if [[ -f package.json ]] && node -p 'require("./package.json").name' | grep -qx nodecg; then
|
||
echo "NodeCG already present — skipping"
|
||
else
|
||
echo "Downloading NodeCG…"
|
||
tarball=$(npm pack nodecg@^2 --silent | tail -n1)
|
||
tar xzf "$tarball" --strip-components=1
|
||
rm -f "$tarball"
|
||
echo "Installing NodeCG dependencies…"
|
||
npm install --omit=dev --no-audit --no-fund
|
||
fi
|
||
|
||
mkdir -p bundles cfg obs
|
||
|
||
# ── the bundle ────────────────────────────────────────────────────────────
|
||
|
||
if [[ ! -d bundles/$BUNDLE ]]; then
|
||
if [[ -n $local_src ]]; then
|
||
echo "Copying bundle from $local_src…"
|
||
mkdir -p "bundles/$BUNDLE"
|
||
(cd "$local_src" && git archive HEAD 2>/dev/null || tar cf - --exclude node_modules --exclude .git .) \
|
||
| tar xf - -C "bundles/$BUNDLE"
|
||
else
|
||
echo "Cloning $repo…"
|
||
git clone "$repo" "bundles/$BUNDLE"
|
||
fi
|
||
fi
|
||
|
||
echo "Building the bundle…"
|
||
(cd "bundles/$BUNDLE" && npm ci --no-audit --no-fund && npm run build)
|
||
|
||
# ── config ────────────────────────────────────────────────────────────────
|
||
|
||
if [[ ! -f cfg/nodecg.json ]]; then
|
||
printf '{\n\t"port": %s\n}\n' "$port" > cfg/nodecg.json
|
||
echo "Wrote cfg/nodecg.json"
|
||
fi
|
||
|
||
if [[ ! -f cfg/$BUNDLE.json ]]; then
|
||
node - "$obs_password" > "cfg/$BUNDLE.json" <<'EOF'
|
||
const password = process.argv[2] ?? "";
|
||
process.stdout.write(JSON.stringify({
|
||
locale: "en",
|
||
obs: { enabled: true, url: "ws://127.0.0.1:4455", password },
|
||
}, null, "\t") + "\n");
|
||
EOF
|
||
echo "Wrote cfg/$BUNDLE.json"
|
||
fi
|
||
|
||
# ── OBS scene collection ──────────────────────────────────────────────────
|
||
|
||
node "bundles/$BUNDLE/scripts/generate-obs-scenes.mjs" \
|
||
--config "cfg/$BUNDLE.json" \
|
||
--nodecg-url "http://localhost:$port" \
|
||
--name VenueGfx \
|
||
--out obs/VenueGfx.json
|
||
|
||
# ── done ──────────────────────────────────────────────────────────────────
|
||
|
||
cat <<EOF
|
||
|
||
──────────────────────────────────────────────────────────────────────
|
||
Install complete. Start NodeCG with:
|
||
|
||
cd $dir && node index.js
|
||
|
||
Dashboard: http://localhost:$port
|
||
|
||
OBS setup (see bundles/$BUNDLE/docs/OBS-SETUP.md for details):
|
||
1. Install exeldro's Move plugin: https://obsproject.com/forum/resources/move.913/
|
||
2. Tools → WebSocket Server Settings: enable, port 4455$( [[ -n $obs_password ]] && echo ", set the password you chose" || echo ", disable authentication" )
|
||
3. Scene Collection → Import → $dir/obs/VenueGfx.json, then switch to it
|
||
4. Replace the "Program Placeholder" source with your real capture
|
||
5. Point the Stinger transition at a real video file
|
||
6. Settings → Video: 1920×1080 base + output, 60 FPS
|
||
7. Dashboard → "OBS setup check" panel → Run checks, fix anything red
|
||
|
||
To run NodeCG as a service that survives reboots:
|
||
bundles/$BUNDLE/scripts/install-service.sh $dir
|
||
──────────────────────────────────────────────────────────────────────
|
||
EOF
|