Skip to content

Commit 5308430

Browse files
committed
feat(EAV-488): add packages topic to LSG
1 parent 3252531 commit 5308430

File tree

30 files changed

+442
-82
lines changed

30 files changed

+442
-82
lines changed

meteor/server/publications/pieceContentStatusUI/checkPieceContentStatus.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ import _ from 'underscore'
3232
import { getSideEffect } from '@sofie-automation/meteor-lib/dist/collections/ExpectedPackages'
3333
import { getActiveRoutes, getRoutedMappings } from '@sofie-automation/meteor-lib/dist/collections/Studios'
3434
import { ensureHasTrailingSlash, generateTranslation, unprotectString } from '../../lib/tempLib'
35-
import { PieceContentStatusObj } from '@sofie-automation/meteor-lib/dist/api/pieceContentStatus'
3635
import { MediaObjects, PackageContainerPackageStatuses, PackageInfos } from '../../collections'
3736
import {
3837
mediaObjectFieldSpecifier,
@@ -43,6 +42,7 @@ import {
4342
PackageInfoLight,
4443
PieceDependencies,
4544
} from './common'
45+
import { PieceContentStatusObj } from '@sofie-automation/corelib/dist/dataModel/PieceContentStatus'
4646

4747
interface ScanInfoForPackages {
4848
[packageId: string]: ScanInfoForPackage

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ import {
1313
} from '@sofie-automation/corelib/dist/dataModel/Ids'
1414
import { MongoFieldSpecifierOnesStrict } from '@sofie-automation/corelib/dist/mongo'
1515
import { ReadonlyDeep } from 'type-fest'
16-
import { CustomCollectionName, MeteorPubSub } from '@sofie-automation/meteor-lib/dist/api/pubsub'
17-
import { UIPieceContentStatus } from '@sofie-automation/meteor-lib/dist/api/rundownNotifications'
16+
import { UIPieceContentStatus } from '@sofie-automation/corelib/dist/dataModel/PieceContentStatus'
1817
import { DBRundownPlaylist } from '@sofie-automation/corelib/dist/dataModel/RundownPlaylist'
1918
import {
2019
MediaObjects,
@@ -57,6 +56,8 @@ import { PieceContentStatusStudio } from '../checkPieceContentStatus'
5756
import { check, Match } from 'meteor/check'
5857
import { DBPartInstance } from '@sofie-automation/corelib/dist/dataModel/PartInstance'
5958
import { triggerWriteAccessBecauseNoCheckNecessary } from '../../../security/securityVerify'
59+
import { CorelibPubSub } from '@sofie-automation/corelib/dist/pubsub'
60+
import { CustomCollectionName } from '@sofie-automation/corelib/dist/dataModel/Collections'
6061

6162
interface UIPieceContentStatusesArgs {
6263
readonly rundownPlaylistId: RundownPlaylistId
@@ -469,15 +470,15 @@ function updatePartAndSegmentInfoForExistingDocs(
469470
}
470471

471472
meteorCustomPublish(
472-
MeteorPubSub.uiPieceContentStatuses,
473+
CorelibPubSub.uiPieceContentStatuses,
473474
CustomCollectionName.UIPieceContentStatuses,
474475
async function (pub, rundownPlaylistId: RundownPlaylistId | null) {
475476
check(rundownPlaylistId, Match.Maybe(String))
476477

477478
triggerWriteAccessBecauseNoCheckNecessary()
478479

479480
if (!rundownPlaylistId) {
480-
logger.info(`Pub.${CustomCollectionName.UISegmentPartNotes}: Not playlistId`)
481+
logger.info(`Pub.${CustomCollectionName.UIPieceContentStatuses}: Not playlistId`)
481482
return
482483
}
483484

@@ -487,7 +488,7 @@ meteorCustomPublish(
487488
UIPieceContentStatusesState,
488489
UIPieceContentStatusesUpdateProps
489490
>(
490-
`pub_${MeteorPubSub.uiPieceContentStatuses}_${rundownPlaylistId}`,
491+
`pub_${CorelibPubSub.uiPieceContentStatuses}_${rundownPlaylistId}`,
491492
{ rundownPlaylistId },
492493
setupUIPieceContentStatusesPublicationObservers,
493494
manipulateUIPieceContentStatusesPublicationData,

meteor/server/publications/pieceContentStatusUI/rundown/regenerateItems.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
RundownBaselineAdLibActionId,
77
} from '@sofie-automation/corelib/dist/dataModel/Ids'
88
import { ReadonlyDeep } from 'type-fest'
9-
import { UIPieceContentStatus } from '@sofie-automation/meteor-lib/dist/api/rundownNotifications'
9+
import { UIPieceContentStatus } from '@sofie-automation/corelib/dist/dataModel/PieceContentStatus'
1010
import { literal, protectString } from '../../../lib/tempLib'
1111
import { CustomPublishCollection } from '../../../lib/customPublication'
1212
import { ContentCache } from './reactiveContentCache'

packages/corelib/src/dataModel/Collections.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,10 @@ export enum CollectionName {
4848
Workers = 'workers',
4949
WorkerThreads = 'workersThreads',
5050
}
51+
52+
/**
53+
* Ids of possible Custom collections, populated by DDP subscriptions
54+
*/
55+
export enum CustomCollectionName {
56+
UIPieceContentStatuses = 'uiPieceContentStatuses',
57+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { ITranslatableMessage, PackageInfo } from '@sofie-automation/blueprints-integration'
2+
import { ProtectedString } from '../protectedString'
3+
import {
4+
RundownId,
5+
PartId,
6+
SegmentId,
7+
PieceId,
8+
AdLibActionId,
9+
RundownBaselineAdLibActionId,
10+
PieceInstanceId,
11+
} from './Ids'
12+
import { PieceStatusCode } from './Piece'
13+
14+
export type UIPieceContentStatusId = ProtectedString<'UIPieceContentStatus'>
15+
export interface UIPieceContentStatus {
16+
_id: UIPieceContentStatusId
17+
18+
segmentRank: number
19+
partRank: number
20+
21+
rundownId: RundownId
22+
partId: PartId | undefined
23+
segmentId: SegmentId | undefined
24+
25+
pieceId: PieceId | AdLibActionId | RundownBaselineAdLibActionId | PieceInstanceId
26+
isPieceInstance: boolean
27+
28+
name: string | ITranslatableMessage
29+
segmentName: string | undefined
30+
31+
status: PieceContentStatusObj
32+
}
33+
34+
export interface PieceContentStatusObj {
35+
status: PieceStatusCode
36+
messages: ITranslatableMessage[]
37+
38+
freezes: Array<PackageInfo.Anomaly>
39+
blacks: Array<PackageInfo.Anomaly>
40+
scenes: Array<number>
41+
42+
thumbnailUrl: string | undefined
43+
previewUrl: string | undefined
44+
45+
packageName: string | null
46+
47+
contentDuration: number | undefined
48+
49+
progress: number | undefined
50+
}

packages/corelib/src/pubsub.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { DBPart } from './dataModel/Part'
2-
import { CollectionName } from './dataModel/Collections'
2+
import { CollectionName, CustomCollectionName } from './dataModel/Collections'
33
import { MongoQuery } from './mongo'
44
import { AdLibAction } from './dataModel/AdlibAction'
55
import { AdLibPiece } from './dataModel/AdLibPiece'
@@ -37,6 +37,7 @@ import {
3737
} from '@sofie-automation/shared-lib/dist/core/model/Ids'
3838
import { BlueprintId, BucketId, RundownPlaylistActivationId, SegmentId, ShowStyleVariantId } from './dataModel/Ids'
3939
import { PackageInfoDB } from './dataModel/PackageInfos'
40+
import { UIPieceContentStatus } from './dataModel/PieceContentStatus'
4041

4142
/**
4243
* Ids of possible DDP subscriptions for any the UI and gateways accessing the Rundown & RundownPlaylist model.
@@ -180,6 +181,12 @@ export enum CorelibPubSub {
180181
* Fetch all the PackageInfos owned by a PeripheralDevice
181182
*/
182183
packageInfos = 'packageInfos',
184+
185+
/**
186+
* Fetch the Pieces content-status in the given RundownPlaylist
187+
* If the id is null, nothing will be returned
188+
*/
189+
uiPieceContentStatuses = 'uiPieceContentStatuses',
183190
}
184191

185192
/**
@@ -317,6 +324,10 @@ export interface CorelibPubSubTypes {
317324
token?: string
318325
) => CollectionName.PackageContainerStatuses
319326
[CorelibPubSub.packageInfos]: (deviceId: PeripheralDeviceId, token?: string) => CollectionName.PackageInfos
327+
328+
[CorelibPubSub.uiPieceContentStatuses]: (
329+
rundownPlaylistId: RundownPlaylistId | null
330+
) => CustomCollectionName.UIPieceContentStatuses
320331
}
321332

322333
export type CorelibPubSubCollections = {
@@ -347,4 +358,8 @@ export type CorelibPubSubCollections = {
347358
[CollectionName.Studios]: DBStudio
348359
[CollectionName.Timelines]: TimelineComplete
349360
[CollectionName.TimelineDatastore]: DBTimelineDatastoreEntry
361+
} & CorelibPubSubCustomCollections
362+
363+
export type CorelibPubSubCustomCollections = {
364+
[CustomCollectionName.UIPieceContentStatuses]: UIPieceContentStatus
350365
}

packages/live-status-gateway/api/asyncapi.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ channels:
4848
- $ref: '#/components/messages/activePieces'
4949
- $ref: '#/components/messages/segments'
5050
- $ref: '#/components/messages/adLibs'
51+
- $ref: '#/components/messages/packages'
5152
components:
5253
messages:
5354
ping:
@@ -124,3 +125,8 @@ components:
124125
description: AdLibs in active Playlist
125126
payload:
126127
$ref: './schemas/adLibs.yaml#/$defs/adLibs'
128+
packages:
129+
name: packages
130+
description: Status of Packages expected by Pieces
131+
payload:
132+
$ref: './schemas/packages.yaml#/$defs/packages'
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
title: Packages
2+
description: Packages schema for websocket subscriptions
3+
$defs:
4+
packages:
5+
type: object
6+
properties:
7+
event:
8+
type: string
9+
const: packages
10+
rundownPlaylistId:
11+
description: Unique id of the rundown playlist, or null if no playlist is active
12+
oneOf:
13+
- type: string
14+
- type: 'null'
15+
packages:
16+
description: The Package statuses for this playlist
17+
type: array
18+
items:
19+
$ref: '#/$defs/package'
20+
required: [event, rundownPlaylistId, packages]
21+
additionalProperties: false
22+
examples:
23+
- event: packages
24+
rundownPlaylistId: 'OKAgZmZ0Buc99lE_2uPPSKVbMrQ_'
25+
packages:
26+
$ref: '#/$defs/package/examples'
27+
package:
28+
type: object
29+
properties:
30+
packageName:
31+
description: Name of the package
32+
oneOf:
33+
- type: string
34+
- type: 'null'
35+
statusCode:
36+
description: Status code
37+
type: number
38+
rundownId:
39+
description: Id of the Rundown that expects this package
40+
type: string
41+
partId:
42+
description: Id of the Part that expects this package
43+
type: string
44+
segmentId:
45+
description: Id of the Segment that expects this package
46+
type: string
47+
pieceId:
48+
description: Id of the Piece that expects this package
49+
type: string
50+
thumbnailUrl:
51+
description: URL where the thumbnail can be accessed
52+
type: string
53+
previewUrl:
54+
description: URL where the preview can be accessed
55+
type: string
56+
required: [packageName, statusCode, rundownId, pieceId]
57+
additionalProperties: false
58+
examples:
59+
- packageName: 'MV000123.mxf'
60+
statusCode: 0
61+
rundownId: 'y9HauyWkcxQS3XaAOsW40BRLLsI_'
62+
pieceId: 'C6K_yIMuGFUk8X_L9A9_jRT6aq4_'
63+
thumbnailUrl: 'https://package-manager.local/package/MV000123.mov_thumbnail.jpg'
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { Logger } from 'winston'
2+
import { CoreHandler } from '../coreHandler'
3+
import { Collection, PickArr, PublicationCollection } from '../wsHandler'
4+
import { DBRundownPlaylist } from '@sofie-automation/corelib/dist/dataModel/RundownPlaylist'
5+
import { UIPieceContentStatus } from '@sofie-automation/corelib/dist/dataModel/PieceContentStatus'
6+
import throttleToNextTick from '@sofie-automation/shared-lib/dist/lib/throttleToNextTick'
7+
import { RundownPlaylistId } from '@sofie-automation/corelib/dist/dataModel/Ids'
8+
import { CorelibPubSub } from '@sofie-automation/corelib/dist/pubsub'
9+
import { CollectionHandlers } from '../liveStatusServer'
10+
import { CustomCollectionName } from '@sofie-automation/corelib/dist/dataModel/Collections'
11+
12+
const PLAYLIST_KEYS = ['_id'] as const
13+
type Playlist = PickArr<DBRundownPlaylist, typeof PLAYLIST_KEYS>
14+
15+
export class PieceContentStatusesHandler
16+
extends PublicationCollection<
17+
UIPieceContentStatus[],
18+
CorelibPubSub.uiPieceContentStatuses,
19+
CustomCollectionName.UIPieceContentStatuses
20+
>
21+
implements Collection<UIPieceContentStatus[]>
22+
{
23+
private _currentPlaylistId: RundownPlaylistId | undefined
24+
25+
private _throttledUpdateAndNotify = throttleToNextTick(() => {
26+
this.updateAndNotify()
27+
})
28+
29+
constructor(logger: Logger, coreHandler: CoreHandler) {
30+
super(CustomCollectionName.UIPieceContentStatuses, CorelibPubSub.uiPieceContentStatuses, logger, coreHandler)
31+
}
32+
33+
init(handlers: CollectionHandlers): void {
34+
super.init(handlers)
35+
36+
handlers.playlistHandler.subscribe(this.onPlaylistUpdated, PLAYLIST_KEYS)
37+
}
38+
39+
changed(): void {
40+
this._throttledUpdateAndNotify()
41+
}
42+
43+
private updateCollectionData() {
44+
const collection = this.getCollectionOrFail()
45+
this._collectionData = collection.find({})
46+
}
47+
48+
private clearCollectionData() {
49+
this._collectionData = []
50+
}
51+
52+
onPlaylistUpdated = (playlist: Playlist | undefined): void => {
53+
this.logUpdateReceived('playlist', `rundownPlaylistId ${playlist?._id}`)
54+
const prevPlaylistId = this._currentPlaylistId
55+
this._currentPlaylistId = playlist?._id
56+
57+
if (this._currentPlaylistId) {
58+
if (prevPlaylistId !== this._currentPlaylistId) {
59+
this.stopSubscription()
60+
this.setupSubscription(this._currentPlaylistId)
61+
}
62+
} else {
63+
this.clearAndNotify()
64+
}
65+
}
66+
67+
private clearAndNotify() {
68+
this.clearCollectionData()
69+
this.notify(this._collectionData)
70+
}
71+
72+
private updateAndNotify() {
73+
this.updateCollectionData()
74+
this.notify(this._collectionData)
75+
}
76+
}

packages/live-status-gateway/src/liveStatusServer.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import { PartsHandler } from './collections/partsHandler'
2323
import { PieceInstancesHandler } from './collections/pieceInstancesHandler'
2424
import { AdLibsTopic } from './topics/adLibsTopic'
2525
import { ActivePiecesTopic } from './topics/activePiecesTopic'
26+
import { PieceContentStatusesHandler } from './collections/pieceContentStatusesHandler'
27+
import { PackagesTopic } from './topics/packagesTopic'
2628

2729
export interface CollectionHandlers {
2830
studioHandler: StudioHandler
@@ -40,6 +42,7 @@ export interface CollectionHandlers {
4042
adLibsHandler: AdLibsHandler
4143
globalAdLibActionsHandler: GlobalAdLibActionsHandler
4244
globalAdLibsHandler: GlobalAdLibsHandler
45+
pieceContentStatusesHandler: PieceContentStatusesHandler
4346
}
4447

4548
export class LiveStatusServer {
@@ -72,6 +75,7 @@ export class LiveStatusServer {
7275
const adLibsHandler = new AdLibsHandler(this._logger, this._coreHandler)
7376
const globalAdLibActionsHandler = new GlobalAdLibActionsHandler(this._logger, this._coreHandler)
7477
const globalAdLibsHandler = new GlobalAdLibsHandler(this._logger, this._coreHandler)
78+
const pieceContentStatusesHandler = new PieceContentStatusesHandler(this._logger, this._coreHandler)
7579

7680
const handlers: CollectionHandlers = {
7781
studioHandler,
@@ -89,6 +93,7 @@ export class LiveStatusServer {
8993
adLibsHandler,
9094
globalAdLibActionsHandler,
9195
globalAdLibsHandler,
96+
pieceContentStatusesHandler,
9297
}
9398

9499
for (const handlerName in handlers) {
@@ -100,12 +105,14 @@ export class LiveStatusServer {
100105
const activePlaylistTopic = new ActivePlaylistTopic(this._logger, handlers)
101106
const segmentsTopic = new SegmentsTopic(this._logger, handlers)
102107
const adLibsTopic = new AdLibsTopic(this._logger, handlers)
108+
const packageStatusTopic = new PackagesTopic(this._logger, handlers)
103109

104110
rootChannel.addTopic(StatusChannels.studio, studioTopic)
105111
rootChannel.addTopic(StatusChannels.activePlaylist, activePlaylistTopic)
106112
rootChannel.addTopic(StatusChannels.activePieces, activePiecesTopic)
107113
rootChannel.addTopic(StatusChannels.segments, segmentsTopic)
108114
rootChannel.addTopic(StatusChannels.adLibs, adLibsTopic)
115+
rootChannel.addTopic(StatusChannels.packages, packageStatusTopic)
109116

110117
const wss = new WebSocketServer({ port: 8080 })
111118
wss.on('connection', (ws, request) => {

0 commit comments

Comments
 (0)