Skip to content

Commit

Permalink
Merge pull request #1261 from bbc/upstream/SUPERFLY-5-ab-sessions-acr…
Browse files Browse the repository at this point in the history
…oss-parts

AB session issues
  • Loading branch information
nytamin committed Sep 25, 2024
2 parents 506b920 + 250b5d7 commit cdb6c90
Show file tree
Hide file tree
Showing 16 changed files with 575 additions and 223 deletions.
2 changes: 2 additions & 0 deletions packages/corelib/src/dataModel/RundownPlaylist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export interface ABSessionInfo {
id: string
/** The name of the session from the blueprints */
name: string
/** Whether the name is treated as globally unique */
isUniqueName: boolean
/** Set if the session is being by lookahead for a future part */
lookaheadForPartId?: PartId
/** Set if the session is being used by an infinite PieceInstance */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,13 @@ export class OnTimelineGenerateContext extends RundownContext implements ITimeli
const partInstanceId = pieceInstance?.partInstanceId
if (!partInstanceId) throw new Error('Missing partInstanceId in call to getPieceABSessionId')

return this.abSessionsHelper.getPieceABSessionId(pieceInstance, sessionName)
return this.abSessionsHelper.getPieceABSessionIdFromSessionName(pieceInstance, sessionName)
}

/**
* @deprecated Use core provided AB resolving
*/
getTimelineObjectAbSessionId(tlObj: OnGenerateTimelineObjExt, sessionName: string): string | undefined {
return this.abSessionsHelper.getTimelineObjectAbSessionId(tlObj, sessionName)
return this.abSessionsHelper.getTimelineObjectAbSessionIdFromSessionName(tlObj, sessionName)
}
}
186 changes: 147 additions & 39 deletions packages/job-worker/src/playout/abPlayback/__tests__/abPlayback.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { ABResolverOptions, IBlueprintPieceType, PieceLifespan } from '@sofie-automation/blueprints-integration'
import {
ABResolverOptions,
IBlueprintPieceType,
PieceAbSessionInfo,
PieceLifespan,
} from '@sofie-automation/blueprints-integration'
import { EmptyPieceTimelineObjectsBlob } from '@sofie-automation/corelib/dist/dataModel/Piece'
import { PieceInstancePiece, ResolvedPieceInstance } from '@sofie-automation/corelib/dist/dataModel/PieceInstance'
import { ABSessionAssignments } from '@sofie-automation/corelib/dist/dataModel/RundownPlaylist'
Expand All @@ -21,7 +26,8 @@ function createBasicResolvedPieceInstance(
start: number,
duration: number | undefined,
reqId: string | undefined,
optional?: boolean
optional?: boolean,
uniqueSessionName?: boolean
): ResolvedPieceInstance {
const piece = literal<PieceInstancePiece>({
_id: protectString(id),
Expand All @@ -47,6 +53,7 @@ function createBasicResolvedPieceInstance(
sessionName: reqId,
poolName: POOL_NAME,
optional: optional,
sessionNameIsGloballyUnique: uniqueSessionName,
},
]
}
Expand Down Expand Up @@ -120,7 +127,9 @@ describe('resolveMediaPlayers', () => {
createBasicResolvedPieceInstance('2', 800, 4000, 'ghi'),
]

mockGetPieceSessionId.mockImplementation((piece, name) => `${piece._id}_${name}`)
mockGetPieceSessionId.mockImplementation(
(piece, session) => `${piece._id}_${session.poolName}_${session.sessionName}`
)

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

expect(mockGetPieceSessionId).toHaveBeenCalledTimes(3)
expect(mockGetObjectSessionId).toHaveBeenCalledTimes(0)
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, 'clip_abc')
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, 'clip_def')
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, 'clip_ghi')
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, {
poolName: 'clip',
sessionName: 'abc',
} satisfies PieceAbSessionInfo)
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, {
poolName: 'clip',
sessionName: 'def',
} satisfies PieceAbSessionInfo)
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, {
poolName: 'clip',
sessionName: 'ghi',
} satisfies PieceAbSessionInfo)
})

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

mockGetPieceSessionId.mockImplementation((piece, name) => `${piece._id}_${name}`)
mockGetPieceSessionId.mockImplementation(
(piece, session) => `${piece._id}_${session.poolName}_${session.sessionName}`
)

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

expect(mockGetPieceSessionId).toHaveBeenCalledTimes(3)
expect(mockGetObjectSessionId).toHaveBeenCalledTimes(0)
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, 'clip_abc')
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, 'clip_def')
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, 'clip_ghi')
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, {
poolName: 'clip',
sessionName: 'abc',
} satisfies PieceAbSessionInfo)
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, {
poolName: 'clip',
sessionName: 'def',
} satisfies PieceAbSessionInfo)
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, {
poolName: 'clip',
sessionName: 'ghi',
} satisfies PieceAbSessionInfo)
})

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

mockGetPieceSessionId.mockImplementation((piece, name) => `${piece._id}_${name}`)
mockGetPieceSessionId.mockImplementation(
(piece, session) => `${piece._id}_${session.poolName}_${session.sessionName}`
)

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

expect(mockGetPieceSessionId).toHaveBeenCalledTimes(3)
expect(mockGetObjectSessionId).toHaveBeenCalledTimes(0)
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, 'clip_abc')
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, 'clip_def')
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, 'clip_ghi')
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, {
poolName: 'clip',
sessionName: 'abc',
} satisfies PieceAbSessionInfo)
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, {
poolName: 'clip',
sessionName: 'def',
} satisfies PieceAbSessionInfo)
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, {
poolName: 'clip',
sessionName: 'ghi',
} satisfies PieceAbSessionInfo)
})

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

