import { randomUUID } from "crypto"; import type { CreatePlaybackSessionPayload, LibraryEvent, NodeHeartbeat, } from "@media-cluster/shared-types"; import { PROTOCOL_VERSION } from "@media-cluster/shared-types"; export { PROTOCOL_VERSION }; export type ControlMessageType = | "HELLO" | "HELLO_ACK" | "HEARTBEAT" | "HEARTBEAT_ACK" | "LIBRARY_EVENT" | "LIBRARY_EVENT_ACK" | "FULL_LIBRARY_SYNC" | "FULL_LIBRARY_SYNC_ACK" | "CREATE_PLAYBACK_SESSION" | "REVOKE_PLAYBACK_SESSION" | "RESCAN" | "CONFIG_UPDATE" | "PLAYBACK_SESSION_ENDED" | "ERROR"; export interface ControlMessage { protocolVersion: number; type: ControlMessageType; messageId: string; timestamp: string; payload: T; } export interface HelloPayload { nodeId: string; apiKey: string; softwareVersion: string; architecture: string; hostname: string; publicStreamUrl: string; } export interface HelloAckPayload { accepted: boolean; reason?: string; heartbeatIntervalSeconds: number; fullScanIntervalSeconds: number; } export interface HeartbeatPayload extends NodeHeartbeat {} export interface LibraryEventPayload { events: LibraryEvent[]; } export interface FullLibrarySyncPayload { nodeRevision: number; files: LibraryEvent["file"][]; } export interface RescanPayload { full: boolean; } export interface PlaybackSessionEndedPayload { sessionId: string; reason: string; } export function createMessage( type: ControlMessageType, payload: T, messageId?: string ): ControlMessage { return { protocolVersion: PROTOCOL_VERSION, type, messageId: messageId ?? randomUUID(), timestamp: new Date().toISOString(), payload, }; } export type CreatePlaybackSessionMessage = ControlMessage; export type RevokePlaybackSessionMessage = ControlMessage<{ sessionId: string }>;