Skip to content

Commit

Permalink
Merge pull request #1155 from nrkno/fix/piece-status
Browse files Browse the repository at this point in the history
  • Loading branch information
Julusian authored Feb 29, 2024
2 parents fd936d0 + 391bfc4 commit 17cec2b
Show file tree
Hide file tree
Showing 10 changed files with 233 additions and 92 deletions.
42 changes: 30 additions & 12 deletions meteor/client/ui/SegmentTimeline/withMediaObjectStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,35 @@ export function withMediaObjectStatus<IProps extends AnyPiece, IState>(): (
}
}

private getStatusDocForPiece(
piece: BucketAdLibUi | IAdLibListItem | AdLibPieceUi | PieceUi | BucketAdLibActionUi
) {
const pieceUnwrapped = WithMediaObjectStatusHOCComponent.unwrapPieceInstance(piece)

// Bucket items use a different collection
if (RundownUtils.isBucketAdLibItem(piece)) {
return UIBucketContentStatuses.findOne({
bucketId: piece.bucketId,
docId: pieceUnwrapped._id,
})
}

// PieceInstance's might have a dedicated status
if (RundownUtils.isPieceInstance(piece)) {
const status = UIPieceContentStatuses.findOne({
// Future: It would be good for this to be stricter.
pieceId: piece.instance._id,
})
if (status) return status
}

// Fallback to using the one from the source piece
return UIPieceContentStatuses.findOne({
// Future: It would be good for this to be stricter.
pieceId: pieceUnwrapped._id,
})
}

