Skip to content

Commit a590920

Browse files
author
Mint de Wit
committed
feat: move next part should respect quickloop bounds
1 parent e885d66 commit a590920

File tree

4 files changed

+111
-19
lines changed

4 files changed

+111
-19
lines changed

packages/job-worker/src/playout/model/PlayoutModel.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,14 @@ export interface PlayoutModelReadonly extends StudioPlayoutModelBaseReadonly {
167167
*/
168168
getRundownIds(): RundownId[]
169169

170+
/**
171+
* Returns any segmentId's that are found between 2 quickloop markers, none will be returned if
172+
* the end is before the start.
173+
* @param start A quickloop marker
174+
* @param end A quickloop marker
175+
*/
176+
getSegmentsBetweenQuickLoopMarker(start: QuickLoopMarker, end: QuickLoopMarker): SegmentId[]
177+
170178
/**
171179
* Search for a PieceInstance in the RundownPlaylist
172180
* @param id Id of the PieceInstance
@@ -345,14 +353,6 @@ export interface PlayoutModel extends PlayoutModelReadonly, StudioPlayoutModelBa
345353
*/
346354
setQuickLoopMarker(type: 'start' | 'end', marker: QuickLoopMarker | null): void
347355

348-
/**
349-
* Returns any segmentId's that are found between 2 quickloop markers, none will be returned if
350-
* the end is before the start.
351-
* @param start A quickloop marker
352-
* @param end A quickloop marker
353-
*/
354-
getSegmentsBetweenQuickLoopMarker(start: QuickLoopMarker, end: QuickLoopMarker): SegmentId[]
355-
356356
calculatePartTimings(
357357
fromPartInstance: PlayoutPartInstanceModel | null,
358358
toPartInstance: PlayoutPartInstanceModel,

packages/job-worker/src/playout/model/implementation/PlayoutModelImpl.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,10 @@ export class PlayoutModelReadonlyImpl implements PlayoutModelReadonly {
240240
return undefined
241241
}
242242

243+
getSegmentsBetweenQuickLoopMarker(start: QuickLoopMarker, end: QuickLoopMarker): SegmentId[] {
244+
return this.quickLoopService.getSegmentsBetweenMarkers(start, end)
245+
}
246+
243247
#isMultiGatewayMode: boolean | undefined = undefined
244248
public get isMultiGatewayMode(): boolean {
245249
if (this.#isMultiGatewayMode === undefined) {
@@ -830,10 +834,6 @@ export class PlayoutModelImpl extends PlayoutModelReadonlyImpl implements Playou
830834
this.#playlistHasChanged = true
831835
}
832836

833-
getSegmentsBetweenQuickLoopMarker(start: QuickLoopMarker, end: QuickLoopMarker): SegmentId[] {
834-
return this.quickLoopService.getSegmentsBetweenMarkers(start, end)
835-
}
836-
837837
/** Notifications */
838838

839839
async getAllNotifications(

packages/job-worker/src/playout/model/services/QuickLoopService.ts

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
} from '@sofie-automation/corelib/dist/dataModel/RundownPlaylist'
99
import { ReadonlyObjectDeep } from 'type-fest/source/readonly-deep'
1010
import { DBPart } from '@sofie-automation/corelib/dist/dataModel/Part'
11-
import { RundownId, SegmentId } from '@sofie-automation/corelib/dist/dataModel/Ids'
11+
import { PartId, RundownId, SegmentId } from '@sofie-automation/corelib/dist/dataModel/Ids'
1212
import { DBSegment } from '@sofie-automation/corelib/dist/dataModel/Segment'
1313
import { PlayoutPartInstanceModel } from '../PlayoutPartInstanceModel'
1414
import { JobContext } from '../../../jobs'
@@ -150,6 +150,7 @@ export class QuickLoopService {
150150
}
151151

152152
getSegmentsBetweenMarkers(startMarker: QuickLoopMarker, endMarker: QuickLoopMarker): SegmentId[] {
153+
// note - this function could be refactored to call getPartsBetweenMarkers instead but it will be less efficient
153154
const segments = this.playoutModel.getAllOrderedSegments()
154155
const segmentIds: SegmentId[] = []
155156

@@ -201,6 +202,71 @@ export class QuickLoopService {
201202
return segmentIds
202203
}
203204

205+
getPartsBetweenMarkers(
206+
startMarker: QuickLoopMarker,
207+
endMarker: QuickLoopMarker
208+
): { parts: PartId[]; segments: SegmentId[] } {
209+
const parts = this.playoutModel.getAllOrderedParts()
210+
const segmentIds: SegmentId[] = []
211+
const partIds: PartId[] = []
212+
213+
let passedStart = false
214+
let seenLastRundown = false
215+
let seenLastSegment = false
216+
217+
for (const p of parts) {
218+
if (
219+
!passedStart &&
220+
((startMarker.type === QuickLoopMarkerType.PART && p._id === startMarker.id) ||
221+
(startMarker.type === QuickLoopMarkerType.SEGMENT && p.segmentId === startMarker.id) ||
222+
(startMarker.type === QuickLoopMarkerType.RUNDOWN && p.rundownId === startMarker.id) ||
223+
startMarker.type === QuickLoopMarkerType.PLAYLIST)
224+
) {
225+
// the start marker is this part, this is the first part in the loop, or this is the first segment that is in the loop
226+
// segments from here on are included in the loop
227+
passedStart = true
228+
}
229+
230+
if (endMarker.type === QuickLoopMarkerType.RUNDOWN) {
231+
// last rundown needs to be inclusive so we need to break once the rundownId is not equal to segment's rundownId
232+
if (p.rundownId === endMarker.id) {
233+
if (!passedStart) {
234+
// we hit the end before the start so quit now:
235+
break
236+
}
237+
seenLastRundown = true
238+
} else if (seenLastRundown) {
239+
// we have passed the last rundown
240+
break
241+
}
242+
} else if (endMarker.type === QuickLoopMarkerType.SEGMENT) {
243+
// last segment needs to be inclusive so we need to break once the segmentId changes but not before
244+
if (p.segmentId === endMarker.id) {
245+
if (!passedStart) {
246+
// we hit the end before the start so quit now:
247+
break
248+
}
249+
seenLastSegment = true
250+
} else if (seenLastSegment) {
251+
// we have passed the last rundown
252+
break
253+
}
254+
}
255+
256+
if (passedStart) {
257+
if (segmentIds.slice(-1)[0] !== p.segmentId) segmentIds.push(p.segmentId)
258+
partIds.push(p._id)
259+
}
260+
261+
if (endMarker.type === QuickLoopMarkerType.PART && p._id === endMarker.id) {
262+
// the endMarker is this part so we can quit now
263+
break
264+
}
265+
}
266+
267+
return { parts: partIds, segments: segmentIds }
268+
}
269+
204270
private areMarkersFlipped(startPosition: MarkerPosition, endPosition: MarkerPosition) {
205271
return compareMarkerPositions(startPosition, endPosition) < 0
206272
}

packages/job-worker/src/playout/moveNextPart.ts

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ import { sortPartsInSortedSegments } from '@sofie-automation/corelib/dist/playou
66
import { logger } from '../logging'
77
import { SegmentOrphanedReason } from '@sofie-automation/corelib/dist/dataModel/Segment'
88
import { ReadonlyDeep } from 'type-fest'
9+
import { QuickLoopService } from './model/services/QuickLoopService'
910

1011
export function selectNewPartWithOffsets(
1112
_context: JobContext,
1213
playoutModel: PlayoutModelReadonly,
1314
partDelta: number,
14-
segmentDelta: number
15+
segmentDelta: number,
16+
ignoreQuickLoop = false
1517
): ReadonlyDeep<DBPart> | null {
1618
const playlist = playoutModel.playlist
1719

@@ -23,8 +25,22 @@ export function selectNewPartWithOffsets(
2325
if (!refPart || !refPartInstance)
2426
throw new Error(`RundownPlaylist "${playlist._id}" has no next and no current part!`)
2527

26-
const rawSegments = playoutModel.getAllOrderedSegments()
27-
const rawParts = playoutModel.getAllOrderedParts()
28+
let rawSegments = playoutModel.getAllOrderedSegments()
29+
let rawParts = playoutModel.getAllOrderedParts()
30+
let allowWrap = false // whether we should wrap to the first part if the curIndex + delta exceeds the total number of parts
31+
32+
const quickLoopService = new QuickLoopService(_context, playoutModel)
33+
if (!ignoreQuickLoop && playlist.quickLoop?.start && playlist.quickLoop.end) {
34+
const partsInQuickloop = quickLoopService.getPartsBetweenMarkers(
35+
playlist.quickLoop.start,
36+
playlist.quickLoop.end
37+
)
38+
if (partsInQuickloop.parts.includes(refPart._id)) {
39+
rawParts = rawParts.filter((p) => partsInQuickloop.parts.includes(p._id))
40+
rawSegments = rawSegments.filter((s) => partsInQuickloop.segments.includes(s.segment._id))
41+
allowWrap = true
42+
}
43+
}
2844

2945
if (segmentDelta) {
3046
// Ignores horizontalDelta
@@ -37,7 +53,14 @@ export function selectNewPartWithOffsets(
3753
const refSegmentIndex = considerSegments.findIndex((s) => s.segment._id === refPart.segmentId)
3854
if (refSegmentIndex === -1) throw new Error(`Segment "${refPart.segmentId}" not found!`)
3955

40-
const targetSegmentIndex = refSegmentIndex + segmentDelta
56+
let targetSegmentIndex = refSegmentIndex + segmentDelta
57+
if (allowWrap) {
58+
targetSegmentIndex = targetSegmentIndex % considerSegments.length
59+
if (targetSegmentIndex < 0) {
60+
// -1 becomes last segment
61+
targetSegmentIndex = considerSegments.length + targetSegmentIndex
62+
}
63+
}
4164
const targetSegment = considerSegments[targetSegmentIndex]
4265
if (!targetSegment) return null
4366

@@ -64,7 +87,6 @@ export function selectNewPartWithOffsets(
6487
}
6588
}
6689

67-
// TODO - looping playlists
6890
if (selectedPart) {
6991
// Switch to that part
7092
return selectedPart
@@ -88,7 +110,11 @@ export function selectNewPartWithOffsets(
88110
}
89111

90112
// Get the past we are after
91-
const targetPartIndex = refPartIndex + partDelta
113+
let targetPartIndex = allowWrap ? (refPartIndex + partDelta) % playabaleParts.length : refPartIndex + partDelta
114+
if (allowWrap) {
115+
targetPartIndex = targetPartIndex % playabaleParts.length
116+
if (targetPartIndex < 0) targetPartIndex = playabaleParts.length + targetPartIndex // -1 becomes last part
117+
}
92118
let targetPart = playabaleParts[targetPartIndex]
93119
if (targetPart && targetPart._id === currentPartInstance?.part._id) {
94120
// Cant go to the current part (yet)

0 commit comments

Comments
 (0)