Skip to content

Commit 4b6a43c

Browse files
authored
Merge pull request Sofie-Automation#1614 from tv2norge-collab/contribute/EAV-693
fix: converting pieces to infinites and modifying them
2 parents 84ac32c + baf033b commit 4b6a43c

File tree

8 files changed

+140
-9
lines changed

8 files changed

+140
-9
lines changed

packages/blueprints-integration/src/context/onSetAsNextContext.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export interface IOnSetAsNextContext extends IShowStyleUserContext, IEventContex
6969
*/
7070
/** Insert a pieceInstance. Returns id of new PieceInstance. Any timelineObjects will have their ids changed, so are not safe to reference from another piece */
7171
insertPiece(part: 'next', piece: IBlueprintPiece): Promise<IBlueprintPieceInstance>
72-
/** Update a piecesInstance from the partInstance being set as Next */
72+
/** Update a piecesInstance */
7373
updatePieceInstance(pieceInstanceId: string, piece: Partial<IBlueprintPiece>): Promise<IBlueprintPieceInstance>
7474

7575
/** Update a partInstance */

packages/corelib/src/dataModel/PieceInstance.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ export interface PieceInstance {
6161
/** If this piece has been insterted during run of rundown (such as adLibs), then this is set to the timestamp it was inserted */
6262
dynamicallyInserted?: Time
6363

64+
/** If this piece's lifespan has been changed to infinite during run of the rundown (adLib action, onTake, ...), then this is set to the timestamp it was changed */
65+
dynamicallyConvertedToInfinite?: Time
66+
6467
/** This is set when the duration needs to be overriden from some user action */
6568
userDuration?: {
6669
/** The time relative to the part (milliseconds since start of part) */

packages/corelib/src/playout/__tests__/infinites.test.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,47 @@ describe('Infinites', () => {
177177
},
178178
])
179179
})
180+
test('piece dynamically converted to infinite should be continued', () => {
181+
const playlistId = protectString('playlist0')
182+
const rundownId = protectString('rundown0')
183+
const segmentId = protectString('segment0')
184+
const partId = protectString('part0')
185+
const previousPartInstance = { rundownId, segmentId, partId }
186+
const previousSegment = { _id: previousPartInstance.segmentId }
187+
const previousPartPieces: PieceInstance[] = [
188+
{
189+
...createPieceInstanceAsInfinite(
190+
'one',
191+
rundownId,
192+
partId,
193+
{ start: 0 },
194+
'one',
195+
PieceLifespan.OutOnRundownEnd
196+
),
197+
dynamicallyConvertedToInfinite: Date.now(),
198+
},
199+
]
200+
const segment = { _id: segmentId }
201+
const part = { rundownId, segmentId }
202+
const instanceId = protectString('newInstance0')
203+
const rundown = createRundown(rundownId, playlistId, 'Test Rundown', 'rundown0')
204+
205+
const continuedInstances = runAndTidyResult(
206+
previousPartInstance,
207+
previousSegment,
208+
previousPartPieces,
209+
rundown,
210+
segment,
211+
part,
212+
instanceId
213+
)
214+
expect(continuedInstances).toEqual([
215+
{
216+
_id: 'newInstance0_one_p_continue',
217+
start: 0,
218+
},
219+
])
220+
})
180221
test('ignore pieces that have stopped', () => {
181222
const playlistId = protectString('playlist0')
182223
const rundownId = protectString('rundown0')

packages/corelib/src/playout/infinites.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ export function getPlayheadTrackingInfinitesForPart(
181181
// Check if we should persist any adlib onEnd infinites
182182
if (canContinueAdlibOnEnds) {
183183
const piecesByInfiniteMode = groupByToMapFunc(
184-
pieceInstances.filter((p) => p.dynamicallyInserted),
184+
pieceInstances.filter((p) => p.dynamicallyInserted || p.dynamicallyConvertedToInfinite),
185185
(p) => p.piece.lifespan
186186
)
187187
for (const mode0 of [
@@ -194,7 +194,9 @@ export function getPlayheadTrackingInfinitesForPart(
194194
| PieceLifespan.OutOnSegmentEnd
195195
| PieceLifespan.OutOnShowStyleEnd
196196
const pieces = (piecesByInfiniteMode.get(mode) || []).filter(
197-
(p) => p.infinite && (p.infinite.fromPreviousPlayhead || p.dynamicallyInserted)
197+
(p) =>
198+
p.infinite &&
199+
(p.infinite.fromPreviousPlayhead || p.dynamicallyInserted || p.dynamicallyConvertedToInfinite)
198200
)
199201
// This is the piece we may copy across
200202
const candidatePiece =
@@ -277,6 +279,7 @@ export function getPlayheadTrackingInfinitesForPart(
277279
function markPieceInstanceAsContinuation(previousInstance: ReadonlyDeep<PieceInstance>, instance: PieceInstance) {
278280
instance._id = protectString(`${instance._id}_continue`)
279281
instance.dynamicallyInserted = previousInstance.dynamicallyInserted
282+
instance.dynamicallyConvertedToInfinite = previousInstance.dynamicallyConvertedToInfinite
280283
instance.adLibSourceId = previousInstance.adLibSourceId
281284
instance.reportedStartedPlayback = previousInstance.reportedStartedPlayback
282285
instance.plannedStartedPlayback = previousInstance.plannedStartedPlayback

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import { WatchedPackagesHelper } from './watchedPackages.js'
2222
import { PlayoutModel } from '../../playout/model/PlayoutModel.js'
2323
import { ReadonlyDeep } from 'type-fest'
2424
import { getCurrentTime } from '../../lib/index.js'
25-
import { protectString } from '@sofie-automation/corelib/dist/protectedString'
2625
import { BlueprintQuickLookInfo } from '@sofie-automation/blueprints-integration/dist/context/quickLoopInfo'
2726
import { DBPart } from '@sofie-automation/corelib/dist/dataModel/Part'
2827
import { selectNewPartWithOffsets } from '../../playout/moveNextPart.js'
@@ -120,9 +119,6 @@ export class OnSetAsNextContext
120119
pieceInstanceId: string,
121120
piece: Partial<IBlueprintPiece<unknown>>
122121
): Promise<IBlueprintPieceInstance<unknown>> {
123-
if (protectString(pieceInstanceId) === this.playoutModel.playlist.currentPartInfo?.partInstanceId) {
124-
throw new Error('Cannot update a Piece Instance from the current Part Instance')
125-
}
126122
return this.partAndPieceInstanceService.updatePieceInstance(pieceInstanceId, piece)
127123
}
128124

packages/job-worker/src/blueprints/context/services/PartAndPieceInstanceActionService.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,10 @@ export class PartAndPieceInstanceActionService {
318318

319319
const { pieceInstance } = foundPieceInstance
320320

321-
if (pieceInstance.pieceInstance.infinite?.fromPreviousPart) {
321+
if (
322+
pieceInstance.pieceInstance.infinite?.fromPreviousPart &&
323+
pieceInstance.pieceInstance.partInstanceId === this._playoutModel.playlist.nextPartInfo?.partInstanceId
324+
) {
322325
throw new Error('Cannot update an infinite piece that is continued from a previous part')
323326
}
324327

packages/job-worker/src/blueprints/context/services/__tests__/PartAndPieceInstanceActionService.test.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1499,6 +1499,81 @@ describe('Test blueprint api context', () => {
14991499
expect(service.currentPartState).toEqual(ActionPartChange.SAFE_CHANGE)
15001500
})
15011501
})
1502+
1503+
test('can update infinite piece from previous part if in current part instance', async () => {
1504+
const { jobContext, playlistId, allPartInstances } = await setupMyDefaultRundown()
1505+
1506+
const currentPartInstance = allPartInstances[1]
1507+
1508+
// Create an infinite piece instance continued from previous part
1509+
const pieceInstance: PieceInstance = {
1510+
_id: protectString('piece_infinite'),
1511+
rundownId: currentPartInstance.partInstance.rundownId,
1512+
partInstanceId: currentPartInstance.partInstance._id,
1513+
playlistActivationId: currentPartInstance.partInstance.playlistActivationId,
1514+
piece: {
1515+
_id: protectString('piece_infinite_p'),
1516+
externalId: '-',
1517+
enable: { start: 0 },
1518+
name: 'infinite',
1519+
sourceLayerId: '',
1520+
outputLayerId: '',
1521+
startPartId: allPartInstances[0].partInstance.part._id,
1522+
content: {},
1523+
timelineObjectsString: EmptyPieceTimelineObjectsBlob,
1524+
lifespan: PieceLifespan.OutOnRundownEnd,
1525+
pieceType: IBlueprintPieceType.Normal,
1526+
invalid: false,
1527+
},
1528+
infinite: {
1529+
infiniteInstanceId: getRandomId(),
1530+
infiniteInstanceIndex: 1,
1531+
infinitePieceId: protectString('piece_infinite_p'),
1532+
fromPreviousPart: true,
1533+
},
1534+
}
1535+
1536+
await jobContext.mockCollections.PieceInstances.insertOne(pieceInstance)
1537+
1538+
await setPartInstances(jobContext, playlistId, currentPartInstance, undefined)
1539+
1540+
await wrapWithPlayoutModel(jobContext, playlistId, async (playoutModel) => {
1541+
const { service } = await getTestee(jobContext, playoutModel)
1542+
1543+
// Updating it in CURRENT part should succeed
1544+
await expect(
1545+
service.updatePieceInstance(unprotectString(pieceInstance._id), { name: 'updated' })
1546+
).resolves.toBeTruthy()
1547+
})
1548+
})
1549+
1550+
test('updating lifespan to infinite sets dynamicallyConvertedToInfinite', async () => {
1551+
const { jobContext, playlistId, allPartInstances } = await setupMyDefaultRundown()
1552+
1553+
const currentPartInstance = allPartInstances[0]
1554+
const pieceInstance = (await jobContext.mockCollections.PieceInstances.findOne({
1555+
partInstanceId: currentPartInstance.partInstance._id,
1556+
})) as PieceInstance
1557+
expect(pieceInstance).toBeTruthy()
1558+
expect(pieceInstance.piece.lifespan).toEqual(PieceLifespan.WithinPart)
1559+
expect(pieceInstance.infinite).toBeUndefined()
1560+
1561+
await setPartInstances(jobContext, playlistId, currentPartInstance, undefined)
1562+
1563+
await wrapWithPlayoutModel(jobContext, playlistId, async (playoutModel) => {
1564+
const { service } = await getTestee(jobContext, playoutModel)
1565+
1566+
await service.updatePieceInstance(unprotectString(pieceInstance._id), {
1567+
lifespan: PieceLifespan.OutOnRundownEnd,
1568+
})
1569+
1570+
const updatedPieceInstance = playoutModel.findPieceInstance(pieceInstance._id)?.pieceInstance
1571+
expect(updatedPieceInstance).toBeTruthy()
1572+
expect(updatedPieceInstance?.pieceInstance.piece.lifespan).toEqual(PieceLifespan.OutOnRundownEnd)
1573+
expect(updatedPieceInstance?.pieceInstance.infinite).toBeTruthy()
1574+
expect(updatedPieceInstance?.pieceInstance.dynamicallyConvertedToInfinite).toBeTruthy()
1575+
})
1576+
})
15021577
})
15031578

15041579
describe('stopPiecesOnLayers', () => {

packages/job-worker/src/playout/model/implementation/PlayoutPieceInstanceModelImpl.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ import { ExpectedPackageId, PieceInstanceInfiniteId, RundownId } from '@sofie-au
22
import { ReadonlyDeep } from 'type-fest'
33
import { PieceInstance, PieceInstancePiece } from '@sofie-automation/corelib/dist/dataModel/PieceInstance'
44
import { clone, getRandomId } from '@sofie-automation/corelib/dist/lib'
5-
import { ExpectedPackage, Time } from '@sofie-automation/blueprints-integration'
5+
import { ExpectedPackage, Time, PieceLifespan } from '@sofie-automation/blueprints-integration'
66
import { PlayoutPieceInstanceModel } from '../PlayoutPieceInstanceModel.js'
77
import _ from 'underscore'
88
import { getExpectedPackageId } from '@sofie-automation/corelib/dist/dataModel/ExpectedPackages'
9+
import { setupPieceInstanceInfiniteProperties } from '../../pieces.js'
10+
import { getCurrentTime } from '../../../lib/time.js'
911

1012
export class PlayoutPieceInstanceModelImpl implements PlayoutPieceInstanceModel {
1113
/**
@@ -158,6 +160,14 @@ export class PlayoutPieceInstanceModelImpl implements PlayoutPieceInstanceModel
158160
},
159161
true
160162
)
163+
if (
164+
props.lifespan !== undefined &&
165+
props.lifespan !== PieceLifespan.WithinPart &&
166+
!this.PieceInstanceImpl.infinite
167+
) {
168+
setupPieceInstanceInfiniteProperties(this.PieceInstanceImpl)
169+
this.PieceInstanceImpl.dynamicallyConvertedToInfinite = getCurrentTime()
170+
}
161171
}
162172
}
163173

0 commit comments

Comments
 (0)