Timers: LTC is listener-only; admin switch, device/channel, grouped settings

Simplify LTC to its one working source — the built-in audio listener — and
make the admin UI reflect it.

Module:
- Remove the stage-display websocket source (stage-ws.js, stagePassword)
  and the /v1/timecode/status API probe — both dead on ProPresenter 21.4.
- Remove the remote-reader ingest (POST /ltc, ingestToken, tools/
  ltc-reader.js): the module owns LTC end to end now. The decoder selftest
  moves to modules/propresenter-timers/ltc.selftest.js so the engine stays
  covered.
- composeLtc collapses to the listener: running/stopped/nosignal, or a
  clear note when disabled / no device / capture failed / starting.
- Manifest: ltcEnabled is now a `switch`; ltcDevice and ltcChannel carry
  showWhen:"ltcEnabled"; ProPresenter and LTC fields carry `group` labels.

Shell (admin) — backward-compatible additions, coordinate with the shell lane:
- New `switch` field type (boolean shown as the enable-toggle control).
- `group`: consecutive fields render under a labeled subsection.
- `showWhen: "<key>"`: a field is shown only while that boolean/switch is
  on (and actually collapses — .field[hidden] override, since .field's
  display:flex outranks the [hidden] UA default).
- applyConfigPatch coerces `switch` as boolean. Documented in MODULE-GUIDE.

Verified: engine selftest (29 checks); listener decodes BlackHole loopback
(running 30fps, ageMs); device picker lists inputs; POST /ltc now 404s;
admin shows two labeled groups, the switch, and device/channel appearing
only when the switch is on (computed display none↔flex confirmed).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Waters Church
2026-09-10 13:29:06 -04:00
co-authored by Claude Opus 4.8
parent 7e75146838
commit 1d46657e06
14 changed files with 386 additions and 1119 deletions
+15 -24
View File
@@ -175,31 +175,22 @@ below. Nothing is installed without someone clicking.
elapsed) and LTC timecode as auto-sized cards. The + picker lists every
discovered timer (and the LTC card) as its own tile, or add "All timers"
and pick a selection via the tile's "Timers ▾" menu; overruns are
unmistakably red. Read-only — no start/stop controls. Admin config:
ProPresenter endpoint (host : port), API password, the LTC listener
switch + audio device + channel, LTC reader token, stage display
password.
unmistakably red. Read-only — no start/stop controls. Admin config is in
two groups: **ProPresenter** (endpoint host : port, API password) and
**LTC Timecode** (the listener switch, and — when it's on — the audio
device and channel).
LTC lives behind the admin **LTC listener** switch — off means no LTC
tile in the picker at all. On, the sources, best first (ProPresenter's
HTTP API has no timecode route):
1. **Built-in listener** — the module captures the admin-selected audio
input device/channel on the ProdDash machine (native `ltc-capture`
helper, compiled on first use) and decodes the SMPTE frames
in-process: running/stopped/no-signal, timecode, frame rate (incl.
29.97 drop-frame). macOS note: the ProdDash server needs microphone
permission — launch it from a logged-in session (Start
ProdDash.command) and approve the one-time prompt; a server started
over SSH or by bare launchd silently captures zeros.
2. **Remote reader** — when a *different* machine hears the LTC, run
`tools/ltc-reader.js` there; it decodes the same way from a PCM pipe
(`ltc-capture`, ffmpeg, or sox; `--channels 18 --ch 5` picks one
input of a multichannel interface — never downmix LTC into program
audio) and POSTs to the module's `/ltc` route. `--selftest` checks
the decoder; `--demo` feeds synthetic LTC end-to-end with no audio
hardware.
3. A stage-display layout field labeled "LTC", read over ProPresenter's
stage websocket — value and freshness only, no frame rate.
ProPresenter exposes no timecode over any API, so LTC is decoded from the
audio signal, behind the admin **LTC listener** switch (off ⇒ no LTC tile
in the picker at all). On, the module captures the selected audio input
device/channel on the ProdDash machine — a native `ltc-capture` helper,
compiled on first use — and decodes the SMPTE frames in-process:
running/stopped/no-signal, the timecode counting frame-accurately in real
time, and the frame rate (incl. 29.97 drop-frame). macOS note: the
ProdDash server needs microphone permission — launch it from a logged-in
session (Start ProdDash.command) and approve the one-time prompt; a server
started over SSH or by bare launchd silently captures zeros. (Engine
self-test: `node modules/propresenter-timers/ltc.selftest.js`.)
- **Clock** — a big booth clock; also the smallest possible module and the
reference for module authors.