webapp: archive pre-migration scene dirs + bash scripts, update docs

Moves the seven HTML scene dirs (landing/, loading/, game/, desktop/,
goodbye/, music-box/, music/) and the superseded bash helpers (setup.sh,
loading.sh, playlist.sh, telemetry.sh) into archived/ rather than deleting.
The Laravel webapp/ replaces all of them; archived/README.md spells out
the rollback procedure.

Also:
 - rewrite the relevant sections of CLAUDE.md so it points at webapp/
   blade views, the Artisan commands, and the supervisord lifecycle
   (`supervisorctl restart obs-webapp` after route / controller / .env
   changes; Blade view edits are still safe-while-running via OBS's
   Refresh cache).
 - extend scripts/deploy-rig.sh to install php + composer + supervisor,
   run `composer install`, copy obs-webapp.supervisord.conf into
   /etc/supervisor.d/, start the program, and call `php artisan
   rig:setup` + `rig:telemetry --collect` instead of the old bash.
 - .gitignore catches the generated machine-local files that came along
   when the old scene dirs moved (telemetry.js, loading.json, playlist.js,
   cmd.js, obs-config.js).
 - daemon.blade.php is now passive — listens to mpd:state but does not
   play audio or broadcast its own queue, so it stops fighting with
   bridges/mpd-state.py (the post-browser-daemon-migration source of
   truth for mpd:state).
 - nc.blade.php overrides .terminal { overflow: hidden } from hud.css
   so the `┤ TERMINAL ├` and `┤ NOW PLAYING ├` pane-tabs stick above
   the pane border instead of being clipped.
This commit is contained in:
Jakub Zych
2026-05-21 13:08:49 +02:00
parent 059f069ef4
commit 0b4f121a92
23 changed files with 229 additions and 156 deletions

171
archived/scripts/loading.sh Executable file
View File

