Skip to content

Repository files navigation

vindral-composer-connection

TypeScript/Node.js library for connecting to a Vindral Composer instance via its WebSocket and HTTP Web APIs.

Designed to be used as the foundation for a TSR (Timeline State Resolver) device integration and potentially useful for others in future.

Features

  • WebSocket connection to Vindral Composer's WS API
  • HTTP Web API integration for controlling Composer (connectors, scenes, media, script engine)
  • Automatic reconnection with exponential backoff; connected is only emitted once both the WebSocket and HTTP API are healthy
  • HTTP keepalive polling (/api/getstatistics) to detect connectivity loss and trigger reconnection
  • Typed state model mirroring Composer's AudioMixerStrips
  • EventEmitter-based API, similar to sofie-atem-connection
  • Manual subscribe/unsubscribe to Composer channels (AudioMixer, etc.)

Installation

npm install vindral-composer-connection
# or
yarn add vindral-composer-connection

Usage

import { VindralComposer } from 'vindral-composer-connection'

const composer = new VindralComposer({
	host: '192.168.1.100',
	wsPort: 8081,
	httpPort: 44433,
})

composer.on('connected', () => {
	// Both WebSocket and HTTP API are healthy
	console.log('Connected to Vindral Composer')
	void composer.subscribeAudioMixer()
})

composer.on('disconnected', () => {
	console.log('Disconnected from Vindral Composer')
})

composer.on('stateChanged', (state, changedPaths) => {
	console.log('State updated:', changedPaths)
	console.log('Audio strips:', state.audioMixer?.strips)
})

composer.on('error', (err) => {
	console.error('Error:', err)
})

await composer.connect()

// WebSocket — set a property on an audio strip by its object ID
await composer.setPropertyValue('strip-abc123', 'Volume', 0.8)

// HTTP API — trigger a Connector by name
await composer.triggerConnector('MyConnector', { scene: 'intro' })

// HTTP API — trigger a Connector by value
await composer.triggerConnectorByValue('42')

// HTTP API — execute a Script Engine function
await composer.executeScriptFunction('MyFunction', { playerId: 'u123' })

// HTTP API — list all Connectors
const connectors = await composer.listConnectors()

// HTTP API — change a scene layer's source
await composer.setLayerSource('MyScene', 'MyLayer', 'MyInput')

// HTTP API — load and play a video file
await composer.playVideoFileInput('MyVideoInput', '/media/clip.mp4')

// Unsubscribe and disconnect when done
await composer.unsubscribeAudioMixer()
composer.disconnect()

API

VindralComposer

Constructor options

Option Type Default Description
host string 'localhost' Hostname or IP of the Composer instance
wsPort number 8081 WebSocket port
httpPort number 44433 HTTP Web API port
autoReconnect boolean true Whether to automatically reconnect on disconnect
reconnectInterval number 1000 Initial reconnect delay in ms (doubles on each attempt, capped at 30s)
pollInterval number 10000 Interval in ms between HTTP keepalive polls to /api/getstatistics
pollTimeout number 5000 Timeout in ms for each keepalive poll request
requestTimeout number 10000 Timeout in ms for HTTP API method calls
apiKey string '' API key sent as the apikey header on every HTTP request. Required when apikeys.json on the Composer host defines keys.

WebSocket methods

  • connect(): Promise<void> — Open the WebSocket connection
  • disconnect(): void — Close the connection and stop reconnecting
  • subscribeAudioMixer(): Promise<void> — Subscribe to the AudioMixer channel
  • unsubscribeAudioMixer(): Promise<void> — Unsubscribe from the AudioMixer channel
  • setPropertyValue(objectId: string, propertyName: string, value: string | number | boolean): Promise<void> — Set a writable property on a Composer object

Note: subscribeAudioMixer(), unsubscribeAudioMixer(), and setPropertyValue() are fire-and-forget — the returned promise resolves immediately after sending the message, before the server has processed it. There is no acknowledgement from Composer's WebSocket API.

HTTP API methods

All HTTP methods require the composer to be in the CONNECTED state and throw if it is not. They also throw on non-2xx HTTP responses.

  • triggerConnector(name: string, params?: Record<string, string>): Promise<void> — Trigger a Connector by name. Optional params are appended to the request URL and accessible via @@paramname in Connector commands.
  • triggerConnectorByValue(value: string, params?: Record<string, string>): Promise<void> — Trigger a Connector by value. Optional params are appended as above.
  • executeScriptFunction(functionName: string, parameter?: Record<string, unknown>, signal?: AbortSignal): Promise<void> — Execute a Script Engine function. parameter is JSON-serialised and passed as the parameter query argument.
  • listConnectors(signal?: AbortSignal): Promise<VindralConnector[]> — Return all Connectors configured in Composer.
  • setLayerSource(scene: string, layer: string, source: string, signal?: AbortSignal): Promise<void> — Change the input source of a scene layer. The source must already exist in the Composer Inputs list.
  • playVideoFileInput(inputName: string, sourceUri: string, signal?: AbortSignal): Promise<void> — Load and play a video file in a named Video File Input. Playback starts automatically once the file is parsed.

Events

Event Payload Description
connected WebSocket connected, Welcome received, and HTTP API is reachable
disconnected Connection closed; state is reset to initial values (consumers should treat cached state as stale)
error string Error message
stateChanged VindralState, string[] State updated; second arg is list of changed paths
info string Informational message
debug string Debug message

State (VindralState)

interface VindralState {
	info: {
		composerVersion: string
		projectName: string
		composerOS: string
	}
	audioMixer:
		| {
				strips: AudioMixerStrip[]
		  }
		| undefined
}

Note: The connected event is deferred until the first HTTP keepalive poll to /api/getstatistics succeeds. If the HTTP API becomes unreachable after connection, the socket is reconnected and disconnected is emitted.

Types

VindralConnector — shape of each entry returned by listConnectors():

interface VindralConnector {
	Id: string
	Name: string
	IsActive: boolean
	InvokeCount: number
	Description: string
}

API setup notes

WebSocket API

Composer's WebSocket API defaults to port 8081. You must enable it in Composer settings:

  • Desktop: Settings → Web API → Enable WebSockets
  • Runtime: Edit settings.xml:
"EnableWebSockets": true,
"WebSocketsHostName": "localhost",
"WebSocketsPort": 8081

Note: For remote connections, set WebSocketsHostName to the machine's IP address (not localhost).

HTTP Web API

Composer's HTTP Web API defaults to port 44433. You must enable it in Composer settings. Refer to the HTTP Web API docs for the exact configuration keys.

Note: For remote connections, configure the API to bind to the machine's IP address rather than localhost.

Message protocol

All WebSocket messages are JSON with the shape:

{ "Type": "MessageType", "Content": "string or JSON string" }

Key message types:

  • Subscribe / Unsubscribe — Subscribe to a named channel (AudioMixer, LogFile)
  • SetPropertyValueByObjectId — Write a property value on a Composer object
  • AudioMixerSummary (incoming) — Full AudioMixer state (sent after subscribe and on changes)
  • Welcome (incoming) — Sent on connect with Composer version/project info

Development

yarn install
yarn build      # compile TypeScript
yarn test       # lint + unit tests
yarn unit       # unit tests only
yarn watch      # run tests in watch mode

Reference

About

Sofie Vindral Composer Connection: A Part of the Sofie TV Studio Automation System

Resources

Contributing

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages