Skip to content

Commit cdb6c90

Browse files
authored
Merge pull request Sofie-Automation#1261 from bbc/upstream/SUPERFLY-5-ab-sessions-across-parts
AB session issues
2 parents 506b920 + 250b5d7 commit cdb6c90

File tree

16 files changed

+575
-223
lines changed

16 files changed

+575
-223
lines changed

packages/corelib/src/dataModel/RundownPlaylist.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ export interface ABSessionInfo {
1717
id: string
1818
/** The name of the session from the blueprints */
1919
name: string
20+
/** Whether the name is treated as globally unique */
21+
isUniqueName: boolean
2022
/** Set if the session is being by lookahead for a future part */
2123
lookaheadForPartId?: PartId
2224
/** Set if the session is being used by an infinite PieceInstance */

packages/job-worker/src/blueprints/context/OnTimelineGenerateContext.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,13 @@ export class OnTimelineGenerateContext extends RundownContext implements ITimeli
8585
const partInstanceId = pieceInstance?.partInstanceId
8686
if (!partInstanceId) throw new Error('Missing partInstanceId in call to getPieceABSessionId')
8787

88-
return this.abSessionsHelper.getPieceABSessionId(pieceInstance, sessionName)
88+
return this.abSessionsHelper.getPieceABSessionIdFromSessionName(pieceInstance, sessionName)
8989
}
9090

9191
/**
9292
* @deprecated Use core provided AB resolving
9393
*/
9494
getTimelineObjectAbSessionId(tlObj: OnGenerateTimelineObjExt, sessionName: string): string | undefined {
95-
return this.abSessionsHelper.getTimelineObjectAbSessionId(tlObj, sessionName)
95+
return this.abSessionsHelper.getTimelineObjectAbSessionIdFromSessionName(tlObj, sessionName)
9696
}
9797
}

packages/job-worker/src/playout/abPlayback/__tests__/abPlayback.spec.ts

Lines changed: 147 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { ABResolverOptions, IBlueprintPieceType, PieceLifespan } from '@sofie-automation/blueprints-integration'
1+
import {
2+
ABResolverOptions,
3+
IBlueprintPieceType,
4+
PieceAbSessionInfo,
5+
PieceLifespan,
6+
} from '@sofie-automation/blueprints-integration'
27
import { EmptyPieceTimelineObjectsBlob } from '@sofie-automation/corelib/dist/dataModel/Piece'
38
import { PieceInstancePiece, ResolvedPieceInstance } from '@sofie-automation/corelib/dist/dataModel/PieceInstance'
49
import { ABSessionAssignments } from '@sofie-automation/corelib/dist/dataModel/RundownPlaylist'
@@ -21,7 +26,8 @@ function createBasicResolvedPieceInstance(
2126
start: number,
2227
duration: number | undefined,
2328
reqId: string | undefined,
24-
optional?: boolean
29+
optional?: boolean,
30+
uniqueSessionName?: boolean
2531
): ResolvedPieceInstance {
2632
const piece = literal<PieceInstancePiece>({
2733
_id: protectString(id),
@@ -47,6 +53,7 @@ function createBasicResolvedPieceInstance(
4753
sessionName: reqId,
4854
poolName: POOL_NAME,
4955
optional: optional,
56+
sessionNameIsGloballyUnique: uniqueSessionName,
5057
},
5158
]
5259
}
@@ -120,7 +127,9 @@ describe('resolveMediaPlayers', () => {
120127
createBasicResolvedPieceInstance('2', 800, 4000, 'ghi'),
121128
]
122129

123-
mockGetPieceSessionId.mockImplementation((piece, name) => `${piece._id}_${name}`)
130+
mockGetPieceSessionId.mockImplementation(
131+
(piece, session) => `${piece._id}_${session.poolName}_${session.sessionName}`
132+
)
124133

125134
const assignments = resolveAbSessions(
126135
abSessionHelper,
@@ -143,9 +152,18 @@ describe('resolveMediaPlayers', () => {
143152

144153
expect(mockGetPieceSessionId).toHaveBeenCalledTimes(3)
145154
expect(mockGetObjectSessionId).toHaveBeenCalledTimes(0)
146-
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, 'clip_abc')
147-
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, 'clip_def')
148-
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, 'clip_ghi')
155+
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, {
156+
poolName: 'clip',
157+
sessionName: 'abc',
158+
} satisfies PieceAbSessionInfo)
159+
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, {
160+
poolName: 'clip',
161+
sessionName: 'def',
162+
} satisfies PieceAbSessionInfo)
163+
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, {
164+
poolName: 'clip',
165+
sessionName: 'ghi',
166+
} satisfies PieceAbSessionInfo)
149167
})
150168

