forked from Sofie-Automation/sofie-core
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmoveNextPart.ts
More file actions
135 lines (122 loc) · 5.15 KB
/
moveNextPart.ts
File metadata and controls
135 lines (122 loc) · 5.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import { groupByToMap } from '@sofie-automation/corelib/dist/lib'
import { DBPart, isPartPlayable } from '@sofie-automation/corelib/dist/dataModel/Part'
import { JobContext } from '../jobs'
import { PlayoutModelReadonly } from './model/PlayoutModel'
import { sortPartsInSortedSegments } from '@sofie-automation/corelib/dist/playout/playlist'
import { logger } from '../logging'
import { SegmentOrphanedReason } from '@sofie-automation/corelib/dist/dataModel/Segment'
import { ReadonlyDeep } from 'type-fest'
export function selectNewPartWithOffsets(
_context: JobContext,
playoutModel: PlayoutModelReadonly,
partDelta: number,
segmentDelta: number,
ignoreQuickLoop = false
): ReadonlyDeep<DBPart> | null {
const playlist = playoutModel.playlist
const currentPartInstance = playoutModel.currentPartInstance?.partInstance
const nextPartInstance = playoutModel.nextPartInstance?.partInstance
const refPartInstance = nextPartInstance ?? currentPartInstance
const refPart = refPartInstance?.part
if (!refPart || !refPartInstance)
throw new Error(`RundownPlaylist "${playlist._id}" has no next and no current part!`)
let rawSegments = playoutModel.getAllOrderedSegments()
let rawParts = playoutModel.getAllOrderedParts()
let allowWrap = false // whether we should wrap to the first part if the curIndex + delta exceeds the total number of parts
if (!ignoreQuickLoop && playlist.quickLoop?.start && playlist.quickLoop.end) {
const partsInQuickloop = playoutModel.getPartsBetweenQuickLoopMarker(
playlist.quickLoop.start,
playlist.quickLoop.end
)
if (partsInQuickloop.parts.includes(refPart._id)) {
rawParts = rawParts.filter((p) => partsInQuickloop.parts.includes(p._id))
rawSegments = rawSegments.filter((s) => partsInQuickloop.segments.includes(s.segment._id))
allowWrap = true
}
}
if (segmentDelta) {
// Ignores horizontalDelta
const considerSegments = rawSegments.filter(
(s) =>
s.segment._id === refPart.segmentId ||
!s.segment.isHidden ||
s.segment.orphaned === SegmentOrphanedReason.ADLIB_TESTING
)
const refSegmentIndex = considerSegments.findIndex((s) => s.segment._id === refPart.segmentId)
if (refSegmentIndex === -1) throw new Error(`Segment "${refPart.segmentId}" not found!`)
let targetSegmentIndex = refSegmentIndex + segmentDelta
if (allowWrap) {
targetSegmentIndex = targetSegmentIndex % considerSegments.length
if (targetSegmentIndex < 0) {
// -1 becomes last segment
targetSegmentIndex = considerSegments.length + targetSegmentIndex
}
}
const targetSegment = considerSegments[targetSegmentIndex]
if (!targetSegment) return null
// find the allowable segment ids
const allowedSegments =
segmentDelta > 0
? considerSegments.slice(targetSegmentIndex)
: considerSegments.slice(0, targetSegmentIndex + 1).reverse()
const playablePartsBySegment = groupByToMap(
rawParts.filter((p) => isPartPlayable(p)),
'segmentId'
)
// Iterate through segments and find the first part
let selectedPart: ReadonlyDeep<DBPart> | undefined
for (const segment of allowedSegments) {
const parts = playablePartsBySegment.get(segment.segment._id) ?? []
// Cant go to the current part (yet)
const filteredParts = parts.filter((p) => p._id !== currentPartInstance?.part._id)
if (filteredParts.length > 0) {
selectedPart = filteredParts[0]
break
}
}
if (selectedPart) {
// Switch to that part
return selectedPart
} else {
// Nothing looked valid so do nothing
// Note: we should try and a smaller delta if it is not -1/1
logger.info(`moveNextPart: Found no new part (verticalDelta=${segmentDelta})`)
return null
}
} else if (partDelta) {
let playabaleParts: ReadonlyDeep<DBPart>[] = rawParts.filter((p) => refPart._id === p._id || isPartPlayable(p))
let refPartIndex = playabaleParts.findIndex((p) => p._id === refPart._id)
if (refPartIndex === -1) {
const tmpRefPart = { ...refPart, invalid: true } // make sure it won't be found as playable
playabaleParts = sortPartsInSortedSegments(
[...playabaleParts, tmpRefPart],
rawSegments.map((s) => s.segment)
)
refPartIndex = playabaleParts.findIndex((p) => p._id === refPart._id)
if (refPartIndex === -1) throw new Error(`Part "${refPart._id}" not found after insert!`)
}
// Get the past we are after
let targetPartIndex = allowWrap ? (refPartIndex + partDelta) % playabaleParts.length : refPartIndex + partDelta
if (allowWrap) {
targetPartIndex = targetPartIndex % playabaleParts.length
if (targetPartIndex < 0) targetPartIndex = playabaleParts.length + targetPartIndex // -1 becomes last part
}
let targetPart = playabaleParts[targetPartIndex]
if (targetPart && targetPart._id === currentPartInstance?.part._id) {
// Cant go to the current part (yet)
const newIndex = targetPartIndex + (partDelta > 0 ? 1 : -1)
targetPart = playabaleParts[newIndex]
}
if (targetPart) {
// Switch to that part
return targetPart
} else {
// Nothing looked valid so do nothing
// Note: we should try and a smaller delta if it is not -1/1
logger.info(`moveNextPart: Found no new part (horizontalDelta=${partDelta})`)
return null
}
} else {
throw new Error(`Missing delta to move by!`)
}
}