-
Notifications
You must be signed in to change notification settings - Fork 57
feat: TSR Device Feedback -> Rundown (SOFIE-311) #1731
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
f3124e9
wip: blueprint api sketch
Julusian 26a3d15
wip: refine types
Julusian 0cf1509
wip: ddp api and job queueing
Julusian 13b4980
wip: poc implement job-worker
Julusian a63cba7
wip: event plumbing
Julusian 090ebdb
wip: subscription hookup
Julusian 39ef3ff
logging
Julusian c2de90a
wip
Julusian f411206
fix
Julusian cd57149
wip
Julusian 4b46d14
wip: tsr
Julusian 6cb9c0b
fix
Julusian 58dd4f0
fix
Julusian 1a24a58
Merge branch 'main' into feat/tsr-device-feedback
Julusian aa72256
review comments
Julusian File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
179 changes: 179 additions & 0 deletions
179
meteor/server/publications/externalEventSubscriptions.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,179 @@ | ||
| import { PeripheralDeviceId, StudioId } from '@sofie-automation/corelib/dist/dataModel/Ids' | ||
| import { assertNever, getHash, literal } from '@sofie-automation/corelib/dist/lib' | ||
| import { protectString } from '@sofie-automation/corelib/dist/protectedString' | ||
| import { MongoFieldSpecifierOnesStrict } from '@sofie-automation/corelib/dist/mongo' | ||
| import { DBRundown } from '@sofie-automation/corelib/dist/dataModel/Rundown' | ||
| import { DBRundownPlaylist } from '@sofie-automation/corelib/dist/dataModel/RundownPlaylist/RundownPlaylist' | ||
| import { ReadonlyDeep } from 'type-fest' | ||
| import { | ||
| CustomPublish, | ||
| CustomPublishCollection, | ||
| meteorCustomPublish, | ||
| setUpCollectionOptimizedObserver, | ||
| SetupObserversResult, | ||
| TriggerUpdate, | ||
| } from '../lib/customPublication' | ||
| import { logger } from '../logging' | ||
| import { RundownPlaylists, Rundowns } from '../collections' | ||
| import { | ||
| PeripheralDevicePubSub, | ||
| PeripheralDevicePubSubCollectionsNames, | ||
| ExternalEventSubscriptionDocument, | ||
| ExternalEventSubscriptionId, | ||
| } from '@sofie-automation/shared-lib/dist/pubsub/peripheralDevice' | ||
| import type { PeripheralDeviceExternalEvent } from '@sofie-automation/shared-lib/dist/peripheralDevice/externalEvents' | ||
| import { checkAccessAndGetPeripheralDevice } from '../security/check' | ||
| import { check } from '../lib/check' | ||
|
|
||
| type RundownPlaylistFields = '_id' | 'activationId' | ||
| const rundownPlaylistFieldSpecifier = literal< | ||
| MongoFieldSpecifierOnesStrict<Pick<DBRundownPlaylist, RundownPlaylistFields>> | ||
| >({ | ||
| _id: 1, | ||
| activationId: 1, | ||
| }) | ||
|
|
||
| type RundownFields = '_id' | 'playlistId' | 'externalEventSubscriptions' | ||
| const rundownFieldSpecifier = literal<MongoFieldSpecifierOnesStrict<Pick<DBRundown, RundownFields>>>({ | ||
| _id: 1, | ||
| playlistId: 1, | ||
| externalEventSubscriptions: 1, | ||
| }) | ||
|
|
||
| interface ExternalEventSubscriptionsArgs { | ||
| readonly studioId: StudioId | ||
| readonly type: PeripheralDeviceExternalEvent['type'] | ||
| } | ||
|
|
||
| // eslint-disable-next-line @typescript-eslint/no-empty-object-type | ||
| interface ExternalEventSubscriptionsState {} | ||
|
|
||
| interface ExternalEventSubscriptionsUpdateProps { | ||
| invalidateAll: true | ||
| } | ||
|
|
||
| async function setupExternalEventSubscriptionsObservers( | ||
| args: ReadonlyDeep<ExternalEventSubscriptionsArgs>, | ||
| triggerUpdate: TriggerUpdate<ExternalEventSubscriptionsUpdateProps> | ||
| ): Promise<SetupObserversResult> { | ||
| const trigger = () => triggerUpdate({ invalidateAll: true }) | ||
|
|
||
| return [ | ||
| // Observe active playlists in the studio — activation/deactivation changes which rundowns are in scope | ||
| RundownPlaylists.observeChanges( | ||
| { studioId: args.studioId }, | ||
| { added: trigger, changed: trigger, removed: trigger }, | ||
| { projection: rundownPlaylistFieldSpecifier } | ||
| ), | ||
| // Observe rundowns in the studio — react only when externalEventSubscriptions or playlistId changes | ||
| Rundowns.observeChanges( | ||
| { studioId: args.studioId }, | ||
| { added: trigger, changed: trigger, removed: trigger }, | ||
| { projection: rundownFieldSpecifier } | ||
| ), | ||
| ] | ||
| } | ||
|
|
||
| async function manipulateExternalEventSubscriptionsData( | ||
| args: ReadonlyDeep<ExternalEventSubscriptionsArgs>, | ||
| _state: Partial<ExternalEventSubscriptionsState>, | ||
| collection: CustomPublishCollection<ExternalEventSubscriptionDocument>, | ||
| _updateProps: Partial<ReadonlyDeep<ExternalEventSubscriptionsUpdateProps>> | undefined | ||
| ): Promise<void> { | ||
| // Find all active playlists in the studio | ||
| const activePlaylists = (await RundownPlaylists.findFetchAsync( | ||
| { studioId: args.studioId, activationId: { $exists: true } }, | ||
| { projection: rundownPlaylistFieldSpecifier } | ||
| )) as Pick<DBRundownPlaylist, RundownPlaylistFields>[] | ||
| const activePlaylistIds = activePlaylists.map((p) => p._id) | ||
|
|
||
| // Find rundowns belonging to active playlists | ||
| const activeRundowns = (await Rundowns.findFetchAsync( | ||
| { studioId: args.studioId, playlistId: { $in: activePlaylistIds } }, | ||
| { projection: rundownFieldSpecifier } | ||
| )) as Pick<DBRundown, RundownFields>[] | ||
|
|
||
| // Build the set of valid IDs and the docs to upsert (filtered by type) | ||
| const validIds = new Set<ExternalEventSubscriptionId>() | ||
| const subsToUpsert: ExternalEventSubscriptionDocument[] = [] | ||
|
|
||
| for (const rundown of activeRundowns) { | ||
| for (const sub of rundown.externalEventSubscriptions ?? []) { | ||
| if (sub.type !== args.type) continue | ||
|
|
||
| switch (sub.type) { | ||
| case 'tsr': { | ||
| const id = protectString<ExternalEventSubscriptionId>( | ||
| getHash(`tsr_${sub.deviceId}_${sub.deviceType}_${String(sub.event)}`) | ||
| ) | ||
| validIds.add(id) | ||
| subsToUpsert.push({ | ||
| _id: id, | ||
| type: 'tsr', | ||
| deviceId: sub.deviceId, | ||
| deviceType: sub.deviceType, | ||
| event: sub.event as string, | ||
| }) | ||
| break | ||
| } | ||
| default: | ||
| assertNever(sub.type) | ||
| break | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Remove docs for subscriptions that are no longer active | ||
| collection.remove((doc) => !validIds.has(doc._id)) | ||
|
|
||
| // Upsert each individual subscription doc | ||
| for (const sub of subsToUpsert) { | ||
| collection.replace(sub) | ||
| } | ||
| } | ||
|
|
||
| async function startOrJoinExternalEventSubscriptionsPublication( | ||
| pub: CustomPublish<ExternalEventSubscriptionDocument>, | ||
| studioId: StudioId, | ||
| type: PeripheralDeviceExternalEvent['type'] | ||
| ) { | ||
| await setUpCollectionOptimizedObserver< | ||
| ExternalEventSubscriptionDocument, | ||
| ExternalEventSubscriptionsArgs, | ||
| ExternalEventSubscriptionsState, | ||
| ExternalEventSubscriptionsUpdateProps | ||
| >( | ||
| `pub_${PeripheralDevicePubSub.externalEventSubscriptionsForDevice}_${studioId}_${type}`, | ||
| { studioId, type }, | ||
| setupExternalEventSubscriptionsObservers, | ||
| manipulateExternalEventSubscriptionsData, | ||
| pub, | ||
| 100 | ||
| ) | ||
| } | ||
|
|
||
| meteorCustomPublish( | ||
| PeripheralDevicePubSub.externalEventSubscriptionsForDevice, | ||
| PeripheralDevicePubSubCollectionsNames.externalEventSubscriptions, | ||
| async function ( | ||
| pub: CustomPublish<ExternalEventSubscriptionDocument>, | ||
| type: PeripheralDeviceExternalEvent['type'], | ||
| deviceId: PeripheralDeviceId, | ||
| token: string | undefined | ||
| ) { | ||
| check(deviceId, String) | ||
| check(type, String) | ||
|
|
||
| const peripheralDevice = await checkAccessAndGetPeripheralDevice(deviceId, token, this) | ||
|
Julusian marked this conversation as resolved.
|
||
|
|
||
| const studioId = peripheralDevice.studioAndConfigId?.studioId | ||
| if (!studioId) { | ||
| logger.warn( | ||
| `Publication ${PeripheralDevicePubSub.externalEventSubscriptionsForDevice}: device ${deviceId} has no studio` | ||
| ) | ||
| return | ||
| } | ||
|
|
||
| await startOrJoinExternalEventSubscriptionsPublication(pub, studioId, type) | ||
| } | ||
| ) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.