151169
test('basic pieces - players with string Ids', () => {
@@ -156,7 +174,9 @@ describe('resolveMediaPlayers', () => {
156174
createBasicResolvedPieceInstance('2', 800, 4000, 'ghi'),
157175
]
158176

159-
mockGetPieceSessionId.mockImplementation((piece, name) => `${piece._id}_${name}`)
177+
mockGetPieceSessionId.mockImplementation(
178+
(piece, session) => `${piece._id}_${session.poolName}_${session.sessionName}`
179+
)
160180

161181
const assignments = resolveAbSessions(
162182
abSessionHelper,
@@ -179,9 +199,18 @@ describe('resolveMediaPlayers', () => {
179199

180200
expect(mockGetPieceSessionId).toHaveBeenCalledTimes(3)
181201
expect(mockGetObjectSessionId).toHaveBeenCalledTimes(0)
182-
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, 'clip_abc')
183-
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, 'clip_def')
184-
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, 'clip_ghi')
202+
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, {
203+
poolName: 'clip',
204+
sessionName: 'abc',
205+
} satisfies PieceAbSessionInfo)
206+
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, {
207+
poolName: 'clip',
208+
sessionName: 'def',
209+
} satisfies PieceAbSessionInfo)
210+
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, {
211+
poolName: 'clip',
212+
sessionName: 'ghi',
213+
} satisfies PieceAbSessionInfo)
185214
})
186215

187216
test('basic pieces - players with number and string Ids', () => {
@@ -192,7 +221,9 @@ describe('resolveMediaPlayers', () => {
192221
createBasicResolvedPieceInstance('2', 800, 4000, 'ghi'),
193222
]
194223

195-
mockGetPieceSessionId.mockImplementation((piece, name) => `${piece._id}_${name}`)
224+
mockGetPieceSessionId.mockImplementation(
225+
(piece, session) => `${piece._id}_${session.poolName}_${session.sessionName}`
226+
)
196227

197228
const assignments = resolveAbSessions(
198229
abSessionHelper,
@@ -215,9 +246,18 @@ describe('resolveMediaPlayers', () => {
215246

216247
expect(mockGetPieceSessionId).toHaveBeenCalledTimes(3)
217248
expect(mockGetObjectSessionId).toHaveBeenCalledTimes(0)
218-
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, 'clip_abc')
219-
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, 'clip_def')
220-
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, 'clip_ghi')
249+
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, {
250+
poolName: 'clip',
251+
sessionName: 'abc',
252+
} satisfies PieceAbSessionInfo)
253+
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, {
254+
poolName: 'clip',
255+
sessionName: 'def',
256+
} satisfies PieceAbSessionInfo)
257+
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, {
258+
poolName: 'clip',
259+
sessionName: 'ghi',
260+
} satisfies PieceAbSessionInfo)
221261
})
222262

