Skip to content

Commit ae0b339

Browse files
committed
Allow control from config and blueprints
1 parent 6077a81 commit ae0b339

File tree

4 files changed

+115
-38
lines changed

4 files changed

+115
-38
lines changed

packages/blueprints-integration/src/documents/piece.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ export interface IBlueprintPiece<TPrivateData = unknown, TPublicData = unknown>
4545
* Whether to stop this piece before the 'keepalive' period of the part
4646
*/
4747
excludeDuringPartKeepalive?: boolean
48+
49+
/**
50+
* Whether to display AB resolver channel assignment on Director screen.
51+
* If set, overrides show style configuration for this piece.
52+
*/
53+
displayAbChannel?: boolean
4854
}
4955
export interface IBlueprintPieceDB<TPrivateData = unknown, TPublicData = unknown>
5056
extends IBlueprintPiece<TPrivateData, TPublicData> {

packages/corelib/src/dataModel/ShowStyleBase.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,19 @@ export interface DBShowStyleBase {
4848
/** Config values are used by the Blueprints */
4949
blueprintConfigWithOverrides: ObjectWithOverrides<IBlueprintConfig>
5050

51+
/** Configuration for displaying AB resolver channel assignments across different screens */
52+
abChannelDisplay?: {
53+
/** Source layer IDs that should show AB channel info */
54+
sourceLayerIds: string[]
55+
/** Configure by source layer type */
56+
sourceLayerTypes: SourceLayerType[]
57+
/** Only show for specific output layers (e.g., only PGM) */
58+
outputLayerIds: string[]
59+
/** Enable display on Director screen */
60+
showOnDirectorScreen: boolean
61+
// Future: showOnPresenterScreen, showOnCameraScreen when those views are implemented
62+
}
63+
5164
_rundownVersionHash: string
5265

5366
/** Details on the last blueprint used to generate the defaults values for this */

packages/meteor-lib/src/api/showStyles.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { ShowStyleBaseId, ShowStyleVariantId, StudioId } from '@sofie-automation/corelib/dist/dataModel/Ids'
2-
import { HotkeyDefinition, OutputLayers, SourceLayers } from '@sofie-automation/corelib/dist/dataModel/ShowStyleBase'
2+
import {
3+
HotkeyDefinition,
4+
OutputLayers,
5+
SourceLayers,
6+
type DBShowStyleBase,
7+
} from '@sofie-automation/corelib/dist/dataModel/ShowStyleBase'
38
import { DBShowStyleVariant } from '@sofie-automation/corelib/dist/dataModel/ShowStyleVariant'
49

510
export interface NewShowStylesAPI {
@@ -44,6 +49,9 @@ export interface UIShowStyleBase {
4449
outputLayers: OutputLayers
4550
/** "Layers" in the GUI */
4651
sourceLayers: SourceLayers
52+
53+
/** Configuration for displaying AB resolver channels on various screens */
54+
abChannelDisplay?: DBShowStyleBase['abChannelDisplay']
4755
}
4856

4957
export interface CreateAdlibTestingRundownOption {

packages/webui/src/client/ui/ClockView/DirectorScreen.tsx

Lines changed: 87 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,65 @@ import {
5454
import { AdjustLabelFit } from '../util/AdjustLabelFit.js'
5555
import { AutoNextStatus } from '../RundownView/RundownTiming/AutoNextStatus.js'
5656
import { useTranslation } from 'react-i18next'
57+
import { DBShowStyleBase } from '@sofie-automation/corelib/dist/dataModel/ShowStyleBase'
58+
import { PieceInstance } from '@sofie-automation/corelib/dist/dataModel/PieceInstance.js'
5759

5860
interface SegmentUi extends DBSegment {
5961
items: Array<PartUi>
6062
}
6163

64+
/**
65+
* Determines whether a piece instance should display its AB resolver channel assignment on the Director screen.
66+
* Checks piece-level override first, then falls back to show style configuration.
67+
* Note: Future screens (presenter, camera) will have their own showOn* flags when implemented.
68+
*/
69+
function shouldDisplayAbChannel(
70+
pieceInstance: PieceInstance,
71+
showStyleBase: UIShowStyleBase,
72+
config?: DBShowStyleBase['abChannelDisplay']
73+
): boolean {
74+
// Check piece-level override first (from blueprint)
75+
const piece = pieceInstance.piece as any
76+
if (piece.displayAbChannel !== undefined) {
77+
return piece.displayAbChannel
78+
}
79+
80+
// If no config, use sensible defaults but don't show (screen flag defaults to false)
81+
const effectiveConfig: NonNullable<DBShowStyleBase['abChannelDisplay']> = config ?? {
82+
// Default: guess VT and LIVE_SPEAK types
83+
sourceLayerIds: [],
84+
sourceLayerTypes: [SourceLayerType.VT, SourceLayerType.LIVE_SPEAK],
85+
outputLayerIds: [],
86+
87+
// But don't show by default
88+
showOnDirectorScreen: false,
89+
}
90+
91+
// Check if display is enabled for director screen
92+
if (!effectiveConfig.showOnDirectorScreen) return false
93+
94+
const sourceLayer = showStyleBase.sourceLayers?.[pieceInstance.piece.sourceLayerId]
95+
96+
// Check if output layer filter is specified and doesn't match
97+
if (effectiveConfig.outputLayerIds.length > 0) {
98+
if (!effectiveConfig.outputLayerIds.includes(pieceInstance.piece.outputLayerId)) {
99+
return false
100+
}
101+
}
102+
103+
// Check if source layer ID is explicitly listed
104+
if (effectiveConfig.sourceLayerIds.includes(pieceInstance.piece.sourceLayerId)) {
105+
return true
106+
}
107+
108+
// Check sourceLayer type match
109+
if (sourceLayer?.type && effectiveConfig.sourceLayerTypes.includes(sourceLayer.type)) {
110+
return true
111+
}
112+
113+
return false
114+
}
115+
62116
interface TimeMap {
63117
[key: string]: number
64118
}
@@ -382,33 +436,31 @@ function DirectorScreenRender({
382436
)
383437
}
384438

385-
// Compute current and next clip player ids (for VT pieces)
439+
// Compute current and next clip player ids (for pieces with AB sessions)
386440
const currentClipPlayer: string | undefined = useTracker(() => {
387441
if (!currentPartInstance || !currentShowStyleBase || !playlist?.assignedAbSessions) return undefined
442+
const config = currentShowStyleBase.abChannelDisplay
388443
const instances = PieceInstances.find({
389444
partInstanceId: currentPartInstance.instance._id,
390445
reset: { $ne: true },
391446
}).fetch()
392447
for (const pi of instances) {
393-
const sl = currentShowStyleBase.sourceLayers?.[pi.piece.sourceLayerId]
394-
// Only consider VT and LIVE_SPEAK on the PGM output layer
395-
const ol = currentShowStyleBase.outputLayers?.[pi.piece.outputLayerId]
396-
if ((sl?.type === SourceLayerType.VT || sl?.type === SourceLayerType.LIVE_SPEAK) && ol?.isPGM) {
397-
const ab = pi.piece.abSessions
398-
if (!ab || ab.length === 0) continue
399-
for (const s of ab) {
400-
const pool = playlist.assignedAbSessions?.[s.poolName]
401-
if (!pool) continue
402-
const matches: ABSessionAssignment[] = []
403-
for (const key in pool) {
404-
const a = pool[key]
405-
if (a && a.sessionName === s.sessionName) matches.push(a)
406-
}
407-
const live = matches.find((m) => !m.lookahead)
408-
const la = matches.find((m) => m.lookahead)
409-
if (live) return String(live.playerId)
410-
if (la) return String(la.playerId)
448+
// Use configuration to determine if this piece should display AB channel
449+
if (!shouldDisplayAbChannel(pi, currentShowStyleBase, config)) continue
450+
const ab = pi.piece.abSessions
451+
if (!ab || ab.length === 0) continue
452+
for (const s of ab) {
453+
const pool = playlist.assignedAbSessions?.[s.poolName]
454+
if (!pool) continue
455+
const matches: ABSessionAssignment[] = []
456+
for (const key in pool) {
457+
const a = pool[key]
458+
if (a && a.sessionName === s.sessionName) matches.push(a)
411459
}
460+
const live = matches.find((m) => !m.lookahead)
461+
const la = matches.find((m) => m.lookahead)
462+
if (live) return String(live.playerId)
463+
if (la) return String(la.playerId)
412464
}
413465
}
414466
return undefined
@@ -419,30 +471,28 @@ function DirectorScreenRender({
419471
// We need the ShowStyleBase to resolve sourceLayer types
420472
const ssb = UIShowStyleBases.findOne(nextShowStyleBaseId)
421473
if (!ssb) return undefined
474+
const config = ssb.abChannelDisplay
422475
const instances = PieceInstances.find({
423476
partInstanceId: nextPartInstance.instance._id,
424477
reset: { $ne: true },
425478
}).fetch()
426479
for (const pi of instances) {
427-
const sl = ssb.sourceLayers?.[pi.piece.sourceLayerId]
428-
// Only consider VT and LIVE_SPEAK on the PGM output layer
429-
const ol = ssb.outputLayers?.[pi.piece.outputLayerId]
430-
if ((sl?.type === SourceLayerType.VT || sl?.type === SourceLayerType.LIVE_SPEAK) && ol?.isPGM) {
431-
const ab = pi.piece.abSessions
432-
if (!ab || ab.length === 0) continue
433-
for (const s of ab) {
434-
const pool = playlist.assignedAbSessions?.[s.poolName]
435-
if (!pool) continue
436-
const matches: ABSessionAssignment[] = []
437-
for (const key in pool) {
438-
const a = pool[key]
439-
if (a && a.sessionName === s.sessionName) matches.push(a)
440-
}
441-
const live = matches.find((m) => !m.lookahead)
442-
const la = matches.find((m) => m.lookahead)
443-
if (live) return String(live.playerId)
444-
if (la) return String(la.playerId)
480+
// Use configuration to determine if this piece should display AB channel
481+
if (!shouldDisplayAbChannel(pi, ssb, config)) continue
482+
const ab = pi.piece.abSessions
483+
if (!ab || ab.length === 0) continue
484+
for (const s of ab) {
485+
const pool = playlist.assignedAbSessions?.[s.poolName]
486+
if (!pool) continue
487+
const matches: ABSessionAssignment[] = []
488+
for (const key in pool) {
489+
const a = pool[key]
490+
if (a && a.sessionName === s.sessionName) matches.push(a)
445491
}
492+
const live = matches.find((m) => !m.lookahead)
493+
const la = matches.find((m) => m.lookahead)
494+
if (live) return String(live.playerId)
495+
if (la) return String(la.playerId)
446496
}
447497
}
448498
return undefined

0 commit comments

Comments
 (0)