@@ -5,6 +5,7 @@ import { TimelineObjRundown } from '@sofie-automation/corelib/dist/dataModel/Tim
5
5
import { normalizeArray } from '@sofie-automation/corelib/dist/lib'
6
6
import { PieceTimelineMetadata } from './pieceGroup'
7
7
import { StudioPlayoutModelBase } from '../../studio/model/StudioPlayoutModel'
8
+ import { logger } from '../../logging'
8
9
import { JobContext } from '../../jobs'
9
10
import { getCurrentTime } from '../../lib'
10
11
import { PlayoutModel } from '../model/PlayoutModel'
@@ -46,7 +47,7 @@ export function deNowifyMultiGatewayTimeline(
46
47
playoutModel . nextPartInstance
47
48
)
48
49
49
- deNowifyCurrentPieces (
50
+ const { objectsNotDeNowified } = deNowifyCurrentPieces (
50
51
targetNowTime ,
51
52
timingContext ,
52
53
currentPartInstance ,
@@ -55,6 +56,9 @@ export function deNowifyMultiGatewayTimeline(
55
56
)
56
57
57
58
updatePlannedTimingsForPieceInstances ( playoutModel , currentPartInstance , partGroupTimings , timelineObjsMap )
59
+
60
+ // Because updatePlannedTimingsForPieceInstances changes start times of infinites, we can now run deNowifyInfinites()
61
+ deNowifyInfinites ( targetNowTime , objectsNotDeNowified , timelineObjsMap )
58
62
}
59
63
60
64
/**
@@ -142,16 +146,83 @@ function updatePartInstancePlannedTimes(
142
146
}
143
147
}
144
148
149
+ /**
150
+ * Replace the `now` time in any timeline objects in freshly-placed infinites.
151
+ *
152
+ * This is effectively only needed when a new item has been placed on the timeline just now and changes made by
153
+ * `updatePlannedTimingsForPieceInstances` haven't been taken into account when generating the timeline. On the next
154
+ * regeneration, items will already use the timestamps persited by `updatePlannedTimingsForPieceInstances` and will not
155
+ * be included in `infiniteObjs`.
156
+ */
157
+ function deNowifyInfinites (
158
+ targetNowTime : number ,
159
+ /** A list of objects that need to be updated */
160
+ infiniteObjs : TimelineObjRundown [ ] ,
161
+ timelineObjsMap : Record < string , TimelineObjRundown >
162
+ ) {
163
+ /**
164
+ * Recursively look up the absolute starttime of a timeline object
165
+ * taking into account its parent's times.
166
+ * Note: This only supports timeline objects that have absolute enable.start times.
167
+ */
168
+ const getStartTime = ( obj : TimelineObjRundown ) : number | undefined => {
169
+ if ( Array . isArray ( obj . enable ) ) return undefined
170
+
171
+ const myStartTime = typeof obj . enable . start === 'number' ? obj . enable . start : undefined
172
+
173
+ if ( ! obj . inGroup ) return myStartTime
174
+
175
+ if ( myStartTime === undefined ) return undefined
176
+
177
+ const parentObject = timelineObjsMap [ obj . inGroup ]
178
+ if ( ! parentObject ) return undefined
179
+
180
+ const parentStartTime = getStartTime ( parentObject )
181
+ if ( parentStartTime === undefined ) return undefined
182
+
183
+ return parentStartTime + myStartTime
184
+ }
185
+
186
+ for ( const obj of infiniteObjs ) {
187
+ if ( Array . isArray ( obj . enable ) || obj . enable . start !== 'now' ) continue
188
+
189
+ if ( ! obj . inGroup ) {
190
+ obj . enable = { start : targetNowTime }
191
+ continue
192
+ }
193
+
194
+ const parentObject = timelineObjsMap [ obj . inGroup ]
195
+ if ( ! parentObject ) {
196
+ logger . error ( `deNowifyInfinites: Parent obj "${ obj . inGroup } " not found of object "${ obj . id } "` )
197
+ continue
198
+ }
199
+
200
+ const parentStartTime = getStartTime ( parentObject )
201
+ if ( parentStartTime === undefined ) {
202
+ logger . error (
203
+ `deNowifyInfinites: Unable to derive an absolute start time of parent "${ obj . inGroup } " for object "${ obj . id } "`
204
+ )
205
+ continue
206
+ }
207
+
208
+ obj . enable = { start : targetNowTime - parentStartTime }
209
+ logger . silly (
210
+ `deNowifyInfinites: Setting "${ obj . id } " enable.start = ${ obj . enable . start } , ${ targetNowTime } ${ parentStartTime } parentObject: "${ parentObject . id } "`
211
+ )
212
+ }
213
+ }
145
214
/**
146
215
* Replace the `now` time in any Pieces on the timeline from the current Part with concrete start times
216
+ * @returns a list of object that couldn't be updated at this time.
147
217
*/
148
218
function deNowifyCurrentPieces (
149
219
targetNowTime : number ,
150
220
timingContext : RundownTimelineTimingContext ,
151
221
currentPartInstance : PlayoutPartInstanceModel ,
152
222
currentPartGroupStartTime : number ,
153
223
timelineObjsMap : Record < string , TimelineObjRundown >
154
- ) {
224
+ ) : { objectsNotDeNowified : TimelineObjRundown [ ] } {
225
+ const objectsNotDeNowified : TimelineObjRundown [ ] = [ ]
155
226
// The relative time for 'now' to be resolved to, inside of the part group
156
227
const nowInPart = targetNowTime - currentPartGroupStartTime
157
228
@@ -176,6 +247,8 @@ function deNowifyCurrentPieces(
176
247
obj . enable = { start : nowInPart }
177
248
} else if ( ! obj . inGroup ) {
178
249
obj . enable = { start : targetNowTime }
250
+ } else {
251
+ objectsNotDeNowified . push ( obj )
179
252
}
180
253
}
181
254
}
@@ -203,6 +276,7 @@ function deNowifyCurrentPieces(
203
276
}
204
277
}
205
278
}
279
+ return { objectsNotDeNowified }
206
280
}
207
281
208
282
function updatePlannedTimingsForPieceInstances (
0 commit comments