Skip to content

Commit 169419b

Browse files
committed
feat(lsg): add rundown layer to extendedActivePlaylistTopic
1 parent 97aeb9c commit 169419b

7 files changed

Lines changed: 159 additions & 24 deletions

File tree

packages/live-status-gateway/src/collections/rundownHandler.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { DBRundownPlaylist } from '@sofie-automation/corelib/dist/dataModel/Rund
55
import { DBRundown } from '@sofie-automation/corelib/dist/dataModel/Rundown'
66
import { RundownId, RundownPlaylistId } from '@sofie-automation/corelib/dist/dataModel/Ids'
77
import { CollectionName } from '@sofie-automation/corelib/dist/dataModel/Collections'
8-
import { RundownsHandler } from './rundownsHandler.js'
98
import { CorelibPubSub } from '@sofie-automation/corelib/dist/pubsub'
109
import { unprotectString } from '@sofie-automation/server-core-integration'
1110
import { CollectionHandlers } from '../liveStatusServer.js'
@@ -22,11 +21,7 @@ export class RundownHandler extends PublicationCollection<
2221
private _currentPlaylistId: RundownPlaylistId | undefined
2322
private _currentRundownId: RundownId | undefined
2423

25-
constructor(
26-
logger: Logger,
27-
coreHandler: CoreHandler,
28-
private _rundownsHandler?: RundownsHandler
29-
) {
24+
constructor(logger: Logger, coreHandler: CoreHandler) {
3025
super(CollectionName.Rundowns, CorelibPubSub.rundownsInPlaylists, logger, coreHandler)
3126
}
3227

@@ -42,7 +37,6 @@ export class RundownHandler extends PublicationCollection<
4237

4338
private updateAndNotify(): void {
4439
const collection = this.getCollectionOrFail()
45-
this._rundownsHandler?.setRundowns(collection.find(undefined))
4640
if (this._currentRundownId) {
4741
this._collectionData = collection.findOne(this._currentRundownId)
4842
} else {
Lines changed: 97 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,107 @@
11
import { Logger } from 'winston'
22
import { CoreHandler } from '../coreHandler.js'
3-
import { CollectionBase } from '../collectionBase.js'
4-
import { DBRundown } from '@sofie-automation/corelib/dist/dataModel/Rundown'
3+
import { PublicationCollection } from '../publicationCollection.js'
4+
import { DBRundownPlaylist } from '@sofie-automation/corelib/dist/dataModel/RundownPlaylist'
55
import { CollectionName } from '@sofie-automation/corelib/dist/dataModel/Collections'
6+
import _ from 'underscore'
7+
import { CorelibPubSub } from '@sofie-automation/corelib/dist/pubsub'
8+
import { RundownId } from '@sofie-automation/corelib/dist/dataModel/Ids'
9+
import { CollectionHandlers } from '../liveStatusServer.js'
10+
import { PickKeys } from '@sofie-automation/shared-lib/dist/lib/types'
11+
import areElementsShallowEqual from '@sofie-automation/shared-lib/dist/lib/isShallowEqual'
12+
import { DBRundown } from '@sofie-automation/corelib/dist/dataModel/Rundown'
13+
14+
const PLAYLIST_KEYS = ['_id', 'activationId', 'rundownIdsInOrder'] as const
15+
type Playlist = PickKeys<DBRundownPlaylist, typeof PLAYLIST_KEYS>
16+
17+
export class RundownsHandler extends PublicationCollection<
18+
DBRundown[],
19+
CorelibPubSub.rundownsInPlaylists,
20+
CollectionName.Rundowns
21+
> {
22+
private _currentPlaylist: Playlist | undefined
23+
private _rundownIds: RundownId[] = []
624

7-
export class RundownsHandler extends CollectionBase<DBRundown[], CollectionName.Rundowns> {
825
constructor(logger: Logger, coreHandler: CoreHandler) {
9-
super(CollectionName.Rundowns, logger, coreHandler)
26+
super(CollectionName.Rundowns, CorelibPubSub.rundownsInPlaylists, logger, coreHandler)
27+
this._collectionData = []
28+
}
29+
30+
init(handlers: CollectionHandlers): void {
31+
super.init(handlers)
32+
handlers.playlistHandler.subscribe(this.onPlaylistUpdate, PLAYLIST_KEYS)
33+
}
34+
35+
protected changed(): void {
36+
this.updateAndNotify()
37+
}
38+
39+
private updateCollectionData(): boolean {
40+
if (!this._collectionData) return false
41+
const collection = this.getCollectionOrFail()
42+
43+
// Find all rundowns belonging to the current playlist
44+
// const allRundowns: DBRundown[] = collection.find({
45+
// _id: { $in: this._rundownIds.map(unprotectString) },
46+
// })
47+
48+
const allRundowns: DBRundown[] = []
49+
50+
this._rundownIds.map((rundownId) => {
51+
const rundown = collection.findOne(rundownId)
52+
53+
if (rundown) allRundowns.push(rundown)
54+
})
55+
56+
const hasAnythingChanged = !_.isEqual(this._collectionData, allRundowns)
57+
if (hasAnythingChanged) {
58+
this._collectionData = allRundowns
59+
}
60+
61+
return hasAnythingChanged
1062
}
1163

12-
setRundowns(rundowns: DBRundown[]): void {
13-
this.logUpdateReceived('rundowns', rundowns.length)
14-
this._collectionData = rundowns
64+
private clearCollectionData() {
65+
if (!this._collectionData) return
66+
this._collectionData = []
67+
}
68+
69+
private onPlaylistUpdate = (playlist: Playlist | undefined): void => {
70+
this.logUpdateReceived('playlist', `rundownPlaylistId ${playlist?._id}, active ${!!playlist?.activationId}`)
71+
72+
const prevRundownIds = this._rundownIds
73+
const prevPlaylist = this._currentPlaylist
74+
75+
this._currentPlaylist = playlist
76+
this._rundownIds = this._currentPlaylist ? this._currentPlaylist.rundownIdsInOrder : []
77+
78+
if (this._currentPlaylist && this._rundownIds.length && this._currentPlaylist?.activationId) {
79+
const sameSubscription =
80+
areElementsShallowEqual(this._rundownIds, prevRundownIds) &&
81+
areElementsShallowEqual(prevPlaylist?.rundownIdsInOrder ?? [], this._currentPlaylist.rundownIdsInOrder)
82+
83+
if (!sameSubscription) {
84+
// The subscription is based on the current playlist Id
85+
this.setupSubscription([this._currentPlaylist._id])
86+
} else if (this._subscriptionId) {
87+
this.updateAndNotify()
88+
} else {
89+
this.clearAndNotify()
90+
}
91+
} else {
92+
this.clearAndNotify()
93+
}
94+
}
95+
96+
private clearAndNotify() {
97+
this.clearCollectionData()
1598
this.notify(this._collectionData)
1699
}
100+
101+
private updateAndNotify() {
102+
const hasAnythingChanged = this.updateCollectionData()
103+
if (hasAnythingChanged) {
104+
this.notify(this._collectionData)
105+
}
106+
}
17107
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@ import { BucketAdLibActionsHandler } from './collections/bucketAdLibActionsHandl
3232
import { BucketsTopic } from './topics/bucketsTopic.js'
3333
import { ExtendedActivePlaylistTopic } from './topics/extendedActivePlaylistTopic.js'
3434
import { PiecesHandler } from './collections/piecesHandler.js'
35+
import { RundownsHandler } from './collections/rundownsHandler.js'
3536

3637
export interface CollectionHandlers {
3738
studioHandler: StudioHandler
3839
showStyleBaseHandler: ShowStyleBaseHandler
3940
playlistHandler: PlaylistHandler
4041
playlistsHandler: PlaylistsHandler
4142
rundownHandler: RundownHandler
43+
rundownsHandler: RundownsHandler
4244
segmentsHandler: SegmentsHandler
4345
segmentHandler: SegmentHandler
4446
partsHandler: PartsHandler
@@ -76,6 +78,7 @@ export class LiveStatusServer {
7678
const playlistHandler = new PlaylistHandler(this._logger, this._coreHandler)
7779
const playlistsHandler = playlistHandler.playlistsHandler
7880
const rundownHandler = new RundownHandler(this._logger, this._coreHandler)
81+
const rundownsHandler = new RundownsHandler(this._logger, this._coreHandler)
7982
const segmentsHandler = new SegmentsHandler(this._logger, this._coreHandler)
8083
const segmentHandler = new SegmentHandler(this._logger, this._coreHandler, segmentsHandler)
8184
const partsHandler = new PartsHandler(this._logger, this._coreHandler)
@@ -98,6 +101,7 @@ export class LiveStatusServer {
98101
playlistHandler,
99102
playlistsHandler,
100103
rundownHandler,
104+
rundownsHandler,
101105
segmentsHandler,
102106
segmentHandler,
103107
partsHandler,

packages/live-status-gateway/src/topics/extendedActivePlaylistTopic.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@ import {
2323
} from './helpers/playlist/playlistStatus.js'
2424
import { toExtendedPlaylistStatus } from './helpers/playlist/extendedPlaylistStatus.js'
2525
import { Piece } from '@sofie-automation/corelib/dist/dataModel/Piece'
26+
import { DBRundown } from '@sofie-automation/corelib/dist/dataModel/Rundown'
2627

2728
const THROTTLE_PERIOD_MS = 100
2829

2930
export class ExtendedActivePlaylistTopic extends WebSocketTopicBase implements WebSocketTopic {
3031
private _playlistStatusCache: ExtendedPlaylistStatusCache = {
3132
partInstancesInCurrentSegment: [],
33+
rundownsInCurrentPlaylist: [],
3234
partsById: {},
3335
partsBySegmentId: {},
3436
segmentsById: {},
@@ -39,6 +41,7 @@ export class ExtendedActivePlaylistTopic extends WebSocketTopicBase implements W
3941
super(ExtendedActivePlaylistTopic.name, logger, THROTTLE_PERIOD_MS)
4042

4143
handlers.playlistHandler.subscribe(this.onPlaylistUpdate, PLAYLIST_KEYS)
44+
handlers.rundownsHandler.subscribe(this.onRundownsUpdapte)
4245
handlers.partsHandler.subscribe(this.onPartsUpdate)
4346
handlers.partInstancesHandler.subscribe(this.onPartInstancesUpdate, PART_INSTANCES_KEYS)
4447
handlers.pieceInstancesHandler.subscribe(this.onPieceInstancesUpdate, PIECE_INSTANCES_KEYS)
@@ -89,6 +92,18 @@ export class ExtendedActivePlaylistTopic extends WebSocketTopicBase implements W
8992
this.throttledSendStatusToAll()
9093
}
9194

95+
private onRundownsUpdapte = (rundowns: DBRundown[] | undefined): void => {
96+
this.logUpdateReceived('rundowns')
97+
if (!rundowns) return
98+
99+
this._playlistStatusCache.rundownsInCurrentPlaylist = this._playlistStatusCache.activePlaylist
100+
? this._playlistStatusCache.activePlaylist?.rundownIdsInOrder
101+
.map((rundownId) => rundowns.find((rundown) => rundown._id === rundownId))
102+
.filter((rundown) => rundown !== undefined)
103+
: []
104+
this.throttledSendStatusToAll()
105+
}
106+
92107
private onPartsUpdate = (parts: DBPart[] | undefined): void => {
93108
const previousParts = this._playlistStatusCache.partsBySegmentId
94109
this._playlistStatusCache.partsBySegmentId = _.groupBy(parts ?? [], 'segmentId')

packages/live-status-gateway/src/topics/helpers/playlist/extendedPlaylistStatus.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
11
import { DBPart } from '@sofie-automation/corelib/dist/dataModel/Part'
22
import { ExtendedActivePlaylistEvent, RundownPlaylistTimingMode } from '@sofie-automation/live-status-gateway-api'
33
import { literal, unprotectString } from '@sofie-automation/server-core-integration'
4-
import { DBSegment } from '@sofie-automation/corelib/dist/dataModel/Segment'
54
import { ExtendedPlaylistStatusCache } from './playlistStatus.js'
65
import { toPlaylistTiming } from './timing.js'
76
import { transformQuickLoopStatus } from './quickLoop.js'
8-
import { toCurrentSegmentStatus, toExtendedSegmentStatus } from '../segment/segmentStatus.js'
7+
import { toCurrentSegmentStatus } from '../segment/segmentStatus.js'
98
import { toCurrentPartStatus, toPartStatus } from '../part/partStatus.js'
9+
import toRundownStatus from '../rundown/toRundownStatus.js'
1010

1111
export function toExtendedPlaylistStatus(props: ExtendedPlaylistStatusCache): ExtendedActivePlaylistEvent {
12-
const { activePlaylist, partsById, segmentsById, currentPartInstance, partsBySegmentId, nextPartInstance } = props
12+
const {
13+
activePlaylist,
14+
partsById,
15+
segmentsById,
16+
currentPartInstance,
17+
partsBySegmentId,
18+
nextPartInstance,
19+
rundownsInCurrentPlaylist,
20+
} = props
1321
const currentPart: DBPart | null = currentPartInstance ? currentPartInstance.part : null
1422
console.log('nextPartInstance', nextPartInstance)
1523
const nextPart: DBPart | null = nextPartInstance ? nextPartInstance.part : null
@@ -23,15 +31,9 @@ export function toExtendedPlaylistStatus(props: ExtendedPlaylistStatusCache): Ex
2331
id: unprotectString(activePlaylist._id),
2432
externalId: activePlaylist.externalId,
2533
name: activePlaylist.name,
26-
rundowns: activePlaylist.rundownIdsInOrder.map((r) => unprotectString(r)),
34+
rundowns: rundownsInCurrentPlaylist.map((rundown) => toRundownStatus(props, rundown)),
2735
currentPart: toCurrentPartStatus(props, currentPart),
2836
currentSegment: toCurrentSegmentStatus({ ...props }, currentPart, currentSegmentParts),
29-
// TODO: add all fields to this object, then add parts to it.
30-
segments: segmentsById
31-
? Object.entries<DBSegment | undefined>(segmentsById)
32-
.map(([_id, segment]) => (segment ? toExtendedSegmentStatus(props, segment) : null))
33-
.filter((segment) => segment !== null)
34-
: [],
3537
nextPart: toPartStatus(props, nextPart),
3638
quickLoop: transformQuickLoopStatus(activePlaylist, partsById, segmentsById),
3739
publicData: activePlaylist.publicData,

packages/live-status-gateway/src/topics/helpers/playlist/playlistStatus.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { transformQuickLoopStatus } from './quickLoop.js'
1313
import { toCurrentSegmentStatus } from '../segment/segmentStatus.js'
1414
import { toCurrentPartStatus, toPartStatus } from '../part/partStatus.js'
1515
import { Piece } from '@sofie-automation/corelib/dist/dataModel/Piece'
16+
import { DBRundown } from '@sofie-automation/corelib/dist/dataModel/Rundown'
1617

1718
export interface PlaylistStatusCache {
1819
// main data
@@ -37,6 +38,8 @@ export interface PlaylistStatusCache {
3738
}
3839

3940
export interface ExtendedPlaylistStatusCache extends PlaylistStatusCache {
41+
//rundowns
42+
rundownsInCurrentPlaylist: DBRundown[]
4043
// pieces
4144
piecesByPartId: Record<string, Piece[]>
4245
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { DBRundown } from '@sofie-automation/corelib/dist/dataModel/Rundown'
2+
import { Rundown } from '@sofie-automation/live-status-gateway-api'
3+
import { unprotectString } from '@sofie-automation/server-core-integration'
4+
import { toPlaylistTiming } from '../playlist/timing.js'
5+
import { toExtendedSegmentStatus } from '../segment/segmentStatus.js'
6+
import { ExtendedPlaylistStatusCache } from '../playlist/playlistStatus.js'
7+
import { DBSegment } from '@sofie-automation/corelib/dist/dataModel/Segment'
8+
9+
export default function toRundownStatus(props: ExtendedPlaylistStatusCache, rundown: DBRundown): Rundown {
10+
const rundownId = unprotectString(rundown._id)
11+
12+
return {
13+
id: rundownId,
14+
description: rundown.description ?? undefined,
15+
name: rundown.name,
16+
segments: props.segmentsById
17+
? Object.entries<DBSegment | undefined>(props.segmentsById)
18+
.map(([_id, segment]) =>
19+
segment && segment.rundownId === rundown._id ? toExtendedSegmentStatus(props, segment) : null
20+
)
21+
.filter((segment) => segment !== null)
22+
: [],
23+
timing: rundown.timing ? toPlaylistTiming(rundown.timing) : undefined,
24+
publicData: rundown.publicData ?? undefined,
25+
endOfRundownIsShowBreak: rundown.endOfRundownIsShowBreak ?? undefined,
26+
}
27+
}

0 commit comments

Comments
 (0)