The Electron app that runs on the backstage PC: stage lineup photos with mic/pack labels, packaged for Windows. It also carries the ProPresenter Now/Next feature in two forms that share one engine — propresenter-core, where every ProPresenter 7/20 quirk lives, and propresenter-app, the standalone display it serves. Until now this existed only on the production machine and as a read-only snapshot inside the ProdDash repo. Source only: the 231 MB dist/ is build output, and credentials, photos and processed images are written to the Electron userData directory, never here. tmp/pp-mock.js stays — it is the ProPresenter mock ProdDash's own CLAUDE.md tells people to run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
234 lines
11 KiB
Markdown
234 lines
11 KiB
Markdown
# Stage Now / Next
|
|
|
|
The ProPresenter **Now / Next** display, as its own program — the same feature that
|
|
lives in the Band Lineup app's right-hand column, but on its own so it can run on
|
|
studio machines, hallway TVs, green-room tablets, anywhere in the building.
|
|
|
|
Both programs share one engine: [`../propresenter-core`](../propresenter-core/index.js).
|
|
Fix a ProPresenter quirk there and both get the fix.
|
|
|
|
---
|
|
|
|
## The easiest way: let the Band Lineup app host it
|
|
|
|
If the backstage machine already runs **Band Lineup**, you don't need to start anything here.
|
|
Open the app, and in the **People** panel find **Studio Displays** → **Turn On**.
|
|
|
|
It then lists the addresses to use, best first, each with a **Copy** button. Open one on any
|
|
screen in the building and bookmark it. Nothing to install on those screens.
|
|
|
|
Because the app hands the display its own ProPresenter connection, there is exactly one
|
|
connection to ProPresenter no matter how many screens are watching, and the studios always
|
|
show precisely what the backstage screen shows. The connection is still set up in one place — the
|
|
app's ProPresenter section — and the studio screens' own settings are locked, with a note
|
|
saying so.
|
|
|
|
The displays are live whenever Band Lineup is open. Close the app and every screen says
|
|
**Display offline** and names the backstage computer, rather than quietly showing a frozen
|
|
lineup. They come back on their own when the app reopens — no reloading needed.
|
|
|
|
### The bookmark that keeps working
|
|
|
|
The first address in the list is `http://nownext.local:7654`. The app publishes that name
|
|
itself over mDNS, so the bookmark survives the backstage machine getting a new IP from DHCP.
|
|
It resolves on Macs, iPads and Windows 10/11.
|
|
|
|
Do **not** bookmark the computer's own name (`http://backstage-pc.local:7654`) — Windows can
|
|
*resolve* mDNS names but never *publishes* its own, so Macs and iPads get nothing back.
|
|
|
|
If `nownext.local` doesn't work on a particular screen (some managed networks and separate
|
|
VLANs block mDNS), use one of the numeric addresses below it. Once any screen has loaded
|
|
the page from an address, that address is relabelled **Confirmed working** and moves to the
|
|
top — so after the first studio connects, the app knows the right answer for your building.
|
|
|
|
For a permanently fixed address, ask IT for a DHCP reservation on the backstage machine, and
|
|
optionally a DNS name pointing at it. One caveat if you go the DNS route: the zone must not
|
|
end in `.local`, or Apple devices will route the lookup to mDNS and it will fail.
|
|
|
|
### If a studio screen can't load the page (Windows)
|
|
|
|
Almost always Windows Defender Firewall. Two things make this confusing:
|
|
|
|
- The prompt appears when the app **starts serving**, not when a studio first connects — so
|
|
the backstage machine looks perfectly fine on `localhost` while every other screen times out.
|
|
- If whoever answered the prompt wasn't a local admin, Windows creates **block** rules no
|
|
matter which button they pressed, and it never asks again.
|
|
|
|
The installer adds the allow rules for you, so a normal install is fine. If you're running
|
|
an unpacked build or the rules got lost, an admin can restore them:
|
|
|
|
```bash
|
|
netsh advfirewall firewall add rule name="Band Lineup Display (TCP)" dir=in action=allow program="C:\Program Files\Band Lineup\Band Lineup.exe" enable=yes profile=domain,private protocol=TCP
|
|
```
|
|
|
|
The app helps you spot this: if hosting is on and no screen has ever connected, the Studio
|
|
Displays status line says so and names the port to allow.
|
|
|
|
Other things that break a working setup: the backstage PC sleeping (turn off sleep on a machine
|
|
that hosts displays), and Windows flipping the network profile to *Public* — the allow rules
|
|
above only cover Domain and Private.
|
|
|
|
### Keep the studio screens awake
|
|
|
|
The page asks the browser for a screen wake lock, but browsers only grant that on a secure
|
|
(HTTPS) origin, and this is served over plain HTTP on the LAN — so the request is always
|
|
refused. Turn off screen sleep in the OS settings on any machine or tablet you leave running
|
|
as a dedicated display; the web page cannot do it for you.
|
|
|
|
---
|
|
|
|
## Security: what's protected, and what isn't
|
|
|
|
Be clear-eyed about this one, because the honest answer is unusual.
|
|
|
|
**Viewing is deliberately open.** Anyone who can reach the port can watch the page. There is no
|
|
password, and adding one was considered and rejected: a wall TV has to come back by itself
|
|
after a power cut with nobody standing there, and a login prompt, a key in the URL, or a
|
|
certificate warning all break exactly that. A key in the URL would also live forever in every
|
|
bookmark and in the address list volunteers copy into group chats.
|
|
|
|
**More importantly, gating this page would not raise the bar much.** ProPresenter's own network
|
|
API on the same LAN has no authentication at all, and unlike this page it is not read-only.
|
|
Anyone who can reach this display can already read the same service order straight from
|
|
ProPresenter — and put whatever they like on the main screen. **Network placement is the control
|
|
that actually covers both.**
|
|
|
|
### What is protected
|
|
|
|
- **Nothing can be changed over the network** while the Band Lineup app is hosting. The
|
|
connection routes return `409`, so a browser on the LAN cannot retarget or disconnect the
|
|
display. Standalone additionally requires `application/json`, a same-origin request, and a
|
|
recognised `Host` — which blocks the JavaScript-free form CSRF and DNS-rebinding writes that
|
|
are otherwise possible against a local HTTP server.
|
|
- **Hidden items stay hidden.** Anything flagged hidden in ProPresenter — cut songs, contingency
|
|
cues, staff-only slides — has its name stripped before it leaves the machine, not merely
|
|
skipped when drawing the page.
|
|
- **The playlist library is not exposed.** `/api/playlists` is refused while the app is hosting.
|
|
- **Connection limits.** Concurrent viewers are capped (32 total, 6 per device) so nobody can pile
|
|
up connections inside the app, and a screen that stops reading is dropped rather than buffered
|
|
forever. Established screens are never dropped to make room for a new one.
|
|
- **The mDNS name can't be stolen with one packet.** A claim on `nownext.local` is verified by
|
|
probing before this host stands aside, and it reclaims the name when the other host leaves.
|
|
|
|
### What is exposed
|
|
|
|
Song and sermon titles, slide numbers, and the upcoming service order — in the clear, over plain
|
|
HTTP, to anything that can reach the port. Also the ProPresenter machine's address and port, and
|
|
the host's LAN addresses. Decide whether an unannounced sermon title or a baptism in the order
|
|
matters to you before a service; if it does, the answer is the VLAN below, not a password.
|
|
|
|
### Where this should live on the network
|
|
|
|
- Backstage PC, ProPresenter, and the display screens on a **trusted A/V or management VLAN**.
|
|
- **Guest Wi-Fi kept off that VLAN.** This is the single highest-value control here.
|
|
- A **DHCP reservation** for the backstage machine — which you want anyway so bookmarks survive.
|
|
- If you ever set a ProPresenter network password, treat it as a LAN-visible string: it travels
|
|
in the query string of ProPresenter's own API on every poll, because that is the interface
|
|
ProPresenter offers.
|
|
|
|
---
|
|
|
|
## Run it standalone
|
|
|
|
For a machine that doesn't run Band Lineup — or to host the display without it — run the
|
|
server on its own. Every other screen just opens a web page.
|
|
|
|
```bash
|
|
cd propresenter-app
|
|
node server.js
|
|
```
|
|
|
|
Then open `http://localhost:7654` on that machine, press the **gear** (bottom-right,
|
|
appears when you move the mouse or tap), and enter the IP and port of the computer
|
|
running ProPresenter. That's it — the connection is stored on the server, so every
|
|
other screen that opens the page is already following along.
|
|
|
|
The startup log prints the addresses other screens should use, e.g.
|
|
`http://10.1.1.42:7654`. They're also listed in the settings panel under
|
|
**Other screens**.
|
|
|
|
Requirements: Node 18 or newer. No `npm install`, no dependencies.
|
|
|
|
### ProPresenter side
|
|
|
|
Turn the network API on in **ProPresenter ▸ Preferences ▸ Network**. Note the port
|
|
(1025 by default) and set a password there if you want one.
|
|
|
|
## Run it as an app window
|
|
|
|
For the machine that should *host* the display, there's an Electron wrapper that
|
|
starts the same server and shows the page in a window:
|
|
|
|
```bash
|
|
npm run kiosk
|
|
```
|
|
|
|
```bash
|
|
npm run kiosk -- --kiosk
|
|
```
|
|
|
|
The second form is true kiosk: no window chrome, fullscreen, for a dedicated screen.
|
|
Either way the server still serves the rest of the building.
|
|
|
|
## Options
|
|
|
|
| What | How |
|
|
| --- | --- |
|
|
| Different port | `PORT=9000 node server.js` |
|
|
| Lock the settings so viewers can't change the connection | `PP_DISPLAY_READONLY=1 node server.js` |
|
|
| Move where `config.json` lives (a NAS, a shared path) | `PP_DISPLAY_DATA_DIR=/path/to/dir node server.js` |
|
|
|
|
Per-screen looks — text size (Small → Huge), whether to show the playlist name, the
|
|
progress bar, and whether to tidy titles — are set in the gear panel and saved on
|
|
**that device only**, so a 65" TV and an iPad can each be sized for their own room.
|
|
|
|
Keyboard: `f` toggles fullscreen, `Esc` closes the settings panel.
|
|
|
|
## Running it as a service
|
|
|
|
Any process manager works since it's a plain Node script. For example, with `pm2`:
|
|
|
|
```bash
|
|
pm2 start server.js --name stage-now-next -- --port 7654
|
|
```
|
|
|
|
## What's in here
|
|
|
|
| File | Job |
|
|
| --- | --- |
|
|
| `server.js` | Serves the UI and pushes the live view-model to every browser over SSE. Standalone it polls ProPresenter itself; embedded it mirrors the client the Band Lineup app is already polling with |
|
|
| `config.js` | Reads/writes `config.json` (password obfuscated at rest — treat the file as a secret), and ranks which LAN address to bookmark |
|
|
| `mdns.js` | Publishes `nownext.local` so a bookmark survives a DHCP change. Zero dependencies |
|
|
| `public/index.html`, `public/app.js`, `public/styles.css` | The display itself and the settings panel |
|
|
| `main.js` | Optional Electron kiosk wrapper |
|
|
| `../propresenter-core/index.js` | The shared ProPresenter client — also used by the Band Lineup app |
|
|
|
|
## API
|
|
|
|
Anything on the LAN can read the live state, which makes it easy to hang other
|
|
displays or automations off the same feed:
|
|
|
|
| Endpoint | Returns |
|
|
| --- | --- |
|
|
| `GET /api/state` | `{ state, status }` — one JSON snapshot |
|
|
| `GET /api/stream` | Server-Sent Events: `state` on every poll, `status` on connection changes |
|
|
| `GET /api/status` | Connection info only |
|
|
| `GET /api/playlists` | Every playlist ProPresenter knows about |
|
|
| `GET /api/health` | `{ ok, viewers, reachable }` |
|
|
| `POST /api/connect` | `{ host, port, password? }` — tests, saves, and starts following |
|
|
| `POST /api/disconnect` | Stops following; keeps host/port for next time |
|
|
|
|
The two `POST` routes return `403` when `PP_DISPLAY_READONLY=1`, and `409` when the Band
|
|
Lineup app is hosting — a browser on the LAN must never be able to retarget or disconnect
|
|
the backstage app's own ProPresenter connection.
|
|
|
|
## Notes
|
|
|
|
- **The display auto-follows whatever ProPresenter is presenting** — the live playlist
|
|
and item come from ProPresenter itself, so there's nothing to pick each week.
|
|
- **Slide totals respect arrangements.** If an arrangement repeats a chorus, the total
|
|
counts the repeat, so "Slide 41 / 43" always matches what the operator sees.
|
|
- **Building a Windows installer** (`npm run dist`) needs `npm install` in this folder
|
|
first, and packages `../propresenter-core` alongside — keep the two folders siblings.
|
|
The everyday `node server.js` path needs neither step.
|