Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions meteor/server/api/deviceTriggers/RundownsObserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { literal } from '@sofie-automation/corelib/dist/lib'
import { MongoFieldSpecifierOnesStrict } from '@sofie-automation/corelib/dist/mongo'
import { DBRundown } from '@sofie-automation/corelib/dist/dataModel/Rundown'
import { PromiseDebounce } from '../../publications/lib/PromiseDebounce'
import { stringifyError } from '@sofie-automation/shared-lib/dist/lib/stringifyError'
import { logger } from '../../logging'

const REACTIVITY_DEBOUNCE = 20

Expand All @@ -24,15 +26,19 @@ export class RundownsObserver {
#disposed = false

readonly #triggerUpdateRundownContent = new PromiseDebounce(async () => {
if (this.#disposed) return
try {
if (this.#disposed) return

if (!this.#changed) return
this.#cleanup?.()
if (!this.#changed) return
this.#cleanup?.()

const changed = this.#changed
this.#cleanup = await changed(this.rundownIds)
const changed = this.#changed
this.#cleanup = await changed(this.rundownIds)

if (this.#disposed) this.#cleanup?.()
if (this.#disposed) this.#cleanup?.()
} catch (e) {
logger.error(`Error in RundownsObserver triggerUpdateRundownContent: ${stringifyError(e)}`)
}
}, REACTIVITY_DEBOUNCE)

private constructor(onChanged: ChangedHandler) {
Expand Down
1 change: 1 addition & 0 deletions meteor/server/publications/lib/PromiseDebounce.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export class PromiseDebounce<TResult = void, TArgs extends unknown[] = []> {

/**
* Trigger an execution, but don't report the result.
* Warning: If the function throws an error, that will not be logged or reported to the caller
*/
trigger = (...args: TArgs): void => {
// If an execution is 'imminent', don't do anything
Expand Down
26 changes: 18 additions & 8 deletions meteor/server/publications/lib/rundownsObserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import { RundownId, RundownPlaylistId, StudioId } from '@sofie-automation/corelib/dist/dataModel/Ids'
import { Rundowns } from '../../collections'
import { PromiseDebounce } from './PromiseDebounce'
import { logger } from '../../logging'
import { stringifyError } from '@sofie-automation/shared-lib/dist/lib/stringifyError'

const REACTIVITY_DEBOUNCE = 20

Expand All @@ -20,14 +22,22 @@
#disposed = false

readonly #triggerUpdateRundownContent = new PromiseDebounce(async () => {
if (this.#disposed) return
if (!this.#changed) return
this.#cleanup?.()

const changed = this.#changed
this.#cleanup = await changed(this.rundownIds)

if (this.#disposed) this.#cleanup?.()
try {
if (this.#disposed) return
if (!this.#changed) return
this.#cleanup?.()
this.#cleanup = undefined

const changed = this.#changed
this.#cleanup = await changed(this.rundownIds)

if (this.#disposed) {
this.#cleanup?.()
this.#cleanup = undefined
}

Check warning on line 37 in meteor/server/publications/lib/rundownsObserver.ts

View check run for this annotation

Codecov / codecov/patch

meteor/server/publications/lib/rundownsObserver.ts#L35-L37

Added lines #L35 - L37 were not covered by tests
} catch (e) {
logger.error(`Error in RundownsObserver triggerUpdateRundownContent: ${stringifyError(e)}`)
}

Check warning on line 40 in meteor/server/publications/lib/rundownsObserver.ts

View check run for this annotation

Codecov / codecov/patch

meteor/server/publications/lib/rundownsObserver.ts#L39-L40

Added lines #L39 - L40 were not covered by tests
}, REACTIVITY_DEBOUNCE)

private constructor(onChanged: ChangedHandler) {
Expand Down
65 changes: 40 additions & 25 deletions packages/job-worker/src/playout/snapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,22 @@ import { DBRundownPlaylist } from '@sofie-automation/corelib/dist/dataModel/Rund
import { RundownOrphanedReason } from '@sofie-automation/corelib/dist/dataModel/Rundown'
import { SofieIngestDataCacheObj } from '@sofie-automation/corelib/dist/dataModel/SofieIngestDataCache'

class IdMapWithGenerator<V extends ProtectedString<any>> extends Map<V, V> {
getOrGenerate(key: V): V {
return this.getOrGenerateAndWarn(key, '')
}

getOrGenerateAndWarn(key: V, name: string): V {
const existing = this.get(key)
if (existing) return existing

const newValue = getRandomId<V>()
this.set(key, newValue)
if (name) logger.warn(`Couldn't find "${name}" when restoring snapshot`)
return newValue
}
}

/**
* Generate the Playlist owned portions of a Playlist snapshot
*/
Expand Down Expand Up @@ -244,7 +260,7 @@ export async function handleRestorePlaylistSnapshot(
rd._id = getRandomId()
rundownIdMap.set(oldId, rd._id)
}
const partIdMap = new Map<PartId, PartId>()
const partIdMap = new IdMapWithGenerator<PartId>()
for (const part of snapshot.parts) {
const oldId = part._id
part._id = part.externalId ? getPartId(getNewRundownId(part.rundownId), part.externalId) : getRandomId()
Expand All @@ -253,32 +269,34 @@ export async function handleRestorePlaylistSnapshot(
}

const partInstanceOldRundownIdMap = new Map<PartInstanceId, RundownId>()
const partInstanceIdMap = new Map<PartInstanceId, PartInstanceId>()
const partInstanceIdMap = new IdMapWithGenerator<PartInstanceId>()
for (const partInstance of snapshot.partInstances) {
const oldId = partInstance._id
partInstance._id = getRandomId()
partInstanceIdMap.set(oldId, partInstance._id)

partInstance.part._id = partIdMap.get(partInstance.part._id) || getRandomId()
partInstance.part._id = partIdMap.getOrGenerate(partInstance.part._id)
partInstanceOldRundownIdMap.set(oldId, partInstance.rundownId)
}
const segmentIdMap = new Map<SegmentId, SegmentId>()
const segmentIdMap = new IdMapWithGenerator<SegmentId>()
for (const segment of snapshot.segments) {
const oldId = segment._id
segment._id = getSegmentId(getNewRundownId(segment.rundownId), segment.externalId)
segmentIdMap.set(oldId, segment._id)
}
type AnyPieceId = PieceId | AdLibActionId | RundownBaselineAdLibActionId
const pieceIdMap = new Map<AnyPieceId, AnyPieceId>()
const pieceIdMap = new IdMapWithGenerator<AnyPieceId>()
for (const piece of snapshot.pieces) {
const oldId = piece._id
piece.startRundownId = getNewRundownId(piece.startRundownId)
piece.startPartId =
partIdMap.get(piece.startPartId) ||
getRandomIdAndWarn(`piece.startPartId=${piece.startPartId} of piece=${piece._id}`)
piece.startSegmentId =
segmentIdMap.get(piece.startSegmentId) ||
getRandomIdAndWarn(`piece.startSegmentId=${piece.startSegmentId} of piece=${piece._id}`)
piece.startPartId = partIdMap.getOrGenerateAndWarn(
piece.startPartId,
`piece.startPartId=${piece.startPartId} of piece=${piece._id}`
)
piece.startSegmentId = segmentIdMap.getOrGenerateAndWarn(
piece.startSegmentId,
`piece.startSegmentId=${piece.startSegmentId} of piece=${piece._id}`
)
piece._id = getRandomId()
pieceIdMap.set(oldId, piece._id)
}
Expand All @@ -297,10 +315,11 @@ export async function handleRestorePlaylistSnapshot(
for (const pieceInstance of snapshot.pieceInstances) {
pieceInstance._id = getRandomId()

pieceInstance.piece._id = (pieceIdMap.get(pieceInstance.piece._id) || getRandomId()) as PieceId // Note: don't warn if not found, as the piece may have been deleted
pieceInstance.piece._id = pieceIdMap.getOrGenerate(pieceInstance.piece._id) as PieceId // Note: don't warn if not found, as the piece may have been deleted
if (pieceInstance.infinite) {
pieceInstance.infinite.infinitePieceId =
(pieceIdMap.get(pieceInstance.infinite.infinitePieceId) as PieceId) || getRandomId() // Note: don't warn if not found, as the piece may have been deleted
pieceInstance.infinite.infinitePieceId = pieceIdMap.getOrGenerate(
pieceInstance.infinite.infinitePieceId
) as PieceId // Note: don't warn if not found, as the piece may have been deleted
}
}

Expand Down Expand Up @@ -330,9 +349,10 @@ export async function handleRestorePlaylistSnapshot(
case ExpectedPackageDBType.ADLIB_ACTION:
case ExpectedPackageDBType.BASELINE_ADLIB_PIECE:
case ExpectedPackageDBType.BASELINE_ADLIB_ACTION: {
expectedPackage.pieceId =
pieceIdMap.get(expectedPackage.pieceId) ||
getRandomIdAndWarn(`expectedPackage.pieceId=${expectedPackage.pieceId}`)
expectedPackage.pieceId = pieceIdMap.getOrGenerateAndWarn(
expectedPackage.pieceId,
`expectedPackage.pieceId=${expectedPackage.pieceId}`
)

expectedPackage._id = getExpectedPackageId(expectedPackage.pieceId, expectedPackage.blueprintPackageId)

Expand Down Expand Up @@ -383,13 +403,13 @@ export async function handleRestorePlaylistSnapshot(
}

if (obj.partId) {
obj.partId = partIdMap.get(obj.partId) || getRandomId()
obj.partId = partIdMap.getOrGenerate(obj.partId)
}
if (obj.segmentId) {
obj.segmentId = segmentIdMap.get(obj.segmentId) || getRandomId()
obj.segmentId = segmentIdMap.getOrGenerate(obj.segmentId)
}
if (obj.partInstanceId) {
obj.partInstanceId = partInstanceIdMap.get(obj.partInstanceId) || getRandomId()
obj.partInstanceId = partInstanceIdMap.getOrGenerate(obj.partInstanceId)
}

if (updateOwnId) {
Expand Down Expand Up @@ -516,11 +536,6 @@ export async function handleRestorePlaylistSnapshot(
}
}

function getRandomIdAndWarn<T extends ProtectedString<any>>(name: string): T {
logger.warn(`Couldn't find "${name}" when restoring snapshot`)
return getRandomId<T>()
}

function fixupImportedSelectedPartInstanceIds(
snapshot: CoreRundownPlaylistSnapshot,
rundownIdMap: Map<RundownId, RundownId>,
Expand Down
Loading