Skip to content

Commit a72355f

Browse files
committed
SOFIE-252 | make timing of rehearsal end relative to the first TAKE
1 parent 656a680 commit a72355f

File tree

4 files changed

+150
-68
lines changed

4 files changed

+150
-68
lines changed

packages/webui/src/client/lib/rundownPlaylistUtil.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ export class RundownPlaylistClientUtil {
9696
currentPartInstance: PartInstance | undefined
9797
nextPartInstance: PartInstance | undefined
9898
previousPartInstance: PartInstance | undefined
99+
firstTakenPartInstance: PartInstance | undefined
99100
} {
100101
let unorderedRundownIds = rundownIds0
101102
if (!unorderedRundownIds) {
@@ -116,10 +117,22 @@ export class RundownPlaylistClientUtil {
116117
}).fetch()
117118
: []
118119

120+
const firstTakenPartInstance = UIPartInstances.findOne(
121+
{
122+
rundownId: { $in: unorderedRundownIds },
123+
reset: { $ne: true },
124+
takeCount: { $exists: true },
125+
},
126+
{
127+
sort: { takeCount: 1 },
128+
}
129+
)
130+
119131
return {
120132
currentPartInstance: instances.find((inst) => inst._id === playlist.currentPartInfo?.partInstanceId),
121133
nextPartInstance: instances.find((inst) => inst._id === playlist.nextPartInfo?.partInstanceId),
122134
previousPartInstance: instances.find((inst) => inst._id === playlist.previousPartInfo?.partInstanceId),
135+
firstTakenPartInstance,
123136
}
124137
}
125138

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { RundownTimingProvider } from '../RundownView/RundownTiming/RundownTimin
55

66
import { StudioScreenSaver } from '../StudioScreenSaver/StudioScreenSaver.js'
77
import { PresenterScreen } from './PresenterScreen.js'
8-
import { DirectorScreen } from './DirectorScreen.js'
8+
import { DirectorScreen } from './DirectorScreen/DirectorScreen'
99
import { OverlayScreen } from './OverlayScreen.js'
1010
import { OverlayScreenSaver } from './OverlayScreenSaver.js'
1111
import { RundownPlaylists } from '../../collections/index.js'

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

Lines changed: 53 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,24 @@
11
import ClassNames from 'classnames'
22
import { DBSegment } from '@sofie-automation/corelib/dist/dataModel/Segment'
3-
import { PartUi } from '../SegmentTimeline/SegmentTimelineContainer.js'
3+
import { PartUi } from '../../SegmentTimeline/SegmentTimelineContainer.js'
44
import { DBRundownPlaylist } from '@sofie-automation/corelib/dist/dataModel/RundownPlaylist'
55
import { Rundown } from '@sofie-automation/corelib/dist/dataModel/Rundown'
6-
import { useTiming } from '../RundownView/RundownTiming/withTiming.js'
76
import {
87
useSubscription,
98
useSubscriptions,
109
useTracker,
1110
withTracker,
12-
} from '../../lib/ReactMeteorData/ReactMeteorData.js'
13-
import { getCurrentTime } from '../../lib/systemTime.js'
11+
} from '../../../lib/ReactMeteorData/ReactMeteorData.js'
12+
import { getCurrentTime } from '../../../lib/systemTime.js'
1413
import { PartInstance } from '@sofie-automation/meteor-lib/dist/collections/PartInstances'
1514
import { MeteorPubSub } from '@sofie-automation/meteor-lib/dist/api/pubsub'
16-
import { PieceIconContainer } from './ClockViewPieceIcons/ClockViewPieceIcon.js'
17-
import { PieceNameContainer } from './ClockViewPieceIcons/ClockViewPieceName.js'
18-
import { Timediff } from './Timediff.js'
19-
import { RundownUtils } from '../../lib/rundown.js'
15+
import { PieceIconContainer } from '../ClockViewPieceIcons/ClockViewPieceIcon.js'
16+
import { PieceNameContainer } from '../ClockViewPieceIcons/ClockViewPieceName.js'
17+
import { Timediff } from '../Timediff.js'
18+
import { RundownUtils } from '../../../lib/rundown.js'
2019
import { PieceLifespan } from '@sofie-automation/blueprints-integration'
2120
import { DBPart } from '@sofie-automation/corelib/dist/dataModel/Part'
22-
import { PieceFreezeContainer } from './ClockViewPieceIcons/ClockViewFreezeCount.js'
21+
import { PieceFreezeContainer } from '../ClockViewPieceIcons/ClockViewFreezeCount.js'
2322
import { PlaylistTiming } from '@sofie-automation/corelib/dist/playout/rundownTiming'
2423
import {
2524
RundownId,
@@ -30,26 +29,27 @@ import {
3029
} from '@sofie-automation/corelib/dist/dataModel/Ids'
3130
import { DBShowStyleVariant } from '@sofie-automation/corelib/dist/dataModel/ShowStyleVariant'
3231
import { calculatePartInstanceExpectedDurationWithTransition } from '@sofie-automation/corelib/dist/playout/timings'
33-
import { getPlaylistTimingDiff } from '../../lib/rundownTiming.js'
3432
import { UIShowStyleBase } from '@sofie-automation/meteor-lib/dist/api/showStyles'
35-
import { UIShowStyleBases, UIStudios } from '../Collections.js'
33+
import { UIShowStyleBases, UIStudios } from '../../Collections.js'
3634
import { UIStudio } from '@sofie-automation/meteor-lib/dist/api/studios'
37-
import { PieceInstances, RundownPlaylists, Rundowns, ShowStyleVariants } from '../../collections/index.js'
38-
import { RundownPlaylistCollectionUtil } from '../../collections/rundownPlaylistUtil.js'
39-
import { CorelibPubSub } from '@sofie-automation/corelib/dist/pubsub'
40-
import { useSetDocumentClass } from '../util/useSetDocumentClass.js'
41-
import { useRundownAndShowStyleIdsForPlaylist } from '../util/useRundownAndShowStyleIdsForPlaylist.js'
42-
import { RundownPlaylistClientUtil } from '../../lib/rundownPlaylistUtil.js'
43-
import { CurrentPartOrSegmentRemaining } from '../RundownView/RundownTiming/CurrentPartOrSegmentRemaining.js'
4435
import {
45-
OverUnderClockComponent,
46-
PlannedEndComponent,
47-
TimeSincePlannedEndComponent,
48-
TimeToPlannedEndComponent,
49-
} from '../../lib/Components/CounterComponents.js'
50-
import { AdjustLabelFit } from '../util/AdjustLabelFit.js'
51-
import { AutoNextStatus } from '../RundownView/RundownTiming/AutoNextStatus.js'
36+
PartInstances,
37+
PieceInstances,
38+
RundownPlaylists,
39+
Rundowns,
40+
ShowStyleVariants,
41+
} from '../../../collections/index.js'
42+
import { RundownPlaylistCollectionUtil } from '../../../collections/rundownPlaylistUtil.js'
43+
import { CorelibPubSub } from '@sofie-automation/corelib/dist/pubsub'
44+
import { useSetDocumentClass } from '../../util/useSetDocumentClass.js'
45+
import { useRundownAndShowStyleIdsForPlaylist } from '../../util/useRundownAndShowStyleIdsForPlaylist.js'
46+
import { RundownPlaylistClientUtil } from '../../../lib/rundownPlaylistUtil.js'
47+
import { CurrentPartOrSegmentRemaining } from '../../RundownView/RundownTiming/CurrentPartOrSegmentRemaining.js'
48+
49+
import { AdjustLabelFit } from '../../util/AdjustLabelFit.js'
50+
import { AutoNextStatus } from '../../RundownView/RundownTiming/AutoNextStatus.js'
5251
import { useTranslation } from 'react-i18next'
52+
import { DirectorScreenTop } from './DirectorScreenTop.js'
5353

5454
interface SegmentUi extends DBSegment {
5555
items: Array<PartUi>
@@ -80,6 +80,7 @@ export interface DirectorScreenTrackedProps {
8080
nextShowStyleBaseId: ShowStyleBaseId | undefined
8181
showStyleBaseIds: ShowStyleBaseId[]
8282
rundownIds: RundownId[]
83+
firstTakenPartInstance: PartInstance | undefined
8384
}
8485

8586
function getShowStyleBaseIdSegmentPartUi(
@@ -185,6 +186,7 @@ const getDirectorScreenReactive = (props: DirectorScreenProps): DirectorScreenTr
185186
restoredFromSnapshotId: 0,
186187
},
187188
})
189+
188190
const segments: Array<SegmentUi> = []
189191
let showStyleBaseIds: ShowStyleBaseId[] = []
190192
let rundowns: Rundown[] = []
@@ -200,17 +202,24 @@ const getDirectorScreenReactive = (props: DirectorScreenProps): DirectorScreenTr
200202
let nextSegment: SegmentUi | undefined = undefined
201203
let nextPartInstanceUi: PartUi | undefined = undefined
202204
let nextShowStyleBaseId: ShowStyleBaseId | undefined = undefined
205+
let firstTakenPartInstanceUi: PartInstance | undefined = undefined
203206

204207
if (playlist) {
205208
rundowns = RundownPlaylistCollectionUtil.getRundownsOrdered(playlist)
209+
206210
const orderedSegmentsAndParts = RundownPlaylistClientUtil.getSegmentsAndPartsSync(playlist)
207211
rundownIds = rundowns.map((rundown) => rundown._id)
208212
const rundownsToShowstyles: Map<RundownId, ShowStyleBaseId> = new Map()
209213
for (const rundown of rundowns) {
210214
rundownsToShowstyles.set(rundown._id, rundown.showStyleBaseId)
211215
}
216+
212217
showStyleBaseIds = rundowns.map((rundown) => rundown.showStyleBaseId)
213-
const { currentPartInstance, nextPartInstance } = RundownPlaylistClientUtil.getSelectedPartInstances(playlist)
218+
const { currentPartInstance, nextPartInstance, firstTakenPartInstance } =
219+
RundownPlaylistClientUtil.getSelectedPartInstances(playlist)
220+
221+
firstTakenPartInstanceUi = firstTakenPartInstance
222+
214223
const partInstance = currentPartInstance ?? nextPartInstance
215224
if (partInstance) {
216225
// This is to register a reactive dependency on Rundown-spanning PieceInstances, that we may miss otherwise.
@@ -262,6 +271,7 @@ const getDirectorScreenReactive = (props: DirectorScreenProps): DirectorScreenTr
262271
}
263272
}
264273
}
274+
265275
return {
266276
studio,
267277
segments,
@@ -278,6 +288,7 @@ const getDirectorScreenReactive = (props: DirectorScreenProps): DirectorScreenTr
278288
nextSegment,
279289
nextPartInstance: nextPartInstanceUi,
280290
nextShowStyleBaseId,
291+
firstTakenPartInstance: firstTakenPartInstanceUi,
281292
}
282293
}
283294

