Skip to content

Commit f7bb557

Browse files
committed
feat: allow translating part externalId for mos statuses
1 parent c81244d commit f7bb557

File tree

4 files changed

+100
-34
lines changed

4 files changed

+100
-34
lines changed

meteor/server/publications/ingestStatus/createIngestRundownStatus.ts

+93-34
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import type { RundownId, PartId } from '@sofie-automation/corelib/dist/dataModel/Ids'
1+
import type { RundownId } from '@sofie-automation/corelib/dist/dataModel/Ids'
22
import { NrcsIngestCacheType } from '@sofie-automation/corelib/dist/dataModel/NrcsIngestDataCache'
3-
import type { DBPartInstance } from '@sofie-automation/corelib/dist/dataModel/PartInstance'
43
import {
54
IngestRundownStatus,
65
IngestPartPlaybackStatus,
76
IngestRundownActiveStatus,
87
IngestPartStatus,
8+
IngestPartNotifyItemReady,
99
} from '@sofie-automation/shared-lib/dist/ingest/rundownStatus'
1010
import type { ReadonlyDeep } from 'type-fest'
1111
import _ from 'underscore'
@@ -15,6 +15,7 @@ import { ReactiveCacheCollection } from '../lib/ReactiveCacheCollection'
1515
import { PartInstance } from '@sofie-automation/meteor-lib/dist/collections/PartInstances'
1616
import { IngestPart } from '@sofie-automation/blueprints-integration'
1717
import { DBPart } from '@sofie-automation/corelib/dist/dataModel/Part'
18+
import { unprotectString } from '@sofie-automation/corelib/dist/protectedString'
1819

