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 @@ -381,10 +381,9 @@ export class PartAndPieceInstanceActionService {
throw new Error('New part must contain at least one piece')
}

const newPart: Omit<DBPart, 'segmentId' | 'rundownId'> = {
const newPart: Omit<DBPart, 'segmentId' | 'rundownId' | '_rank'> = {
...rawPart,
_id: getRandomId(),
_rank: 99999, // Corrected in innerStartQueuedAdLib
notes: [],
invalid: false,
invalidReason: undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { DBPartInstance } from '@sofie-automation/corelib/dist/dataModel/PartIns
import { getCurrentTime } from '../../../../lib'
import {
EmptyPieceTimelineObjectsBlob,
Piece,
serializePieceTimelineObjectsBlob,
} from '@sofie-automation/corelib/dist/dataModel/Piece'
import { PlayoutPartInstanceModel } from '../../../../playout/model/PlayoutPartInstanceModel'
Expand Down Expand Up @@ -176,7 +177,9 @@ describe('Test blueprint api context', () => {
return runJobWithPlayoutModel(context, { playlistId }, null, fcn as any)
}

async function setupMyDefaultRundown(): Promise<{
async function setupMyDefaultRundown(
insertExtraContents?: (jobContext: MockJobContext, rundownId: RundownId) => Promise<void>
): Promise<{
jobContext: MockJobContext
playlistId: RundownPlaylistId
rundownId: RundownId
Expand All @@ -200,6 +203,8 @@ describe('Test blueprint api context', () => {

await setupDefaultRundown(context, showStyleCompound, playlistId, rundownId)

if (insertExtraContents) await insertExtraContents(context, rundownId)

const allPartInstances = await generateSparsePieceInstances(context, activationId, rundownId)
expect(allPartInstances).toHaveLength(5)

Expand Down Expand Up @@ -1187,16 +1192,84 @@ describe('Test blueprint api context', () => {
expect(newPartInstance.partInstance.part._rank).toEqual(0.5)
expect(newPartInstance.partInstance.orphaned).toEqual('adlib-part')

const newNextPartInstances = await service.getPieceInstances('next')
expect(newNextPartInstances).toHaveLength(1)
expect(newNextPartInstances[0].partInstanceId).toEqual(
const newNextPieceInstances = await service.getPieceInstances('next')
expect(newNextPieceInstances).toHaveLength(1)
expect(newNextPieceInstances[0].partInstanceId).toEqual(
unprotectString(newPartInstance.partInstance._id)
)

expect(service.nextPartState).toEqual(ActionPartChange.SAFE_CHANGE)
expect(service.currentPartState).toEqual(ActionPartChange.NONE)
})
})

test('queued part does not hijack infinites from following parts', async () => {
// makes sure that infinites which would normally start in the part AFTER the part that is being queued,
// are not and starting in the queued part itself

const { jobContext, playlistId, rundownId } = await setupMyDefaultRundown(
async (context, rundownId) => {
const secondPart = await context.mockCollections.Parts.findOne({ externalId: 'MOCK_PART_0_1' })
if (!secondPart) throw Error('could not find mock part')
const piece001: Piece = {
_id: protectString(rundownId + '_piece012'),
externalId: 'MOCK_PIECE_012',
startRundownId: rundownId,
startSegmentId: secondPart.segmentId,
startPartId: secondPart._id,
name: 'Piece 012',
enable: {
start: 0,
},
sourceLayerId: '',
outputLayerId: '',
pieceType: IBlueprintPieceType.Normal,
lifespan: PieceLifespan.OutOnSegmentEnd,
invalid: false,
content: {},
timelineObjectsString: EmptyPieceTimelineObjectsBlob,
}
await context.mockCollections.Pieces.insertOne(piece001)
}
)

const partInstance = (await jobContext.mockCollections.PartInstances.findOne({
rundownId,
})) as DBPartInstance
expect(partInstance).toBeTruthy()
await setPartInstances(jobContext, playlistId, partInstance, undefined)

await wrapWithPlayoutModel(jobContext, playlistId, async (playoutModel) => {
const { service } = await getTestee(jobContext, playoutModel)

const newPiece: IBlueprintPiece = {
name: 'test piece',
sourceLayerId: 'sl1',
outputLayerId: 'o1',
externalId: '-',
enable: { start: 0 },
lifespan: PieceLifespan.OutOnRundownEnd,
content: {
timelineObjects: [],
},
}
const newPart: IBlueprintPart = {
externalId: 'nope',
title: 'something',
}

// Create it with most of the real flow
postProcessPiecesMock.mockImplementationOnce(postProcessPiecesOrig)
insertQueuedPartWithPiecesMock.mockImplementationOnce(insertQueuedPartWithPiecesOrig)
expect((await service.queuePart(newPart, [newPiece]))._id).toEqual(
playoutModel.playlist.nextPartInfo?.partInstanceId
)

const newNextPartInstances = await service.getPieceInstances('next')
expect(newNextPartInstances).toHaveLength(1)
expect(newNextPartInstances[0].piece.name).toBe('test piece')
})
})
})

describe('insertPiece', () => {
Expand Down
1 change: 0 additions & 1 deletion packages/job-worker/src/ingest/__tests__/ingest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1733,7 +1733,6 @@ describe('Test ingest actions for rundowns and segments', () => {
currentPartInstance,
{
_id: protectString(`after_${currentPartInstance.partInstance._id}_part`),
_rank: 0,
externalId: `after_${currentPartInstance.partInstance._id}_externalId`,
title: 'New part',
expectedDurationWithTransition: undefined,
Expand Down
34 changes: 11 additions & 23 deletions packages/job-worker/src/playout/adlibUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,41 +187,29 @@ export async function innerFindLastScriptedPieceOnLayer(
return fullPiece
}

function updateRankForAdlibbedPartInstance(
_context: JobContext,
playoutModel: PlayoutModel,
newPartInstance: PlayoutPartInstanceModel
) {
const currentPartInstance = playoutModel.currentPartInstance
if (!currentPartInstance) throw new Error('CurrentPartInstance not found')

// Parts are always integers spaced by one, and orphaned PartInstances will be decimals spaced between two Part
// so we can predict a 'safe' rank to get the desired position with some simple maths
newPartInstance.setRank(
getRank(
currentPartInstance.partInstance.part._rank,
Math.floor(currentPartInstance.partInstance.part._rank + 1)
)
)

updatePartInstanceRanksAfterAdlib(playoutModel, currentPartInstance, newPartInstance)
}

export async function insertQueuedPartWithPieces(
context: JobContext,
playoutModel: PlayoutModel,
rundown: PlayoutRundownModel,
currentPartInstance: PlayoutPartInstanceModel,
newPart: Omit<DBPart, 'segmentId' | 'rundownId'>,
newPart: Omit<DBPart, 'segmentId' | 'rundownId' | '_rank'>,
initialPieces: Omit<PieceInstancePiece, 'startPartId'>[],
fromAdlibId: PieceId | undefined
): Promise<PlayoutPartInstanceModel> {
const span = context.startSpan('insertQueuedPartWithPieces')

// Parts are always integers spaced by one, and orphaned PartInstances will be decimals spaced between two Part
// so we can predict a 'safe' rank to get the desired position with some simple maths
const newRank = getRank(
currentPartInstance.partInstance.part._rank,
Math.floor(currentPartInstance.partInstance.part._rank + 1)
)

const newPartFull: DBPart = {
...newPart,
segmentId: currentPartInstance.partInstance.segmentId,
rundownId: currentPartInstance.partInstance.rundownId,
_rank: newRank,
}

// Find any rundown defined infinites that we should inherit
Expand All @@ -237,13 +225,13 @@ export async function insertQueuedPartWithPieces(
)

const newPartInstance = playoutModel.createAdlibbedPartInstance(
newPart,
newPartFull,
initialPieces,
fromAdlibId,
infinitePieceInstances
)

updateRankForAdlibbedPartInstance(context, playoutModel, newPartInstance)
updatePartInstanceRanksAfterAdlib(playoutModel, currentPartInstance, newPartInstance)

await setNextPart(context, playoutModel, newPartInstance, false)

Expand Down
Loading