diff --git a/packages/job-worker/src/playout/model/PlayoutModel.ts b/packages/job-worker/src/playout/model/PlayoutModel.ts index 396a465373..3b944f9f3f 100644 --- a/packages/job-worker/src/playout/model/PlayoutModel.ts +++ b/packages/job-worker/src/playout/model/PlayoutModel.ts @@ -234,6 +234,11 @@ export interface PlayoutModel extends PlayoutModelReadonly, StudioPlayoutModelBa */ cycleSelectedPartInstances(): void + /** + * Reset the hold state to a base state + */ + resetHoldState(): void + /** * Set the RundownPlaylist as deactivated */ diff --git a/packages/job-worker/src/playout/model/implementation/PlayoutModelImpl.ts b/packages/job-worker/src/playout/model/implementation/PlayoutModelImpl.ts index b2d0508a08..5b57a57152 100644 --- a/packages/job-worker/src/playout/model/implementation/PlayoutModelImpl.ts +++ b/packages/job-worker/src/playout/model/implementation/PlayoutModelImpl.ts @@ -420,15 +420,13 @@ export class PlayoutModelImpl extends PlayoutModelReadonlyImpl implements Playou this.playlistImpl.nextPartInfo = null this.playlistImpl.lastTakeTime = getCurrentTime() - if (!this.playlistImpl.holdState || this.playlistImpl.holdState === RundownHoldState.COMPLETE) { - this.playlistImpl.holdState = RundownHoldState.NONE - } else { - this.playlistImpl.holdState = this.playlistImpl.holdState + 1 - } - this.#playlistHasChanged = true } + resetHoldState(): void { + this.setHoldState(RundownHoldState.NONE) + } + deactivatePlaylist(): void { delete this.playlistImpl.activationId diff --git a/packages/job-worker/src/playout/take.ts b/packages/job-worker/src/playout/take.ts index f4a404d993..c089685d8d 100644 --- a/packages/job-worker/src/playout/take.ts +++ b/packages/job-worker/src/playout/take.ts @@ -178,20 +178,23 @@ export async function performTakeToNextedPart( } } + // If hold is COMPLETE, clear the hold state by this take if (playoutModel.playlist.holdState === RundownHoldState.COMPLETE) { playoutModel.setHoldState(RundownHoldState.NONE) - // If hold is active, then this take is to clear it + // If hold is ACTIVE, then this take is to complete it } else if (playoutModel.playlist.holdState === RundownHoldState.ACTIVE) { await completeHold(context, playoutModel, await pShowStyle, currentPartInstance) + await updateTimeline(context, playoutModel) + if (span) span.end() return } const takePartInstance = nextPartInstance - if (!takePartInstance) throw new Error('takePart not found!') + if (!takePartInstance) throw new Error('takePartInstance not found!') const takeRundown = playoutModel.getRundown(takePartInstance.partInstance.rundownId) if (!takeRundown) throw new Error(`takeRundown: takeRundown not found! ("${takePartInstance.partInstance.rundownId}")`) @@ -263,12 +266,10 @@ export async function performTakeToNextedPart( // Once everything is synced, we can choose the next part await setNextPart(context, playoutModel, nextPart, false) - // Setup the parts for the HOLD we are starting - if ( - playoutModel.playlist.previousPartInfo && - (playoutModel.playlist.holdState as RundownHoldState) === RundownHoldState.ACTIVE - ) { - startHold(context, currentPartInstance, nextPartInstance) + // If the Hold is PENDING, make it active + if (playoutModel.playlist.holdState === RundownHoldState.PENDING) { + // Setup the parts for the HOLD we are starting + activateHold(context, playoutModel, currentPartInstance, takePartInstance) } await afterTake(context, playoutModel, takePartInstance) @@ -535,35 +536,39 @@ export async function afterTake( /** * A Hold starts by extending the "extendOnHold"-able pieces in the previous Part. */ -function startHold( +function activateHold( context: JobContext, + playoutModel: PlayoutModel, holdFromPartInstance: PlayoutPartInstanceModel | null, holdToPartInstance: PlayoutPartInstanceModel | undefined ) { if (!holdFromPartInstance) throw new Error('previousPart not found!') if (!holdToPartInstance) throw new Error('currentPart not found!') - const span = context.startSpan('startHold') + const span = context.startSpan('activateHold') + + playoutModel.setHoldState(RundownHoldState.ACTIVE) // Make a copy of any item which is flagged as an 'infinite' extension const pieceInstancesToCopy = holdFromPartInstance.pieceInstances.filter((p) => !!p.pieceInstance.piece.extendOnHold) - pieceInstancesToCopy.forEach((instance) => { - if (!instance.pieceInstance.infinite) { - // mark current one as infinite - instance.prepareForHold() - - // This gets deleted once the nextpart is activated, so it doesnt linger for long - const extendedPieceInstance = holdToPartInstance.insertHoldPieceInstance(instance) - - const content = clone(instance.pieceInstance.piece.content) as VTContent | undefined - if (content?.fileName && content.sourceDuration && instance.pieceInstance.plannedStartedPlayback) { - content.seek = Math.min( - content.sourceDuration, - getCurrentTime() - instance.pieceInstance.plannedStartedPlayback - ) - } - extendedPieceInstance.updatePieceProps({ content }) + for (const instance of pieceInstancesToCopy) { + // skip any infinites + if (instance.pieceInstance.infinite) continue + + instance.prepareForHold() + + // This gets deleted once the nextpart is activated, so it doesnt linger for long + const extendedPieceInstance = holdToPartInstance.insertHoldPieceInstance(instance) + + const content = clone(instance.pieceInstance.piece.content) as VTContent | undefined + if (content?.fileName && content.sourceDuration && instance.pieceInstance.plannedStartedPlayback) { + content.seek = Math.min( + content.sourceDuration, + getCurrentTime() - instance.pieceInstance.plannedStartedPlayback + ) } - }) + extendedPieceInstance.updatePieceProps({ content }) + } + if (span) span.end() } @@ -575,19 +580,16 @@ async function completeHold( ): Promise { playoutModel.setHoldState(RundownHoldState.COMPLETE) - if (playoutModel.playlist.currentPartInfo) { - if (!currentPartInstance) throw new Error('currentPart not found!') + if (!playoutModel.playlist.currentPartInfo) return + if (!currentPartInstance) throw new Error('currentPart not found!') - // Clear the current extension line - innerStopPieces( - context, - playoutModel, - showStyleCompound.sourceLayers, - currentPartInstance, - (p) => !!p.infinite?.fromHold, - undefined - ) - } - - await updateTimeline(context, playoutModel) + // Clear the current extension line + innerStopPieces( + context, + playoutModel, + showStyleCompound.sourceLayers, + currentPartInstance, + (p) => !!p.infinite?.fromHold, + undefined + ) } diff --git a/packages/job-worker/src/playout/timings/partPlayback.ts b/packages/job-worker/src/playout/timings/partPlayback.ts index 8c3fc07d16..279d1dd410 100644 --- a/packages/job-worker/src/playout/timings/partPlayback.ts +++ b/packages/job-worker/src/playout/timings/partPlayback.ts @@ -58,6 +58,7 @@ export async function onPartPlaybackStarted( // this is the next part, clearly an autoNext has taken place playoutModel.cycleSelectedPartInstances() + playoutModel.resetHoldState() reportPartInstanceHasStarted(context, playoutModel, playingPartInstance, data.startedPlayback)