init: initial commit
- 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
This commit is contained in:
Executable
+166
@@ -0,0 +1,166 @@
|
||||
#!/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
|
||||
Reference in New Issue
Block a user