Skip to content

Commit 94ec9af

Browse files
committed
wip: timeline building
1 parent 43882e8 commit 94ec9af

File tree

3 files changed

+107
-39
lines changed

3 files changed

+107
-39
lines changed

packages/job-worker/src/playout/adlibUtils.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -323,13 +323,15 @@ export function innerStopPieces(
323323

324324
const pieceInstanceModel = playoutModel.findPieceInstance(pieceInstance._id)
325325
if (pieceInstanceModel) {
326-
const newDuration: Required<PieceInstance>['userDuration'] = playoutModel.isMultiGatewayMode
327-
? {
328-
endRelativeToNow: offsetRelativeToNow,
329-
}
330-
: {
331-
endRelativeToPart: relativeStopAt,
332-
}
326+
const newDuration: Required<PieceInstance>['userDuration'] =
327+
playoutModel.isMultiGatewayMode ||
328+
pieceInstanceModel.pieceInstance.pieceInstance.piece.enable.isAbsolute
329+
? {
330+
endRelativeToNow: offsetRelativeToNow,
331+
}
332+
: {
333+
endRelativeToPart: relativeStopAt,
334+
}
333335

334336
pieceInstanceModel.pieceInstance.setDuration(newDuration)
335337

packages/job-worker/src/playout/infinites.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,8 @@ export async function getBaselineInfinitesForPart(
409409
return pieces.map((piece) => {
410410
const instance = wrapPieceToInstance(clone<Piece>(piece), playlistActivationId, partInstanceId, false)
411411

412+
// All these pieces are expected to be outOnRundownChange infinites, as that is how they are ingested
413+
412414
instance.infinite = {
413415
infiniteInstanceId: getRandomId(),
414416
infiniteInstanceIndex: 0,

packages/job-worker/src/playout/timeline/rundown.ts

Lines changed: 96 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -273,9 +273,25 @@ function generateCurrentInfinitePieceObjects(
273273
return []
274274
}
275275

276-
const infiniteGroup = createPartGroup(currentPartInfo.partInstance, {
277-
start: `#${timingContext.currentPartGroup.id}.start`, // This gets overriden with a concrete time if the original piece is known to have already started
278-
})
276+
const { pieceEnable, infiniteGroupEnable, nowInParent } = calculateInfinitePieceEnable(
277+
currentPartInfo,
278+
timingContext,
279+
pieceInstance,
280+
currentTime,
281+
currentPartInstanceTimings
282+
)
283+
284+
const { pieceInstanceWithUpdatedEndCap, cappedInfiniteGroupEnable } = applyInfinitePieceGroupEndCap(
285+
currentPartInfo,
286+
timingContext,
287+
pieceInstance,
288+
infiniteGroupEnable,
289+
currentPartInstanceTimings,
290+
nextPartInstanceTimings,
291+
nextPartInfinites.get(pieceInstance.infinite.infiniteInstanceId)
292+
)
293+
294+
const infiniteGroup = createPartGroup(currentPartInfo.partInstance, cappedInfiniteGroupEnable)
279295
infiniteGroup.id = getInfinitePartGroupId(pieceInstance._id) // This doesnt want to belong to a part, so force the ids
280296
infiniteGroup.priority = 1
281297

@@ -285,6 +301,34 @@ function generateCurrentInfinitePieceObjects(
285301
groupClasses.push('continues_infinite')
286302
}
287303

304+
// Still show objects flagged as 'HoldMode.EXCEPT' if this is a infinite continuation as they belong to the previous too
305+
const isOriginOfInfinite = pieceInstance.piece.startPartId !== currentPartInfo.partInstance.part._id
306+
const isInHold = activePlaylist.holdState === RundownHoldState.ACTIVE
307+
308+
return [
309+
infiniteGroup,
310+
...transformPieceGroupAndObjects(
311+
activePlaylist._id,
312+
infiniteGroup,
313+
nowInParent,
314+
pieceInstanceWithUpdatedEndCap,
315+
pieceEnable,
316+
0,
317+
groupClasses,
318+
isInHold,
319+
isOriginOfInfinite
320+
),
321+
]
322+
}
323+
324+
function calculateInfinitePieceEnable(
325+
currentPartInfo: SelectedPartInstanceTimelineInfo,
326+
timingContext: RundownTimelineTimingContext,
327+
pieceInstance: ReadonlyDeep<PieceInstanceWithTimings>,
328+
// infiniteGroup: TimelineObjGroupPart & OnGenerateTimelineObjExt,
329+
currentTime: number,
330+
currentPartInstanceTimings: PartCalculatedTimings
331+
) {
288332
const pieceEnable = getPieceEnableInsidePart(
289333
pieceInstance,
290334
currentPartInstanceTimings,
@@ -293,6 +337,10 @@ function generateCurrentInfinitePieceObjects(
293337
timingContext.currentPartGroup.enable.duration !== undefined
294338
)
295339

340+
let infiniteGroupEnable: PartEnable = {
341+
start: `#${timingContext.currentPartGroup.id}.start`, // This gets overriden with a concrete time if the original piece is known to have already started
342+
}
343+
296344
let nowInParent = currentPartInfo.nowInPart // Where is 'now' inside of the infiniteGroup?
297345
if (pieceInstance.plannedStartedPlayback !== undefined) {
298346
// We have a absolute start time, so we should use that.
@@ -311,47 +359,80 @@ function generateCurrentInfinitePieceObjects(
311359
pieceEnable.start = 0
312360
}
313361

314-
infiniteGroup.enable = { start: infiniteGroupStart }
362+
infiniteGroupEnable = { start: infiniteGroupStart }
315363

316364
// If an end time has been set by a hotkey, then update the duration to be correct
317365
if (pieceInstance.userDuration && pieceInstance.piece.enable.start !== 'now') {
318366
if ('endRelativeToPart' in pieceInstance.userDuration) {
319-
infiniteGroup.enable.duration =
367+
infiniteGroupEnable.duration =
320368
pieceInstance.userDuration.endRelativeToPart - pieceInstance.piece.enable.start
321369
} else {
322-
infiniteGroup.enable.end = 'now'
370+
infiniteGroupEnable.end = 'now'
323371
}
324372
}
373+
} else if (pieceInstance.piece.enable.isAbsolute) {
374+
// Piece is absolute, so we should use the absolute time. This is a special case for pieces belonging to the rundown directly.
375+
376+
if (typeof pieceInstance.piece.enable.start === 'number') {
377+
nowInParent = currentTime - pieceInstance.piece.enable.start
378+
} else {
379+
// We should never hit this, but in case start is "now"
380+
nowInParent = 0
381+
}
382+
383+
infiniteGroupEnable = { start: pieceInstance.piece.enable.start }
384+
pieceEnable.start = 0
385+
386+
// nocommit prerollDuration?
387+
388+
// Ingore userDuration, it gets handled later
325389
}
326390

391+
return {
392+
pieceEnable,
393+
infiniteGroupEnable,
394+
nowInParent,
395+
}
396+
}
397+
398+
function applyInfinitePieceGroupEndCap(
399+
currentPartInfo: SelectedPartInstanceTimelineInfo,
400+
timingContext: RundownTimelineTimingContext,
401+
pieceInstance: ReadonlyDeep<PieceInstanceWithTimings>,
402+
infiniteGroupEnable: Readonly<PartEnable>,
403+
currentPartInstanceTimings: PartCalculatedTimings,
404+
nextPartInstanceTimings: PartCalculatedTimings | null,
405+
infiniteInNextPart: PieceInstanceWithTimings | undefined
406+
) {
407+
const cappedInfiniteGroupEnable: PartEnable = { ...infiniteGroupEnable }
408+
327409
// If this infinite piece continues to the next part, and has a duration then we should respect that in case it is really close to the take
328410
const hasDurationOrEnd = (enable: TSR.Timeline.TimelineEnable) =>
329411
enable.duration !== undefined || enable.end !== undefined
330-
const infiniteInNextPart = nextPartInfinites.get(pieceInstance.infinite.infiniteInstanceId)
331412
if (
332413
infiniteInNextPart &&
333-
!hasDurationOrEnd(infiniteGroup.enable) &&
414+
!hasDurationOrEnd(cappedInfiniteGroupEnable) &&
334415
hasDurationOrEnd(infiniteInNextPart.piece.enable)
335416
) {
336417
// infiniteGroup.enable.end = infiniteInNextPart.piece.enable.end
337-
infiniteGroup.enable.duration = infiniteInNextPart.piece.enable.duration
418+
cappedInfiniteGroupEnable.duration = infiniteInNextPart.piece.enable.duration
338419
}
339420

340421
const pieceInstanceWithUpdatedEndCap: PieceInstanceWithTimings = { ...pieceInstance }
341422
// Give the infinite group and end cap when the end of the piece is known
342423
if (pieceInstance.resolvedEndCap) {
343424
// If the cap is a number, it is relative to the part, not the parent group so needs to be handled here
344425
if (typeof pieceInstance.resolvedEndCap === 'number') {
345-
infiniteGroup.enable.end = `#${timingContext.currentPartGroup.id}.start + ${pieceInstance.resolvedEndCap}`
346-
delete infiniteGroup.enable.duration
426+
cappedInfiniteGroupEnable.end = `#${timingContext.currentPartGroup.id}.start + ${pieceInstance.resolvedEndCap}`
427+
delete cappedInfiniteGroupEnable.duration
347428
delete pieceInstanceWithUpdatedEndCap.resolvedEndCap
348429
}
349430
} else if (
350431
// If this piece does not continue in the next part, then set it to end with the part it belongs to
351432
!infiniteInNextPart &&
352433
currentPartInfo.partInstance.part.autoNext &&
353-
infiniteGroup.enable.duration === undefined &&
354-
infiniteGroup.enable.end === undefined
434+
cappedInfiniteGroupEnable.duration === undefined &&
435+
cappedInfiniteGroupEnable.end === undefined
355436
) {
356437
let endOffset = 0
357438

@@ -363,27 +444,10 @@ function generateCurrentInfinitePieceObjects(
363444
endOffset -= nextPartInstanceTimings.fromPartKeepalive
364445

365446
// cap relative to the currentPartGroup
366-
infiniteGroup.enable.end = `#${timingContext.currentPartGroup.id}.end + ${endOffset}`
447+
cappedInfiniteGroupEnable.end = `#${timingContext.currentPartGroup.id}.end + ${endOffset}`
367448
}
368449

369-
// Still show objects flagged as 'HoldMode.EXCEPT' if this is a infinite continuation as they belong to the previous too
370-
const isOriginOfInfinite = pieceInstance.piece.startPartId !== currentPartInfo.partInstance.part._id
371-
const isInHold = activePlaylist.holdState === RundownHoldState.ACTIVE
372-
373-
return [
374-
infiniteGroup,
375-
...transformPieceGroupAndObjects(
376-
activePlaylist._id,
377-
infiniteGroup,
378-
nowInParent,
379-
pieceInstanceWithUpdatedEndCap,
380-
pieceEnable,
381-
0,
382-
groupClasses,
383-
isInHold,
384-
isOriginOfInfinite
385-
),
386-
]
450+
return { pieceInstanceWithUpdatedEndCap, cappedInfiniteGroupEnable }
387451
}
388452

389453
function generatePreviousPartInstanceObjects(

0 commit comments

Comments
 (0)