223263
test('Multiple pieces same id', () => {
@@ -229,7 +269,7 @@ describe('resolveMediaPlayers', () => {
229269
createBasicResolvedPieceInstance('3', 6400, 1000, 'abc'), // Gap before
230270
]
231271

232-
mockGetPieceSessionId.mockImplementation((_piece, name) => `tmp_${name}`)
272+
mockGetPieceSessionId.mockImplementation((_piece, session) => `tmp_${session.poolName}_${session.sessionName}`)
233273

234274
const assignments = resolveAbSessions(
235275
abSessionHelper,
@@ -250,10 +290,22 @@ describe('resolveMediaPlayers', () => {
250290

251291
expect(mockGetPieceSessionId).toHaveBeenCalledTimes(4)
252292
expect(mockGetObjectSessionId).toHaveBeenCalledTimes(0)
253-
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, 'clip_abc')
254-
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, 'clip_abc')
255-
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, 'clip_abc')
256-
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(4, pieces[3].instance, 'clip_abc')
293+
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, {
294+
poolName: 'clip',
295+
sessionName: 'abc',
296+
} satisfies PieceAbSessionInfo)
297+
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, {
298+
poolName: 'clip',
299+
sessionName: 'abc',
300+
} satisfies PieceAbSessionInfo)
301+
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, {
302+
poolName: 'clip',
303+
sessionName: 'abc',
304+
} satisfies PieceAbSessionInfo)
305+
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(4, pieces[3].instance, {
306+
poolName: 'clip',
307+
sessionName: 'abc',
308+
} satisfies PieceAbSessionInfo)
257309
})
258310

259311
test('Reuse after gap', () => {
@@ -264,7 +316,9 @@ describe('resolveMediaPlayers', () => {
264316
createBasicResolvedPieceInstance('3', 6400, 1000, 'ghi'), // Wait, then reuse first
265317
]
266318

267-
mockGetPieceSessionId.mockImplementation((piece, name) => `${piece._id}_${name}`)
319+
mockGetPieceSessionId.mockImplementation(
320+
(piece, session) => `${piece._id}_${session.poolName}_${session.sessionName}`
321+
)
268322

269323
const assignments = resolveAbSessions(
270324
abSessionHelper,
@@ -287,9 +341,18 @@ describe('resolveMediaPlayers', () => {
287341

288342
expect(mockGetPieceSessionId).toHaveBeenCalledTimes(3)
289343
expect(mockGetObjectSessionId).toHaveBeenCalledTimes(0)
290-
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, 'clip_abc')
291-
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, 'clip_def')
292-
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, 'clip_ghi')
344+
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, {
345+
poolName: 'clip',
346+
sessionName: 'abc',
347+
} satisfies PieceAbSessionInfo)
348+
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, {
349+
poolName: 'clip',
350+
sessionName: 'def',
351+
} satisfies PieceAbSessionInfo)
352+
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, {
353+
poolName: 'clip',
354+
sessionName: 'ghi',
355+
} satisfies PieceAbSessionInfo)
293356
})
294357

295358
test('Reuse immediately', () => {
@@ -300,7 +363,9 @@ describe('resolveMediaPlayers', () => {
300363
createBasicResolvedPieceInstance('3', 5400, 1000, 'ghi'), // Wait, then reuse first
301364
]
302365

303-
mockGetPieceSessionId.mockImplementation((piece, name) => `${piece._id}_${name}`)
366+
mockGetPieceSessionId.mockImplementation(
367+
(piece, session) => `${piece._id}_${session.poolName}_${session.sessionName}`
368+
)
304369

305370
const assignments = resolveAbSessions(
306371
abSessionHelper,
@@ -323,9 +388,18 @@ describe('resolveMediaPlayers', () => {
323388

324389
expect(mockGetPieceSessionId).toHaveBeenCalledTimes(3)
325390
expect(mockGetObjectSessionId).toHaveBeenCalledTimes(0)
326-
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, 'clip_abc')
327-
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, 'clip_def')
328-
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, 'clip_ghi')
391+
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, {
392+
poolName: 'clip',
393+
sessionName: 'abc',
394+
} satisfies PieceAbSessionInfo)
395+
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, {
396+
poolName: 'clip',
397+
sessionName: 'def',
398+
} satisfies PieceAbSessionInfo)
399+
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, {
400+
poolName: 'clip',
401+
sessionName: 'ghi',
402+
} satisfies PieceAbSessionInfo)
329403
})
330404

