fix(display): keep blackout across reloads and follow the display's current assignment
A kiosk that reloaded during a blackout restored its last frame from local state without the blackout, so the hidden slide flashed on the wall until the server answered. A display resolved its channel once, at registration. Moving it to another group or audience, or deleting the channel it was on, never reached a running screen: it kept showing the old channel, or fell into offline continuation against a 404 forever. The display now fetches its state by viewer, so the server resolves the current assignment on every fetch, and the heartbeat reply names the assigned channel so a reassignment lands within one heartbeat. A display removed from the roster registers again once, on backoff; only Signage's own not-found means removal, so a foreign 404 can never cause a registration loop. The channel route stays for the preview and for display code a kiosk cached earlier. A replaced realtime subscription can no longer schedule a stray resubscription when it reports closing. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
6bf9167f85
commit
67d89f1020
@@ -206,9 +206,28 @@ export function parseDisplayHeartbeat(raw: unknown): ProtocolResult<DisplayHeart
|
||||
export interface DisplayHeartbeatResponse {
|
||||
readonly acknowledged: true;
|
||||
readonly recorded: boolean;
|
||||
/** The channel an administrator has this viewer assigned to now. A display whose own idea of its
|
||||
* channel differs refetches its state, so a reassignment reaches a running screen within one
|
||||
* heartbeat. Optional: a display ignores a response without it. */
|
||||
readonly channelId?: string;
|
||||
}
|
||||
|
||||
// --- Channel state: GET channels/:channelId/display-state -----------------------------------------
|
||||
/** The assigned channel from a heartbeat response, if it carried a usable one. */
|
||||
export function assignedChannelOf(raw: unknown): string | undefined {
|
||||
if (typeof raw !== "object" || raw === null) {
|
||||
return undefined;
|
||||
}
|
||||
const channelId = (raw as { readonly channelId?: unknown }).channelId;
|
||||
return typeof channelId === "string" && channelId.length > 0 && channelId.length <= 64
|
||||
? channelId
|
||||
: undefined;
|
||||
}
|
||||
|
||||
// --- Channel state: GET viewers/:viewerId/display-state (and channels/:channelId/display-state) -----
|
||||
//
|
||||
// A registered display asks by its own viewer id, so the server resolves which channel it is assigned
|
||||
// to at that moment. The channel route remains for the management preview and for display code a
|
||||
// kiosk cached before the viewer route existed.
|
||||
|
||||
/** A fresh, signed delivery URL. A bearer capability with an expiry: never persisted by a display. */
|
||||
export interface DisplayMedia {
|
||||
|
||||
Reference in New Issue
Block a user