mockGetPieceSessionId.mockImplementation((_piece, name) => `tmp_${name}`)
mockGetPieceSessionId.mockImplementation((_piece, session) => `tmp_${session.poolName}_${session.sessionName}`)

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

expect(mockGetPieceSessionId).toHaveBeenCalledTimes(4)
expect(mockGetObjectSessionId).toHaveBeenCalledTimes(0)
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, 'clip_abc')
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, 'clip_abc')
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, 'clip_abc')
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(4, pieces[3].instance, 'clip_abc')
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, {
poolName: 'clip',
sessionName: 'abc',
} satisfies PieceAbSessionInfo)
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, {
poolName: 'clip',
sessionName: 'abc',
} satisfies PieceAbSessionInfo)
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, {
poolName: 'clip',
sessionName: 'abc',
} satisfies PieceAbSessionInfo)
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(4, pieces[3].instance, {
poolName: 'clip',
sessionName: 'abc',
} satisfies PieceAbSessionInfo)
})

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

mockGetPieceSessionId.mockImplementation((piece, name) => `${piece._id}_${name}`)
mockGetPieceSessionId.mockImplementation(
(piece, session) => `${piece._id}_${session.poolName}_${session.sessionName}`
)

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

expect(mockGetPieceSessionId).toHaveBeenCalledTimes(3)
expect(mockGetObjectSessionId).toHaveBeenCalledTimes(0)
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, 'clip_abc')
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, 'clip_def')
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, 'clip_ghi')
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, {
poolName: 'clip',
sessionName: 'abc',
} satisfies PieceAbSessionInfo)
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, {
poolName: 'clip',
sessionName: 'def',
} satisfies PieceAbSessionInfo)
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, {
poolName: 'clip',
sessionName: 'ghi',
} satisfies PieceAbSessionInfo)
})

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

mockGetPieceSessionId.mockImplementation((piece, name) => `${piece._id}_${name}`)
mockGetPieceSessionId.mockImplementation(
(piece, session) => `${piece._id}_${session.poolName}_${session.sessionName}`
)

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

expect(mockGetPieceSessionId).toHaveBeenCalledTimes(3)
expect(mockGetObjectSessionId).toHaveBeenCalledTimes(0)
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, 'clip_abc')
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, 'clip_def')
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, 'clip_ghi')
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, {
poolName: 'clip',
sessionName: 'abc',
} satisfies PieceAbSessionInfo)
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, {
poolName: 'clip',
sessionName: 'def',
} satisfies PieceAbSessionInfo)
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, {
poolName: 'clip',
sessionName: 'ghi',
} satisfies PieceAbSessionInfo)
})

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

mockGetPieceSessionId.mockImplementation((piece, name) => `${piece._id}_${name}`)
mockGetPieceSessionId.mockImplementation(
(piece, session) => `${piece._id}_${session.poolName}_${session.sessionName}`
)

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

expect(mockGetPieceSessionId).toHaveBeenCalledTimes(3)
expect(mockGetObjectSessionId).toHaveBeenCalledTimes(0)
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, 'clip_abc')
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, 'clip_def')
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, 'clip_ghi')
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, {
poolName: 'clip',
sessionName: 'abc',
} satisfies PieceAbSessionInfo)
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, {
poolName: 'clip',
sessionName: 'def',
} satisfies PieceAbSessionInfo)
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, {
poolName: 'clip',
sessionName: 'ghi',
} satisfies PieceAbSessionInfo)
})

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

mockGetPieceSessionId.mockImplementation((piece, name) => `${piece._id}_${name}`)
mockGetPieceSessionId.mockImplementation(
(piece, session) => `${piece._id}_${session.poolName}_${session.sessionName}`
)

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

expect(mockGetPieceSessionId).toHaveBeenCalledTimes(3)
expect(mockGetObjectSessionId).toHaveBeenCalledTimes(0)
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, 'clip_abc')
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, 'clip_def')
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, 'clip_ghi')
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, {
poolName: 'clip',
sessionName: 'abc',
} satisfies PieceAbSessionInfo)
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, {
poolName: 'clip',
sessionName: 'def',
} satisfies PieceAbSessionInfo)
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, {
poolName: 'clip',
sessionName: 'ghi',
} satisfies PieceAbSessionInfo)
})

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

mockGetPieceSessionId.mockImplementation((piece, name) => `${piece._id}_${name}`)
mockGetPieceSessionId.mockImplementation(
(piece, session) => `${piece._id}_${session.poolName}_${session.sessionName}`
)

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

expect(mockGetPieceSessionId).toHaveBeenCalledTimes(3)
expect(mockGetObjectSessionId).toHaveBeenCalledTimes(0)
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, 'clip_abc')
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, 'clip_def')
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, 'clip_ghi')
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(1, pieces[0].instance, {
poolName: 'clip',
sessionName: 'abc',
} satisfies PieceAbSessionInfo)
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(2, pieces[1].instance, {
poolName: 'clip',
sessionName: 'def',
optional: true,
} satisfies PieceAbSessionInfo)
expect(mockGetPieceSessionId).toHaveBeenNthCalledWith(3, pieces[2].instance, {
poolName: 'clip',
sessionName: 'ghi',
} satisfies PieceAbSessionInfo)
})

// TODO add some tests which check lookahead
Expand Down
Loading

0 comments on commit cdb6c90

Please sign in to comment.