Skip to content

Commit dc3f171

Browse files
authored
feat: allow defining OnAir only timeline objects (Sofie-Automation#1642)
1 parent 70690cc commit dc3f171

13 files changed

Lines changed: 1070 additions & 79 deletions

File tree

packages/blueprints-integration/src/timeline.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export { TSR }
55

66
export {
77
TimelineObjHoldMode,
8+
TimelineObjOnAirMode,
89
TimelineObjectCoreExt,
910
TimelineKeyframeCoreExt,
1011
} from '@sofie-automation/shared-lib/dist/core/model/Timeline'

packages/job-worker/src/playout/lookahead/__tests__/findForLayer.test.ts

Lines changed: 203 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@ type TfindLookaheadObjectsForPart = jest.MockedFunction<typeof findLookaheadObje
1212
const findLookaheadObjectsForPartMock = findLookaheadObjectsForPart as TfindLookaheadObjectsForPart
1313
findLookaheadObjectsForPartMock.mockImplementation(() => []) // Default mock
1414

15+
const DEFAULT_PLAYOUT_STATE = { isInHold: false, isRehearsal: false }
16+
1517
describe('findLookaheadForLayer', () => {
1618
const context = setupDefaultJobEnvironment()
1719

1820
test('no data', () => {
19-
const res = findLookaheadForLayer(context, null, [], undefined, [], 'abc', 1, 1)
21+
const res = findLookaheadForLayer(context, null, [], undefined, [], 'abc', 1, 1, DEFAULT_PLAYOUT_STATE)
2022
expect(res.timed).toHaveLength(0)
2123
expect(res.future).toHaveLength(0)
2224
})
@@ -38,7 +40,8 @@ describe('findLookaheadForLayer', () => {
3840
usesInTransition: false,
3941
pieces: partInstanceInfo.allPieces,
4042
},
41-
partInstanceInfo.part._id
43+
partInstanceInfo.part._id,
44+
expect.objectContaining({ isInHold: expect.any(Boolean), isRehearsal: expect.any(Boolean) })
4245
)
4346
}
4447

@@ -88,7 +91,17 @@ describe('findLookaheadForLayer', () => {
8891
.mockReturnValueOnce(['t4', 't5'] as any)
8992

9093
// Run it
91-
const res = findLookaheadForLayer(context, null, partInstancesInfo, undefined, [], layer, 1, 1)
94+
const res = findLookaheadForLayer(
95+
context,
96+
null,
97+
partInstancesInfo,
98+
undefined,
99+
[],
100+
layer,
101+
1,
102+
1,
103+
DEFAULT_PLAYOUT_STATE
104+
)
92105
expect(res.timed).toEqual(['t0', 't1', 't2', 't3'])
93106
expect(res.future).toEqual(['t4', 't5'])
94107

@@ -105,7 +118,17 @@ describe('findLookaheadForLayer', () => {
105118
onTimeline: true,
106119
} as any
107120
findLookaheadObjectsForPartMock.mockReset().mockReturnValue([])
108-
findLookaheadForLayer(context, null, partInstancesInfo, previousPartInfo, [], layer, 1, 1)
121+
findLookaheadForLayer(
122+
context,
123+
null,
124+
partInstancesInfo,
125+
previousPartInfo,
126+
[],
127+
layer,
128+
1,
129+
1,
130+
DEFAULT_PLAYOUT_STATE
131+
)
109132
expect(findLookaheadObjectsForPartMock).toHaveBeenCalledTimes(3)
110133
expectInstancesToMatch(1, layer, partInstancesInfo[0], previousPartInfo)
111134

@@ -117,7 +140,17 @@ describe('findLookaheadForLayer', () => {
117140
.mockReturnValueOnce(['t2', 't3'] as any)
118141
.mockReturnValueOnce(['t4', 't5'] as any)
119142

120-
const res2 = findLookaheadForLayer(context, null, partInstancesInfo, undefined, [], layer, 1, 0)
143+
const res2 = findLookaheadForLayer(
144+
context,
145+
null,
146+
partInstancesInfo,
147+
undefined,
148+
[],
149+
layer,
150+
1,
151+
0,
152+
DEFAULT_PLAYOUT_STATE
153+
)
121154
expect(res2.timed).toEqual(['t0', 't1', 't2', 't3'])
122155
expect(res2.future).toHaveLength(0)
123156
expect(findLookaheadObjectsForPartMock).toHaveBeenCalledTimes(2)
@@ -138,7 +171,8 @@ describe('findLookaheadForLayer', () => {
138171
layer,
139172
previousPart,
140173
partInfo,
141-
null
174+
null,
175+
expect.objectContaining({ isInHold: expect.any(Boolean), isRehearsal: expect.any(Boolean) })
142176
)
143177
}
144178

@@ -167,21 +201,51 @@ describe('findLookaheadForLayer', () => {
167201
.mockReturnValueOnce(['t8', 't9'] as any)
168202

169203
// Cant search far enough
170-
const res = findLookaheadForLayer(context, null, [], undefined, orderedParts, layer, 1, 1)
204+
const res = findLookaheadForLayer(
205+
context,
206+
null,
207+
[],
208+
undefined,
209+
orderedParts,
210+
layer,
211+
1,
212+
1,
213+
DEFAULT_PLAYOUT_STATE
214+
)
171215
expect(res.timed).toHaveLength(0)
172216
expect(res.future).toHaveLength(0)
173217
expect(findLookaheadObjectsForPartMock).toHaveBeenCalledTimes(0)
174218

175219
// Find the target of 1
176-
const res2 = findLookaheadForLayer(context, null, [], undefined, orderedParts, layer, 1, 4)
220+
const res2 = findLookaheadForLayer(
221+
context,
222+
null,
223+
[],
224+
undefined,
225+
orderedParts,
226+
layer,
227+
1,
228+
4,
229+
DEFAULT_PLAYOUT_STATE
230+
)
177231
expect(res2.timed).toHaveLength(0)
178232
expect(res2.future).toEqual(['t0', 't1'])
179233
expect(findLookaheadObjectsForPartMock).toHaveBeenCalledTimes(1)
180234
expectPartToMatch(1, layer, orderedParts[0], undefined)
181235

182236
// Find the target of 0
183237
findLookaheadObjectsForPartMock.mockReset().mockReturnValue([])
184-
const res3 = findLookaheadForLayer(context, null, [], undefined, orderedParts, layer, 0, 4)
238+
const res3 = findLookaheadForLayer(
239+
context,
240+
null,
241+
[],
242+
undefined,
243+
orderedParts,
244+
layer,
245+
0,
246+
4,
247+
DEFAULT_PLAYOUT_STATE
248+
)
185249
expect(res3.timed).toHaveLength(0)
186250
expect(res3.future).toHaveLength(0)
187251
expect(findLookaheadObjectsForPartMock).toHaveBeenCalledTimes(0)
@@ -196,12 +260,141 @@ describe('findLookaheadForLayer', () => {
196260
.mockReturnValueOnce(['t6', 't7'] as any)
197261
.mockReturnValueOnce(['t8', 't9'] as any)
198262

199-
const res4 = findLookaheadForLayer(context, null, [], undefined, orderedParts, layer, 100, 5)
263+
const res4 = findLookaheadForLayer(
264+
context,
265+
null,
266+
[],
267+
undefined,
268+
orderedParts,
269+
layer,
270+
100,
271+
5,
272+
DEFAULT_PLAYOUT_STATE
273+
)
200274
expect(res4.timed).toHaveLength(0)
201275
expect(res4.future).toEqual(['t0', 't1', 't2', 't3', 't4', 't5'])
202276
expect(findLookaheadObjectsForPartMock).toHaveBeenCalledTimes(3)
203277
expectPartToMatch(1, layer, orderedParts[0], undefined)
204278
expectPartToMatch(2, layer, orderedParts[2], orderedParts[0].part)
205279
expectPartToMatch(3, layer, orderedParts[3], orderedParts[2].part)
206280
})
281+
282+
test('playoutState propagates to findLookaheadObjectsForPart for partInstances', () => {
283+
const layer = getRandomString()
284+
const partInstancesInfo: PartInstanceAndPieceInstances[] = [
285+
{
286+
part: { _id: '1', part: '1p' },
287+
allPieces: [createFakePiece('1')],
288+
onTimeline: true,
289+
nowInPart: 2000,
290+
calculatedTimings: { inTransitionStart: null },
291+
},
292+
{
293+
part: { _id: '2', part: '2p' },
294+
allPieces: [createFakePiece('2')],
295+
onTimeline: false,
296+
nowInPart: 0,
297+
calculatedTimings: { inTransitionStart: null },
298+
},
299+
] as any
300+
301+
findLookaheadObjectsForPartMock.mockReset().mockReturnValue([])
302+
303+
// Test with isInHold: true
304+
findLookaheadForLayer(context, null, partInstancesInfo, undefined, [], layer, 1, 1, {
305+
isInHold: true,
306+
isRehearsal: false,
307+
})
308+
309+
expect(findLookaheadObjectsForPartMock).toHaveBeenCalledTimes(2)
310+
expect(findLookaheadObjectsForPartMock).toHaveBeenNthCalledWith(
311+
1,
312+
context,
313+
null,
314+
layer,
315+
undefined,
316+
expect.any(Object),
317+
partInstancesInfo[0].part._id,
318+
{ isInHold: true, isRehearsal: false }
319+
)
320+
expect(findLookaheadObjectsForPartMock).toHaveBeenNthCalledWith(
321+
2,
322+
context,
323+
null,
324+
layer,
325+
partInstancesInfo[0].part.part,
326+
expect.any(Object),
327+
partInstancesInfo[1].part._id,
328+
{ isInHold: true, isRehearsal: false }
329+
)
330+
331+
// Test with isRehearsal: true
332+
findLookaheadObjectsForPartMock.mockReset().mockReturnValue([])
333+
findLookaheadForLayer(context, null, partInstancesInfo, undefined, [], layer, 1, 1, {
334+
isInHold: false,
335+
isRehearsal: true,
336+
})
337+
338+
expect(findLookaheadObjectsForPartMock).toHaveBeenCalledTimes(2)
339+
expect(findLookaheadObjectsForPartMock).toHaveBeenNthCalledWith(
340+
1,
341+
context,
342+
null,
343+
layer,
344+
undefined,
345+
expect.any(Object),
346+
partInstancesInfo[0].part._id,
347+
{ isInHold: false, isRehearsal: true }
348+
)
349+
expect(findLookaheadObjectsForPartMock).toHaveBeenNthCalledWith(
350+
2,
351+
context,
352+
null,
353+
layer,
354+
partInstancesInfo[0].part.part,
355+
expect.any(Object),
356+
partInstancesInfo[1].part._id,
357+
{ isInHold: false, isRehearsal: true }
358+
)
359+
})
360+
361+
test('playoutState propagates to findLookaheadObjectsForPart for parts', () => {
362+
const layer = getRandomString()
363+
const orderedParts: PartAndPieces[] = [{ _id: 'p1' }, { _id: 'p2' }].map((p) => ({
364+
part: p as any,
365+
usesInTransition: true,
366+
pieces: [{ _id: p._id + '_p1' } as any],
367+
}))
368+
369+
findLookaheadObjectsForPartMock.mockReset().mockReturnValue([])
370+
371+
// Test with both flags set - orderedParts (future parts) always get isInHold: false with includeWhenNotInHoldObjects: true
372+
findLookaheadForLayer(context, null, [], undefined, orderedParts, layer, 100, 5, {
373+
isInHold: true,
374+
isRehearsal: true,
375+
})
376+
377+
expect(findLookaheadObjectsForPartMock).toHaveBeenCalledTimes(2)
378+
// All future parts get modified playoutState (isInHold forced to false, includeWhenNotInHoldObjects added)
379+
expect(findLookaheadObjectsForPartMock).toHaveBeenNthCalledWith(
380+
1,
381+
context,
382+
null,
383+
layer,
384+
undefined,
385+
orderedParts[0],
386+
null,
387+
{ isInHold: false, isRehearsal: true, includeWhenNotInHoldObjects: true }
388+
)
389+
expect(findLookaheadObjectsForPartMock).toHaveBeenNthCalledWith(
390+
2,
391+
context,
392+
null,
393+
layer,
394+
orderedParts[0].part,
395+
orderedParts[1],
396+
null,
397+
{ isInHold: false, isRehearsal: true, includeWhenNotInHoldObjects: true }
398+
)
399+
})
207400
})

0 commit comments

Comments
 (0)