# OPHI-118 webapp

Laravel app that renders every on-stream overlay for the OPHI-118 Twitch rig and
hosts the streamer's control surfaces. Runs under supervisord on `http://127.0.0.1:1118/`.

> Looking for the rig-wide picture (OBS, MPD, twitch-bot, deployment)?
> See [`../README.md`](../README.md). This file is just the webapp.

## What's in here

### Control panel — `/`

Tile-based dashboard. Entry point from a browser bookmark; not used by OBS itself.

![Control panel](resources/img/screenshots/home.webp)

- **ONBOARD** — opens the project-loading wizard.
- **SCENES** — opens the full route index.
- **ophi118.com** — public channel landing page.
- **gamez.ciemnosc.com** — self-hosted game-server roster.

### Onboard wizard — `/onboard`

Web equivalent of `php artisan rig:loading`. Compiles the Project Loading manifest
(game, subtitle, countdown, camera/mic state) and optionally pushes the channel
title + category to Twitch via Helix.

![Onboard form](resources/img/screenshots/onboard1.webp)

- **Type-ahead game search** against the Twitch Helix `search/categories`
  endpoint, debounced. ↑/↓/Enter/Esc keys to pick a match; locks the `gameId`.
- **Live preview** mirrors what the `/loading` overlay will render — every edit
  re-renders the terminal-style output immediately.
- **Stepper** for the countdown, **ENABLED/DISABLED toggles** for camera/mic,
  **push-to-Twitch** checkbox to commit the channel update via Helix.
- **Graceful fallback**: with `TWITCH_CLIENT_ID` / `TWITCH_ACCESS_TOKEN` unset,
  the search disables and the manifest still saves locally.

![Onboard preview](resources/img/screenshots/onboard2.webp)

### Scenes index — `/scenes`

All overlay URLs the OBS browser-sources point at, plus the data endpoints.

![Scenes](resources/img/screenshots/scenes-page.webp)

### Overlay routes

Browser sources in `basic/scenes/Default_Stream_HUD.json` consume these. Edits
to the Blade views are picked up on the next OBS **Refresh cache** — no server
restart needed.

| Route | Purpose |
|---|---|
| `/landing` | Fallout-style "PLEASE STAND BY" + telemetry rotator |
| `/loading` | Terminal-style Project Loading + countdown |
| `/game` | Game HUD (camera-frame OBS-WS sync, status bar) |
| `/desktop` | Desktop HUD (camera + screen-capture frames) |
| `/goodbye` | Sign-off terminal + "OFFLINE FOR" counter |
| `/music-box` | Full-screen music box terminal |
| `/music-box/widget` | Compact 549×880 widget |
| `/music-box/cover` | Album-art widget (`?bars=0` for compact mode) |
| `/music-box/nc` | ncurses-styled 2-pane variant |
| `/music-daemon` | Passive WebSocket listener (diagnostic) |

### Data + control endpoints

| Route | Notes |
|---|---|
| `GET /cover.jpg` | Streams `bridges/cover.jpg` (kept fresh by `mpd-state.py`) |
| `GET /track?p=<base64>` | Audio stream — path-traversal-guarded |
| `POST /cmd/{skip\|prev\|pause\|resume}` | Broadcasts `mpd:cmd` over OBS WebSocket |

## Artisan commands

Each lives at `app/Console/Commands/Rig*Command.php`. Run from the `webapp/`
directory.

```bash
php artisan rig:setup            # gen public/js/obs-config.js + OBS_WS_* in .env
php artisan rig:telemetry        # refresh storage/data/telemetry.json
php artisan rig:telemetry --collect   # re-prompt every field interactively
php artisan rig:loading          # interactive Project Loading manifest (CLI mirror of /onboard)
php artisan rig:cmd skip         # CLI mirror of POST /cmd/skip
```

## Running it

The supervisord program file lives at [`../scripts/obs-webapp.supervisord.conf`](../scripts/obs-webapp.supervisord.conf)
and is installed (root-owned) to `/etc/supervisor.d/obs-webapp.conf` by
`scripts/deploy-rig.sh`.

```bash
sudo supervisorctl status obs-webapp        # → RUNNING
sudo supervisorctl restart obs-webapp       # after route / controller / .env changes
tail -F storage/logs/supervisord.{out,err}.log
```

For local hacking without supervisord:

```bash
php artisan serve --host=127.0.0.1 --port=1118
```

## When to restart

| Change | Action |
|---|---|
| Blade view (`resources/views/`) | OBS → right-click source → **Refresh cache** |
| Static CSS / JS (`public/`) | Same — Refresh cache |
| Route / controller / service / config / `.env` | `sudo supervisorctl restart obs-webapp` |

## Layout

```
webapp/
├── app/
│   ├── Console/Commands/        rig:setup, rig:telemetry, rig:loading,
│   │                            rig:cmd
│   ├── Http/Controllers/        Overlays/{Landing,Loading,Game,Desktop,
│   │                            Goodbye,MusicBox,MusicDaemon}, Data,
│   │                            MusicCommand, Audio, Onboard
│   ├── Services/                ObsWsClient (Pawl), TwitchHelix,
│   │                            HardwareSnapshot
│   └── Support/RigData.php
├── resources/
│   ├── views/
│   │   ├── home.blade.php       /
│   │   ├── scenes.blade.php     /scenes
│   │   ├── onboard.blade.php    /onboard
│   │   ├── overlays/            per-scene overlays
│   │   ├── partials/            topbar, hud-strip, crt-overlays, …
│   │   └── layouts/             overlay base layout
│   └── img/                     favicon + screenshots (this README)
├── public/
│   ├── css/hud.css              shared HUD chrome, scanlines, terminal styling
│   ├── js/                      hud, crt-static, obs-ws-bootstrap, obs-ws-mini
│   ├── audio/static-hum.wav     ffmpeg_source (not browser-served)
│   └── favicon.ico              → symlink to ../resources/img/favicon.ico
├── routes/web.php
├── config/rig.php               storage paths, OBS WS creds, Twitch keys
└── storage/data/                rig:* commands write JSON here
    ├── loading.json
    └── telemetry.json
```

## Config

`.env` keys the webapp cares about:

```
APP_URL=http://127.0.0.1:1118

OBS_WS_URL=ws://localhost:4455
OBS_WS_PASSWORD=…                # written by `php artisan rig:setup`

TWITCH_CLIENT_ID=…
TWITCH_ACCESS_TOKEN=…            # legacy TWITCH_TOKEN is also accepted
TWITCH_BROADCASTER_ID=…

RIG_NAME=…
OBS_PROFILE=ophi118
MUSIC_DIR_1=…
```

See [`.env.example`](.env.example) for the full set.
