This platform has never been able to screenshot itself. Video decodes onto a
hardware plane the DOM cannot read, so an in-page canvas composite comes back
with the content missing — the panel reported "Video is playing on the hardware
plane and cannot be captured" while playing perfectly.
@brightsign/screenshot composites the video and graphics layers, which is
exactly the thing a canvas cannot do. It is reached through the same Node
require() the widget already exposes — the one that also makes `module` visible
to classic scripts, which is what broke the shared UMD modules on this platform.
The same quirk caused that bug and enables this fix.
WHY THIS WORKS WHERE THE LONG WAY ROUND DID NOT.
The obvious route was to ask the HOST to capture through the player's own DWS,
because BrightScript can reach it. That is a dead end here: page->host messaging
stops working after page load, so the request never arrives — instrumenting the
host to echo the reason of EVERY roHtmlWidgetEvent produced nothing at all while
the page was posting. This API needs no host, no messageport and no DWS, so none
of that is in the path. The host route stays as a fallback for firmware without
the module, but it is no longer how this works.
The API writes a FILE rather than returning bytes, so it is read straight back
with Node's fs and sent over the socket the player already has.
TO RAM, NOT TO FLASH. The remote-control view drives this once a second, and a
screenshot per second written to the boot flash is a wear-out mechanism with
nothing to show for it: the file is read back and deleted microseconds later, so
it never needs to be durable. tmp is tried first and real storage only as a
fallback for a unit that does not present it. The directory must already exist
or the capture fails, so each candidate is checked rather than assumed.
Ordering is part of the fix: the native API is tried BEFORE the host route,
because trying the dead end first would spend an operator's patience on a 15s
timeout before reaching the path that works. Every failure still falls through
to the canvas, so a capture never comes back blank.
Remote streaming inherits all of it — startStreaming already drives the same
captureAndSend — so the live view now shows real video rather than a card
explaining why it cannot.
Verified on the hardware: a real 960x540 frame of the playing video, captured by
the player, delivered to the dashboard over its own socket.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014skWYXJUWhF73EvNPgB2AS