init: initial commit

- ticker / QR ad overlay / break slide graphics (1920×1080) with all
  geometry derived from one shared module (src/shared/obs-geometry.mjs)
- OBS scene-collection generator (scripts/generate-obs-scenes.mjs) and a
  read-only in-dashboard setup verifier, both driven by the same module
- en/sr dashboard i18n selected via bundle config; configschema.json with
  neutral first-run defaults seeded from optional branding config
- install.sh / systemd user service / update.sh for venue deployment
- reference OBS scene collection + profile from a real venue as fixtures
This commit is contained in:
2026-07-15 17:43:05 +02:00
commit 4bb64a8ca3
71 changed files with 18186 additions and 0 deletions
+152
View File
@@ -0,0 +1,152 @@
# nodecg-venue-gfx
Broadcast graphics for a venue screen, as a [NodeCG](https://nodecg.dev) 2
bundle: an always-on scrolling ticker with operator-added TTL messages,
corner-alternating ads that shrink the live program into a corner, a break
announcement slide, and automated + manual OBS scene choreography over
obs-websocket. Built for festivals and concerts; generic enough for any
single-screen show.
- All on-screen text is edited live from the dashboard
- Support for multiple locales
- OBS setup is generated. One script emits an importable scene collection, and a
dashboard panel verifies a live OBS against the exact geometry the graphics
render from.
- If OBS dies mid-show, the scheduler keeps running and re-applies the right
scene the moment OBS comes back. If NodeCG dies, restart it and it comes back
in live mode with all content intact.
## Quick start
**Fresh venue machine** (installs NodeCG + this bundle + config + OBS scene
collection in one go):
```sh
curl -fsSLO https://git.dvdrw.dev/kckljajicevo/nodecg-venue-gfx/raw/branch/master/scripts/install.sh
bash install.sh
```
**Into an existing NodeCG install:**
```sh
cd <nodecg>/bundles
git clone https://git.dvdrw.dev/kckljajicevo/nodecg-venue-gfx.git
cd nodecg-venue-gfx
npm ci && npm run build
```
Then:
1. Write `cfg/nodecg-venue-gfx.json` (see
[docs/examples/cfg.example.json](docs/examples/cfg.example.json)).
2. Set up OBS: `npm run obs:scenes` -> import -> done. Details in
[docs/OBS-SETUP.md](docs/OBS-SETUP.md).
3. Start NodeCG (`node index.js` from the install root) and open the
dashboard at <http://localhost:9090>.
To run as a service that survives reboots: `scripts/install-service.sh`
(systemd user unit). To update later: `scripts/update.sh`.
## Configuration
`cfg/nodecg-venue-gfx.json` in the NodeCG install, validated against
[configschema.json](configschema.json). Everything is optional:
| Key | Default | Purpose |
| --- | --- | --- |
| `locale` | `"en"` | Dashboard language (`"en"` or `"sr"`) |
| `branding.tickerBlurb` | `"Welcome ♪"` | First-run seed for the recurring ticker blurb |
| `branding.breakHeading` | `"Stay tuned — there's more!"` | First-run seed for the break-slide heading |
| `branding.sampleQrUrl` | `"https://example.com"` | First-run seed for the sample ad's QR URL |
| `obs.enabled` | `false` | Connect to OBS and drive scenes |
| `obs.url` | `"ws://127.0.0.1:4455"` | obs-websocket URL |
| `obs.password` | `""` | obs-websocket password |
| `obs.scenes.*` | `MAIN`, `MAIN_AD_TR`, … | Names of the six driven scenes |
| `obs.transitions.*` | `Move`, `Stinger` | Names of the two transitions |
| `obs.sources.*` | `TickerOverlay`, … | Names of the shared sources/nested scenes (for the generator + verifier) |
`branding.*` values only seed the very first run — after that, everything is
edited from the dashboard and persists in NodeCG's database. Set
`obs.enabled: false` to develop without OBS; everything else keeps working.
## Dashboard panels
- **Ticker messages** — ticker settings (loop blurb, separator, default TTL,
speed) and message management; each active message shows a depleting fill
for its remaining time, with renew/remove/inline-edit.
- **Ads** — CRUD for ads (strip text, column text, column image from the
Assets dialog, QR URL, enabled), with a live QR preview per row.
- **Choreography / OBS** — connection status, mode/phase badges, countdown to
the next automatic transition, timing configuration, break-slide content,
and manual controls: show ad now, skip ad, break toggle.
- **OBS setup check** — read-only verification of the live OBS against the
expected scenes/sources/transforms/transitions, with concrete fixes.
Images (ad backgrounds, column images, break background, act logos) are
uploaded through NodeCG's standard **Assets** dialog.
## Graphics (1920×1080)
| URL | Default OBS source | Purpose |
| --- | --- | --- |
| `graphics/ticker.html` | `TickerOverlay` | Bottom 96 px scrolling ticker |
| `graphics/ad.html` | `AdOverlay` | QR + headline in the gamma-shaped cutout |
| `graphics/break.html` | `BreakSlide` | Break announcement slide (ambient motion) |
The ticker uses a segment-queue marquee: loop content is snapshotted per
revolution, so adding/removing messages mid-scroll never causes visual jumps.
## Theming
- **Colors**: edit the `THEME LAB` block in
[`src/graphics/index.css`](src/graphics/index.css) — every brand color the
graphics use is a token there.
- **Fonts**: the `FONT LAB` block right above it (Margarine is bundled as the
default; drop a `.ttf`/`.woff2` into `src/graphics/fonts/` to swap).
- **Geometry** (canvas size, the 70% shrink, ad cutout, OBS transforms):
[`src/shared/obs-geometry.mjs`](src/shared/obs-geometry.mjs) — one module
shared by the graphics, the scene-collection generator, and the verify
panel, so they can never disagree. v1 is fixed at 1920×1080.
## Adding a language
1. Copy `src/i18n/en.ts` to `src/i18n/<code>.ts` and translate (the `Strings`
type keeps you honest).
2. Register it in `src/i18n/index.ts` (`locales`).
3. Add the code to the `locale` enum in `configschema.json`.
## Development
```sh
npm run dev # parcel watch + nodemon restarting NodeCG
npm run build # one-shot production build
npm run obs:scenes # regenerate the OBS scene collection
```
Dev mode assumes the bundle lives inside a NodeCG install
(`bundles/nodecg-venue-gfx`); `nodemon` runs `node ../../index.js`.
Rules of thumb:
- **Graphics/dashboard** changes: rebuild (or let the watcher do it), then
refresh the page / OBS browser source. No server restart.
- **Extension** (`src/extension/`), **types**, or **package.json** changes:
rebuild **and restart NodeCG** — plain `node index.js` never rebuilds
anything.
- Graphics are fixed 1920×1080 canvases — judge them in OBS or a fullscreen
1080p tab; smaller windows show a crop.
## Architecture notes
- The scheduler is a state machine over `(mode: normal|break, phase: live|ad)`
with exactly one pending timer; OBS calls are fire-and-forget, so an
unreachable OBS never stalls the choreography.
- Corner alternation and ad round-robin persist across restarts
(`schedulerState` replicant). On startup the bundle always returns to
(normal, live) + the main scene.
- Replicant defaults are only applied when nothing is persisted, so operator
edits survive restarts.
## License
[AGPL-3.0](LICENSE)