@@ -54,11 +54,65 @@ import {
5454import { AdjustLabelFit } from '../util/AdjustLabelFit.js'
5555import { AutoNextStatus } from '../RundownView/RundownTiming/AutoNextStatus.js'
5656import { 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
5860interface 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+
62116interface 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