mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-19 14:14:17 +00:00
V3 (#657)
* refactor: cleanup routes * style: smaller base font * chore: upgrade dependencies * chore: lock node version to electron * refactor: pass HTTP to integration controller (#652) * refactor: deprecate onair control * refactor: remove playback router * Several project files user folder (#617) * chore: automated screenshots (#667) * feat: app settings (#658) * refactor: remove deprecated event data (#674) * Studio clock (#663) --------- Co-authored-by: Carlos Valente <carlosvalente@pm.me> * Feat: reorder events with alt+ctrl + arrow up/down (#645) * Warning and danger per event (#677) --------- Co-authored-by: Fabian Posenau <fabian@fphome.de> * refactor: stabilise actionHandler (#683) Co-authored-by: Fabian Posenau <fabian@fphome.de> * improvement: hide seconds (#675) * wip: overview (#688) * fix: focus cursor (#695) * refactor: update lower third (#665) * Refactor/time formatting (#696) --------- Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com> Co-authored-by: Carlos Valente <carlosvalente@pm.me> * feat: multiple selection (#703) --------- Co-authored-by: asharonbaltazar <asharonbaltazar@outlook.com> Co-authored-by: Alex <ac@omnivox.dk> * fix: test - go to `Edit mode` befor tying to click `Event options` button (#708) * refactor: runtime service (#715) * fix: issue with loosing cursor position on message (#719) * remove info panel (#721) * Event editor continue (#722) * update API - part (#709) --------- Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com> Co-authored-by: Carlos Valente <carlosvalente@pm.me> * refactor: update timers (#729) * feat: many timers (#706) --------- Co-authored-by: arc-alex <ac@omnivox.dk> * refactor: excel cleanup (#734) * refactor: allow import of blocks and skip import (#735) * Project manager (#697) * refactor: UI for linking events (#763) * upgraded pipeline actions (#777) * Over under (#771) * custom fields (#744) --------- Co-authored-by: Carlos Valente <carlosvalente@pm.me> * Sheets settings (#774) --------- Co-authored-by: arc-alex <ac@omnivox.dk> * style: tweaks to lower thirds (#785) * refactor: delays account for gaps (#784) * refactor: partial state updates (#780) * feat: generate crash report (#787) * Sheet use limited input device auth flow (#782) --------- Co-authored-by: cv <34649812+cpvalente@users.noreply.github.com> Co-authored-by: Carlos Valente <carlosvalente@pm.me> * Custom fields views (#789) * refactor: deprecate presenter and subtitle (#795) * refactor: organise API around resources (#798) --------- Co-authored-by: Bianca Procopio <biancahprocopio@gmail.com> * Time to end (#804) * Skip fixes (#805) * fix: onair derives from playback * Param nav (#822) --------- Co-authored-by: Alex Christoffer Rasmussen <ac@omnivox.dk> * refactor: download files from interface (#831) * Quick options (#814) * End pause (#832) * chore: bump node version in docker (#834) * refactor: follow in run mode (#840) * fix: uncaught error in http integration (#837) * Apply project (#843) Co-authored-by: Matteo Gheza <matteo.gheza07@gmail.com> Co-authored-by: Ary <arylmoraesn@gmail.com> Co-authored-by: Alex Christoffer Rasmussen <ac@omnivox.dk> Co-authored-by: Fabian Posenau <19673098+kellhogs@users.noreply.github.com> Co-authored-by: Fabian Posenau <fabian@fphome.de> Co-authored-by: Alex Rohleder <alexrohleder96@gmail.com> Co-authored-by: asharonbaltazar <asharonbaltazar@outlook.com> Co-authored-by: Bianca Procopio <biancahprocopio@gmail.com> Co-authored-by: Fabian Posenau <fabianpos99+github@gmail.com>
This commit is contained in:
@@ -1,133 +1,144 @@
|
||||
import { ArgumentType, Client, Message } from 'node-osc';
|
||||
import { OSCSettings, OscSubscription, OscSubscriptionOptions } from 'ontime-types';
|
||||
import { LogOrigin, MaybeNumber, MaybeString, OSCSettings, OscSubscription } from 'ontime-types';
|
||||
|
||||
import IIntegration, { TimerLifeCycleKey } from './IIntegration.js';
|
||||
import { parseTemplateNested } from './integrationUtils.js';
|
||||
import { isObject } from '../../utils/varUtils.js';
|
||||
import { dbModel } from '../../models/dataModel.js';
|
||||
import { validateOscSubscriptionObject } from '../../utils/parserFunctions.js';
|
||||
|
||||
type Action = TimerLifeCycleKey | string;
|
||||
import { logger } from '../../classes/Logger.js';
|
||||
import { OscServer } from '../../adapters/OscAdapter.js';
|
||||
|
||||
/**
|
||||
* @description Class contains logic towards outgoing OSC communications
|
||||
* @class
|
||||
*/
|
||||
export class OscIntegration implements IIntegration<OscSubscriptionOptions> {
|
||||
export class OscIntegration implements IIntegration<OscSubscription, OSCSettings> {
|
||||
protected oscClient: null | Client;
|
||||
subscriptions: OscSubscription;
|
||||
protected oscServer: OscServer | null = null;
|
||||
|
||||
subscriptions: OscSubscription[];
|
||||
targetIP: MaybeString;
|
||||
portOut: MaybeNumber;
|
||||
portIn: MaybeNumber;
|
||||
enabledOut: boolean;
|
||||
enabledIn: boolean;
|
||||
|
||||
constructor() {
|
||||
this.oscClient = null;
|
||||
this.subscriptions = dbModel.osc.subscriptions;
|
||||
this.subscriptions = [];
|
||||
this.targetIP = null;
|
||||
this.portOut = null;
|
||||
this.portIn = null;
|
||||
this.enabledOut = false;
|
||||
this.enabledIn = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes oscClient
|
||||
*/
|
||||
init(config: OSCSettings) {
|
||||
const { targetIP, portOut, subscriptions, enabledOut } = config;
|
||||
const { targetIP, portOut, subscriptions, enabledOut, enabledIn, portIn } = config;
|
||||
|
||||
if (!enabledOut) {
|
||||
this.oscClient?.close();
|
||||
return {
|
||||
success: false,
|
||||
message: 'OSC output disabled',
|
||||
};
|
||||
this.initTX(enabledOut, targetIP, portOut, subscriptions);
|
||||
this.initRX(enabledIn, portIn);
|
||||
// return `OSC integration client connected to ${targetIP}:${portOut}`;
|
||||
}
|
||||
|
||||
private initSubscriptions(subscriptions: OscSubscription[]) {
|
||||
this.subscriptions = subscriptions;
|
||||
}
|
||||
|
||||
dispatch(action: TimerLifeCycleKey, state?: object) {
|
||||
// noop
|
||||
if (!this.oscClient) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.subscriptions.length; i++) {
|
||||
const { cycle, address, payload, enabled } = this.subscriptions[i];
|
||||
if (cycle !== action || !enabled || !address) {
|
||||
continue;
|
||||
}
|
||||
const parsedAddress = parseTemplateNested(address, state || {});
|
||||
const parsedPayload = payload ? parseTemplateNested(payload, state || {}) : undefined;
|
||||
try {
|
||||
this.emit(parsedAddress, parsedPayload);
|
||||
} catch (error) {
|
||||
logger.error(LogOrigin.Tx, `OSC Integration: ${error}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emit(address: string, payload?: ArgumentType) {
|
||||
if (!this.oscClient) {
|
||||
return;
|
||||
}
|
||||
|
||||
const message = new Message(address);
|
||||
if (payload) {
|
||||
if (isObject(payload)) {
|
||||
message.append(JSON.stringify(payload));
|
||||
} else {
|
||||
message.append(payload);
|
||||
}
|
||||
}
|
||||
|
||||
this.oscClient.send(message);
|
||||
}
|
||||
|
||||
private initTX(enabledOut: boolean, targetIP: string, portOut: number, subscriptions: OscSubscription[]) {
|
||||
this.initSubscriptions(subscriptions);
|
||||
|
||||
// runtime validation
|
||||
const validateType = typeof targetIP !== 'string' || typeof portOut !== 'number';
|
||||
const validateNull = !targetIP || !portOut;
|
||||
|
||||
if (validateType || validateNull) {
|
||||
return {
|
||||
success: false,
|
||||
message: 'Config options incorrect',
|
||||
};
|
||||
if (!enabledOut && this.enabledOut) {
|
||||
this.targetIP = targetIP;
|
||||
this.portOut = portOut;
|
||||
this.enabledOut = enabledOut;
|
||||
this.shutdownTX();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.oscClient && targetIP === this.targetIP && portOut === this.portOut) {
|
||||
// nothing changed that would mean we need a new client
|
||||
return;
|
||||
}
|
||||
|
||||
this.targetIP = targetIP;
|
||||
this.portOut = portOut;
|
||||
this.enabledOut = enabledOut;
|
||||
|
||||
try {
|
||||
// this allows re-calling the init function during runtime
|
||||
this.oscClient?.close();
|
||||
this.oscClient = new Client(targetIP, portOut);
|
||||
return {
|
||||
success: true,
|
||||
message: `OSC integration client connected to ${targetIP}:${portOut}`,
|
||||
};
|
||||
} catch (error) {
|
||||
this.oscClient = null;
|
||||
return {
|
||||
success: false,
|
||||
message: `Failed initialising OSC Client: ${error}`,
|
||||
};
|
||||
throw new Error(`Failed initialising OSC client: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
initSubscriptions(subscriptionOptions: OscSubscription) {
|
||||
if (validateOscSubscriptionObject(subscriptionOptions)) {
|
||||
this.subscriptions = { ...subscriptionOptions };
|
||||
}
|
||||
}
|
||||
|
||||
dispatch(action: Action, state?: object) {
|
||||
if (!this.oscClient) {
|
||||
return {
|
||||
success: false,
|
||||
message: 'Client not initialised',
|
||||
};
|
||||
private initRX(enabledIn: boolean, portIn: number) {
|
||||
if (!enabledIn && this.enabledIn) {
|
||||
this.shutdownRX();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!action) {
|
||||
return {
|
||||
success: false,
|
||||
message: 'OSC called with no action',
|
||||
};
|
||||
}
|
||||
|
||||
// check subscriptions for action
|
||||
const eventSubscriptions = this.subscriptions?.[action] || [];
|
||||
|
||||
eventSubscriptions.forEach((sub) => {
|
||||
const { enabled, message } = sub;
|
||||
if (enabled && message) {
|
||||
const parsedMessage = parseTemplateNested(message, state || {});
|
||||
this.emit(parsedMessage);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
emit(path: string, payload?: ArgumentType) {
|
||||
const message = new Message(path);
|
||||
if (payload) {
|
||||
try {
|
||||
if (isObject(payload)) {
|
||||
message.append(JSON.stringify(payload));
|
||||
} else {
|
||||
message.append(payload);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('OSC ERROR', error, payload);
|
||||
}
|
||||
}
|
||||
|
||||
this.oscClient.send(message, (error) => {
|
||||
if (error) {
|
||||
return {
|
||||
success: false,
|
||||
message: `Error sending message: ${JSON.stringify(error)}`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
message: 'OSC Message sent',
|
||||
};
|
||||
});
|
||||
// Start OSC Server
|
||||
logger.info(LogOrigin.Rx, `Starting OSC Server on port: ${portIn}`);
|
||||
this.oscServer = new OscServer(portIn);
|
||||
}
|
||||
|
||||
shutdown() {
|
||||
console.log('Shutting down OSC integration');
|
||||
this.shutdownTX();
|
||||
this.shutdownRX();
|
||||
}
|
||||
|
||||
private shutdownTX() {
|
||||
logger.info(LogOrigin.Rx, 'Shutting down OSC integration');
|
||||
if (this.oscServer) {
|
||||
this.oscServer?.shutdown();
|
||||
this.oscServer = null;
|
||||
}
|
||||
}
|
||||
|
||||
private shutdownRX() {
|
||||
logger.info(LogOrigin.Tx, 'Shutting down OSC integration');
|
||||
if (this.oscClient) {
|
||||
this.oscClient?.close();
|
||||
this.oscClient = null;
|
||||
|
||||
Reference in New Issue
Block a user