mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-14 11:53:49 +00:00
722e045b20
* Revert "refactor: add version to session endpoint" This reverts commitcd40a6e55e. * Revert "Remove public event feature (#1645)" This reverts commit08d9e24871. * Revert "Fix: rearrange playing event (#1640)" This reverts commit0649678dca. * Revert "refactor: style tweaks to edit css modal" This reverts commita00ec2d02a. * Revert "refactor: remove usages of framer-motion" This reverts commit54a74ccc2a. * Revert "Upgrade expressjs (#1633)" This reverts commit6f3ab274bd. * Revert "Refactor: require trigger in all events objects (#1636)" This reverts commit90870ecfb6. * Revert "Fix: Correct boundary condition in applyDelay" This reverts commitb640e0e181. * Revert "let vite be the proxy to the dev server (#1630)" This reverts commitc41fe824cf. * Revert "refactor: migrate custom fields to transactions" This reverts commit62c8319d70. * Revert "refactor: simplify validations" This reverts commitb1d23467a2. * Revert "refactor: create transaction system and apply to adding entry (#1620)" This reverts commit9a62daf047. * Revert "Refactor: better rounding (#1594)" This reverts commitb9ab1c6fd7. * Revert "refactor: improve reorder logic" This reverts commitc1054711b0. * Revert "chore: simplify URLs" This reverts commita4d4f29a37. * Revert "refactor: order is single source of truth" This reverts commit2793aadea0. * Revert "refactor: refetch targets is enum" This reverts commite7cfb7d9d9. * Revert "refactor: small ux improvements" This reverts commit256a851c9b. * Revert "refactor: remove trivially inferred numEvents" This reverts commit4ab9c81cb8. * Revert "feat: duplicate groups" This reverts commit2f13d6c89e. * Revert "feat: create group from entry selection" This reverts commitf1f7bad25e. * Revert "feat: create block from rundown empty" This reverts commita8b52a48f7. * Revert "fix: collapsed blocks dont render children" This reverts commitcd0999b2ab. * Revert "refactor: type cleanup and test improvements" This reverts commitb4c60f3f04. * Revert "feat: allow dissolving a block" This reverts commit5cefad3666. * Revert "fix: uncontrolled prop on controlled component" This reverts commit7a6ecd8c34. * Revert "refactor: improve return of reorder" This reverts commitba96ecfd91. * Revert "refactor: mutations on batch elements must have IDs" This reverts commit7bed3757f2. * Revert "chore: upgrade dependencies" This reverts commite2e755b1d2. * Revert "refactor: extract utility to merge two arrays" This reverts commita77d23109d. * Revert "refactor: change network mode defaults" This reverts commit0eb3b8d382. * Revert "fix: delete nested events" This reverts commit0021185288. * Revert "fix: add event at end of block" This reverts commit94c72ff4f6. * Revert "refactor: make finder available in exported rundown" This reverts commite4c08dc9b2. * Revert "assert non null and update test (#1604)" This reverts commitec74af0d62. * Revert "Fix project renumber (#1597)" This reverts commitb6d72dd082. * Revert "Refactor: WebSocket from flush queue to one patch (#1595)" This reverts commit31c311daf0. * Revert "Refactor: ms for api calls (#1593)" This reverts commite9b3cc6090. * Revert "fix test (#1601)" This reverts commit543b04a097. * Revert "fix: rebase master" This reverts commitd39b85b6e6. * Revert "chore: correct test path" This reverts commitbbe107bb2b. * Revert "refactor: extract rundown parsing" This reverts commitc616240db1. * Revert "chore: improve convention entry <> event" This reverts commit166be66ce3. * Revert "refactor: maintain flat orders" This reverts commit4180d0a337. * Revert "refactor: implement operations on nested events" This reverts commit78108e316c. * Revert "refactor: process events in rundown" This reverts commit3bb8b70915. * Revert "chore: improve convention entry <> event" This reverts commit1c4f13a0ed. * Revert "chore: rename currentBlock > parent" This reverts commit3ca0abad53. * Revert "refactor: fix delay positioning in gaps" This reverts commit030c8f897f. * Revert "refactor(e2e): skip flaky test" This reverts commit68175cfa3b. * Revert "refactor: improve project loading" This reverts commitfd8f757851. * Revert "refactor: gather group metadata" This reverts commit876d111c61. * Revert "refactor: swap maintains schedule" This reverts commit730cb95c04. * Revert "chore: rename files" This reverts commit3c388d4fb5. * Revert "refactor: restructure model to contain an object of rundowns" This reverts commit2e23718d73. * Revert "refactor: clearer relationship on rundown elements" This reverts commit89ea8c470b. * Revert "refactor: use strict typing" This reverts commit178640bfc4. * Revert "refactor: remove stop as a possible end action" This reverts commit4ed38340e0. * Revert "refactor: restructure model to contain an object of rundowns" This reverts commit69eb9a5eff. * Revert "chore: remove IDE files" This reverts commit351425127a. * Revert "refactor: remove unused and legacy code" This reverts commit95f2ba37cc.
291 lines
8.2 KiB
TypeScript
291 lines
8.2 KiB
TypeScript
import {
|
|
CustomFields,
|
|
LogOrigin,
|
|
OntimeBlock,
|
|
OntimeDelay,
|
|
OntimeEvent,
|
|
OntimeRundownEntry,
|
|
isOntimeBlock,
|
|
isOntimeDelay,
|
|
isOntimeEvent,
|
|
OntimeRundown,
|
|
PatchWithId,
|
|
EventPostPayload,
|
|
} from 'ontime-types';
|
|
import { getCueCandidate } from 'ontime-utils';
|
|
|
|
import { block as blockDef, delay as delayDef } from '../../models/eventsDefinition.js';
|
|
import { sendRefetch } from '../../adapters/websocketAux.js';
|
|
import { logger } from '../../classes/Logger.js';
|
|
import { createEvent } from '../../utils/parser.js';
|
|
import { updateRundownData } from '../../stores/runtimeState.js';
|
|
import { runtimeService } from '../runtime-service/RuntimeService.js';
|
|
|
|
import * as cache from './rundownCache.js';
|
|
import { getPlayableEvents, getTimedEvents } from './rundownUtils.js';
|
|
|
|
type CompleteEntry<T> =
|
|
T extends Partial<OntimeEvent>
|
|
? OntimeEvent
|
|
: T extends Partial<OntimeDelay>
|
|
? OntimeDelay
|
|
: T extends Partial<OntimeBlock>
|
|
? OntimeBlock
|
|
: never;
|
|
|
|
function generateEvent<T extends Partial<OntimeEvent> | Partial<OntimeDelay> | Partial<OntimeBlock>>(
|
|
eventData: T,
|
|
afterId?: string,
|
|
): CompleteEntry<T> {
|
|
// we discard any UI provided IDs and add our own
|
|
const id = cache.getUniqueId();
|
|
|
|
if (isOntimeEvent(eventData)) {
|
|
return createEvent(eventData, getCueCandidate(cache.getPersistedRundown(), afterId)) as CompleteEntry<T>;
|
|
}
|
|
|
|
if (isOntimeDelay(eventData)) {
|
|
return { ...delayDef, duration: eventData.duration ?? 0, id } as CompleteEntry<T>;
|
|
}
|
|
|
|
if (isOntimeBlock(eventData)) {
|
|
return { ...blockDef, title: eventData?.title ?? '', id } as CompleteEntry<T>;
|
|
}
|
|
|
|
throw new Error('Invalid event type');
|
|
}
|
|
|
|
/**
|
|
* @description creates a new event with given data
|
|
* @param {object} eventData
|
|
* @return {OntimeRundownEntry}
|
|
*/
|
|
export async function addEvent(eventData: EventPostPayload): Promise<OntimeRundownEntry> {
|
|
// if the user didnt provide an index, we add the event to start
|
|
let atIndex = 0;
|
|
let afterId: string | undefined = eventData?.after;
|
|
|
|
if (eventData?.after !== undefined) {
|
|
const previousIndex = cache.getIndexOf(eventData.after);
|
|
if (previousIndex < 0) {
|
|
logger.warning(LogOrigin.Server, `Could not find event with id ${eventData.after}`);
|
|
} else {
|
|
atIndex = previousIndex + 1;
|
|
}
|
|
} else if (eventData?.before !== undefined) {
|
|
const previousIndex = cache.getIndexOf(eventData.before);
|
|
if (previousIndex < 0) {
|
|
logger.warning(LogOrigin.Server, `Could not find event with id ${eventData.before}`);
|
|
} else {
|
|
atIndex = previousIndex;
|
|
if (previousIndex > 0) {
|
|
afterId = cache.getPersistedRundown()[atIndex - 1].id;
|
|
}
|
|
}
|
|
}
|
|
|
|
// generate a fully formed event from the patch
|
|
const eventToAdd = generateEvent(eventData, afterId);
|
|
|
|
// modify rundown
|
|
const scopedMutation = cache.mutateCache(cache.add);
|
|
const { newEvent } = await scopedMutation({ atIndex, event: eventToAdd });
|
|
|
|
// notify runtime that rundown has changed
|
|
updateRuntimeOnChange();
|
|
|
|
// notify timer and external services of change
|
|
notifyChanges({ timer: [eventData.id], external: true });
|
|
|
|
return newEvent;
|
|
}
|
|
|
|
/**
|
|
* deletes event by its ID
|
|
* @param eventId
|
|
*/
|
|
export async function deleteEvent(eventIds: string[]) {
|
|
const scopedMutation = cache.mutateCache(cache.remove);
|
|
const { didMutate } = await scopedMutation({ eventIds });
|
|
|
|
if (!didMutate) {
|
|
return;
|
|
}
|
|
|
|
// notify runtime that rundown has changed
|
|
updateRuntimeOnChange();
|
|
|
|
// notify timer and external services of change
|
|
notifyChanges({ timer: eventIds, external: true });
|
|
}
|
|
|
|
/**
|
|
* deletes all events in database
|
|
*/
|
|
export async function deleteAllEvents() {
|
|
const scopedMutation = cache.mutateCache(cache.removeAll);
|
|
await scopedMutation({});
|
|
|
|
// notify event loader that rundown has changed
|
|
updateRuntimeOnChange();
|
|
|
|
// notify timer and external services of change
|
|
notifyChanges({ timer: true, external: true });
|
|
}
|
|
|
|
/**
|
|
* Apply patch to an element in rundown
|
|
* @param patch
|
|
*/
|
|
export async function editEvent(patch: PatchWithId) {
|
|
if (isOntimeEvent(patch) && patch?.cue === '') {
|
|
throw new Error('Cue value invalid');
|
|
}
|
|
|
|
const scopedMutation = cache.mutateCache(cache.edit);
|
|
const { newEvent, didMutate } = await scopedMutation({ patch, eventId: patch.id });
|
|
|
|
// short circuit if nothing changed
|
|
if (!didMutate) {
|
|
return newEvent;
|
|
}
|
|
|
|
// notify runtime that rundown has changed
|
|
updateRuntimeOnChange();
|
|
|
|
// notify timer and external services of change
|
|
notifyChanges({ timer: [patch.id], external: true });
|
|
|
|
return newEvent;
|
|
}
|
|
|
|
/**
|
|
* Applies a patch to several elements in a rundown
|
|
* @param ids
|
|
* @param data
|
|
*/
|
|
export async function batchEditEvents(ids: string[], data: Partial<OntimeEvent>) {
|
|
const scopedMutation = cache.mutateCache(cache.batchEdit);
|
|
await scopedMutation({ patch: data, eventIds: ids });
|
|
|
|
// notify runtime that rundown has changed
|
|
updateRuntimeOnChange();
|
|
|
|
// notify timer and external services of change
|
|
notifyChanges({ timer: ids, external: true });
|
|
}
|
|
|
|
/**
|
|
* reorders a given event
|
|
* @param {string} eventId - ID of event from, for sanity check
|
|
* @param {number} from - index of event from
|
|
* @param {number} to - index of event to
|
|
*/
|
|
export async function reorderEvent(eventId: string, from: number, to: number) {
|
|
const scopedMutation = cache.mutateCache(cache.reorder);
|
|
const reorderedItem = await scopedMutation({ eventId, from, to });
|
|
|
|
// notify runtime that rundown has changed
|
|
updateRuntimeOnChange();
|
|
|
|
// notify timer and external services of change
|
|
notifyChanges({ timer: true, external: true });
|
|
|
|
return reorderedItem;
|
|
}
|
|
|
|
export async function applyDelay(eventId: string) {
|
|
const scopedMutation = cache.mutateCache(cache.applyDelay);
|
|
await scopedMutation({ eventId });
|
|
|
|
// notify runtime that rundown has changed
|
|
updateRuntimeOnChange();
|
|
|
|
// notify timer and external services of change
|
|
notifyChanges({ timer: true, external: true });
|
|
}
|
|
|
|
/**
|
|
* swaps two events
|
|
* @param {string} from - id of event from
|
|
* @param {string} to - id of event to
|
|
* @returns {Promise<void>}
|
|
*/
|
|
export async function swapEvents(from: string, to: string) {
|
|
const scopedMutation = cache.mutateCache(cache.swap);
|
|
await scopedMutation({ fromId: from, toId: to });
|
|
|
|
// notify runtime that rundown has changed
|
|
updateRuntimeOnChange();
|
|
|
|
// notify timer and external services of change
|
|
notifyChanges({ timer: true, external: true });
|
|
}
|
|
|
|
/**
|
|
* Forces update in the store
|
|
* Called when we make changes to the rundown object
|
|
*/
|
|
function updateRuntimeOnChange() {
|
|
const timedEvents = getTimedEvents();
|
|
const numEvents = timedEvents.length;
|
|
const metadata = cache.getMetadata();
|
|
|
|
// schedule an update for the end of the event loop
|
|
setImmediate(() =>
|
|
updateRundownData({
|
|
numEvents,
|
|
...metadata,
|
|
}),
|
|
);
|
|
}
|
|
|
|
type NotifyChangesOptions = {
|
|
timer?: boolean | string[]; // whether to notify the timer, could be a yes / no or an array of affected IDs
|
|
external?: boolean; // whether to notify external services
|
|
reload?: boolean; // major change, clients should consider refetching everything
|
|
};
|
|
|
|
/**
|
|
* Notify services of changes in the rundown
|
|
*/
|
|
function notifyChanges(options: NotifyChangesOptions) {
|
|
if (options.timer) {
|
|
const playableEvents = getPlayableEvents();
|
|
|
|
if (playableEvents.length === 0) {
|
|
runtimeService.stop();
|
|
} else {
|
|
// notify timer service of changed events
|
|
// timer can be true or an array of changed IDs
|
|
const affected = Array.isArray(options.timer) ? options.timer : undefined;
|
|
runtimeService.notifyOfChangedEvents(affected);
|
|
}
|
|
}
|
|
|
|
if (options.external) {
|
|
// advice socket subscribers of change
|
|
const payload = {
|
|
target: 'RUNDOWN',
|
|
changes: Array.isArray(options.timer) ? options.timer : undefined,
|
|
reload: options.reload,
|
|
revision: cache.getMetadata().revision,
|
|
};
|
|
sendRefetch(payload);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Overrides the rundown with the given
|
|
* @param rundown
|
|
*/
|
|
export async function initRundown(rundown: Readonly<OntimeRundown>, customFields: Readonly<CustomFields>) {
|
|
await cache.init(rundown, customFields);
|
|
|
|
// notify runtime that rundown has changed
|
|
updateRuntimeOnChange();
|
|
|
|
// notify timer of change
|
|
notifyChanges({ timer: true, external: true, reload: true });
|
|
}
|