diff --git a/packages/job-worker/src/blueprints/__tests__/context-adlibActions.test.ts b/packages/job-worker/src/blueprints/__tests__/context-adlibActions.test.ts index ce3a3cef2e1..ae952edbb41 100644 --- a/packages/job-worker/src/blueprints/__tests__/context-adlibActions.test.ts +++ b/packages/job-worker/src/blueprints/__tests__/context-adlibActions.test.ts @@ -264,7 +264,7 @@ describe('Test blueprint api context', () => { partInstanceId: info.PartInstance._id, rundownId: info.PartInstance.rundownId, manuallySelected: false, - consumesNextSegmentId: false, + consumesQueuedSegmentId: false, } } else { return { @@ -1242,7 +1242,7 @@ describe('Test blueprint api context', () => { // Ensure there are no pending updates already for (const partInstance of cache.LoadedPartInstances) { - expect((partInstance as PlayoutPartInstanceModelImpl).HasChanges).toBeFalsy() + expect((partInstance as PlayoutPartInstanceModelImpl).HasAnyChanges()).toBeFalsy() } // Update it and expect it to match @@ -1289,15 +1289,10 @@ describe('Test blueprint api context', () => { }, } expect(pieceInstance1).toEqual(pieceInstance0After) - expect((partInstance1 as PlayoutPartInstanceModelImpl).HasChanges).toBeTruthy() - // expect( - // Array.from(cache.PieceInstances.documents.values()).filter((doc) => !doc || !!doc.updated) - // ).toMatchObject([ - // { - // updated: true, - // document: { _id: pieceInstance1._id }, - // }, - // ]) + expect((partInstance1 as PlayoutPartInstanceModelImpl).PartInstanceHasChanges).toBeFalsy() + expect((partInstance1 as PlayoutPartInstanceModelImpl).ChangedPieceInstanceIds()).toEqual([ + pieceInstance1._id, + ]) expect(context.nextPartState).toEqual(ActionPartChange.NONE) expect(context.currentPartState).toEqual(ActionPartChange.SAFE_CHANGE) @@ -1720,7 +1715,7 @@ describe('Test blueprint api context', () => { const { context } = await getActionExecutionContext(jobContext, cache) // Ensure there are no pending updates already - expect((cache.NextPartInstance! as PlayoutPartInstanceModelImpl).HasChanges).toBeFalsy() + expect((cache.NextPartInstance! as PlayoutPartInstanceModelImpl).HasAnyChanges()).toBeFalsy() // Update it and expect it to match const partInstance0Before = clone(partInstance0) @@ -1731,11 +1726,11 @@ describe('Test blueprint api context', () => { classes: ['123'], badProperty: 9, // This will be dropped } - const resultPiece = await context.updatePartInstance('next', partInstance0Delta) - const partInstance1 = cache.NextPartInstance! + const resultPart = await context.updatePartInstance('next', partInstance0Delta) + const partInstance1 = cache.NextPartInstance! as PlayoutPartInstanceModelImpl expect(partInstance1).toBeTruthy() - expect(resultPiece).toEqual(convertPartInstanceToBlueprints(partInstance1.PartInstance)) + expect(resultPart).toEqual(convertPartInstanceToBlueprints(partInstance1.PartInstance)) const pieceInstance0After = { ...partInstance0Before, @@ -1745,15 +1740,8 @@ describe('Test blueprint api context', () => { }, } expect(partInstance1.PartInstance).toEqual(pieceInstance0After) - expect((partInstance1 as PlayoutPartInstanceModelImpl).HasChanges).toBeTruthy() - // expect( - // Array.from(cache.PartInstances.documents.values()).filter((doc) => !doc || !!doc.updated) - // ).toMatchObject([ - // { - // updated: true, - // document: { _id: partInstance1._id }, - // }, - // ]) + expect(partInstance1.PartInstanceHasChanges).toBeTruthy() + expect(partInstance1.ChangedPieceInstanceIds()).toHaveLength(0) expect(context.nextPartState).toEqual(ActionPartChange.SAFE_CHANGE) expect(context.currentPartState).toEqual(ActionPartChange.NONE) diff --git a/packages/job-worker/src/cache/__tests__/DatabaseCaches.test.ts b/packages/job-worker/src/cache/__tests__/DatabaseCaches.test.ts index 9f558584bd3..bcaa17afd6a 100644 --- a/packages/job-worker/src/cache/__tests__/DatabaseCaches.test.ts +++ b/packages/job-worker/src/cache/__tests__/DatabaseCaches.test.ts @@ -292,18 +292,18 @@ describe('DatabaseCaches', () => { }).toThrow(/failed .+ assertion,.+ was modified/gi) } - { - const cache = await loadStudioPlayoutModel(context) - - // Insert a document: - cache.deferBeforeSave(() => { - // - }) - - expect(() => { - cache.assertNoChanges() - }).toThrow(/failed .+ assertion,.+ deferred/gi) - } + // { + // const cache = await loadStudioPlayoutModel(context) + + // // Insert a document: + // cache.deferBeforeSave(() => { + // // + // }) + + // expect(() => { + // cache.assertNoChanges() + // }).toThrow(/failed .+ assertion,.+ deferred/gi) + // } { const cache = await loadStudioPlayoutModel(context) diff --git a/packages/job-worker/src/playout/__tests__/__snapshots__/playout.test.ts.snap b/packages/job-worker/src/playout/__tests__/__snapshots__/playout.test.ts.snap index 48a2edb15c5..0ca490d670b 100644 --- a/packages/job-worker/src/playout/__tests__/__snapshots__/playout.test.ts.snap +++ b/packages/job-worker/src/playout/__tests__/__snapshots__/playout.test.ts.snap @@ -11,8 +11,8 @@ exports[`Playout API Basic rundown control 1`] = ` "core": "0.0.0-test", "studio": "asdf", }, - "timelineBlob": "[{"id":"playlist_randomId9000_status","objectType":"rundown","enable":{"while":1},"layer":"rundown_status","content":{"deviceType":"ABSTRACT"},"classes":["rundown_active"],"priority":0},{"id":"part_group_randomId9000_part0_0_randomId9002","objectType":"rundown","enable":{"start":"now"},"priority":5,"layer":"","content":{"deviceType":"ABSTRACT","type":"group"},"children":[],"isGroup":true,"metaData":{"isPieceTimeline":true}},{"id":"part_group_firstobject_randomId9000_part0_0_randomId9002","objectType":"rundown","enable":{"start":0},"layer":"group_first_object","content":{"deviceType":"ABSTRACT","type":"callback","callBack":"partPlaybackStarted","callBackData":{"rundownPlaylistId":"playlist_randomId9000","partInstanceId":"randomId9000_part0_0_randomId9002"},"callBackStopped":"partPlaybackStopped"},"inGroup":"part_group_randomId9000_part0_0_randomId9002","classes":[],"priority":0},{"id":"piece_group_control_randomId9000_part0_0_randomId9002_randomId9000_piece001","objectType":"rundown","enable":{"start":0},"layer":"vt0","priority":5,"content":{"deviceType":"ABSTRACT","type":"callback","callBack":"piecePlaybackStarted","callBackData":{"rundownPlaylistId":"playlist_randomId9000","partInstanceId":"randomId9000_part0_0_randomId9002","pieceInstanceId":"randomId9000_part0_0_randomId9002_randomId9000_piece001","dynamicallyInserted":false},"callBackStopped":"piecePlaybackStopped"},"classes":["current_part"],"inGroup":"part_group_randomId9000_part0_0_randomId9002","metaData":{"isPieceTimeline":true,"triggerPieceInstanceId":"randomId9000_part0_0_randomId9002_randomId9000_piece001"}},{"id":"piece_group_randomId9000_part0_0_randomId9002_randomId9000_piece001","content":{"deviceType":"ABSTRACT","type":"group"},"children":[],"inGroup":"part_group_randomId9000_part0_0_randomId9002","isGroup":true,"objectType":"rundown","enable":{"start":"#piece_group_control_randomId9000_part0_0_randomId9002_randomId9000_piece001.start - 0","end":"#piece_group_control_randomId9000_part0_0_randomId9002_randomId9000_piece001.end + 0"},"layer":"","metaData":{"isPieceTimeline":true,"pieceInstanceGroupId":"randomId9000_part0_0_randomId9002_randomId9000_piece001"},"priority":0}]", - "timelineHash": "randomId9006", + "timelineBlob": "[{"id":"playlist_randomId9000_status","objectType":"rundown","enable":{"while":1},"layer":"rundown_status","content":{"deviceType":"ABSTRACT"},"classes":["rundown_active"],"priority":0},{"id":"part_group_randomId9000_part0_0_randomId9003","objectType":"rundown","enable":{"start":"now"},"priority":5,"layer":"","content":{"deviceType":"ABSTRACT","type":"group"},"children":[],"isGroup":true,"metaData":{"isPieceTimeline":true}},{"id":"part_group_firstobject_randomId9000_part0_0_randomId9003","objectType":"rundown","enable":{"start":0},"layer":"group_first_object","content":{"deviceType":"ABSTRACT","type":"callback","callBack":"partPlaybackStarted","callBackData":{"rundownPlaylistId":"playlist_randomId9000","partInstanceId":"randomId9000_part0_0_randomId9003"},"callBackStopped":"partPlaybackStopped"},"inGroup":"part_group_randomId9000_part0_0_randomId9003","classes":[],"priority":0},{"id":"piece_group_control_randomId9000_part0_0_randomId9003_randomId9000_piece001","objectType":"rundown","enable":{"start":0},"layer":"vt0","priority":5,"content":{"deviceType":"ABSTRACT","type":"callback","callBack":"piecePlaybackStarted","callBackData":{"rundownPlaylistId":"playlist_randomId9000","partInstanceId":"randomId9000_part0_0_randomId9003","pieceInstanceId":"randomId9000_part0_0_randomId9003_randomId9000_piece001","dynamicallyInserted":false},"callBackStopped":"piecePlaybackStopped"},"classes":["current_part"],"inGroup":"part_group_randomId9000_part0_0_randomId9003","metaData":{"isPieceTimeline":true,"triggerPieceInstanceId":"randomId9000_part0_0_randomId9003_randomId9000_piece001"}},{"id":"piece_group_randomId9000_part0_0_randomId9003_randomId9000_piece001","content":{"deviceType":"ABSTRACT","type":"group"},"children":[],"inGroup":"part_group_randomId9000_part0_0_randomId9003","isGroup":true,"objectType":"rundown","enable":{"start":"#piece_group_control_randomId9000_part0_0_randomId9003_randomId9000_piece001.start - 0","end":"#piece_group_control_randomId9000_part0_0_randomId9003_randomId9000_piece001.end + 0"},"layer":"","metaData":{"isPieceTimeline":true,"pieceInstanceGroupId":"randomId9000_part0_0_randomId9003_randomId9000_piece001"},"priority":0}]", + "timelineHash": "randomId9008", }, ] `; @@ -55,7 +55,7 @@ exports[`Playout API Basic rundown control 3`] = ` "studio": "asdf", }, "timelineBlob": "[]", - "timelineHash": "randomId9007", + "timelineHash": "randomId9010", }, ] `; @@ -67,7 +67,6 @@ exports[`Playout API Basic rundown control 4`] = ` "currentPartInfo": null, "externalId": "MOCK_RUNDOWNPLAYLIST", "holdState": 0, - "lastTakeTime": 0, "modified": 0, "name": "Default RundownPlaylist", "nextPartInfo": null, diff --git a/packages/job-worker/src/playout/__tests__/__snapshots__/timeline.test.ts.snap b/packages/job-worker/src/playout/__tests__/__snapshots__/timeline.test.ts.snap index 633dd169689..ef48638e9a3 100644 --- a/packages/job-worker/src/playout/__tests__/__snapshots__/timeline.test.ts.snap +++ b/packages/job-worker/src/playout/__tests__/__snapshots__/timeline.test.ts.snap @@ -12,7 +12,7 @@ exports[`Timeline Adlib pieces Current part with preroll 1`] = ` "studio": "asdf", }, "timelineBlob": "[]", - "timelineHash": "randomId9013", + "timelineHash": "randomId9022", }, ] `; @@ -29,7 +29,7 @@ exports[`Timeline Adlib pieces Current part with preroll and adlib preroll 1`] = "studio": "asdf", }, "timelineBlob": "[]", - "timelineHash": "randomId9013", + "timelineHash": "randomId9022", }, ] `; @@ -45,8 +45,8 @@ exports[`Timeline Basic rundown 1`] = ` "core": "0.0.0-test", "studio": "asdf", }, - "timelineBlob": "[{"id":"playlist_randomId9000_status","objectType":"rundown","enable":{"while":1},"layer":"rundown_status","content":{"deviceType":"ABSTRACT"},"classes":["rundown_active"],"priority":0},{"id":"part_group_randomId9000_part0_0_randomId9004","objectType":"rundown","enable":{"start":"now"},"priority":5,"layer":"","content":{"deviceType":"ABSTRACT","type":"group"},"children":[],"isGroup":true,"metaData":{"isPieceTimeline":true}},{"id":"part_group_firstobject_randomId9000_part0_0_randomId9004","objectType":"rundown","enable":{"start":0},"layer":"group_first_object","content":{"deviceType":"ABSTRACT","type":"callback","callBack":"partPlaybackStarted","callBackData":{"rundownPlaylistId":"playlist_randomId9000","partInstanceId":"randomId9000_part0_0_randomId9004"},"callBackStopped":"partPlaybackStopped"},"inGroup":"part_group_randomId9000_part0_0_randomId9004","classes":[],"priority":0},{"id":"piece_group_control_randomId9002_randomId9000_piece001","objectType":"rundown","enable":{"start":0},"layer":"vt0","priority":5,"content":{"deviceType":"ABSTRACT","type":"callback","callBack":"piecePlaybackStarted","callBackData":{"rundownPlaylistId":"playlist_randomId9000","partInstanceId":"randomId9000_part0_0_randomId9004","pieceInstanceId":"randomId9002_randomId9000_piece001","dynamicallyInserted":false},"callBackStopped":"piecePlaybackStopped"},"classes":["current_part"],"inGroup":"part_group_randomId9000_part0_0_randomId9004","metaData":{"isPieceTimeline":true,"triggerPieceInstanceId":"randomId9002_randomId9000_piece001"}},{"id":"piece_group_randomId9002_randomId9000_piece001","content":{"deviceType":"ABSTRACT","type":"group"},"children":[],"inGroup":"part_group_randomId9000_part0_0_randomId9004","isGroup":true,"objectType":"rundown","enable":{"start":"#piece_group_control_randomId9002_randomId9000_piece001.start - 0","end":"#piece_group_control_randomId9002_randomId9000_piece001.end + 0"},"layer":"","metaData":{"isPieceTimeline":true,"pieceInstanceGroupId":"randomId9002_randomId9000_piece001"},"priority":0}]", - "timelineHash": "randomId9012", + "timelineBlob": "[{"id":"playlist_randomId9000_status","objectType":"rundown","enable":{"while":1},"layer":"rundown_status","content":{"deviceType":"ABSTRACT"},"classes":["rundown_active"],"priority":0},{"id":"part_group_randomId9000_part0_0_randomId9003","objectType":"rundown","enable":{"start":"now"},"priority":5,"layer":"","content":{"deviceType":"ABSTRACT","type":"group"},"children":[],"isGroup":true,"metaData":{"isPieceTimeline":true}},{"id":"part_group_firstobject_randomId9000_part0_0_randomId9003","objectType":"rundown","enable":{"start":0},"layer":"group_first_object","content":{"deviceType":"ABSTRACT","type":"callback","callBack":"partPlaybackStarted","callBackData":{"rundownPlaylistId":"playlist_randomId9000","partInstanceId":"randomId9000_part0_0_randomId9003"},"callBackStopped":"partPlaybackStopped"},"inGroup":"part_group_randomId9000_part0_0_randomId9003","classes":[],"priority":0},{"id":"piece_group_control_randomId9000_part0_0_randomId9003_randomId9000_piece001","objectType":"rundown","enable":{"start":0},"layer":"vt0","priority":5,"content":{"deviceType":"ABSTRACT","type":"callback","callBack":"piecePlaybackStarted","callBackData":{"rundownPlaylistId":"playlist_randomId9000","partInstanceId":"randomId9000_part0_0_randomId9003","pieceInstanceId":"randomId9000_part0_0_randomId9003_randomId9000_piece001","dynamicallyInserted":false},"callBackStopped":"piecePlaybackStopped"},"classes":["current_part"],"inGroup":"part_group_randomId9000_part0_0_randomId9003","metaData":{"isPieceTimeline":true,"triggerPieceInstanceId":"randomId9000_part0_0_randomId9003_randomId9000_piece001"}},{"id":"piece_group_randomId9000_part0_0_randomId9003_randomId9000_piece001","content":{"deviceType":"ABSTRACT","type":"group"},"children":[],"inGroup":"part_group_randomId9000_part0_0_randomId9003","isGroup":true,"objectType":"rundown","enable":{"start":"#piece_group_control_randomId9000_part0_0_randomId9003_randomId9000_piece001.start - 0","end":"#piece_group_control_randomId9000_part0_0_randomId9003_randomId9000_piece001.end + 0"},"layer":"","metaData":{"isPieceTimeline":true,"pieceInstanceGroupId":"randomId9000_part0_0_randomId9003_randomId9000_piece001"},"priority":0}]", + "timelineHash": "randomId9010", }, ] `; @@ -63,7 +63,7 @@ exports[`Timeline Basic rundown 2`] = ` "studio": "asdf", }, "timelineBlob": "[]", - "timelineHash": "randomId9014", + "timelineHash": "randomId9012", }, ] `; @@ -80,7 +80,7 @@ exports[`Timeline In transitions Basic inTransition 1`] = ` "studio": "asdf", }, "timelineBlob": "[]", - "timelineHash": "randomId9016", + "timelineHash": "randomId9014", }, ] `; @@ -97,7 +97,7 @@ exports[`Timeline In transitions Basic inTransition with contentDelay + preroll "studio": "asdf", }, "timelineBlob": "[]", - "timelineHash": "randomId9016", + "timelineHash": "randomId9014", }, ] `; @@ -114,7 +114,7 @@ exports[`Timeline In transitions Basic inTransition with contentDelay 1`] = ` "studio": "asdf", }, "timelineBlob": "[]", - "timelineHash": "randomId9016", + "timelineHash": "randomId9014", }, ] `; @@ -131,7 +131,7 @@ exports[`Timeline In transitions Basic inTransition with planned pieces 1`] = ` "studio": "asdf", }, "timelineBlob": "[]", - "timelineHash": "randomId9016", + "timelineHash": "randomId9014", }, ] `; @@ -148,7 +148,7 @@ exports[`Timeline In transitions Preroll 1`] = ` "studio": "asdf", }, "timelineBlob": "[]", - "timelineHash": "randomId9016", + "timelineHash": "randomId9014", }, ] `; @@ -165,7 +165,7 @@ exports[`Timeline In transitions inTransition disabled 1`] = ` "studio": "asdf", }, "timelineBlob": "[]", - "timelineHash": "randomId9016", + "timelineHash": "randomId9014", }, ] `; @@ -182,7 +182,7 @@ exports[`Timeline In transitions inTransition is disabled during hold 1`] = ` "studio": "asdf", }, "timelineBlob": "[]", - "timelineHash": "randomId9018", + "timelineHash": "randomId9016", }, ] `; @@ -199,7 +199,7 @@ exports[`Timeline In transitions inTransition with existing infinites 1`] = ` "studio": "asdf", }, "timelineBlob": "[]", - "timelineHash": "randomId9017", + "timelineHash": "randomId9015", }, ] `; @@ -216,7 +216,7 @@ exports[`Timeline In transitions inTransition with new infinite 1`] = ` "studio": "asdf", }, "timelineBlob": "[]", - "timelineHash": "randomId9017", + "timelineHash": "randomId9015", }, ] `; @@ -233,7 +233,7 @@ exports[`Timeline Infinite Pieces Infinite Piece has stable timing across timeli "studio": "asdf", }, "timelineBlob": "[]", - "timelineHash": "randomId9017", + "timelineHash": "randomId9016", }, ] `; @@ -250,7 +250,7 @@ exports[`Timeline Out transitions Basic outTransition 1`] = ` "studio": "asdf", }, "timelineBlob": "[]", - "timelineHash": "randomId9016", + "timelineHash": "randomId9014", }, ] `; @@ -267,7 +267,7 @@ exports[`Timeline Out transitions outTransition + inTransition 1`] = ` "studio": "asdf", }, "timelineBlob": "[]", - "timelineHash": "randomId9016", + "timelineHash": "randomId9014", }, ] `; @@ -284,7 +284,7 @@ exports[`Timeline Out transitions outTransition + preroll (2) 1`] = ` "studio": "asdf", }, "timelineBlob": "[]", - "timelineHash": "randomId9016", + "timelineHash": "randomId9014", }, ] `; @@ -301,7 +301,7 @@ exports[`Timeline Out transitions outTransition + preroll 1`] = ` "studio": "asdf", }, "timelineBlob": "[]", - "timelineHash": "randomId9016", + "timelineHash": "randomId9014", }, ] `; @@ -318,7 +318,7 @@ exports[`Timeline Out transitions outTransition is disabled during hold 1`] = ` "studio": "asdf", }, "timelineBlob": "[]", - "timelineHash": "randomId9018", + "timelineHash": "randomId9016", }, ] `; diff --git a/packages/job-worker/src/playout/__tests__/timeline.test.ts b/packages/job-worker/src/playout/__tests__/timeline.test.ts index 013c8705680..6fff275e524 100644 --- a/packages/job-worker/src/playout/__tests__/timeline.test.ts +++ b/packages/job-worker/src/playout/__tests__/timeline.test.ts @@ -1129,12 +1129,11 @@ describe('Timeline', () => { }) describe('Adlib pieces', () => { - async function doStartAdlibPiece( - playlistId: RundownPlaylistId, - currentPartInstance: PlayoutPartInstanceModel, - adlibSource: AdLibPiece - ) { + async function doStartAdlibPiece(playlistId: RundownPlaylistId, adlibSource: AdLibPiece) { await runJobWithPlayoutCache(context, { playlistId }, null, async (cache) => { + const currentPartInstance = cache.CurrentPartInstance as PlayoutPartInstanceModel + expect(currentPartInstance).toBeTruthy() + const rundown = cache.getRundown(currentPartInstance.PartInstance.rundownId) as PlayoutRundownModel expect(rundown).toBeTruthy() @@ -1214,10 +1213,11 @@ describe('Timeline', () => { const { currentPartInstance } = await getPartInstances() expect(currentPartInstance).toBeTruthy() + console.log('inst', currentPartInstance?.PartInstance._id) + // Insert an adlib piece await doStartAdlibPiece( playlistId, - currentPartInstance!, literal({ _id: protectString('adlib1'), rundownId: currentPartInstance!.PartInstance.rundownId, @@ -1232,7 +1232,7 @@ describe('Timeline', () => { }) ) - const adlibbedPieceId = 'randomId9007' + const adlibbedPieceId = 'randomId9010' // The adlib should be starting at 'now' await checkTimings({ @@ -1383,7 +1383,6 @@ describe('Timeline', () => { // Insert an adlib piece await doStartAdlibPiece( playlistId, - currentPartInstance!, literal({ _id: protectString('adlib1'), rundownId: currentPartInstance!.PartInstance.rundownId, @@ -1399,7 +1398,7 @@ describe('Timeline', () => { }) ) - const adlibbedPieceId = 'randomId9007' + const adlibbedPieceId = 'randomId9010' // The adlib should be starting at 'now' await checkTimings({ diff --git a/packages/job-worker/src/playout/model/implementation/PlayoutModelImpl.ts b/packages/job-worker/src/playout/model/implementation/PlayoutModelImpl.ts index 25a82c52876..1535d70caf1 100644 --- a/packages/job-worker/src/playout/model/implementation/PlayoutModelImpl.ts +++ b/packages/job-worker/src/playout/model/implementation/PlayoutModelImpl.ts @@ -20,7 +20,7 @@ import { ReadonlyDeep } from 'type-fest' import { JobContext } from '../../../jobs' import { DBPart } from '@sofie-automation/corelib/dist/dataModel/Part' import { DBPartInstance } from '@sofie-automation/corelib/dist/dataModel/PartInstance' -import { PieceInstance } from '@sofie-automation/corelib/dist/dataModel/PieceInstance' +import { getPieceInstanceIdForPiece, PieceInstance } from '@sofie-automation/corelib/dist/dataModel/PieceInstance' import { serializeTimelineBlob, TimelineComplete, @@ -263,6 +263,7 @@ export class PlayoutModelImpl implements PlayoutModel { for (const pieceInstance of pieceInstances) { // TODO - should these be PieceInstance already, or should that be handled here? + pieceInstance._id = getPieceInstanceIdForPiece(newPartInstance._id, pieceInstance.piece._id) pieceInstance.partInstanceId = newPartInstance._id } @@ -448,7 +449,7 @@ export class PlayoutModelImpl implements PlayoutModel { this.#Playlist.holdState = RundownHoldState.NONE delete this.#Playlist.lastTakeTime - delete this.#Playlist.nextSegmentId + delete this.#Playlist.queuedSegmentId this.#PlaylistHasChanged = true } @@ -492,7 +493,7 @@ export class PlayoutModelImpl implements PlayoutModel { delete this.#Playlist.rundownsStartedPlayback delete this.#Playlist.previousPersistentState delete this.#Playlist.trackedAbSessions - delete this.#Playlist.nextSegmentId + delete this.#Playlist.queuedSegmentId if (regenerateActivationId) this.#Playlist.activationId = getRandomId() @@ -571,7 +572,7 @@ export class PlayoutModelImpl implements PlayoutModel { if ( Array.from(this.#AllPartInstances.values()).find( - (part) => !part || part.PartInstanceHasChanges || part.AnyPieceInstanceHasChanges() + (part) => !part || part.PartInstanceHasChanges || part.ChangedPieceInstanceIds().length > 0 ) ) logOrThrowError(new Error(`Failed no changes in cache assertion, a PartInstance has been changed`)) diff --git a/packages/job-worker/src/playout/model/implementation/PlayoutPartInstanceModelImpl.ts b/packages/job-worker/src/playout/model/implementation/PlayoutPartInstanceModelImpl.ts index 8017fadfbd6..8ee08d194d0 100644 --- a/packages/job-worker/src/playout/model/implementation/PlayoutPartInstanceModelImpl.ts +++ b/packages/job-worker/src/playout/model/implementation/PlayoutPartInstanceModelImpl.ts @@ -43,11 +43,15 @@ export class PlayoutPartInstanceModelImpl implements PlayoutPartInstanceModel { get PartInstanceHasChanges(): boolean { return this.#PartInstanceHasChanges } - AnyPieceInstanceHasChanges(): boolean { - for (const pieceInstance of this.PieceInstancesImpl.values()) { - if (pieceInstance.changed || !pieceInstance.doc) return true + ChangedPieceInstanceIds(): PieceInstanceId[] { + const result: PieceInstanceId[] = [] + for (const [id, pieceInstance] of this.PieceInstancesImpl.entries()) { + if (pieceInstance.changed || !pieceInstance.doc) result.push(id) } - return false + return result + } + HasAnyChanges(): boolean { + return this.#PartInstanceHasChanges || this.ChangedPieceInstanceIds().length > 0 } clearChangedFlags(): void { this.#PartInstanceHasChanges = false @@ -82,7 +86,7 @@ export class PlayoutPartInstanceModelImpl implements PlayoutPartInstanceModel { for (const pieceInstance of pieceInstances) { this.PieceInstancesImpl.set(pieceInstance._id, { doc: pieceInstance, - changed: false, + changed: hasChanges, }) } } @@ -107,6 +111,7 @@ export class PlayoutPartInstanceModelImpl implements PlayoutPartInstanceModel { for (const pieceInstance of this.PieceInstancesImpl.values()) { if (!pieceInstance.doc) continue + pieceInstance.changed = true pieceInstance.doc.playlistActivationId = id } } @@ -180,6 +185,7 @@ export class PlayoutPartInstanceModelImpl implements PlayoutPartInstanceModel { for (const pieceInstance of this.PieceInstancesImpl.values()) { if (!pieceInstance.doc) continue + pieceInstance.changed = true pieceInstance.doc.reset = true } @@ -265,6 +271,7 @@ export class PlayoutPartInstanceModelImpl implements PlayoutPartInstanceModel { const pieceInstance = this.PieceInstancesImpl.get(id) if (!pieceInstance?.doc) throw new Error('Bad pieceinstance') + pieceInstance.changed = true pieceInstance.doc.piece = { ...pieceInstance.doc.piece, ...props, @@ -390,6 +397,8 @@ export class PlayoutPartInstanceModelImpl implements PlayoutPartInstanceModel { pieceInstance.doc.plannedStartedPlayback = time delete pieceInstance.doc.plannedStoppedPlayback + pieceInstance.changed = true + return true } return false @@ -401,6 +410,8 @@ export class PlayoutPartInstanceModelImpl implements PlayoutPartInstanceModel { if (pieceInstance.doc.plannedStoppedPlayback !== time) { pieceInstance.doc.plannedStoppedPlayback = time + pieceInstance.changed = true + return true } return false @@ -413,6 +424,8 @@ export class PlayoutPartInstanceModelImpl implements PlayoutPartInstanceModel { pieceInstance.doc.reportedStartedPlayback = time delete pieceInstance.doc.reportedStoppedPlayback + pieceInstance.changed = true + return true } return false @@ -424,6 +437,8 @@ export class PlayoutPartInstanceModelImpl implements PlayoutPartInstanceModel { if (pieceInstance.doc.reportedStoppedPlayback !== time) { pieceInstance.doc.reportedStoppedPlayback = time + pieceInstance.changed = true + return true } return false @@ -453,6 +468,7 @@ export class PlayoutPartInstanceModelImpl implements PlayoutPartInstanceModel { infinitePieceId: pieceInstance.doc.piece._id, fromPreviousPart: false, } + pieceInstance.changed = true return infiniteInstanceId } @@ -502,6 +518,7 @@ export class PlayoutPartInstanceModelImpl implements PlayoutPartInstanceModel { const pieceInstance = this.PieceInstancesImpl.get(pieceInstanceId) if (!pieceInstance?.doc) throw new Error(`PieceInstance ${pieceInstanceId} not found`) // TODO - is this ok? + pieceInstance.changed = true pieceInstance.doc.userDuration = duration } @@ -549,6 +566,7 @@ export class PlayoutPartInstanceModelImpl implements PlayoutPartInstanceModel { const pieceInstance = this.PieceInstancesImpl.get(pieceInstanceId) if (!pieceInstance?.doc) throw new Error(`PieceInstance ${pieceInstanceId} not found`) // TODO - is this ok? + pieceInstance.changed = true pieceInstance.doc.disabled = disabled } } diff --git a/packages/job-worker/src/playout/model/implementation/SavePlayoutModel.ts b/packages/job-worker/src/playout/model/implementation/SavePlayoutModel.ts index 27d0b905263..434a859dd91 100644 --- a/packages/job-worker/src/playout/model/implementation/SavePlayoutModel.ts +++ b/packages/job-worker/src/playout/model/implementation/SavePlayoutModel.ts @@ -61,13 +61,15 @@ export function writePartInstancesAndPieceInstances( if (!partInstance) { deletedPartInstanceIds.push(partInstanceId) } else { - partInstanceOps.push({ - replaceOne: { - filter: { _id: partInstanceId }, - replacement: partInstance.PartInstanceImpl, - upsert: true, - }, - }) + if (partInstance.PartInstanceHasChanges) { + partInstanceOps.push({ + replaceOne: { + filter: { _id: partInstanceId }, + replacement: partInstance.PartInstanceImpl, + upsert: true, + }, + }) + } for (const [pieceInstanceId, pieceInstance] of partInstance.PieceInstancesImpl.entries()) { if (!pieceInstance.doc) { diff --git a/packages/job-worker/src/playout/setNext.ts b/packages/job-worker/src/playout/setNext.ts index 1842d9c1bc8..cac65206177 100644 --- a/packages/job-worker/src/playout/setNext.ts +++ b/packages/job-worker/src/playout/setNext.ts @@ -1,4 +1,4 @@ -import { assertNever, getRandomId } from '@sofie-automation/corelib/dist/lib' +import { assertNever } from '@sofie-automation/corelib/dist/lib' import { SegmentOrphanedReason } from '@sofie-automation/corelib/dist/dataModel/Segment' import { DBPart, isPartPlayable } from '@sofie-automation/corelib/dist/dataModel/Part' import { JobContext } from '../jobs' @@ -20,6 +20,7 @@ import { UserError, UserErrorMessage } from '@sofie-automation/corelib/dist/erro import { SelectNextPartResult } from './selectNextPart' import { ReadonlyDeep } from 'type-fest' import { QueueNextSegmentResult } from '@sofie-automation/corelib/dist/worker/studio' +import { protectString } from '@sofie-automation/corelib/dist/protectedString' /** * Set or clear the nexted part, from a given PartInstance, or SelectNextPartResult @@ -147,7 +148,7 @@ async function preparePartInstanceForPartBeingNexted( rundown, nextPart, possiblePieces, - getRandomId() // Replaced inside cache.createInstanceForPart + protectString('') // Replaced inside cache.createInstanceForPart ) return cache.createInstanceForPart(nextPart, newPieceInstances)