webapp: introduce Laravel app under webapp/ for scene overlays

Replaces the per-scene HTML directories (landing/, loading/, game/,
desktop/, goodbye/, music-box/, music/) with a single Laravel app
serving every overlay over HTTP. Supervisord runs `php artisan serve`
on 127.0.0.1:1118 and the OBS scene JSON now references HTTP routes
instead of file:// URLs.

Highlights:
 - public/css/hud.css consolidates the duplicated HUD chrome,
   scanlines/vignette/flicker, terminal styling, and pulse keyframes
   that were copy-pasted across all seven scenes.
 - Blade partials own hud-strip, crt-overlays, obs-ws-scripts,
   camera-frame, screen-frame; the seven scenes extend a shared
   overlay layout.
 - Artisan commands (`rig:setup`, `rig:telemetry`, `rig:loading`,
   `rig:playlist`, `rig:cmd`) replace the shell scripts that wrote
   per-rig JSON snapshots. TwitchHelix + HardwareSnapshot services
   handle the work the bash + Python helpers used to.
 - ObsWsClient + MusicCommandController kill the 250 ms cmd.js poll
   in the music daemon: POST /cmd/{skip|prev|pause|resume} opens a
   short-lived Pawl WS, authenticates, and broadcasts mpd:cmd.
 - AudioController streams files from the configured music dirs so
   CEF can load tracks under the HTTP origin (Chromium blocks
   HTTP-origin pages from loading file:// media).
 - DataController serves /data/playlist.js (with ETag mtime cache)
   and /cover.jpg (no-store) so the existing overlays' window.__PLAYLIST
   and cover.jpg cache-bust pattern keeps working.

scripts/obs-webapp.supervisord.conf is the supervisord unit; install
to /etc/supervisor.d/obs-webapp.conf.
scripts/rewrite-scene-urls.py is a one-shot tool that rewrites
basic/scenes/Default_Stream_HUD.json from file:// to HTTP URLs.
basic/scenes/Default_Stream_HUD.json.pre-webapp is the rollback
artifact (made with OBS closed; full pre-migration state).

The seven old scene directories, vendor/, and the bash scripts are
still on disk pending visual verification; the next commit will
prune them.
This commit is contained in:
Jakub Zych
2026-05-21 12:47:10 +02:00
parent c8f9c11c93
commit 059f069ef4
85 changed files with 19180 additions and 49 deletions

View File

@@ -0,0 +1,24 @@
@php
/** @var string $id */
$id = $id ?? 'cam';
$label = $label ?? 'CAM 01';
$showPlaceholder = $showPlaceholder ?? true;
@endphp
<div class="camera-frame" id="{{ $id }}">
<span class="tick tl"></span>
<span class="tick tr"></span>
<span class="tick bl"></span>
<span class="tick br"></span>
<span class="label">{{ $label }}</span>
@if ($showPlaceholder)
<div class="placeholder">
<svg width="64" height="48" viewBox="0 0 64 48" xmlns="http://www.w3.org/2000/svg" fill="none">
<rect x="2" y="8" width="44" height="32" rx="2" stroke="#4fd2ff" stroke-width="2"/>
<path d="M46 18 L60 10 L60 38 L46 30 Z" stroke="#4fd2ff" stroke-width="2" stroke-linejoin="round"/>
<circle cx="14" cy="14" r="2" fill="#e63a2e"/>
</svg>
<span class="ph-text"> WAITING FOR CAMERA </span>
<span class="ph-coords">auto-syncs from OBS scene transform</span>
</div>
@endif
</div>

View File

@@ -0,0 +1,4 @@
<canvas id="static"></canvas>
<div class="scanlines"></div>
<div class="vignette"></div>
<div class="flicker"></div>

View File

@@ -0,0 +1,27 @@
@php
/** @var string $variant variant: 'live' | 'onair' | 'offair' */
$variant = $variant ?? 'live';
$label = $label ?? 'TRANSMISSION';
$idText = $idText ?? 'OPHI-118';
$signalGlyph = $signalGlyph ?? '▮▮▮▯▯';
$signalProfile = $signalProfile ?? 'normal';
$extraIdSlot = $extraIdSlot ?? null;
@endphp
<header class="hud hud--{{ $variant }}">
<div class="group">
<span><span class="led"></span>{{ $label }}</span>
@if ($extraIdSlot)
{!! $extraIdSlot !!}
@else
<span>{{ $idText }}</span>
@endif
</div>
<div class="group">
<span class="dim">SIGNAL</span>
<span id="signal" data-profile="{{ $signalProfile }}">{{ $signalGlyph }}</span>
</div>
<div class="group">
<span class="dim">UTC</span>
<span id="clock">--:--:--</span>
</div>
</header>

View File

@@ -0,0 +1,2 @@
<script src="{{ asset('js/obs-config.js') }}"></script>
<script src="{{ asset('js/obs-ws-mini.js') }}"></script>

View File

@@ -0,0 +1,11 @@
@php
$id = $id ?? 'scr';
$label = $label ?? 'SCR 01';
@endphp
<div class="screen-frame" id="{{ $id }}">
<span class="tick tl"></span>
<span class="tick tr"></span>
<span class="tick bl"></span>
<span class="tick br"></span>
<span class="label">{{ $label }}</span>
</div>