1920
export function createIngestRundownStatus(
2021
cache: ReadonlyDeep<ContentCache>,
@@ -41,9 +42,6 @@ export function createIngestRundownStatus(
4142
newDoc.active = playlist.rehearsal ? IngestRundownActiveStatus.REHEARSAL : IngestRundownActiveStatus.ACTIVE
4243
}
4344

44-
// Find the most important part instance for each part
45-
const partInstanceMap = findPartInstanceForEachPart(playlist, rundownId, cache.PartInstances)
46-
4745
const nrcsSegments = cache.NrcsIngestData.find({ rundownId, type: NrcsIngestCacheType.SEGMENT }).fetch()
4846
for (const nrcsSegment of nrcsSegments) {
4947
const nrcsParts = cache.NrcsIngestData.find({
@@ -58,10 +56,25 @@ export function createIngestRundownStatus(
5856
nrcsParts.map((nrcsPart) => {
5957
if (!nrcsPart.partId || !nrcsPart.segmentId) return null
6058

61-
const part = cache.Parts.findOne({ _id: nrcsPart.partId, rundownId })
62-
const partInstance = partInstanceMap.get(nrcsPart.partId)
63-
64-
return createIngestPartStatus(playlist, partInstance, part, nrcsPart.data as IngestPart)
59+
const parts = cache.Parts.find({
60+
rundownId: rundownId,
61+
$or: [
62+
{
63+
externalId: nrcsPart.data.externalId,
64+
},
65+
{
66+
ingestNotifyPartExternalId: nrcsPart.data.externalId,
67+
},
68+
],
69+
}).fetch()
70+
const partInstances = findPartInstancesForIngestPart(
71+
playlist,
72+
rundownId,
73+
cache.PartInstances,
74+
nrcsPart.data.externalId
75+
)
76+
77+
return createIngestPartStatus(playlist, partInstances, parts, nrcsPart.data as IngestPart)
6578
})
6679
),
6780
})
@@ -70,64 +83,110 @@ export function createIngestRundownStatus(
7083
return newDoc
7184
}
7285

73-
function findPartInstanceForEachPart(
86+
function findPartInstancesForIngestPart(
7487
playlist: Pick<DBRundownPlaylist, PlaylistFields> | undefined,
7588
rundownId: RundownId,
76-
partInstancesCache: ReadonlyDeep<ReactiveCacheCollection<Pick<PartInstance, PartInstanceFields>>>
89+
partInstancesCache: ReadonlyDeep<ReactiveCacheCollection<Pick<PartInstance, PartInstanceFields>>>,
90+
partExternalId: string
7791
) {
78-
const partInstanceMap = new Map<PartId, Pick<DBPartInstance, PartInstanceFields>>()
79-
if (!playlist) return partInstanceMap
92+
const result: Record<string, Pick<PartInstance, PartInstanceFields>> = {}
93+
if (!playlist) return result
94+
95+
const candidatePartInstances = partInstancesCache
96+
.find({
97+
rundownId: rundownId,
98+
$or: [
99+
{
100+
'part.externalId': partExternalId,
101+
},
102+
{
103+
'part.ingestNotifyPartExternalId': partExternalId,
104+
},
105+
],
106+
})
107+
.fetch()
80108

81-
for (const partInstance of partInstancesCache.find({}).fetch()) {
109+
for (const partInstance of candidatePartInstances) {
82110
if (partInstance.rundownId !== rundownId) continue
83111
// Ignore the next partinstance
84112
if (partInstance._id === playlist.nextPartInfo?.partInstanceId) continue
85113

114+
const partId = unprotectString(partInstance.part._id)
115+
86116
// The current part instance is the most important
87117
if (partInstance._id === playlist.currentPartInfo?.partInstanceId) {
88-
partInstanceMap.set(partInstance.part._id, partInstance)
118+
result[partId] = partInstance
89119
continue
90120
}
91121

92122
// Take the part with the highest takeCount
93-
const existingEntry = partInstanceMap.get(partInstance.part._id)
123+
const existingEntry = result[partId]
94124
if (!existingEntry || existingEntry.takeCount < partInstance.takeCount) {
95-
partInstanceMap.set(partInstance.part._id, partInstance)
125+
result[partId] = partInstance
96126
}
97127
}
98128

99-
return partInstanceMap
129+
return result
100130
}
101131

102132
function createIngestPartStatus(
103133
playlist: Pick<DBRundownPlaylist, PlaylistFields> | undefined,
104-
partInstance: Pick<PartInstance, PartInstanceFields> | undefined,
105-
part: Pick<DBPart, PartFields> | undefined,
134+
partInstances: Record<string, Pick<PartInstance, PartInstanceFields>>,
135+
parts: Pick<DBPart, PartFields>[],
106136
ingestPart: IngestPart
107137
): IngestPartStatus {
108138
// Determine the playback status from the PartInstance
109139
let playbackStatus = IngestPartPlaybackStatus.UNKNOWN
110-
if (playlist && partInstance && partInstance.part.shouldNotifyCurrentPlayingPart) {
111-
const isCurrentPartInstance = playlist.currentPartInfo?.partInstanceId === partInstance._id
112-
113-
if (isCurrentPartInstance) {
114-
// If the current, it is playing
115-
playbackStatus = IngestPartPlaybackStatus.PLAY
116-
} else {
117-
// If not the current, but has been played, it is stopped
118-
playbackStatus = IngestPartPlaybackStatus.STOP
140+
141+
let isReady: boolean | null = null // Start off as null, the first value will make this true or false
142+
143+
const itemsReady: IngestPartNotifyItemReady[] = []
144+
145+
const updateStatusWithPart = (part: Pick<DBPart, PartFields>) => {
146+
// If the part affects the ready status, update it
147+
if (typeof part.ingestNotifyPartReady === 'boolean') {
148+
isReady = (isReady ?? true) && part.ingestNotifyPartReady
149+
}
150+
151+
// Include the items
152+
if (part.ingestNotifyItemsReady) {
153+
itemsReady.push(...part.ingestNotifyItemsReady)
119154
}
120155
}
121156

122-
// Determine the ready status from the PartInstance or Part
123-
const isReady = partInstance ? partInstance.part.ingestNotifyPartReady : part?.ingestNotifyPartReady
124-
const itemsReady = partInstance ? partInstance.part.ingestNotifyItemsReady : part?.ingestNotifyItemsReady
157+
// Loop through the partInstances, starting off the state
158+
if (playlist) {
159+
for (const partInstance of Object.values<Pick<PartInstance, PartInstanceFields>>(partInstances)) {
160+
if (!partInstance) continue
161+
162+
if (partInstance.part.shouldNotifyCurrentPlayingPart) {
163+
const isCurrentPartInstance = playlist.currentPartInfo?.partInstanceId === partInstance._id
164+
165+
if (isCurrentPartInstance) {
166+
// If the current, it is playing
167+
playbackStatus = IngestPartPlaybackStatus.PLAY
168+
} else if (playbackStatus === IngestPartPlaybackStatus.UNKNOWN) {
169+
// If not the current, but has been played, it is stopped
170+
playbackStatus = IngestPartPlaybackStatus.STOP
171+
}
172+
}
173+
174+
updateStatusWithPart(partInstance.part)
175+
}
176+
}
177+
178+
for (const part of parts) {
179+
// Check if the part has already been handled by a partInstance
180+
if (partInstances[unprotectString(part._id)]) continue
181+
182+
updateStatusWithPart(part)
183+
}
125184

126185
return {
127186
externalId: ingestPart.externalId,
128187

129-
isReady: isReady ?? null,
130-
itemsReady: itemsReady ?? [],
188+
isReady: isReady,
189+
itemsReady: itemsReady,
131190

132191
playbackStatus,
133192
}

meteor/server/publications/ingestStatus/reactiveContentCache.ts

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export type PartFields =
3232
| 'shouldNotifyCurrentPlayingPart'
3333
| 'ingestNotifyPartReady'
3434
| 'ingestNotifyItemsReady'
35+
| 'ingestNotifyPartExternalId'
3536
export const partFieldSpecifier = literal<MongoFieldSpecifierOnesStrict<Pick<DBPart, PartFields>>>({
3637
_id: 1,
3738
rundownId: 1,
@@ -40,6 +41,7 @@ export const partFieldSpecifier = literal<MongoFieldSpecifierOnesStrict<Pick<DBP
4041
shouldNotifyCurrentPlayingPart: 1,
4142
ingestNotifyPartReady: 1,
4243
ingestNotifyItemsReady: 1,
44+
ingestNotifyPartExternalId: 1,
4345
})
4446

4547
export type PartInstanceFields = '_id' | 'rundownId' | 'segmentId' | 'part' | 'takeCount'

packages/blueprints-integration/src/documents/part.ts

+3
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ export interface IBlueprintMutatablePart<TPrivateData = unknown, TPublicData = u
5959
/** Whether this segment line supports being used in HOLD */
6060
holdMode?: PartHoldMode
6161

62+
/** The externalId of the part as expected by the NRCS. If not set, the externalId property will be used */
63+
ingestNotifyPartExternalId?: string
64+
6265
/** Set to true if ingest-device should be notified when this part starts playing */
6366
shouldNotifyCurrentPlayingPart?: boolean
6467

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

+2
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ export const PlayoutMutatablePartSampleKeys = allKeysOfObject<PlayoutMutatablePa
120120
expectedDuration: true,
121121
holdMode: true,
122122
shouldNotifyCurrentPlayingPart: true,
123+
ingestNotifyPartExternalId: true,
123124
ingestNotifyPartReady: true,
124125
ingestNotifyItemsReady: true,
125126
classes: true,
@@ -283,6 +284,7 @@ export function convertPartToBlueprints(part: ReadonlyDeep<DBPart>): IBlueprintP
283284
expectedDuration: part.expectedDuration,
284285
holdMode: part.holdMode,
285286
shouldNotifyCurrentPlayingPart: part.shouldNotifyCurrentPlayingPart,
287+
ingestNotifyPartExternalId: part.ingestNotifyPartExternalId,
286288
ingestNotifyPartReady: part.ingestNotifyPartReady,
287289
ingestNotifyItemsReady: clone<IngestPartNotifyItemReady[] | undefined>(part.ingestNotifyItemsReady),
288290
classes: clone<string[] | undefined>(part.classes),

0 commit comments

Comments
 (0)