Skip to content
Merged
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
Expand Up @@ -19,6 +19,9 @@ export interface IOnSetAsNextContext extends IShowStyleUserContext, IEventContex
/** Information about the current loop, if there is one */
readonly quickLoopInfo: BlueprintQuickLookInfo | null

/** Whether the part being set as next was selected as a result of user's actions */
readonly manuallySelected: boolean

/**
* Data fetching
*/
Expand Down
4 changes: 3 additions & 1 deletion packages/blueprints-integration/src/context/onTakeContext.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IEventContext, IShowStyleUserContext, Time } from '..'
import { IBlueprintPart, IBlueprintPiece, IEventContext, IShowStyleUserContext, Time } from '..'
import { IPartAndPieceActionContext } from './partsAndPieceActionContext'
import { IExecuteTSRActionsContext } from './executeTsrActionContext'

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
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { PartAndPieceInstanceActionService } from '../context/services/PartAndPi
import { OnSetAsNextContext } from '../context'

describe('Test blueprint api context', () => {
async function getTestee() {
async function getTestee(setManually = false) {
const mockActionService = mock<PartAndPieceInstanceActionService>()
const context = new OnSetAsNextContext(
{
Expand All @@ -19,7 +19,8 @@ describe('Test blueprint api context', () => {
mock<PlayoutModel>(),
mock<ProcessedShowStyleCompound>(),
mock<WatchedPackagesHelper>(),
mockActionService
mockActionService,
setManually
)

return {
Expand All @@ -28,7 +29,7 @@ describe('Test blueprint api context', () => {
}
}

describe('ActionExecutionContext', () => {
describe('OnSetAsNextContext', () => {
test('getPartInstance', async () => {
const { context, mockActionService } = await getTestee()

Expand Down Expand Up @@ -126,5 +127,17 @@ describe('Test blueprint api context', () => {
expect(mockActionService.updatePartInstance).toHaveBeenCalledTimes(1)
expect(mockActionService.updatePartInstance).toHaveBeenCalledWith('next', { title: 'My Part' })
})

test('manuallySelected when false', async () => {
const { context } = await getTestee(false)

expect(context.manuallySelected).toBe(false)
})

test('manuallySelected when true', async () => {
const { context } = await getTestee(true)

expect(context.manuallySelected).toBe(true)
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ export class OnSetAsNextContext
private playoutModel: PlayoutModel,
showStyle: ReadonlyDeep<ProcessedShowStyleCompound>,
watchedPackages: WatchedPackagesHelper,
private partAndPieceInstanceService: PartAndPieceInstanceActionService
private partAndPieceInstanceService: PartAndPieceInstanceActionService,
public readonly manuallySelected: boolean
) {
super(contextInfo, context, showStyle, watchedPackages)
}
Expand Down
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 @@ -27,6 +27,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 @@ -149,6 +150,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
8 changes: 5 additions & 3 deletions packages/job-worker/src/playout/setNext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ async function setNextPartAndCheckForPendingMoveNextPart(

playoutModel.setPartInstanceAsNext(newPartInstance, setManually, consumesQueuedSegmentId, nextTimeOffset)

return executeOnSetAsNextCallback(playoutModel, newPartInstance, context)
return executeOnSetAsNextCallback(playoutModel, newPartInstance, context, setManually)
} else {
// Set to null

Expand All @@ -188,7 +188,8 @@ async function setNextPartAndCheckForPendingMoveNextPart(
async function executeOnSetAsNextCallback(
playoutModel: PlayoutModel,
newPartInstance: PlayoutPartInstanceModel,
context: JobContext
context: JobContext,
setManually: boolean
) {
const NOTIFICATION_CATEGORY = 'onSetAsNext'

Expand Down Expand Up @@ -218,7 +219,8 @@ async function executeOnSetAsNextCallback(
playoutModel,
showStyle,
watchedPackagesHelper,
new PartAndPieceInstanceActionService(context, playoutModel, showStyle, rundownOfNextPart)
new PartAndPieceInstanceActionService(context, playoutModel, showStyle, rundownOfNextPart),
setManually
)

// Clear any existing notifications for this partInstance. This will clear any from the previous setAsNext
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)
}

// 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,12 +325,18 @@ async function executeOnTakeCallback(
playoutModel,
showStyle,
watchedPackagesHelper,
new PartAndPieceInstanceActionService(context, playoutModel, showStyle, currentRundown)
actionService
)
try {
await blueprint.blueprint.onTake(onSetAsNextContext)
await applyOnTakeSideEffects(context, playoutModel, onSetAsNextContext)
isTakeAborted = onSetAsNextContext.isTakeAborted
if (onSetAsNextContext.partToQueue) {
const partToQueue = onSetAsNextContext.partToQueue
queuePart = async () => {
await actionService.queuePart(partToQueue.rawPart, partToQueue.rawPieces)
}
}

for (const note of onSetAsNextContext.notes) {
// Update the notifications. Even though these are related to a partInstance, they will be cleared on the next take
Expand Down Expand Up @@ -346,7 +364,7 @@ async function executeOnTakeCallback(
})
}
}
return { isTakeAborted }
return { isTakeAborted, queuePart }
}

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