Skip to content

Commit dac27bb

Browse files
authored
Merge pull request Sofie-Automation#1421 from bbc/upstream/processIngestData-docs
chore: add documentation page for `processIngestData`
2 parents df7ba42 + 28934d9 commit dac27bb

File tree

1 file changed

+139
-0
lines changed

1 file changed

+139
-0
lines changed
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# Manipulating Ingest Data
2+
3+
In Sofie we receive the rundown from an NRCS in the form of the `IngestRundown`, `IngestSegment` and `IngestPart` types. ([Source Code](https://github.com/nrkno/sofie-core/blob/master/packages/shared-lib/src/peripheralDevice/ingest.ts))
4+
These are passed into the `getRundown` or `getSegment` blueprints methods to transform them into a Rundown that Sofie can display and play.
5+
6+
At times it can be useful to manipulate this data before it gets passed into these methods. This wants to be done before `getSegment` in order to limit the scope of the re-generation needed. We could have made it so that `getSegment` is able to view the whole `IngestRundown`, but that would mean that any change to the `IngestRundown` would require re-generating every segment. This would be costly and could have side effects.
7+
8+
A new method `processIngestData` was added to transform the `NRCSIngestRundown` into a `SofieIngestRundown`. The types of the two are the same, so implementing the `processIngestData` method is optional, with the default being to pass through the NRCS rundown unchanged. (There is an exception here for MOS, which is explained below).
9+
10+
The basic implementation of this method which simply propogates nrcs changes is:
11+
12+
```ts
13+
function processIngestData(
14+
context: IProcessIngestDataContext,
15+
mutableIngestRundown: MutableIngestRundown<any, any, any>,
16+
nrcsIngestRundown: IngestRundown,
17+
previousNrcsIngestRundown: IngestRundown | undefined,
18+
changes: NrcsIngestChangeDetails | UserOperationChange
19+
) {
20+
if (changes.source === 'ingest') {
21+
blueprintContext.defaultApplyIngestChanges(mutableIngestRundown, nrcsIngestRundown, changes)
22+
}
23+
}
24+
```
25+
26+
In this method, the key part is the `mutableIngestRundown` which is the `IngestRundown` that will get used for `getRundown` and `getSegment` later. It is a class with various mutator methods which allows Sofie to cheaply check what has changed and know what needs to be regenerated. (We did consider performing deep diffs, but were concerned about the cost of diffing these very large rundown objects).
27+
This object internally contains an `IngestRundown`.
28+
29+
The `nrcsIngestRundown` parameter is the full `IngestRundown` as seen by the NRCS. The `previousNrcsIngestRundown` parameter is the `nrcsIngestRundown` from the previous call. This is to allow you to perform any comparisons between the data that may be useful.
30+
31+
The `changes` object is a structure that defines what the NRCS provided changes for. The changes have already been applied onto the `nrcsIngestRundown`, this provides a description of what/where the changes were applied to.
32+
33+
Finally, the `blueprintContext.defaultApplyIngestChanges` call is what performs the 'magic'. Inside of this it is interpreting the `changes` object, and calling the appropriate methods on `mutableIngestRundown`. It is expected that this logic should be able to handle most use cases, but there may be some where they need something custom, so it is completely possible to reimplement inside blueprints.
34+
35+
So far this has ignored that the `changes` object can be of type `UserOperationChange`; this is explained below.
36+
37+
## Modifying NRCS Ingest Data
38+
39+
MOS does not have Segments, to handle this Sofie creates a Segment and Part for each MOS Story, expecting them to be grouped later if needed.
40+
41+
In the past Sofie has had a hardcoded grouping logic, based on how NRK define this as a prefix in the Part names. Obviously this doesn't work for everyone, so this needed to be made more customisable. (This is still the default behaviour when `processIngestData` is not implemented)
42+
43+
To perform the NRK grouping behaviour the following implementation can be used:
44+
45+
```ts
46+
function processIngestData(
47+
context: IProcessIngestDataContext,
48+
mutableIngestRundown: MutableIngestRundown<any, any, any>,
49+
nrcsIngestRundown: IngestRundown,
50+
previousNrcsIngestRundown: IngestRundown | undefined,
51+
changes: NrcsIngestChangeDetails | UserOperationChange
52+
) {
53+
if (changes.source === 'ingest') {
54+
// Group parts by interpreting the slug to be in the form `SEGMENTNAME;PARTNAME`
55+
const groupedResult = context.groupMosPartsInRundownAndChangesWithSeparator(
56+
nrcsIngestRundown,
57+
previousNrcsIngestRundown,
58+
ingestRundownChanges.changes,
59+
';' // Backwards compatibility
60+
)
61+
62+
context.defaultApplyIngestChanges(
63+
mutableIngestRundown,
64+
groupedResult.nrcsIngestRundown,
65+
groupedResult.ingestChanges
66+
)
67+
}
68+
}
69+
```
70+
71+
There is also a helper method for doing your own logic:
72+
73+
```ts
74+
function processIngestData(
75+
context: IProcessIngestDataContext,
76+
mutableIngestRundown: MutableIngestRundown<any, any, any>,
77+
nrcsIngestRundown: IngestRundown,
78+
previousNrcsIngestRundown: IngestRundown | undefined,
79+
changes: NrcsIngestChangeDetails | UserOperationChange
80+
) {
81+
if (changes.source === 'ingest') {
82+
// Group parts by some custom logic
83+
const groupedResult = context.groupPartsInRundownAndChanges(
84+
nrcsIngestRundown,
85+
previousNrcsIngestRundown,
86+
ingestRundownChanges.changes,
87+
(segments) => {
88+
// TODO - perform the grouping here
89+
return segmentsAfterMyChanges
90+
}
91+
)
92+
93+
context.defaultApplyIngestChanges(
94+
mutableIngestRundown,
95+
groupedResult.nrcsIngestRundown,
96+
groupedResult.ingestChanges
97+
)
98+
}
99+
}
100+
```
101+
102+
Both of these return a modified `nrcsIngestRundown` with the changes applied, and a new `changes` object which is similarly updated to match the new layout.
103+
104+
You can of course do any portions of this yourself if you desire.
105+
106+
## User Edits
107+
108+
In some cases, it can be beneficial to allow the user to perform some editing of the Rundown from within the Sofie UI. AdLibs and AdLib Actions can allow for some of this to be done in the current and next Part, but this is limited and doesn't persist when re-running the Part.
109+
110+
The idea here is that the UI will be given some descriptors on operations it can perform, which will then make calls to `processIngestData` so that they can be applied to the IngestRundown. Doing it at this level allows things to persist and for decisions to be made by blueprints over how to merge the changes when an update for a Part is received from the NRCS.
111+
112+
This page doesn't go into how to define the editor for the UI, just how to handle the operations.
113+
114+
There are a few Sofie defined definitions of operations, but it is also expected that custom operations will be defined. You can check the Typescript types for the builtin operations that you might want to handle.
115+
116+
For example, it could be possible for Segments to be locked, so that any NRCS changes for them are ignored.
117+
118+
```ts
119+
function processIngestData(
120+
context: IProcessIngestDataContext,
121+
mutableIngestRundown: MutableIngestRundown<any, any, any>,
122+
nrcsIngestRundown: IngestRundown,
123+
previousNrcsIngestRundown: IngestRundown | undefined,
124+
changes: NrcsIngestChangeDetails | UserOperationChange
125+
) {
126+
if (changes.source === 'ingest') {
127+
for (const segment of mutableIngestRundown.segments) {
128+
delete ingestRundownChanges.changes.segmentChanges[segment.externalId]
129+
// TODO - does this need to revert nrcsIngestRundown too?
130+
}
131+
132+
blueprintContext.defaultApplyIngestChanges(mutableIngestRundown, nrcsIngestRundown, changes)
133+
} else if (changes.source === 'user') {
134+
if (changes.operation.id === 'lock-segment') {
135+
mutableIngestRundown.getSegment(changes.operationTarget.segmentExternalId)?.setUserEditState('locked', true)
136+
}
137+
}
138+
}
139+
```

0 commit comments

Comments
 (0)