mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-08 08:53:51 +00:00
84 lines
2.7 KiB
TypeScript
84 lines
2.7 KiB
TypeScript
import { LogOrigin } from 'ontime-types';
|
|
|
|
import { fromBuffer, type OscPacketOutput } from 'osc-min';
|
|
import * as dgram from 'node:dgram';
|
|
|
|
import { logger } from '../classes/Logger.js';
|
|
import { dispatchFromAdapter } from '../api-integration/integration.controller.js';
|
|
import { isOntimeCloud } from '../setup/environment.js';
|
|
|
|
import { integrationPayloadFromPath } from './utils/parse.js';
|
|
import type { IAdapter } from './IAdapter.js';
|
|
|
|
class OscServer implements IAdapter {
|
|
private udpSocket: dgram.Socket | null = null;
|
|
|
|
init(port: number) {
|
|
if (isOntimeCloud) {
|
|
logger.warning(LogOrigin.Rx, 'OSC: Skip starting server in cloud environment');
|
|
}
|
|
this.udpSocket?.close();
|
|
logger.info(LogOrigin.Rx, `OSC: Starting server on port ${port}`);
|
|
this.udpSocket = dgram.createSocket('udp4');
|
|
this.udpSocket.on('error', (error) => logger.error(LogOrigin.Rx, `OSC IN: ${error}`));
|
|
this.udpSocket.on('message', (buf) => {
|
|
// message should look like /ontime/{command}/{params?} {args} where
|
|
// ontime: fixed message for app
|
|
// command: command to be called
|
|
// params: used to create a nested object to patch with
|
|
// args: extra data, only used on some API entries
|
|
|
|
let msg: OscPacketOutput;
|
|
try {
|
|
msg = fromBuffer(buf);
|
|
} catch (_e) {
|
|
logger.error(LogOrigin.Rx, 'OSC IN: Received invalid OSC message');
|
|
return;
|
|
}
|
|
|
|
if (msg.oscType === 'bundle') {
|
|
logger.error(LogOrigin.Rx, 'OSC IN: Ontime is unable to handle OSC bundles');
|
|
return;
|
|
}
|
|
|
|
const { address, args: oscArgs } = msg;
|
|
|
|
// split message
|
|
const [, ontimeKey, command, ...params] = address.split('/');
|
|
const args = oscArgs[0]?.value ?? undefined;
|
|
|
|
// get first part (ontime)
|
|
if (ontimeKey !== 'ontime') {
|
|
logger.error(LogOrigin.Rx, `OSC IN: OSC messages to ontime must start with /ontime/, received: ${msg}`);
|
|
return;
|
|
}
|
|
|
|
// get second part (command)
|
|
if (!command) {
|
|
logger.error(LogOrigin.Rx, 'OSC IN: No path found');
|
|
return;
|
|
}
|
|
|
|
let transformedPayload: unknown = args;
|
|
// we need to transform the params for the more complex endpoints
|
|
if (params.length) {
|
|
transformedPayload = integrationPayloadFromPath(params, args);
|
|
}
|
|
|
|
try {
|
|
dispatchFromAdapter(command, transformedPayload, 'osc');
|
|
} catch (error) {
|
|
logger.error(LogOrigin.Rx, `OSC IN: ${error}`);
|
|
}
|
|
});
|
|
this.udpSocket.bind(port);
|
|
}
|
|
shutdown() {
|
|
logger.info(LogOrigin.Rx, 'OSC: Closing server');
|
|
this.udpSocket?.close();
|
|
this.udpSocket = null;
|
|
}
|
|
}
|
|
|
|
export const oscServer = new OscServer();
|