Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,9 @@ export default translateWithTracker<IProps, {}, ITrackedProps>((props: IProps) =
/>
<div className="shelf-inspector__action-editor">
<div className="shelf-inspector__action-editor__panel">
{action.userDataManifest && action.userDataManifest.editableFields && !targetAction ? (
{action.userDataManifest && action.userDataManifest.optionsSchema && !targetAction ? (
<Spinner />
) : action.userDataManifest && action.userDataManifest.editableFields && targetAction ? (
) : action.userDataManifest && action.userDataManifest.optionsSchema && targetAction ? (
<span>Editable Fields are not currently supported.</span>
) : null}
</div>
Expand Down
6 changes: 4 additions & 2 deletions packages/blueprints-integration/src/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,10 @@ export interface IBlueprintActionManifest<TPrivateData = unknown, TPublicData =
allVariants?: boolean

userDataManifest: {
/** List of editable fields in userData, to allow for customising */
editableFields?: JSONBlob<JSONSchema>
/** Schema for the executeAdLib adLibOptions property to allow for customising */
optionsSchema?: JSONBlob<JSONSchema>
/** Sets this to be a template action, requiring adLibOptions to be provided and validated */
template?: boolean
// Potential future properties:
// /** Execute the action after userData is changed. If not present ActionExecuteAfterChanged.none is assumed. */
// executeOnUserDataChanged?: ActionExecuteAfterChanged
Expand Down
12 changes: 12 additions & 0 deletions packages/live-status-gateway/api/schemas/adLibs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ $defs:
type: array
items:
$ref: '#/$defs/globalAdLib'
templateAdLibs:
description: The available Template AdLibs
type: array
items:
$ref: '#/$defs/templateAdLib'
required: [event, rundownPlaylistId, adLibs, globalAdLibs]
additionalProperties: false
examples:
Expand Down Expand Up @@ -56,6 +61,9 @@ $defs:
globalAdLib:
$ref: '#/$defs/adLibBase'
additionalProperties: false
templateAdLib:
$ref: '#/$defs/adLibBase'
additionalProperties: false
adLibBase:
type: object
properties:
Expand Down Expand Up @@ -92,6 +100,9 @@ $defs:
type: string
publicData:
description: Optional arbitrary data
optionsSchema:
description: JSON schema definition of the adLib properties that can be modified using the adLibOptions property in executeAdLib
type: string
required: [id, name, sourceLayer, actionType]
examples:
- id: 'C6K_yIMuGFUk8X_L9A9_jRT6aq4_'
Expand All @@ -103,3 +114,4 @@ $defs:
tags: ['music_video']
publicData:
fileName: MV000123.mxf
optionsSchema: '{"$schema":"https://json-schema.org/draft/2020-12/schema","title":"Play Video Clip","type":"object","properties":{"type":"adlib_action_video_clip","label":{"type":"string"},"clipId":{"type":"string"},"vo":{"type":"boolean"},"target":{"$schema":"https://json-schema.org/draft/2020-12/schema","title":"Object Id","description":"Id of an object sent to Sofie","type":"string"},"duration":{"type":"number","exclusiveMinimum":0},"takeType":{"$schema":"https://json-schema.org/draft/2020-12/schema","title":"AdLib Action Take Type","type":"string","enum":["take_immediate","queue"]},"transition":{"$schema":"https://json-schema.org/draft/2020-12/schema","title":"AdLib Action Transition Type","oneOf":[{"type":"object","properties":{"type":"cut"},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":"mix","duration":{"type":"number","exclusiveMinimum":0,"description":"Duration in ms"}},"required":["type","duration"],"additionalProperties":false},{"type":"object","properties":{"type":"wipe","duration":{"type":"number","exclusiveMinimum":0,"description":"Duration in ms"},"patternId":{"type":"string","description":"Type of wipe to use"}},"required":["type","duration","patternId"],"additionalProperties":false},{"type":"object","properties":{"type":"macro","macroId":{"type":"string","description":"Macro template to recall"}},"required":["type","macroId"],"additionalProperties":false}]}},"required":["type","clipId","vo","target"],"additionalProperties":false}"'
54 changes: 53 additions & 1 deletion packages/live-status-gateway/src/topics/adLibsTopic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export interface AdLibsStatus {
rundownPlaylistId: string | null
adLibs: AdLibStatus[]
globalAdLibs: GlobalAdLibStatus[]
templateAdLibs?: TemplateAdLibStatus[]
}

interface AdLibActionType {
Expand All @@ -45,6 +46,7 @@ interface AdLibStatus extends AdLibStatusBase {
}

type GlobalAdLibStatus = AdLibStatusBase
type TemplateAdLibStatus = AdLibStatusBase

interface AdLibStatusBase {
id: string
Expand All @@ -54,6 +56,7 @@ interface AdLibStatusBase {
actionType: AdLibActionType[]
tags?: string[]
publicData: unknown
optionsSchema?: any
}

export class AdLibsTopic
Expand All @@ -75,6 +78,7 @@ export class AdLibsTopic
private _parts: ReadonlyMap<PartId, DBPart> = new Map()
private _segments: ReadonlyMap<SegmentId, DBSegment> = new Map()
private _globalAdLibActions: RundownBaselineAdLibAction[] | undefined
private _templateAdLibActions: RundownBaselineAdLibAction[] | undefined
private _globalAdLibs: RundownBaselineAdLibItem[] | undefined
private throttledSendStatusToAll: () => void

Expand All @@ -94,6 +98,7 @@ export class AdLibsTopic
sendStatus(subscribers: Iterable<WebSocket>): void {
const adLibs: WithSortingMetadata<AdLibStatus>[] = []
const globalAdLibs: WithSortingMetadata<GlobalAdLibStatus>[] = []
const templateAdLibs: WithSortingMetadata<TemplateAdLibStatus>[] = []

if (this._adLibActions) {
adLibs.push(
Expand Down Expand Up @@ -125,6 +130,7 @@ export class AdLibsTopic
actionType: triggerModes,
tags: action.display.tags,
publicData: action.publicData,
optionsSchema: action.userDataManifest.optionsSchema,
},
id: unprotectString(action._id),
label: name,
Expand Down Expand Up @@ -193,6 +199,7 @@ export class AdLibsTopic
actionType: triggerModes,
tags: action.display.tags,
publicData: action.publicData,
optionsSchema: action.userDataManifest.optionsSchema,
},
id: unprotectString(action._id),
label: name,
Expand Down Expand Up @@ -227,12 +234,51 @@ export class AdLibsTopic
)
}

if (this._templateAdLibActions) {
templateAdLibs.push(
...this._templateAdLibActions.map((action) => {
const sourceLayerName = this._sourceLayersMap.get(
(action.display as IBlueprintActionManifestDisplayContent).sourceLayerId
)
const outputLayerName = this._outputLayersMap.get(
(action.display as IBlueprintActionManifestDisplayContent).outputLayerId
)
const triggerModes = action.triggerModes
? action.triggerModes.map((t) =>
literal<AdLibActionType>({
name: t.data,
label: t.display.label.key,
})
)
: []
const name = interpollateTranslation(action.display.label.key, action.display.label.args)
return literal<WithSortingMetadata<TemplateAdLibStatus>>({
obj: {
id: unprotectString(action._id),
name,
sourceLayer: sourceLayerName ?? 'invalid',
outputLayer: outputLayerName ?? 'invalid',
actionType: triggerModes,
tags: action.display.tags,
publicData: action.publicData,
optionsSchema: action.userDataManifest.optionsSchema,
},
id: unprotectString(action._id),
label: name,
rundownRank: this._activePlaylist?.rundownIdsInOrder.indexOf(action.rundownId),
itemRank: action.display._rank,
})
})
)
}

const adLibsStatus: AdLibsStatus = this._activePlaylist
? {
event: 'adLibs',
rundownPlaylistId: unprotectString(this._activePlaylist._id),
adLibs: sortContent(adLibs),
globalAdLibs: sortContent(globalAdLibs),
templateAdLibs: templateAdLibs.length ? sortContent(templateAdLibs) : undefined,
}
: { event: 'adLibs', rundownPlaylistId: null, adLibs: [], globalAdLibs: [] }

Expand Down Expand Up @@ -275,7 +321,13 @@ export class AdLibsTopic
case GlobalAdLibActionsHandler.name: {
const globalAdLibActions = data ? (data as RundownBaselineAdLibAction[]) : []
this.logUpdateReceived('globalAdLibActions', source)
this._globalAdLibActions = globalAdLibActions
this._globalAdLibActions = []
globalAdLibActions.forEach((action) => {
if (action.userDataManifest?.template) {
if (!this._templateAdLibActions) this._templateAdLibActions = [action]
else this._templateAdLibActions.push(action)
} else this._globalAdLibActions?.push(action)
})
break
}
case AdLibsHandler.name: {
Expand Down