Skip to content

Commit 40b4cb9

Browse files
committed
Merge branch 'release51' into release52
2 parents cb59dd9 + ad370c1 commit 40b4cb9

File tree

15 files changed

+190
-64
lines changed

15 files changed

+190
-64
lines changed

meteor/CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,22 @@
22

33
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
44

5+
### [1.51.6](///compare/v1.51.5...v1.51.6) (2025-01-14)
6+
7+
8+
### Features
9+
10+
* add more logging 8c16ce8
11+
12+
13+
### Bug Fixes
14+
15+
* Include previousPartInstance in check to orphan segments rather than remove them. 51b7104
16+
* only run onPart/PiecePlaybackStarted/Stopped on current, next or previous parts a9fe401
17+
* **PoGw:** filter log output to ensure that message field in JSONL output is never an object 0d2b844
18+
* set nextPartInstance to null if it's referring to a Segment that has been removed b1045f9
19+
* updatePartInstancesSegmentIds: take into account when multiple segments have been merged into one. b769157
20+
521
### [1.51.5](///compare/v1.51.4...v1.51.5) (2025-01-07)
622

723

packages/blueprints-integration/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@
33
All notable changes to this project will be documented in this file.
44
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
55

6+
## [1.51.6](https://github.com/nrkno/sofie-core/compare/v1.51.5...v1.51.6) (2025-01-14)
7+
8+
**Note:** Version bump only for package @sofie-automation/blueprints-integration
9+
10+
11+
12+
13+
614
## [1.51.5](https://github.com/nrkno/sofie-core/compare/v1.51.4...v1.51.5) (2025-01-07)
715

816
**Note:** Version bump only for package @sofie-automation/blueprints-integration

