Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IEventContext, IShowStyleUserContext, Time } from '../index.js'
import { IBlueprintPart, IBlueprintPiece, IEventContext, IShowStyleUserContext, Time } from '../index.js'
import { IPartAndPieceActionContext } from './partsAndPieceActionContext.js'
import { IExecuteTSRActionsContext } from './executeTsrActionContext.js'

Expand All @@ -18,4 +18,6 @@ export interface IOnTakeContext
* but the next part will not be taken.
*/
abortTake(): void
/** Insert a queued part to follow the taken part */
queuePartAfterTake(part: IBlueprintPart, pieces: IBlueprintPiece[]): void
}
5 changes: 5 additions & 0 deletions packages/job-worker/src/blueprints/context/OnTakeContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { BlueprintQuickLookInfo } from '@sofie-automation/blueprints-integration

export class OnTakeContext extends ShowStyleUserContext implements IOnTakeContext, IEventContext {
public isTakeAborted: boolean
public partToQueue: { rawPart: IBlueprintPart; rawPieces: IBlueprintPiece[] } | undefined

public get quickLoopInfo(): BlueprintQuickLookInfo | null {
return this.partAndPieceInstanceService.quickLoopInfo
Expand Down Expand Up @@ -153,6 +154,10 @@ export class OnTakeContext extends ShowStyleUserContext implements IOnTakeContex
return executePeripheralDeviceAction(this._context, deviceId, null, actionId, payload)
}

queuePartAfterTake(rawPart: IBlueprintPart, rawPieces: IBlueprintPiece[]): void {
this.partToQueue = { rawPart, rawPieces }
}

getCurrentTime(): number {
return getCurrentTime()
}
Expand Down
30 changes: 24 additions & 6 deletions packages/job-worker/src/playout/take.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,13 @@ export async function performTakeToNextedPart(
const showStyle = await pShowStyle
const blueprint = await context.getShowStyleBlueprint(showStyle._id)

const { isTakeAborted } = await executeOnTakeCallback(context, playoutModel, showStyle, blueprint, currentRundown)
const { isTakeAborted, queuePart } = await executeOnTakeCallback(
context,
playoutModel,
showStyle,
blueprint,
currentRundown
)

if (isTakeAborted) {
await updateTimeline(context, playoutModel)
Expand Down Expand Up @@ -264,8 +270,12 @@ export async function performTakeToNextedPart(
resetPreviousSegmentIfLooping(context, playoutModel)
}

// Once everything is synced, we can choose the next part
await setNextPart(context, playoutModel, nextPart, false)
if (queuePart) {
await queuePart()
} else {
// Once everything is synced, we can choose the next part
await setNextPart(context, playoutModel, nextPart, false)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The definition of nextPart would be good to move into this else block, so that it is only run when needed and make sure it doesnt get used elsewhere accidentally

}

// If the Hold is PENDING, make it active
if (playoutModel.playlist.holdState === RundownHoldState.PENDING) {
Expand All @@ -289,17 +299,19 @@ async function executeOnTakeCallback(
showStyle: ReadonlyObjectDeep<ProcessedShowStyleCompound>,
blueprint: ReadonlyObjectDeep<WrappedShowStyleBlueprint>,
currentRundown: PlayoutRundownModel
): Promise<{ isTakeAborted: boolean }> {
): Promise<{ isTakeAborted: boolean; queuePart: (() => Promise<void>) | undefined }> {
const NOTIFICATION_CATEGORY = 'onTake'

let isTakeAborted = false
let queuePart: (() => Promise<void>) | undefined = undefined
if (blueprint.blueprint.onTake) {
const rundownId = currentRundown.rundown._id
const partInstanceId = playoutModel.playlist.nextPartInfo?.partInstanceId
if (!partInstanceId) throw new Error('Cannot call blueprint onTake when there is no next partInstance!')

// Clear any existing notifications for this partInstance. This will clear any from the previous take
playoutModel.clearAllNotifications(NOTIFICATION_CATEGORY)
const actionService = new PartAndPieceInstanceActionService(context, playoutModel, showStyle, currentRundown)

const watchedPackagesHelper = WatchedPackagesHelper.empty(context)
const onSetAsNextContext = new OnTakeContext(
Expand All @@ -313,7 +325,7 @@ async function executeOnTakeCallback(
playoutModel,
showStyle,
watchedPackagesHelper,
new PartAndPieceInstanceActionService(context, playoutModel, showStyle, currentRundown)
actionService
)
try {
const blueprintPersistentState = new PersistentPlayoutStateStore(
Expand All @@ -323,6 +335,12 @@ async function executeOnTakeCallback(
await blueprint.blueprint.onTake(onSetAsNextContext, blueprintPersistentState)
await applyOnTakeSideEffects(context, playoutModel, onSetAsNextContext)
isTakeAborted = onSetAsNextContext.isTakeAborted
if (onSetAsNextContext.partToQueue) {
const partToQueue = onSetAsNextContext.partToQueue
queuePart = async () => {
await actionService.queuePart(partToQueue.rawPart, partToQueue.rawPieces)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like how this function gets returned, and am also wondering does this even work correctly?

Largely because this working is reliant on the nextPartState in the service as being NONE. So if any other changes are made to the current part in the callback then this will fail.

Being pickier (perhaps too much so), this relies on the implementation detail of how the actionService gets currentPartInstance. If that were changed to be a less dynamic reference, then this would break (I am pretty sure that nowhere else relies on this mutability).

}
}

if (blueprintPersistentState.hasChanges) {
playoutModel.setBlueprintPersistentState(blueprintPersistentState.getAll())
Expand Down Expand Up @@ -354,7 +372,7 @@ async function executeOnTakeCallback(
})
}
}
return { isTakeAborted }
return { isTakeAborted, queuePart }
}

async function applyOnTakeSideEffects(context: JobContext, playoutModel: PlayoutModel, onTakeContext: OnTakeContext) {
Expand Down
Loading