refactor: use runtime metadate in runtimeState (#1653)

* change load to use metadata

* update roll to use metadata

* remove filterTimedEvents

* add comment

* atempth to refactor rollUtils

* refactor util functions
This commit is contained in:
Alex Christoffer Rasmussen
2025-06-22 10:32:48 +02:00
committed by GitHub
parent e090bde8bb
commit 14a99ce27b
7 changed files with 698 additions and 395 deletions
+36 -40
View File
@@ -1,12 +1,9 @@
import {
CurrentBlockState,
EntryId,
isPlayableEvent,
MaybeNumber,
MaybeString,
OffsetMode,
OntimeBlock,
OntimeEvent,
PlayableEvent,
Playback,
Rundown,
@@ -28,7 +25,8 @@ import {
} from '../services/timerUtils.js';
import { loadRoll, normaliseRollStart } from '../services/rollUtils.js';
import { timerConfig } from '../setup/config.js';
import { filterTimedEvents } from '../services/runtime-service/rundownService.utils.js';
import { RundownMetadata } from '../api-data/rundown/rundown.types.js';
import { getPlayableIndexFromTimedIndex } from '../api-data/rundown/rundown.utils.js';
export type RuntimeState = {
clock: number; // realtime clock
@@ -171,12 +169,13 @@ export function updateRundownData(rundownData: RundownData) {
export function load(
event: PlayableEvent,
rundown: Rundown,
timedEventOrder: EntryId[],
metadata: RundownMetadata,
initialData?: Partial<TimerState & RestorePoint>,
): boolean {
clearEventData();
if (timedEventOrder.length === 0 || !isPlayableEvent(event)) {
const { timedEventOrder } = metadata;
if (timedEventOrder.length === 0) {
return false;
}
@@ -186,18 +185,16 @@ export function load(
return false;
}
// TODO(remove public): it is wasteful to recreate the object
const timedEvents = filterTimedEvents(rundown, timedEventOrder);
// load events in memory along with their data
loadNow(timedEvents, eventIndex);
loadNext(timedEvents, eventIndex);
loadNow(rundown, metadata, eventIndex);
loadNext(rundown, metadata, eventIndex);
loadBlock(rundown);
// update state
runtimeState.timer.playback = Playback.Armed;
runtimeState.timer.duration = calculateDuration(event.timeStart, event.timeEnd);
runtimeState.timer.current = getCurrent(runtimeState);
runtimeState.runtime.numEvents = timedEvents.length;
runtimeState.runtime.numEvents = metadata.timedEventOrder.length;
// patch with potential provided data
if (initialData) {
@@ -220,7 +217,11 @@ export function load(
/**
* Loads current event and its public counterpart
*/
export function loadNow(timedEvents: OntimeEvent[], eventIndex: MaybeNumber = runtimeState.runtime.selectedEventIndex) {
export function loadNow(
rundown: Rundown,
metadata: RundownMetadata,
eventIndex: MaybeNumber = runtimeState.runtime.selectedEventIndex,
) {
if (eventIndex === null) {
// reset the state to indicate there is no selection
runtimeState.runtime.selectedEventIndex = null;
@@ -228,7 +229,7 @@ export function loadNow(timedEvents: OntimeEvent[], eventIndex: MaybeNumber = ru
return;
}
const event = timedEvents[eventIndex] as PlayableEvent;
const event = rundown.entries[metadata.timedEventOrder[eventIndex]] as PlayableEvent;
runtimeState.runtime.selectedEventIndex = eventIndex;
runtimeState.eventNow = event;
}
@@ -237,7 +238,8 @@ export function loadNow(timedEvents: OntimeEvent[], eventIndex: MaybeNumber = ru
* Loads the next event and its public counterpart
*/
export function loadNext(
timedEvents: OntimeEvent[],
rundown: Rundown,
metadata: RundownMetadata,
eventIndex: MaybeNumber = runtimeState.runtime.selectedEventIndex,
) {
if (eventIndex === null) {
@@ -245,27 +247,22 @@ export function loadNext(
runtimeState.eventNext = null;
return;
}
const nowPlayableIndex = getPlayableIndexFromTimedIndex(metadata, eventIndex);
// temporarily reset this value to simplify loop logic
runtimeState.eventNext = null;
//TODO: do we already have a it as a list of not skipped events
for (let i = eventIndex + 1; i < timedEvents.length; i++) {
const event = timedEvents[i];
// we dont deal with events that are not playable
if (!isPlayableEvent(event)) {
continue;
}
runtimeState.eventNext = event;
if (!nowPlayableIndex || nowPlayableIndex > metadata.playableEventOrder.length - 2) {
// we cound not find the event now or the event now is the last playable event
runtimeState.eventNext = null;
return;
}
const nextId = metadata.playableEventOrder[nowPlayableIndex + 1];
runtimeState.eventNext = rundown.entries[nextId] as PlayableEvent;
}
/**
* Resume from restore point
*/
export function resume(restorePoint: RestorePoint, event: PlayableEvent, rundown: Rundown, timedEventOrder: EntryId[]) {
load(event, rundown, timedEventOrder, restorePoint);
export function resume(restorePoint: RestorePoint, event: PlayableEvent, rundown: Rundown, metadata: RundownMetadata) {
load(event, rundown, metadata, restorePoint);
}
/**
@@ -325,12 +322,12 @@ export function updateLoaded(event?: PlayableEvent): string | undefined {
/**
* Used in situations when we want to hot-reload all events without interrupting timer
*/
export function updateAll(rundown: Rundown, timedEventsOrder: EntryId[]) {
const timedEvents = filterTimedEvents(rundown, timedEventsOrder);
// TODO(remove public): we dont need to make the timedEvents object, we pass primitives and let the functions handle it
const eventNowIndex = timedEventsOrder.findIndex((id) => id === runtimeState.eventNow?.id);
loadNow(timedEvents, eventNowIndex >= 0 ? eventNowIndex : undefined);
loadNext(timedEvents, eventNowIndex >= 0 ? eventNowIndex : undefined);
export function updateAll(rundown: Rundown, metadata: RundownMetadata) {
// event now might have moved so we find the event now id and recalculate the the index again
const eventNowIndex = metadata.timedEventOrder.findIndex((id) => id === runtimeState.eventNow?.id);
loadNow(rundown, metadata, eventNowIndex >= 0 ? eventNowIndex : undefined);
loadNext(rundown, metadata, eventNowIndex >= 0 ? eventNowIndex : undefined);
updateLoaded(runtimeState.eventNow ?? undefined);
loadBlock(rundown);
}
@@ -542,7 +539,7 @@ export function update(): UpdateResult {
export function roll(
rundown: Rundown,
timedEventOrder: EntryId[],
metadata: RundownMetadata,
offset = 0,
): { eventId: MaybeString; didStart: boolean } {
// 1. if an event is running, we simply take over the playback
@@ -601,8 +598,7 @@ export function roll(
}
// 3. if there is no event running, we need to find the next event
const timedEvents = filterTimedEvents(rundown, timedEventOrder);
if (timedEvents.length === 0) {
if (metadata.playableEventOrder.length === 0) {
throw new Error('No playable events found');
}
@@ -613,16 +609,16 @@ export function roll(
runtimeState.runtime.offset = offset;
const offsetClock = runtimeState.clock + runtimeState.runtime.offset;
const { index, isPending } = loadRoll(timedEvents, offsetClock);
const { index, isPending } = loadRoll(rundown, metadata, offsetClock);
// load events in memory along with their data
loadNow(timedEvents, index);
loadNext(timedEvents, index);
loadNow(rundown, metadata, index);
loadNext(rundown, metadata, index);
loadBlock(rundown);
// update roll state
runtimeState.timer.playback = Playback.Roll;
runtimeState.runtime.numEvents = timedEvents.length;
runtimeState.runtime.numEvents = metadata.timedEventOrder.length;
// in roll mode spec, there should always be something to load
// as long as playableEvents is not empty