Skip to content

Commit b0daf75

Browse files
committed
feat: option to disable HOLD & direct play (#32)
1 parent 8c78ed2 commit b0daf75

File tree

18 files changed

+143
-9
lines changed

18 files changed

+143
-9
lines changed

meteor/__mocks__/defaultCollectionObjects.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ export function defaultStudio(_id: StudioId): DBStudio {
110110
mediaPreviewsUrl: '',
111111
minimumTakeSpan: DEFAULT_MINIMUM_TAKE_SPAN,
112112
fallbackPartDuration: DEFAULT_FALLBACK_PART_DURATION,
113+
allowHold: false,
114+
allowPieceDirectPlay: false,
113115
},
114116
_rundownVersionHash: '',
115117
routeSetsWithOverrides: wrapDefaultObject({}),

meteor/server/api/rest/v1/typeConversion.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {
1919
StudioId,
2020
} from '@sofie-automation/corelib/dist/dataModel/Ids'
2121
import { DBStudio, IStudioSettings } from '@sofie-automation/corelib/dist/dataModel/Studio'
22-
import { assertNever, getRandomId, literal } from '@sofie-automation/corelib/dist/lib'
22+
import { assertNever, Complete, getRandomId, literal } from '@sofie-automation/corelib/dist/lib'
2323
import { protectString, unprotectString } from '@sofie-automation/corelib/dist/protectedString'
2424
import {
2525
applyAndValidateOverrides,
@@ -333,7 +333,7 @@ export async function studioFrom(apiStudio: APIStudio, existingId?: StudioId): P
333333
}
334334
}
335335

