LTC usually rides one input of a console bus or Dante/MADI interface, and
downmixing it into program audio (-ac 1) corrupts the bit transitions. New
flags --channels <n> (interleaved count of the stdin stream, no practical
upper bound) and --ch <n> (1-based pick) deinterleave in the reader, so
the capture command stays a plain passthrough. The extractor keeps frame
alignment across arbitrary chunk splits; selftest adds an LTC-on-5-of-8
case fed in odd-sized chunks. Mono remains the default.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ProPresenter 21.4 exposes no timecode through any API, so read the LTC
where it actually lives — the audio feed:
- tools/ltc-reader.js: zero-dependency SMPTE LTC decoder (biphase-mark,
adaptive bit clock, envelope-scaled comparator) fed s16le PCM on stdin
from ffmpeg/sox; POSTs running/stopped/nosignal + timecode + frame rate
(incl. 29.97 drop-frame) to the module. --selftest runs an encoder→
decoder suite (24/25/30 fps, DF minute counting, low level + noise + DC,
dropout recovery); --demo synthesizes live LTC through the real decoder
and ingest path with no audio hardware.
- Module: POST /ltc ingest route (optional bearer token, validated body,
6 s staleness with automatic fallback to the API probe / stage source),
richer LTC state (status, fps, df, reader source), health messages.
- Client: LTC card shows Running · <rate> / Stopped (held frame) /
No signal; works LTC-only with no ProPresenter host configured; the
reader-fed card no longer grays out when only ProPresenter is down.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Verified against ProPresenter 21.4: the legacy stage-display protocol
(ptl 610 — the only version PP21 accepts; newer values get 'out of
date') omits stage objects it predates, so a timecode field labeled LTC
never appears in any layout's frame list, and no timecode messages were
observed while idle. Fallback: an unknown uid streaming timecode-shaped
text (a fourth :FF group; not any configured timer's uuid) is bound as
LTC after two advancing samples, and that binding survives layout
refreshes. pp-timers-mock gains --ltc-unlisted to exercise the path.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ProPresenter's HTTP API has no timecode route (verified: 21.4 returns
404), but its classic stage-display websocket still answers — so LTC now
comes from a stage layout field labeled "LTC": a zero-dependency RFC6455
client (stage-ws.js) authenticates (new stagePassword admin config),
resolves the LTC-labeled frame's uid from the layout list, and treats any
message carrying that uid as the timecode text. "Receiving" is a 2.5s
staleness heuristic; frozen or placeholder text reads as no-signal. The
HTTP probe stays authoritative when it ever answers (a 404 verdict is
retried once a minute), and the LTC card surfaces the reason when
timecode is unavailable (bad stage password, no LTC field). pp-timers-mock
grows a /stagedisplay websocket with --stage-pwd and --no-ltc-field to
exercise every path.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adopt main's new multi-tile contract: the module's server exports
tiles() (answered from the poller's held state, never a fresh upstream
request) listing All timers, one entry per discovered timer, and LTC
timecode. The client branches on moduleApi.variant — solo tiles render
exactly one card with no selection menu, and a solo timer that vanishes
from ProPresenter shows a labeled "Not in ProPresenter" card instead of
erroring (visible even in the compact layout). pp-timers-mock gains
--drop=<uuid,...> to simulate a timer deleted in ProPresenter.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New propresenter-timers module: polls /v1/timers + /v1/timers/current
(shapes verified against openapi.propresenter.com), probes the
undocumented /v1/timecode/status for LTC and honestly reports
"not available on this ProPresenter" when absent. Per-tile timer/LTC
selection via a Timers ▾ header menu (never-seen timers default on),
responsive auto-sized cards with state-derived colors (the API exposes
no per-timer color), unmistakable overrun. Includes tools/pp-timers-mock.js
for developing without a real ProPresenter.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Milestone 3. Server part proxies /prodcom/* (SSE passthrough included) to
the admin-configured ProdCom URL, adding the Bearer key server-side so it
never reaches browsers; reports proxy health for the admin page. Client
ports the prodcom-listener UI into the module contract: history load,
live SSE with in-place updates of in-progress speech, channel colors,
channel visibility toggles + group presets (persisted per tile), hard
channel filter / text size / timestamps as instance settings, auto-scroll
with a jump-to-latest pill. tools/prodcom-mock.js simulates ProdCom
(word-by-word in-progress speech) for testing; verified against it,
including kill-and-restart recovery.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RNYUv9EyrGSFmCnmH7WYKH