mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-13 19:33:46 +00:00
fix: apply delay (#494)
* fix: bug with applying delays * fix: show delay data only in production screens * chore: cover delay with feature test * refactor: simplify type assertion
This commit is contained in:
@@ -0,0 +1,249 @@
|
||||
import { OntimeBlock, OntimeDelay, OntimeEvent, OntimeRundown, SupportedEvent } from 'ontime-types';
|
||||
import { _applyDelay } from '../delayUtils.js';
|
||||
|
||||
describe('_applyDelay() ', () => {
|
||||
describe('in a rundown without the delay field, persisted rundown', () => {
|
||||
it('applies delays', () => {
|
||||
const delayId = '1';
|
||||
const testRundown: OntimeRundown = [
|
||||
{ id: delayId, type: SupportedEvent.Delay, duration: 10 } as OntimeDelay,
|
||||
{ id: '2', type: SupportedEvent.Event, timeStart: 0, timeEnd: 10, duration: 10, revision: 1 } as OntimeEvent,
|
||||
{ id: '3', type: SupportedEvent.Event, timeStart: 0, timeEnd: 10, duration: 10, revision: 1 } as OntimeEvent,
|
||||
{ id: '4', type: SupportedEvent.Block } as OntimeBlock,
|
||||
{ id: '5', type: SupportedEvent.Event, timeStart: 0, timeEnd: 10, duration: 10, revision: 1 } as OntimeEvent,
|
||||
];
|
||||
|
||||
const expected = [
|
||||
{ id: '2', type: SupportedEvent.Event, timeStart: 10, timeEnd: 20, duration: 10, revision: 2 } as OntimeEvent,
|
||||
{ id: '3', type: SupportedEvent.Event, timeStart: 10, timeEnd: 20, duration: 10, revision: 2 } as OntimeEvent,
|
||||
{ id: '4', type: SupportedEvent.Block } as OntimeBlock,
|
||||
{ id: '5', type: SupportedEvent.Event, timeStart: 0, timeEnd: 10, duration: 10, revision: 1 } as OntimeEvent,
|
||||
];
|
||||
|
||||
const updatedRundown = _applyDelay(delayId, testRundown);
|
||||
expect(updatedRundown).toStrictEqual(expected);
|
||||
});
|
||||
it('applies negative delays', () => {
|
||||
const delayId = '1';
|
||||
const testRundown: OntimeRundown = [
|
||||
{ id: delayId, type: SupportedEvent.Delay, duration: -10 } as OntimeDelay,
|
||||
{ id: '2', type: SupportedEvent.Event, timeStart: 0, timeEnd: 10, duration: 10, revision: 1 } as OntimeEvent,
|
||||
{ id: '3', type: SupportedEvent.Event, timeStart: 20, timeEnd: 40, duration: 20, revision: 1 } as OntimeEvent,
|
||||
{ id: '4', type: SupportedEvent.Block } as OntimeBlock,
|
||||
{ id: '5', type: SupportedEvent.Event, timeStart: 0, timeEnd: 10, duration: 10, revision: 1 } as OntimeEvent,
|
||||
];
|
||||
|
||||
const expected = [
|
||||
{ id: '2', type: SupportedEvent.Event, timeStart: 0, timeEnd: 10, duration: 10, revision: 2 } as OntimeEvent,
|
||||
{ id: '3', type: SupportedEvent.Event, timeStart: 10, timeEnd: 30, duration: 20, revision: 2 } as OntimeEvent,
|
||||
{ id: '4', type: SupportedEvent.Block } as OntimeBlock,
|
||||
{ id: '5', type: SupportedEvent.Event, timeStart: 0, timeEnd: 10, duration: 10, revision: 1 } as OntimeEvent,
|
||||
];
|
||||
|
||||
const updatedRundown = _applyDelay(delayId, testRundown);
|
||||
expect(updatedRundown).toStrictEqual(expected);
|
||||
});
|
||||
it('maintains constant duration', () => {
|
||||
const delayId = '1';
|
||||
const testRundown: OntimeRundown = [
|
||||
{ id: delayId, type: SupportedEvent.Delay, duration: -30 } as OntimeDelay,
|
||||
{ id: '2', type: SupportedEvent.Event, timeStart: 0, timeEnd: 10, duration: 10, revision: 1 } as OntimeEvent,
|
||||
{ id: '3', type: SupportedEvent.Event, timeStart: 20, timeEnd: 40, duration: 20, revision: 1 } as OntimeEvent,
|
||||
];
|
||||
|
||||
const expected = [
|
||||
{ id: '2', type: SupportedEvent.Event, timeStart: 0, timeEnd: 10, duration: 10, revision: 2 } as OntimeEvent,
|
||||
{ id: '3', type: SupportedEvent.Event, timeStart: 0, timeEnd: 20, duration: 20, revision: 2 } as OntimeEvent,
|
||||
];
|
||||
|
||||
const updatedRundown = _applyDelay(delayId, testRundown);
|
||||
expect(updatedRundown).toStrictEqual(expected);
|
||||
});
|
||||
});
|
||||
describe('in a rundown with the delay field, cached rundown', () => {
|
||||
it('applies delays', () => {
|
||||
const delayId = '1';
|
||||
const testRundown: OntimeRundown = [
|
||||
{ id: delayId, type: SupportedEvent.Delay, duration: 10 } as OntimeDelay,
|
||||
{
|
||||
id: '2',
|
||||
type: SupportedEvent.Event,
|
||||
timeStart: 0,
|
||||
timeEnd: 10,
|
||||
duration: 10,
|
||||
revision: 1,
|
||||
delay: 10,
|
||||
} as OntimeEvent,
|
||||
{
|
||||
id: '3',
|
||||
type: SupportedEvent.Event,
|
||||
timeStart: 0,
|
||||
timeEnd: 10,
|
||||
duration: 10,
|
||||
revision: 1,
|
||||
delay: 10,
|
||||
} as OntimeEvent,
|
||||
{ id: '4', type: SupportedEvent.Block } as OntimeBlock,
|
||||
{
|
||||
id: '5',
|
||||
type: SupportedEvent.Event,
|
||||
timeStart: 0,
|
||||
timeEnd: 10,
|
||||
duration: 10,
|
||||
revision: 1,
|
||||
delay: 0,
|
||||
} as OntimeEvent,
|
||||
];
|
||||
|
||||
const expected = [
|
||||
{
|
||||
id: '2',
|
||||
type: SupportedEvent.Event,
|
||||
timeStart: 10,
|
||||
timeEnd: 20,
|
||||
duration: 10,
|
||||
revision: 2,
|
||||
delay: 0,
|
||||
} as OntimeEvent,
|
||||
{
|
||||
id: '3',
|
||||
type: SupportedEvent.Event,
|
||||
timeStart: 10,
|
||||
timeEnd: 20,
|
||||
duration: 10,
|
||||
revision: 2,
|
||||
delay: 0,
|
||||
} as OntimeEvent,
|
||||
{ id: '4', type: SupportedEvent.Block } as OntimeBlock,
|
||||
{
|
||||
id: '5',
|
||||
type: SupportedEvent.Event,
|
||||
timeStart: 0,
|
||||
timeEnd: 10,
|
||||
duration: 10,
|
||||
revision: 1,
|
||||
delay: 0,
|
||||
} as OntimeEvent,
|
||||
];
|
||||
|
||||
const updatedRundown = _applyDelay(delayId, testRundown);
|
||||
expect(updatedRundown).toStrictEqual(expected);
|
||||
});
|
||||
it('applies negative delays', () => {
|
||||
const delayId = '1';
|
||||
const testRundown: OntimeRundown = [
|
||||
{ id: delayId, type: SupportedEvent.Delay, duration: -10 } as OntimeDelay,
|
||||
{
|
||||
id: '2',
|
||||
type: SupportedEvent.Event,
|
||||
timeStart: 0,
|
||||
timeEnd: 10,
|
||||
duration: 10,
|
||||
revision: 1,
|
||||
delay: -10,
|
||||
} as OntimeEvent,
|
||||
{
|
||||
id: '3',
|
||||
type: SupportedEvent.Event,
|
||||
timeStart: 20,
|
||||
timeEnd: 40,
|
||||
duration: 20,
|
||||
revision: 1,
|
||||
delay: -10,
|
||||
} as OntimeEvent,
|
||||
{ id: '4', type: SupportedEvent.Block } as OntimeBlock,
|
||||
{
|
||||
id: '5',
|
||||
type: SupportedEvent.Event,
|
||||
timeStart: 0,
|
||||
timeEnd: 10,
|
||||
duration: 10,
|
||||
revision: 1,
|
||||
delay: 0,
|
||||
} as OntimeEvent,
|
||||
];
|
||||
|
||||
const expected = [
|
||||
{
|
||||
id: '2',
|
||||
type: SupportedEvent.Event,
|
||||
timeStart: 0,
|
||||
timeEnd: 10,
|
||||
duration: 10,
|
||||
revision: 2,
|
||||
delay: 0,
|
||||
} as OntimeEvent,
|
||||
{
|
||||
id: '3',
|
||||
type: SupportedEvent.Event,
|
||||
timeStart: 10,
|
||||
timeEnd: 30,
|
||||
duration: 20,
|
||||
revision: 2,
|
||||
delay: 0,
|
||||
} as OntimeEvent,
|
||||
{ id: '4', type: SupportedEvent.Block } as OntimeBlock,
|
||||
{
|
||||
id: '5',
|
||||
type: SupportedEvent.Event,
|
||||
timeStart: 0,
|
||||
timeEnd: 10,
|
||||
duration: 10,
|
||||
revision: 1,
|
||||
delay: 0,
|
||||
} as OntimeEvent,
|
||||
];
|
||||
|
||||
const updatedRundown = _applyDelay(delayId, testRundown);
|
||||
expect(updatedRundown).toStrictEqual(expected);
|
||||
});
|
||||
it('maintains constant duration', () => {
|
||||
const delayId = '1';
|
||||
const testRundown: OntimeRundown = [
|
||||
{ id: delayId, type: SupportedEvent.Delay, duration: -30 } as OntimeDelay,
|
||||
{
|
||||
id: '2',
|
||||
type: SupportedEvent.Event,
|
||||
timeStart: 0,
|
||||
timeEnd: 10,
|
||||
duration: 10,
|
||||
revision: 1,
|
||||
delay: -30,
|
||||
} as OntimeEvent,
|
||||
{
|
||||
id: '3',
|
||||
type: SupportedEvent.Event,
|
||||
timeStart: 20,
|
||||
timeEnd: 40,
|
||||
duration: 20,
|
||||
revision: 1,
|
||||
delay: -30,
|
||||
} as OntimeEvent,
|
||||
];
|
||||
|
||||
const expected = [
|
||||
{
|
||||
id: '2',
|
||||
type: SupportedEvent.Event,
|
||||
timeStart: 0,
|
||||
timeEnd: 10,
|
||||
duration: 10,
|
||||
revision: 2,
|
||||
delay: 0,
|
||||
} as OntimeEvent,
|
||||
{
|
||||
id: '3',
|
||||
type: SupportedEvent.Event,
|
||||
timeStart: 0,
|
||||
timeEnd: 20,
|
||||
duration: 20,
|
||||
revision: 2,
|
||||
delay: 0,
|
||||
} as OntimeEvent,
|
||||
];
|
||||
|
||||
const updatedRundown = _applyDelay(delayId, testRundown);
|
||||
expect(updatedRundown).toStrictEqual(expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
import { isOntimeBlock, isOntimeEvent, OntimeRundown, SupportedEvent } from 'ontime-types';
|
||||
|
||||
import { deleteAtIndex } from '../utils/arrayUtils.js';
|
||||
|
||||
export function _applyDelay(eventId: string, rundown: OntimeRundown): OntimeRundown {
|
||||
const delayIndex = rundown.findIndex((event) => event.id === eventId);
|
||||
const delayEvent = rundown.at(delayIndex);
|
||||
|
||||
if (delayEvent.type !== SupportedEvent.Delay) {
|
||||
throw new Error('Given event ID is not a delay');
|
||||
}
|
||||
|
||||
const updatedRundown = [...rundown];
|
||||
const delayValue = delayEvent.duration;
|
||||
|
||||
if (delayValue === 0 || delayIndex === rundown.length - 1) {
|
||||
// nothing to apply
|
||||
return updatedRundown;
|
||||
}
|
||||
|
||||
for (let i = delayIndex + 1; i < rundown.length; i++) {
|
||||
const currentEvent = updatedRundown[i];
|
||||
|
||||
if (isOntimeBlock(currentEvent)) {
|
||||
break;
|
||||
} else if (isOntimeEvent(currentEvent)) {
|
||||
currentEvent.timeStart = Math.max(0, currentEvent.timeStart + delayValue);
|
||||
currentEvent.timeEnd = Math.max(currentEvent.duration, currentEvent.timeEnd + delayValue);
|
||||
if (currentEvent.delay) {
|
||||
currentEvent.delay = currentEvent.delay - delayValue;
|
||||
}
|
||||
currentEvent.revision += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return deleteAtIndex(delayIndex, updatedRundown);
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
OntimeBlock,
|
||||
OntimeDelay,
|
||||
OntimeEvent,
|
||||
OntimeRundown,
|
||||
Playback,
|
||||
SupportedEvent,
|
||||
} from 'ontime-types';
|
||||
@@ -18,6 +17,7 @@ import { sendRefetch } from '../../adapters/websocketAux.js';
|
||||
import { runtimeCacheStore } from '../../stores/cachingStore.js';
|
||||
import {
|
||||
cachedAdd,
|
||||
cachedApplyDelay,
|
||||
cachedClear,
|
||||
cachedDelete,
|
||||
cachedEdit,
|
||||
@@ -166,7 +166,7 @@ export async function addEvent(eventData: Partial<OntimeEvent> | Partial<OntimeD
|
||||
const id = generateId();
|
||||
|
||||
let insertIndex = 0;
|
||||
if (eventData?.after !== 'undefined') {
|
||||
if (eventData?.after !== undefined) {
|
||||
const index = DataProvider.getIndexOf(eventData.after);
|
||||
if (index < 0) {
|
||||
logger.warning(LogOrigin.Server, `Could not find event with id ${eventData.after}`);
|
||||
@@ -268,46 +268,14 @@ export async function reorderEvent(eventId: string, from: number, to: number) {
|
||||
return reorderedItem;
|
||||
}
|
||||
|
||||
export function _applyDelay(
|
||||
eventId: string,
|
||||
rundown: OntimeRundown,
|
||||
): {
|
||||
delayIndex: number | null;
|
||||
updatedRundown: OntimeRundown;
|
||||
} {
|
||||
const updatedRundown = [...rundown];
|
||||
let delayIndex = null;
|
||||
let delayValue = 0;
|
||||
export async function applyDelay(eventId: string) {
|
||||
await cachedApplyDelay(eventId);
|
||||
|
||||
for (const [index, event] of updatedRundown.entries()) {
|
||||
// look for delay
|
||||
if (delayIndex === null) {
|
||||
if (event.type === SupportedEvent.Delay && event.id === eventId) {
|
||||
delayValue = event.duration;
|
||||
delayIndex = index;
|
||||
// notify timer service of changed events
|
||||
updateTimer();
|
||||
|
||||
if (delayValue === 0) {
|
||||
// nothing to apply
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// once delay is found, apply delay value to all items until block or end
|
||||
if (event.type === SupportedEvent.Event) {
|
||||
updatedRundown[index] = {
|
||||
...event,
|
||||
timeStart: Math.max(0, event.timeStart + delayValue),
|
||||
timeEnd: Math.max(event.duration, event.timeEnd + delayValue),
|
||||
revision: event.revision + 1,
|
||||
};
|
||||
} else if (event.type === SupportedEvent.Block) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return { delayIndex, updatedRundown };
|
||||
// advice socket subscribers of change
|
||||
sendRefetch();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -326,22 +294,6 @@ export async function swapEvents(from: string, to: string) {
|
||||
sendRefetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* applies delay value for given event
|
||||
* @param eventId
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function applyDelay(eventId: string) {
|
||||
const rundown: OntimeRundown = DataProvider.getRundown();
|
||||
const { delayIndex, updatedRundown } = _applyDelay(eventId, rundown);
|
||||
if (delayIndex === null) {
|
||||
throw new Error(`Delay event with ID ${eventId} not found`);
|
||||
}
|
||||
|
||||
await DataProvider.setRundown(updatedRundown);
|
||||
await deleteEvent(eventId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces update in the store
|
||||
* Called when we make changes to the rundown object
|
||||
|
||||
@@ -1,319 +0,0 @@
|
||||
import { EndAction, OntimeRundown, SupportedEvent, TimerType } from 'ontime-types';
|
||||
import { _applyDelay } from '../RundownService.js';
|
||||
|
||||
describe('applyDelay()', () => {
|
||||
it('applies its duration to following events', () => {
|
||||
const rundown: OntimeRundown = [
|
||||
{
|
||||
title: '',
|
||||
subtitle: '',
|
||||
presenter: '',
|
||||
note: '',
|
||||
endAction: EndAction.None,
|
||||
timerType: TimerType.CountDown,
|
||||
timeStart: 600000,
|
||||
timeEnd: 1200000,
|
||||
duration: 600000,
|
||||
isPublic: true,
|
||||
skip: false,
|
||||
colour: '',
|
||||
user0: '',
|
||||
user1: '',
|
||||
user2: '',
|
||||
user3: '',
|
||||
user4: '',
|
||||
user5: '',
|
||||
user6: '',
|
||||
user7: '',
|
||||
user8: '',
|
||||
user9: '',
|
||||
type: SupportedEvent.Event,
|
||||
revision: 4,
|
||||
id: '659e1',
|
||||
},
|
||||
{
|
||||
duration: 600000,
|
||||
type: SupportedEvent.Delay,
|
||||
revision: 0,
|
||||
id: '07986',
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
subtitle: '',
|
||||
presenter: '',
|
||||
note: '',
|
||||
endAction: EndAction.None,
|
||||
timerType: TimerType.CountDown,
|
||||
timeStart: 600000,
|
||||
timeEnd: 1200000,
|
||||
duration: 600000,
|
||||
isPublic: true,
|
||||
skip: false,
|
||||
colour: '',
|
||||
user0: '',
|
||||
user1: '',
|
||||
user2: '',
|
||||
user3: '',
|
||||
user4: '',
|
||||
user5: '',
|
||||
user6: '',
|
||||
user7: '',
|
||||
user8: '',
|
||||
user9: '',
|
||||
type: SupportedEvent.Event,
|
||||
revision: 4,
|
||||
id: 'd48c2',
|
||||
},
|
||||
];
|
||||
|
||||
const eventId = rundown[1].id;
|
||||
const { delayIndex, updatedRundown } = _applyDelay(eventId, rundown);
|
||||
|
||||
expect(delayIndex).toBe(1);
|
||||
// we do not delay delays anymore
|
||||
expect(updatedRundown.length).toBe(3);
|
||||
expect(rundown.length).toBe(3);
|
||||
expect(updatedRundown[0].timeStart).toBe(rundown[0].timeStart);
|
||||
expect(updatedRundown[2].timeStart).toBe(rundown[1].duration + rundown[2].timeStart);
|
||||
expect(updatedRundown[2].timeEnd).toBe(rundown[1].duration + rundown[2].timeEnd);
|
||||
});
|
||||
it('stops propagating on blocks', () => {
|
||||
const rundown: OntimeRundown = [
|
||||
{
|
||||
title: '',
|
||||
subtitle: '',
|
||||
presenter: '',
|
||||
note: '',
|
||||
endAction: EndAction.None,
|
||||
timerType: TimerType.CountDown,
|
||||
timeStart: 600000,
|
||||
timeEnd: 1200000,
|
||||
duration: 600000,
|
||||
isPublic: true,
|
||||
skip: false,
|
||||
colour: '',
|
||||
user0: '',
|
||||
user1: '',
|
||||
user2: '',
|
||||
user3: '',
|
||||
user4: '',
|
||||
user5: '',
|
||||
user6: '',
|
||||
user7: '',
|
||||
user8: '',
|
||||
user9: '',
|
||||
type: SupportedEvent.Event,
|
||||
revision: 0,
|
||||
id: '659e1',
|
||||
},
|
||||
{
|
||||
duration: 600000,
|
||||
type: SupportedEvent.Delay,
|
||||
revision: 0,
|
||||
id: '07986',
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
subtitle: '',
|
||||
presenter: '',
|
||||
note: '',
|
||||
endAction: EndAction.None,
|
||||
timerType: TimerType.CountDown,
|
||||
timeStart: 600000,
|
||||
timeEnd: 1200000,
|
||||
duration: 600000,
|
||||
isPublic: true,
|
||||
skip: false,
|
||||
colour: '',
|
||||
user0: '',
|
||||
user1: '',
|
||||
user2: '',
|
||||
user3: '',
|
||||
user4: '',
|
||||
user5: '',
|
||||
user6: '',
|
||||
user7: '',
|
||||
user8: '',
|
||||
user9: '',
|
||||
type: SupportedEvent.Event,
|
||||
revision: 0,
|
||||
id: 'd48c2',
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
type: SupportedEvent.Block,
|
||||
id: '9870d',
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
subtitle: '',
|
||||
presenter: '',
|
||||
note: '',
|
||||
endAction: EndAction.None,
|
||||
timerType: TimerType.CountDown,
|
||||
timeStart: 1200000,
|
||||
timeEnd: 1800000,
|
||||
duration: 600000,
|
||||
isPublic: true,
|
||||
skip: false,
|
||||
colour: '',
|
||||
user0: '',
|
||||
user1: '',
|
||||
user2: '',
|
||||
user3: '',
|
||||
user4: '',
|
||||
user5: '',
|
||||
user6: '',
|
||||
user7: '',
|
||||
user8: '',
|
||||
user9: '',
|
||||
type: SupportedEvent.Event,
|
||||
revision: 2,
|
||||
id: '2f185',
|
||||
},
|
||||
];
|
||||
|
||||
const eventId = rundown[1].id;
|
||||
const { updatedRundown } = _applyDelay(eventId, rundown);
|
||||
|
||||
expect(updatedRundown[0].timeStart).toBe(rundown[0].timeStart);
|
||||
expect(updatedRundown[2].timeStart).toBe(rundown[1].duration + rundown[2].timeStart);
|
||||
expect(updatedRundown[4].timeStart).toBe(rundown[4].timeStart);
|
||||
});
|
||||
it('only applies given delay', () => {
|
||||
const rundown: OntimeRundown = [
|
||||
{
|
||||
title: '',
|
||||
subtitle: '',
|
||||
presenter: '',
|
||||
note: '',
|
||||
endAction: EndAction.None,
|
||||
timerType: TimerType.CountDown,
|
||||
timeStart: 600000,
|
||||
timeEnd: 1200000,
|
||||
duration: 600000,
|
||||
isPublic: true,
|
||||
skip: false,
|
||||
colour: '',
|
||||
user0: '',
|
||||
user1: '',
|
||||
user2: '',
|
||||
user3: '',
|
||||
user4: '',
|
||||
user5: '',
|
||||
user6: '',
|
||||
user7: '',
|
||||
user8: '',
|
||||
user9: '',
|
||||
type: SupportedEvent.Event,
|
||||
revision: 0,
|
||||
id: '659e1',
|
||||
},
|
||||
{
|
||||
duration: 600000,
|
||||
type: SupportedEvent.Delay,
|
||||
revision: 0,
|
||||
id: '07986',
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
subtitle: '',
|
||||
presenter: '',
|
||||
note: '',
|
||||
endAction: EndAction.None,
|
||||
timerType: TimerType.CountDown,
|
||||
timeStart: 1200000,
|
||||
timeEnd: 1200000,
|
||||
duration: 0,
|
||||
isPublic: true,
|
||||
skip: false,
|
||||
colour: '',
|
||||
user0: '',
|
||||
user1: '',
|
||||
user2: '',
|
||||
user3: '',
|
||||
user4: '',
|
||||
user5: '',
|
||||
user6: '',
|
||||
user7: '',
|
||||
user8: '',
|
||||
user9: '',
|
||||
type: SupportedEvent.Event,
|
||||
revision: 0,
|
||||
id: '1c48f',
|
||||
},
|
||||
{
|
||||
duration: 1200000,
|
||||
type: SupportedEvent.Delay,
|
||||
revision: 0,
|
||||
id: '7db42',
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
subtitle: '',
|
||||
presenter: '',
|
||||
note: '',
|
||||
endAction: EndAction.None,
|
||||
timerType: TimerType.CountDown,
|
||||
timeStart: 600000,
|
||||
timeEnd: 1200000,
|
||||
duration: 600000,
|
||||
isPublic: true,
|
||||
skip: false,
|
||||
colour: '',
|
||||
user0: '',
|
||||
user1: '',
|
||||
user2: '',
|
||||
user3: '',
|
||||
user4: '',
|
||||
user5: '',
|
||||
user6: '',
|
||||
user7: '',
|
||||
user8: '',
|
||||
user9: '',
|
||||
type: SupportedEvent.Event,
|
||||
revision: 0,
|
||||
id: 'd48c2',
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
type: SupportedEvent.Block,
|
||||
id: '9870d',
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
subtitle: '',
|
||||
presenter: '',
|
||||
note: '',
|
||||
endAction: EndAction.None,
|
||||
timerType: TimerType.CountDown,
|
||||
timeStart: 1200000,
|
||||
timeEnd: 1800000,
|
||||
duration: 600000,
|
||||
isPublic: true,
|
||||
skip: false,
|
||||
colour: '',
|
||||
user0: '',
|
||||
user1: '',
|
||||
user2: '',
|
||||
user3: '',
|
||||
user4: '',
|
||||
user5: '',
|
||||
user6: '',
|
||||
user7: '',
|
||||
user8: '',
|
||||
user9: '',
|
||||
type: SupportedEvent.Event,
|
||||
revision: 0,
|
||||
id: '2f185',
|
||||
},
|
||||
];
|
||||
|
||||
const eventId = rundown[1].id;
|
||||
const { updatedRundown } = _applyDelay(eventId, rundown);
|
||||
|
||||
expect(updatedRundown[0].timeStart).toBe(rundown[0].timeStart);
|
||||
expect(updatedRundown[2].timeStart).toBe(rundown[1].duration + rundown[2].timeStart);
|
||||
expect(updatedRundown[4].timeStart).toBe(rundown[1].duration + rundown[4].timeStart);
|
||||
});
|
||||
});
|
||||
@@ -1,10 +1,20 @@
|
||||
import { OntimeBlock, OntimeDelay, OntimeEvent, OntimeRundown, SupportedEvent } from 'ontime-types';
|
||||
import {
|
||||
isOntimeBlock,
|
||||
isOntimeDelay,
|
||||
isOntimeEvent,
|
||||
OntimeBlock,
|
||||
OntimeDelay,
|
||||
OntimeEvent,
|
||||
OntimeRundown,
|
||||
OntimeRundownEntry,
|
||||
} from 'ontime-types';
|
||||
import { swapOntimeEvents } from 'ontime-utils';
|
||||
|
||||
import { DataProvider } from '../../classes/data-provider/DataProvider.js';
|
||||
import { getCached, runtimeCacheStore } from '../../stores/cachingStore.js';
|
||||
import { isProduction } from '../../setup.js';
|
||||
import { deleteAtIndex, insertAtIndex, reorderArray } from '../../utils/arrayUtils.js';
|
||||
import { swapOntimeEvents } from 'ontime-utils';
|
||||
import { _applyDelay } from '../delayUtils.js';
|
||||
|
||||
/**
|
||||
* Key of rundown in cache
|
||||
@@ -51,7 +61,7 @@ export async function cachedAdd(eventIndex: number, event: OntimeEvent | OntimeD
|
||||
let newDelayedRundown = insertAtIndex(eventIndex, event, delayedRundown);
|
||||
|
||||
// update delay cache
|
||||
if (event.type === SupportedEvent.Event) {
|
||||
if (isOntimeEvent(event)) {
|
||||
// if it is an event, we need its delay
|
||||
(newDelayedRundown[eventIndex] as OntimeEvent).delay = getDelayAt(eventIndex, newDelayedRundown);
|
||||
} else {
|
||||
@@ -79,22 +89,20 @@ export async function cachedEdit(
|
||||
}
|
||||
|
||||
const updatedRundown = DataProvider.getRundown();
|
||||
const newEvent = { ...updatedRundown[indexInMemory], ...patchObject };
|
||||
if (newEvent.type === SupportedEvent.Event) {
|
||||
const newEvent = { ...updatedRundown[indexInMemory], ...patchObject } as OntimeRundownEntry;
|
||||
if (isOntimeEvent(newEvent)) {
|
||||
newEvent.revision++;
|
||||
}
|
||||
// @ts-expect-error -- this merge is safe
|
||||
updatedRundown[indexInMemory] = newEvent;
|
||||
|
||||
let newDelayedRundown = getDelayedRundown();
|
||||
if (newDelayedRundown?.[indexInMemory].id !== newEvent.id) {
|
||||
invalidateFromError();
|
||||
} else {
|
||||
// @ts-expect-error -- this merge is safe
|
||||
newDelayedRundown[indexInMemory] = newEvent;
|
||||
if (newEvent.type === SupportedEvent.Event) {
|
||||
if (isOntimeEvent(newEvent)) {
|
||||
(newDelayedRundown[indexInMemory] as OntimeEvent).delay = getDelayAt(indexInMemory, newDelayedRundown);
|
||||
} else if (newEvent.type === SupportedEvent.Delay) {
|
||||
} else if (isOntimeDelay(newEvent)) {
|
||||
// blocks have no reason to change the rundown, from delays we need to recalculate
|
||||
newDelayedRundown = calculateRuntimeDelaysFromIndex(indexInMemory, newDelayedRundown);
|
||||
}
|
||||
@@ -124,13 +132,12 @@ export async function cachedDelete(eventId: string) {
|
||||
}
|
||||
|
||||
let updatedRundown = DataProvider.getRundown();
|
||||
const eventType = updatedRundown[eventIndex].type;
|
||||
updatedRundown = deleteAtIndex(eventIndex, updatedRundown);
|
||||
if (eventId !== delayedRundown[eventIndex].id) {
|
||||
invalidateFromError();
|
||||
} else {
|
||||
delayedRundown = deleteAtIndex(eventIndex, delayedRundown);
|
||||
if (eventType === SupportedEvent.Delay || eventType === SupportedEvent.Block) {
|
||||
if (isOntimeDelay(updatedRundown[eventIndex]) || isOntimeBlock(updatedRundown[eventIndex])) {
|
||||
// for events, we do not have to worry
|
||||
// the following event, would have taken the place of the deleted event by now
|
||||
delayedRundown = calculateRuntimeDelaysFromIndex(eventIndex, delayedRundown);
|
||||
@@ -205,6 +212,19 @@ export async function cachedSwap(fromEventId: string, toEventId: string) {
|
||||
await DataProvider.setRundown(rundownToUpdate);
|
||||
}
|
||||
|
||||
export async function cachedApplyDelay(eventId: string) {
|
||||
// update persisted rundown
|
||||
const rundown: OntimeRundown = DataProvider.getRundown();
|
||||
const persistedRundown = _applyDelay(eventId, rundown);
|
||||
|
||||
const delayedRundown = getDelayedRundown();
|
||||
const cachedRundown = _applyDelay(eventId, delayedRundown);
|
||||
|
||||
// update
|
||||
runtimeCacheStore.setCached(delayedRundownCacheKey, cachedRundown);
|
||||
await DataProvider.setRundown(persistedRundown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates all delays in a given rundown
|
||||
* @param rundown
|
||||
@@ -214,11 +234,11 @@ export function calculateRuntimeDelays(rundown: OntimeRundown) {
|
||||
const updatedRundown = [...rundown];
|
||||
|
||||
for (const [index, event] of updatedRundown.entries()) {
|
||||
if (event.type === SupportedEvent.Delay) {
|
||||
if (isOntimeDelay(event)) {
|
||||
accumulatedDelay += event.duration;
|
||||
} else if (event.type === SupportedEvent.Block) {
|
||||
} else if (isOntimeBlock(event)) {
|
||||
accumulatedDelay = 0;
|
||||
} else if (event.type === SupportedEvent.Event) {
|
||||
} else if (isOntimeEvent(event)) {
|
||||
updatedRundown[index] = {
|
||||
...event,
|
||||
delay: accumulatedDelay,
|
||||
@@ -243,15 +263,15 @@ export function calculateRuntimeDelaysFromIndex(eventIndex: number, rundown: Ont
|
||||
|
||||
for (let i = eventIndex; i < rundown.length; i++) {
|
||||
const event = rundown[i];
|
||||
if (event.type === SupportedEvent.Delay) {
|
||||
if (isOntimeDelay(event)) {
|
||||
accumulatedDelay += event.duration;
|
||||
} else if (event.type === SupportedEvent.Block) {
|
||||
} else if (isOntimeBlock(event)) {
|
||||
if (i === eventIndex) {
|
||||
accumulatedDelay = 0;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else if (event.type === SupportedEvent.Event) {
|
||||
} else if (isOntimeEvent(event)) {
|
||||
updatedRundown[i] = {
|
||||
...event,
|
||||
delay: accumulatedDelay,
|
||||
@@ -284,11 +304,11 @@ export function getDelayAt(eventIndex: number, rundown: OntimeRundown): number {
|
||||
// we need to check the event before
|
||||
const event = rundown[eventIndex - 1];
|
||||
|
||||
if (event.type === SupportedEvent.Delay) {
|
||||
if (isOntimeDelay(event)) {
|
||||
return event.duration + getDelayAt(eventIndex - 1, rundown);
|
||||
} else if (event.type === SupportedEvent.Block) {
|
||||
} else if (isOntimeBlock(event)) {
|
||||
return 0;
|
||||
} else if (event.type === SupportedEvent.Event) {
|
||||
} else if (isOntimeEvent(event)) {
|
||||
return event.delay ?? 0;
|
||||
}
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user