mirror of
https://github.com/cpvalente/ontime.git
synced 2026-09-11 00:59:35 +00:00
1849b4d39f
* chore: migrate eslint to oxlint * chore: migrate prettier to oxfmt * chore: migrate typescript * chore: toThrow should have a expected value * chore: cast test value as Day * chore: small title fix * chore: mocks should be hoisted * chore: incorrect async useage * chore: test should be inside description * chore: test sohuld include an expeced * chore: oxfmt --------- Co-authored-by: alex-Arc <omnivox@LAPTOP-RC5SNBVV.localdomain>
167 lines
5.1 KiB
TypeScript
167 lines
5.1 KiB
TypeScript
import { deepEqual } from 'fast-equals';
|
|
import {
|
|
EntryId,
|
|
MaybeNumber,
|
|
Offset,
|
|
OntimeEvent,
|
|
Rundown,
|
|
TimerState,
|
|
TimerType,
|
|
isOntimeEvent,
|
|
isPlayableEvent,
|
|
} from 'ontime-types';
|
|
import { millisToSeconds } from 'ontime-utils';
|
|
|
|
export function isNewSecond(
|
|
previousValue: MaybeNumber | undefined,
|
|
currentValue: MaybeNumber | undefined,
|
|
direction: TimerType.CountDown | TimerType.CountUp = TimerType.CountDown,
|
|
) {
|
|
return millisToSeconds(currentValue ?? null, direction) !== millisToSeconds(previousValue ?? null, direction);
|
|
}
|
|
|
|
/**
|
|
* Checks whether we should update the clock value
|
|
* - we have rolled into a new seconds unit
|
|
*/
|
|
export function getShouldClockUpdate(previousUpdate: number, now: number): boolean {
|
|
const newSeconds = millisToSeconds(now, TimerType.CountUp) !== millisToSeconds(previousUpdate, TimerType.CountUp);
|
|
return newSeconds;
|
|
}
|
|
|
|
/**
|
|
* Checks whether we should update the timer values
|
|
* - `current` and `secondaryTimer` trigger on seconds roll over
|
|
* - the rest trigger on any change
|
|
* - `elapsed` and `expectedFinish` is not checked
|
|
*/
|
|
export function getShouldTimerUpdate(previousValue: TimerState | undefined, currentValue: TimerState): boolean {
|
|
if (previousValue === undefined) return true;
|
|
return (
|
|
// current timer value
|
|
isNewSecond(previousValue.current, currentValue.current) ||
|
|
//secondary timer value, when in pre-roll
|
|
isNewSecond(previousValue.secondaryTimer, currentValue.secondaryTimer) ||
|
|
// other timer values that could have changed
|
|
previousValue.addedTime !== currentValue.addedTime ||
|
|
previousValue.duration !== currentValue.duration ||
|
|
previousValue.phase !== currentValue.phase ||
|
|
previousValue.playback !== currentValue.playback ||
|
|
previousValue.startedAt !== currentValue.startedAt
|
|
// elapsed - this would be the direct invert of current value so no need to check
|
|
// expectedFinish - this will be moved out by the current value going into over time, no need to check
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Checks whether we should update the offset values
|
|
* - `mode` triggers update
|
|
* - `absolute`, `relative`, `expected**End` are ticked with `didDependencyUpdate`
|
|
*/
|
|
export function getShouldOffsetUpdate(
|
|
previousValue: Offset | undefined,
|
|
currentValue: Offset,
|
|
didDependencyUpdate: boolean,
|
|
): boolean {
|
|
if (previousValue === undefined) return true;
|
|
if (previousValue.mode !== currentValue.mode) return true;
|
|
return didDependencyUpdate && !deepEqual(previousValue, currentValue);
|
|
}
|
|
|
|
/**
|
|
* finds the previous playable event, if it exists
|
|
*/
|
|
export function findPreviousPlayableId(playableEventsOrder: EntryId[], currentEventId?: string): EntryId | undefined {
|
|
if (!playableEventsOrder.length) {
|
|
return;
|
|
}
|
|
|
|
// if there is no event running, go to first
|
|
if (!currentEventId) {
|
|
return getFirstPlayableId(playableEventsOrder);
|
|
}
|
|
|
|
const currentIndex = playableEventsOrder.findIndex((eventId) => eventId === currentEventId);
|
|
|
|
if (currentIndex < 1) {
|
|
return getFirstPlayableId(playableEventsOrder);
|
|
}
|
|
|
|
return playableEventsOrder.at(currentIndex - 1);
|
|
}
|
|
|
|
/**
|
|
* finds the next event playable event, if it exists
|
|
*/
|
|
export function findNextPlayableId(playableEventsOrder: EntryId[], currentEventId?: string): EntryId | undefined {
|
|
if (!playableEventsOrder.length) {
|
|
return;
|
|
}
|
|
|
|
// if there is no event running, go to first
|
|
if (!currentEventId) {
|
|
return getFirstPlayableId(playableEventsOrder);
|
|
}
|
|
|
|
const currentIndex = playableEventsOrder.findIndex((eventId) => eventId === currentEventId);
|
|
if (currentIndex === -1 || currentIndex >= playableEventsOrder.length - 1) {
|
|
return getFirstPlayableId(playableEventsOrder);
|
|
}
|
|
|
|
return playableEventsOrder.at(currentIndex + 1);
|
|
}
|
|
|
|
/**
|
|
* returns next event that matches a given cue
|
|
* then loops from the start
|
|
*/
|
|
export function findNextPlayableWithCue(
|
|
rundown: Rundown,
|
|
timedEventsOrder: EntryId[],
|
|
targetCue: string,
|
|
currentEventIndex = 0,
|
|
allowCurrent = false,
|
|
): OntimeEvent | undefined {
|
|
const startFromIndex = allowCurrent ? currentEventIndex : currentEventIndex + 1;
|
|
|
|
for (let i = startFromIndex; i < timedEventsOrder.length; i++) {
|
|
const eventId = timedEventsOrder[i];
|
|
const event = rundown.entries[eventId];
|
|
if (isOntimeEvent(event) && isPlayableEvent(event) && event.cue === targetCue) {
|
|
return event;
|
|
}
|
|
}
|
|
|
|
for (let i = 0; i < startFromIndex; i++) {
|
|
const eventId = timedEventsOrder[i];
|
|
const event = rundown.entries[eventId];
|
|
if (isOntimeEvent(event) && isPlayableEvent(event) && event.cue === targetCue) {
|
|
return event;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Utility returns the first playable event in rundown, if it exists
|
|
*/
|
|
export function getFirstPlayableId(playableOrder: EntryId[]): EntryId | undefined {
|
|
return playableOrder.at(0);
|
|
}
|
|
|
|
/**
|
|
* This is a utility function to return an event at a given index
|
|
* It uses the timedEventOrder so that the index is the same as the one in the UI
|
|
*/
|
|
export function getEventAtIndex(
|
|
rundown: Rundown,
|
|
timedEventOrder: EntryId[],
|
|
eventIndex: number,
|
|
): OntimeEvent | undefined {
|
|
const eventId = timedEventOrder[eventIndex];
|
|
if (!eventId) {
|
|
return undefined;
|
|
}
|
|
|
|
return rundown.entries[eventId] as OntimeEvent | undefined;
|
|
}
|