336-
export async function APIStudioFrom(studio: DBStudio): Promise<APIStudio> {
336+
export async function APIStudioFrom(studio: DBStudio): Promise<Complete<APIStudio>> {
337337
const studioSettings = APIStudioSettingsFrom(studio.settings)
338338

339339
return {
@@ -346,7 +346,7 @@ export async function APIStudioFrom(studio: DBStudio): Promise<APIStudio> {
346346
}
347347
}
348348

349-
export function studioSettingsFrom(apiStudioSettings: APIStudioSettings): IStudioSettings {
349+
export function studioSettingsFrom(apiStudioSettings: APIStudioSettings): Complete<IStudioSettings> {
350350
return {
351351
frameRate: apiStudioSettings.frameRate,
352352
mediaPreviewsUrl: apiStudioSettings.mediaPreviewsUrl,
@@ -362,10 +362,13 @@ export function studioSettingsFrom(apiStudioSettings: APIStudioSettings): IStudi
362362
enableQuickLoop: apiStudioSettings.enableQuickLoop,
363363
forceQuickLoopAutoNext: forceQuickLoopAutoNextFrom(apiStudioSettings.forceQuickLoopAutoNext),
364364
fallbackPartDuration: apiStudioSettings.fallbackPartDuration ?? DEFAULT_FALLBACK_PART_DURATION,
365+
allowAdlibTestingSegment: apiStudioSettings.allowAdlibTestingSegment,
366+
allowHold: apiStudioSettings.allowHold ?? true, // Backwards compatible
367+
allowPieceDirectPlay: apiStudioSettings.allowPieceDirectPlay ?? true, // Backwards compatible
365368
}
366369
}
367370

368-
export function APIStudioSettingsFrom(settings: IStudioSettings): APIStudioSettings {
371+
export function APIStudioSettingsFrom(settings: IStudioSettings): Complete<APIStudioSettings> {
369372
return {
370373
frameRate: settings.frameRate,
371374
mediaPreviewsUrl: settings.mediaPreviewsUrl,
@@ -381,6 +384,9 @@ export function APIStudioSettingsFrom(settings: IStudioSettings): APIStudioSetti
381384
enableQuickLoop: settings.enableQuickLoop,
382385
forceQuickLoopAutoNext: APIForceQuickLoopAutoNextFrom(settings.forceQuickLoopAutoNext),
383386
fallbackPartDuration: settings.fallbackPartDuration,
387+
allowAdlibTestingSegment: settings.allowAdlibTestingSegment,
388+
allowHold: settings.allowHold,
389+
allowPieceDirectPlay: settings.allowPieceDirectPlay,
384390
}
385391
}
386392

meteor/server/api/studio/api.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ export async function insertStudioInner(organizationId: OrganizationId | null, n
4848
frameRate: 25,
4949
mediaPreviewsUrl: '',
5050
minimumTakeSpan: DEFAULT_MINIMUM_TAKE_SPAN,
51+
allowHold: false,
52+
allowPieceDirectPlay: false,
5153
},
5254
_rundownVersionHash: '',
5355
routeSetsWithOverrides: wrapDefaultObject({}),

meteor/server/lib/rest/v1/studios.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,4 +214,7 @@ export interface APIStudioSettings {
214214
forceQuickLoopAutoNext?: 'disabled' | 'enabled_when_valid_duration' | 'enabled_forcing_min_duration'
215215
minimumTakeSpan?: number
216216
fallbackPartDuration?: number
217+
allowAdlibTestingSegment?: boolean
218+
allowHold?: boolean
219+
allowPieceDirectPlay?: boolean
217220
}

meteor/server/migration/0_1_0.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,8 @@ export const addSteps = addMigrationSteps('0.1.0', [
441441
frameRate: 25,
442442
mediaPreviewsUrl: '',
443443
minimumTakeSpan: DEFAULT_MINIMUM_TAKE_SPAN,
444+
allowHold: false,
445+
allowPieceDirectPlay: false,
444446
},
445447
mappingsWithOverrides: wrapDefaultObject({}),
446448
blueprintConfigWithOverrides: wrapDefaultObject({}),

meteor/server/migration/X_X_X.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,4 +187,41 @@ export const addSteps = addMigrationSteps(CURRENT_SYSTEM_VERSION, [
187187
}
188188
},
189189
},
190+
191+
{
192+
id: `add studio settings allowHold & allowPieceDirectPlay`,
193+
canBeRunAutomatically: true,
194+
validate: async () => {
195+
const studios = await Studios.findFetchAsync({
196+
$or: [
197+
{ 'settings.allowHold': { $exists: false } },
198+
{ 'settings.allowPieceDirectPlay': { $exists: false } },
199+
],
200+
})
201+
202+
if (studios.length > 0) {
203+
return 'studios must have settings.allowHold and settings.allowPieceDirectPlay defined'
204+
}
205+
206+
return false
207+
},
208+
migrate: async () => {
209+
const studios = await Studios.findFetchAsync({
210+
$or: [
211+
{ 'settings.allowHold': { $exists: false } },
212+
{ 'settings.allowPieceDirectPlay': { $exists: false } },
213+
],
214+
})
215+
216+
for (const studio of studios) {
217+
// Populate the settings to be backwards compatible
218+
await Studios.updateAsync(studio._id, {
219+
$set: {
220+
'settings.allowHold': true,
221+
'settings.allowPieceDirectPlay': true,
222+
},
223+
})
224+
}
225+
},
226+
},
190227
])

meteor/server/migration/__tests__/migrations.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ describe('Migrations', () => {
126126
mediaPreviewsUrl: '',
127127
frameRate: 25,
128128
minimumTakeSpan: DEFAULT_MINIMUM_TAKE_SPAN,
129+
allowHold: true,
130+
allowPieceDirectPlay: true,
129131
},
130132
mappingsWithOverrides: wrapDefaultObject({}),
131133
blueprintConfigWithOverrides: wrapDefaultObject({}),
@@ -164,6 +166,8 @@ describe('Migrations', () => {
164166
mediaPreviewsUrl: '',
165167
frameRate: 25,
166168
minimumTakeSpan: DEFAULT_MINIMUM_TAKE_SPAN,
169+
allowHold: true,
170+
allowPieceDirectPlay: true,
167171
},
168172
mappingsWithOverrides: wrapDefaultObject({}),
169173
blueprintConfigWithOverrides: wrapDefaultObject({}),
@@ -202,6 +206,8 @@ describe('Migrations', () => {
202206
mediaPreviewsUrl: '',
203207
frameRate: 25,
204208
minimumTakeSpan: DEFAULT_MINIMUM_TAKE_SPAN,
209+
allowHold: true,
210+
allowPieceDirectPlay: true,
205211
},
206212
mappingsWithOverrides: wrapDefaultObject({}),
207213
blueprintConfigWithOverrides: wrapDefaultObject({}),

meteor/server/publications/pieceContentStatusUI/__tests__/checkPieceContentStatus.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,7 @@ describe('lib/mediaObjects', () => {
8585
test('getAcceptedFormats', () => {
8686
const acceptedFormats = getAcceptedFormats({
8787
supportedMediaFormats: '1920x1080i5000, 1280x720, i5000, i5000tff',
88-
mediaPreviewsUrl: '',
8988
frameRate: 25,
90-
minimumTakeSpan: DEFAULT_MINIMUM_TAKE_SPAN,
9189
})
9290
expect(acceptedFormats).toEqual([
9391
['1920', '1080', 'i', '5000', undefined],
@@ -170,6 +168,8 @@ describe('lib/mediaObjects', () => {
170168
supportedAudioStreams: '4',
171169
frameRate: 25,
172170
minimumTakeSpan: DEFAULT_MINIMUM_TAKE_SPAN,
171+
allowHold: false,
172+
allowPieceDirectPlay: false,
173173
}
174174

175175
const mockDefaultStudio = defaultStudio(protectString('studio0'))

meteor/server/publications/pieceContentStatusUI/checkPieceContentStatus.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,9 @@ export function acceptFormat(format: string, formats: Array<Array<string>>): boo
135135
* [undefined, undefined, i, 5000, tff]
136136
* ]
137137
*/
138-
export function getAcceptedFormats(settings: IStudioSettings | undefined): Array<Array<string>> {
138+
export function getAcceptedFormats(
139+
settings: Pick<IStudioSettings, 'supportedMediaFormats' | 'frameRate'> | undefined
140+
): Array<Array<string>> {
139141
const formatsConfigField = settings ? settings.supportedMediaFormats : ''
140142
const formatsString: string =
141143
(formatsConfigField && formatsConfigField !== '' ? formatsConfigField : '1920x1080i5000') + ''

packages/corelib/src/dataModel/Studio.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,19 @@ export interface IStudioSettings {
8888
* Default: 3000
8989
*/
9090
fallbackPartDuration?: number
91+
92+
/**
93+
* Whether to allow hold operations for Rundowns in this Studio
94+
* When disabled, any action-triggers that would normally trigger a hold operation will be silently ignored
95+
* This should only block entering hold, to ensure Sofie doesn't get stuck if it somehow gets into hold
96+
*/
97+
allowHold: boolean
98+
99+
/**
100+
* Whether to allow direct playing of a piece in the rundown
101+
* This behaviour is usally triggered by double-clicking on a piece in the GUI
102+
*/
103+
allowPieceDirectPlay: boolean
91104
}
92105

93106
export type StudioLight = Omit<DBStudio, 'mappingsWithOverrides' | 'blueprintConfigWithOverrides'>

0 commit comments

Comments
 (0)