diff --git a/apps/server/src/services/rundown-service/__tests__/rundownCache.test.ts b/apps/server/src/services/rundown-service/__tests__/rundownCache.test.ts index 55348b759..0c644738e 100644 --- a/apps/server/src/services/rundown-service/__tests__/rundownCache.test.ts +++ b/apps/server/src/services/rundown-service/__tests__/rundownCache.test.ts @@ -170,6 +170,73 @@ describe('generate()', () => { expect(initResult.totalDuration).toBe(dayInMs + MILLIS_PER_HOUR); // day + last end - first start }); + it('maintains delays over gaps', () => { + const rundown = makeRundown({ + order: ['1', 'delay', '2'], + entries: { + '1': makeOntimeEvent({ id: '1', timeStart: 0, timeEnd: 100, duration: 100 }), + delay: makeOntimeDelay({ id: 'delay', duration: 500 }), + '2': makeOntimeEvent({ id: '2', timeStart: 500, timeEnd: 600, duration: 100 }), + }, + }); + + const initResult = generate(rundown, {}); + expect(initResult.entries['2']).toMatchObject({ + gap: 400, + delay: 500, + }); + expect(initResult.totalDelay).toBe(500); + expect(initResult.totalDuration).toBe(600); + }); + + it('maintains delays over gaps when there are cumulative delays', () => { + const rundown = makeRundown({ + order: ['delay1', '1', 'delay2', '2'], + entries: { + delay1: makeOntimeDelay({ id: 'delay', duration: 100 }), + '1': makeOntimeEvent({ id: '1', timeStart: 0, timeEnd: 100, duration: 100 }), + delay2: makeOntimeDelay({ id: 'delay', duration: 300 }), + '2': makeOntimeEvent({ id: '2', timeStart: 300, timeEnd: 400, duration: 100 }), + }, + }); + + const initResult = generate(rundown, {}); + expect(initResult.entries['1']).toMatchObject({ + gap: 0, + delay: 100, + }); + expect(initResult.entries['2']).toMatchObject({ + gap: 200, + delay: 300, // first delay was fully absorbed + }); + expect(initResult.totalDelay).toBe(300); + expect(initResult.totalDuration).toBe(400); + }); + + it('handles negative delays on gaps', () => { + const rundown = makeRundown({ + order: ['delay1', '1', 'delay2', '2'], + entries: { + delay1: makeOntimeDelay({ id: 'delay', duration: -100 }), + '1': makeOntimeEvent({ id: '1', timeStart: 0, timeEnd: 100, duration: 100 }), + delay2: makeOntimeDelay({ id: 'delay', duration: -300 }), + '2': makeOntimeEvent({ id: '2', timeStart: 300, timeEnd: 400, duration: 100 }), + }, + }); + + const initResult = generate(rundown, {}); + expect(initResult.entries['1']).toMatchObject({ + gap: 0, + delay: -100, + }); + expect(initResult.entries['2']).toMatchObject({ + gap: 200, + delay: -400, + }); + expect(initResult.totalDelay).toBe(-400); + expect(initResult.totalDuration).toBe(400); + }); + it('handles negative delays', () => { const rundown = makeRundown({ order: ['1', 'delay', '2', 'block', '3', 'another-block', '4'], diff --git a/apps/server/src/services/rundown-service/rundownCache.utils.ts b/apps/server/src/services/rundown-service/rundownCache.utils.ts index ac6263911..42a34e2ac 100644 --- a/apps/server/src/services/rundown-service/rundownCache.utils.ts +++ b/apps/server/src/services/rundown-service/rundownCache.utils.ts @@ -144,6 +144,7 @@ export type ProcessedRundownMetadata = RundownMetadata & { order: EntryId[]; previousEvent: PlayableEvent | null; // The playableEvent from the previous iteration latestEvent: PlayableEvent | null; // The playableEvent most forwards in time processed so far + previousEntry: OntimeEntry | null; // The entry processed in the previous iteration }; export function makeRundownMetadata(customFields: CustomFields, customFieldChangelog: Record) { @@ -163,6 +164,7 @@ export function makeRundownMetadata(customFields: CustomFields, customFieldChang order: [], previousEvent: null, latestEvent: null, + previousEntry: null, }; function process( @@ -252,8 +254,16 @@ function processEntry( // remove eventual gaps from the accumulated delay // we only affect positive delays (time forwards) if (processedData.totalDelay > 0 && currentEntry.gap > 0) { + let correctedDelay = 0; + // we need to separate the delay that is accumulated from one that may exist after the gap + if (isOntimeDelay(processedData.previousEntry)) { + correctedDelay = processedData.previousEntry.duration; + processedData.totalDelay -= correctedDelay; + } processedData.totalDelay = Math.max(processedData.totalDelay - currentEntry.gap, 0); + processedData.totalDelay += correctedDelay; } + // current event delay is the current accumulated delay currentEntry.delay = processedData.totalDelay; @@ -275,6 +285,7 @@ function processEntry( processedData.order.push(currentEntry.id); } processedData.entries[currentEntry.id] = currentEntry; + processedData.previousEntry = currentEntry; return { processedData, processedEntry: currentEntry }; }