Timers: built-in LTC listener, configured in the admin page

The LTC listener now lives inside the module: it spawns the native
ltc-capture helper (moved into the module folder, compiled on first use,
exact-name device matching) for the admin-selected input device, extracts
the configured channel, and decodes SMPTE LTC in-process (ltc.js — the
engine, shared with tools/ltc-reader.js, which remains the remote-machine
option posting to /ltc).

Admin config: an "LTC listener" switch, an audio-device select populated
live from the new GET /ltc/devices route, and a 1-based channel number.
The switch off means LTC is gone entirely: no LTC entry in the Add-tile
picker, no LTC row in the Timers menu or All-timers grid, no stage/API
probes, and POST /ltc answers 409. Source precedence with it on:
listener-with-data, fresh remote reader, listener's own no-signal verdict,
then the legacy API/stage chain.

Shell (admin page): `select` fields learn `optionsRoute` — options fetched
from a module route at render, saved value kept selectable when the route
or device is gone. Documented in MODULE-GUIDE; backward compatible.
Coordinate with the shell lane before its next admin.js change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Waters Church
2026-09-08 17:21:48 -04:00
co-authored by Claude Fable 5
parent c4db412014
commit b95aa7dc34
11 changed files with 770 additions and 404 deletions
+22 -6
View File
@@ -1,7 +1,7 @@
{
"id": "propresenter-timers",
"name": "Timers",
"version": "1.1.0",
"version": "1.2.0",
"description": "Live ProPresenter timers and LTC timecode",
"client": "client.js",
"server": "server.js",
@@ -19,14 +19,30 @@
"label": "API password (only if set in ProPresenter)",
"default": ""
},
"stagePassword": {
"type": "password",
"label": "Stage display password (for LTC — ProPresenter Settings → Network)",
"default": ""
"ltcEnabled": {
"type": "boolean",
"label": "LTC listener — decode timecode from an audio input on this machine",
"default": false
},
"ltcDevice": {
"type": "select",
"label": "LTC audio device",
"default": "",
"optionsRoute": "/ltc/devices"
},
"ltcChannel": {
"type": "number",
"label": "LTC channel on that device (1-based)",
"default": 1
},
"ingestToken": {
"type": "password",
"label": "LTC reader token (optional — must match tools/ltc-reader.js --token)",
"label": "LTC reader token (optional — for tools/ltc-reader.js on another machine)",
"default": ""
},
"stagePassword": {
"type": "password",
"label": "Stage display password (LTC fallback via a stage field labeled \"LTC\")",
"default": ""
}
}