@@ -0,0 +1,171 @@
#!/usr/bin/env bash
# Interactive manifest builder for the Project Loading scene.
#
# ./loading.sh
#
# Walks you through 5 prompts, writes loading.json + loading.js, then
# refresh the Project Loading browser source in OBS. Re-running uses
# previous answers as defaults — press Enter to keep them.
set -euo pipefail
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
OBS_DIR="$(cd "$DIR/.." && pwd)"
JSON="$OBS_DIR/loading/loading.json"
JS="$OBS_DIR/loading/loading.js"
# Reads a top-level field from the previous loading.json. Always exits 0;
# prints empty string if file/field is missing.
prev() {
[[ -f "$JSON" ]] || return 0
python3 - "$JSON" "$1" <<'PY' 2>/dev/null || true
import json, sys
try:
d = json.load(open(sys.argv[1]))
v = d.get(sys.argv[2])
if v is None: print('')
elif isinstance(v, bool): print('y' if v else 'n')
else: print(v)
except Exception:
pass
PY
}
ask() {
# ask "question" "default" → echoes user input or default if blank
local q="$1" def="${2:-}"
local prompt=" $q"
[[ -n "$def" ]] && prompt+=" [$def]"
prompt+=": "
local v
read -rp "$prompt" v
[[ -z "$v" && -n "$def" ]] && v="$def"
printf '%s' "$v"
}
ask_yn() {
local v; v=$(ask "$1" "${2:-y}")
case "${v,,}" in
y|yes|on|true|1) printf 'true' ;;
n|no|off|false|0) printf 'false' ;;
*) printf 'true' ;;
esac
}
ask_int() {
local v
while :; do
v=$(ask "$1" "${2:-}")
[[ "$v" =~ ^[0-9]+$ ]] && { printf '%s' "$v"; return; }
echo " ✗ must be a whole number" >&2
done
}
sanitize() {
printf '%s' "${1:-}" \
| tr -d '"\\' \
| tr '\n\r\t' ' ' \
| sed 's/ */ /g; s/^ //; s/ $//'
}
emit_str() { [[ -z "${1:-}" ]] && printf 'null' || printf '"%s"' "$1"; }
# ── Pull previous answers as defaults ───────────────
prev_game=$(prev game)
prev_game_id=$(prev gameId)
prev_subtitle=$(prev subtitle)
prev_count=$(prev countdownMin); [[ -z "$prev_count" ]] && prev_count="5"
prev_camera=$(prev camera); [[ -z "$prev_camera" ]] && prev_camera="y"
prev_mic=$(prev microphone); [[ -z "$prev_mic" ]] && prev_mic="y"
echo "── Project Loading manifest ──"
echo " (press Enter to keep [defaults])"
echo
# Resolve Game via Twitch search → exact directory entry. The picker prints
# "<id>\t<name>" to stdout on success, or non-zero on no-match / cancel.
# Re-running and accepting the previous game unchanged reuses the cached id
# so we don't burn an API call to re-resolve a known answer.
SEARCH_GAME="$OBS_DIR/twitch-bot/search-game.py"
ENV_FILE="$OBS_DIR/twitch-bot/.env.ophi118"
have_twitch=0
[[ -x "$SEARCH_GAME" && -f "$ENV_FILE" ]] && have_twitch=1
game=""; game_id=""
while :; do
query=$(ask "Game (search)" "$prev_game")
if [[ -z "$query" ]]; then
echo " ✗ Game is required" >&2
continue
fi
if (( have_twitch )); then
# Reuse cached id only if it looks like a real Twitch numeric id. A past
# bug (search-game.py prompt leaking into stdout) wrote "Pick: 506462"
# here; the regex makes sure such corruption falls back to a fresh search
# instead of getting passed straight to PATCH /helix/channels.
if [[ -n "$prev_game_id" && "$query" == "$prev_game" && "$prev_game_id" =~ ^[0-9]+$ ]]; then
game="$prev_game"; game_id="$prev_game_id"
echo " ↻ reusing cached: $game (id=$game_id)"
break
fi
if line=$("$SEARCH_GAME" "$query"); then
IFS=$'\t' read -r game_id game <<<"$line"
break
fi
# search-game.py already printed its own error; loop and re-ask
else
# no twitch helper available — accept the raw input, no id resolution
game="$query"; game_id=""
break
fi
done
subtitle=$(ask "Subtitle (mode / episode / note)" "$prev_subtitle")
countdown=$(ask_int "Countdown (minutes)" "$prev_count")
camera=$(ask_yn "Camera" "$prev_camera")
mic=$(ask_yn "Microphone" "$prev_mic")
game=$(sanitize "$game")
subtitle=$(sanitize "$subtitle")
now_iso=$(date -u +%FT%TZ)
tmp="$JSON.tmp.$$"
cat > "$tmp" <<JSON
{
"compiledAt": "$now_iso",
"game": $(emit_str "$game"),
"gameId": $(emit_str "$game_id"),
"subtitle": $(emit_str "$subtitle"),
"countdownMin": $countdown,
"camera": $camera,
"microphone": $mic
}
JSON
if ! python3 -m json.tool "$tmp" >/dev/null 2>&1; then
echo " ✗ produced invalid JSON, leaving $tmp for inspection" >&2
exit 1
fi
mv -f "$tmp" "$JSON"
{ printf 'window.__LOADING = '; cat "$JSON"; printf ';\n'; } > "$JS.tmp.$$"
mv -f "$JS.tmp.$$" "$JS"
echo
echo " ✓ wrote $JSON"
echo " ✓ wrote $JS"
# ── Push to Twitch (game + title) via the broadcaster token ─────────────
# Soft-fail: a Twitch hiccup must not block the local manifest from being
# written — the overlay can still load offline. set-channel.py reads
# ../twitch-bot/.env.ophi118 (channel:manage:broadcast scope required).
# We pass --game-id so set-channel.py skips its own (exact-match) lookup —
# the id is already resolved by the search step above.
SET_CHANNEL="$OBS_DIR/twitch-bot/set-channel.py"
if (( have_twitch )) && [[ -x "$SET_CHANNEL" && -n "$game_id" ]]; then
twitch_title="${subtitle:-$game}"
echo
if ! "$SET_CHANNEL" --game-id "$game_id" "$twitch_title"; then
echo " ! Twitch sync failed (manifest still saved) — fix and re-run if needed" >&2
fi
fi
echo
echo " → refresh the Project Loading browser source in OBS."