From b47f8fca83983b451e81f7528e349e3498c87d6b Mon Sep 17 00:00:00 2001 From: Jakub Zych Date: Thu, 21 May 2026 13:32:25 +0200 Subject: [PATCH] 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/. --- README.md | 181 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..fc79206 --- /dev/null +++ b/README.md @@ -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/`, so +they always pick up the current state of the webapp on next **Refresh cache**. + +## Fresh rig (Midgolem) + +```bash +git clone --recurse-submodules ~/.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.