331405
test('Reuse immediately dense', () => {
@@ -336,7 +410,9 @@ describe('resolveMediaPlayers', () => {
336410
createBasicResolvedPieceInstance('3', 5400, 1000, 'ghi'), // Wait, then reuse first
337411
]
338412

339-
mockGetPieceSessionId.mockImplementation((piece, name) => `${piece._id}_${name}`)
413+
mockGetPieceSessionId.mockImplementation(
414+
(piece, session) => `${piece._id}_${session.poolName}_${session.sessionName}`
415+
)
340416

341417
const assignments = resolveAbSessions(
342418
abSessionHelper,
@@ -359,9 +435,18 @@ describe('resolveMediaPlayers', () => {
359435

360436
expect(mockGetPieceSessionId).toHaveBeenCalledTimes(3)
361437
expect(mockGetObjectSessionId).toHaveBeenCalledTimes(0)
362-
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, 'clip_abc')
363-
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, 'clip_def')
364-
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, 'clip_ghi')
438+
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, {
439+
poolName: 'clip',
440+
sessionName: 'abc',
441+
} satisfies PieceAbSessionInfo)
442+
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, {
443+
poolName: 'clip',
444+
sessionName: 'def',
445+
} satisfies PieceAbSessionInfo)
446+
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, {
447+
poolName: 'clip',
448+
sessionName: 'ghi',
449+
} satisfies PieceAbSessionInfo)
365450
})
366451

367452
test('basic reassignment', () => {
@@ -383,7 +468,9 @@ describe('resolveMediaPlayers', () => {
383468
createBasicResolvedPieceInstance('2', 2800, 4000, 'ghi'),
384469
]
385470

386-
mockGetPieceSessionId.mockImplementation((piece, name) => `${piece._id}_${name}`)
471+
mockGetPieceSessionId.mockImplementation(
472+
(piece, session) => `${piece._id}_${session.poolName}_${session.sessionName}`
473+
)
387474

388475
const assignments = resolveAbSessions(
389476
abSessionHelper,
@@ -406,9 +493,18 @@ describe('resolveMediaPlayers', () => {
406493

407494
expect(mockGetPieceSessionId).toHaveBeenCalledTimes(3)
408495
expect(mockGetObjectSessionId).toHaveBeenCalledTimes(0)
409-
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, 'clip_abc')
410-
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, 'clip_def')
411-
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, 'clip_ghi')
496+
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, {
497+
poolName: 'clip',
498+
sessionName: 'abc',
499+
} satisfies PieceAbSessionInfo)
500+
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, {
501+
poolName: 'clip',
502+
sessionName: 'def',
503+
} satisfies PieceAbSessionInfo)
504+
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, {
505+
poolName: 'clip',
506+
sessionName: 'ghi',
507+
} satisfies PieceAbSessionInfo)
412508
})
413509

414510
test('optional gets discarded', () => {
@@ -430,7 +526,9 @@ describe('resolveMediaPlayers', () => {
430526
createBasicResolvedPieceInstance('2', 2800, 4000, 'ghi'),
431527
]
432528

433-
mockGetPieceSessionId.mockImplementation((piece, name) => `${piece._id}_${name}`)
529+
mockGetPieceSessionId.mockImplementation(
530+
(piece, session) => `${piece._id}_${session.poolName}_${session.sessionName}`
531+
)
434532

435533
const assignments = resolveAbSessions(
436534
abSessionHelper,
@@ -453,9 +551,19 @@ describe('resolveMediaPlayers', () => {
453551

454552
expect(mockGetPieceSessionId).toHaveBeenCalledTimes(3)
455553
expect(mockGetObjectSessionId).toHaveBeenCalledTimes(0)
456-
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, 'clip_abc')
457-
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, 'clip_def')
458-
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, 'clip_ghi')
554+
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, {
555+
poolName: 'clip',
556+
sessionName: 'abc',
557+
} satisfies PieceAbSessionInfo)
558+
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, {
559+
poolName: 'clip',
560+
sessionName: 'def',
561+
optional: true,
562+
} satisfies PieceAbSessionInfo)
563+
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, {
564+
poolName: 'clip',
565+
sessionName: 'ghi',
566+
} satisfies PieceAbSessionInfo)
459567
})
460568

461569
// TODO add some tests which check lookahead

0 commit comments

Comments
 (0)