README: add human-facing root README
CLAUDE.md is the agent-facing repo guide; this is the same project described for a human walking up to the repo cold. Covers the architecture, the daily-operation commands, the fresh-rig bring-up, the critical gotchas (OBS scene-JSON-on-exit, pipewire-pulse restart, the Flatpak detection trap), and pointers into docs/ and archived/.
This commit is contained in:
181
README.md
Normal file
181
README.md
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
# OPHI-118
|
||||||
|
|
||||||
|
OBS Studio rig for the **ophi118** Twitch channel.
|
||||||
|
|
||||||
|
This repo is `~/.config/obs-studio` for two physical machines, **Ignia** and
|
||||||
|
**Midgolem**. It holds the OBS scene collection, profile, plugin config, the
|
||||||
|
**Laravel webapp** that renders all on-stream overlays, the **MPD-backed audio
|
||||||
|
stack** with its OBS-WebSocket bridge, and the **Twitch chat bot** that drives
|
||||||
|
playback from chat.
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────┐ mpd:state ┌──────────────────────────┐
|
||||||
|
│ MPD │ ──────────────► │ bridges/mpd-state.py │
|
||||||
|
│ (PipeWire │ │ (systemd --user) │
|
||||||
|
│ null-sink) │ ◄────────────── │ │
|
||||||
|
└──────────────┘ mpc next └──────────────┬───────────┘
|
||||||
|
▲ │ OBS WS CustomEvent
|
||||||
|
│ ▼
|
||||||
|
│ ┌──────────────────────┐
|
||||||
|
┌───────┴───────┐ │ obs-websocket :4455 │
|
||||||
|
│ twitch-bot.py │◄────►│ (Studio plugin) │
|
||||||
|
│ (!skip/!info) │ └──────────┬───────────┘
|
||||||
|
└───────────────┘ │ on(mpd:state)
|
||||||
|
▼
|
||||||
|
┌───────────────────────────┐
|
||||||
|
│ webapp/ (Laravel) │
|
||||||
|
│ http://127.0.0.1:1118/ │
|
||||||
|
│ ┌─────────────────────┐ │
|
||||||
|
│ │ /landing /loading │ │
|
||||||
|
│ │ /game /desktop │ │
|
||||||
|
│ │ /goodbye │ │
|
||||||
|
│ │ /music-box[/...] │ │
|
||||||
|
│ │ /music-daemon │ │
|
||||||
|
│ │ POST /cmd/{type} │ │
|
||||||
|
│ └─────────────────────┘ │
|
||||||
|
└───────────────────────────┘
|
||||||
|
▲
|
||||||
|
│ browser_source
|
||||||
|
│
|
||||||
|
┌────────┴────────┐
|
||||||
|
│ OBS Studio │
|
||||||
|
│ (Flatpak) │
|
||||||
|
└─────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Stack
|
||||||
|
|
||||||
|
- **OBS Studio** (Flatpak, `com.obsproject.Studio`) with `filesystems=host`
|
||||||
|
permission so `~/.config/obs-studio/` is read directly, not indirected
|
||||||
|
through the per-app private dir.
|
||||||
|
- **Laravel 13** webapp under `webapp/`, served by `php artisan serve` on
|
||||||
|
`127.0.0.1:1118` and managed by **supervisord**. Routes return Blade-rendered
|
||||||
|
overlay pages and a small data API (`/data/playlist.js`, `/cover.jpg`,
|
||||||
|
`/track?p=…`, `POST /cmd/{skip|prev|pause|resume}`).
|
||||||
|
- **MPD** (system service) is the audio player. A small Python bridge
|
||||||
|
(`bridges/mpd-state.py`, `obs-mpd-bridge.service`) listens to MPD and
|
||||||
|
re-broadcasts state as `mpd:state` CustomEvents on the OBS WS bus.
|
||||||
|
- **Twitch chat bot** (`twitch-bot/`, `obs-twitch-bot.service`) handles
|
||||||
|
`!skip` / `!queue` / `!info` by talking to MPD directly.
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
.
|
||||||
|
├── basic/ OBS scene collection + profile
|
||||||
|
├── plugin_config/ obs-websocket password (gitignored)
|
||||||
|
├── plugin_manager/ third-party plugin manifest
|
||||||
|
├── bridges/ MPD → OBS WS state bridge + cover.jpg cache
|
||||||
|
├── twitch-bot/ chat bot (git submodule)
|
||||||
|
├── station/ public rig-spec page (git submodule)
|
||||||
|
├── webapp/ Laravel app — every overlay served from here
|
||||||
|
│ ├── app/
|
||||||
|
│ │ ├── Console/Commands/ rig:setup, rig:telemetry, rig:loading,
|
||||||
|
│ │ │ rig:playlist, rig:cmd
|
||||||
|
│ │ ├── Http/Controllers/ Overlays/{Landing,Loading,Game,…}, Data,
|
||||||
|
│ │ │ MusicCommand, Audio
|
||||||
|
│ │ ├── Services/ ObsWsClient, TwitchHelix, HardwareSnapshot
|
||||||
|
│ │ └── Support/RigData.php
|
||||||
|
│ ├── resources/views/ Blade layouts + partials + per-scene overlays
|
||||||
|
│ ├── public/ css/hud.css, js/{hud,crt-static,obs-ws-*}.js,
|
||||||
|
│ │ audio/static-hum.wav
|
||||||
|
│ └── storage/data/ rig:* commands write JSON here
|
||||||
|
├── scripts/
|
||||||
|
│ ├── deploy-rig.sh idempotent bring-up (run on fresh rig)
|
||||||
|
│ ├── obs-webapp.supervisord.conf → /etc/supervisor.d/obs-webapp.conf
|
||||||
|
│ ├── rewrite-scene-urls.py one-shot migration helper
|
||||||
|
│ ├── convert.sh, clean.sh yt-dlp .webm → .m4a re-encode tools
|
||||||
|
├── archived/ pre-webapp artifacts (rollback only)
|
||||||
|
├── docs/ deeper context — audio, scenes, deployment, history
|
||||||
|
├── CLAUDE.md agent-facing repo guide (orientation for AI tools)
|
||||||
|
└── README.md you are here
|
||||||
|
```
|
||||||
|
|
||||||
|
## Daily operation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# server health
|
||||||
|
sudo supervisorctl status obs-webapp
|
||||||
|
tail -F webapp/storage/logs/supervisord.{out,err}.log
|
||||||
|
|
||||||
|
# restart after route / controller / .env changes
|
||||||
|
sudo supervisorctl restart obs-webapp
|
||||||
|
|
||||||
|
# blade-view-only changes: no restart needed — just right-click the source
|
||||||
|
# in OBS → Refresh cache
|
||||||
|
|
||||||
|
# refresh a per-session manifest before going live (game / mode / countdown)
|
||||||
|
cd webapp && php artisan rig:loading
|
||||||
|
|
||||||
|
# reindex the music library (after dropping new tracks into ~/HDD/Music)
|
||||||
|
cd webapp && php artisan rig:playlist
|
||||||
|
|
||||||
|
# re-snapshot hardware/OBS profile telemetry (kernel/GPU/encoder changes)
|
||||||
|
cd webapp && php artisan rig:telemetry --collect
|
||||||
|
|
||||||
|
# CLI control of music playback (mirrors the Twitch chat commands)
|
||||||
|
cd webapp && php artisan rig:cmd skip
|
||||||
|
# or
|
||||||
|
curl -X POST http://127.0.0.1:1118/cmd/skip
|
||||||
|
|
||||||
|
# rotate the obs-websocket password (or after a fresh rig install)
|
||||||
|
cd webapp && php artisan rig:setup
|
||||||
|
sudo supervisorctl restart obs-webapp
|
||||||
|
```
|
||||||
|
|
||||||
|
The OBS scene browser sources point at `http://127.0.0.1:1118/<route>`, so
|
||||||
|
they always pick up the current state of the webapp on next **Refresh cache**.
|
||||||
|
|
||||||
|
## Fresh rig (Midgolem)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone --recurse-submodules <repo> ~/.config/obs-studio
|
||||||
|
cd ~/.config/obs-studio
|
||||||
|
bash scripts/deploy-rig.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
`deploy-rig.sh` is idempotent. It installs the Arch packages it needs
|
||||||
|
(`pacman -S`, AUR helper for the Sansation font), seeds PipeWire null sinks
|
||||||
|
for the MPD and Discord audio paths, writes the systemd user units for the
|
||||||
|
bridge + bot, installs the supervisord program, runs `composer install` for
|
||||||
|
the webapp, and calls `php artisan rig:setup` / `rig:telemetry --collect` to
|
||||||
|
materialize per-rig state. Then fill in the Twitch tokens it leaves you
|
||||||
|
prompts for and you're done.
|
||||||
|
|
||||||
|
Run it again any time after a `git pull` — it'll only act on what's drifted.
|
||||||
|
|
||||||
|
## Critical gotchas
|
||||||
|
|
||||||
|
- **Close OBS before editing `basic/scenes/*.json`, `global.ini`, or
|
||||||
|
`user.ini`.** OBS rewrites them on exit and will clobber your edits.
|
||||||
|
- **Never restart `pipewire-pulse.service` while OBS is running.** OBS does
|
||||||
|
not auto-rebind `pulse_output_capture` sources when sinks reload; meters
|
||||||
|
go flat and stream audio cuts. Stop OBS first, or load modules ad-hoc
|
||||||
|
with `pactl`.
|
||||||
|
- **OBS is the Flatpak** (`com.obsproject.Studio`), not the pacman `obs`
|
||||||
|
binary. Detect with `flatpak ps --columns=application | grep -qx
|
||||||
|
com.obsproject.Studio` — `pgrep -fa 'flatpak run …'` lies (the wrapper
|
||||||
|
exits, the sandbox keeps running).
|
||||||
|
- **Secrets are per-rig**, never copy between rigs: `twitch-bot/.env*`,
|
||||||
|
`plugin_config/obs-websocket/config.json`, `basic/profiles/*/service.json`,
|
||||||
|
`webapp/.env`. Everything else is portable.
|
||||||
|
|
||||||
|
## Deeper reading
|
||||||
|
|
||||||
|
- [docs/audio.md](docs/audio.md) — PipeWire null-sink architecture, MPD config,
|
||||||
|
the `mpd:state` event payload, systemd user units.
|
||||||
|
- [docs/scenes-and-overlays.md](docs/scenes-and-overlays.md) — scene JSON
|
||||||
|
internals, source ids, HUD browser-resolution constraints,
|
||||||
|
camera-frame OBS WS sync.
|
||||||
|
- [docs/deployment.md](docs/deployment.md) — Twitch identity + token scopes,
|
||||||
|
per-rig vs portable state, Flatpak permission story.
|
||||||
|
- [docs/history.md](docs/history.md) — migration history: linuxbrowser → CEF,
|
||||||
|
browser-daemon → MPD bridge, Discord audio split, file:// → HTTP webapp.
|
||||||
|
- [docs/cheatsheet.md](docs/cheatsheet.md) — one-liners.
|
||||||
|
- [archived/README.md](archived/README.md) — pre-webapp scene HTML / bash
|
||||||
|
helpers, kept around for rollback.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Personal config; nothing here is a published product. The Blade templates,
|
||||||
|
CSS, and scripts are MIT-style — copy what's useful.
|
||||||
Reference in New Issue
Block a user