updateDataTracker() {
if (this.destroyed) return

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

// Check item status
if (piece && (piece.sourceLayer || layer) && studio) {
const pieceUnwrapped = WithMediaObjectStatusHOCComponent.unwrapPieceInstance(piece)
const statusDoc = RundownUtils.isBucketAdLibItem(piece)
? UIBucketContentStatuses.findOne({
bucketId: piece.bucketId,
docId: pieceUnwrapped._id,
})
: UIPieceContentStatuses.findOne({
// Future: It would be good for this to be stricter.
pieceId: pieceUnwrapped._id,
})

// Extract the status or populate some default values
const statusObj = statusDoc?.status ?? DEFAULT_STATUS
const statusObj = this.getStatusDocForPiece(piece)?.status ?? DEFAULT_STATUS

if (RundownUtils.isAdLibPieceOrAdLibListItem(piece)) {
if (!overrides.piece || !_.isEqual(statusObj, (overrides.piece as AdLibPieceUi).contentStatus)) {
Expand Down
1 change: 1 addition & 0 deletions meteor/lib/api/rundownNotifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export interface UIPieceContentStatus {
segmentId: SegmentId | undefined

pieceId: PieceId | AdLibActionId | RundownBaselineAdLibActionId | PieceInstanceId
isPieceInstance: boolean

name: string | ITranslatableMessage
segmentName: string | undefined
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PackageContainerOnPackage, Accessor, AccessorOnPackage } from '@sofie-automation/blueprints-integration'
import { getContentVersionHash } from '@sofie-automation/corelib/dist/dataModel/ExpectedPackages'
import { getContentVersionHash, getExpectedPackageId } from '@sofie-automation/corelib/dist/dataModel/ExpectedPackages'
import { PeripheralDeviceId, ExpectedPackageId, PieceInstanceId } from '@sofie-automation/corelib/dist/dataModel/Ids'
import { protectString, unprotectString } from '@sofie-automation/corelib/dist/protectedString'
import {
Expand Down Expand Up @@ -115,7 +115,7 @@ export async function updateCollectionForPieceInstanceIds(
if (!pieceInstanceDoc.piece?.expectedPackages) continue

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

// Map the expectedPackages onto their specified layer:
const allDeviceIds = new Set<PeripheralDeviceId>()
Expand All @@ -134,7 +134,7 @@ export async function updateCollectionForPieceInstanceIds(
studio,
{
...expectedPackage,
_id: `${pieceInstanceId}_${sanitisedPackageId}`,
_id: unprotectString(sanitisedPackageId),
rundownId: pieceInstanceDoc.rundownId,
contentVersionHash: getContentVersionHash(expectedPackage),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ async function setupUIBucketContentStatusesPublicationObservers(
if (!bucket || bucket.studioId !== args.studioId) throw new Error(`Bucket "${args.bucketId}" not found!`)

const contentCache = createReactiveContentCache()
triggerUpdate({ newCache: contentCache })

// Set up observers:
return [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
VTContent,
} from '@sofie-automation/blueprints-integration'
import { getExpectedPackageId } from '@sofie-automation/corelib/dist/dataModel/ExpectedPackages'
import { ExpectedPackageId, PeripheralDeviceId } from '@sofie-automation/corelib/dist/dataModel/Ids'
import { ExpectedPackageId, PeripheralDeviceId, PieceInstanceId } from '@sofie-automation/corelib/dist/dataModel/Ids'
import {
getPackageContainerPackageId,
PackageContainerPackageStatusDB,
Expand Down Expand Up @@ -156,7 +156,9 @@ export function getMediaObjectMediaId(
return undefined
}

export type PieceContentStatusPiece = Pick<PieceGeneric, '_id' | 'content' | 'expectedPackages'>
export type PieceContentStatusPiece = Pick<PieceGeneric, '_id' | 'content' | 'expectedPackages'> & {
pieceInstanceId?: PieceInstanceId
}
export interface PieceContentStatusStudio
extends Pick<
Studio,
Expand Down Expand Up @@ -506,60 +508,72 @@ async function checkPieceContentExpectedPackageStatus(

checkedPackageContainers.add(packageContainerId)

const expectedPackageId = getExpectedPackageId(piece._id, expectedPackage._id)
const packageOnPackageContainer = await getPackageContainerPackageStatus(
packageContainerId,
expectedPackageId
)
const packageName =
// @ts-expect-error hack
expectedPackage.content.filePath ||
// @ts-expect-error hack
expectedPackage.content.guid ||
expectedPackage._id

if (!thumbnailUrl && packageOnPackageContainer) {
const sideEffect = getSideEffect(expectedPackage, studio)

const packageThumbnailPath = sideEffect.thumbnailPackageSettings?.path
const thumbnailContainerId = sideEffect.thumbnailContainerId
if (packageThumbnailPath && thumbnailContainerId) {
thumbnailUrl = getAssetUrlFromExpectedPackages(
packageThumbnailPath,
thumbnailContainerId,
const expectedPackageIds = [getExpectedPackageId(piece._id, expectedPackage._id)]
if (piece.pieceInstanceId) {
// If this is a PieceInstance, try looking up the PieceInstance first
expectedPackageIds.unshift(getExpectedPackageId(piece.pieceInstanceId, expectedPackage._id))
}

let warningMessage: ContentMessage | null = null
let matchedExpectedPackageId: ExpectedPackageId | null = null
for (const expectedPackageId of expectedPackageIds) {
const packageOnPackageContainer = await getPackageContainerPackageStatus(
packageContainerId,
expectedPackageId
)
if (!packageOnPackageContainer) continue

matchedExpectedPackageId = expectedPackageId

if (!thumbnailUrl) {
const sideEffect = getSideEffect(expectedPackage, studio)

thumbnailUrl = await getAssetUrlFromPackageContainerStatus(
studio,
packageOnPackageContainer
getPackageContainerPackageStatus,
expectedPackageId,
sideEffect.thumbnailContainerId,
sideEffect.thumbnailPackageSettings?.path
)
}
}

if (!previewUrl && packageOnPackageContainer) {
const sideEffect = getSideEffect(expectedPackage, studio)
if (!previewUrl) {
const sideEffect = getSideEffect(expectedPackage, studio)

const packagePreviewPath = sideEffect.previewPackageSettings?.path
const previewContainerId = sideEffect.previewContainerId
if (packagePreviewPath && previewContainerId) {
previewUrl = getAssetUrlFromExpectedPackages(
packagePreviewPath,
previewContainerId,
previewUrl = await getAssetUrlFromPackageContainerStatus(
studio,
packageOnPackageContainer
getPackageContainerPackageStatus,
expectedPackageId,
sideEffect.previewContainerId,
sideEffect.previewPackageSettings?.path
)
}

warningMessage = getPackageWarningMessage(packageOnPackageContainer, sourceLayer)

// Found a packageOnPackageContainer
break
}

const warningMessage = getPackageWarningMessage(packageOnPackageContainer, sourceLayer)
if (warningMessage) {
messages.push(warningMessage)
if (!matchedExpectedPackageId || warningMessage) {
// If no package matched, we must have a warning
messages.push(warningMessage ?? getPackageSoruceMissingWarning(sourceLayer))
} else {
// No warning, must be OK

const packageName =
// @ts-expect-error hack
expectedPackage.content.filePath ||
// @ts-expect-error hack
expectedPackage.content.guid ||
expectedPackage._id

readyCount++
packageInfos[expectedPackage._id] = {
packageName,
}
// Fetch scan-info about the package:
const dbPackageInfos = await getPackageInfos(expectedPackageId)
const dbPackageInfos = await getPackageInfos(matchedExpectedPackageId)
for (const packageInfo of dbPackageInfos) {
if (packageInfo.type === PackageInfo.Type.SCAN) {
packageInfos[expectedPackage._id].scan = packageInfo.payload
Expand Down Expand Up @@ -658,15 +672,32 @@ async function checkPieceContentExpectedPackageStatus(
}
}

async function getAssetUrlFromPackageContainerStatus(
studio: PieceContentStatusStudio,
getPackageContainerPackageStatus: (
packageContainerId: string,
expectedPackageId: ExpectedPackageId
) => Promise<PackageContainerPackageStatusLight | undefined>,
expectedPackageId: ExpectedPackageId,
assetContainerId: string | null | undefined,
packageAssetPath: string | undefined
): Promise<string | undefined> {
if (!assetContainerId || !packageAssetPath) return

const assetPackageContainer = studio.packageContainers[assetContainerId]
if (!assetPackageContainer) return

const previewPackageOnPackageContainer = await getPackageContainerPackageStatus(assetContainerId, expectedPackageId)
if (!previewPackageOnPackageContainer) return

return getAssetUrlFromExpectedPackages(packageAssetPath, assetPackageContainer, previewPackageOnPackageContainer)
}

function getAssetUrlFromExpectedPackages(
assetPath: string,
assetContainerId: string,
studio: PieceContentStatusStudio,
packageContainer: StudioPackageContainer,
packageOnPackageContainer: Pick<PackageContainerPackageStatusDB, 'status'>
): string | undefined {
const packageContainer = studio.packageContainers[assetContainerId]
if (!packageContainer) return

if (packageOnPackageContainer.status.status !== ExpectedPackageStatusAPI.PackageContainerPackageStatusStatus.READY)
return

Expand All @@ -689,25 +720,28 @@ function getAssetUrlFromExpectedPackages(
}
}

function getPackageSoruceMissingWarning(sourceLayer: ISourceLayer): ContentMessage {
// Examples of contents in packageOnPackageContainer?.status.statusReason.user:
// * Target package: Quantel clip "XXX" not found
// * Can't read the Package from PackageContainer "Quantel source 0" (on accessor "${accessorLabel}"), due to: Quantel clip "XXX" not found

return {
status: PieceStatusCode.SOURCE_MISSING,
message: generateTranslation(`{{sourceLayer}} can't be found on the playout system`, {
sourceLayer: sourceLayer.name,
}),
}
}

function getPackageWarningMessage(
packageOnPackageContainer: Pick<PackageContainerPackageStatusDB, 'status'> | undefined,
packageOnPackageContainer: Pick<PackageContainerPackageStatusDB, 'status'>,
sourceLayer: ISourceLayer
): ContentMessage | null {
if (
!packageOnPackageContainer ||
packageOnPackageContainer.status.status ===
ExpectedPackageStatusAPI.PackageContainerPackageStatusStatus.NOT_FOUND
ExpectedPackageStatusAPI.PackageContainerPackageStatusStatus.NOT_FOUND
) {
// Examples of contents in packageOnPackageContainer?.status.statusReason.user:
// * Target package: Quantel clip "XXX" not found
// * Can't read the Package from PackageContainer "Quantel source 0" (on accessor "${accessorLabel}"), due to: Quantel clip "XXX" not found

return {
status: PieceStatusCode.SOURCE_MISSING,
message: generateTranslation(`{{sourceLayer}} can't be found on the playout system`, {
sourceLayer: sourceLayer.name,
}),
}
return getPackageSoruceMissingWarning(sourceLayer)
} else if (
packageOnPackageContainer.status.status ===
ExpectedPackageStatusAPI.PackageContainerPackageStatusStatus.NOT_READY
Expand Down
Loading

0 comments on commit 17cec2b

Please sign in to comment.