@@ -309,7 +320,7 @@ function useDirectorScreenSubscriptions(props: DirectorScreenProps): void {
309320
useSubscription(CorelibPubSub.showStyleVariants, null, showStyleVariantIds)
310321
useSubscription(MeteorPubSub.rundownLayouts, showStyleBaseIds)
311322

312-
const { currentPartInstance, nextPartInstance } = useTracker(
323+
const { currentPartInstance, nextPartInstance, firstTakenPartInstance } = useTracker(
313324
() => {
314325
const playlist = RundownPlaylists.findOne(props.playlistId, {
315326
fields: {
@@ -323,16 +334,27 @@ function useDirectorScreenSubscriptions(props: DirectorScreenProps): void {
323334
if (playlist) {
324335
return RundownPlaylistClientUtil.getSelectedPartInstances(playlist)
325336
} else {
326-
return { currentPartInstance: undefined, nextPartInstance: undefined, previousPartInstance: undefined }
337+
return {
338+
currentPartInstance: undefined,
339+
nextPartInstance: undefined,
340+
previousPartInstance: undefined,
341+
firstTakenPartInstance: undefined,
342+
}
327343
}
328344
},
329345
[props.playlistId],
330-
{ currentPartInstance: undefined, nextPartInstance: undefined, previousPartInstance: undefined }
346+
{
347+
currentPartInstance: undefined,
348+
nextPartInstance: undefined,
349+
previousPartInstance: undefined,
350+
firstTakenPartInstance: undefined,
351+
}
331352
)
332353

333354
useSubscriptions(CorelibPubSub.pieceInstances, [
334355
currentPartInstance && [[currentPartInstance.rundownId], [currentPartInstance._id], {}],
335356
nextPartInstance && [[nextPartInstance.rundownId], [nextPartInstance._id], {}],
357+
firstTakenPartInstance && [[firstTakenPartInstance.rundownId], [firstTakenPartInstance._id], {}],
336358
])
337359
}
338360

@@ -353,53 +375,17 @@ function DirectorScreenRender({
353375
nextPartInstance,
354376
nextSegment,
355377
rundownIds,
378+
firstTakenPartInstance,
356379
}: Readonly<DirectorScreenProps & DirectorScreenTrackedProps>) {
357380
useSetDocumentClass('dark', 'xdark')
358381
const { t } = useTranslation()
359382

360-
const timingDurations = useTiming()
361-
362383
if (playlist && playlistId && segments) {
363384
const expectedStart = PlaylistTiming.getExpectedStart(playlist.timing) || 0
364-
const expectedEnd = PlaylistTiming.getExpectedEnd(playlist.timing)
365-
const expectedDuration = PlaylistTiming.getExpectedDuration(playlist.timing) || 0
366-
const now = timingDurations.currentTime ?? getCurrentTime()
367-
368-
const overUnderClock = getPlaylistTimingDiff(playlist, timingDurations) ?? 0
369385

370386
return (
371387
<div className="director-screen">
372-
<div className="director-screen__top">
373-
{expectedEnd ? (
374-
<div className="director-screen__top__planned-end">
375-
<div>
376-
<PlannedEndComponent value={expectedEnd} />
377-
</div>
378-
{t('Planned End')}
379-
</div>
380-
) : null}
381-
{expectedEnd ? (
382-
<div className="director-screen__top__time-to director-screen__top__planned-container">
383-
<div>
384-
<TimeToPlannedEndComponent value={now - expectedEnd} />
385-
</div>
386-
<span className="director-screen__top__planned-to">{t('Time to planned end')}</span>
387-
</div>
388-
) : (
389-
<div>
390-
<div className="director-screen__top__planned-container">
391-
<TimeSincePlannedEndComponent value={getCurrentTime() - (expectedStart + expectedDuration)} />
392-
<span className="director-screen__top__planned-since">{t('Time since planned end')}</span>
393-
</div>
394-
</div>
395-
)}
396-
<div>
397-
<div>
398-
<OverUnderClockComponent value={overUnderClock} />
399-
</div>
400-
<span className="director-screen__top__over-under">{t('Over/Under')}</span>
401-
</div>
402-
</div>
388+
<DirectorScreenTop firstTakenPartInstance={firstTakenPartInstance} playlist={playlist} />
403389
<div className="director-screen__body">
404390
{
405391
// Current Part:
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import {
2+
OverUnderClockComponent,
3+
PlannedEndComponent,
4+
TimeSincePlannedEndComponent,
5+
TimeToPlannedEndComponent,
6+
} from '../../../lib/Components/CounterComponents'
7+
import { useTiming } from '../../RundownView/RundownTiming/withTiming'
8+
import { getPlaylistTimingDiff } from '../../../lib/rundownTiming'
9+
import { DBRundownPlaylist } from '@sofie-automation/corelib/dist/dataModel/RundownPlaylist'
10+
import { getCurrentTime } from '../../../lib/systemTime'
11+
import { useTranslation } from 'react-i18next'
12+
import { PlaylistTiming } from '@sofie-automation/corelib/dist/playout/rundownTiming'
13+
import { PartInstance } from '@sofie-automation/meteor-lib/dist/collections/PartInstances'
14+
15+
export interface DirectorScreenTopProps {
16+
playlist: DBRundownPlaylist
17+
firstTakenPartInstance: PartInstance | undefined
18+
}
19+
20+
export function DirectorScreenTop({ playlist, firstTakenPartInstance }: Readonly<DirectorScreenTopProps>): JSX.Element {
21+
const timingDurations = useTiming()
22+
23+
const rehearsalInProgress = Boolean(playlist.rehearsal && firstTakenPartInstance?.timings?.take)
24+
25+
const expectedStart = rehearsalInProgress
26+
? firstTakenPartInstance?.timings?.take || 0
27+
: PlaylistTiming.getExpectedStart(playlist.timing) || 0
28+
const expectedDuration = PlaylistTiming.getExpectedDuration(playlist.timing) || 0
29+
30+
const expectedEnd = rehearsalInProgress
31+
? (firstTakenPartInstance?.timings?.take || 0) + expectedDuration
32+
: PlaylistTiming.getExpectedEnd(playlist.timing)
33+
34+
const now = timingDurations.currentTime ?? getCurrentTime()
35+
36+
const overUnderClock = getPlaylistTimingDiff(playlist, timingDurations) ?? 0
37+
38+
const { t } = useTranslation()
39+
40+
return (
41+
<div className="director-screen__top">
42+
{expectedEnd ? (
43+
<div className="director-screen__top__planned-end">
44+
<div>
45+
<PlannedEndComponent value={expectedEnd} />
46+
</div>
47+
{t('Planned End')}
48+
</div>
49+
) : null}
50+
{expectedEnd && expectedEnd > now ? (
51+
<div className="director-screen__top__time-to director-screen__top__planned-container">
52+
<div>
53+
<TimeToPlannedEndComponent value={now - expectedEnd} />
54+
</div>
55+
<span className="director-screen__top__planned-to">
56+
{rehearsalInProgress ? t('Time to rehearsal end') : t('Time to planned end')}
57+
</span>
58+
</div>
59+
) : (
60+
<div>
61+
<div className="director-screen__top__planned-container">
62+
<TimeSincePlannedEndComponent
63+
value={
64+
rehearsalInProgress
65+
? (firstTakenPartInstance?.timings?.take || 0) + expectedDuration - now
66+
: getCurrentTime() - (expectedStart + expectedDuration)
67+
}
68+
/>
69+
<span className="director-screen__top__planned-since">
70+
{rehearsalInProgress ? t('Time since rehearsal end') : t('Time since planned end')}
71+
</span>
72+
</div>
73+
</div>
74+
)}
75+
<div>
76+
<div>
77+
<OverUnderClockComponent value={overUnderClock} />
78+
</div>
79+
<span className="director-screen__top__over-under">{t('Over/Under')}</span>
80+
</div>
81+
</div>
82+
)
83+
}

0 commit comments

Comments
 (0)