Skip to content

Commit 4a570ff

Browse files
committed
wip: theoretical ingest flow
1 parent 8cb3b9d commit 4a570ff

File tree

10 files changed

+198
-18
lines changed

10 files changed

+198
-18
lines changed

packages/corelib/src/dataModel/ExpectedPackages.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export type ExpectedPackageFromRundownBaseline =
3232
| ExpectedPackageDBFromBaselineAdLibAction
3333
| ExpectedPackageDBFromBaselineAdLibPiece
3434
| ExpectedPackageDBFromRundownBaselineObjects
35+
| ExpectedPackageDBFromBaselinePiece
3536

3637
export type ExpectedPackageDBFromBucket = ExpectedPackageDBFromBucketAdLib | ExpectedPackageDBFromBucketAdLibAction
3738

@@ -47,6 +48,7 @@ export enum ExpectedPackageDBType {
4748
ADLIB_ACTION = 'adlib_action',
4849
BASELINE_ADLIB_PIECE = 'baseline_adlib_piece',
4950
BASELINE_ADLIB_ACTION = 'baseline_adlib_action',
51+
BASELINE_PIECE = 'baseline_piece',
5052
BUCKET_ADLIB = 'bucket_adlib',
5153
BUCKET_ADLIB_ACTION = 'bucket_adlib_action',
5254
RUNDOWN_BASELINE_OBJECTS = 'rundown_baseline_objects',
@@ -79,6 +81,13 @@ export interface ExpectedPackageDBFromPiece extends ExpectedPackageDBBase {
7981
/** The rundown of the Piece this package belongs to */
8082
rundownId: RundownId
8183
}
84+
export interface ExpectedPackageDBFromBaselinePiece extends ExpectedPackageDBBase {
85+
fromPieceType: ExpectedPackageDBType.BASELINE_PIECE
86+
/** The Piece this package belongs to */
87+
pieceId: PieceId
88+
/** The rundown of the Piece this package belongs to */
89+
rundownId: RundownId
90+
}
8291

8392
export interface ExpectedPackageDBFromBaselineAdLibPiece extends ExpectedPackageDBBase {
8493
fromPieceType: ExpectedPackageDBType.BASELINE_ADLIB_PIECE

packages/corelib/src/dataModel/Piece.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,15 @@ export interface PieceGeneric extends Omit<IBlueprintPieceGeneric, 'content'> {
5353
export interface Piece
5454
extends PieceGeneric,
5555
Omit<IBlueprintPieceDB, '_id' | 'content' | 'userEditOperations' | 'userEditProperties'> {
56+
/** Timeline enabler. When the piece should be active on the timeline. */
57+
enable: {
58+
start: number | 'now' // TODO - now will be removed from this eventually, but as it is not an acceptable value 99% of the time, that is not really breaking
59+
duration?: number
60+
61+
// Pieces owned by the Rundown should always be absolute
62+
isAbsolute?: boolean
63+
}
64+
5665
/**
5766
* This is the id of the rundown this piece starts playing in.
5867
* Currently this is the only rundown the piece could be playing in

packages/job-worker/src/blueprints/postProcess.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
PieceLifespan,
1414
IBlueprintPieceType,
1515
ITranslatableMessage,
16+
IBlueprintRundownPiece,
1617
} from '@sofie-automation/blueprints-integration'
1718
import {
1819
AdLibActionId,
@@ -357,6 +358,85 @@ export function postProcessAdLibActions(
357358
})
358359
}
359360

361+
/**
362+
* Process and validate some IBlueprintRundownPiece into Piece
363+
* @param context Context from the job queue
364+
* @param pieces IBlueprintPiece to process
365+
* @param blueprintId Id of the Blueprint the Pieces are from
366+
* @param rundownId Id of the Rundown the Pieces belong to
367+
* @param setInvalid If true all Pieces will be marked as `invalid`, this should be set to match the owning Part
368+
*/
369+
export function postProcessGlobalPieces(
370+
context: JobContext,
371+
pieces: Array<IBlueprintRundownPiece>,
372+
blueprintId: BlueprintId,
373+
rundownId: RundownId,
374+
setInvalid?: boolean
375+
): Piece[] {
376+
const span = context.startSpan('blueprints.postProcess.postProcessPieces')
377+
378+
const uniqueIds = new Map<string, number>()
379+
const timelineUniqueIds = new Set<string>()
380+
381+
const processedPieces = pieces.map((orgPiece: IBlueprintRundownPiece) => {
382+
if (!orgPiece.externalId)
383+
throw new Error(
384+
`Error in blueprint "${blueprintId}" externalId not set for rundown piece ("${orgPiece.name}")`
385+
)
386+
387+
const docId = getIdHash(
388+
'Piece',
389+
uniqueIds,
390+
`${rundownId}_${blueprintId}_rundown_piece_${orgPiece.sourceLayerId}_${orgPiece.externalId}`
391+
)
392+
393+
const piece: Piece = {
394+
...orgPiece,
395+
content: omit(orgPiece.content, 'timelineObjects'),
396+
397+
pieceType: IBlueprintPieceType.Normal,
398+
lifespan: PieceLifespan.OutOnRundownChange,
399+
400+
_id: protectString(docId),
401+
startRundownId: rundownId,
402+
startSegmentId: null,
403+
startPartId: null,
404+
invalid: setInvalid ?? false,
405+
timelineObjectsString: EmptyPieceTimelineObjectsBlob,
406+
}
407+
408+
if (piece.pieceType !== IBlueprintPieceType.Normal) {
409+
// transition pieces must not be infinite, lets enforce that
410+
piece.lifespan = PieceLifespan.WithinPart
411+
}
412+
if (piece.extendOnHold) {
413+
// HOLD pieces must not be infinite, as they become that when being held
414+
piece.lifespan = PieceLifespan.WithinPart
415+
}
416+
417+
if (piece.enable.start === 'now')
418+
throw new Error(
419+
`Error in blueprint "${blueprintId}" rundown piece cannot have a start of 'now'! ("${piece.name}")`
420+
)
421+
422+
const timelineObjects = postProcessTimelineObjects(
423+
piece._id,
424+
blueprintId,
425+
orgPiece.content.timelineObjects,
426+
timelineUniqueIds
427+
)
428+
piece.timelineObjectsString = serializePieceTimelineObjectsBlob(timelineObjects)
429+
430+
// Fill in ids of unnamed expectedPackages
431+
setDefaultIdOnExpectedPackages(piece.expectedPackages)
432+
433+
return piece
434+
})
435+
436+
span?.end()
437+
return processedPieces
438+
}
439+
360440
/**
361441
* Process and validate TSRTimelineObj for the StudioBaseline into TimelineObjRundown
362442
* @param blueprintId Id of the Blueprint the TSRTimelineObj are from

packages/job-worker/src/ingest/expectedMediaItems.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ export async function updateExpectedMediaItemsForRundownBaseline(
254254
const expectedMediaItems = generateExpectedMediaItemsFull(
255255
context.studio._id,
256256
ingestModel.rundownId,
257-
[],
257+
ingestModel.getGlobalPieces(),
258258
baselineAdlibPieces,
259259
baselineAdlibActions
260260
)

packages/job-worker/src/ingest/expectedPackages.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,21 @@ export async function updateExpectedPackagesForRundownBaseline(
148148
preserveTypesDuringSave.add(ExpectedPackageDBType.RUNDOWN_BASELINE_OBJECTS)
149149
}
150150

151+
// Add expected packages for global pieces
152+
for (const piece of ingestModel.getGlobalPieces()) {
153+
if (piece.expectedPackages) {
154+
const bases = generateExpectedPackageBases(context.studio, piece._id, piece.expectedPackages)
155+
for (const base of bases) {
156+
expectedPackages.push({
157+
...base,
158+
rundownId: ingestModel.rundownId,
159+
pieceId: piece._id,
160+
fromPieceType: ExpectedPackageDBType.BASELINE_PIECE,
161+
})
162+
}
163+
}
164+
}
165+
151166
// Preserve anything existing
152167
for (const expectedPackage of ingestModel.expectedPackagesForRundownBaseline) {
153168
if (preserveTypesDuringSave.has(expectedPackage.fromPieceType)) {

packages/job-worker/src/ingest/expectedPlayoutItems.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ export async function updateExpectedPlayoutItemsForRundownBaseline(
6262
for (const action of baselineAdlibActions) {
6363
baselineExpectedPlayoutItems.push(...extractExpectedPlayoutItems(studioId, rundownId, undefined, action))
6464
}
65+
for (const piece of ingestModel.getGlobalPieces()) {
66+
baselineExpectedPlayoutItems.push(...extractExpectedPlayoutItems(studioId, rundownId, undefined, piece))
67+
}
6568

6669
if (baseline) {
6770
for (const item of baseline.expectedPlayoutItems ?? []) {

packages/job-worker/src/ingest/generationRundown.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { WatchedPackagesHelper } from '../blueprints/context/watchedPackages'
1111
import {
1212
postProcessAdLibPieces,
1313
postProcessGlobalAdLibActions,
14+
postProcessGlobalPieces,
1415
postProcessRundownBaselineItems,
1516
} from '../blueprints/postProcess'
1617
import { logger } from '../logging'
@@ -313,10 +314,9 @@ export async function regenerateRundownAndBaselineFromIngestData(
313314
)
314315
const globalPieces = postProcessGlobalPieces(
315316
context,
317+
rundownRes.globalPieces || [],
316318
showStyle.base.blueprintId,
317-
dbRundown._id,
318-
undefined,
319-
rundownRes.globalPieces || []
319+
dbRundown._id
320320
)
321321

322322
await ingestModel.setRundownBaseline(timelineObjectsBlob, adlibPieces, adlibActions, globalPieces)

packages/job-worker/src/ingest/model/IngestModel.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ExpectedMediaItemRundown } from '@sofie-automation/corelib/dist/dataMod
22
import {
33
ExpectedPackageDBFromBaselineAdLibAction,
44
ExpectedPackageDBFromBaselineAdLibPiece,
5+
ExpectedPackageDBFromBaselinePiece,
56
ExpectedPackageDBFromRundownBaselineObjects,
67
ExpectedPackageFromRundown,
78
} from '@sofie-automation/corelib/dist/dataModel/ExpectedPackages'
@@ -37,6 +38,7 @@ export type ExpectedPackageForIngestModelBaseline =
3738
| ExpectedPackageDBFromBaselineAdLibAction
3839
| ExpectedPackageDBFromBaselineAdLibPiece
3940
| ExpectedPackageDBFromRundownBaselineObjects
41+
| ExpectedPackageDBFromBaselinePiece
4042
export type ExpectedPackageForIngestModel = ExpectedPackageFromRundown | ExpectedPackageForIngestModelBaseline
4143

4244
export interface IngestModelReadonly {
@@ -130,6 +132,11 @@ export interface IngestModelReadonly {
130132
*/
131133
getAllPieces(): ReadonlyDeep<Piece>[]
132134

135+
/**
136+
* Get the Pieces which belong to the Rundown, not a Part
137+
*/
138+
getGlobalPieces(): ReadonlyDeep<Piece>[]
139+
133140
/**
134141
* Search for a Part through the whole Rundown
135142
* @param id Id of the Part
@@ -251,7 +258,7 @@ export interface IngestModel extends IngestModelReadonly, BaseModel, INotificati
251258
timelineObjectsBlob: PieceTimelineObjectsBlob,
252259
adlibPieces: RundownBaselineAdLibItem[],
253260
adlibActions: RundownBaselineAdLibAction[],
254-
pieces: never[]
261+
pieces: Piece[]
255262
): Promise<void>
256263

257264
/**

packages/job-worker/src/ingest/model/implementation/IngestModelImpl.ts

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ export class IngestModelImpl implements IngestModel, DatabasePersistedModel {
116116
}
117117

118118
protected readonly segmentsImpl: Map<SegmentId, SegmentWrapper>
119+
readonly #piecesWithChanges = new Set<PieceId>()
120+
#piecesImpl: ReadonlyArray<Piece>
119121

120122
readonly #rundownBaselineExpectedPackagesStore: ExpectedPackagesStore<ExpectedPackageForIngestModelBaseline>
121123

@@ -224,6 +226,8 @@ export class IngestModelImpl implements IngestModel, DatabasePersistedModel {
224226
})
225227
}
226228

229+
this.#piecesImpl = groupedPieces.get(null) ?? []
230+
227231
this.#rundownBaselineObjs = new LazyInitialise(async () =>
228232
context.directCollections.RundownBaselineObjects.findFetch({
229233
rundownId: this.rundownId,
@@ -253,6 +257,7 @@ export class IngestModelImpl implements IngestModel, DatabasePersistedModel {
253257
)
254258

255259
this.segmentsImpl = new Map()
260+
this.#piecesImpl = []
256261

257262
this.#rundownBaselineObjs = new LazyInitialise(async () => [])
258263
this.#rundownBaselineAdLibPieces = new LazyInitialise(async () => [])
@@ -334,6 +339,10 @@ export class IngestModelImpl implements IngestModel, DatabasePersistedModel {
334339
return this.getAllOrderedParts().flatMap((part) => part.pieces)
335340
}
336341

342+
getGlobalPieces(): ReadonlyDeep<Piece>[] {
343+
return [...this.#piecesImpl]
344+
}
345+
337346
findPart(partId: PartId): IngestPartModel | undefined {
338347
for (const segment of this.segmentsImpl.values()) {
339348
if (!segment || segment.deleted) continue
@@ -477,7 +486,8 @@ export class IngestModelImpl implements IngestModel, DatabasePersistedModel {
477486
async setRundownBaseline(
478487
timelineObjectsBlob: PieceTimelineObjectsBlob,
479488
adlibPieces: RundownBaselineAdLibItem[],
480-
adlibActions: RundownBaselineAdLibAction[]
489+
adlibActions: RundownBaselineAdLibAction[],
490+
pieces: Piece[]
481491
): Promise<void> {
482492
const [loadedRundownBaselineObjs, loadedRundownBaselineAdLibPieces, loadedRundownBaselineAdLibActions] =
483493
await Promise.all([
@@ -499,11 +509,13 @@ export class IngestModelImpl implements IngestModel, DatabasePersistedModel {
499509
)
500510

501511
// Compare and update the adlibPieces
502-
const newAdlibPieces = adlibPieces.map((piece) => ({
503-
...clone(piece),
504-
partId: undefined,
505-
rundownId: this.rundownId,
506-
}))
512+
const newAdlibPieces = adlibPieces.map(
513+
(piece): AdLibPiece => ({
514+
...clone(piece),
515+
partId: undefined,
516+
rundownId: this.rundownId,
517+
})
518+
)
507519
this.#rundownBaselineAdLibPieces.setValue(
508520
diffAndReturnLatestObjects(
509521
this.#rundownBaselineAdLibPiecesWithChanges,
@@ -513,18 +525,31 @@ export class IngestModelImpl implements IngestModel, DatabasePersistedModel {
513525
)
514526

515527
// Compare and update the adlibActions
516-
const newAdlibActions = adlibActions.map((action) => ({
517-
...clone(action),
518-
partId: undefined,
519-
rundownId: this.rundownId,
520-
}))
528+
const newAdlibActions = adlibActions.map(
529+
(action): RundownBaselineAdLibAction => ({
530+
...clone(action),
531+
partId: undefined,
532+
rundownId: this.rundownId,
533+
})
534+
)
521535
this.#rundownBaselineAdLibActions.setValue(
522536
diffAndReturnLatestObjects(
523537
this.#rundownBaselineAdLibActionsWithChanges,
524538
loadedRundownBaselineAdLibActions,
525539
newAdlibActions
526540
)
527541
)
542+
543+
// Compare and update the rundown pieces
544+
const newPieces = pieces.map(
545+
(piece): Piece => ({
546+
...clone(piece),
547+
startRundownId: this.rundownId,
548+
startPartId: null,
549+
startSegmentId: null,
550+
})
551+
)
552+
this.#piecesImpl = diffAndReturnLatestObjects(this.#piecesWithChanges, this.#piecesImpl, newPieces)
528553
}
529554

530555
setRundownOrphaned(orphaned: RundownOrphanedReason | undefined): void {
@@ -628,9 +653,26 @@ export class IngestModelImpl implements IngestModel, DatabasePersistedModel {
628653
for (const segment of this.segmentsImpl.values()) {
629654
if (segment.deleted) {
630655
logOrThrowError(new Error(`Failed no changes in model assertion, Segment has been changed`))
656+
break
631657
} else {
632658
const err = segment.segmentModel.checkNoChanges()
633-
if (err) logOrThrowError(err)
659+
if (err) {
660+
logOrThrowError(err)
661+
break
662+
}
663+
}
664+
}
665+
666+
if (this.#piecesWithChanges.size) {
667+
logOrThrowError(new Error(`Failed no changes in model assertion, Rundown Pieces have been changed`))
668+
} else {
669+
for (const piece of this.#piecesImpl.values()) {
670+
if (!piece) {
671+
logOrThrowError(
672+
new Error(`Failed no changes in model assertion, Rundown Pieces have been changed`)
673+
)
674+
break
675+
}
634676
}
635677
}
636678
} finally {
@@ -688,6 +730,8 @@ export class IngestModelImpl implements IngestModel, DatabasePersistedModel {
688730
saveHelper.addExpectedPackagesStore(this.#rundownBaselineExpectedPackagesStore)
689731
this.#rundownBaselineExpectedPackagesStore.clearChangedFlags()
690732

733+
saveHelper.addChangedPieces(this.#piecesImpl, this.#piecesWithChanges)
734+
691735
await Promise.all([
692736
this.#rundownHasChanged && this.#rundownImpl
693737
? this.context.directCollections.Rundowns.replace(this.#rundownImpl)

0 commit comments

Comments
 (0)