Skip to content

Commit 17cec2b

Browse files
authored
Merge pull request Sofie-Automation#1155 from nrkno/fix/piece-status
2 parents fd936d0 + 391bfc4 commit 17cec2b

File tree

10 files changed

+233
-92
lines changed

10 files changed

+233
-92
lines changed

meteor/client/ui/SegmentTimeline/withMediaObjectStatus.tsx

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,35 @@ export function withMediaObjectStatus<IProps extends AnyPiece, IState>(): (
7070
}
7171
}
7272

73+
private getStatusDocForPiece(
74+
piece: BucketAdLibUi | IAdLibListItem | AdLibPieceUi | PieceUi | BucketAdLibActionUi
75+
) {
76+
const pieceUnwrapped = WithMediaObjectStatusHOCComponent.unwrapPieceInstance(piece)
77+
78+
// Bucket items use a different collection
79+
if (RundownUtils.isBucketAdLibItem(piece)) {
80+
return UIBucketContentStatuses.findOne({
81+
bucketId: piece.bucketId,
82+
docId: pieceUnwrapped._id,
83+
})
84+
}
85+
86+
// PieceInstance's might have a dedicated status
87+
if (RundownUtils.isPieceInstance(piece)) {
88+
const status = UIPieceContentStatuses.findOne({
89+
// Future: It would be good for this to be stricter.
90+
pieceId: piece.instance._id,
91+
})
92+
if (status) return status
93+
}
94+
95+
// Fallback to using the one from the source piece
96+
return UIPieceContentStatuses.findOne({
97+
// Future: It would be good for this to be stricter.
98+
pieceId: pieceUnwrapped._id,
99+
})
100+
}
101+
73102
updateDataTracker() {
74103
if (this.destroyed) return
75104

@@ -80,19 +109,8 @@ export function withMediaObjectStatus<IProps extends AnyPiece, IState>(): (
80109

81110
// Check item status
82111
if (piece && (piece.sourceLayer || layer) && studio) {
83-
const pieceUnwrapped = WithMediaObjectStatusHOCComponent.unwrapPieceInstance(piece)
84-
const statusDoc = RundownUtils.isBucketAdLibItem(piece)
85-
? UIBucketContentStatuses.findOne({
86-
bucketId: piece.bucketId,
87-
docId: pieceUnwrapped._id,
88-
})
89-
: UIPieceContentStatuses.findOne({
90-
// Future: It would be good for this to be stricter.
91-
pieceId: pieceUnwrapped._id,
92-
})
93-
94112
// Extract the status or populate some default values
95-
const statusObj = statusDoc?.status ?? DEFAULT_STATUS
113+
const statusObj = this.getStatusDocForPiece(piece)?.status ?? DEFAULT_STATUS
96114

97115
if (RundownUtils.isAdLibPieceOrAdLibListItem(piece)) {
98116
if (!overrides.piece || !_.isEqual(statusObj, (overrides.piece as AdLibPieceUi).contentStatus)) {

meteor/lib/api/rundownNotifications.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export interface UIPieceContentStatus {
3838
segmentId: SegmentId | undefined
3939

4040
pieceId: PieceId | AdLibActionId | RundownBaselineAdLibActionId | PieceInstanceId
41+
isPieceInstance: boolean
4142

4243
name: string | ITranslatableMessage
4344
segmentName: string | undefined

meteor/server/publications/packageManager/expectedPackages/generate.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { PackageContainerOnPackage, Accessor, AccessorOnPackage } from '@sofie-automation/blueprints-integration'
2-
import { getContentVersionHash } from '@sofie-automation/corelib/dist/dataModel/ExpectedPackages'
2+
import { getContentVersionHash, getExpectedPackageId } from '@sofie-automation/corelib/dist/dataModel/ExpectedPackages'
33
import { PeripheralDeviceId, ExpectedPackageId, PieceInstanceId } from '@sofie-automation/corelib/dist/dataModel/Ids'
44
import { protectString, unprotectString } from '@sofie-automation/corelib/dist/protectedString'
55
import {
@@ -115,7 +115,7 @@ export async function updateCollectionForPieceInstanceIds(
115115
if (!pieceInstanceDoc.piece?.expectedPackages) continue
116116

117117
pieceInstanceDoc.piece.expectedPackages.forEach((expectedPackage, i) => {
118-
const sanitisedPackageId = expectedPackage._id || '__unnamed' + i
118+
const sanitisedPackageId = getExpectedPackageId(pieceInstanceId, expectedPackage._id || '__unnamed' + i)
119119

120120
// Map the expectedPackages onto their specified layer:
121121
const allDeviceIds = new Set<PeripheralDeviceId>()
@@ -134,7 +134,7 @@ export async function updateCollectionForPieceInstanceIds(
134134
studio,
135135
{
136136
...expectedPackage,
137-
_id: `${pieceInstanceId}_${sanitisedPackageId}`,
137+
_id: unprotectString(sanitisedPackageId),
138138
rundownId: pieceInstanceDoc.rundownId,
139139
contentVersionHash: getContentVersionHash(expectedPackage),
140140
},

meteor/server/publications/pieceContentStatusUI/bucket/publication.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ async function setupUIBucketContentStatusesPublicationObservers(
9999
if (!bucket || bucket.studioId !== args.studioId) throw new Error(`Bucket "${args.bucketId}" not found!`)
100100

101101
const contentCache = createReactiveContentCache()
102+
triggerUpdate({ newCache: contentCache })
102103

103104
// Set up observers:
104105
return [

meteor/server/publications/pieceContentStatusUI/checkPieceContentStatus.ts

Lines changed: 90 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
VTContent,
1212
} from '@sofie-automation/blueprints-integration'
1313
import { getExpectedPackageId } from '@sofie-automation/corelib/dist/dataModel/ExpectedPackages'
14-
import { ExpectedPackageId, PeripheralDeviceId } from '@sofie-automation/corelib/dist/dataModel/Ids'
14+
import { ExpectedPackageId, PeripheralDeviceId, PieceInstanceId } from '@sofie-automation/corelib/dist/dataModel/Ids'
1515
import {
1616
getPackageContainerPackageId,
1717
PackageContainerPackageStatusDB,
@@ -156,7 +156,9 @@ export function getMediaObjectMediaId(
156156
return undefined
157157
}
158158

159-
export type PieceContentStatusPiece = Pick<PieceGeneric, '_id' | 'content' | 'expectedPackages'>
159+
export type PieceContentStatusPiece = Pick<PieceGeneric, '_id' | 'content' | 'expectedPackages'> & {
160+
pieceInstanceId?: PieceInstanceId
161+
}
160162
export interface PieceContentStatusStudio
161163
extends Pick<
162164
Studio,
@@ -506,60 +508,72 @@ async function checkPieceContentExpectedPackageStatus(
506508

507509
checkedPackageContainers.add(packageContainerId)
508510

509-
const expectedPackageId = getExpectedPackageId(piece._id, expectedPackage._id)
510-
const packageOnPackageContainer = await getPackageContainerPackageStatus(
511-
packageContainerId,
512-
expectedPackageId
513-
)
514-
const packageName =
515-
// @ts-expect-error hack
516-
expectedPackage.content.filePath ||
517-
// @ts-expect-error hack
518-
expectedPackage.content.guid ||
519-
expectedPackage._id
520-
521-
if (!thumbnailUrl && packageOnPackageContainer) {
522-
const sideEffect = getSideEffect(expectedPackage, studio)
523-
524-
const packageThumbnailPath = sideEffect.thumbnailPackageSettings?.path
525-
const thumbnailContainerId = sideEffect.thumbnailContainerId
526-
if (packageThumbnailPath && thumbnailContainerId) {
527-
thumbnailUrl = getAssetUrlFromExpectedPackages(
528-
packageThumbnailPath,
529-
thumbnailContainerId,
511+
const expectedPackageIds = [getExpectedPackageId(piece._id, expectedPackage._id)]
512+
if (piece.pieceInstanceId) {
513+
// If this is a PieceInstance, try looking up the PieceInstance first
514+
expectedPackageIds.unshift(getExpectedPackageId(piece.pieceInstanceId, expectedPackage._id))
515+
}
516+
517+
let warningMessage: ContentMessage | null = null
518+
let matchedExpectedPackageId: ExpectedPackageId | null = null
519+
for (const expectedPackageId of expectedPackageIds) {
520+
const packageOnPackageContainer = await getPackageContainerPackageStatus(
521+
packageContainerId,
522+
expectedPackageId
523+
)
524+
if (!packageOnPackageContainer) continue
525+
526+
matchedExpectedPackageId = expectedPackageId
527+
528+
if (!thumbnailUrl) {
529+
const sideEffect = getSideEffect(expectedPackage, studio)
530+
531+
thumbnailUrl = await getAssetUrlFromPackageContainerStatus(
530532
studio,
531-
packageOnPackageContainer
533+
getPackageContainerPackageStatus,
534+
expectedPackageId,
535+
sideEffect.thumbnailContainerId,
536+
sideEffect.thumbnailPackageSettings?.path
532537
)
533538
}
534-
}
535539

536-
if (!previewUrl && packageOnPackageContainer) {
537-
const sideEffect = getSideEffect(expectedPackage, studio)
540+
if (!previewUrl) {
541+
const sideEffect = getSideEffect(expectedPackage, studio)
538542

539-
const packagePreviewPath = sideEffect.previewPackageSettings?.path
540-
const previewContainerId = sideEffect.previewContainerId
541-
if (packagePreviewPath && previewContainerId) {
542-
previewUrl = getAssetUrlFromExpectedPackages(
543-
packagePreviewPath,
544-
previewContainerId,
543+
previewUrl = await getAssetUrlFromPackageContainerStatus(
545544
studio,
546-
packageOnPackageContainer
545+
getPackageContainerPackageStatus,
546+
expectedPackageId,
547+
sideEffect.previewContainerId,
548+
sideEffect.previewPackageSettings?.path
547549
)
548550
}
551+
552+
warningMessage = getPackageWarningMessage(packageOnPackageContainer, sourceLayer)
553+
554+
// Found a packageOnPackageContainer
555+
break
549556
}
550557

551-
const warningMessage = getPackageWarningMessage(packageOnPackageContainer, sourceLayer)
552-
if (warningMessage) {
553-
messages.push(warningMessage)
558+
if (!matchedExpectedPackageId || warningMessage) {
559+
// If no package matched, we must have a warning
560+
messages.push(warningMessage ?? getPackageSoruceMissingWarning(sourceLayer))
554561
} else {
555562
// No warning, must be OK
556563

564+
const packageName =
565+
// @ts-expect-error hack
566+
expectedPackage.content.filePath ||
567+
// @ts-expect-error hack
568+
expectedPackage.content.guid ||
569+
expectedPackage._id
570+
557571
readyCount++
558572
packageInfos[expectedPackage._id] = {
559573
packageName,
560574
}
561575
// Fetch scan-info about the package:
562-
const dbPackageInfos = await getPackageInfos(expectedPackageId)
576+
const dbPackageInfos = await getPackageInfos(matchedExpectedPackageId)
563577
for (const packageInfo of dbPackageInfos) {
564578
if (packageInfo.type === PackageInfo.Type.SCAN) {
565579
packageInfos[expectedPackage._id].scan = packageInfo.payload
@@ -658,15 +672,32 @@ async function checkPieceContentExpectedPackageStatus(
658672
}
659673
}
660674

675+
async function getAssetUrlFromPackageContainerStatus(
676+
studio: PieceContentStatusStudio,
677+
getPackageContainerPackageStatus: (
678+
packageContainerId: string,
679+
expectedPackageId: ExpectedPackageId
680+
) => Promise<PackageContainerPackageStatusLight | undefined>,
681+
expectedPackageId: ExpectedPackageId,
682+
assetContainerId: string | null | undefined,
683+
packageAssetPath: string | undefined
684+
): Promise<string | undefined> {
685+
if (!assetContainerId || !packageAssetPath) return
686+
687+
const assetPackageContainer = studio.packageContainers[assetContainerId]
688+
if (!assetPackageContainer) return
689+
690+
const previewPackageOnPackageContainer = await getPackageContainerPackageStatus(assetContainerId, expectedPackageId)
691+
if (!previewPackageOnPackageContainer) return
692+
693+
return getAssetUrlFromExpectedPackages(packageAssetPath, assetPackageContainer, previewPackageOnPackageContainer)
694+
}
695+
661696
function getAssetUrlFromExpectedPackages(
662697
assetPath: string,
663-
assetContainerId: string,
664-
studio: PieceContentStatusStudio,
698+
packageContainer: StudioPackageContainer,
665699
packageOnPackageContainer: Pick<PackageContainerPackageStatusDB, 'status'>
666700
): string | undefined {
667-
const packageContainer = studio.packageContainers[assetContainerId]
668-
if (!packageContainer) return
669-
670701
if (packageOnPackageContainer.status.status !== ExpectedPackageStatusAPI.PackageContainerPackageStatusStatus.READY)
671702
return
672703

@@ -689,25 +720,28 @@ function getAssetUrlFromExpectedPackages(
689720
}
690721
}
691722

723+
function getPackageSoruceMissingWarning(sourceLayer: ISourceLayer): ContentMessage {
724+
// Examples of contents in packageOnPackageContainer?.status.statusReason.user:
725+
// * Target package: Quantel clip "XXX" not found
726+
// * Can't read the Package from PackageContainer "Quantel source 0" (on accessor "${accessorLabel}"), due to: Quantel clip "XXX" not found
727+
728+
return {
729+
status: PieceStatusCode.SOURCE_MISSING,
730+
message: generateTranslation(`{{sourceLayer}} can't be found on the playout system`, {
731+
sourceLayer: sourceLayer.name,
732+
}),
733+
}
734+
}
735+
692736
function getPackageWarningMessage(
693-
packageOnPackageContainer: Pick<PackageContainerPackageStatusDB, 'status'> | undefined,
737+
packageOnPackageContainer: Pick<PackageContainerPackageStatusDB, 'status'>,
694738
sourceLayer: ISourceLayer
695739
): ContentMessage | null {
696740
if (
697-
!packageOnPackageContainer ||
698741
packageOnPackageContainer.status.status ===
699-
ExpectedPackageStatusAPI.PackageContainerPackageStatusStatus.NOT_FOUND
742+
ExpectedPackageStatusAPI.PackageContainerPackageStatusStatus.NOT_FOUND
700743
) {
701-
// Examples of contents in packageOnPackageContainer?.status.statusReason.user:
702-
// * Target package: Quantel clip "XXX" not found
703-
// * Can't read the Package from PackageContainer "Quantel source 0" (on accessor "${accessorLabel}"), due to: Quantel clip "XXX" not found
704-
705-
return {
706-
status: PieceStatusCode.SOURCE_MISSING,
707-
message: generateTranslation(`{{sourceLayer}} can't be found on the playout system`, {
708-
sourceLayer: sourceLayer.name,
709-
}),
710-
}
744+
return getPackageSoruceMissingWarning(sourceLayer)
711745
} else if (
712746
packageOnPackageContainer.status.status ===
713747
ExpectedPackageStatusAPI.PackageContainerPackageStatusStatus.NOT_READY

0 commit comments

Comments
 (0)