diff --git a/apps/client/src/views/timeline/timeline.utils.ts b/apps/client/src/views/timeline/timeline.utils.ts index a3d9d859a..cb2cde1b0 100644 --- a/apps/client/src/views/timeline/timeline.utils.ts +++ b/apps/client/src/views/timeline/timeline.utils.ts @@ -152,13 +152,10 @@ export function computeScopedRundown( const timeFromPrevious: number = getTimeFrom(currentEntry, lastEntry); - if (timeFromPrevious === 0) { - totalDuration += currentEntry.duration; - } else if (timeFromPrevious > 0) { - totalDuration += timeFromPrevious + currentEntry.duration; - } else if (timeFromPrevious < 0) { - totalDuration += Math.max(currentEntry.duration + timeFromPrevious, 0); - } + // each event only contributes the time it pushes past the latest event so far + // NOTE: timeFromPrevious is negative on overlap, so adding it removes the overlapping part + totalDuration += Math.max(currentEntry.duration + timeFromPrevious, 0); + if (isNewLatest(currentEntry, lastEntry)) { lastEntry = currentEntry; } diff --git a/apps/server/src/api-data/rundown/rundown.parser.ts b/apps/server/src/api-data/rundown/rundown.parser.ts index e269490e9..cfcce5d0c 100644 --- a/apps/server/src/api-data/rundown/rundown.parser.ts +++ b/apps/server/src/api-data/rundown/rundown.parser.ts @@ -309,18 +309,15 @@ function processEntry( entry.gap = getTimeFrom(entry, rundownMetadata.latestEvent); - if (entry.gap === 0) { - // event starts on previous finish, we add its duration - rundownMetadata.totalDuration += entry.duration; - } else if (entry.gap > 0) { - // event has a gap, we add the gap and the duration - rundownMetadata.totalDuration += entry.gap + entry.duration; - } else { - // there is an overlap, we remove the overlap from the duration - // ensuring that the sum is not negative (ie: fully overlapped events) - // NOTE: we add the gap since it is a negative number - rundownMetadata.totalDuration += Math.max(entry.duration + entry.gap, 0); - } + /** + * The rundown duration is the union of all event intervals, so each event + * only contributes the time it pushes past the latest event so far. + * - gap >= 0: the event contributes its gap plus its full duration + * - gap < 0: the event overlaps, only the part after the latest end counts + * (clamped at 0 for events which are fully contained in a previous one) + * NOTE: gap is negative on overlap, so adding it removes the overlapping part + */ + rundownMetadata.totalDuration += Math.max(entry.duration + entry.gap, 0); // remove eventual gaps from the accumulated delay // we only affect positive delays (time forwards) diff --git a/packages/utils/src/date-utils/getTimeFrom.ts b/packages/utils/src/date-utils/getTimeFrom.ts index d29efd30e..fb3c6604c 100644 --- a/packages/utils/src/date-utils/getTimeFrom.ts +++ b/packages/utils/src/date-utils/getTimeFrom.ts @@ -17,17 +17,11 @@ export function getTimeFrom( const normalisedCurrentStart = current.timeStart + current.dayOffset * dayInMs; const normalisedPreviousEnd = previous.timeStart + previous.duration + previous.dayOffset * dayInMs; - // event is linked to previous - if (normalisedCurrentStart === normalisedPreviousEnd) { - return 0; - } - - // event has a gap from previous - if (normalisedCurrentStart > normalisedPreviousEnd) { - // time from previous is difference between start and previous end - return normalisedCurrentStart - normalisedPreviousEnd; - } - - // event overlaps with previous + /** + * The distance between the current start and the previous end + * - positive: there is a gap between the events + * - zero: the current event starts on the previous end + * - negative: the events overlap + */ return normalisedCurrentStart - normalisedPreviousEnd; }