mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-14 03:43:50 +00:00
refactor: pass HTTP to integration controller (#652)
* refactor: pass HTTP to integration controller * refactor: deprecate onair control * refactor: remove playback router
This commit is contained in:
@@ -1,4 +1,8 @@
|
||||
import { messageService } from '../services/message-service/MessageService.js';
|
||||
// skipcq: JS-C1003 - we like the API
|
||||
import * as assert from '../utils/assert.js';
|
||||
import { ONTIME_VERSION } from '../ONTIME_VERSION.js';
|
||||
|
||||
import { isPartialTimerMessage, messageService } from '../services/message-service/MessageService.js';
|
||||
import { PlaybackService } from '../services/PlaybackService.js';
|
||||
import { eventStore } from '../stores/EventStore.js';
|
||||
import { parse, updateEvent } from './integrationController.config.js';
|
||||
@@ -9,273 +13,205 @@ export type ChangeOptions = {
|
||||
value: unknown;
|
||||
};
|
||||
|
||||
//TODO: re-throwing the error does not add any extra information or value
|
||||
export function dispatchFromAdapter(
|
||||
type: string,
|
||||
args: {
|
||||
payload: unknown;
|
||||
},
|
||||
_source?: 'osc' | 'ws',
|
||||
_source?: 'osc' | 'ws' | 'http',
|
||||
) {
|
||||
const payload = args.payload;
|
||||
const typeComponents = type.toLowerCase().split('/');
|
||||
const mainType = typeComponents[0];
|
||||
|
||||
switch (mainType) {
|
||||
case 'test-ontime': {
|
||||
return { topic: 'hello' };
|
||||
}
|
||||
|
||||
case 'ontime-poll': {
|
||||
return {
|
||||
topic: 'poll',
|
||||
payload: eventStore.poll(),
|
||||
};
|
||||
}
|
||||
|
||||
case 'set-onair': {
|
||||
if (typeof payload !== 'undefined') {
|
||||
messageService.setOnAir(Boolean(payload));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'onair': {
|
||||
messageService.setOnAir(true);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'offair': {
|
||||
messageService.setOnAir(false);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'set-timer-blink': {
|
||||
if (typeof payload !== 'undefined') {
|
||||
messageService.setTimerBlink(Boolean(payload));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'set-timer-blackout': {
|
||||
if (typeof payload !== 'undefined') {
|
||||
messageService.setTimerBlackout(Boolean(payload));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'set-timer-message-text': {
|
||||
if (typeof payload !== 'string') {
|
||||
throw new Error(`Unable to parse payload: ${payload}`);
|
||||
}
|
||||
messageService.setTimerText(payload);
|
||||
break;
|
||||
}
|
||||
case 'set-timer-message-visible': {
|
||||
if (typeof payload === 'undefined') {
|
||||
throw new Error(`Unable to parse payload: ${payload}`);
|
||||
}
|
||||
messageService.setTimerVisibility(Boolean(payload));
|
||||
break;
|
||||
}
|
||||
|
||||
case 'set-public-message-text': {
|
||||
if (typeof payload !== 'string') {
|
||||
throw new Error(`Unable to parse payload: ${payload}`);
|
||||
}
|
||||
messageService.setPublicText(payload);
|
||||
break;
|
||||
}
|
||||
case 'set-public-message-visible': {
|
||||
if (typeof payload === 'undefined') {
|
||||
throw new Error(`Unable to parse payload: ${payload}`);
|
||||
}
|
||||
messageService.setPublicVisibility(Boolean(payload));
|
||||
break;
|
||||
}
|
||||
|
||||
case 'set-lower-message-text': {
|
||||
if (typeof payload !== 'string') {
|
||||
throw new Error(`Unable to parse payload: ${payload}`);
|
||||
}
|
||||
messageService.setLowerText(payload);
|
||||
break;
|
||||
}
|
||||
case 'set-lower-message-visible': {
|
||||
if (typeof payload === 'undefined') {
|
||||
throw new Error(`Unable to parse payload: ${payload}`);
|
||||
}
|
||||
messageService.setLowerVisibility(Boolean(payload));
|
||||
break;
|
||||
}
|
||||
|
||||
case 'set-external-message-text': {
|
||||
if (typeof payload !== 'string') {
|
||||
throw new Error(`Unable to parse payload: ${payload}`);
|
||||
}
|
||||
messageService.setExternalText(payload);
|
||||
return;
|
||||
}
|
||||
case 'set-external-message-visible': {
|
||||
if (typeof payload === 'undefined') {
|
||||
throw new Error(`Unable to parse payload: ${payload}`);
|
||||
}
|
||||
messageService.setExternalVisibility(Boolean(payload));
|
||||
break;
|
||||
}
|
||||
case 'start': {
|
||||
PlaybackService.start();
|
||||
break;
|
||||
}
|
||||
|
||||
case 'start-next': {
|
||||
PlaybackService.startNext();
|
||||
break;
|
||||
}
|
||||
|
||||
case 'startindex': {
|
||||
const eventIndex = Number(payload);
|
||||
if (isNaN(eventIndex) || eventIndex <= 0) {
|
||||
throw new Error(`Event index not recognised or out of range ${eventIndex}`);
|
||||
}
|
||||
|
||||
try {
|
||||
// Indexes in frontend are 1 based
|
||||
PlaybackService.startByIndex(eventIndex - 1);
|
||||
} catch (error) {
|
||||
throw new Error(`Error loading event:: ${error}`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'startid': {
|
||||
if (!payload || typeof payload !== 'string') {
|
||||
throw new Error(`Event ID not recognised: ${payload}`);
|
||||
}
|
||||
PlaybackService.startById(payload);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'startcue': {
|
||||
if (!payload || typeof payload !== 'string') {
|
||||
throw new Error(`Event cue not recognised: ${payload}`);
|
||||
}
|
||||
PlaybackService.startByCue(payload);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'pause': {
|
||||
PlaybackService.pause();
|
||||
break;
|
||||
}
|
||||
case 'previous': {
|
||||
PlaybackService.loadPrevious();
|
||||
break;
|
||||
}
|
||||
case 'next': {
|
||||
PlaybackService.loadNext();
|
||||
break;
|
||||
}
|
||||
case 'unload':
|
||||
case 'stop': {
|
||||
PlaybackService.stop();
|
||||
break;
|
||||
}
|
||||
case 'reload': {
|
||||
PlaybackService.reload();
|
||||
break;
|
||||
}
|
||||
case 'roll': {
|
||||
PlaybackService.roll();
|
||||
break;
|
||||
}
|
||||
case 'addtime': {
|
||||
const time = Number(payload);
|
||||
if (isNaN(time)) {
|
||||
throw new Error(`Time not recognised ${payload}`);
|
||||
}
|
||||
try {
|
||||
PlaybackService.addTime(time);
|
||||
} catch (error) {
|
||||
throw new Error(`Could not add time: ${error}`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
//deprecated
|
||||
case 'delay': {
|
||||
const delayTime = Number(payload);
|
||||
if (isNaN(delayTime)) {
|
||||
throw new Error(`Delay time not recognised ${payload}`);
|
||||
}
|
||||
|
||||
try {
|
||||
PlaybackService.setDelay(delayTime);
|
||||
} catch (error) {
|
||||
throw new Error(`Could not add delay: ${error}`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'gotoindex':
|
||||
case 'loadindex': {
|
||||
const eventIndex = Number(payload);
|
||||
if (isNaN(eventIndex) || eventIndex <= 0) {
|
||||
throw new Error(`Event index not recognised or out of range ${eventIndex}`);
|
||||
}
|
||||
|
||||
try {
|
||||
// Indexes in frontend are 1 based
|
||||
PlaybackService.loadByIndex(eventIndex - 1);
|
||||
} catch (error) {
|
||||
throw new Error(`Event index not recognised or out of range ${error}`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'gotoid':
|
||||
case 'loadid': {
|
||||
if (!payload) {
|
||||
throw new Error(`Event ID not recognised: ${payload}`);
|
||||
}
|
||||
|
||||
try {
|
||||
PlaybackService.loadById(payload.toString().toLowerCase());
|
||||
} catch (error) {
|
||||
throw new Error(`OSC IN: error calling goto ${error}`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'gotocue':
|
||||
case 'loadcue': {
|
||||
if (!payload || typeof payload !== 'string') {
|
||||
throw new Error(`Event cue not recognised: ${payload}`);
|
||||
}
|
||||
|
||||
try {
|
||||
PlaybackService.loadByCue(payload);
|
||||
} catch (error) {
|
||||
throw new Error(`OSC IN: error calling goto ${error}`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'get-playback': {
|
||||
const playback = eventStore.get('playback');
|
||||
return { topic: 'playback', payload: playback };
|
||||
}
|
||||
|
||||
case 'get-timer': {
|
||||
const timer = eventStore.get('timer');
|
||||
return { topic: 'timer', payload: timer };
|
||||
}
|
||||
|
||||
// WS: {type: 'change', payload: { eventId, property, value } }
|
||||
case 'change': {
|
||||
const { eventId, property, value } = payload as ChangeOptions;
|
||||
const { parsedPayload, parsedProperty } = parse(property, value);
|
||||
return updateEvent(eventId, parsedProperty, parsedPayload);
|
||||
}
|
||||
|
||||
default: {
|
||||
throw new Error(`Unhandled message ${type}`);
|
||||
}
|
||||
const action = type.toLowerCase();
|
||||
const handler = actionHandlers[action];
|
||||
if (handler) {
|
||||
return handler(payload);
|
||||
} else {
|
||||
throw new Error(`Unhandled message ${type}`);
|
||||
}
|
||||
}
|
||||
|
||||
type ActionHandler = (payload: unknown) => { payload: unknown };
|
||||
|
||||
// TODO: add data to missing returns once available
|
||||
const actionHandlers: Record<string, ActionHandler> = {
|
||||
/* General */
|
||||
'test-ontime': () => ({ payload: `Hello from Ontime version ${ONTIME_VERSION}` }),
|
||||
'ontime-poll': () => ({
|
||||
payload: eventStore.poll(),
|
||||
}),
|
||||
change: (payload) => {
|
||||
// WS: {type: 'change', payload: { eventId, property, value } }
|
||||
const { eventId, property, value } = payload as ChangeOptions;
|
||||
const { parsedPayload, parsedProperty } = parse(property, value);
|
||||
|
||||
const updatedEvent = updateEvent(eventId, parsedProperty, parsedPayload);
|
||||
return { payload: updatedEvent };
|
||||
},
|
||||
/* Message Service */
|
||||
'set-timer-message': (payload) => {
|
||||
if (!isPartialTimerMessage(payload)) {
|
||||
throw new Error('Payload is not a valid timer message');
|
||||
}
|
||||
const newState = messageService.setTimerMessage(payload);
|
||||
return { payload: newState.timerMessage };
|
||||
},
|
||||
'set-timer-blink': (payload) => {
|
||||
assert.isDefined(payload);
|
||||
const newState = messageService.setTimerBlink(Boolean(payload));
|
||||
return { payload: newState.timerMessage };
|
||||
},
|
||||
'set-timer-blackout': (payload) => {
|
||||
assert.isDefined(payload);
|
||||
const newState = messageService.setTimerBlackout(Boolean(payload));
|
||||
return { payload: newState.timerMessage };
|
||||
},
|
||||
'set-timer-message-text': (payload) => {
|
||||
assert.isString(payload);
|
||||
const newState = messageService.setTimerText(payload);
|
||||
return { payload: newState.timerMessage };
|
||||
},
|
||||
'set-timer-message-visible': (payload) => {
|
||||
assert.isDefined(payload);
|
||||
const newState = messageService.setTimerVisibility(Boolean(payload));
|
||||
return { payload: newState.timerMessage };
|
||||
},
|
||||
'set-public-message-text': (payload) => {
|
||||
assert.isString(payload);
|
||||
const newState = messageService.setPublicText(payload);
|
||||
return { payload: newState.publicMessage };
|
||||
},
|
||||
'set-public-message-visible': (payload) => {
|
||||
assert.isDefined(payload);
|
||||
const newState = messageService.setPublicVisibility(Boolean(payload));
|
||||
return { payload: newState.publicMessage };
|
||||
},
|
||||
'set-lower-message-text': (payload) => {
|
||||
assert.isString(payload);
|
||||
const newState = messageService.setLowerText(payload);
|
||||
return { payload: newState.lowerMessage };
|
||||
},
|
||||
'set-lower-message-visible': (payload) => {
|
||||
assert.isDefined(payload);
|
||||
const newState = messageService.setLowerVisibility(Boolean(payload));
|
||||
return { payload: newState.lowerMessage };
|
||||
},
|
||||
'set-external-message-text': (payload) => {
|
||||
assert.isString(payload);
|
||||
const newState = messageService.setExternalText(payload);
|
||||
return { payload: newState.externalMessage };
|
||||
},
|
||||
'set-external-message-visible': (payload) => {
|
||||
assert.isDefined(payload);
|
||||
const newState = messageService.setExternalVisibility(Boolean(payload));
|
||||
return { payload: newState.externalMessage };
|
||||
},
|
||||
/* Playback */
|
||||
start: (payload) => {
|
||||
if (payload && typeof payload === 'object') {
|
||||
if ('index' in payload) {
|
||||
const reply = actionHandlers.startindex(payload.index);
|
||||
return reply;
|
||||
}
|
||||
if ('id' in payload) {
|
||||
const reply = actionHandlers.startid(payload.id);
|
||||
return reply;
|
||||
}
|
||||
if ('cue' in payload) {
|
||||
const reply = actionHandlers.startcue(payload.cue);
|
||||
return reply;
|
||||
}
|
||||
}
|
||||
|
||||
PlaybackService.start();
|
||||
|
||||
return { payload: 'start' };
|
||||
},
|
||||
'start-next': () => {
|
||||
PlaybackService.startNext();
|
||||
return { payload: 'start' };
|
||||
},
|
||||
startindex: (payload) => {
|
||||
const eventIndex = numberOrError(payload);
|
||||
if (eventIndex <= 0) {
|
||||
throw new Error(`Event index out of range ${eventIndex}`);
|
||||
}
|
||||
|
||||
// Indexes in frontend are 1 based
|
||||
PlaybackService.startByIndex(eventIndex - 1);
|
||||
return { payload: 'success' };
|
||||
},
|
||||
startid: (payload) => {
|
||||
assert.isString(payload);
|
||||
PlaybackService.startById(payload);
|
||||
return { payload: 'success' };
|
||||
},
|
||||
startcue: (payload) => {
|
||||
assert.isString(payload);
|
||||
PlaybackService.startByCue(payload);
|
||||
return { payload: 'success' };
|
||||
},
|
||||
pause: () => {
|
||||
PlaybackService.pause();
|
||||
return { payload: 'success' };
|
||||
},
|
||||
previous: () => {
|
||||
PlaybackService.loadPrevious();
|
||||
return { payload: 'success' };
|
||||
},
|
||||
next: () => {
|
||||
PlaybackService.loadNext();
|
||||
return { payload: 'success' };
|
||||
},
|
||||
stop: () => {
|
||||
PlaybackService.stop();
|
||||
return { payload: 'success' };
|
||||
},
|
||||
reload: () => {
|
||||
PlaybackService.reload();
|
||||
return { payload: 'success' };
|
||||
},
|
||||
roll: () => {
|
||||
PlaybackService.roll();
|
||||
return { payload: 'success' };
|
||||
},
|
||||
loadindex: (payload) => {
|
||||
const eventIndex = numberOrError(payload);
|
||||
if (eventIndex <= 0) {
|
||||
throw new Error(`Event index out of range ${eventIndex}`);
|
||||
}
|
||||
|
||||
// Indexes in frontend are 1 based
|
||||
PlaybackService.loadByIndex(eventIndex - 1);
|
||||
return { payload: 'success' };
|
||||
},
|
||||
loadid: (payload) => {
|
||||
assert.isDefined(payload);
|
||||
PlaybackService.loadById(payload.toString().toLowerCase());
|
||||
return { payload: 'success' };
|
||||
},
|
||||
loadcue: (payload) => {
|
||||
assert.isString(payload);
|
||||
PlaybackService.loadByCue(payload);
|
||||
return { payload: 'success' };
|
||||
},
|
||||
addtime: (payload) => {
|
||||
const time = numberOrError(payload);
|
||||
PlaybackService.addTime(time);
|
||||
return { payload: 'success' };
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a value of type number, converting if necessary
|
||||
* Otherwise throws
|
||||
* @param value
|
||||
* @returns number
|
||||
* @throws
|
||||
*/
|
||||
function numberOrError(value: unknown) {
|
||||
const converted = Number(value);
|
||||
if (isNaN(converted)) {
|
||||
throw new Error('Payload is not a valid number');
|
||||
}
|
||||
return converted;
|
||||
}
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
import { PlaybackService } from '../services/PlaybackService.js';
|
||||
import { eventStore } from '../stores/EventStore.js';
|
||||
|
||||
// Create controller for POST request to '/playback'
|
||||
// Returns playback state
|
||||
export const pbGet = async (req, res) => {
|
||||
res.send({ playback: eventStore.get('playback') });
|
||||
};
|
||||
|
||||
// Create controller for POST request to '/playback/start'
|
||||
// Starts timer object
|
||||
export const pbStart = async (req, res) => {
|
||||
const { eventId, eventIndex } = req.query;
|
||||
if (eventId) {
|
||||
const success = PlaybackService.startById(eventId);
|
||||
success ? res.sendStatus(202) : res.status(400).send({ message: 'Invalid event ID' });
|
||||
} else if (eventIndex) {
|
||||
const index = Number(eventIndex);
|
||||
if (!isNaN(index)) {
|
||||
const success = PlaybackService.startByIndex(eventIndex - 1);
|
||||
success ? res.sendStatus(202) : res.status(400).send({ message: 'Invalid event index' });
|
||||
} else {
|
||||
res.status(400).send({ message: 'Invalid event index' });
|
||||
}
|
||||
} else {
|
||||
PlaybackService.start();
|
||||
res.sendStatus(202);
|
||||
}
|
||||
};
|
||||
|
||||
// Create controller for POST request to '/playback/pause'
|
||||
// Pauses timer object
|
||||
export const pbPause = async (req, res) => {
|
||||
PlaybackService.pause();
|
||||
res.sendStatus(202);
|
||||
};
|
||||
|
||||
// Create controller for POST request to '/playback/stop'
|
||||
// Stops timer object
|
||||
export const pbStop = async (req, res) => {
|
||||
PlaybackService.stop();
|
||||
res.sendStatus(202);
|
||||
};
|
||||
|
||||
// Create controller for POST request to '/playback/roll'
|
||||
// Sets timer object to roll mode
|
||||
export const pbRoll = async (req, res) => {
|
||||
PlaybackService.roll();
|
||||
res.sendStatus(202);
|
||||
};
|
||||
|
||||
// Create controller for POST request to '/playback/previous'
|
||||
// Loads previous event
|
||||
export const pbPrevious = async (req, res) => {
|
||||
PlaybackService.loadPrevious();
|
||||
res.sendStatus(202);
|
||||
};
|
||||
|
||||
// Create controller for POST request to '/playback/next'
|
||||
// Loads Next event
|
||||
export const pbNext = async (req, res) => {
|
||||
PlaybackService.loadNext();
|
||||
res.sendStatus(202);
|
||||
};
|
||||
|
||||
// Create controller for POST request to '/playback/load'
|
||||
// Load requested event
|
||||
export const pbLoad = async (req, res) => {
|
||||
const { eventId, eventIndex } = req.query;
|
||||
if (eventId) {
|
||||
const success = PlaybackService.loadById(eventId);
|
||||
success ? res.sendStatus(202) : res.status(400).send({ message: 'Invalid event ID' });
|
||||
} else if (eventIndex) {
|
||||
const index = Number(eventIndex);
|
||||
if (!isNaN(index)) {
|
||||
const success = PlaybackService.loadByIndex(eventIndex - 1);
|
||||
success ? res.sendStatus(202) : res.status(400).send({ message: 'Invalid event index' });
|
||||
} else {
|
||||
res.status(400).send({ message: 'Invalid event index' });
|
||||
}
|
||||
} else {
|
||||
res.status(400).send({ message: 'No event given' });
|
||||
}
|
||||
};
|
||||
|
||||
// Create controller for POST request to '/playback/unload'
|
||||
// Unloads any events
|
||||
export const pbUnload = async (req, res) => {
|
||||
PlaybackService.stop();
|
||||
res.sendStatus(202);
|
||||
};
|
||||
|
||||
// Create controller for POST request to '/playback/reload'
|
||||
// Reloads current event
|
||||
export const pbReload = async (req, res) => {
|
||||
PlaybackService.reload();
|
||||
res.sendStatus(202);
|
||||
};
|
||||
Reference in New Issue
Block a user