Files
ontime/apps/server/src/services/rollUtils.ts
T
Carlos Valente 1849b4d39f Deps migration (#1988)
* 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>
2026-03-08 16:22:12 +01:00

101 lines
3.4 KiB
TypeScript

import { MaybeNumber, PlayableEvent, Rundown } from 'ontime-types';
import { checkIsNow, dayInMs } from 'ontime-utils';
import { RundownMetadata } from '../api-data/rundown/rundown.types.js';
import { getTimedIndexFromPlayableIndex } from '../api-data/rundown/rundown.utils.js';
import { normaliseEndTime } from './timerUtils.js';
/**
* Finds current event in a rolling rundown
*/
export function loadRoll(
rundown: Rundown,
metadata: RundownMetadata,
timeNow: number,
): {
event: PlayableEvent | null;
index: MaybeNumber;
isPending?: boolean;
} {
const firstEventId = metadata.playableEventOrder[0];
if (!firstEventId) {
return { event: null, index: null };
}
// we know we are in the middle of the rundown and we need to find the current event
// account for number of times we went over midnight
let daySpan = 0;
for (let i = 0; i < metadata.playableEventOrder.length; i++) {
const eventId = metadata.playableEventOrder[i];
if (!eventId) continue;
const event = rundown.entries[eventId] as PlayableEvent;
// rolling into events of 0 duration would make the playback be stuck
if (event.duration === 0) {
continue;
}
const correctedDays = dayInMs * daySpan;
const correctedStart = event.timeStart + correctedDays;
const correctedEnd = event.timeEnd + correctedDays;
/**
* there are 3 possible states for an event
* 1. event is already finished
* 2. event is running
* 3. event is running (midnight edge case)
* 4. event is in the future
*/
// 1. event is already finished
// when does the event end (handle midnight)
const normalEnd = normaliseEndTime(correctedStart, correctedEnd);
if (normalEnd <= timeNow) {
// keep track of day progress
if (event.timeStart > event.timeEnd) {
daySpan++;
}
continue;
}
// 2. event is running and is the first event in our time slot
const isFromDayBefore = normalEnd > dayInMs && timeNow < event.timeEnd;
const hasStarted = isFromDayBefore || timeNow >= event.timeStart;
if (hasStarted) {
return { event, index: getTimedIndexFromPlayableIndex(metadata, i) };
}
// 3. check if an overnight event that comes later is currently playing
for (let j = i + 1; j < metadata.playableEventOrder.length; j++) {
const futureEventId = metadata.playableEventOrder[j];
if (!futureEventId) continue;
const futureEvent = rundown.entries[futureEventId] as PlayableEvent;
if (!futureEvent || futureEvent.duration === 0) continue;
const crossesMidnight = futureEvent.timeStart > futureEvent.timeEnd;
if (!crossesMidnight) continue;
// check if timeNow is inside this overnight event
const isRunning = checkIsNow(futureEvent.timeStart, futureEvent.timeEnd, timeNow);
if (isRunning) {
return { event: futureEvent, index: getTimedIndexFromPlayableIndex(metadata, j) };
}
}
// 4. event will run in the future
return { event, index: getTimedIndexFromPlayableIndex(metadata, i), isPending: true };
}
// in case we were unable to find anything, we load the first event
return { event: rundown.entries[firstEventId] as PlayableEvent, index: 0, isPending: true };
}
/**
* Utility function, checks whether the event start is the day after
*/
export function normaliseRollStart(start: number, clock: number) {
return start < clock ? start + dayInMs : start;
}