packages/job-worker/src/ingest/commit.ts

Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,13 @@ export async function CommitIngestOperation(
180180
// Ensure any adlibbed parts are updated to follow the segmentId of the previous part
181181
await updateSegmentIdsForAdlibbedPartInstances(context, ingestModel, beforePartMap)
182182

183+
// TODO: This whole section can probably be removed later, it's really unneccessary in the grand scheme of
184+
// things, it's here only to debug some problems
183185
if (data.renamedSegments && data.renamedSegments.size > 0) {
184-
logger.debug(`Renamed segments: ${JSON.stringify(Array.from(data.renamedSegments.entries()))}`)
186+
logger.verbose(`Renamed segments: ${JSON.stringify(Array.from(data.renamedSegments.entries()))}`)
185187
}
188+
// End of temporary section
189+
186190
// ensure instances have matching segmentIds with the parts
187191
await updatePartInstancesSegmentIds(context, ingestModel, data.renamedSegments, beforePartMap)
188192

@@ -283,12 +287,9 @@ function canRemoveSegment(
283287
logger.warn(`Not allowing removal of current playing segment "${segmentId}", making segment unsynced instead`)
284288
return false
285289
}
286-
287-
if (nextPartInstance?.segmentId === segmentId && nextPartInstance.orphaned === 'adlib-part') {
290+
if (nextPartInstance?.segmentId === segmentId) {
288291
// Don't allow removing an active rundown
289-
logger.warn(
290-
`Not allowing removal of segment "${segmentId}" which contains nexted adlibbed part, making segment unsynced instead`
291-
)
292+
logger.warn(`Not allowing removal of nexted segment "${segmentId}", making segment unsynced instead`)
292293
return false
293294
}
294295

@@ -367,6 +368,8 @@ async function updatePartInstancesSegmentIds(
367368

368369
const writeOps: AnyBulkWriteOperation<DBPartInstance>[] = []
369370

371+
logger.debug(`updatePartInstancesSegmentIds: renameRules: ${JSON.stringify(renameRules)}`)
372+
370373
for (const [newSegmentId, rule] of rulesInOrder) {
371374
if (rule.fromSegmentIds.length) {
372375
writeOps.push({
@@ -404,32 +407,24 @@ async function updatePartInstancesSegmentIds(
404407
if (writeOps.length) await context.directCollections.PartInstances.bulkWrite(writeOps)
405408

406409
// Double check that there are no parts using the old segment ids:
407-
const oldSegmentIds = Array.from(renameRules.keys())
408-
const [badPartInstances, badParts] = await Promise.all([
409-
await context.directCollections.PartInstances.findFetch({
410-
rundownId: ingestModel.rundownId,
411-
segmentId: { $in: oldSegmentIds },
412-
}),
413-
await context.directCollections.Parts.findFetch({
414-
rundownId: ingestModel.rundownId,
415-
segmentId: { $in: oldSegmentIds },
416-
}),
417-
])
410+
// TODO: This whole section can probably be removed later, it's really unneccessary in the grand scheme of
411+
// things, it's here only to debug some problems
412+
const oldSegmentIds: SegmentId[] = []
413+
for (const renameRule of renameRules.values()) {
414+
oldSegmentIds.push(...renameRule.fromSegmentIds)
415+
}
416+
const badPartInstances = await context.directCollections.PartInstances.findFetch({
417+
rundownId: ingestModel.rundownId,
418+
segmentId: { $in: oldSegmentIds },
419+
})
418420
if (badPartInstances.length > 0) {
419421
logger.error(
420422
`updatePartInstancesSegmentIds: Failed to update all PartInstances using old SegmentIds "${JSON.stringify(
421423
oldSegmentIds
422424
)}": ${JSON.stringify(badPartInstances)}, writeOps: ${JSON.stringify(writeOps)}`
423425
)
424426
}
425-
426-
if (badParts.length > 0) {
427-
logger.error(
428-
`updatePartInstancesSegmentIds: Failed to update all Parts using old SegmentIds "${JSON.stringify(
429-
oldSegmentIds
430-
)}": ${JSON.stringify(badParts)}, writeOps: ${JSON.stringify(writeOps)}`
431-
)
432-
}
427+
// End of the temporary section
433428
}
434429
}
435430

@@ -664,10 +659,27 @@ async function getSelectedPartInstances(
664659
})
665660
: []
666661

662+
const currentPartInstance = instances.find((inst) => inst._id === playlist.currentPartInfo?.partInstanceId)
663+
const nextPartInstance = instances.find((inst) => inst._id === playlist.nextPartInfo?.partInstanceId)
664+
const previousPartInstance = instances.find((inst) => inst._id === playlist.previousPartInfo?.partInstanceId)
665+
666+
if (playlist.currentPartInfo?.partInstanceId && !currentPartInstance)
667+
logger.error(
668+
`playlist.currentPartInfo is set, but PartInstance "${playlist.currentPartInfo?.partInstanceId}" was not found!`
669+
)
670+
if (playlist.nextPartInfo?.partInstanceId && !nextPartInstance)
671+
logger.error(
672+
`playlist.nextPartInfo is set, but PartInstance "${playlist.nextPartInfo?.partInstanceId}" was not found!`
673+
)
674+
if (playlist.previousPartInfo?.partInstanceId && !previousPartInstance)
675+
logger.error(
676+
`playlist.previousPartInfo is set, but PartInstance "${playlist.previousPartInfo?.partInstanceId}" was not found!`
677+
)
678+
667679
return {
668-
currentPartInstance: instances.find((inst) => inst._id === playlist.currentPartInfo?.partInstanceId),
669-
nextPartInstance: instances.find((inst) => inst._id === playlist.nextPartInfo?.partInstanceId),
670-
previousPartInstance: instances.find((inst) => inst._id === playlist.previousPartInfo?.partInstanceId),
680+
currentPartInstance,
681+
nextPartInstance,
682+
previousPartInstance,
671683
}
672684
}
673685

@@ -831,6 +843,16 @@ async function removeSegments(
831843
})
832844
}
833845
for (const segmentId of purgeSegmentIds) {
846+
logger.debug(
847+
`IngestModel: Removing segment "${segmentId}" (` +
848+
`previousPartInfo?.partInstanceId: ${newPlaylist.previousPartInfo?.partInstanceId},` +
849+
`currentPartInfo?.partInstanceId: ${newPlaylist.currentPartInfo?.partInstanceId},` +
850+
`nextPartInfo?.partInstanceId: ${newPlaylist.nextPartInfo?.partInstanceId},` +
851+
`previousPartInstance.segmentId: ${!previousPartInstance ? 'N/A' : previousPartInstance.segmentId},` +
852+
`currentPartInstance.segmentId: ${!currentPartInstance ? 'N/A' : currentPartInstance.segmentId},` +
853+
`nextPartInstance.segmentId: ${!nextPartInstance ? 'N/A' : nextPartInstance.segmentId}` +
854+
`)`
855+
)
834856
ingestModel.removeSegment(segmentId)
835857
}
836858
}

packages/job-worker/src/ingest/lock.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@ export interface CommitIngestData {
1414
removedSegmentIds: SegmentId[]
1515
/**
1616
* Segments that had their ids changed. This helps then be orphaned in the correct place
17-
* eg, whole segment is renamed and middle part deleted
18-
* Note: Only supported for MOS, not 'normal' ingest operations
17+
* eg, whole segment is renamed and middle part deleted.
18+
*
19+
* Maps fromSegmentId to toSegmentId.
20+
*
21+
* _Note: Only supported for MOS, not 'normal' ingest operations_
1922
*/
2023
renamedSegments: Map<SegmentId, SegmentId> | null
2124

packages/job-worker/src/ingest/syncChangesToPartInstance.ts

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { ReadonlyDeep } from 'type-fest'
3131
import { convertIngestModelToPlayoutRundownWithSegments } from './commit'
3232
import { convertNoteToNotification } from '../notifications/util'
3333
import { PlayoutRundownModel } from '../playout/model/PlayoutRundownModel'
34+
import { PieceInstance } from '@sofie-automation/corelib/dist/dataModel/PieceInstance'
3435

3536
type PlayStatus = 'previous' | 'current' | 'next'
3637
type SyncedInstance = {
@@ -141,15 +142,29 @@ export async function syncChangesToPartInstances(
141142
if (!playoutRundownModelForPart)
142143
throw new Error(`Internal Error: playoutRundownModelForPart is undefined (it should never be)`)
143144

144-
const proposedPieceInstances = getPieceInstancesForPart(
145-
context,
146-
playoutModel,
147-
previousPartInstance,
148-
playoutRundownModelForPart,
149-
part,
150-
await piecesThatMayBeActive,
151-
existingPartInstance.partInstance._id
152-
)
145+
// TMP: wrap in try/catch for troubleshooting:
146+
let proposedPieceInstances: PieceInstance[] = []
147+
try {
148+
proposedPieceInstances = getPieceInstancesForPart(
149+
context,
150+
playoutModel,
151+
previousPartInstance,
152+
playoutRundownModelForPart,
153+
part,
154+
await piecesThatMayBeActive,
155+
existingPartInstance.partInstance._id
156+
)
157+
} catch (e) {
158+
logger.error(
159+
`TROUBLESHOOTING: currentPartInstance: ${JSON.stringify(playoutModel.currentPartInstance)}`
160+
)
161+
logger.error(`TROUBLESHOOTING: nextPartInstance: ${JSON.stringify(playoutModel.nextPartInstance)}`)
162+
logger.error(
163+
`TROUBLESHOOTING: previousPartInstance: ${JSON.stringify(playoutModel.previousPartInstance)}`
164+
)
165+
166+
throw e
167+
}
153168

154169
logger.info(`Syncing ingest changes for part: ${partId} (orphaned: ${!!newPart})`)
155170

packages/job-worker/src/ingest/updateNext.ts

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,29 @@ export async function ensureNextPartIsValid(context: JobContext, playoutModel: P
4444
const orderedSegments = playoutModel.getAllOrderedSegments()
4545
const orderedParts = playoutModel.getAllOrderedParts()
4646

47-
if (currentPartInstance && nextPartInstance) {
47+
if (!nextPartInstance || nextPartInstance.partInstance.orphaned === 'deleted') {
48+
// Don't have a nextPart or it has been deleted, so autoselect something
49+
const newNextPart = selectNextPart(
50+
context,
51+
playlist,
52+
currentPartInstance?.partInstance ?? null,
53+
nextPartInstance?.partInstance ?? null,
54+
orderedSegments,
55+
orderedParts,
56+
{ ignoreUnplayable: true, ignoreQuickLoop: false }
57+
)
58+
59+
if (!newNextPart && !playoutModel.playlist.nextPartInfo) {
60+
// No currently nexted part, and nothing was selected, so nothing to update
61+
span?.end()
62+
return false
63+
}
64+
65+
await setNextPart(context, playoutModel, newNextPart ?? null, false)
66+
67+
span?.end()
68+
return true
69+
} else if (currentPartInstance && nextPartInstance) {
4870
// Check if the part is the same
4971
const newNextPart = selectNextPart(
5072
context,
@@ -70,28 +92,6 @@ export async function ensureNextPartIsValid(context: JobContext, playoutModel: P
7092
span?.end()
7193
return true
7294
}
73-
} else if (!nextPartInstance || nextPartInstance.partInstance.orphaned === 'deleted') {
74-
// Don't have a nextPart or it has been deleted, so autoselect something
75-
const newNextPart = selectNextPart(
76-
context,
77-
playlist,
78-
currentPartInstance?.partInstance ?? null,
79-
nextPartInstance?.partInstance ?? null,
80-
orderedSegments,
81-
orderedParts,
82-
{ ignoreUnplayable: true, ignoreQuickLoop: false }
83-
)
84-
85-
if (!newNextPart && !playoutModel.playlist.nextPartInfo) {
86-
// No currently nexted part, and nothing was selected, so nothing to update
87-
span?.end()
88-
return false
89-
}
90-
91-
await setNextPart(context, playoutModel, newNextPart ?? null, false)
92-
93-
span?.end()
94-
return true
9595
}
9696

9797
span?.end()

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,7 @@ export async function handleDisableNextPiece(context: JobContext, data: DisableN
446446

447447
return sortedPieces.find((piece) => {
448448
return (
449+
piece.pieceInstance.piece.enable.start !== 'now' &&
449450
piece.pieceInstance.piece.enable.start >= nowInPart &&
450451
((!data.undo && !piece.pieceInstance.disabled) || (data.undo && piece.pieceInstance.disabled))
451452
)

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,9 @@ export function getPieceInstancesForPart(
332332

333333
playingSegment = playingRundown.getSegment(playingPartInstance.partInstance.segmentId)
334334
if (!playingSegment) {
335+
// Double check that there are no parts using the old segment ids:
336+
// TODO: This whole section can probably be removed later, it's really unneccessary in the grand scheme of
337+
// things, it's here only to debug some problems
335338
const rundownId = playingRundown.rundown._id
336339
context.directCollections.Segments.findFetch({
337340
rundownId: rundownId,
@@ -344,11 +347,14 @@ export function getPieceInstancesForPart(
344347
)
345348
})
346349
.catch((e) => logger.error(e))
350+
// End of temporary section
347351

348352
throw new Error(
349353
`Segment "${playingPartInstance.partInstance.segmentId}" in Rundown "${
350354
playingRundown.rundown._id
351-
}" not found! (other segments: ${JSON.stringify(playingRundown.segments.map((s) => s.segment._id))})`
355+
}" not found! (partInstanceId: "${
356+
playingPartInstance.partInstance._id
357+
}", other segments: ${JSON.stringify(playingRundown.segments.map((s) => s.segment._id))})`
352358
)
353359
}
354360
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,12 @@ export class PlayoutModelImpl extends PlayoutModelReadonlyImpl implements Playou
683683
this.context.saveRouteSetChanges(),
684684
])
685685

686+
// Clean up deleted partInstances, since they have now been deleted by writePartInstancesAndPieceInstances
687+
for (const [partInstanceId, partInstance] of this.allPartInstances) {
688+
if (partInstance !== null) continue
689+
this.allPartInstances.delete(partInstanceId)
690+
}
691+
686692
this.#playlistHasChanged = false
687693

688694
// Execute deferAfterSave()'s

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,9 +364,11 @@ async function cleanupOrphanedItems(context: JobContext, playoutModel: PlayoutMo
364364

365365
const selectedPartInstancesSegmentIds = new Set<SegmentId>()
366366

367+
const previousPartInstance = playoutModel.previousPartInstance?.partInstance
367368
const currentPartInstance = playoutModel.currentPartInstance?.partInstance
368369
const nextPartInstance = playoutModel.nextPartInstance?.partInstance
369370

371+
if (previousPartInstance) selectedPartInstancesSegmentIds.add(previousPartInstance.segmentId)
370372
if (currentPartInstance) selectedPartInstancesSegmentIds.add(currentPartInstance.segmentId)
371373
if (nextPartInstance) selectedPartInstancesSegmentIds.add(nextPartInstance.segmentId)
372374

@@ -376,7 +378,7 @@ async function cleanupOrphanedItems(context: JobContext, playoutModel: PlayoutMo
376378

377379
const alterSegmentsFromRundowns = new Map<RundownId, { deleted: SegmentId[]; hidden: SegmentId[] }>()
378380
for (const segment of segments) {
379-
// If the segment is orphaned and not the segment for the next or current partinstance
381+
// If the segment is orphaned and not the segment for the previous, current or next partInstance
380382
if (!selectedPartInstancesSegmentIds.has(segment.segment._id)) {
381383
let rundownSegments = alterSegmentsFromRundowns.get(segment.segment.rundownId)
382384
if (!rundownSegments) {

0 commit comments

Comments
 (0)