From d82da65400b90e3c5441f23000322d681452ca0e Mon Sep 17 00:00:00 2001 From: Rasmus Lindved Date: Fri, 28 Apr 2023 08:02:25 +0200 Subject: [PATCH 01/35] SOF-1422 Able to parse Schema cues and send Schema commands to Viz --- src/tv2-common/blueprintConfig.ts | 2 +- src/tv2-common/evaluateCues.ts | 12 ++++ .../inewsConversion/converters/ParseBody.ts | 16 ++++- .../inewsConversion/converters/ParseCue.ts | 65 ++++++++++++++++++- src/tv2-constants/enums.ts | 3 +- .../pieces/evaluate-cue-graphic-schema.ts | 60 +++++++++++++++++ .../helpers/pieces/evaluateCues.ts | 2 + src/tv2_afvd_showstyle/layers.ts | 1 + .../migrations/sourcelayer-defaults.ts | 8 +++ src/tv2_afvd_studio/layers.ts | 3 +- .../migrations/mappings-defaults.ts | 6 ++ 11 files changed, 171 insertions(+), 7 deletions(-) create mode 100644 src/tv2_afvd_showstyle/helpers/pieces/evaluate-cue-graphic-schema.ts diff --git a/src/tv2-common/blueprintConfig.ts b/src/tv2-common/blueprintConfig.ts index 64483d27..a2c6c01e 100644 --- a/src/tv2-common/blueprintConfig.ts +++ b/src/tv2-common/blueprintConfig.ts @@ -54,7 +54,7 @@ export interface TableConfigItemAdLibTransitions { } export interface TableConfigGfxSchema { - SchemaName: string + GfxSchemaTemplatesName: string INewsSkemaColumn: string VizTemplate: string } diff --git a/src/tv2-common/evaluateCues.ts b/src/tv2-common/evaluateCues.ts index d9255f3d..b35e58a9 100644 --- a/src/tv2-common/evaluateCues.ts +++ b/src/tv2-common/evaluateCues.ts @@ -13,6 +13,7 @@ import { CueDefinitionClearGrafiks, CueDefinitionDVE, CueDefinitionEkstern, + CueDefinitionGraphicSchema, CueDefinitionJingle, CueDefinitionLYD, CueDefinitionRobotCamera, @@ -79,6 +80,12 @@ export interface EvaluateCuesShowstyleOptions { adlib?: boolean, rank?: number ) => void + EvaluateCueGraphicSchema?: ( + context: ShowStyleContext, + pieces: IBlueprintPiece[], + partId: string, + parsedCue: CueDefinitionGraphicSchema + ) => void EvaluateCueRouting?: ( context: ShowStyleContext, partId: string, @@ -291,6 +298,11 @@ export async function EvaluateCuesBase( ) } break + case CueType.GraphicSchema: + if (showStyleOptions.EvaluateCueGraphicSchema) { + showStyleOptions.EvaluateCueGraphicSchema(context, pieces, partDefinition.externalId, cue) + } + break case CueType.ClearGrafiks: if (showStyleOptions.EvaluateCueClearGrafiks) { showStyleOptions.EvaluateCueClearGrafiks( diff --git a/src/tv2-common/inewsConversion/converters/ParseBody.ts b/src/tv2-common/inewsConversion/converters/ParseBody.ts index 52e72aa8..a8e20713 100644 --- a/src/tv2-common/inewsConversion/converters/ParseBody.ts +++ b/src/tv2-common/inewsConversion/converters/ParseBody.ts @@ -698,10 +698,22 @@ export function isMinusMic(inputName: string): boolean { } export function stripRedundantCuesWhenLayoutCueIsPresent(partDefinitions: PartDefinition[]): PartDefinition[] { + return stripRedundantCuesForSchema(stripRedundantCuesForDesign(partDefinitions)) +} + +function stripRedundantCuesForDesign(partDefinitions: PartDefinition[]): PartDefinition[] { + return stripRedundantCues(partDefinitions, [CueType.GraphicDesign, CueType.BackgroundLoop]) +} + +function stripRedundantCuesForSchema(partDefinitions: PartDefinition[]): PartDefinition[] { + return stripRedundantCues(partDefinitions, [CueType.GraphicSchema]) +} + +function stripRedundantCues(partDefinitions: PartDefinition[], cueTypesToCheck: CueType[]): PartDefinition[] { const hasLayoutCue: boolean = partDefinitions.some((definition) => definition.cues.some((cue) => { const cueFromLayout = cue as CueDefinitionFromLayout - return cueFromLayout.isFromLayout + return cueTypesToCheck.includes(cue.type) && cueFromLayout.isFromLayout }) ) @@ -711,7 +723,7 @@ export function stripRedundantCuesWhenLayoutCueIsPresent(partDefinitions: PartDe return partDefinitions.map((definition) => { const cues = definition.cues.filter((cue) => { - if (cue.type !== CueType.GraphicDesign && cue.type !== CueType.BackgroundLoop) { + if (!cueTypesToCheck.includes(cue.type)) { return true } return (cue as CueDefinitionFromLayout).isFromLayout diff --git a/src/tv2-common/inewsConversion/converters/ParseCue.ts b/src/tv2-common/inewsConversion/converters/ParseCue.ts index ad754901..d644a540 100644 --- a/src/tv2-common/inewsConversion/converters/ParseCue.ts +++ b/src/tv2-common/inewsConversion/converters/ParseCue.ts @@ -1,4 +1,10 @@ -import { literal, TableConfigItemGfxDesignTemplate, TV2ShowStyleConfig, UnparsedCue } from 'tv2-common' +import { + literal, + TableConfigGfxSchema, + TableConfigItemGfxDesignTemplate, + TV2ShowStyleConfig, + UnparsedCue +} from 'tv2-common' import { CueType, GraphicEngine, SourceType } from 'tv2-constants' import { getSourceDefinition, @@ -124,6 +130,11 @@ export interface CueDefinitionFromLayout { isFromLayout?: boolean } +export interface CueDefinitionGraphicSchema extends CueDefinitionBase, CueDefinitionFromLayout { + type: CueType.GraphicSchema + schema: string +} + export interface GraphicInternal { type: 'internal' template: string @@ -180,6 +191,7 @@ export type CueDefinition = | CueDefinitionUnpairedPilot | CueDefinitionBackgroundLoop | CueDefinitionGraphicDesign + | CueDefinitionGraphicSchema | CueDefinitionGraphic | CueDefinitionRouting | CueDefinitionPgmClean @@ -254,6 +266,8 @@ export function ParseCue(cue: UnparsedCue, config: TV2ShowStyleConfig): CueDefin return parseMixMinus(cue) } else if (/^DESIGN_LAYOUT=/i.test(cue[0])) { return parseDesignLayout(cue, config) + } else if (/^SCHEMA_FIELD=/i.test(cue[0])) { + return parseSchemaLayout(cue, config) } else if (/^ROBOT\s*=/i.test(cue[0])) { return parseRobotCue(cue) } @@ -267,7 +281,11 @@ export function ParseCue(cue: UnparsedCue, config: TV2ShowStyleConfig): CueDefin function parsekg( cue: string[], config: TV2ShowStyleConfig -): CueDefinitionGraphic | CueDefinitionGraphicDesign | CueDefinitionUnpairedTarget { +): + | CueDefinitionGraphic + | CueDefinitionGraphicDesign + | CueDefinitionGraphicSchema + | CueDefinitionUnpairedTarget { let kgCue: CueDefinitionGraphic = { type: CueType.Graphic, target: 'OVL', @@ -350,6 +368,23 @@ function parsekg( }) } + const graphicsSchemaConfig = code + ? config.showStyle.GfxSchemaTemplates.find( + (template) => template.GfxSchemaTemplatesName.toUpperCase() === graphic.template.toUpperCase() + ) + : undefined + + if (graphicsSchemaConfig) { + return literal({ + type: CueType.GraphicSchema, + schema: graphicsSchemaConfig.VizTemplate, + iNewsCommand: kgCue.iNewsCommand, + start: kgCue.start, + end: kgCue.end, + adlib: kgCue.adlib + }) + } + const graphicConfig = code ? config.showStyle.GfxTemplates.find( (template) => @@ -926,6 +961,32 @@ function findGraphicDesignConfiguration( ) } +function parseSchemaLayout(cue: string[], config: TV2ShowStyleConfig): CueDefinitionGraphicSchema | undefined { + const array = cue[0].split('SCHEMA_FIELD=') + const schema = array[1] + + const schemaConfiguration = findGraphicSchemaConfiguration(config, schema) + if (!schemaConfiguration) { + return undefined + } + + return literal({ + type: CueType.GraphicSchema, + schema: schemaConfiguration.VizTemplate, + iNewsCommand: '', + start: { + frames: 1 + }, + isFromLayout: true + }) +} + +function findGraphicSchemaConfiguration(config: TV2ShowStyleConfig, schema: string): TableConfigGfxSchema | undefined { + return config.showStyle.GfxSchemaTemplates.find( + (template) => template.INewsSkemaColumn && template.INewsSkemaColumn.toUpperCase() === schema.toUpperCase() + ) +} + function parseRobotCue(cue: string[]): CueDefinitionRobotCamera { const presetIdentifier: number = Number(cue[0].match(/\d+/)) const time: Pick = cue[1] ? parseTime(cue[1]) : { start: { seconds: 0 } } diff --git a/src/tv2-constants/enums.ts b/src/tv2-constants/enums.ts index 128d521f..3ef33d20 100644 --- a/src/tv2-constants/enums.ts +++ b/src/tv2-constants/enums.ts @@ -33,7 +33,8 @@ export enum CueType { Routing, PgmClean, MixMinus, - RobotCamera + RobotCamera, + GraphicSchema } export const enum PartType { diff --git a/src/tv2_afvd_showstyle/helpers/pieces/evaluate-cue-graphic-schema.ts b/src/tv2_afvd_showstyle/helpers/pieces/evaluate-cue-graphic-schema.ts new file mode 100644 index 00000000..942007ac --- /dev/null +++ b/src/tv2_afvd_showstyle/helpers/pieces/evaluate-cue-graphic-schema.ts @@ -0,0 +1,60 @@ +import { GraphicsContent, PieceLifespan, WithTimeline } from '@sofie-automation/blueprints-integration' +import { IBlueprintPiece, TSR } from 'blueprints-integration' +import { calculateTime, CueDefinitionGraphicSchema, literal, ShowStyleContext, TV2ShowStyleConfig } from 'tv2-common' +import { SharedOutputLayer } from 'tv2-constants' +import { GraphicLLayer } from '../../../tv2_afvd_studio/layers' +import { SourceLayer } from '../../layers' + +export function EvaluateCueGraphicSchema( + context: ShowStyleContext, + pieces: IBlueprintPiece[], + partId: string, + parsedCue: CueDefinitionGraphicSchema +) { + if (!parsedCue.schema) { + context.core.notifyUserWarning(`No valid Schema found for ${parsedCue.schema}`) + return + } + + const start = (parsedCue.start ? calculateTime(parsedCue.start) : 0) ?? 0 + pieces.push({ + externalId: partId, + name: parsedCue.schema, + enable: { + start + }, + outputLayerId: SharedOutputLayer.SEC, + sourceLayerId: SourceLayer.PgmSchema, + lifespan: PieceLifespan.OutOnShowStyleEnd, + content: literal>({ + fileName: parsedCue.schema, + path: parsedCue.schema, + ignoreMediaObjectStatus: true, + timelineObjects: createTimeline(context.config, parsedCue) + }) + }) +} + +function createTimeline( + config: TV2ShowStyleConfig, + cue: CueDefinitionGraphicSchema +): TSR.TimelineObjVIZMSEElementInternal[] { + if (config.studio.GraphicsType !== 'VIZ') { + return [] + } + return [ + literal({ + id: '', + enable: { start: 0 }, + priority: 100, + layer: GraphicLLayer.GraphicLLayerSchema, + content: { + deviceType: TSR.DeviceType.VIZMSE, + type: TSR.TimelineContentTypeVizMSE.ELEMENT_INTERNAL, + templateName: cue.schema, + templateData: [], + showName: config.selectedGfxSetup.OvlShowName ?? '' + } + }) + ] +} diff --git a/src/tv2_afvd_showstyle/helpers/pieces/evaluateCues.ts b/src/tv2_afvd_showstyle/helpers/pieces/evaluateCues.ts index fbfc1a15..220e2160 100644 --- a/src/tv2_afvd_showstyle/helpers/pieces/evaluateCues.ts +++ b/src/tv2_afvd_showstyle/helpers/pieces/evaluateCues.ts @@ -21,6 +21,7 @@ import { EvaluateClearGrafiks } from './clearGrafiks' import { EvaluateCueDesign } from './design' import { EvaluateDVE } from './dve' import { EvaluateEkstern } from './ekstern' +import { EvaluateCueGraphicSchema } from './evaluate-cue-graphic-schema' import { EvaluateCueGraphic } from './graphic' import { EvaluateCueBackgroundLoop } from './graphicBackgroundLoop' import { EvaluateJingle } from './jingle' @@ -50,6 +51,7 @@ export async function EvaluateCues( EvaluateCueGraphic, EvaluateCueBackgroundLoop, EvaluateCueGraphicDesign: EvaluateCueDesign, + EvaluateCueGraphicSchema, EvaluateCueRouting, EvaluateCueMixMinus, EvaluateCueRobotCamera diff --git a/src/tv2_afvd_showstyle/layers.ts b/src/tv2_afvd_showstyle/layers.ts index 91d8599a..8b8a4e53 100644 --- a/src/tv2_afvd_showstyle/layers.ts +++ b/src/tv2_afvd_showstyle/layers.ts @@ -3,6 +3,7 @@ import { SharedSourceLayer } from 'tv2-constants' export enum AFVDSourceLayer { // Pgm PgmLocal = 'studio0_local', + PgmSchema = 'studio0_schema', VizFullIn1 = 'studio0_aux_viz_full1', AuxStudioScreen = 'studio0_aux_studio_screen', diff --git a/src/tv2_afvd_showstyle/migrations/sourcelayer-defaults.ts b/src/tv2_afvd_showstyle/migrations/sourcelayer-defaults.ts index 9d7a98c4..2ccb5b06 100644 --- a/src/tv2_afvd_showstyle/migrations/sourcelayer-defaults.ts +++ b/src/tv2_afvd_showstyle/migrations/sourcelayer-defaults.ts @@ -449,6 +449,14 @@ const SEC: ISourceLayer[] = [ allowDisable: false, onPresenterScreen: false }, + { + _id: SourceLayer.PgmSchema, + _rank: 30, + name: 'Viz Schema', + abbreviation: '', + type: SourceLayerType.UNKNOWN, + isHidden: true + }, { _id: SourceLayer.PgmDVEBackground, _rank: 40, diff --git a/src/tv2_afvd_studio/layers.ts b/src/tv2_afvd_studio/layers.ts index 812eebaa..37ede432 100644 --- a/src/tv2_afvd_studio/layers.ts +++ b/src/tv2_afvd_studio/layers.ts @@ -22,7 +22,8 @@ export type CasparLLayer = AFVDCasparLLayer | SharedCasparLLayer enum AFVDGraphicLLayer { GraphicLLayerInitialize = 'graphic_initialize', - GraphicLLayerCleanup = 'graphic_cleanup' + GraphicLLayerCleanup = 'graphic_cleanup', + GraphicLLayerSchema = 'graphic_schema' } // tslint:disable-next-line: variable-name diff --git a/src/tv2_afvd_studio/migrations/mappings-defaults.ts b/src/tv2_afvd_studio/migrations/mappings-defaults.ts index acf90db0..1753784e 100644 --- a/src/tv2_afvd_studio/migrations/mappings-defaults.ts +++ b/src/tv2_afvd_studio/migrations/mappings-defaults.ts @@ -541,6 +541,12 @@ export const MAPPINGS_GRAPHICS: BlueprintMappings = { layerName: 'GFX Design', lookahead: LookaheadMode.NONE }), + [GraphicLLayer.GraphicLLayerSchema]: literal({ + device: TSR.DeviceType.VIZMSE, + deviceId: 'viz0', + layerName: 'GFX Skema', + lookahead: LookaheadMode.NONE + }), [GraphicLLayer.GraphicLLayerAdLibs]: literal({ device: TSR.DeviceType.VIZMSE, deviceId: 'viz0', From 3ea9b09e89133333e8c8d3e855f41a5b106a5bb1 Mon Sep 17 00:00:00 2001 From: Rasmus Lindved Date: Fri, 28 Apr 2023 11:36:08 +0200 Subject: [PATCH 02/35] SOF-1422 No longer refer to the field from iNews as layout, but as field instead --- .../inewsConversion/converters/ParseBody.ts | 12 ++++++------ .../inewsConversion/converters/ParseCue.ts | 18 +++++++++--------- .../converters/__tests__/body-parser.spec.ts | 8 ++++---- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/tv2-common/inewsConversion/converters/ParseBody.ts b/src/tv2-common/inewsConversion/converters/ParseBody.ts index a8e20713..2f29a394 100644 --- a/src/tv2-common/inewsConversion/converters/ParseBody.ts +++ b/src/tv2-common/inewsConversion/converters/ParseBody.ts @@ -1,5 +1,5 @@ import { - CueDefinitionFromLayout, + CueDefinitionFromField, parseTransitionStyle, PostProcessDefinitions, TransitionStyle, @@ -710,14 +710,14 @@ function stripRedundantCuesForSchema(partDefinitions: PartDefinition[]): PartDef } function stripRedundantCues(partDefinitions: PartDefinition[], cueTypesToCheck: CueType[]): PartDefinition[] { - const hasLayoutCue: boolean = partDefinitions.some((definition) => + const hasFieldCue: boolean = partDefinitions.some((definition) => definition.cues.some((cue) => { - const cueFromLayout = cue as CueDefinitionFromLayout - return cueTypesToCheck.includes(cue.type) && cueFromLayout.isFromLayout + const cueFromField = cue as CueDefinitionFromField + return cueTypesToCheck.includes(cue.type) && cueFromField.isFromField }) ) - if (!hasLayoutCue) { + if (!hasFieldCue) { return partDefinitions } @@ -726,7 +726,7 @@ function stripRedundantCues(partDefinitions: PartDefinition[], cueTypesToCheck: if (!cueTypesToCheck.includes(cue.type)) { return true } - return (cue as CueDefinitionFromLayout).isFromLayout + return (cue as CueDefinitionFromField).isFromField }) return { ...definition, diff --git a/src/tv2-common/inewsConversion/converters/ParseCue.ts b/src/tv2-common/inewsConversion/converters/ParseCue.ts index d644a540..95c7001a 100644 --- a/src/tv2-common/inewsConversion/converters/ParseCue.ts +++ b/src/tv2-common/inewsConversion/converters/ParseCue.ts @@ -115,22 +115,22 @@ export interface CueDefinitionUnpairedPilot extends CueDefinitionBase { engineNumber?: number } -export interface CueDefinitionBackgroundLoop extends CueDefinitionBase, CueDefinitionFromLayout { +export interface CueDefinitionBackgroundLoop extends CueDefinitionBase, CueDefinitionFromField { type: CueType.BackgroundLoop target: 'FULL' | 'DVE' backgroundLoop: string } -export interface CueDefinitionGraphicDesign extends CueDefinitionBase, CueDefinitionFromLayout { +export interface CueDefinitionGraphicDesign extends CueDefinitionBase, CueDefinitionFromField { type: CueType.GraphicDesign design: string } -export interface CueDefinitionFromLayout { - isFromLayout?: boolean +export interface CueDefinitionFromField { + isFromField?: boolean } -export interface CueDefinitionGraphicSchema extends CueDefinitionBase, CueDefinitionFromLayout { +export interface CueDefinitionGraphicSchema extends CueDefinitionBase, CueDefinitionFromField { type: CueType.GraphicSchema schema: string } @@ -264,7 +264,7 @@ export function ParseCue(cue: UnparsedCue, config: TV2ShowStyleConfig): CueDefin return parsePgmClean(cue) } else if (/^MINUSKAM\s*=/i.test(cue[0])) { return parseMixMinus(cue) - } else if (/^DESIGN_LAYOUT=/i.test(cue[0])) { + } else if (/^DESIGN_FIELD=/i.test(cue[0])) { return parseDesignLayout(cue, config) } else if (/^SCHEMA_FIELD=/i.test(cue[0])) { return parseSchemaLayout(cue, config) @@ -932,7 +932,7 @@ export function parseTime(line: string): Pick Date: Thu, 4 May 2023 09:17:35 +0200 Subject: [PATCH 03/35] SOF-1422 calculateTime() no longer retuns NaN when parsing time from frames --- .../__tests__/calculate-time.spec.ts | 101 ++++++++++++++++++ src/tv2-common/cueTiming.ts | 3 +- 2 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 src/tv2-common/__tests__/calculate-time.spec.ts diff --git a/src/tv2-common/__tests__/calculate-time.spec.ts b/src/tv2-common/__tests__/calculate-time.spec.ts new file mode 100644 index 00000000..321b57ae --- /dev/null +++ b/src/tv2-common/__tests__/calculate-time.spec.ts @@ -0,0 +1,101 @@ +import { calculateTime, CueTime } from 'tv2-common' + +describe('calculateTime', () => { + it('receives an infinite mode - returns undefined', () => { + const time: CueTime = { + infiniteMode: 'B' + } + const result = calculateTime(time) + expect(result).toBeUndefined() + }) + + it('receives empty time - returns 0', () => { + const time: CueTime = {} + const result = calculateTime(time) + expect(result).toBe(0) + }) + + it('receives time with 1 second - returns 1000', () => { + const time: CueTime = { + seconds: 1 + } + const result = calculateTime(time) + expect(result).toBe(1000) + }) + + it('receives time with 10 seconds - returns 10000', () => { + const time: CueTime = { + seconds: 10 + } + const result = calculateTime(time) + expect(result).toBe(10000) + }) + + it('receives time with 17 second - returns 17000', () => { + const time: CueTime = { + seconds: 17 + } + const result = calculateTime(time) + expect(result).toBe(17000) + }) + + it('receives time with 1 frame - returns 40', () => { + const time: CueTime = { + frames: 1 + } + const result = calculateTime(time) + expect(result).toBe(40) + }) + + it('receives time with 10 frames - returns 400', () => { + const time: CueTime = { + frames: 10 + } + const result = calculateTime(time) + expect(result).toBe(400) + }) + + it('receives time with 17 frames - returns 680', () => { + const time: CueTime = { + frames: 17 + } + const result = calculateTime(time) + expect(result).toBe(680) + }) + + it('receives both a infinite mode and seconds - returns undefined', () => { + const time: CueTime = { + infiniteMode: 'B', + seconds: 29 + } + const result = calculateTime(time) + expect(result).toBeUndefined() + }) + + it('receives both a infinite mode and frames - returns undefined', () => { + const time: CueTime = { + infiniteMode: 'B', + frames: 29 + } + const result = calculateTime(time) + expect(result).toBeUndefined() + }) + + it('receives 3 seconds and 4 frames - returns 3160', () => { + const time: CueTime = { + seconds: 3, + frames: 4 + } + const result = calculateTime(time) + expect(result).toBe(3160) + }) + + it('receives 4 seconds and 3 frames - returns 4120', () => { + const time: CueTime = { + seconds: 4, + frames: 3 + } + const result = calculateTime(time) + expect(result).toBe(4120) + }) +}) diff --git a/src/tv2-common/cueTiming.ts b/src/tv2-common/cueTiming.ts index d2378b95..b0e550bf 100644 --- a/src/tv2-common/cueTiming.ts +++ b/src/tv2-common/cueTiming.ts @@ -1,7 +1,8 @@ import { CueDefinition, CueTime } from './inewsConversion/converters/ParseCue' import { IBlueprintPiece, PieceLifespan } from 'blueprints-integration' -import { FRAME_RATE, TV2BlueprintConfigBase, TV2StudioConfigBase } from 'tv2-common' +import { TV2BlueprintConfigBase, TV2StudioConfigBase } from 'tv2-common' +import { FRAME_RATE } from './frameTime' const FRAME_TIME = 1000 / FRAME_RATE // TODO: This should be pulled from config. From 99d5d408185f803baa42fc88fd64faffb5ec868a Mon Sep 17 00:00:00 2001 From: Rasmus Lindved Date: Thu, 4 May 2023 14:16:13 +0200 Subject: [PATCH 04/35] SOF-1422 Design and Schema field cues are now parsed from iNews.fields instead of from the body. Delete no longer used code --- .../inewsConversion/converters/ParseBody.ts | 28 +- .../inewsConversion/converters/ParseCue.ts | 22 +- .../converters/__tests__/body-parser.spec.ts | 373 +++++++++++------- src/tv2-common/types/inews.ts | 1 + src/tv2_afvd_showstyle/__tests__/configs.ts | 4 +- 5 files changed, 258 insertions(+), 170 deletions(-) diff --git a/src/tv2-common/inewsConversion/converters/ParseBody.ts b/src/tv2-common/inewsConversion/converters/ParseBody.ts index 2f29a394..17d5cc0e 100644 --- a/src/tv2-common/inewsConversion/converters/ParseBody.ts +++ b/src/tv2-common/inewsConversion/converters/ParseBody.ts @@ -1,5 +1,8 @@ import { + createCueDefinitionGraphicDesign, + createCueDefinitionGraphicSchema, CueDefinitionFromField, + INewsFields, parseTransitionStyle, PostProcessDefinitions, TransitionStyle, @@ -186,7 +189,7 @@ export function ParseBody( segmentName: string, body: string, cues: UnparsedCue[], - fields: any, + fields: INewsFields, modified: number ): PartDefinition[] { let definitions: PartDefinition[] = [] @@ -361,11 +364,30 @@ export function ParseBody( partDefinition.cues = partDefinition.cues.filter((c) => c.type !== CueType.UNKNOWN) }) - definitions = stripRedundantCuesWhenLayoutCueIsPresent(definitions) + definitions[0]?.cues.push(...parseFieldsToCueDefinitions(fields, config)) + definitions = stripRedundantCuesWhenFieldCueIsPresent(definitions) return PostProcessDefinitions(definitions, segmentId) } +function parseFieldsToCueDefinitions(fields: INewsFields, config: TV2ShowStyleConfig): CueDefinition[] { + const cueDefinitions: CueDefinition[] = [] + if (fields.layout) { + const cueDefinitionGraphicDesign = createCueDefinitionGraphicDesign(fields.layout, config) + if (cueDefinitionGraphicDesign) { + cueDefinitions.push(cueDefinitionGraphicDesign) + } + } + + if (fields.skema) { + const cueDefinitionGraphicSchema = createCueDefinitionGraphicSchema(fields.skema, config) + if (cueDefinitionGraphicSchema) { + cueDefinitions.push(cueDefinitionGraphicSchema) + } + } + return cueDefinitions +} + export function FindTargetPair(partDefinition: PartDefinition): boolean { const index = partDefinition.cues.findIndex( (cue) => (cue.type === CueType.UNPAIRED_TARGET && cue.mergeable) || (cue.type === CueType.Telefon && !cue.graphic) @@ -697,7 +719,7 @@ export function isMinusMic(inputName: string): boolean { return /minus mic/i.test(inputName) } -export function stripRedundantCuesWhenLayoutCueIsPresent(partDefinitions: PartDefinition[]): PartDefinition[] { +export function stripRedundantCuesWhenFieldCueIsPresent(partDefinitions: PartDefinition[]): PartDefinition[] { return stripRedundantCuesForSchema(stripRedundantCuesForDesign(partDefinitions)) } diff --git a/src/tv2-common/inewsConversion/converters/ParseCue.ts b/src/tv2-common/inewsConversion/converters/ParseCue.ts index 95c7001a..b72e656e 100644 --- a/src/tv2-common/inewsConversion/converters/ParseCue.ts +++ b/src/tv2-common/inewsConversion/converters/ParseCue.ts @@ -264,10 +264,6 @@ export function ParseCue(cue: UnparsedCue, config: TV2ShowStyleConfig): CueDefin return parsePgmClean(cue) } else if (/^MINUSKAM\s*=/i.test(cue[0])) { return parseMixMinus(cue) - } else if (/^DESIGN_FIELD=/i.test(cue[0])) { - return parseDesignLayout(cue, config) - } else if (/^SCHEMA_FIELD=/i.test(cue[0])) { - return parseSchemaLayout(cue, config) } else if (/^ROBOT\s*=/i.test(cue[0])) { return parseRobotCue(cue) } @@ -931,10 +927,10 @@ export function parseTime(line: string): Pick({ type: CueType.GraphicDesign, design: designConfig.VizTemplate, - iNewsCommand: layout, start: { frames: 1 }, + iNewsCommand: '', isFromField: true }) } @@ -961,10 +957,10 @@ function findGraphicDesignConfiguration( ) } -function parseSchemaLayout(cue: string[], config: TV2ShowStyleConfig): CueDefinitionGraphicSchema | undefined { - const array = cue[0].split('SCHEMA_FIELD=') - const schema = array[1] - +export function createCueDefinitionGraphicSchema( + schema: string, + config: TV2ShowStyleConfig +): CueDefinitionGraphicSchema | undefined { const schemaConfiguration = findGraphicSchemaConfiguration(config, schema) if (!schemaConfiguration) { return undefined diff --git a/src/tv2-common/inewsConversion/converters/__tests__/body-parser.spec.ts b/src/tv2-common/inewsConversion/converters/__tests__/body-parser.spec.ts index 8aa7f8e8..58fa1e13 100644 --- a/src/tv2-common/inewsConversion/converters/__tests__/body-parser.spec.ts +++ b/src/tv2-common/inewsConversion/converters/__tests__/body-parser.spec.ts @@ -2,8 +2,9 @@ import { CueDefinitionBackgroundLoop, CueDefinitionGraphicDesign, getTransitionProperties, + INewsFields, PartdefinitionTypes, - stripRedundantCuesWhenLayoutCueIsPresent, + stripRedundantCuesWhenFieldCueIsPresent, TransitionStyle, UnparsedCue } from 'tv2-common' @@ -41,7 +42,7 @@ import { GraphicPilot } from '../ParseCue' -const fields = {} +const emptyFields = {} const unparsedUnknown: UnparsedCue = ['Some invalid cue'] @@ -200,7 +201,7 @@ describe('Body parser', () => { unparsedJingle3 ] - const result = ParseBody(config, '00000000001', 'test-segment', body1, cues1, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body1, cues1, emptyFields, 0) expect(stripExternalId(result)).toEqual( literal([ literal({ @@ -247,7 +248,7 @@ describe('Body parser', () => { '\r\n

\r\n

Thid id thr trext for the next DVE

\r\n

***LIVE***

\r\n

\r\n

\r\n

\r\n

Spib her

\r\n

\r\n\r\n

Script here

\r\n' const cues2 = [unparsedUnknown, unparsedGrafik1, null, unparsedGrafik3, unparsedEkstern1] - const result = ParseBody(config, '00000000001', 'test-segment', body2, cues2, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body2, cues2, emptyFields, 0) expect(stripExternalId(result)).toEqual( literal([ literal({ @@ -282,7 +283,7 @@ describe('Body parser', () => { '\r\n

\r\n

Thid id thr trext for the next DVE

\r\n

***LIVE***

\r\n

\r\n

\r\n

\r\n

Spib her

\r\n

\r\n\r\n

Script here

\r\n' const cues2 = [['DVE=MORBARN', 'INP1=Kam 1', 'INP2=Kam 2', 'BYNAVN=Live/Odense'], unparsedEkstern1, unparsedGrafik1] - const result = ParseBody(config, '00000000001', 'test-segment', body2, cues2, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body2, cues2, emptyFields, 0) expect(stripExternalId(result)).toEqual( literal([ literal({ @@ -339,7 +340,7 @@ describe('Body parser', () => { unparsedJingle3 ] - const result = ParseBody(config, '00000000001', 'test-segment', body3, cues3, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body3, cues3, emptyFields, 0) expect(stripExternalId(result)).toEqual( literal([ literal({ @@ -398,7 +399,7 @@ describe('Body parser', () => { const body4 = "\r\n

\r\n

\r\n

CAMERA 1

\r\n

Her står em masse tekst

\r\n" const cues4 = [unparsedUnknown] - const result = ParseBody(config, '00000000001', 'test-segment', body4, cues4, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body4, cues4, emptyFields, 0) expect(stripExternalId(result)).toEqual( literal([ literal({ @@ -408,7 +409,7 @@ describe('Body parser', () => { script: 'Her står em masse tekst\n', sourceDefinition: { sourceType: SourceType.KAM, id: '1', raw: 'CAMERA 1', minusMic: false, name: 'KAM 1' }, externalId: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -421,7 +422,7 @@ describe('Body parser', () => { const body5 = '\r\n

\r\n

\r\n

KAM 1

\r\n

--tlftopt-><--

\r\n

\r\n

\r\n

************ 100%GRAFIK ***********

\r\n

\r\n

\r\n

\r\n' const cues5 = [unparsedUnknown, unparsedGrafik1, unparsedGrafik2, unparsedGrafik3, unparsedEkstern1] - const result = ParseBody(config, '00000000001', 'test-segment', body5, cues5, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body5, cues5, emptyFields, 0) expect(stripExternalId(result)).toEqual( literal([ literal({ @@ -467,7 +468,7 @@ describe('Body parser', () => { const body6 = '\r\n

\r\n

\r\n

KAM 1

\r\n

--værter-><--

\r\n' const cues6 = [unparsedUnknown] - const result = ParseBody(config, '00000000001', 'test-segment', body6, cues6, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body6, cues6, emptyFields, 0) expect(stripExternalId(result)).toEqual( literal([ literal({ @@ -490,7 +491,7 @@ describe('Body parser', () => { const body7 = '\r\n

\r\n

\r\n

\r\n

***ATTACK***

\r\n

----ss3 Sport LOOP-><-

\r\n

---AR DIGI OUT-><---

\r\n

---bundter herunder--->

\r\n

\r\n

\r\n

\r\n

SLUTORD:... wauw

\r\n

\r\n

KAM 4

\r\n

NEDLÆG

\r\n

Long script. Long script. Long script. Long script. Long script. Long script. Long script. Long script. Long script. Long script. Long script. Long script.

\r\n

\r\n' const cues7 = [unparsedUnknown, unparsedGrafik1, unparsedGrafik2, unparsedGrafik3] - const result = ParseBody(config, '00000000001', 'test-segment', body7, cues7, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body7, cues7, emptyFields, 0) expect(stripExternalId(result)).toEqual( literal([ literal({ @@ -526,7 +527,7 @@ describe('Body parser', () => { const body8 = '\r\n

COMMENT OUTSIDE!!

\r\n

KAM 2

\r\n

KADA

\r\n

\r\n

\r\n

Efter "BYNAVN=" og efter "#kg direkte"

\r\n

\r\n

Kilde til optagelse på select-feed.

\r\n

\r\n

Some script

\r\n

\r\n

***LIVE***

\r\n

Some script

\r\n

\r\n

\r\n

- Bullet 1?

\r\n

\r\n

- Bullet 2?

\r\n

\r\n

- Bullet 3?

\r\n

\r\n' const cues8 = [unparsedUnknown, unparsedGrafik1, unparsedGrafik2, unparsedGrafik3, unparsedEkstern1] - const result = ParseBody(config, '00000000001', 'test-segment', body8, cues8, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body8, cues8, emptyFields, 0) expect(stripExternalId(result)).toEqual( literal([ literal({ @@ -561,7 +562,7 @@ describe('Body parser', () => { const body9 = '\r\n

COMMENT OUTSIDE!!

\r\n

KAM 2

\r\n

\r\n

\r\n

Efter "BYNAVN=" og efter "#kg direkte"

\r\n

\r\n

Kilde til optagelse på select-feed.

\r\n

\r\n

Some script.

\r\n

\r\n

Some more script with "a quote"

\r\n

\r\n

Yet more script, this time it\'s a question?

\r\n

\r\n

***LIVE***

\r\n

More commentary

\r\n

\r\n

\r\n

Danmark?

\r\n

\r\n

Grønland en "absurd diskussion"?

\r\n

\r\n

\r\n' const cues9 = [unparsedUnknown, unparsedGrafik1, unparsedGrafik2, unparsedGrafik3, unparsedEkstern1] - const result = ParseBody(config, '00000000001', 'test-segment', body9, cues9, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body9, cues9, emptyFields, 0) expect(stripExternalId(result)).toEqual( literal([ literal({ @@ -596,7 +597,7 @@ describe('Body parser', () => { const body10 = '\r\n

KAM 2

\r\n

\r\n

\r\n

Efter "BYNAVN=" og efter "#kg direkte"

\r\n

\r\n

Kilde til optagelse på select-feed.

\r\n

\r\n

Question?

\r\n

\r\n

Question, but in PI tags?

\r\n

\r\n

USA og Danmark?

\r\n

\r\n

***LIVE***

\r\n

Comment

\r\n

\r\n

This line should be ignored

\r\n

\r\n

Also this one?

\r\n

\r\n

More comments

\r\n

Even more?

\r\n

\r\n' const cues10 = [unparsedUnknown, unparsedGrafik1, unparsedGrafik2, unparsedGrafik3, unparsedEkstern1] - const result = ParseBody(config, '00000000001', 'test-segment', body10, cues10, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body10, cues10, emptyFields, 0) expect(stripExternalId(result)).toEqual( literal([ literal({ @@ -631,7 +632,7 @@ describe('Body parser', () => { const body11 = '\r\n

KAM 1

\r\n

\r\n

Some script.

\r\n

\r\n

***VO***

\r\n

\r\n

SB: Say this over this clip (10 sek)

\r\n

\r\n

More script.

\r\n

\r\n

Even more

\r\n

\r\n

More script again.

\r\n

\r\n

Couple of comments

\r\n

Should be ignored

\r\n

\r\n' const cues11 = [unparsedUnknown, unparsedGrafik1, unparsedGrafik2] - const result = ParseBody(config, '00000000001', 'test-segment', body11, cues11, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body11, cues11, emptyFields, 0) expect(stripExternalId(result)).toEqual( literal([ literal({ @@ -665,7 +666,7 @@ describe('Body parser', () => { const body11 = '\r\n

KAM 1

\r\n

\r\n

Some script.

\r\n

\r\n

***VOV***

\r\n

\r\n

SB: Say this over this clip (10 sek)

\r\n

\r\n

More script.

\r\n

\r\n

Even more

\r\n

\r\n

More script again.

\r\n

\r\n

Couple of comments

\r\n

Should be ignored

\r\n

\r\n' const cues11 = [unparsedUnknown, unparsedGrafik1, unparsedGrafik2] - const result = ParseBody(config, '00000000001', 'test-segment', body11, cues11, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body11, cues11, emptyFields, 0) expect(stripExternalId(result)).toEqual( literal([ literal({ @@ -699,7 +700,7 @@ describe('Body parser', () => { const body12 = '\r\n

This is an interview.

\r\n

\r\n

\r\n

KAM 3

\r\n

\r\n

<-- Comment about this

\r\n

\r\n

Also about this!

\r\n

\r\n

Remember:

\r\n

\r\n

Here is our correspondant.

\r\n

\r\n

What\'s going on over there?

\r\n

\r\n

***LIVE***

\r\n

There is a graphic in this part

\r\n

.

\r\n

\r\n

Ask a question?

\r\n

\r\n

Ask another?

\r\n

\r\n

What\'s the reaction?

\r\n

\r\n

\r\n

\r\n' const cues12 = [unparsedUnknown, unparsedGrafik1, unparsedGrafik2] - const result = ParseBody(config, '00000000001', 'test-segment', body12, cues12, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body12, cues12, emptyFields, 0) expect(stripExternalId(result)).toEqual( literal([ literal({ @@ -722,7 +723,7 @@ describe('Body parser', () => { const body13 = '\r\n

\r\n

\r\n

\r\n

\r\n

\r\n

\r\n

\r\n

\r\n

\r\n

\r\n

\r\n

\r\n

\r\n

\r\n

\r\n' const cues13 = [unparsedUnknown] - const result = ParseBody(config, '00000000001', 'test-segment', body13, cues13, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body13, cues13, emptyFields, 0) expect(stripExternalId(result)).toEqual(literal([])) }) @@ -737,7 +738,7 @@ describe('Body parser', () => { unparsedEkstern1, unparsedEkstern2 ] - const result = ParseBody(config, '00000000001', 'test-segment', body14, cues14, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body14, cues14, emptyFields, 0) expect(stripExternalId(result)).toEqual( literal([ literal({ @@ -783,7 +784,7 @@ describe('Body parser', () => { const body15 = '\r\n

---JINGLE sport grafisk intro---><----

\r\n

\r\n

---AUDIO til grafisk intro , fortsætter under teasere---><----

\r\n

\r\n' const cues15 = [unparsedUnknown, unparsedGrafik1] - const result = ParseBody(config, '00000000001', 'INTRO', body15, cues15, fields, 0) + const result = ParseBody(config, '00000000001', 'INTRO', body15, cues15, emptyFields, 0) expect(stripExternalId(result)).toEqual( literal([ literal({ @@ -813,7 +814,7 @@ describe('Body parser', () => { unparsedJingle2, unparsedJingle3 ] - const result = ParseBody(config, '00000000001', 'test-segment', body16, cues16, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body16, cues16, emptyFields, 0) expect(stripExternalId(result)).toEqual( literal([ literal({ @@ -823,7 +824,7 @@ describe('Body parser', () => { script: 'Hallo, I wnat to tell you......\n', sourceDefinition: SOURCE_DEFINITION_KAM_2, cues: [cueGrafik1], - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -834,7 +835,7 @@ describe('Body parser', () => { rawType: 'SERVER', script: '', cues: [cueGrafik2, cueGrafik3, cueJingle1, cueJingle2, cueJingle3], - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -846,7 +847,7 @@ describe('Body parser', () => { script: '', sourceDefinition: SOURCE_DEFINITION_KAM_2, cues: [], - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', endWords: '', @@ -859,7 +860,7 @@ describe('Body parser', () => { script: '', sourceDefinition: SOURCE_DEFINITION_KAM_2, cues: [], - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -884,7 +885,7 @@ describe('Body parser', () => { unparsedTelefon1, unparsedTelefon2 ] - const result = ParseBody(config, '00000000001', 'test-segment', body17, cues17, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body17, cues17, emptyFields, 0) expect(stripExternalId(result)).toEqual( literal([ literal({ @@ -894,7 +895,7 @@ describe('Body parser', () => { cues: [cueEkstern1], title: 'LIVE 1', script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -906,7 +907,7 @@ describe('Body parser', () => { cues: [cueEkstern2], title: 'LIVE 2', script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -918,7 +919,7 @@ describe('Body parser', () => { rawType: 'KAM 1', cues: [], script: 'Single line of script\n', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -929,7 +930,7 @@ describe('Body parser', () => { rawType: 'SERVER', cues: [cueGrafik2, cueGrafik3, cueJingle1, cueJingle2, cueJingle3], script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -940,7 +941,7 @@ describe('Body parser', () => { rawType: '', cues: [cueTelefon1], script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -951,7 +952,7 @@ describe('Body parser', () => { rawType: '', cues: [cueTelefon2], script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', endWords: 'Skarpere regler.', @@ -964,7 +965,7 @@ describe('Body parser', () => { rawType: 'KAM 2', cues: [], script: 'And some script.\n', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -977,7 +978,7 @@ describe('Body parser', () => { const body18 = '\r\n

***VO EFFEKT 0***

\r\n

\r\n

With some script.

\r\n

\r\n' const cues18 = [unparsedGrafik1] - const result = ParseBody(config, '00000000001', 'test-segment', body18, cues18, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body18, cues18, emptyFields, 0) expect(stripExternalId(result)).toEqual( literal([ literal({ @@ -987,7 +988,7 @@ describe('Body parser', () => { rawType: 'VO', cues: [cueGrafik1], script: 'With some script.\n', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -1000,7 +1001,7 @@ describe('Body parser', () => { const body19 = '\r\n

\r\n

KAM 1 EFFEKT 1

\r\n

Dette er takst

\r\n

\r\n

SERVER

\r\n

\r\n

\r\n

STORT BILLEDE AF STUDIE

\r\n

\r\n' const cues19 = [unparsedGrafik1, unparsedGrafik2] - const result = ParseBody(config, '00000000001', 'test-segment', body19, cues19, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body19, cues19, emptyFields, 0) expect(stripExternalId(result)).toEqual( literal([ literal({ @@ -1011,7 +1012,7 @@ describe('Body parser', () => { rawType: 'KAM 1', cues: [], script: 'Dette er takst\n', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -1022,7 +1023,7 @@ describe('Body parser', () => { rawType: 'SERVER', cues: [cueGrafik1, cueGrafik2], script: 'STORT BILLEDE AF STUDIE\n', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -1035,7 +1036,7 @@ describe('Body parser', () => { const body20 = '\r\n

OBS: der skal være 2 primære templates mellem 2 breakere

\r\n

K2 NBA18_LEAD_OUT

\r\n

\r\n

\r\n

\r\n

\r\n' const cues20 = [unparsedJingle1] - const result = ParseBody(config, '00000000001', 'test-segment', body20, cues20, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body20, cues20, emptyFields, 0) expect(stripExternalId(result)).toEqual( literal([ literal({ @@ -1045,7 +1046,7 @@ describe('Body parser', () => { cues: [cueJingle1], title: '1', script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -1066,7 +1067,7 @@ describe('Body parser', () => { ['kg bund TEXT MORETEXT', 'some@email.fakeTLD', ';x.xx'], ['SS=3-NYH-19-LOOP', ';0.01'] ] - const result = ParseBody(config, '00000000001', 'test-segment', body21, cues21, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body21, cues21, emptyFields, 0) expect(stripExternalId(result)).toEqual( literal([ literal({ @@ -1076,7 +1077,7 @@ describe('Body parser', () => { rawType: 'KAM 2', cues: [], script: 'Hallo, I wnat to tell you......\nHEREEEELLLLOOOK\nYES\n', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -1132,7 +1133,7 @@ describe('Body parser', () => { }) ]), script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -1144,7 +1145,7 @@ describe('Body parser', () => { rawType: 'KAM 1', cues: [], script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -1156,7 +1157,7 @@ describe('Body parser', () => { rawType: 'KAM 1', cues: [], script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', endWords: '', @@ -1169,7 +1170,7 @@ describe('Body parser', () => { rawType: 'KAM 1', cues: [], script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -1186,7 +1187,7 @@ describe('Body parser', () => { ['kg tlfdirekte Odense', ';0.00-S'], ['kg tlftoptlive', 'TEXT MORETEXT', 'place', ';0.00-S'] ] - const result = ParseBody(config, '00000000001', 'test-segment', body22, cues22, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body22, cues22, emptyFields, 0) expect(stripExternalId(result)).toEqual( literal([ literal({ @@ -1196,7 +1197,7 @@ describe('Body parser', () => { rawType: 'KAM 1', cues: [], script: 'Skriv spib her\n', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -1261,7 +1262,7 @@ describe('Body parser', () => { }) ], script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -1278,7 +1279,7 @@ describe('Body parser', () => { ['kg tlfdirekte Odense', ';0.00-S'], ['kg tlftoptlive', 'TEXT MORETEXT', 'Place', ';0.00-S'] ] - const result = ParseBody(config, '00000000001', 'test-segment', body22, cues22, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body22, cues22, emptyFields, 0) expect(stripExternalId(result)).toEqual( literal([ literal({ @@ -1292,7 +1293,7 @@ describe('Body parser', () => { rawType: 'KAM 1', cues: [], script: 'Skriv spib her\n', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -1357,7 +1358,7 @@ describe('Body parser', () => { }) ], script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -1370,7 +1371,7 @@ describe('Body parser', () => { const body18 = '\r\n

***VOSB EFFEKT 0***

\r\n

\r\n

Some script.

\r\n

\r\n' const cues18 = [unparsedGrafik1] - const result = ParseBody(config, '00000000001', 'test-segment', body18, cues18, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body18, cues18, emptyFields, 0) expect(stripExternalId(result)).toEqual( literal([ literal({ @@ -1380,7 +1381,7 @@ describe('Body parser', () => { rawType: 'VOSB', cues: [cueGrafik1], script: 'Some script.\n', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -1393,7 +1394,7 @@ describe('Body parser', () => { const body18 = '\r\n

***VOSB EFFEKT 0***

\r\n

\r\n

Some script here, possibly a note to the presenter

\r\n

Some script.

\r\n

\r\n' const cues18 = [unparsedGrafik1] - const result = ParseBody(config, '00000000001', 'test-segment', body18, cues18, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body18, cues18, emptyFields, 0) expect(stripExternalId(result)).toEqual( literal([ literal({ @@ -1403,7 +1404,7 @@ describe('Body parser', () => { rawType: 'VOSB', cues: [cueGrafik1], script: 'Some script.\n', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -1429,7 +1430,7 @@ describe('Body parser', () => { 'TELEFON/KORT//LIVE_KABUL' ] ] - const result = ParseBody(config, '00000000001', 'test-segment', body26, cues26, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body26, cues26, emptyFields, 0) expect(stripExternalId(result)).toEqual([ literal({ externalId: '', @@ -1452,7 +1453,7 @@ describe('Body parser', () => { iNewsCommand: 'kg' }) ], - fields, + fields: emptyFields, modified: 0, script: '', storyName: 'test-segment', @@ -1493,7 +1494,7 @@ describe('Body parser', () => { }) ], title: 'TELEFON/KORT//LIVE_KABUL', - fields, + fields: emptyFields, modified: 0, script: '', storyName: 'test-segment', @@ -1506,7 +1507,7 @@ describe('Body parser', () => { const body27 = '\r\n

\r\n

\r\n

EVS 1

\r\n

\r\n

\r\n

\r\n

\r\n

\r\n

\r\n

\r\n

Skriv din spib her

\r\n

\r\n' const cues27 = [unparsedGrafik1, unparsedGrafik2, unparsedGrafik3] - const result = ParseBody(config, '00000000001', 'test-segment', body27, cues27, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body27, cues27, emptyFields, 0) expect(stripExternalId(result)).toEqual([ literal({ externalId: '', @@ -1520,7 +1521,7 @@ describe('Body parser', () => { vo: false }, cues: [cueGrafik1, cueGrafik2, cueGrafik3], - fields, + fields: emptyFields, modified: 0, script: 'Skriv din spib her\n', storyName: 'test-segment', @@ -1533,7 +1534,7 @@ describe('Body parser', () => { const body27 = '\r\n

\r\n

\r\n

EVS1VOV

\r\n

\r\n

\r\n

\r\n

\r\n

\r\n

\r\n

\r\n

Skriv din spib her

\r\n

\r\n' const cues27 = [unparsedGrafik1, unparsedGrafik2, unparsedGrafik3] - const result = ParseBody(config, '00000000001', 'test-segment', body27, cues27, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body27, cues27, emptyFields, 0) expect(stripExternalId(result)).toEqual([ literal({ externalId: '', @@ -1547,7 +1548,7 @@ describe('Body parser', () => { vo: true }, cues: [cueGrafik1, cueGrafik2, cueGrafik3], - fields, + fields: emptyFields, modified: 0, script: 'Skriv din spib her\n', storyName: 'test-segment', @@ -1559,7 +1560,7 @@ describe('Body parser', () => { test('test 27c: accepts spaces in EVS VO red text', () => { const body27 = '\r\n

EVS 1 VO

\r\n

EVS 2VO

\r\n

EVS3VO

\r\n

EVS4 VO

\r\n' - const result = ParseBody(config, '00000000001', 'test-segment', body27, [], fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body27, [], emptyFields, 0) expect(stripExternalId(result)).toEqual([ literal({ externalId: '', @@ -1573,7 +1574,7 @@ describe('Body parser', () => { vo: true }, cues: [], - fields, + fields: emptyFields, modified: 0, script: '', storyName: 'test-segment', @@ -1591,7 +1592,7 @@ describe('Body parser', () => { vo: true }, cues: [], - fields, + fields: emptyFields, modified: 0, script: '', storyName: 'test-segment', @@ -1609,7 +1610,7 @@ describe('Body parser', () => { vo: true }, cues: [], - fields, + fields: emptyFields, modified: 0, script: '', storyName: 'test-segment', @@ -1627,7 +1628,7 @@ describe('Body parser', () => { vo: true }, cues: [], - fields, + fields: emptyFields, modified: 0, script: '', storyName: 'test-segment', @@ -1646,7 +1647,7 @@ describe('Body parser', () => { ['DVE=SOMMERFUGL', 'INP1=KAM 1', 'INP2=LIVE 2', 'BYNAVN=Rodovre'], ['EKSTERN=LIVE 2'] ] - const result = ParseBody(config, '00000000001', 'test-segment', body28, cues28, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body28, cues28, emptyFields, 0) expect(stripExternalId(result)).toEqual([ literal({ externalId: '', @@ -1684,7 +1685,7 @@ describe('Body parser', () => { }) ], script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', endWords: 'bare mega fedt', @@ -1708,7 +1709,7 @@ describe('Body parser', () => { ], title: 'SOMMERFUGL', script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -1720,7 +1721,7 @@ describe('Body parser', () => { cues: [cueEkstern2], title: 'LIVE 2', script: 'Some Script here\n', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -1744,7 +1745,7 @@ describe('Body parser', () => { unparsedTelefon1, unparsedTelefon2 ] - const result = ParseBody(config, '00000000001', 'test-segment', body29, cues29, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body29, cues29, emptyFields, 0) expect(stripExternalId(result)).toEqual( literal([ literal({ @@ -1754,7 +1755,7 @@ describe('Body parser', () => { cues: [cueEkstern1], title: 'LIVE 1', script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -1766,7 +1767,7 @@ describe('Body parser', () => { cues: [cueEkstern2], title: 'LIVE 2', script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -1778,7 +1779,7 @@ describe('Body parser', () => { rawType: 'KAM 1', cues: [], script: 'Some script.\n', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -1789,7 +1790,7 @@ describe('Body parser', () => { rawType: 'SERVER', cues: [cueGrafik2, cueGrafik3, cueJingle1, cueJingle2, cueJingle3], script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -1800,7 +1801,7 @@ describe('Body parser', () => { rawType: '', cues: [cueTelefon1], script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -1811,7 +1812,7 @@ describe('Body parser', () => { rawType: '', cues: [cueTelefon2], script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', endWords: 'Skarpere regler.', @@ -1824,7 +1825,7 @@ describe('Body parser', () => { rawType: 'KAM 2', cues: [], script: 'Some script.\n', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -1843,7 +1844,7 @@ describe('Body parser', () => { ['TEMA=sport_kortnyt', 'TEMA SPORT KORT NYT', ';0.00-S'], ['#kg bund TEXT MORETEXT', 'Triatlet', ';0.00'] ] - const result = ParseBody(config, '00000000001', 'test-segment', body30, cues30, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body30, cues30, emptyFields, 0) expect(stripExternalId(result)).toEqual([ literal({ externalId: '', @@ -1863,7 +1864,7 @@ describe('Body parser', () => { ], title: 'SOMMERFUGL', script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -1875,7 +1876,7 @@ describe('Body parser', () => { cues: [cueEkstern2], title: 'LIVE 2', script: 'And some script\n', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -1916,7 +1917,7 @@ describe('Body parser', () => { }) ], script: 'Server script\n', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', endWords: 'bare mega fedt', @@ -1935,7 +1936,7 @@ describe('Body parser', () => { ['DVE=SOMMERFUGL', 'INP1=KAM 1', 'INP2=LIVE 2', 'BYNAVN=Rodovre'], ['EKSTERN=LIVE 2'] ] - const result = ParseBody(config, '00000000001', 'test-segment', body31, cues31, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body31, cues31, emptyFields, 0) expect(stripExternalId(result)).toEqual([ literal({ externalId: '', @@ -1974,7 +1975,7 @@ describe('Body parser', () => { }) ], script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', endWords: 'bare mega fedt', @@ -1998,7 +1999,7 @@ describe('Body parser', () => { ], title: 'SOMMERFUGL', script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -2010,7 +2011,7 @@ describe('Body parser', () => { cues: [cueEkstern2], script: 'Some script\n', title: 'LIVE 2', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -2021,7 +2022,7 @@ describe('Body parser', () => { rawType: 'SERVER', cues: [], script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -2032,7 +2033,7 @@ describe('Body parser', () => { test('test 32', () => { const body32 = '\r\n

\r\n

KAM1

\r\n' const cues32: string[][] = [] - const result = ParseBody(config, '00000000001', 'test-segment', body32, cues32, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body32, cues32, emptyFields, 0) expect(stripExternalId(result)).toEqual([ literal({ externalId: '', @@ -2041,7 +2042,7 @@ describe('Body parser', () => { rawType: 'KAM1', cues: [], script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -2063,7 +2064,7 @@ describe('Body parser', () => { 'TEMA_SPORT_KORTNYT/Mosart=L|00:02|O' ] ] - const result = ParseBody(config, '00000000001', 'test-segment', body33, cues33, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body33, cues33, emptyFields, 0) expect(stripExternalId(result)).toEqual([ literal({ externalId: '', @@ -2111,7 +2112,7 @@ describe('Body parser', () => { ], title: 'SN_breaker_kortnyt_start', script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -2131,7 +2132,7 @@ describe('Body parser', () => { 'HojreVideo/12-12-2019/MOSART=L|00:00|O' ] ] - const result = ParseBody(config, '00000000001', 'test-segment', body34, cues34, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body34, cues34, emptyFields, 0) expect(stripExternalId(result)).toEqual([ literal({ externalId: '', @@ -2173,7 +2174,7 @@ describe('Body parser', () => { }) ], script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -2196,7 +2197,7 @@ describe('Body parser', () => { 'PROFILE/MEST BRUGTE STARTERE I NBA/08-12-2019' ] ] - const result = ParseBody(config, '00000000001', 'test-segment', body35, cues35, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body35, cues35, emptyFields, 0) expect(stripExternalId(result)).toEqual([ literal({ type: PartType.Grafik, @@ -2232,7 +2233,7 @@ describe('Body parser', () => { const body36 = '\r\n

KAM 1

\r\n

Kam 1 script

\r\n

***SERVER***

\r\n

Server script

\r\n

KAM 2

\r\n

KAM 2 script

\r\n' const cues36: string[][] = [] - const result = ParseBody(config, '00000000001', 'test-segment', body36, cues36, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body36, cues36, emptyFields, 0) expect(stripExternalId(result)).toEqual([ literal({ type: PartType.Kam, @@ -2276,7 +2277,7 @@ describe('Body parser', () => { const body36 = '\r\n

KAM 1

\r\n

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis

\r\n

\r\n

\r\n

\r\n

\r\n

KAM 2

\r\n' const cues36 = [['EKSTERN=LIVE 1']] - const result = ParseBody(config, '00000000001', 'test-segment', body36, cues36, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body36, cues36, emptyFields, 0) expect(stripExternalId(result)).toEqual([ literal({ type: PartType.Kam, @@ -2339,14 +2340,14 @@ describe('Body parser', () => { 'News/Citat/ARFG/LIVE/stoppoints_3' ] ] - const result = ParseBody(config, '00000000001', 'test-segment', body38, cues38, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body38, cues38, emptyFields, 0) expect(stripExternalId(result)).toEqual([ literal({ externalId: '', type: PartType.Kam, sourceDefinition: SOURCE_DEFINITION_KAM_1, rawType: 'KAM 1', - fields, + fields: emptyFields, modified: 0, script: '', storyName: 'test-segment', @@ -2391,7 +2392,7 @@ describe('Body parser', () => { } }) ], - fields, + fields: emptyFields, modified: 0, script: '', storyName: 'test-segment', @@ -2406,7 +2407,7 @@ describe('Body parser', () => { ['GRAFIK=FULL', 'INP1=', 'INP='], ['#cg4 pilotdata', 'TELEFON/KORT//LIVE_KABUL', 'VCPID=2552305', 'ContinueCount=3', 'TELEFON/KORT//LIVE_KABU'] ] - const result = ParseBody(config, '00000000001', 'test-segment', bodyTarget, cuesTarget, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', bodyTarget, cuesTarget, emptyFields, 0) expect(stripExternalId(result)).toEqual( literal([ literal({ @@ -2439,7 +2440,7 @@ describe('Body parser', () => { ], title: 'TELEFON/KORT//LIVE_KABUL', script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -2471,7 +2472,7 @@ describe('Body parser', () => { 'Senderplan/23-10-2019' ] ] - const result = ParseBody(config, '00000000001', 'test-segment', bodyTarget, cuesTarget, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', bodyTarget, cuesTarget, emptyFields, 0) expect(stripExternalId(result)).toEqual( literal([ literal({ @@ -2502,7 +2503,7 @@ describe('Body parser', () => { ], title: 'Senderplan/23-10-2019', script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -2535,7 +2536,7 @@ describe('Body parser', () => { ], title: 'Senderplan/23-10-2019', script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -2550,7 +2551,7 @@ describe('Body parser', () => { ['SS=sc-stills', 'INP1=EVS 1', ';0.00.01'], ['#cg4 pilotdata', 'TELEFON/KORT//LIVE_KABUL', 'VCPID=2552305', 'ContinueCount=3', 'TELEFON/KORT//LIVE_KABU'] ] - const result = ParseBody(config, '00000000001', 'test-segment', bodyTarget, cuesTarget, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', bodyTarget, cuesTarget, emptyFields, 0) expect(stripExternalId(result)).toEqual( literal([ literal({ @@ -2582,7 +2583,7 @@ describe('Body parser', () => { }) ], script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -2595,7 +2596,7 @@ describe('Body parser', () => { const body27 = '\r\n

\r\n

\r\n

EVS 1 EFFEKT 1

\r\n

\r\n

\r\n

\r\n

\r\n

\r\n

\r\n

\r\n

Skriv din spib her

\r\n

\r\n' const cues27 = [unparsedGrafik1, unparsedGrafik2, unparsedGrafik3] - const result = ParseBody(config, '00000000001', 'test-segment', body27, cues27, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body27, cues27, emptyFields, 0) expect(stripExternalId(result)).toEqual([ literal({ externalId: '', @@ -2610,7 +2611,7 @@ describe('Body parser', () => { }, effekt: 1, cues: [cueGrafik1, cueGrafik2, cueGrafik3], - fields, + fields: emptyFields, modified: 0, script: 'Skriv din spib her\n', storyName: 'test-segment', @@ -2622,7 +2623,7 @@ describe('Body parser', () => { test('EKSTERN 1 with EFFEKT', () => { const body = '\r\n

***LIVE***

\r\n

\r\n' const cues = [['EKSTERN=LIVE 1 EFFEKT 1']] - const result = ParseBody(config, '00000000001', 'test-segment', body, cues, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body, cues, emptyFields, 0) expect(stripExternalId(result)).toEqual([ literal({ externalId: '', @@ -2631,7 +2632,7 @@ describe('Body parser', () => { rawType: '', effekt: 1, cues: [cueEkstern1], - fields, + fields: emptyFields, modified: 0, script: '', storyName: 'test-segment', @@ -2655,7 +2656,7 @@ describe('Body parser', () => { 'LgfxWeb/-ETKAEM_07-05-2019_17:55:42' ] ] - const result = ParseBody(config, '00000000001', 'test-segment', body, cues, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body, cues, emptyFields, 0) expect(stripExternalId(result)).toEqual([ literal({ type: PartType.Kam, @@ -2679,7 +2680,7 @@ describe('Body parser', () => { ], rawType: 'KAM 1', script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -2700,7 +2701,7 @@ describe('Body parser', () => { 'LgfxWeb/-ETKAEM_07-05-2019_17:55:42' ] ] - const result = ParseBody(config, '00000000001', 'test-segment', body, cues, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body, cues, emptyFields, 0) expect(stripExternalId(result)).toEqual([ literal({ type: PartType.Kam, @@ -2752,7 +2753,7 @@ describe('Body parser', () => { 'LgfxWeb/-ETKAEM_07-05-2019_17:55:42' ] ] - const result = ParseBody(config, '00000000001', 'test-segment', body, cues, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body, cues, emptyFields, 0) expect(stripExternalId(result)).toEqual([ literal({ type: PartType.Kam, @@ -2798,7 +2799,7 @@ describe('Body parser', () => { 'LgfxWeb/-ETKAEM_07-05-2019_17:55:42' ] ] - const result = ParseBody(config, '00000000001', 'test-segment', body, cues, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body, cues, emptyFields, 0) expect(stripExternalId(result)).toEqual([ literal({ type: PartType.Kam, @@ -2807,7 +2808,7 @@ describe('Body parser', () => { cues: [], rawType: 'KAM 1', script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -2834,7 +2835,7 @@ describe('Body parser', () => { title: 'LgfxWeb/-ETKAEM_07-05-2019_17:55:42', rawType: '', script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -2856,7 +2857,7 @@ describe('Body parser', () => { 'LgfxWeb/-ETKAEM_07-05-2019_17:55:42' ] ] - const result = ParseBody(config, '00000000001', 'test-segment', body, cues, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body, cues, emptyFields, 0) expect(stripExternalId(result)).toEqual([ literal({ type: PartType.Kam, @@ -2865,7 +2866,7 @@ describe('Body parser', () => { cues: [], rawType: 'KAM 1', script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -2892,7 +2893,7 @@ describe('Body parser', () => { ], rawType: '100%GRAFIK', script: 'Some script...\n', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -2914,7 +2915,7 @@ describe('Body parser', () => { 'LgfxWeb/-ETKAEM_07-05-2019_17:55:42' ] ] - const result = ParseBody(config, '00000000001', 'test-segment', body, cues, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body, cues, emptyFields, 0) expect(stripExternalId(result)).toEqual([ literal({ type: PartType.Kam, @@ -2923,7 +2924,7 @@ describe('Body parser', () => { cues: [], rawType: 'KAM 1', script: 'Some script...\n', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -2941,7 +2942,7 @@ describe('Body parser', () => { ], rawType: '', script: 'Some script 1...\n', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -2963,7 +2964,7 @@ describe('Body parser', () => { ], rawType: '100%GRAFIK', script: 'Some script 2...\n', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -2984,7 +2985,7 @@ describe('Body parser', () => { 'LgfxWeb/-ETKAEM_07-05-2019_17:55:42' ] ] - const result = ParseBody(config, '00000000001', 'test-segment', body, cues, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body, cues, emptyFields, 0) expect(stripExternalId(result)).toEqual([ literal({ type: PartType.Kam, @@ -3008,7 +3009,7 @@ describe('Body parser', () => { ], rawType: 'KAM 1', script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -3029,7 +3030,7 @@ describe('Body parser', () => { 'LgfxWeb/-ETKAEM_07-05-2019_17:55:42' ] ] - const result = ParseBody(config, '00000000001', 'test-segment', body, cues, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body, cues, emptyFields, 0) expect(stripExternalId(result)).toEqual([ literal({ type: PartType.Kam, @@ -3053,7 +3054,7 @@ describe('Body parser', () => { ], rawType: 'KAM 1', script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -3074,7 +3075,7 @@ describe('Body parser', () => { 'LgfxWeb/-ETKAEM_07-05-2019_17:55:42/MOSART=L|00:00|O' ] ] - const result = ParseBody(config, '00000000001', 'test-segment', body, cues, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body, cues, emptyFields, 0) expect(stripExternalId(result)).toEqual([ literal({ type: PartType.Kam, @@ -3083,7 +3084,7 @@ describe('Body parser', () => { cues: [], rawType: 'KAM 1', script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -3118,7 +3119,7 @@ describe('Body parser', () => { ], rawType: '', script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -3139,7 +3140,7 @@ describe('Body parser', () => { 'LgfxWeb/-ETKAEM_07-05-2019_17:55:42/MOSART=W|00:00|O' ] ] - const result = ParseBody(config, '00000000001', 'test-segment', body, cues, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body, cues, emptyFields, 0) expect(stripExternalId(result)).toEqual([ literal({ type: PartType.Kam, @@ -3148,7 +3149,7 @@ describe('Body parser', () => { cues: [], rawType: 'KAM 1', script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -3183,7 +3184,7 @@ describe('Body parser', () => { ], rawType: '', script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -3204,7 +3205,7 @@ describe('Body parser', () => { 'LgfxWeb/-ETKAEM_07-05-2019_17:55:42/MOSART=F|00:00|O' ] ] - const result = ParseBody(config, '00000000001', 'test-segment', body, cues, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body, cues, emptyFields, 0) expect(stripExternalId(result)).toEqual([ literal({ type: PartType.Kam, @@ -3213,7 +3214,7 @@ describe('Body parser', () => { cues: [], rawType: 'KAM 1', script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -3231,7 +3232,7 @@ describe('Body parser', () => { ], rawType: '', script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -3261,7 +3262,7 @@ describe('Body parser', () => { title: 'LgfxWeb/-ETKAEM_07-05-2019_17:55:42', rawType: '', script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -3285,7 +3286,7 @@ describe('Body parser', () => { 'SP-H/Fakta/EM HÅNDBOLD' ] ] - const result = ParseBody(config, '00000000001', 'test-segment', body, cues, fields, 0) + const result = ParseBody(config, '00000000001', 'test-segment', body, cues, emptyFields, 0) expect(stripExternalId(result)).toEqual([ literal({ type: PartType.Kam, @@ -3310,7 +3311,7 @@ describe('Body parser', () => { ], rawType: 'KAM 1', script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -3337,7 +3338,7 @@ describe('Body parser', () => { ], rawType: 'VO', script: '', - fields, + fields: emptyFields, modified: 0, storyName: 'test-segment', segmentExternalId: '00000000001' @@ -3351,7 +3352,7 @@ describe('Body parser', () => { it('has no no cues, does nothing', () => { const definitions: PartDefinition[] = [createPartDefinition(), createPartDefinition()] - const result: PartDefinition[] = stripRedundantCuesWhenLayoutCueIsPresent(definitions) + const result: PartDefinition[] = stripRedundantCuesWhenFieldCueIsPresent(definitions) expect(result).toEqual(definitions) }) @@ -3365,7 +3366,7 @@ describe('Body parser', () => { ]) ] - const result: PartDefinition[] = stripRedundantCuesWhenLayoutCueIsPresent(definitions) + const result: PartDefinition[] = stripRedundantCuesWhenFieldCueIsPresent(definitions) expect(result[0].cues).toHaveLength(1) const graphicDesignCue: CueDefinitionGraphicDesign = result[0].cues[0] as CueDefinitionGraphicDesign @@ -3375,7 +3376,7 @@ describe('Body parser', () => { it('only have a regular design cue, does nothing', () => { const definitions: PartDefinition[] = [createPartDefinition([createDesignCueDefinition('someDesign')])] - const result: PartDefinition[] = stripRedundantCuesWhenLayoutCueIsPresent(definitions) + const result: PartDefinition[] = stripRedundantCuesWhenFieldCueIsPresent(definitions) expect(result).toEqual(definitions) }) @@ -3385,7 +3386,7 @@ describe('Body parser', () => { createPartDefinition([createDesignCueDefinition('designFromLayout', true)]) ] - const result: PartDefinition[] = stripRedundantCuesWhenLayoutCueIsPresent(definitions) + const result: PartDefinition[] = stripRedundantCuesWhenFieldCueIsPresent(definitions) expect(result).toEqual(definitions) }) @@ -3401,7 +3402,7 @@ describe('Body parser', () => { ]) ] - const result: PartDefinition[] = stripRedundantCuesWhenLayoutCueIsPresent(definitions) + const result: PartDefinition[] = stripRedundantCuesWhenFieldCueIsPresent(definitions) const cues = result[0].cues expect(cues).toHaveLength(3) @@ -3422,7 +3423,7 @@ describe('Body parser', () => { createPartDefinition([createDesignCueDefinition('regularDesign')]) ] - const result: PartDefinition[] = stripRedundantCuesWhenLayoutCueIsPresent(definitions) + const result: PartDefinition[] = stripRedundantCuesWhenFieldCueIsPresent(definitions) const cues: CueDefinition[] = result.flatMap((definition) => definition.cues) expect(cues).toHaveLength(1) @@ -3439,7 +3440,7 @@ describe('Body parser', () => { ]) ] - const result: PartDefinition[] = stripRedundantCuesWhenLayoutCueIsPresent(definitions) + const result: PartDefinition[] = stripRedundantCuesWhenFieldCueIsPresent(definitions) expect(result[0].cues).toHaveLength(1) const backgroundCue: CueDefinitionBackgroundLoop = result[0].cues[0] as CueDefinitionBackgroundLoop @@ -3451,7 +3452,7 @@ describe('Body parser', () => { createPartDefinition([createBackgroundLoopCueDefinition('regularBackground')]) ] - const result: PartDefinition[] = stripRedundantCuesWhenLayoutCueIsPresent(definitions) + const result: PartDefinition[] = stripRedundantCuesWhenFieldCueIsPresent(definitions) expect(result).toEqual(definitions) }) @@ -3461,7 +3462,7 @@ describe('Body parser', () => { createPartDefinition([createBackgroundLoopCueDefinition('layoutBackground', true)]) ] - const result: PartDefinition[] = stripRedundantCuesWhenLayoutCueIsPresent(definitions) + const result: PartDefinition[] = stripRedundantCuesWhenFieldCueIsPresent(definitions) expect(result).toEqual(definitions) }) @@ -3492,6 +3493,74 @@ describe('Body parser', () => { expect(result.transition!.duration).toBe(250) }) }) + + describe('create CueDefinitions from fields', () => { + it('receives a layout field, DesignCueDefinition is added to first PartDefinition', () => { + const segmentId: string = 'randomSegmentId' + const segmentName: string = 'randomSegmentName' + const body: string = '\r\n

CAMERA 1

' + const cues: UnparsedCue[] = [] + const fields: INewsFields = { + layout: config.showStyle.GfxDesignTemplates[0].INewsStyleColumn + } + + const result: PartDefinition[] = ParseBody(config, segmentId, segmentName, body, cues, fields, 0) + + expect(result).toHaveLength(1) + const cueDefinitions = result[0].cues + expect(cueDefinitions.some((cue) => cue.type === CueType.GraphicDesign)).toBeTruthy() + }) + + it('receives both a layout field and a body design cue, only one DesignCueDefinition is added to first PartDefinition', () => { + const segmentId: string = 'randomSegmentId' + const segmentName: string = 'randomSegmentName' + const body: string = `\r\n

CAMERA 1<\a idref="0">

\r\n` + const cues: UnparsedCue[] = [[`kg=DESIGN_FODBOLD_22`]] + const fields: INewsFields = { + layout: config.showStyle.GfxDesignTemplates[0].INewsStyleColumn + } + + const result: PartDefinition[] = ParseBody(config, segmentId, segmentName, body, cues, fields, 0) + + expect(result).toHaveLength(1) + const cueDefinitions = result[0].cues + expect(cueDefinitions).toHaveLength(1) + expect(cueDefinitions.some((cue) => cue.type === CueType.GraphicDesign)).toBeTruthy() + }) + + it('receives a schema field, DesignSchemaDefinition is added to first PartDefinition', () => { + const segmentId: string = 'randomSegmentId' + const segmentName: string = 'randomSegmentName' + const body: string = '\r\n

CAMERA 1

' + const cues: UnparsedCue[] = [] + const fields: INewsFields = { + skema: config.showStyle.GfxSchemaTemplates[0].INewsSkemaColumn + } + + const result: PartDefinition[] = ParseBody(config, segmentId, segmentName, body, cues, fields, 0) + + expect(result).toHaveLength(1) + const cueDefinitions = result[0].cues + expect(cueDefinitions.some((cue) => cue.type === CueType.GraphicSchema)).toBeTruthy() + }) + + it('receives both a schema field and a body schema cue, only one DesignSchemaDefinition is added to first PartDefinition', () => { + const segmentId: string = 'randomSegmentId' + const segmentName: string = 'randomSegmentName' + const body: string = `\r\n

CAMERA 1<\a idref="0">

\r\n` + const cues: UnparsedCue[] = [[`kg=SKEMA_NEWS`]] + const fields: INewsFields = { + skema: config.showStyle.GfxSchemaTemplates[0].INewsSkemaColumn + } + + const result: PartDefinition[] = ParseBody(config, segmentId, segmentName, body, cues, fields, 0) + + expect(result).toHaveLength(1) + const cueDefinitions = result[0].cues + expect(cueDefinitions).toHaveLength(1) + expect(cueDefinitions.some((cue) => cue.type === CueType.GraphicSchema)).toBeTruthy() + }) + }) }) function createPartDefinition(cues?: CueDefinition[]): PartDefinition { diff --git a/src/tv2-common/types/inews.ts b/src/tv2-common/types/inews.ts index 21ca5f70..7905c617 100644 --- a/src/tv2-common/types/inews.ts +++ b/src/tv2-common/types/inews.ts @@ -8,6 +8,7 @@ export interface INewsFields { cumeTime?: string // number backTime?: string // @number (seconds since midnight) layout?: string + skema?: string } export interface INewsMetaData { diff --git a/src/tv2_afvd_showstyle/__tests__/configs.ts b/src/tv2_afvd_showstyle/__tests__/configs.ts index a717fbc1..608b8fde 100644 --- a/src/tv2_afvd_showstyle/__tests__/configs.ts +++ b/src/tv2_afvd_showstyle/__tests__/configs.ts @@ -233,7 +233,7 @@ export const defaultShowStyleConfig: GalleryShowStyleConfig = { GfxDesignTemplates: [ { INewsName: 'DESIGN_FODBOLD_22', - INewsStyleColumn: '', + INewsStyleColumn: 'F_22', VizTemplate: 'DESIGN_FODBOLD_22' } ], @@ -273,7 +273,7 @@ export const defaultShowStyleConfig: GalleryShowStyleConfig = { GfxSetups: [DEFAULT_GFX_SETUP], Transitions: [{ Transition: '1' }, { Transition: '2' }], ShowstyleTransition: 'CUT', - GfxSchemaTemplates: [], + GfxSchemaTemplates: [{ GfxSchemaTemplatesName: 'SKEMA_NEWS', VizTemplate: 'NE', INewsSkemaColumn: 'SKEMA_NEWS' }], GfxShowMapping: [], GfxDefaults: [ { From 143926f66623b021887700f03d82769ac598cfab Mon Sep 17 00:00:00 2001 From: Rasmus Lindved Date: Tue, 9 May 2023 07:44:20 +0200 Subject: [PATCH 05/35] SOF-1422 GraphicDesign and Schema cues now starts at first frame instead of second frame --- src/tv2-common/inewsConversion/converters/ParseCue.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/tv2-common/inewsConversion/converters/ParseCue.ts b/src/tv2-common/inewsConversion/converters/ParseCue.ts index b72e656e..06751d09 100644 --- a/src/tv2-common/inewsConversion/converters/ParseCue.ts +++ b/src/tv2-common/inewsConversion/converters/ParseCue.ts @@ -940,9 +940,6 @@ export function createCueDefinitionGraphicDesign( return literal({ type: CueType.GraphicDesign, design: designConfig.VizTemplate, - start: { - frames: 1 - }, iNewsCommand: '', isFromField: true }) @@ -970,9 +967,6 @@ export function createCueDefinitionGraphicSchema( type: CueType.GraphicSchema, schema: schemaConfiguration.VizTemplate, iNewsCommand: '', - start: { - frames: 1 - }, isFromField: true }) } From 8b4356ca3533446e42fb16b97dcfca303c225216 Mon Sep 17 00:00:00 2001 From: ianshade Date: Fri, 12 May 2023 09:26:49 +0200 Subject: [PATCH 06/35] chore: fix lint-staged pre-commit hook --- .husky/pre-commit | 4 + package.json | 15 +-- yarn.lock | 313 ++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 312 insertions(+), 20 deletions(-) create mode 100644 .husky/pre-commit diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 00000000..d2ae35e8 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +yarn lint-staged diff --git a/package.json b/package.json index ad0ffd4d..cd9998b8 100644 --- a/package.json +++ b/package.json @@ -19,20 +19,15 @@ "unit": "jest --maxWorkers=2", "test": "yarn lint --fix && yarn unit", "release": "standard-version", + "prepare": "husky install", "prepareChangelog": "standard-version --prerelease", "validate": "yarn validate:dependencies", "validate:dependencies": "yarn audit --groups dependencies && yarn license-validate", "license-validate": "license-checker --onlyAllow \"MIT;Apache-2.0;ISC;BSD;CC0;CC-BY-3.0;CC-BY-4.0;UNLICENSED\" --summary" }, - "husky": { - "hooks": { - "pre-commit": "lint-staged" - } - }, "lint-staged": { "*.{js,ts,css,json,md}": [ - "prettier --write", - "git add" + "prettier --write" ] }, "devDependencies": { @@ -41,10 +36,12 @@ "@types/underscore": "^1.8.9", "axios": "^0.19.0", "git-revision-webpack-plugin": "^3.0.3", + "husky": "^8.0.3", "jest": "^27.5.1", "jest-haste-map": "^24.5.0", "jest-resolve": "^24.5.0", "license-checker": "^25.0.1", + "lint-staged": "^13.2.2", "moment": "^2.29.2", "prettier": "^2.0.0", "standard-version": "9.1.1", @@ -61,8 +58,8 @@ }, "dependencies": { "@sofie-automation/blueprints-integration": "npm:@tv2media/blueprints-integration@46.2.2", - "underscore": "^1.12.1", - "ts-mockito": "^2.6.1" + "ts-mockito": "^2.6.1", + "underscore": "^1.12.1" }, "resolutions": { "moment": "^2.29.2" diff --git a/yarn.lock b/yarn.lock index 0fc6e8f0..6be711a0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -935,6 +935,14 @@ agent-base@6: dependencies: debug "4" +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + ajv-errors@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" @@ -955,7 +963,7 @@ ajv@^6.1.0, ajv@^6.10.2: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ansi-escapes@^4.2.1: +ansi-escapes@^4.2.1, ansi-escapes@^4.3.0: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== @@ -972,6 +980,11 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -991,6 +1004,11 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== +ansi-styles@^6.0.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + anymatch@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" @@ -1087,6 +1105,11 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + async-each@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" @@ -1473,6 +1496,11 @@ capture-exit@^2.0.0: dependencies: rsvp "^4.8.4" +chalk@5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.2.0.tgz#249623b7d66869c673699fb66d65723e54dfcfb3" + integrity sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA== + chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -1572,6 +1600,34 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-truncate@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" + integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== + dependencies: + slice-ansi "^3.0.0" + string-width "^4.2.0" + +cli-truncate@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-3.1.0.tgz#3f23ab12535e3d73e839bb43e73c9de487db1389" + integrity sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA== + dependencies: + slice-ansi "^5.0.0" + string-width "^5.0.0" + cliui@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" @@ -1632,6 +1688,11 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +colorette@^2.0.19: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -1639,6 +1700,11 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" +commander@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + commander@^2.12.1, commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -2007,7 +2073,7 @@ dateformat@^3.0.0: resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== -debug@4, debug@^4.1.0, debug@^4.1.1: +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -2200,6 +2266,11 @@ duplexify@^3.4.2, duplexify@^3.6.0: readable-stream "^2.0.0" stream-shift "^1.0.0" +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + electron-to-chromium@^1.4.84: version "1.4.116" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.116.tgz#cf0b106d462a78e43ef33cc269caf2ad70e3c7a8" @@ -2233,6 +2304,11 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + emojis-list@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" @@ -2427,6 +2503,21 @@ execa@^5.0.0: signal-exit "^3.0.3" strip-final-newline "^2.0.0" +execa@^7.0.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-7.1.1.tgz#3eb3c83d239488e7b409d48e8813b76bb55c9c43" + integrity sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.1" + human-signals "^4.3.0" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^3.0.7" + strip-final-newline "^3.0.0" + exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -2733,7 +2824,7 @@ get-stream@^4.0.0: dependencies: pump "^3.0.0" -get-stream@^6.0.0: +get-stream@^6.0.0, get-stream@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== @@ -3036,6 +3127,16 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +human-signals@^4.3.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-4.3.1.tgz#ab7f811e851fca97ffbd2c1fe9a958964de321b2" + integrity sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ== + +husky@^8.0.3: + version "8.0.3" + resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184" + integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg== + iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -3271,6 +3372,11 @@ is-fullwidth-code-point@^3.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== +is-fullwidth-code-point@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz#fae3167c729e7463f8461ce512b080a49268aa88" + integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ== + is-generator-fn@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" @@ -3361,6 +3467,11 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== +is-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" + integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== + is-string@^1.0.5, is-string@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" @@ -4106,11 +4217,49 @@ license-checker@^25.0.1: spdx-satisfies "^4.0.0" treeify "^1.1.0" +lilconfig@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" + integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== + lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== +lint-staged@^13.2.2: + version "13.2.2" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-13.2.2.tgz#5e711d3139c234f73402177be2f8dd312e6508ca" + integrity sha512-71gSwXKy649VrSU09s10uAT0rWCcY3aewhMaHyl2N84oBk4Xs9HgxvUp3AYu+bNsK4NrOYYxvSgg7FyGJ+jGcA== + dependencies: + chalk "5.2.0" + cli-truncate "^3.1.0" + commander "^10.0.0" + debug "^4.3.4" + execa "^7.0.0" + lilconfig "2.1.0" + listr2 "^5.0.7" + micromatch "^4.0.5" + normalize-path "^3.0.0" + object-inspect "^1.12.3" + pidtree "^0.6.0" + string-argv "^0.3.1" + yaml "^2.2.2" + +listr2@^5.0.7: + version "5.0.8" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-5.0.8.tgz#a9379ffeb4bd83a68931a65fb223a11510d6ba23" + integrity sha512-mC73LitKHj9w6v30nLNGPetZIlfpUniNSsxxrbaPcWOjDb92SHPzJPi/t+v1YC/lxKz/AJ9egOjww0qUuFxBpA== + dependencies: + cli-truncate "^2.1.0" + colorette "^2.0.19" + log-update "^4.0.0" + p-map "^4.0.0" + rfdc "^1.3.0" + rxjs "^7.8.0" + through "^2.3.8" + wrap-ansi "^7.0.0" + load-json-file@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" @@ -4180,6 +4329,16 @@ lodash@^4.17.15, lodash@^4.17.5, lodash@^4.7.0: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +log-update@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" + integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== + dependencies: + ansi-escapes "^4.3.0" + cli-cursor "^3.1.0" + slice-ansi "^4.0.0" + wrap-ansi "^6.2.0" + loose-envify@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -4316,7 +4475,7 @@ micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: snapdragon "^0.8.1" to-regex "^3.0.2" -micromatch@^4.0.0, micromatch@^4.0.4: +micromatch@^4.0.0, micromatch@^4.0.4, micromatch@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== @@ -4349,6 +4508,11 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +mimic-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" + integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== + min-indent@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" @@ -4588,6 +4752,13 @@ npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" +npm-run-path@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.1.0.tgz#bc62f7f3f6952d9894bd08944ba011a6ee7b7e00" + integrity sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q== + dependencies: + path-key "^4.0.0" + null-check@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/null-check/-/null-check-1.0.0.tgz#977dffd7176012b9ec30d2a39db5cf72a0439edd" @@ -4617,6 +4788,11 @@ object-inspect@^1.12.0, object-inspect@^1.9.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== +object-inspect@^1.12.3: + version "1.12.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" + integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== + object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -4662,13 +4838,20 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" -onetime@^5.1.2: +onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" +onetime@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" + integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== + dependencies: + mimic-fn "^4.0.0" + optionator@^0.8.1: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" @@ -4758,6 +4941,13 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" @@ -4861,6 +5051,11 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== +path-key@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" + integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" @@ -4894,6 +5089,11 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +pidtree@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.6.0.tgz#90ad7b6d42d5841e69e0a2419ef38f8883aa057c" + integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g== + pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -5289,11 +5489,24 @@ resolve@^1.10.0, resolve@^1.20.0, resolve@^1.3.2: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== +rfdc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" + integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== + rimraf@^2.5.4, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" @@ -5328,6 +5541,13 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" +rxjs@^7.8.0: + version "7.8.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== + dependencies: + tslib "^2.1.0" + safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -5466,7 +5686,7 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: +signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -5486,6 +5706,32 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== +slice-ansi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" + integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +slice-ansi@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-5.0.0.tgz#b73063c57aa96f9cd881654b15294d95d285c42a" + integrity sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ== + dependencies: + ansi-styles "^6.0.0" + is-fullwidth-code-point "^4.0.0" + slide@~1.1.3: version "1.1.6" resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" @@ -5722,6 +5968,11 @@ stream-shift@^1.0.0: resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== +string-argv@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" + integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== + string-length@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" @@ -5748,6 +5999,15 @@ string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string-width@^5.0.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + string.prototype.trimend@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" @@ -5797,6 +6057,13 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" +strip-ansi@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2" + integrity sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw== + dependencies: + ansi-regex "^6.0.1" + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -5817,6 +6084,11 @@ strip-final-newline@^2.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +strip-final-newline@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" + integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== + strip-indent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" @@ -5951,15 +6223,15 @@ through2@^4.0.0: dependencies: readable-stream "3" -through@2, "through@>=2.2.7 <3": +through@2, "through@>=2.2.7 <3", through@^2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= -"timeline-state-resolver-types@npm:@tv2media/timeline-state-resolver-types@3.4.0": - version "3.4.0" - resolved "https://registry.yarnpkg.com/@tv2media/timeline-state-resolver-types/-/timeline-state-resolver-types-3.4.0.tgz#179b66c64616fec2ec541fd9dd5a73517d8706fc" - integrity sha512-P174WyTAa6PVAotOJOEw8Dsqu1L3Cx/jEDmikLDLfFgRgmsYNSjtmgLzuzBY/O39iAxcqXs0ItM7QKcnX+ilwg== +"timeline-state-resolver-types@npm:@tv2media/timeline-state-resolver-types@3.5.0": + version "3.5.0" + resolved "https://registry.yarnpkg.com/@tv2media/timeline-state-resolver-types/-/timeline-state-resolver-types-3.5.0.tgz#02ca12128cbc0df84e630d0da8c66a1d44e5f172" + integrity sha512-DdKXdQf+By8ssBKqd3qfAAhERehlKTorWb3tYK6G6z1vmADSJT+MVAvY0GpLx5/qKe0e3HbutQ2CMlh7b00QIA== dependencies: tslib "^2.3.1" @@ -6110,6 +6382,11 @@ tslib@^1.7.1, tslib@^1.8.0, tslib@^1.8.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@^2.1.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" + integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== + tslib@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" @@ -6548,6 +6825,15 @@ wrap-ansi@^5.1.0: string-width "^3.0.0" strip-ansi "^5.0.0" +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -6612,6 +6898,11 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yaml@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.2.2.tgz#ec551ef37326e6d42872dad1970300f8eb83a073" + integrity sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA== + yargs-parser@20.x, yargs-parser@^20.2.2, yargs-parser@^20.2.3: version "20.2.9" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" From 3310ef092e3fcd458e240396bed725072925fa2a Mon Sep 17 00:00:00 2001 From: Krzysztof Zegzula Date: Wed, 17 May 2023 10:30:53 +0200 Subject: [PATCH 07/35] fix: SOF-1444 prevent crashing after 180 camera cuts (#204) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: SOF-1444 prevent crashing after 180 camera cuts refactors persisted audio not to collect excessively nested data * refactor: SOF-1444 improve naming, extract blocks, and simplify some generics * refactor: simplify the flow Co-authored-by: Anders Frederik Jørgensen --------- Co-authored-by: Anders Frederik Jørgensen --- src/__mocks__/context.ts | 15 +- .../__tests__/onTimelineGenerate.spec.ts | 254 +++++--------- .../actions/__tests__/executeAction.spec.ts | 191 +++++++++++ src/tv2-common/actions/executeAction.ts | 315 +++++++++--------- src/tv2-common/cues/ekstern.ts | 4 +- src/tv2-common/evaluateCues.ts | 5 +- .../graphics/internal/InternalGraphic.ts | 13 - .../graphics/pilot/PilotGraphicGenerator.ts | 14 +- src/tv2-common/onTimelineGenerate.ts | 150 ++++++--- src/tv2-common/parts/server.ts | 2 +- src/tv2-constants/enums.ts | 3 +- .../GlobalAdlibActionsGenerator.ts | 25 +- src/tv2_afvd_showstyle/getRundown.ts | 59 +--- .../pieces/__tests__/grafikViz.spec.ts | 42 --- .../helpers/pieces/__tests__/telefon.spec.ts | 3 - src/tv2_afvd_showstyle/helpers/pieces/dve.ts | 3 - src/tv2_afvd_showstyle/parts/kam.ts | 7 +- src/tv2_afvd_studio/layers.ts | 1 - src/tv2_offtube_showstyle/cues/OfftubeDVE.ts | 3 - .../cues/OfftubeJingle.ts | 5 - src/tv2_offtube_showstyle/parts/OfftubeKam.ts | 2 +- yarn.lock | 8 +- 22 files changed, 586 insertions(+), 538 deletions(-) create mode 100644 src/tv2-common/actions/__tests__/executeAction.spec.ts diff --git a/src/__mocks__/context.ts b/src/__mocks__/context.ts index a56d73ca..33b0900e 100644 --- a/src/__mocks__/context.ts +++ b/src/__mocks__/context.ts @@ -461,9 +461,20 @@ export class ActionExecutionContextMock extends ShowStyleUserContextMock impleme } /** Get the resolved PieceInstances for a modifiable PartInstance */ public async getResolvedPieceInstances( - _part: 'current' | 'next' + part: 'current' | 'next' ): Promise>> { - return [] + const pieces = part === 'current' ? this.currentPieceInstances : this.nextPieceInstances ?? [] + const now = Date.now() + // this is nowhere near to what core does; we should reconsider the way we're mocking this context + return pieces.map((pieceInstance) => { + const pieceStart = pieceInstance.piece.enable.start + const resolvedStart = pieceStart === 'now' ? now : pieceStart + + return { + ...pieceInstance, + resolvedStart + } + }) } /** Get the last active piece on given layer */ public async findLastPieceOnLayer( diff --git a/src/tv2-common/__tests__/onTimelineGenerate.spec.ts b/src/tv2-common/__tests__/onTimelineGenerate.spec.ts index 6e20cbed..6fbcf014 100644 --- a/src/tv2-common/__tests__/onTimelineGenerate.spec.ts +++ b/src/tv2-common/__tests__/onTimelineGenerate.spec.ts @@ -3,23 +3,28 @@ import { SisyfosLLAyer } from '../../tv2_afvd_studio/layers' import { createSisyfosPersistedLevelsTimelineObject, PieceMetaData, - SisyfosPersistMetaData + SisyfosPersistenceMetaData } from '../onTimelineGenerate' const LAYER_THAT_WANTS_TO_BE_PERSISTED = 'layerThatWantsToBePersisted' -const LAYERS_THAT_WANTS_TO_BE_PERSISTED_ARRAY: SisyfosPersistMetaData['sisyfosLayers'] = [ +const LAYERS_THAT_WANTS_TO_BE_PERSISTED_ARRAY: SisyfosPersistenceMetaData['sisyfosLayers'] = [ LAYER_THAT_WANTS_TO_BE_PERSISTED ] +const PART_INSTANCE_ID = 'part1' // tslint:disable:no-object-literal-type-assertion describe('onTimelineGenerate', () => { describe('createSisyfosPersistedLevelsTimelineObject', () => { - it('has one layer to persist, piece accept persist - timelineObject with layer is added', () => { + it('has one layer to persist from the previous part, piece accepts persistence - timelineObject with layer is added', () => { const resolvedPieces: Array> = [ createPieceInstance('currentPiece', 10, undefined, true, true) ] - const result = createSisyfosPersistedLevelsTimelineObject(resolvedPieces, LAYERS_THAT_WANTS_TO_BE_PERSISTED_ARRAY) + const result = createSisyfosPersistedLevelsTimelineObject( + PART_INSTANCE_ID, + resolvedPieces, + LAYERS_THAT_WANTS_TO_BE_PERSISTED_ARRAY + ) const indexOfLayerThatWantToBePersisted = result.content.channels.findIndex( (channel) => channel.mappedLayer === LAYER_THAT_WANTS_TO_BE_PERSISTED @@ -27,12 +32,16 @@ describe('onTimelineGenerate', () => { expect(indexOfLayerThatWantToBePersisted).toBeGreaterThanOrEqual(0) }) - it('has layer to persist, timelineObject with correct Sisyfos information is added', () => { + it('has one layer to persist from the previous part, timelineObject with correct Sisyfos information is added', () => { const resolvedPieces: Array> = [ createPieceInstance('currentPiece', 10, undefined, true, true) ] - const result = createSisyfosPersistedLevelsTimelineObject(resolvedPieces, LAYERS_THAT_WANTS_TO_BE_PERSISTED_ARRAY) + const result = createSisyfosPersistedLevelsTimelineObject( + PART_INSTANCE_ID, + resolvedPieces, + LAYERS_THAT_WANTS_TO_BE_PERSISTED_ARRAY + ) expect(result.layer).toEqual(SisyfosLLAyer.SisyfosPersistedLevels) expect(result.content.deviceType).toEqual(TSR.DeviceType.SISYFOS) @@ -45,49 +54,53 @@ describe('onTimelineGenerate', () => { createPieceInstance('currentPiece', 10, undefined, true, true) ] - const result = createSisyfosPersistedLevelsTimelineObject(resolvedPieces, LAYERS_THAT_WANTS_TO_BE_PERSISTED_ARRAY) + const result = createSisyfosPersistedLevelsTimelineObject( + PART_INSTANCE_ID, + resolvedPieces, + LAYERS_THAT_WANTS_TO_BE_PERSISTED_ARRAY + ) expect(result.content.channels[0].isPgm).toEqual(1) }) - it('should persist only current piece layer when piece wants to persist but dont accept', () => { + it('should not persist anything when no piece in current part accepts persistence', () => { const resolvedPieces: Array> = [ - createPieceInstance('previousPiece', 0, 10, true, true), - createPieceInstance('currentPiece', 10, undefined, true, false) + createPieceInstance('previousPiece', 0, undefined, true, true, 'OTHER_PART') ] - const result = createSisyfosPersistedLevelsTimelineObject(resolvedPieces, LAYERS_THAT_WANTS_TO_BE_PERSISTED_ARRAY) + const result = createSisyfosPersistedLevelsTimelineObject( + PART_INSTANCE_ID, + resolvedPieces, + LAYERS_THAT_WANTS_TO_BE_PERSISTED_ARRAY + ) - expect(result.content.channels).toHaveLength(1) + expect(result.content.channels).toHaveLength(0) }) - it('should not persist anything when current piece dont accept and dont want to persist', () => { + it('should not persist anything when the current piece does not accept and does not want to persist', () => { const resolvedPieces: Array> = [ - createPieceInstance('previousPiece', 0, 10, true, true), + createPieceInstance('previousPiece', 0, undefined, true, true), createPieceInstance('currentPiece', 10, undefined, false, false) ] - const result = createSisyfosPersistedLevelsTimelineObject(resolvedPieces, LAYERS_THAT_WANTS_TO_BE_PERSISTED_ARRAY) + const result = createSisyfosPersistedLevelsTimelineObject( + PART_INSTANCE_ID, + resolvedPieces, + LAYERS_THAT_WANTS_TO_BE_PERSISTED_ARRAY + ) expect(result.content.channels).toHaveLength(0) }) - it('should persist when previous piece does not accept persist, but current does accept', () => { - const resolvedPieces: Array> = [ - createPieceInstance('previousPiece', 0, 10, true, false), - createPieceInstance('currentPiece', 10, undefined, false, true) - ] - - const result = createSisyfosPersistedLevelsTimelineObject(resolvedPieces, LAYERS_THAT_WANTS_TO_BE_PERSISTED_ARRAY) - - expect(result.content.channels).toHaveLength(1) - }) - it('should persist when current piece accepts persist and duration is not undefined', () => { const resolvedPieces: Array> = [ createPieceInstance('currentPiece', 0, 5, false, true) ] - const result = createSisyfosPersistedLevelsTimelineObject(resolvedPieces, LAYERS_THAT_WANTS_TO_BE_PERSISTED_ARRAY) + const result = createSisyfosPersistedLevelsTimelineObject( + PART_INSTANCE_ID, + resolvedPieces, + LAYERS_THAT_WANTS_TO_BE_PERSISTED_ARRAY + ) expect(result.content.channels).toHaveLength(1) }) @@ -96,7 +109,7 @@ describe('onTimelineGenerate', () => { const firstLayerThatWantToBePersisted: string = 'firstLayer' const secondLayerThatWantToBePersisted: string = 'secondLayer' const thirdLayerThatWantToBePersisted: string = 'thirdLayer' - const layersThatWantToBePersisted: SisyfosPersistMetaData['sisyfosLayers'] = [ + const layersThatWantToBePersisted: SisyfosPersistenceMetaData['sisyfosLayers'] = [ firstLayerThatWantToBePersisted, secondLayerThatWantToBePersisted, thirdLayerThatWantToBePersisted @@ -105,7 +118,11 @@ describe('onTimelineGenerate', () => { createPieceInstance('currentPiece', 0, 5, true, true) ] - const result = createSisyfosPersistedLevelsTimelineObject(resolvedPieces, layersThatWantToBePersisted) + const result = createSisyfosPersistedLevelsTimelineObject( + PART_INSTANCE_ID, + resolvedPieces, + layersThatWantToBePersisted + ) expect( result.content.channels.some((channel) => channel.mappedLayer === firstLayerThatWantToBePersisted) @@ -124,39 +141,46 @@ describe('onTimelineGenerate', () => { createExecuteActionPieceInstance('currentPieceIsExecuteAction', 10, undefined, false, false) ] - const result = createSisyfosPersistedLevelsTimelineObject(resolvedPieces, LAYERS_THAT_WANTS_TO_BE_PERSISTED_ARRAY) + const result = createSisyfosPersistedLevelsTimelineObject( + PART_INSTANCE_ID, + resolvedPieces, + LAYERS_THAT_WANTS_TO_BE_PERSISTED_ARRAY + ) expect(result.content.channels).toHaveLength(0) }) - it('cuts to executeAction that accept persist from piece that accept, add persist timelineObject containing all layers that want to be persisted plus previous piece layers', () => { + it('cuts to executeAction that accepts persistence, dont persist layers from previous part', () => { const resolvedPieces: Array> = [ createPieceInstance('previousPieceNotExecuteAction', 0, 10, true, true), createExecuteActionPieceInstance('currentPieceIsExecuteAction', 10, undefined, false, true) ] - const result = createSisyfosPersistedLevelsTimelineObject(resolvedPieces, LAYERS_THAT_WANTS_TO_BE_PERSISTED_ARRAY) + const result = createSisyfosPersistedLevelsTimelineObject( + PART_INSTANCE_ID, + resolvedPieces, + LAYERS_THAT_WANTS_TO_BE_PERSISTED_ARRAY + ) - expect( - result.content.channels.some((channel) => channel.mappedLayer === LAYER_THAT_WANTS_TO_BE_PERSISTED) - ).toBeTruthy() - expect( - result.content.channels.some((channel) => channel.mappedLayer === resolvedPieces[0].piece.name) - ).toBeTruthy() + expect(result.content.channels).toHaveLength(0) }) - it('cuts to executionAction that accept from piece that dont accept, add persist timelineObject that only contain previous piece layers', () => { + it('cuts to executeAction that accepts persistence, from piece that accepts, add persist timelineObject containing all layers that want to be persisted plus previous piece layers', () => { const resolvedPieces: Array> = [ - createPieceInstance('previousPieceNotExecuteAction', 0, 10, true, false), - createExecuteActionPieceInstance('currentPieceIsExecuteAction', 10, undefined, false, true) + createPieceInstance('previousPieceNotExecuteAction', 0, 10, true, true), + createExecuteActionPieceInstance('currentPieceIsExecuteAction', 10, undefined, false, true, PART_INSTANCE_ID, [ + 'previousPieceLayer' + ]) ] - const result = createSisyfosPersistedLevelsTimelineObject(resolvedPieces, LAYERS_THAT_WANTS_TO_BE_PERSISTED_ARRAY) + const result = createSisyfosPersistedLevelsTimelineObject( + PART_INSTANCE_ID, + resolvedPieces, + LAYERS_THAT_WANTS_TO_BE_PERSISTED_ARRAY + ) expect(result.content.channels).toHaveLength(1) - expect( - result.content.channels.some((channel) => channel.mappedLayer === resolvedPieces[0].piece.name) - ).toBeTruthy() + expect(result.content.channels[0].mappedLayer).toBe('previousPieceLayer') }) it('cuts to executeAction that accept persist from piece that dont want to persist and dont accept persist, dont persist any layers', () => { @@ -165,132 +189,21 @@ describe('onTimelineGenerate', () => { createExecuteActionPieceInstance('currentPieceIsExecuteAction', 10, undefined, false, true) ] - const result = createSisyfosPersistedLevelsTimelineObject(resolvedPieces, LAYERS_THAT_WANTS_TO_BE_PERSISTED_ARRAY) - - expect(result.content.channels).toHaveLength(0) - }) - - it('cuts to executeAction that accept persist from piece that dont want to persist and that accept persist, persist previous layers', () => { - const resolvedPieces: Array> = [ - createPieceInstance('previousPieceNotExecuteAction', 0, 10, false, true), - createExecuteActionPieceInstance('currentPieceIsExecuteAction', 10, undefined, false, true) - ] - - const result = createSisyfosPersistedLevelsTimelineObject(resolvedPieces, LAYERS_THAT_WANTS_TO_BE_PERSISTED_ARRAY) - - expect(result.content.channels).toHaveLength(1) - expect( - result.content.channels.some((channel) => channel.mappedLayer === LAYER_THAT_WANTS_TO_BE_PERSISTED) - ).toBeTruthy() - }) - - it('cuts from executeAction that dont accept to piece that accepts, dont persist', () => { - const resolvedPieces: Array> = [ - createExecuteActionPieceInstance('previousPieceIsExecuteAction', 0, 10, false, false), - createPieceInstance('currentPieceNotExecuteAction', 10, undefined, false, true) - ] - - const result = createSisyfosPersistedLevelsTimelineObject(resolvedPieces, LAYERS_THAT_WANTS_TO_BE_PERSISTED_ARRAY) - - expect(result.content.channels).toHaveLength(0) - }) - - it('cuts from executeAction that dont accept to piece that dont accepts, dont persist layers', () => { - const resolvedPieces: Array> = [ - createExecuteActionPieceInstance('previousPieceIsExecuteAction', 0, 10, false, false), - createPieceInstance('currentPieceNotExecuteAction', 10, undefined, false, false) - ] - - const result = createSisyfosPersistedLevelsTimelineObject(resolvedPieces, LAYERS_THAT_WANTS_TO_BE_PERSISTED_ARRAY) - - expect(result.content.channels).toHaveLength(0) - }) - - it('cuts from executeAction that accept to piece that dont accepts, dont persist layers', () => { - const resolvedPieces: Array> = [ - createExecuteActionPieceInstance('previousPieceIsExecuteAction', 0, 10, false, true), - createPieceInstance('currentPieceNotExecuteAction', 10, undefined, false, false) - ] - - const result = createSisyfosPersistedLevelsTimelineObject(resolvedPieces, LAYERS_THAT_WANTS_TO_BE_PERSISTED_ARRAY) - - expect(result.content.channels).toHaveLength(0) - }) - - it('cuts from executeAction that accept to piece that accepts, add persist timelineObject with previous layer before executeAction + new layer', () => { - const resolvedPieces: Array> = [ - createPieceInstance('firstPiece', 0, 5, true, true), - createExecuteActionPieceInstance('previousPieceIsExecuteAction', 5, 5, false, true, { - acceptPersistAudio: true, - sisyfosLayers: [] - }), - createPieceInstance('currentPieceNotExecuteAction', 10, undefined, false, true) - ] - - const result = createSisyfosPersistedLevelsTimelineObject(resolvedPieces, LAYERS_THAT_WANTS_TO_BE_PERSISTED_ARRAY) - - expect(result.content.channels).toHaveLength(2) - }) - - it('cuts from piece that wants to persist to executeAction that do not accept to piece that accepts, do not persist', () => { - const resolvedPieces: Array> = [ - createPieceInstance('firstPiece', 0, 5, true, true), - createExecuteActionPieceInstance('previousPieceIsExecuteAction', 5, 5, false, true, { - acceptPersistAudio: false, - sisyfosLayers: [] - }), - createPieceInstance('currentPieceNotExecuteAction', 10, undefined, false, true) - ] - - const result = createSisyfosPersistedLevelsTimelineObject(resolvedPieces, LAYERS_THAT_WANTS_TO_BE_PERSISTED_ARRAY) - - expect(result.content.channels).toHaveLength(0) - }) - - it('cuts from piece that wants to persist to executeAction that accepts to another executeAction that accepts, persist layer from first piece', () => { - const resolvedPieces: Array> = [ - createPieceInstance('firstPiece', 0, 5, true, true), - createExecuteActionPieceInstance('executeAction', 5, undefined, false, true, { - acceptPersistAudio: true, - sisyfosLayers: [], - previousPersistMetaDataForCurrentPiece: { - acceptPersistAudio: true, - sisyfosLayers: [] - } - }) - ] - - const result = createSisyfosPersistedLevelsTimelineObject(resolvedPieces, []) - - expect(result.content.channels).toHaveLength(1) - expect(result.content.channels.some((channel) => channel.mappedLayer === 'firstPiece')).toBeTruthy() - }) - - it('cuts from piece that wants to persist to executeAction that do not accept to another executeAction that accepts, dont persist any layers', () => { - const resolvedPieces: Array> = [ - createPieceInstance('firstPiece', 0, 5, true, true), - createExecuteActionPieceInstance('executeAction', 5, undefined, false, true, { - acceptPersistAudio: true, - sisyfosLayers: [], - previousPersistMetaDataForCurrentPiece: { - acceptPersistAudio: false, - sisyfosLayers: [] - } - }) - ] - - const result = createSisyfosPersistedLevelsTimelineObject(resolvedPieces, []) + const result = createSisyfosPersistedLevelsTimelineObject( + PART_INSTANCE_ID, + resolvedPieces, + LAYERS_THAT_WANTS_TO_BE_PERSISTED_ARRAY + ) expect(result.content.channels).toHaveLength(0) }) it('should not contain any duplicate layers to persist', () => { const resolvedPieces: Array> = [ - createPieceInstance('piece', 0, 5, true, true), - createPieceInstance('piece', 5, undefined, true, true) + createExecuteActionPieceInstance('piece', 5, undefined, true, true, PART_INSTANCE_ID, ['piece']) ] - const result = createSisyfosPersistedLevelsTimelineObject(resolvedPieces, []) + const result = createSisyfosPersistedLevelsTimelineObject(PART_INSTANCE_ID, resolvedPieces, ['piece']) expect(result.content.channels).toHaveLength(1) }) @@ -302,18 +215,20 @@ function createPieceInstance( start: number, duration: number | undefined, wantToPersistAudio: boolean, - acceptPersistAudio: boolean + acceptPersistAudio: boolean, + partInstanceId: string = PART_INSTANCE_ID ): IBlueprintResolvedPieceInstance { return { resolvedStart: start, resolvedDuration: duration, + partInstanceId, piece: { name, metaData: { sisyfosPersistMetaData: { sisyfosLayers: [name], wantsToPersistAudio: wantToPersistAudio, - acceptPersistAudio + acceptsPersistedAudio: acceptPersistAudio } } } as IBlueprintPieceDB @@ -326,19 +241,22 @@ function createExecuteActionPieceInstance( duration: number | undefined, wantToPersistAudio: boolean, acceptPersistAudio: boolean, - previousMetaData?: SisyfosPersistMetaData + partInstanceId: string = PART_INSTANCE_ID, + previousSisyfosLayers?: string[] ): IBlueprintResolvedPieceInstance { return { resolvedStart: start, resolvedDuration: duration, + partInstanceId, piece: { name, metaData: { sisyfosPersistMetaData: { sisyfosLayers: [name], wantsToPersistAudio: wantToPersistAudio, - acceptPersistAudio, - previousPersistMetaDataForCurrentPiece: previousMetaData + acceptsPersistedAudio: acceptPersistAudio, + isModifiedOrInsertedByAction: true, + previousSisyfosLayers } } } as IBlueprintPieceDB diff --git a/src/tv2-common/actions/__tests__/executeAction.spec.ts b/src/tv2-common/actions/__tests__/executeAction.spec.ts new file mode 100644 index 00000000..6e78efc0 --- /dev/null +++ b/src/tv2-common/actions/__tests__/executeAction.spec.ts @@ -0,0 +1,191 @@ +import { PartEndStateExt } from '../../onTimelineGenerate' +import { mergePersistenceMetaData } from '../executeAction' + +const CURRENT_SEGMENT_ID = 'segment1' +const NEXT_LAYERS = ['nextLayer1', 'nextLayer2'] +const CURRENT_LAYERS = ['currentLayer1', 'currentLayer2'] +const PREVIOUS_LAYERS = ['previousLayer1', 'previousLayer2'] +const PREVIOUS_PERSISTED_LAYERS = ['previousPersistedLayer1', 'previousPersistedLayer2'] + +describe('executeAction', () => { + describe('mergePersistenceMetaData', () => { + it('does not populate previousSisyfosLayers when next accepts and current does not want to persist', () => { + const result = mergePersistenceMetaData( + CURRENT_SEGMENT_ID, + { + sisyfosLayers: NEXT_LAYERS, + acceptsPersistedAudio: true + }, + { + sisyfosLayers: CURRENT_LAYERS, + wantsToPersistAudio: false + }, + undefined + ) + + expect(result).toEqual({ + sisyfosLayers: NEXT_LAYERS, + acceptsPersistedAudio: true + }) + }) + + it('populates previousSisyfosLayers with current layer when next accepts and current wants to persist', () => { + const result = mergePersistenceMetaData( + CURRENT_SEGMENT_ID, + { + sisyfosLayers: NEXT_LAYERS, + acceptsPersistedAudio: true + }, + { + sisyfosLayers: CURRENT_LAYERS, + wantsToPersistAudio: true + }, + undefined + ) + + expect(result).toEqual({ + sisyfosLayers: NEXT_LAYERS, + previousSisyfosLayers: CURRENT_LAYERS, + acceptsPersistedAudio: true + }) + }) + + it('does not populate previousSisyfosLayers when next accepts, current does not accept persistence and previous part wanted to persist', () => { + const result = mergePersistenceMetaData( + CURRENT_SEGMENT_ID, + { + sisyfosLayers: NEXT_LAYERS, + acceptsPersistedAudio: true + }, + { + sisyfosLayers: CURRENT_LAYERS, + wantsToPersistAudio: false + }, + createPartEndState(CURRENT_SEGMENT_ID) + ) + + expect(result).toEqual({ + sisyfosLayers: NEXT_LAYERS, + acceptsPersistedAudio: true + }) + }) + + it('populates previousSisyfosLayers with current and previous layers when next accepts and current&previous want to persist', () => { + const result = mergePersistenceMetaData( + CURRENT_SEGMENT_ID, + { + sisyfosLayers: NEXT_LAYERS, + acceptsPersistedAudio: true + }, + { + sisyfosLayers: CURRENT_LAYERS, + wantsToPersistAudio: true + }, + createPartEndState(CURRENT_SEGMENT_ID) + ) + + expect(result).toEqual({ + sisyfosLayers: NEXT_LAYERS, + previousSisyfosLayers: [...PREVIOUS_LAYERS, ...CURRENT_LAYERS], + acceptsPersistedAudio: true + }) + }) + + it('populates previousSisyfosLayers with current layers when next accepts and current&previous want to persist, but previous was in a different segment', () => { + const result = mergePersistenceMetaData( + CURRENT_SEGMENT_ID, + { + sisyfosLayers: NEXT_LAYERS, + acceptsPersistedAudio: true + }, + { + sisyfosLayers: CURRENT_LAYERS, + wantsToPersistAudio: true + }, + createPartEndState('anotherSegment') + ) + + expect(result).toEqual({ + sisyfosLayers: NEXT_LAYERS, + previousSisyfosLayers: [...CURRENT_LAYERS], + acceptsPersistedAudio: true + }) + }) + + it('populates previousSisyfosLayers with current layers when next accepts, current&previous want to persist, but current is injected', () => { + const result = mergePersistenceMetaData( + CURRENT_SEGMENT_ID, + { + sisyfosLayers: NEXT_LAYERS, + acceptsPersistedAudio: true + }, + { + sisyfosLayers: CURRENT_LAYERS, + wantsToPersistAudio: true, + isModifiedOrInsertedByAction: true + }, + createPartEndState(CURRENT_SEGMENT_ID) + ) + + expect(result).toEqual({ + sisyfosLayers: NEXT_LAYERS, + previousSisyfosLayers: CURRENT_LAYERS, + acceptsPersistedAudio: true + }) + }) + + it('populates previousSisyfosLayers with current layers and previous persisted when next accepts, current&previous want to persist, but current is injected', () => { + const result = mergePersistenceMetaData( + CURRENT_SEGMENT_ID, + { + sisyfosLayers: NEXT_LAYERS, + acceptsPersistedAudio: true + }, + { + sisyfosLayers: CURRENT_LAYERS, + previousSisyfosLayers: PREVIOUS_PERSISTED_LAYERS, + wantsToPersistAudio: true, + isModifiedOrInsertedByAction: true + }, + createPartEndState(CURRENT_SEGMENT_ID) + ) + + expect(result).toEqual({ + sisyfosLayers: NEXT_LAYERS, + previousSisyfosLayers: [...PREVIOUS_PERSISTED_LAYERS, ...CURRENT_LAYERS], + acceptsPersistedAudio: true + }) + }) + + it('does not populate previousSisyfosLayers when next does not accept persistence', () => { + const result = mergePersistenceMetaData( + CURRENT_SEGMENT_ID, + { + sisyfosLayers: NEXT_LAYERS, + acceptsPersistedAudio: false + }, + { + sisyfosLayers: CURRENT_LAYERS, + wantsToPersistAudio: true + }, + createPartEndState(CURRENT_SEGMENT_ID) + ) + + expect(result).toEqual({ + sisyfosLayers: NEXT_LAYERS, + acceptsPersistedAudio: false + }) + }) + }) +}) + +function createPartEndState(segmentId: string): PartEndStateExt { + return { + partInstanceId: 'partId', + segmentId, + sisyfosPersistenceMetaData: { + sisyfosLayers: PREVIOUS_LAYERS + }, + mediaPlayerSessions: {} + } +} diff --git a/src/tv2-common/actions/executeAction.ts b/src/tv2-common/actions/executeAction.ts index 03edfa10..e99aa2f4 100644 --- a/src/tv2-common/actions/executeAction.ts +++ b/src/tv2-common/actions/executeAction.ts @@ -8,6 +8,7 @@ import { IBlueprintPiece, IBlueprintPieceDB, IBlueprintPieceInstance, + IBlueprintResolvedPieceInstance, PieceLifespan, SplitsContent, TimelineObjectCoreExt, @@ -37,6 +38,7 @@ import { DVESources, EvaluateCuesOptions, executeWithContext, + findLastPlayingPieceInstance, GetDVETemplate, getServerPosition, GetSisyfosTimelineObjForCamera, @@ -46,16 +48,18 @@ import { literal, MakeContentDVE2, PartDefinition, + PartEndStateExt, PieceMetaData, PilotGraphicGenerator, ServerSelectMode, ShowStyleContext, - SisyfosPersistMetaData, + SisyfosPersistenceMetaData, TableConfigItemBreaker, TimelineBlueprintExt, TransitionStyle, TV2AdlibAction, TV2BlueprintConfigBase, + TV2ShowStyleConfig, TV2StudioConfigBase, UniformConfig } from 'tv2-common' @@ -66,6 +70,7 @@ import { PartType, SharedGraphicLLayer, SharedOutputLayer, + SharedSisyfosLLayer, SharedSourceLayer, SourceType, TallyTags @@ -99,14 +104,9 @@ const STOPPABLE_GRAPHICS_LAYERS = [ SharedSourceLayer.PgmGraphicsTLF ] -const FADE_SISYFOS_LEVELS_PIECE_NAME = 'fadeDown' - -export interface ActionExecutionSettings< - StudioConfig extends TV2StudioConfigBase, - ShowStyleConfig extends TV2BlueprintConfigBase -> { +export interface ActionExecutionSettings { EvaluateCues: ( - context: ShowStyleContext, + context: ShowStyleContext, part: IBlueprintPart, pieces: IBlueprintPiece[], adLibPieces: IBlueprintAdLibPiece[], @@ -155,7 +155,7 @@ export interface ActionExecutionSettings< SELECTED_ADLIB_LAYERS: string[] } createJingleContent: ( - context: ShowStyleContext, + context: ShowStyleContext, file: string, breakerConfig: TableConfigItemBreaker ) => WithTimeline @@ -172,7 +172,7 @@ export async function executeAction< >( coreContext: IActionExecutionContext, uniformConfig: UniformConfig, - settings: ActionExecutionSettings, + settings: ActionExecutionSettings, actionIdStr: string, userData: ActionUserData, triggerMode?: string @@ -280,7 +280,7 @@ async function getExistingTransition< ShowStyleConfig extends TV2BlueprintConfigBase >( context: ActionExecutionContext, - settings: ActionExecutionSettings, + settings: ActionExecutionSettings, part: 'current' | 'next' ): Promise { const existingTransition = await context.core @@ -385,7 +385,7 @@ async function executeActionSelectServerClip< ShowStyleConfig extends TV2BlueprintConfigBase >( context: ActionExecutionContext, - settings: ActionExecutionSettings, + settings: ActionExecutionSettings, actionId: string, userData: ActionSelectServerClip, triggerMode?: ServerSelectMode, @@ -498,11 +498,7 @@ function dveContainsServer(sources: DVESources): boolean { function isServerOnPgm< StudioConfig extends TV2StudioConfigBase, ShowStyleConfig extends TV2BlueprintConfigBase ->( - pieceInstance: IBlueprintPieceInstance, - settings: ActionExecutionSettings, - voLayer: boolean -) { +>(pieceInstance: IBlueprintPieceInstance, settings: ActionExecutionSettings, voLayer: boolean) { return ( pieceInstance.piece.sourceLayerId === (voLayer ? settings.SourceLayers.VO : settings.SourceLayers.Server) || (pieceInstance.piece.sourceLayerId === settings.SourceLayers.DVEAdLib && @@ -515,7 +511,7 @@ async function executeActionSelectDVE< ShowStyleConfig extends TV2BlueprintConfigBase >( context: ActionExecutionContext, - settings: ActionExecutionSettings, + settings: ActionExecutionSettings, actionId: string, userData: ActionSelectDVE ) { @@ -551,9 +547,6 @@ async function executeActionSelectDVE< mediaPlayerSessions: dveContainsServer(parsedCue.sources) ? [externalId] : [], sources: parsedCue.sources, config: rawTemplate, - sisyfosPersistMetaData: { - sisyfosLayers: [] - }, userData } @@ -601,7 +594,7 @@ async function cutServerToBox< ShowStyleConfig extends TV2BlueprintConfigBase >( context: ActionExecutionContext, - settings: ActionExecutionSettings, + settings: ActionExecutionSettings, newDvePiece: IBlueprintPiece, containedServerBefore?: boolean, modifiesCurrent?: boolean @@ -697,7 +690,7 @@ async function executeActionSelectDVELayout< ShowStyleConfig extends TV2BlueprintConfigBase >( context: ActionExecutionContext, - settings: ActionExecutionSettings, + settings: ActionExecutionSettings, actionId: string, userData: ActionSelectDVELayout ) { @@ -740,9 +733,6 @@ async function executeActionSelectDVELayout< const newMetaData: DVEPieceMetaData = { sources, config: userData.config, - sisyfosPersistMetaData: { - sisyfosLayers: [] - }, userData: { type: AdlibActionType.SELECT_DVE, config: { @@ -790,10 +780,7 @@ async function executeActionSelectDVELayout< const newMetaData2: DVEPieceMetaData = { ...meta, sources: { ...sources, ...meta.sources }, - config: userData.config, - sisyfosPersistMetaData: { - sisyfosLayers: [] - } + config: userData.config } const pieceContent = MakeContentDVE2(context, userData.config, {}, meta.sources, settings.DVEGeneratorOptions) @@ -832,7 +819,7 @@ async function startNewDVELayout< ShowStyleConfig extends TV2BlueprintConfigBase >( context: ActionExecutionContext, - settings: ActionExecutionSettings, + settings: ActionExecutionSettings, dvePiece: IBlueprintPiece, pieceContent: WithTimeline, metaData: DVEPieceMetaData, @@ -921,7 +908,7 @@ async function executeActionSelectJingle< ShowStyleConfig extends TV2BlueprintConfigBase >( context: ActionExecutionContext, - settings: ActionExecutionSettings, + settings: ActionExecutionSettings, actionId: string, userData: ActionSelectJingle ) { @@ -990,7 +977,7 @@ async function executeActionCutToCamera< ShowStyleConfig extends TV2BlueprintConfigBase >( context: ActionExecutionContext, - settings: ActionExecutionSettings, + settings: ActionExecutionSettings, actionId: string, userData: ActionCutToCamera ) { @@ -1020,8 +1007,8 @@ async function executeActionCutToCamera< metaData: { sisyfosPersistMetaData: { sisyfosLayers: [], - acceptPersistAudio: sourceInfoCam.acceptPersistAudio, - isPieceInjectedInPart: true + acceptsPersistedAudio: sourceInfoCam.acceptPersistAudio, + isModifiedOrInsertedByAction: userData.cutDirectly } }, tags: [GetTagForKam(userData.sourceDefinition)], @@ -1042,25 +1029,22 @@ async function executeActionCutToCamera< await executePiece(context, settings, kamPiece, !userData.cutDirectly, part) } -async function executePiece< - StudioConfig extends TV2StudioConfigBase, - ShowStyleConfig extends TV2BlueprintConfigBase ->( - context: ActionExecutionContext, - settings: ActionExecutionSettings, +async function executePiece( + context: ActionExecutionContext, + settings: ActionExecutionSettings, pieceToExecute: IBlueprintPiece, shouldBeQueued: boolean, partToQueue: IBlueprintPart ) { - const currentPieceInstances = await context.core.getPieceInstances('current') + const currentPieceInstances = await context.core.getResolvedPieceInstances('current') const isServerInCurrentPart = currentPieceInstances.some( (p) => p.piece.sourceLayerId === settings.SourceLayers.Server || p.piece.sourceLayerId === settings.SourceLayers.VO ) const layersWithCutDirect: string[] = [settings.SourceLayers.Live, settings.SourceLayers.Cam] - const currentPiece: IBlueprintPieceInstance | undefined = findLastPlayingPieceInstance( + const currentPieceInstance: IBlueprintResolvedPieceInstance | undefined = findLastPlayingPieceInstance( currentPieceInstances, - layersWithCutDirect + (p) => layersWithCutDirect.includes(p.piece.sourceLayerId) ) if (shouldBeQueued || isServerInCurrentPart) { @@ -1074,40 +1058,85 @@ async function executePiece< if (isServerInCurrentPart && !shouldBeQueued) { await context.core.takeAfterExecuteAction(true) } - } else if (currentPiece && !isPlannedPieceOnLayer(currentPiece, settings.SourceLayers.Live)) { - pieceToExecute.externalId = currentPiece.piece.externalId - pieceToExecute.enable = currentPiece.piece.enable - const currentMetaData = currentPiece.piece.metaData! - const metaData = pieceToExecute.metaData! - metaData.sisyfosPersistMetaData!.previousPersistMetaDataForCurrentPiece = currentMetaData.sisyfosPersistMetaData + } else if (currentPieceInstance && !isPlannedPieceOnLayer(currentPieceInstance, settings.SourceLayers.Live)) { + await executePieceByReplacingCurrent(context, pieceToExecute, currentPieceInstance, currentPieceInstances) + } else { + await executePieceByStoppingCurrent(context, pieceToExecute, currentPieceInstances, settings) + } +} - await stopGraphicPiecesThatShouldEndWithPart(context, currentPieceInstances) +async function executePieceByStoppingCurrent( + context: ActionExecutionContext, + pieceToExecute: IBlueprintPiece, + currentPieceInstances: Array>, + settings: ActionExecutionSettings +) { + const currentExternalId = await context.core + .getPartInstance('current') + .then((currentPartInstance) => currentPartInstance?.part.externalId) - await context.core.updatePieceInstance(currentPiece._id, pieceToExecute) - } else { - const currentExternalId = await context.core - .getPartInstance('current') - .then((currentPartInstance) => currentPartInstance?.part.externalId) + if (currentExternalId) { + pieceToExecute.externalId = currentExternalId + } - if (currentExternalId) { - pieceToExecute.externalId = currentExternalId - } + const currentPieceInstanceWithMetaData = findLastPlayingPieceInstance( + currentPieceInstances, + (piece) => !!piece.piece.metaData?.sisyfosPersistMetaData + ) - await context.core.stopPiecesOnLayers([ - settings.SourceLayers.DVE, - ...(settings.SourceLayers.DVEAdLib ? [settings.SourceLayers.DVEAdLib] : []), - settings.SourceLayers.Effekt, - settings.SourceLayers.Live, - settings.SourceLayers.Server, - settings.SourceLayers.VO, - ...(settings.SourceLayers.EVS ? [settings.SourceLayers.EVS] : []), - settings.SourceLayers.Continuity - ]) - await stopGraphicPiecesThatShouldEndWithPart(context, currentPieceInstances) + await updatePersistenceMetaData(context, pieceToExecute, currentPieceInstanceWithMetaData) - pieceToExecute.enable = { start: 'now' } - await context.core.insertPiece('current', pieceToExecute) + await context.core.stopPiecesOnLayers([ + settings.SourceLayers.DVE, + ...(settings.SourceLayers.DVEAdLib ? [settings.SourceLayers.DVEAdLib] : []), + settings.SourceLayers.Effekt, + settings.SourceLayers.Live, + settings.SourceLayers.Server, + settings.SourceLayers.VO, + ...(settings.SourceLayers.EVS ? [settings.SourceLayers.EVS] : []), + settings.SourceLayers.Continuity + ]) + await stopGraphicPiecesThatShouldEndWithPart(context, currentPieceInstances) + + pieceToExecute.enable = { start: 'now' } + await context.core.insertPiece('current', pieceToExecute) +} + +async function executePieceByReplacingCurrent( + context: ActionExecutionContext, + pieceToExecute: IBlueprintPiece, + currentPiece: IBlueprintResolvedPieceInstance, + currentPieceInstances: Array> +) { + pieceToExecute.externalId = currentPiece.piece.externalId + pieceToExecute.enable = currentPiece.piece.enable + + await updatePersistenceMetaData(context, pieceToExecute, currentPiece) + + await stopGraphicPiecesThatShouldEndWithPart(context, currentPieceInstances) + + await context.core.updatePieceInstance(currentPiece._id, pieceToExecute) +} + +async function updatePersistenceMetaData( + context: ActionExecutionContext, + pieceToExecute: IBlueprintPiece, + currentPieceInstance: IBlueprintResolvedPieceInstance | undefined +) { + const currentPart = await context.core.getPartInstance('current') + if (!currentPart) { + return } + + const currentMetaData = currentPieceInstance?.piece.metaData + const nextMetaData = pieceToExecute.metaData! + const previousPartEndState = currentPart.previousPartEndState as PartEndStateExt | undefined + nextMetaData.sisyfosPersistMetaData = mergePersistenceMetaData( + currentPart.segmentId, + nextMetaData.sisyfosPersistMetaData!, + currentMetaData?.sisyfosPersistMetaData, + previousPartEndState + ) } function isPlannedPieceOnLayer(currentPiece: IBlueprintPieceInstance, sourceLayerId: string) { @@ -1118,24 +1147,6 @@ function isPlannedPieceOnLayer(currentPiece: IBlueprintPieceInstance>, - sourceLayerIds: string[] -): IBlueprintPieceInstance | undefined { - const playingPiecesOnSelectedLayers = currentPieceInstances.filter( - (p) => !p.stoppedPlayback && sourceLayerIds.includes(p.piece.sourceLayerId) - ) - if (playingPiecesOnSelectedLayers.length <= 1) { - return playingPiecesOnSelectedLayers[0] - } - return playingPiecesOnSelectedLayers.reduce((prev, current) => { - const prevStartedPlayback = prev.startedPlayback ?? prev.dynamicallyInserted?.time ?? Infinity - const currentStartedPlayback = current.startedPlayback ?? current.dynamicallyInserted?.time ?? Infinity - - return prevStartedPlayback > currentStartedPlayback ? prev : current - }) -} - async function stopGraphicPiecesThatShouldEndWithPart( context: ActionExecutionContext, currentPieceInstances: Array> @@ -1160,7 +1171,7 @@ async function executeActionCutToRemote< ShowStyleConfig extends TV2BlueprintConfigBase >( context: ActionExecutionContext, - settings: ActionExecutionSettings, + settings: ActionExecutionSettings, actionId: string, userData: ActionCutToRemote ) { @@ -1183,13 +1194,13 @@ async function executeActionCutToRemote< const eksternSisyfos: TSR.TimelineObjSisyfosAny[] = GetSisyfosTimelineObjForRemote(context.config, sourceInfo) - const sisyfosPersistMetaData: SisyfosPersistMetaData = + const sisyfosPersistMetaData: SisyfosPersistenceMetaData = sourceInfo !== undefined ? { sisyfosLayers: sourceInfo.sisyfosLayers ?? [], wantsToPersistAudio: sourceInfo.wantsToPersistAudio, - acceptPersistAudio: sourceInfo.acceptPersistAudio, - isPieceInjectedInPart: userData.cutDirectly + acceptsPersistedAudio: sourceInfo.acceptPersistAudio, + isModifiedOrInsertedByAction: userData.cutDirectly } : { sisyfosLayers: [] } @@ -1231,7 +1242,7 @@ async function executeActionCutSourceToBox< ShowStyleConfig extends TV2BlueprintConfigBase >( context: ActionExecutionContext, - settings: ActionExecutionSettings, + settings: ActionExecutionSettings, _actionId: string, userData: ActionCutSourceToBox ) { @@ -1351,7 +1362,7 @@ function groupPiecesBySourceLayer(pieceInstances: Array ->(settings: ActionExecutionSettings, piecesBySourceLayer: PiecesBySourceLayer) { +>(settings: ActionExecutionSettings, piecesBySourceLayer: PiecesBySourceLayer) { const sourceLayersOrderedByPriority = [ settings.SourceLayers.Cam, settings.SourceLayers.DVE, @@ -1376,7 +1387,7 @@ async function executeActionTakeWithTransition< ShowStyleConfig extends TV2BlueprintConfigBase >( context: ActionExecutionContext, - settings: ActionExecutionSettings, + settings: ActionExecutionSettings, actionId: string, userData: ActionTakeWithTransition ) { @@ -1589,7 +1600,7 @@ async function findMediaPlayerSessions( async function executeActionCommentatorSelectServer< StudioConfig extends TV2StudioConfigBase, ShowStyleConfig extends TV2BlueprintConfigBase ->(context: ActionExecutionContext, settings: ActionExecutionSettings) { +>(context: ActionExecutionContext, settings: ActionExecutionSettings) { const data = await findDataStore(context, [ settings.SelectedAdlibs.SourceLayer.Server, settings.SelectedAdlibs.SourceLayer.VO @@ -1622,7 +1633,7 @@ async function executeActionCommentatorSelectServer< async function executeActionCommentatorSelectDVE< StudioConfig extends TV2StudioConfigBase, ShowStyleConfig extends TV2BlueprintConfigBase ->(context: ActionExecutionContext, settings: ActionExecutionSettings) { +>(context: ActionExecutionContext, settings: ActionExecutionSettings) { if (!settings.SelectedAdlibs.SourceLayer.DVE) { return } @@ -1639,7 +1650,7 @@ async function executeActionCommentatorSelectDVE< async function executeActionCommentatorSelectFull< StudioConfig extends TV2StudioConfigBase, ShowStyleConfig extends TV2BlueprintConfigBase ->(context: ActionExecutionContext, settings: ActionExecutionSettings) { +>(context: ActionExecutionContext, settings: ActionExecutionSettings) { const data = await findDataStore(context, [SharedSourceLayer.SelectedAdlibGraphicsFull]) if (!data) { @@ -1652,7 +1663,7 @@ async function executeActionCommentatorSelectFull< async function executeActionCommentatorSelectJingle< StudioConfig extends TV2StudioConfigBase, ShowStyleConfig extends TV2BlueprintConfigBase ->(context: ActionExecutionContext, settings: ActionExecutionSettings) { +>(context: ActionExecutionContext, settings: ActionExecutionSettings) { if (!settings.SelectedAdlibs.SourceLayer.Effekt) { return } @@ -1671,7 +1682,7 @@ async function executeActionRecallLastLive< ShowStyleConfig extends TV2BlueprintConfigBase >( context: ActionExecutionContext, - settings: ActionExecutionSettings, + settings: ActionExecutionSettings, actionId: string ) { const lastLive = await context.core.findLastPieceOnLayer(settings.SourceLayers.Live, { @@ -1728,15 +1739,9 @@ async function executeActionRecallLastDVE< ShowStyleConfig extends TV2BlueprintConfigBase >( context: ActionExecutionContext, - settings: ActionExecutionSettings, + settings: ActionExecutionSettings, actionId: string ) { - const currentPart = context.core.getPartInstance('current') - - if (!currentPart) { - return - } - const lastPlayedScheduledDVE = (await context.core.findLastPieceOnLayer(settings.SourceLayers.DVE, { originalOnly: true })) as IBlueprintPieceInstance | undefined @@ -1778,22 +1783,29 @@ async function addLatestPieceOnLayerForDve( } async function executeActionFadeDownPersistedAudioLevels(context: ActionExecutionContext) { - const fadeSisyfosMetaData = await createFadeSisyfosLevelsMetaData(context) - const resetSisyfosPersistedLevelsPiece: IBlueprintPiece = { - externalId: 'fadeSisyfosPersistedLevelsDown', - name: FADE_SISYFOS_LEVELS_PIECE_NAME, - outputLayerId: '', - sourceLayerId: '', - enable: { start: 'now' }, - lifespan: PieceLifespan.WithinPart, - metaData: { - sisyfosPersistMetaData: fadeSisyfosMetaData - }, - content: { - timelineObjects: [] - } + const resolvedPieceInstances = await context.core.getResolvedPieceInstances('current') + if (resolvedPieceInstances.length === 0) { + return } - await context.core.insertPiece('current', resetSisyfosPersistedLevelsPiece) + + const latestPiece = findLastPlayingPieceInstance( + resolvedPieceInstances, + (piece) => !!piece.piece.metaData?.sisyfosPersistMetaData + ) + if (!latestPiece) { + return + } + + latestPiece.piece.content.timelineObjects = latestPiece.piece.content.timelineObjects.filter( + (timelineObject) => timelineObject.layer !== SharedSisyfosLLayer.SisyfosPersistedLevels + ) + const latestPieceMetaData = latestPiece.piece.metaData?.sisyfosPersistMetaData + if (latestPieceMetaData) { + delete latestPieceMetaData.previousSisyfosLayers + delete latestPieceMetaData.acceptsPersistedAudio + latestPieceMetaData.isModifiedOrInsertedByAction = true + } + return context.core.updatePieceInstance(latestPiece._id, latestPiece.piece) } async function executeActionCallRobotPreset(context: ActionExecutionContext, preset: number): Promise { @@ -1805,38 +1817,12 @@ async function executeActionCallRobotPreset(context: ActionExecutionContext, pre await context.core.insertPiece('current', robotCameraPiece) } -async function createFadeSisyfosLevelsMetaData(context: ActionExecutionContext) { - const resolvedPieceInstances = await context.core.getResolvedPieceInstances('current') - const emptySisyfosMetaData: SisyfosPersistMetaData = { - sisyfosLayers: [] - } - if (resolvedPieceInstances.length === 0) { - return emptySisyfosMetaData - } - - const latestPiece = resolvedPieceInstances - .filter((piece) => piece.piece.name !== FADE_SISYFOS_LEVELS_PIECE_NAME) - .sort((a, b) => b.resolvedStart - a.resolvedStart)[0] - - const latestPieceMetaData = latestPiece.piece.metaData - - if (!latestPieceMetaData || !latestPieceMetaData.sisyfosPersistMetaData) { - return emptySisyfosMetaData - } - - return { - sisyfosLayers: latestPieceMetaData.sisyfosPersistMetaData.sisyfosLayers, - wantsToPersistAudio: latestPieceMetaData.sisyfosPersistMetaData.wantsToPersistAudio, - acceptPersistAudio: false - } -} - async function scheduleLastPlayedDVE< StudioConfig extends TV2StudioConfigBase, ShowStyleConfig extends TV2BlueprintConfigBase >( context: ActionExecutionContext, - settings: ActionExecutionSettings, + settings: ActionExecutionSettings, actionId: string, lastPlayedDVE: IBlueprintPieceInstance ): Promise { @@ -1857,7 +1843,7 @@ async function executeActionSelectFull< ShowStyleConfig extends TV2BlueprintConfigBase >( context: ActionExecutionContext, - settings: ActionExecutionSettings, + settings: ActionExecutionSettings, userData: ActionSelectFullGrafik ) { const externalId = generateExternalId(context, 'cut_to_full', [userData.name]) @@ -1968,3 +1954,28 @@ async function executeActionClearGraphics< tags: userData.sendCommands ? [TallyTags.GFX_CLEAR] : [TallyTags.GFX_ALTUD] }) } + +export function mergePersistenceMetaData( + currentSegmentId: string, + nextMetaData: SisyfosPersistenceMetaData, + currentMetaData: SisyfosPersistenceMetaData | undefined, + previousPartEndState: PartEndStateExt | undefined +): SisyfosPersistenceMetaData { + if (!currentMetaData) { + return nextMetaData + } + if (nextMetaData.acceptsPersistedAudio && currentMetaData.wantsToPersistAudio) { + const includePreviousPart = + !currentMetaData.isModifiedOrInsertedByAction && + previousPartEndState && + previousPartEndState.segmentId === currentSegmentId + nextMetaData.previousSisyfosLayers = Array.from( + new Set([ + ...(includePreviousPart ? previousPartEndState?.sisyfosPersistenceMetaData.sisyfosLayers : []), + ...(currentMetaData.previousSisyfosLayers ?? []), + ...currentMetaData.sisyfosLayers + ]) + ) + } + return nextMetaData +} diff --git a/src/tv2-common/cues/ekstern.ts b/src/tv2-common/cues/ekstern.ts index cb5d86a1..d0cba50b 100644 --- a/src/tv2-common/cues/ekstern.ts +++ b/src/tv2-common/cues/ekstern.ts @@ -61,7 +61,7 @@ export function EvaluateEksternBase< sisyfosPersistMetaData: { sisyfosLayers: sourceInfoEkstern.sisyfosLayers ?? [], wantsToPersistAudio: sourceInfoEkstern.wantsToPersistAudio, - acceptPersistAudio: sourceInfoEkstern.acceptPersistAudio + acceptsPersistedAudio: sourceInfoEkstern.acceptPersistAudio } }, content: literal>({ @@ -98,7 +98,7 @@ export function EvaluateEksternBase< sisyfosPersistMetaData: { sisyfosLayers: sourceInfoEkstern.sisyfosLayers ?? [], wantsToPersistAudio: sourceInfoEkstern.wantsToPersistAudio, - acceptPersistAudio: sourceInfoEkstern.acceptPersistAudio + acceptsPersistedAudio: sourceInfoEkstern.acceptPersistAudio } }, tags: [GetTagForLive(parsedCue.sourceDefinition)], diff --git a/src/tv2-common/evaluateCues.ts b/src/tv2-common/evaluateCues.ts index b35e58a9..ec0ec2ef 100644 --- a/src/tv2-common/evaluateCues.ts +++ b/src/tv2-common/evaluateCues.ts @@ -21,6 +21,7 @@ import { IsTargetingFull, IsTargetingOVL, PartDefinition, + PieceMetaData, ShowStyleContext } from 'tv2-common' import { CueType } from 'tv2-constants' @@ -41,8 +42,8 @@ export interface Adlib { } export class EvaluateCueResult { - public readonly pieces: IBlueprintPiece[] = [] - public readonly adlibPieces: IBlueprintAdLibPiece[] = [] + public readonly pieces: Array> = [] + public readonly adlibPieces: Array> = [] public readonly actions: IBlueprintActionManifest[] = [] public push(source: EvaluateCueResult): EvaluateCueResult { diff --git a/src/tv2-common/helpers/graphics/internal/InternalGraphic.ts b/src/tv2-common/helpers/graphics/internal/InternalGraphic.ts index e32a132e..c2c722c8 100644 --- a/src/tv2-common/helpers/graphics/internal/InternalGraphic.ts +++ b/src/tv2-common/helpers/graphics/internal/InternalGraphic.ts @@ -57,11 +57,6 @@ export abstract class InternalGraphic extends Graphic { sourceLayerId: this.sourceLayerId, outputLayerId: SharedOutputLayer.OVERLAY, lifespan: PieceLifespan.WithinPart, - metaData: { - sisyfosPersistMetaData: { - sisyfosLayers: [] - } - }, expectedDuration: 5000, tags: [AdlibTags.ADLIB_KOMMENTATOR], content: _.clone(this.content) @@ -83,11 +78,6 @@ export abstract class InternalGraphic extends Graphic { expectedDuration: this.getPieceEnable().duration }), lifespan: this.getPieceLifespan(), - metaData: { - sisyfosPersistMetaData: { - sisyfosLayers: [] - } - }, content: _.clone(this.content) } } @@ -105,9 +95,6 @@ export abstract class InternalGraphic extends Graphic { sourceLayerId: this.sourceLayerId, lifespan: this.getPieceLifespan(), metaData: { - sisyfosPersistMetaData: { - sisyfosLayers: [] - }, partType: this.partDefinition?.type, pieceExternalId: this.partDefinition?.externalId }, diff --git a/src/tv2-common/helpers/graphics/pilot/PilotGraphicGenerator.ts b/src/tv2-common/helpers/graphics/pilot/PilotGraphicGenerator.ts index 4becf094..e494d086 100644 --- a/src/tv2-common/helpers/graphics/pilot/PilotGraphicGenerator.ts +++ b/src/tv2-common/helpers/graphics/pilot/PilotGraphicGenerator.ts @@ -21,10 +21,8 @@ import { HtmlPilotGraphicGenerator, IsTargetingFull, IsTargetingWall, - literal, PieceMetaData, ShowStyleContext, - SisyfosPersistMetaData, t, TV2ShowStyleConfig, VizPilotGraphicGenerator @@ -124,11 +122,6 @@ export abstract class PilotGraphicGenerator extends Graphic { sourceLayerId: this.getSourceLayer(), prerollDuration: this.getPrerollDuration(), lifespan: this.getPieceLifespan(), - metaData: { - sisyfosPersistMetaData: { - sisyfosLayers: [] - } - }, content: this.getContent(), tags: IsTargetingFull(this.engine) ? [GetTagForFull(this.segmentExternalId, this.cue.graphic.vcpid), TallyTags.FULL_IS_LIVE] @@ -136,7 +129,7 @@ export abstract class PilotGraphicGenerator extends Graphic { } } - public createAdlibPiece(rank?: number): IBlueprintAdLibPiece { + public createAdlibPiece(rank?: number): IBlueprintAdLibPiece { const pilotPiece = this.createPiece() pilotPiece.tags = [...(pilotPiece.tags ?? []), AdlibTags.ADLIB_FLOW_PRODUCER, AdlibTags.ADLIB_KOMMENTATOR] return { @@ -170,10 +163,7 @@ export abstract class PilotGraphicGenerator extends Graphic { name: this.cue.graphic.name, vcpid: this.cue.graphic.vcpid, segmentExternalId: this.segmentExternalId - }, - sisyfosPersistMetaData: literal({ - sisyfosLayers: [] - }) + } }, content, tags: [GetTagForFullNext(this.segmentExternalId, this.cue.graphic.vcpid)] diff --git a/src/tv2-common/onTimelineGenerate.ts b/src/tv2-common/onTimelineGenerate.ts index 8614d70e..492a8073 100644 --- a/src/tv2-common/onTimelineGenerate.ts +++ b/src/tv2-common/onTimelineGenerate.ts @@ -20,18 +20,17 @@ import { ServerPosition, TimelineContext } from 'tv2-common' -import { AbstractLLayer, PartType, TallyTags } from 'tv2-constants' +import { AbstractLLayer, PartType, SharedSisyfosLLayer, TallyTags } from 'tv2-constants' import * as _ from 'underscore' -import { SisyfosLLAyer } from '../tv2_afvd_studio/layers' import { TV2BlueprintConfigBase, TV2StudioConfigBase } from './blueprintConfig' export interface PartEndStateExt { - sisyfosPersistMetaData: SisyfosPersistMetaData + sisyfosPersistenceMetaData: SisyfosPersistenceMetaData mediaPlayerSessions: { [layer: string]: string[] } isJingle?: boolean fullFileName?: string serverPosition?: ServerPosition - segmentId?: string + segmentId: string partInstanceId: string } @@ -59,7 +58,7 @@ export type TimelineBlueprintExt = TSR.TSRTimelineObjBase & { } export interface PieceMetaData { - sisyfosPersistMetaData?: SisyfosPersistMetaData + sisyfosPersistMetaData?: SisyfosPersistenceMetaData mediaPlayerSessions?: string[] modifiedByAction?: boolean } @@ -81,12 +80,27 @@ export interface ServerPieceMetaData extends PieceMetaData { userData: ActionSelectServerClip } -export interface SisyfosPersistMetaData { +export interface SisyfosPersistenceMetaData { + /** + * The layers this piece wants to persist into the next part + */ sisyfosLayers: string[] + /** + * The layers this piece gathered from previous pieces and wants to persist into the next part + */ + previousSisyfosLayers?: string[] + /** + * Whether `sisyfosLayers` and `previousSisyfosLayers` may be persisted into the next part if accepted + */ wantsToPersistAudio?: boolean - acceptPersistAudio?: boolean - previousPersistMetaDataForCurrentPiece?: SisyfosPersistMetaData - isPieceInjectedInPart?: boolean + /** + * Whether `sisyfosLayers` and `previousSisyfosLayers` from the previous part may be persisted + */ + acceptsPersistedAudio?: boolean + /** + * Whether the piece was inserted/updated by fast Camera/Live cutting within a part or fading down persisted levels + */ + isModifiedOrInsertedByAction?: boolean } export interface PartMetaData { @@ -112,10 +126,16 @@ export function onTimelineGenerate< isNewSegment: context.core.previousPartInstance?.segmentId !== context.core.currentPartInstance?.segmentId } - if (!persistentState.isNewSegment || isAnyPieceInjectedIntoPart(context, resolvedPieces)) { + if ( + (!persistentState.isNewSegment || isAnyPieceInjectedIntoPart(context, resolvedPieces)) && + context.core.currentPartInstance + ) { const sisyfosPersistedLevelsTimelineObject = createSisyfosPersistedLevelsTimelineObject( + context.core.currentPartInstance._id, resolvedPieces, - previousPartEndState2 ? previousPartEndState2.sisyfosPersistMetaData.sisyfosLayers : [] + previousPartEndState2 && !persistentState.isNewSegment + ? previousPartEndState2.sisyfosPersistenceMetaData.sisyfosLayers + : [] ) timeline.push(sisyfosPersistedLevelsTimelineObject) } @@ -211,26 +231,25 @@ function isAnyPieceInjectedIntoPart( return resolvedPieces .filter((piece) => piece.partInstanceId === context.core.currentPartInstance?._id) .some((piece) => { - return piece.piece.metaData?.sisyfosPersistMetaData?.isPieceInjectedInPart + return piece.piece.metaData?.sisyfosPersistMetaData?.isModifiedOrInsertedByAction }) } export function getEndStateForPart( _context: IRundownContext, - _previousPersistentState: TimelinePersistentState | undefined, + previousPersistentState: TimelinePersistentState | undefined, partInstance: IBlueprintPartInstance, resolvedPieces: Array>, time: number ): PartEndState { const endState: PartEndStateExt = { - sisyfosPersistMetaData: { + sisyfosPersistenceMetaData: { sisyfosLayers: [] }, mediaPlayerSessions: {}, segmentId: partInstance.segmentId, partInstanceId: partInstance._id } - const previousPartEndState = partInstance?.previousPartEndState as Partial const activePieces = resolvedPieces.filter( (p) => @@ -240,11 +259,12 @@ export function getEndStateForPart( (!p.piece.enable.duration || p.piece.enable.start + p.piece.enable.duration >= time) ) - const previousPersistentState: TimelinePersistentStateExt = _previousPersistentState as TimelinePersistentStateExt - endState.sisyfosPersistMetaData.sisyfosLayers = findLayersToPersist( + const previousPartEndState = partInstance?.previousPartEndState as Partial + const previousPersistentStateExt: TimelinePersistentStateExt = previousPersistentState as TimelinePersistentStateExt + endState.sisyfosPersistenceMetaData.sisyfosLayers = findLayersToPersistOnPartEnd( activePieces, - !previousPersistentState.isNewSegment && previousPartEndState && previousPartEndState.sisyfosPersistMetaData - ? previousPartEndState.sisyfosPersistMetaData.sisyfosLayers + !previousPersistentStateExt.isNewSegment && previousPartEndState && previousPartEndState.sisyfosPersistenceMetaData + ? previousPartEndState.sisyfosPersistenceMetaData.sisyfosLayers : [] ) @@ -271,16 +291,21 @@ export function getEndStateForPart( } export function createSisyfosPersistedLevelsTimelineObject( + currentPartInstanceId: string, resolvedPieces: Array>, - previousSisyfosLayersThatWantsToBePersisted: SisyfosPersistMetaData['sisyfosLayers'] + layersWantingToPersistFromPreviousPart: string[] ): TSR.TimelineObjSisyfosChannels { - const layersToPersist = findLayersToPersist(resolvedPieces, previousSisyfosLayersThatWantsToBePersisted) + const layersToPersist = findPersistedLayers( + currentPartInstanceId, + resolvedPieces, + layersWantingToPersistFromPreviousPart + ) return { id: 'sisyfosPersistenceObject', enable: { start: 0 }, - layer: SisyfosLLAyer.SisyfosPersistedLevels, + layer: SharedSisyfosLLayer.SisyfosPersistedLevels, content: { deviceType: TSR.DeviceType.SISYFOS, type: TSR.TimelineContentTypeSisyfos.CHANNELS, @@ -295,53 +320,72 @@ export function createSisyfosPersistedLevelsTimelineObject( } } -function findLayersToPersist( - pieces: Array>, - sisyfosLayersThatWantsToBePersisted: string[] +function findLayersToPersistOnPartEnd( + pieceInstances: Array>, + layersWantingToPersistFromPreviousPart: string[] = [] ): string[] { - const sortedPieces = pieces - .filter((piece) => piece.piece.metaData?.sisyfosPersistMetaData) - .sort((a, b) => b.resolvedStart - a.resolvedStart) + const latestPieceInstance = findLastPlayingPieceInstance( + pieceInstances, + (pieceInstance) => !!pieceInstance.piece.metaData?.sisyfosPersistMetaData + ) + const latestPieceMetaData = latestPieceInstance?.piece.metaData?.sisyfosPersistMetaData - if (sortedPieces.length === 0) { + if (!latestPieceMetaData?.wantsToPersistAudio) { return [] } - const firstPieceMetaData = sortedPieces[0].piece.metaData! - if (!firstPieceMetaData.sisyfosPersistMetaData!.acceptPersistAudio) { - return firstPieceMetaData.sisyfosPersistMetaData!.wantsToPersistAudio - ? firstPieceMetaData.sisyfosPersistMetaData!.sisyfosLayers - : [] + if (!latestPieceMetaData.acceptsPersistedAudio) { + return latestPieceMetaData.wantsToPersistAudio ? latestPieceMetaData.sisyfosLayers : [] } const layersToPersist: string[] = [] - for (let i = 0; i < sortedPieces.length; i++) { - const pieceMetaData = sortedPieces[i].piece.metaData! - const sisyfosPersistMetaData: SisyfosPersistMetaData = pieceMetaData.sisyfosPersistMetaData! - if (sisyfosPersistMetaData.wantsToPersistAudio) { - layersToPersist.push(...sisyfosPersistMetaData.sisyfosLayers) - } + if (!latestPieceMetaData?.isModifiedOrInsertedByAction) { + layersToPersist.push(...layersWantingToPersistFromPreviousPart) + } else if (latestPieceMetaData.previousSisyfosLayers) { + layersToPersist.push(...latestPieceMetaData.previousSisyfosLayers) + } + layersToPersist.push(...latestPieceMetaData.sisyfosLayers) - if (doesMetaDataNotAcceptPersistAudioDeep(sisyfosPersistMetaData)) { - break - } + return Array.from(new Set(layersToPersist)) +} - if (i === sortedPieces.length - 1) { - layersToPersist.push(...sisyfosLayersThatWantsToBePersisted) - } +function findPersistedLayers( + currentPartInstanceId: string, + pieceInstances: Array>, + layersWantingToPersistFromPreviousPart: string[] +): string[] { + const latestPieceInstance = findLastPlayingPieceInstance( + pieceInstances, + (pieceInstance) => + !!pieceInstance.piece.metaData?.sisyfosPersistMetaData && pieceInstance.partInstanceId === currentPartInstanceId + ) + + const latestPieceMetaData = latestPieceInstance?.piece.metaData + if (!latestPieceMetaData?.sisyfosPersistMetaData!.acceptsPersistedAudio) { + return [] + } + + const layersToPersist: string[] = [] + if (!latestPieceMetaData.sisyfosPersistMetaData?.isModifiedOrInsertedByAction) { + layersToPersist.push(...layersWantingToPersistFromPreviousPart) + } else if (latestPieceMetaData.sisyfosPersistMetaData.previousSisyfosLayers) { + layersToPersist.push(...latestPieceMetaData.sisyfosPersistMetaData.previousSisyfosLayers) } return Array.from(new Set(layersToPersist)) } -function doesMetaDataNotAcceptPersistAudioDeep(metaData: SisyfosPersistMetaData): boolean { - if (!metaData.acceptPersistAudio) { - return true +export function findLastPlayingPieceInstance( + currentPieceInstances: Array>, + predicate?: (pieceInstance: IBlueprintResolvedPieceInstance) => boolean +): IBlueprintResolvedPieceInstance | undefined { + const playingPieces = currentPieceInstances.filter((p) => !p.stoppedPlayback && (predicate ? predicate(p) : true)) + if (playingPieces.length <= 1) { + return playingPieces[0] } - if (metaData.previousPersistMetaDataForCurrentPiece) { - return doesMetaDataNotAcceptPersistAudioDeep(metaData.previousPersistMetaDataForCurrentPiece) - } - return false + return playingPieces.reduce((prev, current) => { + return prev.resolvedStart > current.resolvedStart ? prev : current + }) } export function disablePilotWipeAfterJingle( diff --git a/src/tv2-common/parts/server.ts b/src/tv2-common/parts/server.ts index 986ac9ae..981e5f7c 100644 --- a/src/tv2-common/parts/server.ts +++ b/src/tv2-common/parts/server.ts @@ -232,7 +232,7 @@ function getServerSelectionBlueprintPiece( userData: userDataElement, sisyfosPersistMetaData: { sisyfosLayers: [], - acceptPersistAudio: partProps.adLibPix && partProps.voLevels + acceptsPersistedAudio: partProps.adLibPix && partProps.voLevels } }, content: contentServerElement, diff --git a/src/tv2-constants/enums.ts b/src/tv2-constants/enums.ts index 3ef33d20..5b6c089b 100644 --- a/src/tv2-constants/enums.ts +++ b/src/tv2-constants/enums.ts @@ -234,7 +234,8 @@ export enum SharedCasparLLayer { export enum SharedSisyfosLLayer { SisyfosSourceAudiobed = 'sisyfos_source_audiobed', SisyfosResync = 'sisyfos_resync', - SisyfosGroupStudioMics = 'sisyfos_group_studio_mics' + SisyfosGroupStudioMics = 'sisyfos_group_studio_mics', + SisyfosPersistedLevels = 'sisyfos_persisted_levels' } export enum RobotCameraLayer { diff --git a/src/tv2_afvd_showstyle/GlobalAdlibActionsGenerator.ts b/src/tv2_afvd_showstyle/GlobalAdlibActionsGenerator.ts index db72624e..6922e69d 100644 --- a/src/tv2_afvd_showstyle/GlobalAdlibActionsGenerator.ts +++ b/src/tv2_afvd_showstyle/GlobalAdlibActionsGenerator.ts @@ -39,8 +39,8 @@ export class GlobalAdlibActionsGenerator { this.config.sources.cameras .slice(0, 5) // the first x cameras to create INP1/2/3 cam-adlibs from .forEach((camera) => { - blueprintActions.push(this.makeCutDirectlyCameraAction(camera, globalRank++)) - blueprintActions.push(this.makeQueueAsNextCameraAction(camera, globalRank++)) + blueprintActions.push(this.makeCutCameraDirectlyAction(camera, globalRank++)) + blueprintActions.push(this.makeQueueCameraAsNextAction(camera, globalRank++)) }) this.config.sources.cameras @@ -53,7 +53,8 @@ export class GlobalAdlibActionsGenerator { .slice(0, 10) // the first x remote to create INP1/2/3 live-adlibs from .forEach((live) => { blueprintActions.push(...this.makeAdlibBoxesActions(live, globalRank++)) - blueprintActions.push(this.makeCutDirectLiveAction(live, globalRank++)) + blueprintActions.push(this.makeCutDirectlyLiveAction(live, globalRank++)) + blueprintActions.push(this.makeQueueAsNextLiveAction(live, globalRank++)) }) this.config.sources.feeds @@ -88,14 +89,22 @@ export class GlobalAdlibActionsGenerator { return blueprintActions } - private makeCutDirectlyCameraAction(cameraSourceInfo: SourceInfo, rank: number): IBlueprintActionManifest { + private makeCutCameraDirectlyAction(cameraSourceInfo: SourceInfo, rank: number): IBlueprintActionManifest { return this.makeCutCameraAction(cameraSourceInfo, true, rank) } - private makeQueueAsNextCameraAction(cameraSourceInfo: SourceInfo, rank: number): IBlueprintActionManifest { + private makeQueueCameraAsNextAction(cameraSourceInfo: SourceInfo, rank: number): IBlueprintActionManifest { return this.makeCutCameraAction(cameraSourceInfo, false, rank) } + private makeCutDirectlyLiveAction(cameraSourceInfo: SourceInfo, rank: number): IBlueprintActionManifest { + return this.makeCutLiveAction(cameraSourceInfo, true, rank) + } + + private makeQueueAsNextLiveAction(cameraSourceInfo: SourceInfo, rank: number): IBlueprintActionManifest { + return this.makeCutLiveAction(cameraSourceInfo, false, rank) + } + private makeCutCameraAction( cameraSourceInfo: SourceInfo, cutDirectly: boolean, @@ -123,11 +132,11 @@ export class GlobalAdlibActionsGenerator { } } - private makeCutDirectLiveAction(info: SourceInfo, rank: number): IBlueprintActionManifest { + private makeCutLiveAction(info: SourceInfo, cutDirectly: boolean, rank: number): IBlueprintActionManifest { const sourceDefinition = SourceInfoToSourceDefinition(info) as SourceDefinitionRemote const userData: ActionCutToRemote = { type: AdlibActionType.CUT_TO_REMOTE, - cutDirectly: true, + cutDirectly, sourceDefinition } return { @@ -141,7 +150,7 @@ export class GlobalAdlibActionsGenerator { sourceLayerId: SourceLayer.PgmLive, outputLayerId: SharedOutputLayer.PGM, content: {}, - tags: [AdlibTags.ADLIB_CUT_DIRECT] + tags: cutDirectly ? [AdlibTags.ADLIB_CUT_DIRECT] : [AdlibTags.ADLIB_QUEUE_NEXT] } } } diff --git a/src/tv2_afvd_showstyle/getRundown.ts b/src/tv2_afvd_showstyle/getRundown.ts index 82d180b2..386b8cdb 100644 --- a/src/tv2_afvd_showstyle/getRundown.ts +++ b/src/tv2_afvd_showstyle/getRundown.ts @@ -20,7 +20,6 @@ import { findDskJingle, getGraphicBaseline, getMixMinusTimelineObject, - GetSisyfosTimelineObjForRemote, GetSisyfosTimelineObjForReplay, literal, MixMinusPriority, @@ -33,14 +32,7 @@ import { SwitcherType, TransitionStyle } from 'tv2-common' -import { - AdlibTags, - CONSTANTS, - ControlClasses, - SharedGraphicLLayer, - SharedOutputLayer, - SwitcherAuxLLayer -} from 'tv2-constants' +import { AdlibTags, CONSTANTS, SharedGraphicLLayer, SharedOutputLayer, SwitcherAuxLLayer } from 'tv2-constants' import * as _ from 'underscore' import { getMixEffectBaseline } from '../tv2_afvd_studio/getBaseline' import { CasparLLayer, SisyfosLLAyer } from '../tv2_afvd_studio/layers' @@ -77,12 +69,6 @@ class GlobalAdLibPiecesGenerator { const adLibPieces: IBlueprintAdLibPiece[] = [] let globalRank = 1000 - this.config.sources.lives - .slice(0, 10) // the first x lives to create live-adlibs from - .forEach((info) => { - adLibPieces.push(...this.makeRemoteAdLibs(info, globalRank++)) - }) - this.config.sources.lives .slice(0, 10) // the first x lives to create AUX1 (studio) adlibs .forEach((info) => { @@ -173,7 +159,7 @@ class GlobalAdLibPiecesGenerator { metaData: { sisyfosPersistMetaData: { sisyfosLayers: info.sisyfosLayers ?? [], - acceptPersistAudio: vo + acceptsPersistedAudio: vo } }, tags: [AdlibTags.ADLIB_QUEUE_NEXT, vo ? AdlibTags.ADLIB_VO_AUDIO_LEVEL : AdlibTags.ADLIB_FULL_AUDIO_LEVEL], @@ -244,45 +230,6 @@ class GlobalAdLibPiecesGenerator { } } - private makeRemoteAdLibs(info: SourceInfo, rank: number): Array> { - const res: Array> = [] - const eksternSisyfos = GetSisyfosTimelineObjForRemote(this.config, info) - res.push({ - externalId: 'live', - name: `LIVE ${info.id}`, - _rank: rank, - sourceLayerId: SourceLayer.PgmLive, - outputLayerId: SharedOutputLayer.PGM, - expectedDuration: 0, - lifespan: PieceLifespan.WithinPart, - toBeQueued: true, - metaData: { - sisyfosPersistMetaData: { - sisyfosLayers: info.sisyfosLayers ?? [], - wantsToPersistAudio: info.wantsToPersistAudio, - acceptPersistAudio: info.acceptPersistAudio - } - }, - tags: [AdlibTags.ADLIB_QUEUE_NEXT], - content: { - timelineObjects: [ - ...this.context.videoSwitcher.getOnAirTimelineObjectsWithLookahead({ - enable: { while: '1' }, - priority: 1, - content: { - input: info.port, - transition: TransitionStyle.CUT - }, - classes: [ControlClasses.OVERRIDDEN_ON_MIX_MINUS] - }), - ...eksternSisyfos - ] - } - }) - - return res - } - // aux adlibs private makeRemoteAuxStudioAdLibs(info: SourceInfo, rank: number): Array> { const res: Array> = [] @@ -298,7 +245,7 @@ class GlobalAdLibPiecesGenerator { sisyfosPersistMetaData: { sisyfosLayers: info.sisyfosLayers ?? [], wantsToPersistAudio: info.wantsToPersistAudio, - acceptPersistAudio: info.acceptPersistAudio + acceptsPersistedAudio: info.acceptPersistAudio } }, tags: [AdlibTags.ADLIB_TO_STUDIO_SCREEN_AUX], diff --git a/src/tv2_afvd_showstyle/helpers/pieces/__tests__/grafikViz.spec.ts b/src/tv2_afvd_showstyle/helpers/pieces/__tests__/grafikViz.spec.ts index 88555a87..ed43b987 100644 --- a/src/tv2_afvd_showstyle/helpers/pieces/__tests__/grafikViz.spec.ts +++ b/src/tv2_afvd_showstyle/helpers/pieces/__tests__/grafikViz.spec.ts @@ -119,9 +119,6 @@ describe('grafik piece', () => { }, lifespan: PieceLifespan.WithinPart, metaData: { - sisyfosPersistMetaData: { - sisyfosLayers: [] - }, partType: PartType.Kam, pieceExternalId: dummyPart.externalId }, @@ -183,11 +180,6 @@ describe('grafik piece', () => { externalId: partId, name: 'bund - Odense\n - Copenhagen', lifespan: PieceLifespan.WithinPart, - metaData: literal({ - sisyfosPersistMetaData: { - sisyfosLayers: [] - } - }), outputLayerId: SharedOutputLayer.OVERLAY, sourceLayerId: SourceLayer.PgmGraphicsLower, uniquenessId: 'gfx_bund - Odense\n - Copenhagen_studio0_graphicsLower_overlay_commentator', @@ -223,11 +215,6 @@ describe('grafik piece', () => { externalId: partId, name: 'bund - Odense\n - Copenhagen', lifespan: PieceLifespan.WithinPart, - metaData: literal({ - sisyfosPersistMetaData: { - sisyfosLayers: [] - } - }), outputLayerId: SharedOutputLayer.OVERLAY, sourceLayerId: SourceLayer.PgmGraphicsLower, uniquenessId: 'gfx_bund - Odense\n - Copenhagen_studio0_graphicsLower_overlay_flow', @@ -289,11 +276,6 @@ describe('grafik piece', () => { externalId: partId, name: 'bund - Odense\n - Copenhagen', lifespan: PieceLifespan.WithinPart, - metaData: literal({ - sisyfosPersistMetaData: { - sisyfosLayers: [] - } - }), outputLayerId: SharedOutputLayer.OVERLAY, sourceLayerId: SourceLayer.PgmGraphicsLower, uniquenessId: 'gfx_bund - Odense\n - Copenhagen_studio0_graphicsLower_overlay_commentator', @@ -329,11 +311,6 @@ describe('grafik piece', () => { externalId: partId, name: 'bund - Odense\n - Copenhagen', lifespan: PieceLifespan.WithinPart, - metaData: literal({ - sisyfosPersistMetaData: { - sisyfosLayers: [] - } - }), outputLayerId: SharedOutputLayer.OVERLAY, sourceLayerId: SourceLayer.PgmGraphicsLower, uniquenessId: 'gfx_bund - Odense\n - Copenhagen_studio0_graphicsLower_overlay_flow', @@ -401,9 +378,6 @@ describe('grafik piece', () => { }, lifespan: PieceLifespan.WithinPart, metaData: { - sisyfosPersistMetaData: { - sisyfosLayers: [] - }, partType: PartType.Kam, pieceExternalId: dummyPart.externalId }, @@ -552,9 +526,6 @@ describe('grafik piece', () => { }, lifespan: PieceLifespan.WithinPart, metaData: { - sisyfosPersistMetaData: { - sisyfosLayers: [] - }, partType: PartType.Kam, pieceExternalId: dummyPart.externalId }, @@ -622,9 +593,6 @@ describe('grafik piece', () => { }, lifespan: PieceLifespan.WithinPart, metaData: { - sisyfosPersistMetaData: { - sisyfosLayers: [] - }, partType: PartType.Kam, pieceExternalId: dummyPart.externalId }, @@ -686,11 +654,6 @@ describe('grafik piece', () => { externalId: partId, name: 'tlftoptlive - Line 1\n - Line 2', lifespan: PieceLifespan.WithinPart, - metaData: literal({ - sisyfosPersistMetaData: { - sisyfosLayers: [] - } - }), outputLayerId: SharedOutputLayer.OVERLAY, sourceLayerId: SourceLayer.PgmGraphicsTop, expectedDuration: 5000, @@ -726,11 +689,6 @@ describe('grafik piece', () => { externalId: partId, name: 'tlftoptlive - Line 1\n - Line 2', lifespan: PieceLifespan.OutOnSegmentEnd, - metaData: literal({ - sisyfosPersistMetaData: { - sisyfosLayers: [] - } - }), outputLayerId: SharedOutputLayer.OVERLAY, sourceLayerId: SourceLayer.PgmGraphicsTop, tags: ['flow_producer'], diff --git a/src/tv2_afvd_showstyle/helpers/pieces/__tests__/telefon.spec.ts b/src/tv2_afvd_showstyle/helpers/pieces/__tests__/telefon.spec.ts index 2280a97e..0b7d853a 100644 --- a/src/tv2_afvd_showstyle/helpers/pieces/__tests__/telefon.spec.ts +++ b/src/tv2_afvd_showstyle/helpers/pieces/__tests__/telefon.spec.ts @@ -71,9 +71,6 @@ describe('telefon', () => { sourceLayerId: SourceLayer.PgmGraphicsLower, lifespan: PieceLifespan.WithinPart, metaData: { - sisyfosPersistMetaData: { - sisyfosLayers: [] - }, partType: PartType.Kam, pieceExternalId: dummyPart.externalId }, diff --git a/src/tv2_afvd_showstyle/helpers/pieces/dve.ts b/src/tv2_afvd_showstyle/helpers/pieces/dve.ts index bd3a2764..800babd1 100644 --- a/src/tv2_afvd_showstyle/helpers/pieces/dve.ts +++ b/src/tv2_afvd_showstyle/helpers/pieces/dve.ts @@ -97,9 +97,6 @@ export function EvaluateDVE( name: pieceName, videoId: partDefinition.fields.videoId, segmentExternalId: partDefinition.segmentExternalId - }, - sisyfosPersistMetaData: { - sisyfosLayers: [] } } }) diff --git a/src/tv2_afvd_showstyle/parts/kam.ts b/src/tv2_afvd_showstyle/parts/kam.ts index ee7d598c..6c4f5f24 100644 --- a/src/tv2_afvd_showstyle/parts/kam.ts +++ b/src/tv2_afvd_showstyle/parts/kam.ts @@ -53,11 +53,6 @@ export async function CreatePartKam( outputLayerId: SharedOutputLayer.PGM, sourceLayerId: SourceLayer.PgmJingle, lifespan: PieceLifespan.WithinPart, - metaData: { - sisyfosPersistMetaData: { - sisyfosLayers: [] - } - }, content: literal>({ ignoreMediaObjectStatus: true, fileName: '', @@ -95,7 +90,7 @@ export async function CreatePartKam( metaData: { sisyfosPersistMetaData: { sisyfosLayers: sourceInfoCam.sisyfosLayers ?? [], - acceptPersistAudio: sourceInfoCam.acceptPersistAudio + acceptsPersistedAudio: sourceInfoCam.acceptPersistAudio } }, content: { diff --git a/src/tv2_afvd_studio/layers.ts b/src/tv2_afvd_studio/layers.ts index 37ede432..d06bf58f 100644 --- a/src/tv2_afvd_studio/layers.ts +++ b/src/tv2_afvd_studio/layers.ts @@ -36,7 +36,6 @@ export type GraphicLLayer = AFVDGraphicLLayer | SharedGraphicLLayer enum AFVDSisyfosLLayer { SisyfosConfig = 'sisyfos_config', - SisyfosPersistedLevels = 'sisyfos_persisted_levels', SisyfosSourceClipPending = 'sisyfos_source_clip_pending', SisyfosSourceJingle = 'sisyfos_source_jingle', SisyfosSourceTLF = 'sisyfos_source_tlf_hybrid', diff --git a/src/tv2_offtube_showstyle/cues/OfftubeDVE.ts b/src/tv2_offtube_showstyle/cues/OfftubeDVE.ts index 456e0e77..56da4ae6 100644 --- a/src/tv2_offtube_showstyle/cues/OfftubeDVE.ts +++ b/src/tv2_offtube_showstyle/cues/OfftubeDVE.ts @@ -77,9 +77,6 @@ export function OfftubeEvaluateDVE( name: parsedCue.template, videoId: partDefinition.fields.videoId, segmentExternalId: partDefinition.segmentExternalId - }, - sisyfosPersistMetaData: { - sisyfosLayers: [] } }), tags: [ diff --git a/src/tv2_offtube_showstyle/cues/OfftubeJingle.ts b/src/tv2_offtube_showstyle/cues/OfftubeJingle.ts index f0383e25..c0165b67 100644 --- a/src/tv2_offtube_showstyle/cues/OfftubeJingle.ts +++ b/src/tv2_offtube_showstyle/cues/OfftubeJingle.ts @@ -80,11 +80,6 @@ export function OfftubeEvaluateJingle( lifespan: PieceLifespan.WithinPart, outputLayerId: SharedOutputLayer.JINGLE, sourceLayerId: OfftubeSourceLayer.PgmJingle, - metaData: { - sisyfosPersistMetaData: { - sisyfosLayers: [] - } - }, prerollDuration: context.config.studio.CasparPrerollDuration + getTimeFromFrames(Number(jingle.StartAlpha)), content: createJingleContentOfftube(context, file, jingle), tags: [ diff --git a/src/tv2_offtube_showstyle/parts/OfftubeKam.ts b/src/tv2_offtube_showstyle/parts/OfftubeKam.ts index 06ed859e..80336f0e 100644 --- a/src/tv2_offtube_showstyle/parts/OfftubeKam.ts +++ b/src/tv2_offtube_showstyle/parts/OfftubeKam.ts @@ -87,7 +87,7 @@ export async function OfftubeCreatePartKam( metaData: { sisyfosPersistMetaData: { sisyfosLayers: sourceInfoCam.sisyfosLayers ?? [], - acceptPersistAudio: sourceInfoCam.acceptPersistAudio + acceptsPersistedAudio: sourceInfoCam.acceptPersistAudio } }, tags: [GetTagForKam(partDefinition.sourceDefinition)], diff --git a/yarn.lock b/yarn.lock index 0fc6e8f0..dfc6f151 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5956,10 +5956,10 @@ through@2, "through@>=2.2.7 <3": resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= -"timeline-state-resolver-types@npm:@tv2media/timeline-state-resolver-types@3.4.0": - version "3.4.0" - resolved "https://registry.yarnpkg.com/@tv2media/timeline-state-resolver-types/-/timeline-state-resolver-types-3.4.0.tgz#179b66c64616fec2ec541fd9dd5a73517d8706fc" - integrity sha512-P174WyTAa6PVAotOJOEw8Dsqu1L3Cx/jEDmikLDLfFgRgmsYNSjtmgLzuzBY/O39iAxcqXs0ItM7QKcnX+ilwg== +"timeline-state-resolver-types@npm:@tv2media/timeline-state-resolver-types@3.5.0": + version "3.5.0" + resolved "https://registry.yarnpkg.com/@tv2media/timeline-state-resolver-types/-/timeline-state-resolver-types-3.5.0.tgz#02ca12128cbc0df84e630d0da8c66a1d44e5f172" + integrity sha512-DdKXdQf+By8ssBKqd3qfAAhERehlKTorWb3tYK6G6z1vmADSJT+MVAvY0GpLx5/qKe0e3HbutQ2CMlh7b00QIA== dependencies: tslib "^2.3.1" From b6e8a78327e4d865d953303b5c71089f817a4a1f Mon Sep 17 00:00:00 2001 From: ianshade Date: Fri, 19 May 2023 10:13:50 +0200 Subject: [PATCH 08/35] chore: SOF-1213 remove unused shelf layouts --- shelf-layouts/List.json | 8 ----- shelf-layouts/Sisyfos layout.json | 19 ----------- shelf-layouts/Streamdeck.json | 51 ----------------------------- shelf-layouts/dve controls.json | 54 ------------------------------- 4 files changed, 132 deletions(-) delete mode 100755 shelf-layouts/List.json delete mode 100755 shelf-layouts/Sisyfos layout.json delete mode 100755 shelf-layouts/Streamdeck.json delete mode 100755 shelf-layouts/dve controls.json diff --git a/shelf-layouts/List.json b/shelf-layouts/List.json deleted file mode 100755 index cf6fbfdc..00000000 --- a/shelf-layouts/List.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "_id": "pmj8mCofYiHfGiGcn", - "name": "List", - "showStyleBaseId": "show0", - "filters": [], - "type": "dashboard_layout", - "regionId": "shelf_layouts" -} \ No newline at end of file diff --git a/shelf-layouts/Sisyfos layout.json b/shelf-layouts/Sisyfos layout.json deleted file mode 100755 index a6b03037..00000000 --- a/shelf-layouts/Sisyfos layout.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "_id": "v5Pa22MpPDnar9Txm", - "name": "Sisyfos layout", - "showStyleBaseId": "show0", - "filters": [ - { - "_id": "mZBzZX3iyerrQk7nT", - "type": "external_frame", - "name": "Sisyfos", - "currentSegment": false, - "displayStyle": "list", - "rank": 0, - "rundownBaseline": false, - "default": true, - "url": "http://192.168.0.2:1176" - } - ], - "type": "rundown_layout" -} \ No newline at end of file diff --git a/shelf-layouts/Streamdeck.json b/shelf-layouts/Streamdeck.json deleted file mode 100755 index e7230973..00000000 --- a/shelf-layouts/Streamdeck.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "_id": "tBxXcFSt8QgJxWLyi", - "name": "Streamdeck", - "showStyleBaseId": "show0", - "filters": [ - { - "_id": "efuNPn68Mwz3HPPXC", - "type": "filter", - "name": "Floating overlay", - "currentSegment": true, - "displayStyle": "list", - "rank": 0, - "rundownBaseline": false, - "default": false, - "x": 0, - "y": 9, - "width": -4, - "height": -1, - "hide": false, - "includeClearInRundownBaseline": false, - "sourceLayerIds": [ - "studio0_graphicsTop", - "studio0_graphicsLower" - ], - "showAsTimeline": false - }, - { - "_id": "6tBKYCCDbxBzTENLx", - "type": "filter", - "name": "Pvw kams", - "currentSegment": false, - "displayStyle": "list", - "rank": 0, - "rundownBaseline": true, - "default": false, - "x": 0, - "y": 0, - "sourceLayerIds": [ - "studio0_camera" - ], - "width": -4, - "height": 4, - "includeClearInRundownBaseline": true, - "label": [ - "k" - ] - } - ], - "type": "dashboard_layout", - "regionId": "shelf_layouts" -} \ No newline at end of file diff --git a/shelf-layouts/dve controls.json b/shelf-layouts/dve controls.json deleted file mode 100755 index 6f8a2b6f..00000000 --- a/shelf-layouts/dve controls.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "_id": "Evne9f6kKGHhxTdz3", - "name": "dve controls", - "showStyleBaseId": "show0", - "filters": [ - { - "_id": "zMN9rx3kQn99uWC5P", - "type": "filter", - "name": "LAYOUT AND IP1", - "currentSegment": false, - "displayStyle": "list", - "rank": 0, - "rundownBaseline": true, - "default": false, - "includeClearInRundownBaseline": true, - "sourceLayerIds": [ - "studio0_dve_adlib", - "studio0_dve_box1" - ] - }, - { - "_id": "cfxi2T5fXiddz4s8p", - "type": "filter", - "name": "IP2", - "currentSegment": false, - "displayStyle": "list", - "rank": 0, - "rundownBaseline": "only", - "default": false, - "includeClearInRundownBaseline": true, - "sourceLayerIds": [ - "studio0_dve_box2" - ], - "y": 11 - }, - { - "_id": "KWQqhz6CeB3EBwt5g", - "type": "filter", - "name": "IP3", - "currentSegment": false, - "displayStyle": "list", - "rank": 0, - "rundownBaseline": "only", - "default": false, - "includeClearInRundownBaseline": true, - "sourceLayerIds": [ - "studio0_dve_box3" - ], - "y": 23 - } - ], - "type": "dashboard_layout", - "regionId": "shelf_layouts" -} \ No newline at end of file From 5e46cacf67dd1ca19aa5cff66c9a4de90e7ffe2d Mon Sep 17 00:00:00 2001 From: ianshade Date: Fri, 19 May 2023 10:28:12 +0200 Subject: [PATCH 09/35] chore: SOF-1213 automatically upload shelf layouts from docker --- docker-entrypoint.sh | 6 ++++- .../Mini_Shelf_Layout.json | 1 + .../Presenter_Clock_Layout.json | 0 .../Rundown_Header_Layout.json | 0 .../Rundown_View_Layout.json | 0 .../Shortcuts_and_adlib_scroll.json} | 0 .../Kommentator.json | 0 .../Q_Flow_Rundown_View_Layout.json | 0 .../Q_flow.json} | 0 .../Rundown_Header_Layout.json | 1 + shelf-layouts/upload.sh | 25 +++++++++++++++++++ 11 files changed, 32 insertions(+), 1 deletion(-) rename shelf-layouts/{ => tv2_afvd_showstyle}/Mini_Shelf_Layout.json (96%) rename shelf-layouts/{ => tv2_afvd_showstyle}/Presenter_Clock_Layout.json (100%) rename shelf-layouts/{ => tv2_afvd_showstyle}/Rundown_Header_Layout.json (100%) rename shelf-layouts/{ => tv2_afvd_showstyle}/Rundown_View_Layout.json (100%) rename shelf-layouts/{Shortcuts and adlib scroll.json => tv2_afvd_showstyle/Shortcuts_and_adlib_scroll.json} (100%) mode change 100755 => 100644 rename shelf-layouts/{ => tv2_offtube_showstyle}/Kommentator.json (100%) rename shelf-layouts/{ => tv2_offtube_showstyle}/Q_Flow_Rundown_View_Layout.json (100%) rename shelf-layouts/{Q flow.json => tv2_offtube_showstyle/Q_flow.json} (100%) create mode 120000 shelf-layouts/tv2_offtube_showstyle/Rundown_Header_Layout.json create mode 100644 shelf-layouts/upload.sh diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 80b2389e..c15707a5 100755 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -20,4 +20,8 @@ if [[ -d "/opt/blueprints/nginx" ]]; then echo "Volume mount exists copying files" cp -r shelf-layouts/ nginx/ cp -r external-frames/ nginx/ -fi \ No newline at end of file +fi + +cd shelf-layouts + +./upload.sh \ No newline at end of file diff --git a/shelf-layouts/Mini_Shelf_Layout.json b/shelf-layouts/tv2_afvd_showstyle/Mini_Shelf_Layout.json similarity index 96% rename from shelf-layouts/Mini_Shelf_Layout.json rename to shelf-layouts/tv2_afvd_showstyle/Mini_Shelf_Layout.json index a06c12f4..89c30258 100644 --- a/shelf-layouts/Mini_Shelf_Layout.json +++ b/shelf-layouts/tv2_afvd_showstyle/Mini_Shelf_Layout.json @@ -1,4 +1,5 @@ { + "_id": "fgDCkL4Y5GQrdyuRW", "name": "Flow Producer Mini Shelf", "type": "dashboard_layout", "icon": "", diff --git a/shelf-layouts/Presenter_Clock_Layout.json b/shelf-layouts/tv2_afvd_showstyle/Presenter_Clock_Layout.json similarity index 100% rename from shelf-layouts/Presenter_Clock_Layout.json rename to shelf-layouts/tv2_afvd_showstyle/Presenter_Clock_Layout.json diff --git a/shelf-layouts/Rundown_Header_Layout.json b/shelf-layouts/tv2_afvd_showstyle/Rundown_Header_Layout.json similarity index 100% rename from shelf-layouts/Rundown_Header_Layout.json rename to shelf-layouts/tv2_afvd_showstyle/Rundown_Header_Layout.json diff --git a/shelf-layouts/Rundown_View_Layout.json b/shelf-layouts/tv2_afvd_showstyle/Rundown_View_Layout.json similarity index 100% rename from shelf-layouts/Rundown_View_Layout.json rename to shelf-layouts/tv2_afvd_showstyle/Rundown_View_Layout.json diff --git a/shelf-layouts/Shortcuts and adlib scroll.json b/shelf-layouts/tv2_afvd_showstyle/Shortcuts_and_adlib_scroll.json old mode 100755 new mode 100644 similarity index 100% rename from shelf-layouts/Shortcuts and adlib scroll.json rename to shelf-layouts/tv2_afvd_showstyle/Shortcuts_and_adlib_scroll.json diff --git a/shelf-layouts/Kommentator.json b/shelf-layouts/tv2_offtube_showstyle/Kommentator.json similarity index 100% rename from shelf-layouts/Kommentator.json rename to shelf-layouts/tv2_offtube_showstyle/Kommentator.json diff --git a/shelf-layouts/Q_Flow_Rundown_View_Layout.json b/shelf-layouts/tv2_offtube_showstyle/Q_Flow_Rundown_View_Layout.json similarity index 100% rename from shelf-layouts/Q_Flow_Rundown_View_Layout.json rename to shelf-layouts/tv2_offtube_showstyle/Q_Flow_Rundown_View_Layout.json diff --git a/shelf-layouts/Q flow.json b/shelf-layouts/tv2_offtube_showstyle/Q_flow.json similarity index 100% rename from shelf-layouts/Q flow.json rename to shelf-layouts/tv2_offtube_showstyle/Q_flow.json diff --git a/shelf-layouts/tv2_offtube_showstyle/Rundown_Header_Layout.json b/shelf-layouts/tv2_offtube_showstyle/Rundown_Header_Layout.json new file mode 120000 index 00000000..8e094bb3 --- /dev/null +++ b/shelf-layouts/tv2_offtube_showstyle/Rundown_Header_Layout.json @@ -0,0 +1 @@ +../tv2_afvd_showstyle/Rundown_Header_Layout.json \ No newline at end of file diff --git a/shelf-layouts/upload.sh b/shelf-layouts/upload.sh new file mode 100644 index 00000000..0ee98143 --- /dev/null +++ b/shelf-layouts/upload.sh @@ -0,0 +1,25 @@ +#!/bin/sh + +echo "Upload Shelf Layouts" + +upload_layouts_from_directory() { + local directory="$1" + local blueprint_id=$(basename "${directory}") + local count=0 + + for file in "$directory"/*.json; do + if [[ -f "$file" ]]; then + count+=1 + url="${SERVER}/shelfLayouts/uploadByShowStyleBlueprintId/${blueprint_id}" + echo "Upload ${file} to blueprint ${blueprint_id}" + target_path="$(readlink -f "$file")" + curl -X POST --data-binary "@$target_path" --header "Content-Type:application/json" -w "\n" "$url" + fi + done +} + +for directory in *; do + if [[ -d "$directory" ]]; then + upload_layouts_from_directory "$directory" + fi +done From 2d7ccbc8a9a45589b9f1167ce1f68dcd2a053a7a Mon Sep 17 00:00:00 2001 From: ianshade Date: Fri, 19 May 2023 14:59:12 +0200 Subject: [PATCH 10/35] chore: SOF-1213 remove duplicated properties --- shelf-layouts/tv2_offtube_showstyle/Kommentator.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/shelf-layouts/tv2_offtube_showstyle/Kommentator.json b/shelf-layouts/tv2_offtube_showstyle/Kommentator.json index 23ee60f4..425bf1b4 100644 --- a/shelf-layouts/tv2_offtube_showstyle/Kommentator.json +++ b/shelf-layouts/tv2_offtube_showstyle/Kommentator.json @@ -179,8 +179,6 @@ "scale": 0, "thumbnailPriorityNextPieces": true, "hideThumbnailsForActivePieces": true, - "thumbnailPriorityNextPieces": true, - "hideThumbnailsForActivePieces": true, "thumbnailSourceLayerIds": [ "studio0_full", "studio0_jingle" From f467af727025af3cd9c65bbe07a212259a38613f Mon Sep 17 00:00:00 2001 From: ianshade Date: Fri, 19 May 2023 14:59:56 +0200 Subject: [PATCH 11/35] chore: SOF-1213 use sofieUrl in Kommentator layout --- shelf-layouts/tv2_offtube_showstyle/Kommentator.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shelf-layouts/tv2_offtube_showstyle/Kommentator.json b/shelf-layouts/tv2_offtube_showstyle/Kommentator.json index 425bf1b4..31d05ac1 100644 --- a/shelf-layouts/tv2_offtube_showstyle/Kommentator.json +++ b/shelf-layouts/tv2_offtube_showstyle/Kommentator.json @@ -11,7 +11,7 @@ "rank": 0, "rundownBaseline": false, "default": false, - "url": "http://multiview.sofqbXX-od.tv2.local/", + "url": "http://multiview.{{studio.settings.sofieUrl}}/", "width": -1, "height": -1, "x": 0, From 692bf1783b077aa6931d0b52a8c4449ba298b80d Mon Sep 17 00:00:00 2001 From: Rasmus Lindved Date: Mon, 22 May 2023 10:57:10 +0200 Subject: [PATCH 12/35] SOF-1105 Now able to create an action trigger to fade the sound bed --- src/tv2-common/actions/actionTypes.ts | 5 ++++ src/tv2-common/actions/executeAction.ts | 26 +++++++++++++++++++ src/tv2-common/cues/lyd.ts | 7 +++++ src/tv2-constants/enums.ts | 3 ++- .../GlobalAdlibActionsGenerator.ts | 21 +++++++++++++++ src/tv2_afvd_showstyle/getRundown.ts | 2 +- 6 files changed, 62 insertions(+), 2 deletions(-) diff --git a/src/tv2-common/actions/actionTypes.ts b/src/tv2-common/actions/actionTypes.ts index b69062d9..c7929b04 100644 --- a/src/tv2-common/actions/actionTypes.ts +++ b/src/tv2-common/actions/actionTypes.ts @@ -141,6 +141,10 @@ export interface ActionFadeDownPersistedAudioLevels extends ActionBase { type: AdlibActionType.FADE_DOWN_PERSISTED_AUDIO_LEVELS } +export interface ActionFadeDownSoundPlayer extends ActionBase { + type: AdlibActionType.FADE_DOWN_SOUND_PLAYER +} + export type TV2AdlibAction = | ActionSelectServerClip | ActionSelectDVE @@ -159,3 +163,4 @@ export type TV2AdlibAction = | ActionRecallLastLive | ActionRecallLastDVE | ActionFadeDownPersistedAudioLevels + | ActionFadeDownSoundPlayer diff --git a/src/tv2-common/actions/executeAction.ts b/src/tv2-common/actions/executeAction.ts index 03edfa10..469cfa4e 100644 --- a/src/tv2-common/actions/executeAction.ts +++ b/src/tv2-common/actions/executeAction.ts @@ -27,6 +27,7 @@ import { ActionSelectServerClip, calculateTime, createDipTransitionBlueprintPieceForPart, + createFadeSoundContent, createInTransitionForTransitionStyle, CreatePartServerBase, CueDefinition, @@ -249,6 +250,15 @@ export async function executeAction< await executeActionCallRobotPreset(context, preset) break } + case AdlibActionType.FADE_DOWN_SOUND_PLAYER: { + const fadeDurationFrames: number = Number(triggerMode) + if (Number.isNaN(fadeDurationFrames)) { + context.core.notifyUserWarning(`Calling Fade Sound Player ignored. '${triggerMode}' is not a number`) + break + } + await fadeDownSoundPlayer(context, fadeDurationFrames) + break + } default: assertUnreachable(actionId) break @@ -1805,6 +1815,22 @@ async function executeActionCallRobotPreset(context: ActionExecutionContext, pre await context.core.insertPiece('current', robotCameraPiece) } +async function fadeDownSoundPlayer(context: ActionExecutionContext, fadeDurationFrames: number): Promise { + const fadeSoundPlayerPiece: IBlueprintPiece = { + externalId: `fadeSoundPlayerIn${fadeDurationFrames}frames`, + name: 'Fade Sound Player', + enable: { + start: 'now', + duration: getTimeFromFrames(fadeDurationFrames) + }, + outputLayerId: SharedOutputLayer.MUSIK, + sourceLayerId: SharedSourceLayer.PgmAudioBed, + lifespan: PieceLifespan.WithinPart, + content: createFadeSoundContent(context.config, fadeDurationFrames) + } + await context.core.insertPiece('current', fadeSoundPlayerPiece) +} + async function createFadeSisyfosLevelsMetaData(context: ActionExecutionContext) { const resolvedPieceInstances = await context.core.getResolvedPieceInstances('current') const emptySisyfosMetaData: SisyfosPersistMetaData = { diff --git a/src/tv2-common/cues/lyd.ts b/src/tv2-common/cues/lyd.ts index 0cec4474..0c27f23c 100644 --- a/src/tv2-common/cues/lyd.ts +++ b/src/tv2-common/cues/lyd.ts @@ -95,6 +95,13 @@ export function EvaluateLYD( } } +export function createFadeSoundContent( + config: TV2ShowStyleConfig, + fadeDurationFrames: number +): WithTimeline { + return LydContent(config, 'empty', 'fade', fadeDurationFrames, fadeDurationFrames) +} + function LydContent( config: TV2ShowStyleConfig, file: string, diff --git a/src/tv2-constants/enums.ts b/src/tv2-constants/enums.ts index 3ef33d20..abb97a01 100644 --- a/src/tv2-constants/enums.ts +++ b/src/tv2-constants/enums.ts @@ -142,7 +142,8 @@ export enum AdlibActionType { RECALL_LAST_LIVE = 'recall_last_live', RECALL_LAST_DVE = 'recall_last_dve', FADE_DOWN_PERSISTED_AUDIO_LEVELS = 'fade_down_persisted_audio_levels', - CALL_ROBOT_PRESET = 'call_robot_preset' + CALL_ROBOT_PRESET = 'call_robot_preset', + FADE_DOWN_SOUND_PLAYER = 'fade_down_sound_player' } export enum TallyTags { diff --git a/src/tv2_afvd_showstyle/GlobalAdlibActionsGenerator.ts b/src/tv2_afvd_showstyle/GlobalAdlibActionsGenerator.ts index db72624e..bee84efc 100644 --- a/src/tv2_afvd_showstyle/GlobalAdlibActionsGenerator.ts +++ b/src/tv2_afvd_showstyle/GlobalAdlibActionsGenerator.ts @@ -6,6 +6,7 @@ import { ActionCutToCamera, ActionCutToRemote, ActionFadeDownPersistedAudioLevels, + ActionFadeDownSoundPlayer, ActionRecallLastDVE, ActionRecallLastLive, ActionSelectDVELayout, @@ -84,6 +85,7 @@ export class GlobalAdlibActionsGenerator { blueprintActions.push(this.makePersistedAudioLevelsAction()) blueprintActions.push(this.makeRobotPresetAction()) + blueprintActions.push(this.makeFadeSoundPlayerAction()) return blueprintActions } @@ -342,6 +344,25 @@ export class GlobalAdlibActionsGenerator { } } + private makeFadeSoundPlayerAction(): IBlueprintActionManifest { + const fadeDownSoundPlayer: ActionFadeDownSoundPlayer = { + type: AdlibActionType.FADE_DOWN_SOUND_PLAYER + } + return { + externalId: generateExternalId(this.context.core, fadeDownSoundPlayer), + actionId: AdlibActionType.FADE_DOWN_SOUND_PLAYER, + userData: fadeDownSoundPlayer, + userDataManifest: {}, + display: { + _rank: 400, + label: t(`Fade down sound player`), + sourceLayerId: SourceLayer.PgmAudioBed, + outputLayerId: SharedOutputLayer.SEC, + tags: [] + } + } + } + private makeDveLayoutActions(): IBlueprintActionManifest[] { const blueprintActions: IBlueprintActionManifest[] = [] _.each(this.config.showStyle.DVEStyles, (dveConfig, i) => { diff --git a/src/tv2_afvd_showstyle/getRundown.ts b/src/tv2_afvd_showstyle/getRundown.ts index 82d180b2..24cac723 100644 --- a/src/tv2_afvd_showstyle/getRundown.ts +++ b/src/tv2_afvd_showstyle/getRundown.ts @@ -475,7 +475,7 @@ class GlobalAdLibPiecesGenerator { name: 'Stop Soundplayer', _rank: 700, sourceLayerId: SourceLayer.PgmAudioBed, - outputLayerId: 'musik', + outputLayerId: SharedOutputLayer.MUSIK, expectedDuration: 1000, lifespan: PieceLifespan.WithinPart, tags: [AdlibTags.ADLIB_STOP_AUDIO_BED], From 713ffaa0f9260b2c5e9efc37d719a7ac495f1611 Mon Sep 17 00:00:00 2001 From: Rasmus Lindved Date: Tue, 23 May 2023 07:43:37 +0200 Subject: [PATCH 13/35] SOF-1105 FadeSoundPlayerAction is now on the MUSIK outputlayer --- src/tv2_afvd_showstyle/GlobalAdlibActionsGenerator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tv2_afvd_showstyle/GlobalAdlibActionsGenerator.ts b/src/tv2_afvd_showstyle/GlobalAdlibActionsGenerator.ts index 5b208b8e..a1b58391 100644 --- a/src/tv2_afvd_showstyle/GlobalAdlibActionsGenerator.ts +++ b/src/tv2_afvd_showstyle/GlobalAdlibActionsGenerator.ts @@ -366,7 +366,7 @@ export class GlobalAdlibActionsGenerator { _rank: 400, label: t(`Fade down sound player`), sourceLayerId: SourceLayer.PgmAudioBed, - outputLayerId: SharedOutputLayer.SEC, + outputLayerId: SharedOutputLayer.MUSIK, tags: [] } } From 1c28dfc666c3afb6def85a9b7313871c1c727708 Mon Sep 17 00:00:00 2001 From: Rasmus Lindved Date: Thu, 25 May 2023 08:41:55 +0200 Subject: [PATCH 14/35] SOF-1420 Schema cues will now send a list of Design GFX values to CasparCG --- src/__mocks__/context.ts | 2 +- src/tv2-common/blueprintConfig.ts | 13 +++ .../cues/evaluate-cue-graphic-schema.ts | 110 ++++++++++++++++++ .../inewsConversion/converters/ParseCue.ts | 12 +- src/tv2-constants/enums.ts | 4 +- src/tv2_afvd_showstyle/__tests__/configs.ts | 9 +- src/tv2_afvd_showstyle/config-manifests.ts | 10 ++ .../pieces/evaluate-cue-graphic-schema.ts | 60 ---------- .../helpers/pieces/evaluateCues.ts | 2 +- src/tv2_afvd_showstyle/layers.ts | 1 - src/tv2_afvd_studio/layers.ts | 3 +- src/tv2_offtube_showstyle/config-manifests.ts | 10 ++ .../helpers/EvaluateCues.ts | 4 +- .../migrations/sourcelayer-defaults.ts | 9 ++ .../migrations/mappings-defaults.ts | 9 ++ 15 files changed, 188 insertions(+), 70 deletions(-) create mode 100644 src/tv2-common/cues/evaluate-cue-graphic-schema.ts delete mode 100644 src/tv2_afvd_showstyle/helpers/pieces/evaluate-cue-graphic-schema.ts diff --git a/src/__mocks__/context.ts b/src/__mocks__/context.ts index 33b0900e..5619b36a 100644 --- a/src/__mocks__/context.ts +++ b/src/__mocks__/context.ts @@ -469,7 +469,7 @@ export class ActionExecutionContextMock extends ShowStyleUserContextMock impleme return pieces.map((pieceInstance) => { const pieceStart = pieceInstance.piece.enable.start const resolvedStart = pieceStart === 'now' ? now : pieceStart - + return { ...pieceInstance, resolvedStart diff --git a/src/tv2-common/blueprintConfig.ts b/src/tv2-common/blueprintConfig.ts index a2c6c01e..46ccabc7 100644 --- a/src/tv2-common/blueprintConfig.ts +++ b/src/tv2-common/blueprintConfig.ts @@ -57,6 +57,7 @@ export interface TableConfigGfxSchema { GfxSchemaTemplatesName: string INewsSkemaColumn: string VizTemplate: string + CasparCgDesignValues: string } export interface TableConfigGfxSetup { @@ -67,6 +68,18 @@ export interface TableConfigGfxSetup { FullShowName?: string } +export interface CasparCgGfxDesignValues { + name: string + mainColor: string + bundColor: string + trompetColor: string + mainTextColor: string + trompetTextColor: string + panelColor: string + panelTextColor: string + backgroundLoop: string +} + export interface ProcessedStudioConfig { sources: SourceMapping mediaPlayers: MediaPlayerConfig // Atem Input Ids diff --git a/src/tv2-common/cues/evaluate-cue-graphic-schema.ts b/src/tv2-common/cues/evaluate-cue-graphic-schema.ts new file mode 100644 index 00000000..29945a5f --- /dev/null +++ b/src/tv2-common/cues/evaluate-cue-graphic-schema.ts @@ -0,0 +1,110 @@ +import { GraphicsContent, IBlueprintPiece, PieceLifespan, TSR, WithTimeline } from 'blueprints-integration' +import { + calculateTime, + CueDefinitionGraphicSchema, + getHtmlTemplateName, + literal, + ShowStyleContext, + TV2ShowStyleConfig +} from 'tv2-common' +import { SharedGraphicLLayer, SharedOutputLayer, SharedSourceLayer } from 'tv2-constants' + +export function EvaluateCueGraphicSchema( + context: ShowStyleContext, + pieces: IBlueprintPiece[], + partId: string, + parsedCue: CueDefinitionGraphicSchema +) { + if (!parsedCue.schema) { + context.core.notifyUserWarning(`No valid Schema found for ${parsedCue.schema}`) + return + } + + const start = (parsedCue.start ? calculateTime(parsedCue.start) : 0) ?? 0 + pieces.push({ + externalId: partId, + name: parsedCue.schema, + enable: { + start + }, + outputLayerId: SharedOutputLayer.SEC, + sourceLayerId: SharedSourceLayer.PgmSchema, + lifespan: PieceLifespan.OutOnShowStyleEnd, + content: literal>({ + fileName: parsedCue.schema, + path: parsedCue.schema, + ignoreMediaObjectStatus: true, + timelineObjects: createTimelineObjects(context, parsedCue) + }) + }) +} + +function createTimelineObjects(context: ShowStyleContext, cue: CueDefinitionGraphicSchema): TSR.TSRTimelineObjBase[] { + switch (context.config.studio.GraphicsType) { + case 'VIZ': { + return createVizTimelineObjects(context.config, cue) + } + case 'HTML': { + return createCasparCgTimelineObjects(context, cue) + } + default: { + return [] + } + } +} + +function createVizTimelineObjects( + config: TV2ShowStyleConfig, + cue: CueDefinitionGraphicSchema +): TSR.TimelineObjVIZMSEElementInternal[] { + return [ + literal({ + id: '', + enable: { start: 0 }, + priority: 100, + layer: SharedGraphicLLayer.GraphicLLayerSchema, + content: { + deviceType: TSR.DeviceType.VIZMSE, + type: TSR.TimelineContentTypeVizMSE.ELEMENT_INTERNAL, + templateName: cue.schema, + templateData: [], + showName: config.selectedGfxSetup.OvlShowName ?? '' + } + }) + ] +} + +function createCasparCgTimelineObjects( + context: ShowStyleContext, + cue: CueDefinitionGraphicSchema +): TSR.TimelineObjCasparCGBase[] { + return [ + literal({ + id: '', + enable: { start: 0 }, + priority: 100, + layer: SharedGraphicLLayer.GraphicLLayerSchema, + content: { + deviceType: TSR.DeviceType.CASPARCG, + type: TSR.TimelineContentTypeCasparCg.TEMPLATE, + templateType: 'html', + name: getHtmlTemplateName(context.config), + data: createCasparCgHtmlSchemaData(context, cue), + useStopCommand: false + } + }) + ] +} + +function createCasparCgHtmlSchemaData(context: ShowStyleContext, cue: CueDefinitionGraphicSchema): any { + if (!cue.CasparCgDesignValues || cue.CasparCgDesignValues.length === 0) { + context.core.notifyUserError( + `Unable to find Schema Design combination for "${cue.schema}". Design values will not be sent to CasparCG!` + ) + return {} + } + return { + designs: cue.CasparCgDesignValues, + partialUpdate: true + } +} diff --git a/src/tv2-common/inewsConversion/converters/ParseCue.ts b/src/tv2-common/inewsConversion/converters/ParseCue.ts index 06751d09..e78a042e 100644 --- a/src/tv2-common/inewsConversion/converters/ParseCue.ts +++ b/src/tv2-common/inewsConversion/converters/ParseCue.ts @@ -1,4 +1,5 @@ import { + CasparCgGfxDesignValues, literal, TableConfigGfxSchema, TableConfigItemGfxDesignTemplate, @@ -133,6 +134,7 @@ export interface CueDefinitionFromField { export interface CueDefinitionGraphicSchema extends CueDefinitionBase, CueDefinitionFromField { type: CueType.GraphicSchema schema: string + CasparCgDesignValues?: CasparCgGfxDesignValues[] } export interface GraphicInternal { @@ -377,7 +379,10 @@ function parsekg( iNewsCommand: kgCue.iNewsCommand, start: kgCue.start, end: kgCue.end, - adlib: kgCue.adlib + adlib: kgCue.adlib, + CasparCgDesignValues: graphicsSchemaConfig.CasparCgDesignValues + ? JSON.parse(graphicsSchemaConfig.CasparCgDesignValues) + : [] }) } @@ -967,7 +972,10 @@ export function createCueDefinitionGraphicSchema( type: CueType.GraphicSchema, schema: schemaConfiguration.VizTemplate, iNewsCommand: '', - isFromField: true + isFromField: true, + CasparCgDesignValues: schemaConfiguration.CasparCgDesignValues + ? JSON.parse(schemaConfiguration.CasparCgDesignValues) + : [] }) } diff --git a/src/tv2-constants/enums.ts b/src/tv2-constants/enums.ts index 5b6c089b..f104eca7 100644 --- a/src/tv2-constants/enums.ts +++ b/src/tv2-constants/enums.ts @@ -180,7 +180,8 @@ export enum SharedGraphicLLayer { GraphicLLayerAdLibs = 'graphic_adlibs', // <= viz_layer_adlibs GraphicLLayerWall = 'graphic_wall', // <= viz_layer_wall GraphicLLayerLocators = 'graphic_locators', - GraphicLLayerConcept = 'graphic_concept' + GraphicLLayerConcept = 'graphic_concept', + GraphicLLayerSchema = 'graphic_schema' } export enum AbstractLLayer { @@ -276,6 +277,7 @@ export enum SharedSourceLayer { PgmPilotOverlay = 'studio0_pilotOverlay', // "Design" templates PgmDesign = 'studio0_design', + PgmSchema = 'studio0_schema', /** General, 'fallback', overlay layer */ PgmGraphicsOverlay = 'studio0_overlay', diff --git a/src/tv2_afvd_showstyle/__tests__/configs.ts b/src/tv2_afvd_showstyle/__tests__/configs.ts index 608b8fde..bed0a9b0 100644 --- a/src/tv2_afvd_showstyle/__tests__/configs.ts +++ b/src/tv2_afvd_showstyle/__tests__/configs.ts @@ -273,7 +273,14 @@ export const defaultShowStyleConfig: GalleryShowStyleConfig = { GfxSetups: [DEFAULT_GFX_SETUP], Transitions: [{ Transition: '1' }, { Transition: '2' }], ShowstyleTransition: 'CUT', - GfxSchemaTemplates: [{ GfxSchemaTemplatesName: 'SKEMA_NEWS', VizTemplate: 'NE', INewsSkemaColumn: 'SKEMA_NEWS' }], + GfxSchemaTemplates: [ + { + GfxSchemaTemplatesName: 'SKEMA_NEWS', + VizTemplate: 'NE', + INewsSkemaColumn: 'SKEMA_NEWS', + CasparCgDesignValues: '' + } + ], GfxShowMapping: [], GfxDefaults: [ { diff --git a/src/tv2_afvd_showstyle/config-manifests.ts b/src/tv2_afvd_showstyle/config-manifests.ts index 0b071afa..da97ad01 100644 --- a/src/tv2_afvd_showstyle/config-manifests.ts +++ b/src/tv2_afvd_showstyle/config-manifests.ts @@ -258,6 +258,16 @@ export const gfxSchemaTemplates: ConfigManifestEntry[] = [ required: true, defaultVal: '', rank: 2 + }, + { + id: 'CasparCgDesignValues', + name: 'CasparCG Design Values', + description: + 'A JSON array containing color values and background loops for the different Designs of the Schema used by CasparCG', + type: ConfigManifestEntryType.JSON, + required: false, + defaultVal: '', + rank: 3 } ] } diff --git a/src/tv2_afvd_showstyle/helpers/pieces/evaluate-cue-graphic-schema.ts b/src/tv2_afvd_showstyle/helpers/pieces/evaluate-cue-graphic-schema.ts deleted file mode 100644 index 942007ac..00000000 --- a/src/tv2_afvd_showstyle/helpers/pieces/evaluate-cue-graphic-schema.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { GraphicsContent, PieceLifespan, WithTimeline } from '@sofie-automation/blueprints-integration' -import { IBlueprintPiece, TSR } from 'blueprints-integration' -import { calculateTime, CueDefinitionGraphicSchema, literal, ShowStyleContext, TV2ShowStyleConfig } from 'tv2-common' -import { SharedOutputLayer } from 'tv2-constants' -import { GraphicLLayer } from '../../../tv2_afvd_studio/layers' -import { SourceLayer } from '../../layers' - -export function EvaluateCueGraphicSchema( - context: ShowStyleContext, - pieces: IBlueprintPiece[], - partId: string, - parsedCue: CueDefinitionGraphicSchema -) { - if (!parsedCue.schema) { - context.core.notifyUserWarning(`No valid Schema found for ${parsedCue.schema}`) - return - } - - const start = (parsedCue.start ? calculateTime(parsedCue.start) : 0) ?? 0 - pieces.push({ - externalId: partId, - name: parsedCue.schema, - enable: { - start - }, - outputLayerId: SharedOutputLayer.SEC, - sourceLayerId: SourceLayer.PgmSchema, - lifespan: PieceLifespan.OutOnShowStyleEnd, - content: literal>({ - fileName: parsedCue.schema, - path: parsedCue.schema, - ignoreMediaObjectStatus: true, - timelineObjects: createTimeline(context.config, parsedCue) - }) - }) -} - -function createTimeline( - config: TV2ShowStyleConfig, - cue: CueDefinitionGraphicSchema -): TSR.TimelineObjVIZMSEElementInternal[] { - if (config.studio.GraphicsType !== 'VIZ') { - return [] - } - return [ - literal({ - id: '', - enable: { start: 0 }, - priority: 100, - layer: GraphicLLayer.GraphicLLayerSchema, - content: { - deviceType: TSR.DeviceType.VIZMSE, - type: TSR.TimelineContentTypeVizMSE.ELEMENT_INTERNAL, - templateName: cue.schema, - templateData: [], - showName: config.selectedGfxSetup.OvlShowName ?? '' - } - }) - ] -} diff --git a/src/tv2_afvd_showstyle/helpers/pieces/evaluateCues.ts b/src/tv2_afvd_showstyle/helpers/pieces/evaluateCues.ts index 220e2160..527fa793 100644 --- a/src/tv2_afvd_showstyle/helpers/pieces/evaluateCues.ts +++ b/src/tv2_afvd_showstyle/helpers/pieces/evaluateCues.ts @@ -15,13 +15,13 @@ import { PartDefinition, ShowStyleContext } from 'tv2-common' +import { EvaluateCueGraphicSchema } from '../../../tv2-common/cues/evaluate-cue-graphic-schema' import { GalleryBlueprintConfig } from '../../../tv2_afvd_showstyle/helpers/config' import { EvaluateAdLib } from './adlib' import { EvaluateClearGrafiks } from './clearGrafiks' import { EvaluateCueDesign } from './design' import { EvaluateDVE } from './dve' import { EvaluateEkstern } from './ekstern' -import { EvaluateCueGraphicSchema } from './evaluate-cue-graphic-schema' import { EvaluateCueGraphic } from './graphic' import { EvaluateCueBackgroundLoop } from './graphicBackgroundLoop' import { EvaluateJingle } from './jingle' diff --git a/src/tv2_afvd_showstyle/layers.ts b/src/tv2_afvd_showstyle/layers.ts index 8b8a4e53..91d8599a 100644 --- a/src/tv2_afvd_showstyle/layers.ts +++ b/src/tv2_afvd_showstyle/layers.ts @@ -3,7 +3,6 @@ import { SharedSourceLayer } from 'tv2-constants' export enum AFVDSourceLayer { // Pgm PgmLocal = 'studio0_local', - PgmSchema = 'studio0_schema', VizFullIn1 = 'studio0_aux_viz_full1', AuxStudioScreen = 'studio0_aux_studio_screen', diff --git a/src/tv2_afvd_studio/layers.ts b/src/tv2_afvd_studio/layers.ts index d06bf58f..a3d6b2c5 100644 --- a/src/tv2_afvd_studio/layers.ts +++ b/src/tv2_afvd_studio/layers.ts @@ -22,8 +22,7 @@ export type CasparLLayer = AFVDCasparLLayer | SharedCasparLLayer enum AFVDGraphicLLayer { GraphicLLayerInitialize = 'graphic_initialize', - GraphicLLayerCleanup = 'graphic_cleanup', - GraphicLLayerSchema = 'graphic_schema' + GraphicLLayerCleanup = 'graphic_cleanup' } // tslint:disable-next-line: variable-name diff --git a/src/tv2_offtube_showstyle/config-manifests.ts b/src/tv2_offtube_showstyle/config-manifests.ts index b771d7ff..58b7d8e8 100644 --- a/src/tv2_offtube_showstyle/config-manifests.ts +++ b/src/tv2_offtube_showstyle/config-manifests.ts @@ -223,6 +223,16 @@ export const gfxSchemaTemplates: ConfigManifestEntry[] = [ required: true, defaultVal: '', rank: 2 + }, + { + id: 'CasparCgDesignValues', + name: 'CasparCG Design Values', + description: + 'A JSON array containing color values and background loops for the different Designs of the Schema used by CasparCG', + type: ConfigManifestEntryType.JSON, + required: false, + defaultVal: '', + rank: 3 } ] } diff --git a/src/tv2_offtube_showstyle/helpers/EvaluateCues.ts b/src/tv2_offtube_showstyle/helpers/EvaluateCues.ts index afa2a007..a0506843 100644 --- a/src/tv2_offtube_showstyle/helpers/EvaluateCues.ts +++ b/src/tv2_offtube_showstyle/helpers/EvaluateCues.ts @@ -13,6 +13,7 @@ import { PartDefinition, SegmentContext } from 'tv2-common' +import { EvaluateCueGraphicSchema } from '../../tv2-common/cues/evaluate-cue-graphic-schema' import { OfftubeEvaluateAdLib } from '../cues/OfftubeAdlib' import { OfftubeEvaluateDVE } from '../cues/OfftubeDVE' import { OfftubeEvaluateEkstern } from '../cues/OfftubeEkstern' @@ -44,7 +45,8 @@ export async function OfftubeEvaluateCues( EvaluateCueBackgroundLoop: OfftubeEvaluateCueBackgroundLoop, EvaluateCueGraphicDesign: OfftubeEvaluateGraphicDesign, EvaluateCuePgmClean: OfftubeEvaluatePgmClean, - EvaluateCueLYD: EvaluateLYD + EvaluateCueLYD: EvaluateLYD, + EvaluateCueGraphicSchema }, context, part, diff --git a/src/tv2_offtube_showstyle/migrations/sourcelayer-defaults.ts b/src/tv2_offtube_showstyle/migrations/sourcelayer-defaults.ts index 0de7574b..49de9c03 100644 --- a/src/tv2_offtube_showstyle/migrations/sourcelayer-defaults.ts +++ b/src/tv2_offtube_showstyle/migrations/sourcelayer-defaults.ts @@ -1,6 +1,7 @@ import { ISourceLayer, SourceLayerType } from 'blueprints-integration' import { GetDSKSourceLayerDefaults, literal } from 'tv2-common' import { SharedSourceLayer } from 'tv2-constants' +import { SourceLayer } from '../../tv2_afvd_showstyle/layers' import { ATEMModel } from '../../types/atem' import { OfftubeSourceLayer } from '../layers' @@ -378,6 +379,14 @@ const SEC: ISourceLayer[] = [ allowDisable: false, onPresenterScreen: false }, + { + _id: SourceLayer.PgmSchema, + _rank: 30, + name: 'Caspar Schema', + abbreviation: '', + type: SourceLayerType.UNKNOWN, + isHidden: true + }, { _id: OfftubeSourceLayer.PgmSisyfosAdlibs, _rank: 50, diff --git a/src/tv2_offtube_studio/migrations/mappings-defaults.ts b/src/tv2_offtube_studio/migrations/mappings-defaults.ts index 87108ab3..1c605eb8 100644 --- a/src/tv2_offtube_studio/migrations/mappings-defaults.ts +++ b/src/tv2_offtube_studio/migrations/mappings-defaults.ts @@ -9,6 +9,7 @@ import { prefixLayers } from 'tv2-common' import { AbstractLLayer, SwitcherAuxLLayer, SwitcherDveLLayer, SwitcherMixEffectLLayer } from 'tv2-constants' +import { GraphicLLayer } from '../../tv2_afvd_studio/layers' import { ATEMModel } from '../../types/atem' import { OfftubeCasparLLayer, OfftubeGraphicLLayer, OfftubeSisyfosLLayer } from '../layers' @@ -355,6 +356,14 @@ const MAPPINGS_GRAPHICS: BlueprintMappings = { channel: 3, layer: 111 }), + [GraphicLLayer.GraphicLLayerSchema]: literal({ + device: TSR.DeviceType.CASPARCG, + deviceId: 'caspar01', + layerName: 'GFX Skema', + lookahead: LookaheadMode.NONE, + channel: 3, + layer: 111 + }), [OfftubeGraphicLLayer.GraphicLLayerOverlay]: literal({ device: TSR.DeviceType.CASPARCG, deviceId: 'caspar01', From c5fac74a31cd55d210e5da3468c35cfdf9f8db8d Mon Sep 17 00:00:00 2001 From: ianshade Date: Fri, 26 May 2023 07:32:13 +0200 Subject: [PATCH 15/35] chore: SOF-1213 improve directory structure rename shelf-layouts to layouts move layouts upload script --- docker-entrypoint.sh | 5 +---- external-frames/multiview/off-tubes-shelf.json | 2 +- .../AFVD_Default_showstyle_hotkeys.json | 0 {shelf-layouts => layouts}/inspect all adlibs.json | 0 .../tv2_afvd_showstyle/Mini_Shelf_Layout.json | 0 .../tv2_afvd_showstyle/Presenter_Clock_Layout.json | 0 .../tv2_afvd_showstyle/Rundown_Header_Layout.json | 0 .../tv2_afvd_showstyle/Rundown_View_Layout.json | 0 .../tv2_afvd_showstyle/Shortcuts_and_adlib_scroll.json | 0 .../tv2_offtube_showstyle/Kommentator.json | 0 .../tv2_offtube_showstyle/Q_Flow_Rundown_View_Layout.json | 0 .../tv2_offtube_showstyle/Q_flow.json | 0 .../tv2_offtube_showstyle/Rundown_Header_Layout.json | 0 shelf-layouts/upload.sh => scripts/upload-layouts.sh | 8 +++++--- 14 files changed, 7 insertions(+), 8 deletions(-) rename {shelf-layouts => layouts}/AFVD_Default_showstyle_hotkeys.json (100%) rename {shelf-layouts => layouts}/inspect all adlibs.json (100%) mode change 100755 => 100644 rename {shelf-layouts => layouts}/tv2_afvd_showstyle/Mini_Shelf_Layout.json (100%) rename {shelf-layouts => layouts}/tv2_afvd_showstyle/Presenter_Clock_Layout.json (100%) rename {shelf-layouts => layouts}/tv2_afvd_showstyle/Rundown_Header_Layout.json (100%) rename {shelf-layouts => layouts}/tv2_afvd_showstyle/Rundown_View_Layout.json (100%) rename {shelf-layouts => layouts}/tv2_afvd_showstyle/Shortcuts_and_adlib_scroll.json (100%) rename {shelf-layouts => layouts}/tv2_offtube_showstyle/Kommentator.json (100%) rename {shelf-layouts => layouts}/tv2_offtube_showstyle/Q_Flow_Rundown_View_Layout.json (100%) rename {shelf-layouts => layouts}/tv2_offtube_showstyle/Q_flow.json (100%) rename {shelf-layouts => layouts}/tv2_offtube_showstyle/Rundown_Header_Layout.json (100%) rename shelf-layouts/upload.sh => scripts/upload-layouts.sh (80%) diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index c15707a5..b31e8c37 100755 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -18,10 +18,7 @@ yarn build-now --env.server=$SERVER if [[ -d "/opt/blueprints/nginx" ]]; then echo "Volume mount exists copying files" - cp -r shelf-layouts/ nginx/ cp -r external-frames/ nginx/ fi -cd shelf-layouts - -./upload.sh \ No newline at end of file +./scripts/upload-layouts.sh layouts/ diff --git a/external-frames/multiview/off-tubes-shelf.json b/external-frames/multiview/off-tubes-shelf.json index ce90d267..1359332a 120000 --- a/external-frames/multiview/off-tubes-shelf.json +++ b/external-frames/multiview/off-tubes-shelf.json @@ -1 +1 @@ -../../shelf-layouts/Kommentator.json \ No newline at end of file +../../layouts/tv2_offtube_showstyle/Kommentator.json \ No newline at end of file diff --git a/shelf-layouts/AFVD_Default_showstyle_hotkeys.json b/layouts/AFVD_Default_showstyle_hotkeys.json similarity index 100% rename from shelf-layouts/AFVD_Default_showstyle_hotkeys.json rename to layouts/AFVD_Default_showstyle_hotkeys.json diff --git a/shelf-layouts/inspect all adlibs.json b/layouts/inspect all adlibs.json old mode 100755 new mode 100644 similarity index 100% rename from shelf-layouts/inspect all adlibs.json rename to layouts/inspect all adlibs.json diff --git a/shelf-layouts/tv2_afvd_showstyle/Mini_Shelf_Layout.json b/layouts/tv2_afvd_showstyle/Mini_Shelf_Layout.json similarity index 100% rename from shelf-layouts/tv2_afvd_showstyle/Mini_Shelf_Layout.json rename to layouts/tv2_afvd_showstyle/Mini_Shelf_Layout.json diff --git a/shelf-layouts/tv2_afvd_showstyle/Presenter_Clock_Layout.json b/layouts/tv2_afvd_showstyle/Presenter_Clock_Layout.json similarity index 100% rename from shelf-layouts/tv2_afvd_showstyle/Presenter_Clock_Layout.json rename to layouts/tv2_afvd_showstyle/Presenter_Clock_Layout.json diff --git a/shelf-layouts/tv2_afvd_showstyle/Rundown_Header_Layout.json b/layouts/tv2_afvd_showstyle/Rundown_Header_Layout.json similarity index 100% rename from shelf-layouts/tv2_afvd_showstyle/Rundown_Header_Layout.json rename to layouts/tv2_afvd_showstyle/Rundown_Header_Layout.json diff --git a/shelf-layouts/tv2_afvd_showstyle/Rundown_View_Layout.json b/layouts/tv2_afvd_showstyle/Rundown_View_Layout.json similarity index 100% rename from shelf-layouts/tv2_afvd_showstyle/Rundown_View_Layout.json rename to layouts/tv2_afvd_showstyle/Rundown_View_Layout.json diff --git a/shelf-layouts/tv2_afvd_showstyle/Shortcuts_and_adlib_scroll.json b/layouts/tv2_afvd_showstyle/Shortcuts_and_adlib_scroll.json similarity index 100% rename from shelf-layouts/tv2_afvd_showstyle/Shortcuts_and_adlib_scroll.json rename to layouts/tv2_afvd_showstyle/Shortcuts_and_adlib_scroll.json diff --git a/shelf-layouts/tv2_offtube_showstyle/Kommentator.json b/layouts/tv2_offtube_showstyle/Kommentator.json similarity index 100% rename from shelf-layouts/tv2_offtube_showstyle/Kommentator.json rename to layouts/tv2_offtube_showstyle/Kommentator.json diff --git a/shelf-layouts/tv2_offtube_showstyle/Q_Flow_Rundown_View_Layout.json b/layouts/tv2_offtube_showstyle/Q_Flow_Rundown_View_Layout.json similarity index 100% rename from shelf-layouts/tv2_offtube_showstyle/Q_Flow_Rundown_View_Layout.json rename to layouts/tv2_offtube_showstyle/Q_Flow_Rundown_View_Layout.json diff --git a/shelf-layouts/tv2_offtube_showstyle/Q_flow.json b/layouts/tv2_offtube_showstyle/Q_flow.json similarity index 100% rename from shelf-layouts/tv2_offtube_showstyle/Q_flow.json rename to layouts/tv2_offtube_showstyle/Q_flow.json diff --git a/shelf-layouts/tv2_offtube_showstyle/Rundown_Header_Layout.json b/layouts/tv2_offtube_showstyle/Rundown_Header_Layout.json similarity index 100% rename from shelf-layouts/tv2_offtube_showstyle/Rundown_Header_Layout.json rename to layouts/tv2_offtube_showstyle/Rundown_Header_Layout.json diff --git a/shelf-layouts/upload.sh b/scripts/upload-layouts.sh similarity index 80% rename from shelf-layouts/upload.sh rename to scripts/upload-layouts.sh index 0ee98143..872618a2 100644 --- a/shelf-layouts/upload.sh +++ b/scripts/upload-layouts.sh @@ -2,6 +2,8 @@ echo "Upload Shelf Layouts" +directory=$1 + upload_layouts_from_directory() { local directory="$1" local blueprint_id=$(basename "${directory}") @@ -18,8 +20,8 @@ upload_layouts_from_directory() { done } -for directory in *; do - if [[ -d "$directory" ]]; then - upload_layouts_from_directory "$directory" +for subdirectory in "$directory"/*; do + if [[ -d "$subdirectory" ]]; then + upload_layouts_from_directory "$subdirectory" fi done From 990933c2a136b71c22e70f9344063eac84c50f28 Mon Sep 17 00:00:00 2001 From: ianshade Date: Fri, 26 May 2023 09:10:13 +0200 Subject: [PATCH 16/35] chore: SOF-1181 update positioning in layouts --- .../Rundown_Header_Layout.json | 124 +++++++++++------ .../Shortcuts_and_adlib_scroll.json | 76 ++++++----- .../tv2_offtube_showstyle/Kommentator.json | 128 ++++++++++++++---- 3 files changed, 228 insertions(+), 100 deletions(-) diff --git a/layouts/tv2_afvd_showstyle/Rundown_Header_Layout.json b/layouts/tv2_afvd_showstyle/Rundown_Header_Layout.json index 3e40d02f..1d0f91d7 100644 --- a/layouts/tv2_afvd_showstyle/Rundown_Header_Layout.json +++ b/layouts/tv2_afvd_showstyle/Rundown_Header_Layout.json @@ -25,11 +25,15 @@ "y": 0.3, "width": 1, "height": 1, - "scale": 0.7, + "scale": 1, "customClasses": [ "" ], - "hideDiff": true + "hideDiff": true, + "xUnit": "rem", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "4QhdsoZfbDXeNXZLY", @@ -45,14 +49,17 @@ "nextInCurrentPart": false, "oneNextPerSourceLayer": false, "plannedEndText": "", - "scale": 0.7, + "scale": 1, "width": 4, "height": 1, "x": 8.8, "y": 0.3, "hidePlannedEnd": true, "hideCountdown": true, - "xUnit": "%" + "xUnit": "%", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "NAT56wYzfbKRX9Dw2", @@ -70,12 +77,15 @@ "plannedEndText": "Planned end", "hideCountdown": true, "hideDiff": true, - "scale": 0.7, + "scale": 1, "y": 0.3, "x": 18.2, "width": 1, "height": 1, - "xUnit": "%" + "xUnit": "%", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "qYm6yy529p69wkxC4", @@ -90,12 +100,15 @@ "default": false, "nextInCurrentPart": false, "oneNextPerSourceLayer": false, - "scale": 0.7, + "scale": 1, "y": 0.9, "x": 22, "width": 10, "height": 1, - "xUnit": "%" + "xUnit": "%", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "tsCeq5TJR6PNk8mbx", @@ -110,13 +123,16 @@ "default": false, "nextInCurrentPart": false, "oneNextPerSourceLayer": false, - "scale": 0.7, + "scale": 1, "width": 10, "height": 1, "text": "INEWS RUNDOWN", "x": 22, "y": 0.3, - "xUnit": "%" + "xUnit": "%", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "uWHhxH2Mchdeds4q6", @@ -131,11 +147,15 @@ "default": false, "nextInCurrentPart": false, "oneNextPerSourceLayer": false, - "scale": 0.7, + "scale": 1, "y": 0.3, "x": -4.1, "width": 2.6, - "height": 1 + "height": 1, + "xUnit": "rem", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "awa4skeRvYqC2ZFhd", @@ -150,12 +170,15 @@ "default": false, "nextInCurrentPart": false, "oneNextPerSourceLayer": false, - "scale": 0.7, + "scale": 1, "y": 0.3, "x": 74.4, "width": 15, "height": 1, - "xUnit": "%" + "xUnit": "%", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "NaaYLEHtqmtWkAD48", @@ -170,12 +193,15 @@ "default": false, "nextInCurrentPart": false, "oneNextPerSourceLayer": false, - "scale": 0.7, + "scale": 1, "x": 67.3, "y": 0.3, "width": 5, "height": 1, - "xUnit": "%" + "xUnit": "%", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "NFhMKGcs94BGTAvs2", @@ -193,10 +219,12 @@ "iconColor": "#000000", "x": 37, "width": 27, - "height": 2, + "height": 1.4, "y": 0, "xUnit": "%", - "widthUnit": "%" + "widthUnit": "%", + "yUnit": "rem", + "heightUnit": "rem" }, { "_id": "ckTBdCMfJp7mJhBTp", @@ -211,20 +239,24 @@ "default": false, "nextInCurrentPart": false, "oneNextPerSourceLayer": false, - "scale": 1.3, + "scale": 1.6, "customClasses": [ "" ], "timingType": "count_down", - "y": -1.35, - "x": 37.5, + "y": -1.6, + "x": 38, "hideLabel": true, "requiredLayerIds": [ "studio0_clip", "studio0_voiceover" ], "xUnit": "%", - "width": 5 + "width": 5, + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem", + "height": -0.8 }, { "_id": "uW28ZfmJ6b2hMZo5Y", @@ -240,11 +272,14 @@ "nextInCurrentPart": false, "oneNextPerSourceLayer": false, "text": "PART COUNTDOWN", - "scale": 0.7, - "y": 0.05, + "scale": 0.9, + "y": 0.1, "x": 38, "xUnit": "%", - "width": 6 + "width": 6, + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "zvkPAEWTZWjLbyHPg", @@ -260,12 +295,15 @@ "nextInCurrentPart": false, "oneNextPerSourceLayer": false, "timingType": "count_up", - "scale": 0.6, + "scale": 0.9, "x": -36.7, - "y": 0.3, + "y": 0.1, "xUnit": "%", - "widthUnit": "em", - "width": 3.8 + "widthUnit": "rem", + "width": 3.8, + "hideLabel": false, + "yUnit": "rem", + "heightUnit": "rem" }, { "_id": "ou4qa2eEmDihBykfQ", @@ -281,11 +319,14 @@ "nextInCurrentPart": false, "oneNextPerSourceLayer": false, "timingType": "count_down", - "scale": 0.6, - "y": 0.3, + "scale": 0.9, + "y": 0.1, "x": 47, "xUnit": "%", - "width": 6 + "width": 6, + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "5dPjQgaZEd3cgjoCo", @@ -300,9 +341,9 @@ "default": false, "nextInCurrentPart": false, "oneNextPerSourceLayer": false, - "scale": 0.6, - "y": 1.4, - "x": 50.6, + "scale": 0.8, + "y": 1.2, + "x": 50.7, "requiredLayerIds": [ "studio0_script" ], @@ -315,7 +356,9 @@ ], "requireAllAdditionalSourcelayers": false, "xUnit": "%", - "widthUnit": "%" + "widthUnit": "%", + "yUnit": "rem", + "heightUnit": "rem" }, { "_id": "mK85XTh7ERR6Ddhm6", @@ -331,10 +374,13 @@ "nextInCurrentPart": false, "oneNextPerSourceLayer": false, "text": "END WORDS", - "scale": 0.7, + "scale": 1, "x": 47, - "y": 1.7, - "xUnit": "%" + "y": 1.8, + "xUnit": "%", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" } ], "showStyleBaseId": "show0" diff --git a/layouts/tv2_afvd_showstyle/Shortcuts_and_adlib_scroll.json b/layouts/tv2_afvd_showstyle/Shortcuts_and_adlib_scroll.json index e77fea9c..35d3ffa4 100644 --- a/layouts/tv2_afvd_showstyle/Shortcuts_and_adlib_scroll.json +++ b/layouts/tv2_afvd_showstyle/Shortcuts_and_adlib_scroll.json @@ -13,8 +13,8 @@ "rundownBaseline": false, "default": false, "x": 1, - "y": 7, - "width": -15.8, + "y": 6.9, + "width": -15.3, "height": 5, "overflowHorizontally": true, "showAsTimeline": true, @@ -42,7 +42,11 @@ "flow_producer" ], "toggleOnSingleClick": true, - "scale": 0 + "scale": 1.05, + "xUnit": "rem", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "s4KkTnsERPNcrkqeD", @@ -53,12 +57,14 @@ "rank": 0, "rundownBaseline": false, "default": false, - "url": "http://10.201.76.12:8005/keyboardmap", - "x": 1.2, - "y": 18, - "width": 54.8, - "height": 18, - "scale": 0.79 + "x": 1, + "y": 21.3, + "width": 59.2, + "scale": 0.79, + "xUnit": "rem", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "pyRvtqdtAbFE2gekk", @@ -71,8 +77,8 @@ "default": false, "x": -1, "y": 0, - "width": 15.7, - "height": 15, + "width": 13.5, + "height": 18.9, "includeClearInRundownBaseline": false, "assignHotKeys": false, "hide": false, @@ -85,25 +91,11 @@ "buttonWidthScale": 1.7, "buttonHeightScale": 1.7, "toggleOnSingleClick": true, - "scale": 0.6 - }, - { - "_id": "QZ6xdWdFmm5Mu7ZKJ", - "type": "filter", - "name": "", - "currentSegment": false, - "displayStyle": "list", - "rank": 0, - "rundownBaseline": "only", - "default": false, - "width": 0, - "height": 0, - "y": -1, - "x": -1, - "includeClearInRundownBaseline": true, - "assignHotKeys": true, - "hide": true, - "toggleOnSingleClick": true + "scale": 0.9, + "xUnit": "rem", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "j54rMBuuTvufKJ8yp", @@ -117,17 +109,22 @@ "hideDuplicates": true, "default": false, "x": 1, - "y": 14, + "y": 13.9, "sourceLayerIds": [ "studio0_dve" ], - "width": -15.8, + "width": -15.3, "height": 5, "buttonWidthScale": 1.5, "buttonHeightScale": 1.5, "showAsTimeline": true, "includeClearInRundownBaseline": true, - "toggleOnSingleClick": true + "toggleOnSingleClick": true, + "scale": 1.05, + "xUnit": "rem", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "nmNfZ8azaAAHD7jjT", @@ -135,7 +132,7 @@ "name": "ADLIB PANEL: server", "currentSegment": false, "displayStyle": "buttons", - "rank": 0, + "rank": 0.3, "rundownBaseline": false, "showThumbnailsInList": false, "hideDuplicates": true, @@ -147,11 +144,16 @@ "buttonWidthScale": 1.5, "buttonHeightScale": 1.5, "y": 0, - "width": -15.8, - "height": 5, + "width": -15.3, + "height": 4.9, "showAsTimeline": true, "includeClearInRundownBaseline": true, - "toggleOnSingleClick": true + "toggleOnSingleClick": true, + "scale": 1.05, + "xUnit": "rem", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" } ], "type": "dashboard_layout", diff --git a/layouts/tv2_offtube_showstyle/Kommentator.json b/layouts/tv2_offtube_showstyle/Kommentator.json index 31d05ac1..51b6fa75 100644 --- a/layouts/tv2_offtube_showstyle/Kommentator.json +++ b/layouts/tv2_offtube_showstyle/Kommentator.json @@ -16,7 +16,11 @@ "height": -1, "x": 0, "y": 0, - "scale": 0 + "scale": 1, + "xUnit": "rem", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "pyRvtqdtAbFE2gekk", @@ -45,7 +49,11 @@ ], "showThumbnailsInList": true, "hideDuplicates": false, - "toggleOnSingleClick": true + "toggleOnSingleClick": true, + "xUnit": "rem", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "QZ6xdWdFmm5Mu7ZKJ", @@ -79,7 +87,11 @@ "kommentator" ], "showThumbnailsInList": true, - "toggleOnSingleClick": true + "toggleOnSingleClick": true, + "xUnit": "rem", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "zHX89zpASjzyn7GTz", @@ -107,7 +119,11 @@ "studio0_voiceover" ], "showThumbnailsInList": true, - "toggleOnSingleClick": true + "toggleOnSingleClick": true, + "xUnit": "rem", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "YC7RNPTbkXd5wnagp", @@ -125,7 +141,6 @@ "displayTakeButtons": true, "sourceLayerIds": [ "studio0_graphicsIdent", - "studio0_graphicsIdent_persistent", "studio0_graphicsTop", "studio0_graphicsLower", "studio0_pilotOverlay" @@ -135,7 +150,11 @@ ], "hideDuplicates": true, "lineBreak": "\n - ", - "toggleOnSingleClick": true + "toggleOnSingleClick": true, + "xUnit": "rem", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "icuekg6wiJxejsYsZ", @@ -159,7 +178,11 @@ "kommentator" ], "showThumbnailsInList": true, - "toggleOnSingleClick": true + "toggleOnSingleClick": true, + "xUnit": "rem", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "6GDpdq547HjjZL9YK", @@ -182,7 +205,11 @@ "thumbnailSourceLayerIds": [ "studio0_full", "studio0_jingle" - ] + ], + "xUnit": "rem", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "gho9Kigti38sQYqq3", @@ -203,7 +230,11 @@ "studio0_offtube_jingle" ], "showBlackIfNoThumbnailPiece": false, - "hideThumbnailsForActivePieces": true + "hideThumbnailsForActivePieces": true, + "xUnit": "rem", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "ooCyWRXeDE6ufYWWQ", @@ -221,7 +252,11 @@ "role": "queue", "tags": [ "offtube_set_cam_next" - ] + ], + "xUnit": "rem", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "PDKiKYeYB7zcAq6Lp", @@ -240,7 +275,11 @@ "tags": [ "offtube_set_remote_next" ], - "adlibRank": 0 + "adlibRank": 0, + "xUnit": "rem", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "AMrCRTvefKDS6qogp", @@ -259,7 +298,11 @@ "offtube_set_remote_next" ], "role": "queue", - "adlibRank": 1 + "adlibRank": 1, + "xUnit": "rem", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "HLzpg7NdLk6tdYZfT", @@ -278,7 +321,11 @@ "offtube_set_remote_next" ], "role": "queue", - "adlibRank": 2 + "adlibRank": 2, + "xUnit": "rem", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "mRPYndcCsfZJL9zYH", @@ -298,7 +345,11 @@ ], "role": "queue", "thumbnailPriorityNextPieces": false, - "showBlackIfNoThumbnailPiece": false + "showBlackIfNoThumbnailPiece": false, + "xUnit": "rem", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "PDKiKYeYB7zcAq6La", @@ -321,7 +372,11 @@ "studio0_offtube_jingle", "studio0_jingle" ], - "showBlackIfNoThumbnailPiece": true + "showBlackIfNoThumbnailPiece": true, + "xUnit": "rem", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "AMrCRTvefKDS6qoga", @@ -340,7 +395,11 @@ "tags": [ "offtube_set_server_next" ], - "assignHotKeys": false + "assignHotKeys": false, + "xUnit": "rem", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "HLzpg7NdLk6tdYZfa", @@ -359,7 +418,11 @@ "tags": [ "offtube_set_dve_next" ], - "role": "queue" + "role": "queue", + "xUnit": "rem", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "zavcByHmMndMvZjCA", @@ -379,7 +442,12 @@ "studio0_clip", "studio0_voiceover", "studio0_jingle" - ] + ], + "xUnit": "rem", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem", + "scale": 1.5 }, { "_id": "get64yaM3ZzT9aRs3", @@ -397,8 +465,12 @@ "x": 1.3, "y": 24.9, "width": 22, - "scale": 0, - "showPartTitle": true + "scale": 1.4, + "showPartTitle": true, + "xUnit": "rem", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "h9xDp3HvzLaSo4yJi", @@ -416,8 +488,12 @@ "x": 3, "y": 3.8, "width": 16, - "height": 3, - "scale": 0 + "height": 3.2, + "scale": 1, + "xUnit": "rem", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" }, { "_id": "dkKTHZAwF98aKkD47", @@ -432,7 +508,7 @@ "default": false, "nextInCurrentPart": false, "oneNextPerSourceLayer": false, - "url": "http://sofqb06-od:1176/?view=mic-tally", + "url": "http://{{studio.settings.sofieUrl}}:1176/?view=mic-tally", "x": 3, "y": 0, "width": 16, @@ -440,7 +516,11 @@ "customClasses": [ "" ], - "scale": 0 + "scale": 0, + "xUnit": "rem", + "yUnit": "rem", + "widthUnit": "rem", + "heightUnit": "rem" } ], "type": "dashboard_layout", From df8620d0a400bdb3463342de7f7576158d6f225b Mon Sep 17 00:00:00 2001 From: ianshade Date: Mon, 29 May 2023 10:07:10 +0200 Subject: [PATCH 17/35] feat: SOF-772 add temaud adlib action the tag to look for when defining triggers is `gfx_temaud` --- src/__mocks__/context.ts | 2 +- src/tv2-common/actions/actionTypes.ts | 10 ++- src/tv2-common/actions/executeAction.ts | 65 +++++++++++++++++-- src/tv2-common/evaluateCues.ts | 11 ---- src/tv2-constants/enums.ts | 5 +- .../GlobalAdlibActionsGenerator.ts | 38 +++++++++-- src/tv2_afvd_showstyle/getRundown.ts | 23 ++++++- src/tv2_offtube_showstyle/getRundown.ts | 8 +-- 8 files changed, 129 insertions(+), 33 deletions(-) diff --git a/src/__mocks__/context.ts b/src/__mocks__/context.ts index 33b0900e..5619b36a 100644 --- a/src/__mocks__/context.ts +++ b/src/__mocks__/context.ts @@ -469,7 +469,7 @@ export class ActionExecutionContextMock extends ShowStyleUserContextMock impleme return pieces.map((pieceInstance) => { const pieceStart = pieceInstance.piece.enable.start const resolvedStart = pieceStart === 'now' ? now : pieceStart - + return { ...pieceInstance, resolvedStart diff --git a/src/tv2-common/actions/actionTypes.ts b/src/tv2-common/actions/actionTypes.ts index b69062d9..9887df2e 100644 --- a/src/tv2-common/actions/actionTypes.ts +++ b/src/tv2-common/actions/actionTypes.ts @@ -87,12 +87,16 @@ export interface ActionCommentatorSelectJingle extends ActionBase { type: AdlibActionType.COMMENTATOR_SELECT_JINGLE } -export interface ActionClearGraphics extends ActionBase { - type: AdlibActionType.CLEAR_GRAPHICS +export interface ActionClearAllGraphics extends ActionBase { + type: AdlibActionType.CLEAR_ALL_GRAPHICS sendCommands?: boolean label: string } +export interface ActionClearTemaGraphics extends ActionBase { + type: AdlibActionType.CLEAR_TEMA_GRAPHICS +} + export interface ActionTakeWithTransitionVariantBase { type: 'cut' | 'mix' | 'breaker' | 'dip' } @@ -154,7 +158,7 @@ export type TV2AdlibAction = | ActionCommentatorSelectDVE | ActionCommentatorSelectFull | ActionCommentatorSelectJingle - | ActionClearGraphics + | ActionClearAllGraphics | ActionTakeWithTransition | ActionRecallLastLive | ActionRecallLastDVE diff --git a/src/tv2-common/actions/executeAction.ts b/src/tv2-common/actions/executeAction.ts index e99aa2f4..a9dfdfc0 100644 --- a/src/tv2-common/actions/executeAction.ts +++ b/src/tv2-common/actions/executeAction.ts @@ -17,7 +17,7 @@ import { WithTimeline } from 'blueprints-integration' import { - ActionClearGraphics, + ActionClearAllGraphics, ActionCutSourceToBox, ActionCutToCamera, ActionCutToRemote, @@ -204,8 +204,11 @@ export async function executeAction< case AdlibActionType.SELECT_JINGLE: await executeActionSelectJingle(context, settings, actionId, userData as ActionSelectJingle) break - case AdlibActionType.CLEAR_GRAPHICS: - await executeActionClearGraphics(context, userData as ActionClearGraphics) + case AdlibActionType.CLEAR_ALL_GRAPHICS: + await executeActionClearAllGraphics(context, userData as ActionClearAllGraphics) + break + case AdlibActionType.CLEAR_TEMA_GRAPHICS: + await executeActionClearTemaGraphics(context) break case AdlibActionType.CUT_TO_CAMERA: await executeActionCutToCamera(context, settings, actionId, userData as ActionCutToCamera) @@ -1901,10 +1904,10 @@ async function executeActionSelectFull< await context.core.stopPiecesOnLayers([SharedSourceLayer.SelectedAdlibGraphicsFull]) } -async function executeActionClearGraphics< +async function executeActionClearAllGraphics< StudioConfig extends TV2StudioConfigBase, ShowStyleConfig extends TV2BlueprintConfigBase ->(context: ActionExecutionContext, userData: ActionClearGraphics) { +>(context: ActionExecutionContext, userData: ActionClearAllGraphics) { await context.core.stopPiecesOnLayers(STOPPABLE_GRAPHICS_LAYERS) await context.core.insertPiece('current', { enable: { @@ -1955,6 +1958,58 @@ async function executeActionClearGraphics< }) } +async function executeActionClearTemaGraphics(context: ActionExecutionContext) { + await context.core.stopPiecesOnLayers([SharedSourceLayer.PgmGraphicsTema]) + await context.core.insertPiece('current', { + enable: { + start: 'now', + duration: 3000 + }, + externalId: 'clearTemaGFX', + name: 'GFX Temaud', + sourceLayerId: SharedSourceLayer.PgmAdlibGraphicCmd, + outputLayerId: SharedOutputLayer.SEC, + lifespan: PieceLifespan.WithinPart, + content: + context.config.studio.GraphicsType === 'HTML' + ? { + timelineObjects: [ + literal({ + id: '', + enable: { + start: 0 + }, + priority: 1, + layer: SharedGraphicLLayer.GraphicLLayerAdLibs, + content: { + deviceType: TSR.DeviceType.ABSTRACT + } + }) + ] + } + : { + timelineObjects: [ + literal({ + id: '', + enable: { + start: 0 + }, + priority: 100, + layer: SharedGraphicLLayer.GraphicLLayerAdLibs, + content: { + deviceType: TSR.DeviceType.VIZMSE, + type: TSR.TimelineContentTypeVizMSE.ELEMENT_INTERNAL, + templateName: 'OUT_TEMA_H', + templateData: [], + showName: context.config.selectedGfxSetup.OvlShowName ?? '' + } + }) + ] + }, + tags: [TallyTags.GFX_TEMAUD] + }) +} + export function mergePersistenceMetaData( currentSegmentId: string, nextMetaData: SisyfosPersistenceMetaData, diff --git a/src/tv2-common/evaluateCues.ts b/src/tv2-common/evaluateCues.ts index ec0ec2ef..ddb99e9f 100644 --- a/src/tv2-common/evaluateCues.ts +++ b/src/tv2-common/evaluateCues.ts @@ -408,17 +408,6 @@ export async function EvaluateCuesBase( } }) } - } else if (obj.content.type === TSR.TimelineContentTypeVizMSE.CLEAR_ALL_ELEMENTS) { - const o = obj as TSR.TimelineObjVIZMSEClearAllElements - piece.expectedPlayoutItems.push({ - deviceSubType: TSR.DeviceType.VIZMSE, - content: { - templateName: 'altud', - channel: 'OVL1', - templateData: [], - showName: o.content.showName - } - }) } } }) diff --git a/src/tv2-constants/enums.ts b/src/tv2-constants/enums.ts index 5b6c089b..0159d2f3 100644 --- a/src/tv2-constants/enums.ts +++ b/src/tv2-constants/enums.ts @@ -88,6 +88,7 @@ export enum AdlibTags { ADLIB_CUT_TO_BOX_3 = 'cut_to_box_3', ADLIB_CUT_TO_BOX_4 = 'cut_to_box_4', ADLIB_GFX_ALTUD = 'gfx_altud', + ADLIB_GFX_TEMAUD = 'gfx_temaud', ADLIB_GFX_LOAD = 'gfx_load', ADLIB_GFX_CONTINUE_FORWARD = 'gfx_continue_forward', ADLIB_DSK_ON = 'dsk_on', @@ -137,7 +138,8 @@ export enum AdlibActionType { COMMENTATOR_SELECT_DVE = 'commentator_select_dve', COMMENTATOR_SELECT_FULL = 'commentator_select_full', COMMENTATOR_SELECT_JINGLE = 'commentator_select_jingle', - CLEAR_GRAPHICS = 'clear_graphics', + CLEAR_ALL_GRAPHICS = 'clear_all_graphics', + CLEAR_TEMA_GRAPHICS = 'clear_tema_graphics', TAKE_WITH_TRANSITION = 'take_with_transition', RECALL_LAST_LIVE = 'recall_last_live', RECALL_LAST_DVE = 'recall_last_dve', @@ -149,6 +151,7 @@ export enum TallyTags { // Actions GFX_CLEAR = 'GFX_CLEAR', GFX_ALTUD = 'GFX_ALTUD', + GFX_TEMAUD = 'GFX_TEMAUD', TAKE_WITH_TRANSITION = 'TAKE_WITH_TRANSITION', // A particular source is live diff --git a/src/tv2_afvd_showstyle/GlobalAdlibActionsGenerator.ts b/src/tv2_afvd_showstyle/GlobalAdlibActionsGenerator.ts index 6922e69d..1a7c8748 100644 --- a/src/tv2_afvd_showstyle/GlobalAdlibActionsGenerator.ts +++ b/src/tv2_afvd_showstyle/GlobalAdlibActionsGenerator.ts @@ -1,7 +1,8 @@ import { IBlueprintActionManifest } from 'blueprints-integration' import { ActionCallRobotPreset, - ActionClearGraphics, + ActionClearAllGraphics, + ActionClearTemaGraphics, ActionCutSourceToBox, ActionCutToCamera, ActionCutToRemote, @@ -76,6 +77,7 @@ export class GlobalAdlibActionsGenerator { blueprintActions.push(this.makeClearGraphicsAction()) blueprintActions.push(this.makeClearGraphicsAltudAction()) + blueprintActions.push(this.makeClearGraphicsTemaudAction()) blueprintActions.push(...GetTransitionAdLibActions(this.config, 800)) @@ -266,14 +268,14 @@ export class GlobalAdlibActionsGenerator { } private makeClearGraphicsAction(): IBlueprintActionManifest { - const userData: ActionClearGraphics = { - type: AdlibActionType.CLEAR_GRAPHICS, + const userData: ActionClearAllGraphics = { + type: AdlibActionType.CLEAR_ALL_GRAPHICS, sendCommands: true, label: 'GFX Clear' } return { externalId: generateExternalId(this.context.core, userData), - actionId: AdlibActionType.CLEAR_GRAPHICS, + actionId: AdlibActionType.CLEAR_ALL_GRAPHICS, userData, userDataManifest: {}, display: { @@ -290,14 +292,14 @@ export class GlobalAdlibActionsGenerator { } private makeClearGraphicsAltudAction(): IBlueprintActionManifest { - const userData: ActionClearGraphics = { - type: AdlibActionType.CLEAR_GRAPHICS, + const userData: ActionClearAllGraphics = { + type: AdlibActionType.CLEAR_ALL_GRAPHICS, sendCommands: false, label: 'GFX Altud' } return { externalId: generateExternalId(this.context.core, userData), - actionId: AdlibActionType.CLEAR_GRAPHICS, + actionId: AdlibActionType.CLEAR_ALL_GRAPHICS, userData, userDataManifest: {}, display: { @@ -313,6 +315,28 @@ export class GlobalAdlibActionsGenerator { } } + private makeClearGraphicsTemaudAction(): IBlueprintActionManifest { + const userData: ActionClearTemaGraphics = { + type: AdlibActionType.CLEAR_TEMA_GRAPHICS + } + return { + externalId: generateExternalId(this.context.core, userData), + actionId: AdlibActionType.CLEAR_TEMA_GRAPHICS, + userData, + userDataManifest: {}, + display: { + _rank: 400, + label: t(`GFX Temaud`), + sourceLayerId: SourceLayer.PgmAdlibGraphicCmd, + outputLayerId: SharedOutputLayer.SEC, + content: {}, + tags: [AdlibTags.ADLIB_GFX_TEMAUD], + currentPieceTags: [TallyTags.GFX_TEMAUD], + nextPieceTags: [TallyTags.GFX_TEMAUD] + } + } + } + private makeLastDveAction(): IBlueprintActionManifest { const recallLastLiveDveUserData: ActionRecallLastDVE = { type: AdlibActionType.RECALL_LAST_DVE diff --git a/src/tv2_afvd_showstyle/getRundown.ts b/src/tv2_afvd_showstyle/getRundown.ts index 386b8cdb..bb75ee2b 100644 --- a/src/tv2_afvd_showstyle/getRundown.ts +++ b/src/tv2_afvd_showstyle/getRundown.ts @@ -451,6 +451,7 @@ class GlobalAdLibPiecesGenerator { function getBaseline(context: ShowStyleContext): BlueprintResultBaseline { const jingleDsk = findDskJingle(context.config) const fullGfxDsk = findDskFullGfx(context.config) + const selectedGfxSetup = context.config.selectedGfxSetup return { timelineObjects: _.compact([ @@ -729,6 +730,26 @@ function getBaseline(context: ShowStyleContext): Bluepri concept: context.config.selectedGfxSetup.VcpConcept } }) - ]) + ]), + expectedPlayoutItems: [ + { + deviceSubType: TSR.DeviceType.VIZMSE, + content: { + templateName: 'OUT_TEMA_H', + channel: 'OVL1', + templateData: [], + showName: selectedGfxSetup.OvlShowName + } + }, + { + deviceSubType: TSR.DeviceType.VIZMSE, + content: { + templateName: 'altud', + channel: 'OVL1', + templateData: [], + showName: selectedGfxSetup.OvlShowName + } + } + ] } } diff --git a/src/tv2_offtube_showstyle/getRundown.ts b/src/tv2_offtube_showstyle/getRundown.ts index ac0ad6ae..cb5fac2a 100644 --- a/src/tv2_offtube_showstyle/getRundown.ts +++ b/src/tv2_offtube_showstyle/getRundown.ts @@ -12,7 +12,7 @@ import { TSR } from 'blueprints-integration' import { - ActionClearGraphics, + ActionClearAllGraphics, ActionCommentatorSelectDVE, ActionCommentatorSelectFull, ActionCommentatorSelectJingle, @@ -425,14 +425,14 @@ function getGlobalAdlibActionsOfftube( blueprintActions.push(makeCommentatorSelectFullAction()) function makeClearGraphicsAltudAction(): IBlueprintActionManifest { - const userData: ActionClearGraphics = { - type: AdlibActionType.CLEAR_GRAPHICS, + const userData: ActionClearAllGraphics = { + type: AdlibActionType.CLEAR_ALL_GRAPHICS, sendCommands: false, label: 'GFX Altud' } return { externalId: generateExternalId(context, userData), - actionId: AdlibActionType.CLEAR_GRAPHICS, + actionId: AdlibActionType.CLEAR_ALL_GRAPHICS, userData, userDataManifest: {}, display: { From f4509d9135edf5427574b59c71c9b376c28c4355 Mon Sep 17 00:00:00 2001 From: ianshade Date: Mon, 29 May 2023 22:36:08 +0200 Subject: [PATCH 18/35] feat: SOF-1408 add audio filter support for audiobeds --- src/__mocks__/context.ts | 2 +- src/tv2-common/blueprintConfig.ts | 1 + src/tv2-common/cues/lyd.ts | 17 +++++++++++++++-- src/tv2_afvd_showstyle/__tests__/configs.ts | 3 ++- .../__tests__/config-manifest.spec.ts | 3 ++- src/tv2_afvd_studio/config-manifests.ts | 8 ++++++++ 6 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/__mocks__/context.ts b/src/__mocks__/context.ts index 33b0900e..5619b36a 100644 --- a/src/__mocks__/context.ts +++ b/src/__mocks__/context.ts @@ -469,7 +469,7 @@ export class ActionExecutionContextMock extends ShowStyleUserContextMock impleme return pieces.map((pieceInstance) => { const pieceStart = pieceInstance.piece.enable.start const resolvedStart = pieceStart === 'now' ? now : pieceStart - + return { ...pieceInstance, resolvedStart diff --git a/src/tv2-common/blueprintConfig.ts b/src/tv2-common/blueprintConfig.ts index a2c6c01e..3230ec4d 100644 --- a/src/tv2-common/blueprintConfig.ts +++ b/src/tv2-common/blueprintConfig.ts @@ -149,6 +149,7 @@ export interface TV2StudioConfigBase { fadeIn: number fadeOut: number volume: number + useAudioFilterSyntax?: boolean } } diff --git a/src/tv2-common/cues/lyd.ts b/src/tv2-common/cues/lyd.ts index 0cec4474..908144a4 100644 --- a/src/tv2-common/cues/lyd.ts +++ b/src/tv2-common/cues/lyd.ts @@ -137,11 +137,11 @@ function LydContent( deviceType: TSR.DeviceType.CASPARCG, type: TSR.TimelineContentTypeCasparCg.MEDIA, file: filePath, - channelLayout: 'bed', + ...getAudioBedChannelLayoutProperties(config), loop: true, noStarttime: true, mixer: { - volume: Number(config.studio.AudioBedSettings.volume) / 100 + volume: config.studio.AudioBedSettings.volume / 100 }, transitions: { inTransition: { @@ -177,6 +177,19 @@ function LydContent( }) } +function getAudioBedChannelLayoutProperties( + config: TV2ShowStyleConfig +): Pick { + if (config.studio.AudioBedSettings.useAudioFilterSyntax) { + return { + audioFilter: 'pan=4c|c2=c0|c3=c1' + } + } + return { + channelLayout: 'bed' + } +} + export function CreateLYDBaseline(studio: string): TSR.TSRTimelineObj[] { return [ literal({ diff --git a/src/tv2_afvd_showstyle/__tests__/configs.ts b/src/tv2_afvd_showstyle/__tests__/configs.ts index 608b8fde..634e26d7 100644 --- a/src/tv2_afvd_showstyle/__tests__/configs.ts +++ b/src/tv2_afvd_showstyle/__tests__/configs.ts @@ -127,7 +127,8 @@ export const defaultStudioConfig: StudioConfig = { AudioBedSettings: { fadeIn: 1000, fadeOut: 1000, - volume: 80 + volume: 80, + useAudioFilterSyntax: false }, CasparPrerollDuration: 280, GraphicsType: 'VIZ', diff --git a/src/tv2_afvd_studio/__tests__/config-manifest.spec.ts b/src/tv2_afvd_studio/__tests__/config-manifest.spec.ts index f8947a85..d95b6ba9 100644 --- a/src/tv2_afvd_studio/__tests__/config-manifest.spec.ts +++ b/src/tv2_afvd_studio/__tests__/config-manifest.spec.ts @@ -59,7 +59,8 @@ const blankStudioConfig: StudioConfig = { AudioBedSettings: { fadeIn: 0, fadeOut: 0, - volume: 0 + volume: 0, + useAudioFilterSyntax: false }, CasparPrerollDuration: 0, PreventOverlayWithFull: true, diff --git a/src/tv2_afvd_studio/config-manifests.ts b/src/tv2_afvd_studio/config-manifests.ts index 33eb18ba..7663379b 100644 --- a/src/tv2_afvd_studio/config-manifests.ts +++ b/src/tv2_afvd_studio/config-manifests.ts @@ -401,6 +401,14 @@ export const studioConfigManifest: ConfigManifestEntry[] = [ required: false, defaultVal: 25 }, + { + id: 'AudioBedSettings.useAudioFilterSyntax', + name: 'Use Audio Filter Syntax', + description: 'Required for CasparCG 2.3 and higher', + type: ConfigManifestEntryType.BOOLEAN, + required: false, + defaultVal: false + }, { id: 'CasparPrerollDuration', name: 'Caspar preroll duration', From fa3a8897f57255b1cf52a2d132f7825e1ec0cb27 Mon Sep 17 00:00:00 2001 From: ianshade Date: Tue, 30 May 2023 08:47:48 +0200 Subject: [PATCH 19/35] chore: SOF-1213 print error when upload-layouts.sh is missing an argument --- scripts/upload-layouts.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/upload-layouts.sh b/scripts/upload-layouts.sh index 872618a2..7c5e0101 100644 --- a/scripts/upload-layouts.sh +++ b/scripts/upload-layouts.sh @@ -2,6 +2,12 @@ echo "Upload Shelf Layouts" +if [ $# -eq 0 ]; then + echo "Error: Path to layouts directory not provided." + echo "Usage: $(basename "$0") LAYOUTS_PATH" + exit 1 +fi + directory=$1 upload_layouts_from_directory() { From 93b8e19e3b089631b4ca932e570da0aa115a1f48 Mon Sep 17 00:00:00 2001 From: Rasmus Lindved Date: Thu, 1 Jun 2023 12:02:00 +0200 Subject: [PATCH 20/35] SOF-1420 Add GfxShowMapping to Qbox. Reduce duplication of configuration --- src/tv2-common/blueprintConfig.ts | 1 + src/tv2-common/showstyle/config-manifests.ts | 145 +++++++++++++++++ src/tv2_afvd_showstyle/config-manifests.ts | 153 +----------------- src/tv2_afvd_showstyle/helpers/config.ts | 10 +- .../__tests__/config-manifest.spec.ts | 1 + src/tv2_offtube_showstyle/config-manifests.ts | 106 ++---------- 6 files changed, 165 insertions(+), 251 deletions(-) diff --git a/src/tv2-common/blueprintConfig.ts b/src/tv2-common/blueprintConfig.ts index 46ccabc7..ac3d433c 100644 --- a/src/tv2-common/blueprintConfig.ts +++ b/src/tv2-common/blueprintConfig.ts @@ -192,6 +192,7 @@ export interface TV2ShowstyleBlueprintConfigBase { LYDConfig: TableConfigItemValue GfxSchemaTemplates: TableConfigGfxSchema[] GfxSetups: TableConfigGfxSetup[] + GfxShowMapping: TableConfigItemGfxShowMapping[] GfxDefaults: TableConfigItemGfxDefaults[] } diff --git a/src/tv2-common/showstyle/config-manifests.ts b/src/tv2-common/showstyle/config-manifests.ts index 3b2f839e..d675ffb1 100644 --- a/src/tv2-common/showstyle/config-manifests.ts +++ b/src/tv2-common/showstyle/config-manifests.ts @@ -1,4 +1,5 @@ import { ConfigManifestEntry, ConfigManifestEntryTable, ConfigManifestEntryType } from 'blueprints-integration' +import { DEFAULT_GRAPHICS } from '../migrations' export enum ShowStyleConfigId { GRAPHICS_SETUPS_TABLE_ID = 'GfxSetups', @@ -47,6 +48,150 @@ export const getGfxSetupsEntries = (columns: ConfigManifestEntryTable['columns'] } ] +const DESIGN_TABLE_ID = 'GfxDesignTemplates' +const DESIGN_NAME_COLUMN_ID = 'INewsName' + +export const gfxDesignTemplates: ConfigManifestEntry[] = [ + { + id: DESIGN_TABLE_ID, + name: 'GFX Design Templates', + description: '', + type: ConfigManifestEntryType.TABLE, + required: true, + defaultVal: DEFAULT_GRAPHICS.map((val) => ({ _id: '', ...val })).filter((template) => template.IsDesign), + columns: [ + { + id: DESIGN_NAME_COLUMN_ID, + name: 'iNews Name', + description: 'The name of the design', + type: ConfigManifestEntryType.STRING, + required: false, + defaultVal: '', + rank: 0 + }, + { + id: 'INewsStyleColumn', + name: 'iNews Style Column', + description: 'The selected style', + type: ConfigManifestEntryType.STRING, + required: false, + defaultVal: '', + rank: 1 + }, + { + id: 'VizTemplate', + name: 'GFX Template Name', + description: 'The name of the Viz Template', + type: ConfigManifestEntryType.STRING, + required: true, + defaultVal: '', + rank: 2 + } + ] + } +] + +const GFX_SCHEMA_TABLE_ID = 'GfxSchemaTemplates' +const GFX_SCHEMA_NAME_COLUMN_ID = 'GfxSchemaTemplatesName' + +export const gfxSchemaTemplates: ConfigManifestEntry[] = [ + { + id: GFX_SCHEMA_TABLE_ID, + name: 'GFX Skema Templates', + description: 'The values for the Skema and Design combinations', + type: ConfigManifestEntryType.TABLE, + required: false, + defaultVal: [], + columns: [ + { + id: GFX_SCHEMA_NAME_COLUMN_ID, + name: 'iNews Name', + description: 'The name of the design', + type: ConfigManifestEntryType.STRING, + required: false, + defaultVal: '', + rank: 0 + }, + { + id: 'INewsSkemaColumn', + name: 'iNews Skema Column', + description: 'The selected skema', + type: ConfigManifestEntryType.STRING, + required: false, + defaultVal: '', + rank: 1 + }, + { + id: 'VizTemplate', + name: 'GFX Template Name', + description: 'The name of the Viz Template', + type: ConfigManifestEntryType.STRING, + required: true, + defaultVal: '', + rank: 2 + }, + { + id: 'CasparCgDesignValues', + name: 'CasparCG Design Values', + description: + 'A JSON array containing color values and background loops for the different Designs of the Schema used by CasparCG', + type: ConfigManifestEntryType.JSON, + required: false, + defaultVal: '', + rank: 3 + } + ] + } +] + +export const gfxShowMapping: ConfigManifestEntry = { + id: 'GfxShowMapping', + name: 'GFX Show mapping', + description: + 'Maps Overlay Shows to the variety of Skemas and Designs. If a Setup does not have a corresponding Design/Skema, it should be left out of this table.', + type: ConfigManifestEntryType.TABLE, + required: false, + defaultVal: [], + columns: [ + { + id: ShowStyleConfigId.GFX_SHOW_MAPPING_DESIGN_COLUMN_ID, + name: 'Design', + rank: 0, + description: 'Name of the Design from the GFX Design table', + type: ConfigManifestEntryType.SELECT_FROM_COLUMN, + tableId: DESIGN_TABLE_ID, + columnId: DESIGN_NAME_COLUMN_ID, + multiple: false, + required: false, + defaultVal: '' + }, + { + id: ShowStyleConfigId.GFX_SHOW_MAPPING_GFX_SETUP_COLUMN_ID, + name: 'GFX Setup', + rank: 1, + description: 'Names of the GFX Setups', + type: ConfigManifestEntryType.SELECT_FROM_COLUMN, + tableId: ShowStyleConfigId.GRAPHICS_SETUPS_TABLE_ID, + columnId: ShowStyleConfigId.GRAPHICS_SETUPS_NAME_COLUMN_ID, + multiple: true, + required: false, + defaultVal: [] + }, + { + id: ShowStyleConfigId.GFX_SHOW_MAPPING_SCHEMA_COLUMN_ID, + name: 'GFX Skema Templates', + rank: 2, + description: 'Names of the Skemas', + type: ConfigManifestEntryType.SELECT_FROM_COLUMN, + tableId: GFX_SCHEMA_TABLE_ID, + columnId: GFX_SCHEMA_NAME_COLUMN_ID, + multiple: true, + required: false, + defaultVal: [] + } + ] +} + const GFX_DEFAULT_VALUES = { DefaultSetupName: '', DefaultSchema: '', diff --git a/src/tv2_afvd_showstyle/config-manifests.ts b/src/tv2_afvd_showstyle/config-manifests.ts index da97ad01..319fbc8e 100644 --- a/src/tv2_afvd_showstyle/config-manifests.ts +++ b/src/tv2_afvd_showstyle/config-manifests.ts @@ -1,5 +1,12 @@ import { ConfigManifestEntry, ConfigManifestEntryType, TSR } from 'blueprints-integration' -import { DEFAULT_GRAPHICS, getGfxDefaults, getGfxSetupsEntries, ShowStyleConfigId } from 'tv2-common' +import { + DEFAULT_GRAPHICS, + getGfxDefaults, + getGfxSetupsEntries, + gfxDesignTemplates, + gfxSchemaTemplates, + gfxShowMapping +} from 'tv2-common' export const dveStylesManifest: ConfigManifestEntry = { id: 'DVEStyles', @@ -177,150 +184,6 @@ const gfxSetups = getGfxSetupsEntries([ const gfxDefaults = getGfxDefaults -const DESIGN_TABLE_ID = 'GfxDesignTemplates' -const DESIGN_NAME_COLUMN_ID = 'INewsName' - -export const gfxDesignTemplates: ConfigManifestEntry[] = [ - { - id: DESIGN_TABLE_ID, - name: 'GFX Design Templates', - description: '', - type: ConfigManifestEntryType.TABLE, - required: true, - defaultVal: DEFAULT_GRAPHICS.map((val) => ({ _id: '', ...val })).filter((template) => template.IsDesign), - columns: [ - { - id: DESIGN_NAME_COLUMN_ID, - name: 'iNews Name', - description: 'The name of the design', - type: ConfigManifestEntryType.STRING, - required: false, - defaultVal: '', - rank: 0 - }, - { - id: 'INewsStyleColumn', - name: 'iNews Style Column', - description: 'The selected style', - type: ConfigManifestEntryType.STRING, - required: false, - defaultVal: '', - rank: 1 - }, - { - id: 'VizTemplate', - name: 'GFX Template Name', - description: 'The name of the Viz Template', - type: ConfigManifestEntryType.STRING, - required: true, - defaultVal: '', - rank: 2 - } - ] - } -] - -const GFX_SCHEMA_TABLE_ID = 'GfxSchemaTemplates' -const GFX_SCHEMA_NAME_COLUMN_ID = 'GfxSchemaTemplatesName' - -export const gfxSchemaTemplates: ConfigManifestEntry[] = [ - { - id: GFX_SCHEMA_TABLE_ID, - name: 'GFX Skema Templates', - description: 'The values for the Skema and Design combinations', - type: ConfigManifestEntryType.TABLE, - required: false, - defaultVal: [], - columns: [ - { - id: GFX_SCHEMA_NAME_COLUMN_ID, - name: 'iNews Name', - description: 'The name of the design', - type: ConfigManifestEntryType.STRING, - required: false, - defaultVal: '', - rank: 0 - }, - { - id: 'INewsSkemaColumn', - name: 'iNews Skema Column', - description: 'The selected skema', - type: ConfigManifestEntryType.STRING, - required: false, - defaultVal: '', - rank: 1 - }, - { - id: 'VizTemplate', - name: 'GFX Template Name', - description: 'The name of the Viz Template', - type: ConfigManifestEntryType.STRING, - required: true, - defaultVal: '', - rank: 2 - }, - { - id: 'CasparCgDesignValues', - name: 'CasparCG Design Values', - description: - 'A JSON array containing color values and background loops for the different Designs of the Schema used by CasparCG', - type: ConfigManifestEntryType.JSON, - required: false, - defaultVal: '', - rank: 3 - } - ] - } -] - -export const gfxShowMapping: ConfigManifestEntry = { - id: 'GfxShowMapping', - name: 'GFX Show mapping', - description: - 'Maps Overlay Shows to the variety of Skemas and Designs. If a Setup does not have a corresponding Design/Skema, it should be left out of this table.', - type: ConfigManifestEntryType.TABLE, - required: false, - defaultVal: [], - columns: [ - { - id: ShowStyleConfigId.GFX_SHOW_MAPPING_DESIGN_COLUMN_ID, - name: 'Design', - rank: 0, - description: 'Name of the Design from the GFX Design table', - type: ConfigManifestEntryType.SELECT_FROM_COLUMN, - tableId: DESIGN_TABLE_ID, - columnId: DESIGN_NAME_COLUMN_ID, - multiple: false, - required: false, - defaultVal: '' - }, - { - id: ShowStyleConfigId.GFX_SHOW_MAPPING_GFX_SETUP_COLUMN_ID, - name: 'GFX Setup', - rank: 1, - description: 'Names of the GFX Setups', - type: ConfigManifestEntryType.SELECT_FROM_COLUMN, - tableId: ShowStyleConfigId.GRAPHICS_SETUPS_TABLE_ID, - columnId: ShowStyleConfigId.GRAPHICS_SETUPS_NAME_COLUMN_ID, - multiple: true, - required: false, - defaultVal: [] - }, - { - id: ShowStyleConfigId.GFX_SHOW_MAPPING_SCHEMA_COLUMN_ID, - name: 'GFX Skema Templates', - rank: 2, - description: 'Names of the Skemas', - type: ConfigManifestEntryType.SELECT_FROM_COLUMN, - tableId: GFX_SCHEMA_TABLE_ID, - columnId: GFX_SCHEMA_NAME_COLUMN_ID, - multiple: true, - required: false, - defaultVal: [] - } - ] -} - export const showStyleConfigManifest: ConfigManifestEntry[] = [ { id: 'MakeAdlibsForFulls', diff --git a/src/tv2_afvd_showstyle/helpers/config.ts b/src/tv2_afvd_showstyle/helpers/config.ts index 0e19d7ce..19e442fb 100644 --- a/src/tv2_afvd_showstyle/helpers/config.ts +++ b/src/tv2_afvd_showstyle/helpers/config.ts @@ -1,11 +1,5 @@ import { IBlueprintConfig, ICommonContext, TableConfigItemValue } from 'blueprints-integration' -import { - findGfxSetup, - TableConfigGfxSetup, - TableConfigItemGfxDefaults, - TableConfigItemGfxShowMapping, - TV2ShowstyleBlueprintConfigBase -} from 'tv2-common' +import { findGfxSetup, TableConfigGfxSetup, TV2ShowstyleBlueprintConfigBase } from 'tv2-common' import { GalleryStudioConfig } from '../../tv2_afvd_studio/helpers/config' export interface GalleryTableConfigGfxSetup extends TableConfigGfxSetup { @@ -22,8 +16,6 @@ export interface GalleryBlueprintConfig extends GalleryStudioConfig { export interface GalleryShowStyleConfig extends TV2ShowstyleBlueprintConfigBase { WipesConfig: TableConfigItemValue GfxSetups: GalleryTableConfigGfxSetup[] - GfxShowMapping: TableConfigItemGfxShowMapping[] - GfxDefaults: TableConfigItemGfxDefaults[] } export function preprocessConfig(context: ICommonContext, rawConfig: IBlueprintConfig): any { diff --git a/src/tv2_offtube_showstyle/__tests__/config-manifest.spec.ts b/src/tv2_offtube_showstyle/__tests__/config-manifest.spec.ts index ad83a326..980523dd 100644 --- a/src/tv2_offtube_showstyle/__tests__/config-manifest.spec.ts +++ b/src/tv2_offtube_showstyle/__tests__/config-manifest.spec.ts @@ -16,6 +16,7 @@ const blankShowStyleConfig: OfftubeShowStyleConfig = { MakeAdlibsForFulls: true, GfxSchemaTemplates: [], GfxSetups: [], + GfxShowMapping: [], GfxDefaults: [] } diff --git a/src/tv2_offtube_showstyle/config-manifests.ts b/src/tv2_offtube_showstyle/config-manifests.ts index 58b7d8e8..f3e25135 100644 --- a/src/tv2_offtube_showstyle/config-manifests.ts +++ b/src/tv2_offtube_showstyle/config-manifests.ts @@ -1,5 +1,12 @@ import { ConfigManifestEntry, ConfigManifestEntryType, TSR } from 'blueprints-integration' -import { DEFAULT_GRAPHICS, getGfxDefaults, getGfxSetupsEntries } from 'tv2-common' +import { + DEFAULT_GRAPHICS, + getGfxDefaults, + getGfxSetupsEntries, + gfxDesignTemplates, + gfxSchemaTemplates, + gfxShowMapping +} from 'tv2-common' export const dveStylesManifest: ConfigManifestEntry = { id: 'DVEStyles', @@ -142,102 +149,6 @@ export const dveStylesManifest: ConfigManifestEntry = { ] } -const DESIGN_TABLE_ID = 'GfxDesignTemplates' -const DESIGN_NAME_COLUMN_ID = 'INewsName' - -export const gfxDesignTemplates: ConfigManifestEntry[] = [ - { - id: DESIGN_TABLE_ID, - name: 'GFX Design Templates', - description: '', - type: ConfigManifestEntryType.TABLE, - required: true, - defaultVal: DEFAULT_GRAPHICS.map((val) => ({ _id: '', ...val })).filter((template) => template.IsDesign), - columns: [ - { - id: DESIGN_NAME_COLUMN_ID, - name: 'iNews Name', - description: 'The name of the design', - type: ConfigManifestEntryType.STRING, - required: false, - defaultVal: '', - rank: 0 - }, - { - id: 'INewsStyleColumn', - name: 'iNews Style Column', - description: 'The selected style', - type: ConfigManifestEntryType.STRING, - required: false, - defaultVal: '', - rank: 1 - }, - { - id: 'VizTemplate', - name: 'GFX Template Name', - description: 'The name of the design in the HTML package', - type: ConfigManifestEntryType.STRING, - required: true, - defaultVal: '', - rank: 2 - } - ] - } -] - -const GFX_SCHEMA_TABLE_ID = 'GfxSchemaTemplates' -const GFX_SCHEMA_NAME_COLUMN_ID = 'GfxSchemaTemplatesName' - -export const gfxSchemaTemplates: ConfigManifestEntry[] = [ - { - id: GFX_SCHEMA_TABLE_ID, - name: 'GFX Skema Templates', - description: 'The values for the Skema and Design combinations', - type: ConfigManifestEntryType.TABLE, - required: false, - defaultVal: [], - columns: [ - { - id: GFX_SCHEMA_NAME_COLUMN_ID, - name: 'iNews Name', - description: 'The name of the design', - type: ConfigManifestEntryType.STRING, - required: false, - defaultVal: '', - rank: 0 - }, - { - id: 'INewsSkemaColumn', - name: 'iNews Skema Column', - description: 'The selected skema', - type: ConfigManifestEntryType.STRING, - required: false, - defaultVal: '', - rank: 1 - }, - { - id: 'VizTemplate', - name: 'GFX Template Name', - description: 'For future asset control', - type: ConfigManifestEntryType.STRING, - required: true, - defaultVal: '', - rank: 2 - }, - { - id: 'CasparCgDesignValues', - name: 'CasparCG Design Values', - description: - 'A JSON array containing color values and background loops for the different Designs of the Schema used by CasparCG', - type: ConfigManifestEntryType.JSON, - required: false, - defaultVal: '', - rank: 3 - } - ] - } -] - export const showStyleConfigManifest: ConfigManifestEntry[] = [ { id: 'MakeAdlibsForFulls', @@ -593,6 +504,7 @@ export const showStyleConfigManifest: ConfigManifestEntry[] = [ ] }, ...gfxSchemaTemplates, + gfxShowMapping, ...getGfxSetupsEntries([]), getGfxDefaults ] From df113799e2794b8f7499283d4ed752b4b38d0ad2 Mon Sep 17 00:00:00 2001 From: ianshade Date: Tue, 6 Jun 2023 09:07:27 +0200 Subject: [PATCH 21/35] refactor: SOF-772 remove unnecessary abstract timeline object HTML graphics do not need any special objects - it is enough to call `stopPiecesOnLayers`, so the inserted piece can be only for display --- src/tv2-common/actions/executeAction.ts | 116 +++++++++--------------- 1 file changed, 45 insertions(+), 71 deletions(-) diff --git a/src/tv2-common/actions/executeAction.ts b/src/tv2-common/actions/executeAction.ts index a9dfdfc0..962afa68 100644 --- a/src/tv2-common/actions/executeAction.ts +++ b/src/tv2-common/actions/executeAction.ts @@ -1909,6 +1909,25 @@ async function executeActionClearAllGraphics< ShowStyleConfig extends TV2BlueprintConfigBase >(context: ActionExecutionContext, userData: ActionClearAllGraphics) { await context.core.stopPiecesOnLayers(STOPPABLE_GRAPHICS_LAYERS) + const timelineObjects: TSR.TSRTimelineObj[] = + context.config.studio.GraphicsType === 'VIZ' + ? [ + { + id: '', + enable: { + start: 0 + }, + priority: 100, + layer: SharedGraphicLLayer.GraphicLLayerAdLibs, + content: { + deviceType: TSR.DeviceType.VIZMSE, + type: TSR.TimelineContentTypeVizMSE.CLEAR_ALL_ELEMENTS, + channelsToSendCommands: userData.sendCommands ? ['OVL1', 'FULL1', 'WALL1'] : undefined, + showName: context.config.selectedGfxSetup.OvlShowName ?? '' // @todo: improve types at the junction of HTML and Viz + } + } + ] + : [] await context.core.insertPiece('current', { enable: { start: 'now', @@ -1919,47 +1938,35 @@ async function executeActionClearAllGraphics< sourceLayerId: SharedSourceLayer.PgmAdlibGraphicCmd, outputLayerId: SharedOutputLayer.SEC, lifespan: PieceLifespan.WithinPart, - content: - context.config.studio.GraphicsType === 'HTML' - ? { - timelineObjects: [ - literal({ - id: '', - enable: { - start: 0 - }, - priority: 1, - layer: SharedGraphicLLayer.GraphicLLayerAdLibs, - content: { - deviceType: TSR.DeviceType.ABSTRACT - } - }) - ] - } - : { - timelineObjects: [ - literal({ - id: '', - enable: { - start: 0 - }, - priority: 100, - layer: SharedGraphicLLayer.GraphicLLayerAdLibs, - content: { - deviceType: TSR.DeviceType.VIZMSE, - type: TSR.TimelineContentTypeVizMSE.CLEAR_ALL_ELEMENTS, - channelsToSendCommands: userData.sendCommands ? ['OVL1', 'FULL1', 'WALL1'] : undefined, - showName: context.config.selectedGfxSetup.OvlShowName ?? '' // @todo: improve types at the junction of HTML and Viz - } - }) - ] - }, + content: { + timelineObjects + }, tags: userData.sendCommands ? [TallyTags.GFX_CLEAR] : [TallyTags.GFX_ALTUD] }) } async function executeActionClearTemaGraphics(context: ActionExecutionContext) { await context.core.stopPiecesOnLayers([SharedSourceLayer.PgmGraphicsTema]) + const timelineObjects: TSR.TSRTimelineObj[] = + context.config.studio.GraphicsType === 'VIZ' + ? [ + { + id: '', + enable: { + start: 0 + }, + priority: 100, + layer: SharedGraphicLLayer.GraphicLLayerAdLibs, + content: { + deviceType: TSR.DeviceType.VIZMSE, + type: TSR.TimelineContentTypeVizMSE.ELEMENT_INTERNAL, + templateName: 'OUT_TEMA_H', + templateData: [], + showName: context.config.selectedGfxSetup.OvlShowName ?? '' + } + } + ] + : [] await context.core.insertPiece('current', { enable: { start: 'now', @@ -1970,42 +1977,9 @@ async function executeActionClearTemaGraphics(context: ActionExecutionContext) { sourceLayerId: SharedSourceLayer.PgmAdlibGraphicCmd, outputLayerId: SharedOutputLayer.SEC, lifespan: PieceLifespan.WithinPart, - content: - context.config.studio.GraphicsType === 'HTML' - ? { - timelineObjects: [ - literal({ - id: '', - enable: { - start: 0 - }, - priority: 1, - layer: SharedGraphicLLayer.GraphicLLayerAdLibs, - content: { - deviceType: TSR.DeviceType.ABSTRACT - } - }) - ] - } - : { - timelineObjects: [ - literal({ - id: '', - enable: { - start: 0 - }, - priority: 100, - layer: SharedGraphicLLayer.GraphicLLayerAdLibs, - content: { - deviceType: TSR.DeviceType.VIZMSE, - type: TSR.TimelineContentTypeVizMSE.ELEMENT_INTERNAL, - templateName: 'OUT_TEMA_H', - templateData: [], - showName: context.config.selectedGfxSetup.OvlShowName ?? '' - } - }) - ] - }, + content: { + timelineObjects + }, tags: [TallyTags.GFX_TEMAUD] }) } From bc07f46733a8310817aba148270a319f6e4ad975 Mon Sep 17 00:00:00 2001 From: ianshade Date: Fri, 9 Jun 2023 08:46:06 +0200 Subject: [PATCH 22/35] fix: SOF-1127 server audio jump on DVE end `noStarttime` prevents seeking when a transition or next part's preroll begins --- src/tv2-common/content/server.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tv2-common/content/server.ts b/src/tv2-common/content/server.ts index c8c1c557..5aa5a773 100644 --- a/src/tv2-common/content/server.ts +++ b/src/tv2-common/content/server.ts @@ -84,7 +84,8 @@ function GetServerTimeline( seek: contentProps.seek, length: contentProps.seek ? contentProps.clipDuration : undefined, inPoint: contentProps.seek ? 0 : undefined, - playing: true + playing: true, + noStarttime: true }, metaData: { mediaPlayerSession: contentProps.mediaPlayerSession From c01cadc6afb276ee6a35efdd7d2c2c07308b916d Mon Sep 17 00:00:00 2001 From: ianshade Date: Mon, 12 Jun 2023 09:03:46 +0200 Subject: [PATCH 23/35] fix: SOF-1195 don't create pieces for robot cues with adlib timecode --- .../__tests__/EvaluateCueTelemetrics.spec.ts | 14 +++++++++++++ src/tv2-common/cues/EvaluateCueRobotCamera.ts | 4 ++++ .../inewsConversion/converters/ParseCue.ts | 21 ++++++++++++------- .../converters/__tests__/cue-parser.spec.ts | 6 ++++++ 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/tv2-common/__tests__/EvaluateCueTelemetrics.spec.ts b/src/tv2-common/__tests__/EvaluateCueTelemetrics.spec.ts index 276d1565..aa231bb1 100644 --- a/src/tv2-common/__tests__/EvaluateCueTelemetrics.spec.ts +++ b/src/tv2-common/__tests__/EvaluateCueTelemetrics.spec.ts @@ -171,6 +171,20 @@ describe('EvaluateCueRobotCamera', () => { } } + it('does not create a piece when adlib is true', () => { + const cueDefinition: CueDefinitionRobotCamera = { + type: CueType.RobotCamera, + presetIdentifier: 1, + iNewsCommand: '', + adlib: true + } + const pieces: IBlueprintPiece[] = [] + + EvaluateCueRobotCamera(cueDefinition, pieces, '') + + expect(pieces.length).toEqual(0) + }) + describe('already has a piece with same externalId', () => { it('has another start time, creates another blueprint piece', () => { const cueDefinition: CueDefinitionRobotCamera = createRobotCameraCueDefinition(1, 20) diff --git a/src/tv2-common/cues/EvaluateCueRobotCamera.ts b/src/tv2-common/cues/EvaluateCueRobotCamera.ts index d626706d..45d5055b 100644 --- a/src/tv2-common/cues/EvaluateCueRobotCamera.ts +++ b/src/tv2-common/cues/EvaluateCueRobotCamera.ts @@ -9,6 +9,10 @@ export function EvaluateCueRobotCamera( pieces: IBlueprintPiece[], externalId: string ): void { + if (cueDefinition.adlib) { + // robot adlibs from iNews cues are not supported + return + } const startTime: number = cueDefinition.start ? calculateTime(cueDefinition.start) ?? 0 : 0 const existingPiece = findExistingPieceForRobotCameraLayerAndStartTime(pieces, startTime) diff --git a/src/tv2-common/inewsConversion/converters/ParseCue.ts b/src/tv2-common/inewsConversion/converters/ParseCue.ts index 06751d09..3dbf9a2b 100644 --- a/src/tv2-common/inewsConversion/converters/ParseCue.ts +++ b/src/tv2-common/inewsConversion/converters/ParseCue.ts @@ -327,7 +327,7 @@ function parsekg( let textFields = cue.length - 1 if (isTime(cue[cue.length - 1])) { kgCue = { ...kgCue, ...parseTime(cue[cue.length - 1]) } - } else if (!cue[cue.length - 1].match(/;[x|\d+].[x|\d+]x/i)) { + } else if (!isAdLibTimecode(cue[cue.length - 1])) { textFields += 1 } else { kgCue.adlib = true @@ -667,12 +667,10 @@ function parseLYD(cue: string[]) { lydCue.variant = command[1] } - if (cue[1]) { - if (isTime(cue[1])) { - lydCue = { ...lydCue, ...parseTime(cue[1]) } - } else if (cue[1].match(/;[x|\d+].[x|\d+]x/i)) { - lydCue.adlib = true - } + if (cue[1] && isTime(cue[1])) { + lydCue = { ...lydCue, ...parseTime(cue[1]) } + } else if (isAdLibTimecode(cue[1])) { + lydCue.adlib = true } return lydCue @@ -979,11 +977,14 @@ function findGraphicSchemaConfiguration(config: TV2ShowStyleConfig, schema: stri function parseRobotCue(cue: string[]): CueDefinitionRobotCamera { const presetIdentifier: number = Number(cue[0].match(/\d+/)) - const time: Pick = cue[1] ? parseTime(cue[1]) : { start: { seconds: 0 } } + const isAdLib = isAdLibTimecode(cue[1]) + const time: Pick = + cue[1] && !isAdLib ? parseTime(cue[1]) : { start: { seconds: 0 } } return { type: CueType.RobotCamera, iNewsCommand: 'RobotCamera', presetIdentifier, + adlib: isAdLib, ...time } } @@ -1010,3 +1011,7 @@ export function UnpairedPilotToGraphic( adlib: targetCue?.adlib ?? pilotCue.adlib }) } + +function isAdLibTimecode(timecode: string | undefined): boolean { + return !!timecode && /;[x|\d+].[x|\d+]x/i.test(timecode) +} diff --git a/src/tv2-common/inewsConversion/converters/__tests__/cue-parser.spec.ts b/src/tv2-common/inewsConversion/converters/__tests__/cue-parser.spec.ts index 17bcb04b..ddac4131 100644 --- a/src/tv2-common/inewsConversion/converters/__tests__/cue-parser.spec.ts +++ b/src/tv2-common/inewsConversion/converters/__tests__/cue-parser.spec.ts @@ -1918,5 +1918,11 @@ describe('Cue parser', () => { expect(result!.start!.seconds).toBe(24) expect(result!.start!.frames).toBe(10) }) + + it('receives time code ;x.xx, it is an adlib', () => { + const cue = ['ROBOT=11', ';x.xx'] + const result = ParseCue(cue, config) as CueDefinitionRobotCamera + expect(result.adlib).toBe(true) + }) }) }) From d3dbe260a91fd98438f191f8d35285d60e4f3c4a Mon Sep 17 00:00:00 2001 From: Rasmus Lindved Date: Mon, 12 Jun 2023 10:46:08 +0200 Subject: [PATCH 24/35] SOF-1420 Able to change DVE bacground loop and overlay values depending on Schema/Design combination --- .../__tests__/gfx-schema-generator.spec.ts | 236 ++++++++++++++++++ .../__tests__/mock-context-builder.ts | 62 +++++ src/tv2-common/blueprintConfig.ts | 12 +- .../cues/evaluate-cue-graphic-schema.ts | 110 -------- .../cues/gfx-schema-generator-facade.ts | 8 + src/tv2-common/cues/gfx-schema-generator.ts | 145 +++++++++++ src/tv2-common/evaluateCues.ts | 4 +- .../__tests__/dve-loop-generator.spec.ts | 189 ++++++++++++++ .../graphics/caspar/dve-loop-generator.ts | 41 +++ .../helpers/graphics/caspar/util.ts | 15 +- .../helpers/graphics/design/index.ts | 46 ++-- src/tv2-common/helpers/graphics/util.ts | 6 +- .../inewsConversion/converters/ParseBody.ts | 2 +- .../inewsConversion/converters/ParseCue.ts | 12 +- .../converters/__tests__/body-parser.spec.ts | 17 -- src/tv2-constants/enums.ts | 1 + .../__tests__/config-manifest.spec.ts | 4 +- src/tv2_afvd_showstyle/__tests__/configs.ts | 6 +- src/tv2_afvd_showstyle/getRundown.ts | 22 +- .../helpers/pieces/evaluateCues.ts | 5 +- src/tv2_afvd_studio/layers.ts | 1 - src/tv2_offtube_showstyle/getRundown.ts | 22 +- .../helpers/EvaluateCues.ts | 5 +- src/tv2_offtube_studio/layers.ts | 1 - 24 files changed, 759 insertions(+), 213 deletions(-) create mode 100644 src/tv2-common/__tests__/gfx-schema-generator.spec.ts create mode 100644 src/tv2-common/__tests__/mock-context-builder.ts delete mode 100644 src/tv2-common/cues/evaluate-cue-graphic-schema.ts create mode 100644 src/tv2-common/cues/gfx-schema-generator-facade.ts create mode 100644 src/tv2-common/cues/gfx-schema-generator.ts create mode 100644 src/tv2-common/helpers/graphics/caspar/__tests__/dve-loop-generator.spec.ts create mode 100644 src/tv2-common/helpers/graphics/caspar/dve-loop-generator.ts diff --git a/src/tv2-common/__tests__/gfx-schema-generator.spec.ts b/src/tv2-common/__tests__/gfx-schema-generator.spec.ts new file mode 100644 index 00000000..d797e97c --- /dev/null +++ b/src/tv2-common/__tests__/gfx-schema-generator.spec.ts @@ -0,0 +1,236 @@ +import { IShowStyleUserContext, TSR } from 'blueprints-integration' +import { anyNumber, anything, instance, mock, verify, when } from 'ts-mockito' +import { + CasparCgGfxDesignValues, + TableConfigGfxSchema, + TableConfigGfxSetup, + TableConfigItemGfxDefaults +} from 'tv2-common' +import { SharedGraphicLLayer } from 'tv2-constants' +import { GfxSchemaGenerator } from '../cues/gfx-schema-generator' +import { DveLoopGenerator } from '../helpers/graphics/caspar/dve-loop-generator' +import { MockContextBuilder } from './mock-context-builder' + +// tslint:disable:no-object-literal-type-assertion +describe('GfxSchemaGenerator', () => { + describe('createTimelineObjectsFromGfxDefaults', () => { + it('has no schema configured - throws error', () => { + const context = new MockContextBuilder() + .setGfxDefaults({ + DefaultSchema: { label: 'someSchema' } + } as TableConfigItemGfxDefaults) + .build() + + const core = mock() + when(context.core).thenReturn(instance(core)) + + const testee = createTestee() + + expect(() => { + testee.createTimelineObjectsFromGfxDefaults(instance(context)) + }).toThrow() + }) + + function createTestee(dveLoopGenerator?: DveLoopGenerator) { + if (!dveLoopGenerator) { + dveLoopGenerator = mock(DveLoopGenerator) + when(dveLoopGenerator.createCasparCgDveLoopsFromCue(anything(), anything(), anyNumber())).thenReturn([]) + } + return new GfxSchemaGenerator(instance(dveLoopGenerator)) + } + + it('has schema configured, but no match from default schema - throws error', () => { + const context = new MockContextBuilder() + .setGfxDefaults({ + DefaultSchema: { label: 'someSchema' } + } as TableConfigItemGfxDefaults) + .setGfxSchemaTemplates([ + { + GfxSchemaTemplatesName: 'someOtherSchema' + } + ] as TableConfigGfxSchema[]) + .build() + + const core = mock() + when(context.core).thenReturn(instance(core)) + + const testee = createTestee() + expect(() => { + testee.createTimelineObjectsFromGfxDefaults(instance(context)) + }).toThrow() + }) + + it('has correctly configured schema - does not throw', () => { + const schemaName = 'someSchema' + const context = new MockContextBuilder() + .setGfxDefaults({ + DefaultSchema: { label: schemaName } + } as TableConfigItemGfxDefaults) + .setGfxSchemaTemplates([ + { + GfxSchemaTemplatesName: schemaName, + CasparCgDesignValues: '[{}]' + } + ] as TableConfigGfxSchema[]) + .build() + + const core = mock() + when(context.core).thenReturn(instance(core)) + + const testee = createTestee() + const result = testee.createTimelineObjectsFromGfxDefaults(instance(context)) + expect(result).toBeTruthy() + }) + + describe('graphicsType is "VIZ"', () => { + it('creates Viz timelineObject', () => { + const schemaName = 'someSchema' + const vizTemplateName = 'someVizTemplate' + const ovlShowName = 'someOvlName' + const context = new MockContextBuilder() + .setGraphicsType('VIZ') + .setGfxDefaults({ + DefaultSchema: { label: schemaName } + } as TableConfigItemGfxDefaults) + .setGfxSchemaTemplates([ + { + GfxSchemaTemplatesName: schemaName, + CasparCgDesignValues: '[{}]', + VizTemplate: vizTemplateName + } + ] as TableConfigGfxSchema[]) + .setSelectedGfxSetup({ + OvlShowName: ovlShowName + } as TableConfigGfxSetup) + .build() + const testee = createTestee() + + const result = testee.createTimelineObjectsFromGfxDefaults(instance(context)) + const schemaTimelineObject = result.find( + (timelineObject) => timelineObject.layer === SharedGraphicLLayer.GraphicLLayerSchema + ) + expect(schemaTimelineObject).toBeTruthy() + const vizSchema = schemaTimelineObject as TSR.TimelineObjVIZMSEElementInternal + expect(vizSchema.classes).toContain(vizTemplateName) + expect(vizSchema.content.deviceType).toBe(TSR.DeviceType.VIZMSE) + expect(vizSchema.content.type).toBe(TSR.TimelineContentTypeVizMSE.ELEMENT_INTERNAL) + expect(vizSchema.content.templateName).toBe(vizTemplateName) + expect(vizSchema.content.showName).toBe(ovlShowName) + }) + + it('creates casparCg dve loop timelineObjects', () => { + const schemaName = 'someSchema' + const context = new MockContextBuilder() + .setGraphicsType('VIZ') + .setGfxDefaults({ + DefaultSchema: { label: schemaName } + } as TableConfigItemGfxDefaults) + .setGfxSchemaTemplates([ + { + GfxSchemaTemplatesName: schemaName, + CasparCgDesignValues: '[{}]' + } + ] as TableConfigGfxSchema[]) + .build() + + const casparCgDveLoopTimelineObjects: TSR.TimelineObjCCGMedia[] = [ + { + id: 'id1' + }, + { + id: 'id2' + } + ] as TSR.TimelineObjCCGMedia[] + + const dveLoopGenerator = mock(DveLoopGenerator) + when(dveLoopGenerator.createCasparCgDveLoopsFromCue(anything(), anything(), anyNumber())).thenReturn( + casparCgDveLoopTimelineObjects + ) + + const testee = createTestee(dveLoopGenerator) + const result = testee.createTimelineObjectsFromGfxDefaults(instance(context)) + + verify(dveLoopGenerator.createCasparCgDveLoopsFromCue(anything(), anything(), anyNumber())).called() + expect(result.includes(casparCgDveLoopTimelineObjects[0])).toBeTruthy() + expect(result.includes(casparCgDveLoopTimelineObjects[1])).toBeTruthy() + }) + }) + + describe('graphicsType is "HTML"', () => { + it('creates CasparCG timelineObject', () => { + const schemaName = 'someSchema' + const vizTemplateName = 'someVizTemplate' + const casparCgDesignValues: CasparCgGfxDesignValues[] = [ + { + name: 'designName', + backgroundLoop: 'someBackgroundLoop', + cssRules: ['rule1', 'rule2', 'rule3'] + } + ] + const context = new MockContextBuilder() + .setGraphicsType('HTML') + .setGfxDefaults({ + DefaultSchema: { label: schemaName } + } as TableConfigItemGfxDefaults) + .setGfxSchemaTemplates([ + { + GfxSchemaTemplatesName: schemaName, + CasparCgDesignValues: JSON.stringify(casparCgDesignValues), + VizTemplate: vizTemplateName + } + ] as TableConfigGfxSchema[]) + .build() + const testee = createTestee() + + const result = testee.createTimelineObjectsFromGfxDefaults(instance(context)) + const schemaTimelineObject = result.find( + (timelineObject) => timelineObject.layer === SharedGraphicLLayer.GraphicLLayerSchema + ) + expect(schemaTimelineObject).toBeTruthy() + const casparCgSchema = schemaTimelineObject as TSR.TimelineObjCCGTemplate + expect(casparCgSchema.classes).toContain(vizTemplateName) + expect(casparCgSchema.content.deviceType).toBe(TSR.DeviceType.CASPARCG) + expect(casparCgSchema.content.type).toBe(TSR.TimelineContentTypeCasparCg.TEMPLATE) + expect(casparCgSchema.content.templateType).toBe('html') + expect(casparCgSchema.content.data.designs).toStrictEqual(casparCgDesignValues) + }) + + it('creates casparCg dve loop timelineObjects', () => { + const schemaName = 'someSchema' + const context = new MockContextBuilder() + .setGraphicsType('HTML') + .setGfxDefaults({ + DefaultSchema: { label: schemaName } + } as TableConfigItemGfxDefaults) + .setGfxSchemaTemplates([ + { + GfxSchemaTemplatesName: schemaName, + CasparCgDesignValues: '[{}]' + } + ] as TableConfigGfxSchema[]) + .build() + + const casparCgDveLoopTimelineObjects: TSR.TimelineObjCCGMedia[] = [ + { + id: 'id1' + }, + { + id: 'id2' + } + ] as TSR.TimelineObjCCGMedia[] + + const dveLoopGenerator = mock(DveLoopGenerator) + when(dveLoopGenerator.createCasparCgDveLoopsFromCue(anything(), anything(), anyNumber())).thenReturn( + casparCgDveLoopTimelineObjects + ) + + const testee = createTestee(dveLoopGenerator) + const result = testee.createTimelineObjectsFromGfxDefaults(instance(context)) + + verify(dveLoopGenerator.createCasparCgDveLoopsFromCue(anything(), anything(), anyNumber())).called() + expect(result.includes(casparCgDveLoopTimelineObjects[0])).toBeTruthy() + expect(result.includes(casparCgDveLoopTimelineObjects[1])).toBeTruthy() + }) + }) + }) +}) diff --git a/src/tv2-common/__tests__/mock-context-builder.ts b/src/tv2-common/__tests__/mock-context-builder.ts new file mode 100644 index 00000000..997ce3bc --- /dev/null +++ b/src/tv2-common/__tests__/mock-context-builder.ts @@ -0,0 +1,62 @@ +import { mock, when } from 'ts-mockito' +import { + ShowStyleContext, + TableConfigGfxSchema, + TableConfigGfxSetup, + TableConfigItemGfxDefaults, + TV2ShowStyleConfig +} from 'tv2-common' + +// tslint:disable:no-object-literal-type-assertion +/** + * A builder class to create mocks for ShowStyleContext. + * To make this useful to as many as possible all defaults should be empty or as close to empty as possible. + * Feel free to add more setters methods as needed. + */ +export class MockContextBuilder { + private gfxDefaults: TableConfigItemGfxDefaults[] = [] + private gfxSchemaTemplates: TableConfigGfxSchema[] = [] + private selectedGfxSetup: TableConfigGfxSetup = { + _id: '', + Name: '', + HtmlPackageFolder: '' + } + private graphicsType: 'HTML' | 'VIZ' = 'HTML' + + public build(): ShowStyleContext { + const context = mock() + + when(context.config).thenReturn({ + showStyle: { + GfxDefaults: this.gfxDefaults, + GfxSchemaTemplates: this.gfxSchemaTemplates + }, + studio: { + GraphicsType: this.graphicsType + }, + selectedGfxSetup: this.selectedGfxSetup + } as TV2ShowStyleConfig) + + return context + } + + public setGfxDefaults(gfxDefault: TableConfigItemGfxDefaults): MockContextBuilder { + this.gfxDefaults = [gfxDefault] + return this + } + + public setGfxSchemaTemplates(gfxSchemaTemplates: TableConfigGfxSchema[]): MockContextBuilder { + this.gfxSchemaTemplates = gfxSchemaTemplates + return this + } + + public setSelectedGfxSetup(selectedGfxSetup: TableConfigGfxSetup): MockContextBuilder { + this.selectedGfxSetup = selectedGfxSetup + return this + } + + public setGraphicsType(graphicsType: 'HTML' | 'VIZ'): MockContextBuilder { + this.graphicsType = graphicsType + return this + } +} diff --git a/src/tv2-common/blueprintConfig.ts b/src/tv2-common/blueprintConfig.ts index ac3d433c..471e0635 100644 --- a/src/tv2-common/blueprintConfig.ts +++ b/src/tv2-common/blueprintConfig.ts @@ -45,8 +45,8 @@ export interface TableConfigItemGfxShowMapping { export interface TableConfigItemGfxDefaults { DefaultSetupName: { value: string; label: string } - DefaultSchema: string - DefaultDesign: string + DefaultSchema: { value: string; label: string } + DefaultDesign: { value: string; label: string } } export interface TableConfigItemAdLibTransitions { @@ -70,13 +70,7 @@ export interface TableConfigGfxSetup { export interface CasparCgGfxDesignValues { name: string - mainColor: string - bundColor: string - trompetColor: string - mainTextColor: string - trompetTextColor: string - panelColor: string - panelTextColor: string + cssRules: string[] backgroundLoop: string } diff --git a/src/tv2-common/cues/evaluate-cue-graphic-schema.ts b/src/tv2-common/cues/evaluate-cue-graphic-schema.ts deleted file mode 100644 index 29945a5f..00000000 --- a/src/tv2-common/cues/evaluate-cue-graphic-schema.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { GraphicsContent, IBlueprintPiece, PieceLifespan, TSR, WithTimeline } from 'blueprints-integration' -import { - calculateTime, - CueDefinitionGraphicSchema, - getHtmlTemplateName, - literal, - ShowStyleContext, - TV2ShowStyleConfig -} from 'tv2-common' -import { SharedGraphicLLayer, SharedOutputLayer, SharedSourceLayer } from 'tv2-constants' - -export function EvaluateCueGraphicSchema( - context: ShowStyleContext, - pieces: IBlueprintPiece[], - partId: string, - parsedCue: CueDefinitionGraphicSchema -) { - if (!parsedCue.schema) { - context.core.notifyUserWarning(`No valid Schema found for ${parsedCue.schema}`) - return - } - - const start = (parsedCue.start ? calculateTime(parsedCue.start) : 0) ?? 0 - pieces.push({ - externalId: partId, - name: parsedCue.schema, - enable: { - start - }, - outputLayerId: SharedOutputLayer.SEC, - sourceLayerId: SharedSourceLayer.PgmSchema, - lifespan: PieceLifespan.OutOnShowStyleEnd, - content: literal>({ - fileName: parsedCue.schema, - path: parsedCue.schema, - ignoreMediaObjectStatus: true, - timelineObjects: createTimelineObjects(context, parsedCue) - }) - }) -} - -function createTimelineObjects(context: ShowStyleContext, cue: CueDefinitionGraphicSchema): TSR.TSRTimelineObjBase[] { - switch (context.config.studio.GraphicsType) { - case 'VIZ': { - return createVizTimelineObjects(context.config, cue) - } - case 'HTML': { - return createCasparCgTimelineObjects(context, cue) - } - default: { - return [] - } - } -} - -function createVizTimelineObjects( - config: TV2ShowStyleConfig, - cue: CueDefinitionGraphicSchema -): TSR.TimelineObjVIZMSEElementInternal[] { - return [ - literal({ - id: '', - enable: { start: 0 }, - priority: 100, - layer: SharedGraphicLLayer.GraphicLLayerSchema, - content: { - deviceType: TSR.DeviceType.VIZMSE, - type: TSR.TimelineContentTypeVizMSE.ELEMENT_INTERNAL, - templateName: cue.schema, - templateData: [], - showName: config.selectedGfxSetup.OvlShowName ?? '' - } - }) - ] -} - -function createCasparCgTimelineObjects( - context: ShowStyleContext, - cue: CueDefinitionGraphicSchema -): TSR.TimelineObjCasparCGBase[] { - return [ - literal({ - id: '', - enable: { start: 0 }, - priority: 100, - layer: SharedGraphicLLayer.GraphicLLayerSchema, - content: { - deviceType: TSR.DeviceType.CASPARCG, - type: TSR.TimelineContentTypeCasparCg.TEMPLATE, - templateType: 'html', - name: getHtmlTemplateName(context.config), - data: createCasparCgHtmlSchemaData(context, cue), - useStopCommand: false - } - }) - ] -} - -function createCasparCgHtmlSchemaData(context: ShowStyleContext, cue: CueDefinitionGraphicSchema): any { - if (!cue.CasparCgDesignValues || cue.CasparCgDesignValues.length === 0) { - context.core.notifyUserError( - `Unable to find Schema Design combination for "${cue.schema}". Design values will not be sent to CasparCG!` - ) - return {} - } - return { - designs: cue.CasparCgDesignValues, - partialUpdate: true - } -} diff --git a/src/tv2-common/cues/gfx-schema-generator-facade.ts b/src/tv2-common/cues/gfx-schema-generator-facade.ts new file mode 100644 index 00000000..bb9ea9e7 --- /dev/null +++ b/src/tv2-common/cues/gfx-schema-generator-facade.ts @@ -0,0 +1,8 @@ +import { DveLoopGenerator } from '../helpers/graphics/caspar/dve-loop-generator' +import { GfxSchemaGenerator } from './gfx-schema-generator' + +export abstract class GfxSchemaGeneratorFacade { + public static create(): GfxSchemaGenerator { + return new GfxSchemaGenerator(new DveLoopGenerator()) + } +} diff --git a/src/tv2-common/cues/gfx-schema-generator.ts b/src/tv2-common/cues/gfx-schema-generator.ts new file mode 100644 index 00000000..48c013c1 --- /dev/null +++ b/src/tv2-common/cues/gfx-schema-generator.ts @@ -0,0 +1,145 @@ +import { GraphicsContent, IBlueprintPiece, PieceLifespan, TSR, WithTimeline } from 'blueprints-integration' +import { + calculateTime, + CueDefinitionGfxSchema, + getHtmlTemplateName, + literal, + ShowStyleContext, + TableConfigGfxSchema, + TV2ShowStyleConfig +} from 'tv2-common' +import { CueType, SharedGraphicLLayer, SharedOutputLayer, SharedSourceLayer } from 'tv2-constants' +import { DveLoopGenerator } from '../helpers/graphics/caspar/dve-loop-generator' + +export class GfxSchemaGenerator { + constructor(private dveLoopGenerator: DveLoopGenerator) {} + + public createTimelineObjectsFromGfxDefaults(context: ShowStyleContext): TSR.TSRTimelineObjBase[] { + const schemaName = context.config.showStyle.GfxDefaults[0].DefaultSchema.label + const schema: TableConfigGfxSchema | undefined = context.config.showStyle.GfxSchemaTemplates.find( + (s) => s.GfxSchemaTemplatesName === schemaName + ) + if (!schema || !schema.CasparCgDesignValues) { + context.core.notifyUserError( + `Unable to create baseline DVE loops for CasparCG. Check GfxDefaults Schema is configured.` + ) + throw new Error(`Incorrectly configured GfxDefaults.schema`) + } + + const cue: CueDefinitionGfxSchema = { + type: CueType.GraphicSchema, + schema: schema.VizTemplate, + CasparCgDesignValues: schema.CasparCgDesignValues ? JSON.parse(schema.CasparCgDesignValues) : [], + iNewsCommand: '' + } + return this.createTimelineObjects(context, cue, 10) + } + + public createBlueprintPieceFromGfxSchemaCue( + context: ShowStyleContext, + pieces: IBlueprintPiece[], + partId: string, + cue: CueDefinitionGfxSchema + ): void { + if (!cue.schema) { + context.core.notifyUserWarning(`No valid Schema found for ${cue.schema}`) + return + } + + const start = (cue.start ? calculateTime(cue.start) : 0) ?? 0 + pieces.push({ + externalId: partId, + name: cue.schema, + enable: { + start + }, + outputLayerId: SharedOutputLayer.SEC, + sourceLayerId: SharedSourceLayer.PgmSchema, + lifespan: PieceLifespan.OutOnShowStyleEnd, + content: literal>({ + fileName: cue.schema, + path: cue.schema, + ignoreMediaObjectStatus: true, + timelineObjects: this.createTimelineObjects(context, cue) + }) + }) + } + + private createTimelineObjects( + context: ShowStyleContext, + cue: CueDefinitionGfxSchema, + priority?: number + ): TSR.TSRTimelineObjBase[] { + switch (context.config.studio.GraphicsType) { + case 'VIZ': { + return [ + this.createVizSchemaTimelineObject(context.config, cue), + ...this.dveLoopGenerator.createCasparCgDveLoopsFromCue(context, cue, priority) + ] + } + case 'HTML': { + return [ + this.createCasparCgSchemaTimelineObject(context, cue), + ...this.dveLoopGenerator.createCasparCgDveLoopsFromCue(context, cue, priority) + ] + } + default: { + return [] + } + } + } + + private createVizSchemaTimelineObject( + config: TV2ShowStyleConfig, + cue: CueDefinitionGfxSchema + ): TSR.TimelineObjVIZMSEElementInternal { + return literal({ + id: '', + enable: { start: 0 }, + priority: 100, + classes: [cue.schema], + layer: SharedGraphicLLayer.GraphicLLayerSchema, + content: { + deviceType: TSR.DeviceType.VIZMSE, + type: TSR.TimelineContentTypeVizMSE.ELEMENT_INTERNAL, + templateName: cue.schema, + templateData: [], + showName: config.selectedGfxSetup.OvlShowName ?? '' + } + }) + } + + private createCasparCgSchemaTimelineObject( + context: ShowStyleContext, + cue: CueDefinitionGfxSchema + ): TSR.TimelineObjCasparCGBase { + return literal({ + id: '', + enable: { start: 0 }, + priority: 100, + classes: [cue.schema], + layer: SharedGraphicLLayer.GraphicLLayerSchema, + content: { + deviceType: TSR.DeviceType.CASPARCG, + type: TSR.TimelineContentTypeCasparCg.TEMPLATE, + templateType: 'html', + name: getHtmlTemplateName(context.config), + data: this.createCasparCgHtmlSchemaData(context, cue), + useStopCommand: false + } + }) + } + + private createCasparCgHtmlSchemaData(context: ShowStyleContext, cue: CueDefinitionGfxSchema): any { + if (!cue.CasparCgDesignValues || cue.CasparCgDesignValues.length === 0) { + context.core.notifyUserError( + `Unable to find Schema Design combination for "${cue.schema}". Design values will not be sent to CasparCG!` + ) + return {} + } + return { + designs: cue.CasparCgDesignValues, + partialUpdate: true + } + } +} diff --git a/src/tv2-common/evaluateCues.ts b/src/tv2-common/evaluateCues.ts index ec0ec2ef..ebd6257d 100644 --- a/src/tv2-common/evaluateCues.ts +++ b/src/tv2-common/evaluateCues.ts @@ -13,7 +13,7 @@ import { CueDefinitionClearGrafiks, CueDefinitionDVE, CueDefinitionEkstern, - CueDefinitionGraphicSchema, + CueDefinitionGfxSchema, CueDefinitionJingle, CueDefinitionLYD, CueDefinitionRobotCamera, @@ -85,7 +85,7 @@ export interface EvaluateCuesShowstyleOptions { context: ShowStyleContext, pieces: IBlueprintPiece[], partId: string, - parsedCue: CueDefinitionGraphicSchema + parsedCue: CueDefinitionGfxSchema ) => void EvaluateCueRouting?: ( context: ShowStyleContext, diff --git a/src/tv2-common/helpers/graphics/caspar/__tests__/dve-loop-generator.spec.ts b/src/tv2-common/helpers/graphics/caspar/__tests__/dve-loop-generator.spec.ts new file mode 100644 index 00000000..9bdb6b92 --- /dev/null +++ b/src/tv2-common/helpers/graphics/caspar/__tests__/dve-loop-generator.spec.ts @@ -0,0 +1,189 @@ +import { Timeline, TSR } from '@sofie-automation/blueprints-integration' +import { mock } from 'ts-mockito' +import { CueDefinitionGfxSchema, ShowStyleContext } from 'tv2-common' +import { CueType, SharedCasparLLayer } from 'tv2-constants' +import { DveLoopGenerator } from '../dve-loop-generator' + +describe('DveLoopGenerator', () => { + describe('createCasparCgDveLoopsFromCue', () => { + it('does not have any CasparCgDesignValues configured - throws error', () => { + const context = mock() + const cue: CueDefinitionGfxSchema = { + type: CueType.GraphicSchema, + schema: 'randomSchema', + iNewsCommand: '' + } + + const testee: DveLoopGenerator = new DveLoopGenerator() + expect(() => { + testee.createCasparCgDveLoopsFromCue(context, cue) + }).toThrow() + }) + + it('has an empty CasparCgDesignValues array configured - throws error', () => { + const context = mock() + const cue: CueDefinitionGfxSchema = { + type: CueType.GraphicSchema, + schema: 'randomSchema', + iNewsCommand: '', + CasparCgDesignValues: [] + } + + const testee: DveLoopGenerator = new DveLoopGenerator() + expect(() => { + testee.createCasparCgDveLoopsFromCue(context, cue) + }).toThrow() + }) + + it('has one CasparCgDesignValue configured - returns one TimelineObject', () => { + const context = mock() + const cue: CueDefinitionGfxSchema = { + type: CueType.GraphicSchema, + schema: 'randomSchema', + iNewsCommand: '', + CasparCgDesignValues: [{ name: 'someName', backgroundLoop: 'someLoop', cssRules: ['someRule'] }] + } + + const testee: DveLoopGenerator = new DveLoopGenerator() + const result = testee.createCasparCgDveLoopsFromCue(context, cue) + + expect(result).toHaveLength(1) + }) + + it('has 5 CasparCgDesignValues configured - returns five TimelineObjects', () => { + const context = mock() + + const cue: CueDefinitionGfxSchema = { + type: CueType.GraphicSchema, + schema: 'randomSchema', + iNewsCommand: '', + CasparCgDesignValues: new Array(5).map(() => ({ + name: 'someName', + backgroundLoop: 'someLoop', + cssRules: ['someRule'] + })) + } + + const testee: DveLoopGenerator = new DveLoopGenerator() + const result = testee.createCasparCgDveLoopsFromCue(context, cue) + + expect(result).toHaveLength(5) + }) + + it('sets the enable to be while .schema and .design name', () => { + const context = mock() + const schemaName = 'someSchemaName' + const designName = 'someDesignName' + + const cue: CueDefinitionGfxSchema = { + type: CueType.GraphicSchema, + schema: schemaName, + iNewsCommand: '', + CasparCgDesignValues: [ + { + name: designName, + backgroundLoop: 'someLoop', + cssRules: [] + } + ] + } + + const testee: DveLoopGenerator = new DveLoopGenerator() + const result = testee.createCasparCgDveLoopsFromCue(context, cue) + const timelineEnable: Timeline.TimelineEnable = result[0].enable as Timeline.TimelineEnable + const references: string[] = (timelineEnable.while as string).split('&').map((s) => s.trim()) + + expect(references).toHaveLength(2) + expect(references).toContain(`.${schemaName}`) + expect(references).toContain(`.${designName}`) + expect(references[0]).not.toEqual(references[1]) + }) + + it('creates the timelineObject on CasparCgDveLoop layer', () => { + const context = mock() + const cue = createCueDefinitionGfxSchema() + + const testee: DveLoopGenerator = new DveLoopGenerator() + const result = testee.createCasparCgDveLoopsFromCue(context, cue) + + expect(result[0].layer).toBe(SharedCasparLLayer.CasparCGDVELoop) + }) + + function createCueDefinitionGfxSchema(): CueDefinitionGfxSchema { + const cue: CueDefinitionGfxSchema = { + type: CueType.GraphicSchema, + schema: 'someSchemaName', + iNewsCommand: '', + CasparCgDesignValues: [ + { + name: 'someDesignName', + backgroundLoop: 'someLoop', + cssRules: [] + } + ] + } + return cue + } + + it('gets no priority - priority is set to 100', () => { + const context = mock() + const cue = createCueDefinitionGfxSchema() + + const testee: DveLoopGenerator = new DveLoopGenerator() + const result: TSR.TimelineObjCCGMedia[] = testee.createCasparCgDveLoopsFromCue(context, cue) + + expect(result[0].priority).toBe(100) + }) + + it('gets a priority - priority is set to the parsed priority', () => { + const context = mock() + const cue = createCueDefinitionGfxSchema() + const priority = 50 + + const testee: DveLoopGenerator = new DveLoopGenerator() + const result: TSR.TimelineObjCCGMedia[] = testee.createCasparCgDveLoopsFromCue(context, cue, priority) + + expect(result[0].priority).toBe(priority) + }) + + it('creates the timelineObject with DeviceType CasparCG', () => { + const context = mock() + const cue = createCueDefinitionGfxSchema() + + const testee: DveLoopGenerator = new DveLoopGenerator() + const result: TSR.TimelineObjCCGMedia[] = testee.createCasparCgDveLoopsFromCue(context, cue) + + expect(result[0].content.deviceType).toBe(TSR.DeviceType.CASPARCG) + }) + + it('creates the timelineObject with CasparCG media type', () => { + const context = mock() + const cue = createCueDefinitionGfxSchema() + + const testee: DveLoopGenerator = new DveLoopGenerator() + const result: TSR.TimelineObjCCGMedia[] = testee.createCasparCgDveLoopsFromCue(context, cue) + + expect(result[0].content.type).toBe(TSR.TimelineContentTypeCasparCg.MEDIA) + }) + + it('sets the background loop as file prefixed with "dve/"', () => { + const context = mock() + const cue = createCueDefinitionGfxSchema() + + const testee: DveLoopGenerator = new DveLoopGenerator() + const result: TSR.TimelineObjCCGMedia[] = testee.createCasparCgDveLoopsFromCue(context, cue) + + expect(result[0].content.file).toBe(`dve/${cue.CasparCgDesignValues![0].backgroundLoop}`) + }) + + it('sets loop to be true', () => { + const context = mock() + const cue = createCueDefinitionGfxSchema() + + const testee: DveLoopGenerator = new DveLoopGenerator() + const result: TSR.TimelineObjCCGMedia[] = testee.createCasparCgDveLoopsFromCue(context, cue) + + expect(result[0].content.loop).toBeTruthy() + }) + }) +}) diff --git a/src/tv2-common/helpers/graphics/caspar/dve-loop-generator.ts b/src/tv2-common/helpers/graphics/caspar/dve-loop-generator.ts new file mode 100644 index 00000000..0c5ba532 --- /dev/null +++ b/src/tv2-common/helpers/graphics/caspar/dve-loop-generator.ts @@ -0,0 +1,41 @@ +import { TSR } from '@sofie-automation/blueprints-integration' +import { CasparCgGfxDesignValues, CueDefinitionGfxSchema, ShowStyleContext } from 'tv2-common' +import { SharedCasparLLayer } from 'tv2-constants' + +export class DveLoopGenerator { + public createCasparCgDveLoopsFromCue( + context: ShowStyleContext, + cue: CueDefinitionGfxSchema, + priority?: number + ): TSR.TimelineObjCCGMedia[] { + if (!cue.CasparCgDesignValues || !cue.CasparCgDesignValues.length || cue.CasparCgDesignValues.length === 0) { + const errorMessage = `No CasparCgDesignValues configured for Schema {${cue.schema}}` + context.core.notifyUserError(errorMessage) + throw new Error(errorMessage) + } + return cue.CasparCgDesignValues.map((designValues) => + this.createDveLoopTimelineObject(cue.schema, designValues, priority) + ) + } + + private createDveLoopTimelineObject( + schemaName: string, + design: CasparCgGfxDesignValues, + priority?: number + ): TSR.TimelineObjCCGMedia { + return { + id: '', + enable: { + while: `.${schemaName} & .${design.name}` + }, + priority: priority ?? 100, + layer: SharedCasparLLayer.CasparCGDVELoop, + content: { + deviceType: TSR.DeviceType.CASPARCG, + type: TSR.TimelineContentTypeCasparCg.MEDIA, + file: `dve/${design.backgroundLoop}`, + loop: true + } + } + } +} diff --git a/src/tv2-common/helpers/graphics/caspar/util.ts b/src/tv2-common/helpers/graphics/caspar/util.ts index 8ca061db..17604168 100644 --- a/src/tv2-common/helpers/graphics/caspar/util.ts +++ b/src/tv2-common/helpers/graphics/caspar/util.ts @@ -3,6 +3,7 @@ import { getTimelineLayerForGraphic, joinAssetToFolder, layerToHTMLGraphicSlot, + NON_BASELINE_DESIGN_ID, Slots, TV2ShowStyleConfig } from 'tv2-common' @@ -54,7 +55,7 @@ export function getHtmlTemplateContent( } } -export function getHtmlGraphicBaseline(config: TV2ShowStyleConfig) { +export function getHtmlGraphicBaseline(config: TV2ShowStyleConfig): TSR.TSRTimelineObj[] { const templateName = getHtmlTemplateName(config) const partiallyUpdatableLayerMappings = [ SharedGraphicLLayer.GraphicLLayerOverlayIdent, @@ -67,7 +68,7 @@ export function getHtmlGraphicBaseline(config: TV2ShowStyleConfig) { return [ ...getSlotBaselineTimelineObjects(templateName, partiallyUpdatableLayerMappings), getCompoundSlotBaselineTimelineObject(templateName, partiallyUpdatableLayerMappings), - getDesignBaselineTimelineObject(templateName), + getDesignBaselineTimelineObject(config, templateName), getFullPilotBaselineTimelineObject(templateName) ] } @@ -140,13 +141,15 @@ function getCompoundSlotBaselineTimelineObject( } } -function getDesignBaselineTimelineObject(templateName: string): TSR.TimelineObjCCGTemplate { +function getDesignBaselineTimelineObject(config: TV2ShowStyleConfig, templateName: string): TSR.TimelineObjCCGTemplate { + const design: string = config.showStyle.GfxDefaults[0].DefaultDesign.label return { id: '', enable: { - while: '1' + while: `!.${NON_BASELINE_DESIGN_ID}` }, - priority: 0, + priority: 10, + classes: [`${design}`], layer: SharedGraphicLLayer.GraphicLLayerDesign, content: { deviceType: TSR.DeviceType.CASPARCG, @@ -155,7 +158,7 @@ function getDesignBaselineTimelineObject(templateName: string): TSR.TimelineObjC name: templateName, data: { display: 'program', - design: '', + design, partialUpdate: true }, useStopCommand: false diff --git a/src/tv2-common/helpers/graphics/design/index.ts b/src/tv2-common/helpers/graphics/design/index.ts index 93c6d5db..a1830a9b 100644 --- a/src/tv2-common/helpers/graphics/design/index.ts +++ b/src/tv2-common/helpers/graphics/design/index.ts @@ -12,6 +12,7 @@ import { CueDefinitionGraphicDesign, getHtmlTemplateName, literal, + NON_BASELINE_DESIGN_ID, ShowStyleContext, TV2ShowStyleConfig } from 'tv2-common' @@ -78,6 +79,7 @@ function designTimeline(config: TV2ShowStyleConfig, parsedCue: CueDefinitionGrap start: 0 }, priority: 1, + classes: [`${parsedCue.design}`, NON_BASELINE_DESIGN_ID], layer: SharedGraphicLLayer.GraphicLLayerDesign, content: { deviceType: TSR.DeviceType.CASPARCG, @@ -94,22 +96,36 @@ function designTimeline(config: TV2ShowStyleConfig, parsedCue: CueDefinitionGrap }) ] case 'VIZ': - return [ - literal({ - id: '', - enable: { start: 0 }, - priority: 100, - layer: SharedGraphicLLayer.GraphicLLayerDesign, - content: { - deviceType: TSR.DeviceType.VIZMSE, - type: TSR.TimelineContentTypeVizMSE.ELEMENT_INTERNAL, - templateName: parsedCue.design, - templateData: [], - showName: config.selectedGfxSetup.OvlShowName ?? '' // @todo: improve types at the junction of HTML and Viz - } - }) - ] + return [getNonBaselineVizDesignTimelineObject(config, parsedCue.design)] default: return [] } } + +function getNonBaselineVizDesignTimelineObject(config: TV2ShowStyleConfig, design: string) { + const vizDesignTimelineObject = getVizDesignTimelineObject(config, design) + vizDesignTimelineObject.classes!.push(NON_BASELINE_DESIGN_ID) + return vizDesignTimelineObject +} + +function getVizDesignTimelineObject(config: TV2ShowStyleConfig, design: string) { + return literal({ + id: '', + enable: { start: 0 }, + priority: 100, + classes: [`${design}`], + layer: SharedGraphicLLayer.GraphicLLayerDesign, + content: { + deviceType: TSR.DeviceType.VIZMSE, + type: TSR.TimelineContentTypeVizMSE.ELEMENT_INTERNAL, + templateName: design, + templateData: [], + showName: config.selectedGfxSetup.OvlShowName ?? '' // @todo: improve types at the junction of HTML and Viz + } + }) +} + +export function getVizBaselineDesignTimelineObject(config: TV2ShowStyleConfig) { + const design = config.showStyle.GfxDefaults[0].DefaultDesign.label + return getVizDesignTimelineObject(config, design) +} diff --git a/src/tv2-common/helpers/graphics/util.ts b/src/tv2-common/helpers/graphics/util.ts index 1c661ff7..e5e4b4d5 100644 --- a/src/tv2-common/helpers/graphics/util.ts +++ b/src/tv2-common/helpers/graphics/util.ts @@ -1,5 +1,7 @@ import { IBlueprintPart, TSR } from 'blueprints-integration' -import { getHtmlGraphicBaseline, TV2ShowStyleConfig } from 'tv2-common' +import { getHtmlGraphicBaseline, getVizBaselineDesignTimelineObject, TV2ShowStyleConfig } from 'tv2-common' + +export const NON_BASELINE_DESIGN_ID = 'NON_BASELINE_DESIGN_ID' export function applyFullGraphicPropertiesToPart(config: TV2ShowStyleConfig, part: IBlueprintPart) { const keepAliveDuration = @@ -19,7 +21,7 @@ export function applyFullGraphicPropertiesToPart(config: TV2ShowStyleConfig, par export function getGraphicBaseline(config: TV2ShowStyleConfig): TSR.TSRTimelineObj[] { if (config.studio.GraphicsType === 'VIZ') { - return [] + return [getVizBaselineDesignTimelineObject(config)] } else { return getHtmlGraphicBaseline(config) } diff --git a/src/tv2-common/inewsConversion/converters/ParseBody.ts b/src/tv2-common/inewsConversion/converters/ParseBody.ts index 17d5cc0e..8e8b6aa6 100644 --- a/src/tv2-common/inewsConversion/converters/ParseBody.ts +++ b/src/tv2-common/inewsConversion/converters/ParseBody.ts @@ -724,7 +724,7 @@ export function stripRedundantCuesWhenFieldCueIsPresent(partDefinitions: PartDef } function stripRedundantCuesForDesign(partDefinitions: PartDefinition[]): PartDefinition[] { - return stripRedundantCues(partDefinitions, [CueType.GraphicDesign, CueType.BackgroundLoop]) + return stripRedundantCues(partDefinitions, [CueType.GraphicDesign]) } function stripRedundantCuesForSchema(partDefinitions: PartDefinition[]): PartDefinition[] { diff --git a/src/tv2-common/inewsConversion/converters/ParseCue.ts b/src/tv2-common/inewsConversion/converters/ParseCue.ts index e78a042e..d39d6103 100644 --- a/src/tv2-common/inewsConversion/converters/ParseCue.ts +++ b/src/tv2-common/inewsConversion/converters/ParseCue.ts @@ -131,7 +131,7 @@ export interface CueDefinitionFromField { isFromField?: boolean } -export interface CueDefinitionGraphicSchema extends CueDefinitionBase, CueDefinitionFromField { +export interface CueDefinitionGfxSchema extends CueDefinitionBase, CueDefinitionFromField { type: CueType.GraphicSchema schema: string CasparCgDesignValues?: CasparCgGfxDesignValues[] @@ -193,7 +193,7 @@ export type CueDefinition = | CueDefinitionUnpairedPilot | CueDefinitionBackgroundLoop | CueDefinitionGraphicDesign - | CueDefinitionGraphicSchema + | CueDefinitionGfxSchema | CueDefinitionGraphic | CueDefinitionRouting | CueDefinitionPgmClean @@ -282,7 +282,7 @@ function parsekg( ): | CueDefinitionGraphic | CueDefinitionGraphicDesign - | CueDefinitionGraphicSchema + | CueDefinitionGfxSchema | CueDefinitionUnpairedTarget { let kgCue: CueDefinitionGraphic = { type: CueType.Graphic, @@ -373,7 +373,7 @@ function parsekg( : undefined if (graphicsSchemaConfig) { - return literal({ + return literal({ type: CueType.GraphicSchema, schema: graphicsSchemaConfig.VizTemplate, iNewsCommand: kgCue.iNewsCommand, @@ -962,13 +962,13 @@ function findGraphicDesignConfiguration( export function createCueDefinitionGraphicSchema( schema: string, config: TV2ShowStyleConfig -): CueDefinitionGraphicSchema | undefined { +): CueDefinitionGfxSchema | undefined { const schemaConfiguration = findGraphicSchemaConfiguration(config, schema) if (!schemaConfiguration) { return undefined } - return literal({ + return literal({ type: CueType.GraphicSchema, schema: schemaConfiguration.VizTemplate, iNewsCommand: '', diff --git a/src/tv2-common/inewsConversion/converters/__tests__/body-parser.spec.ts b/src/tv2-common/inewsConversion/converters/__tests__/body-parser.spec.ts index 58fa1e13..235204c0 100644 --- a/src/tv2-common/inewsConversion/converters/__tests__/body-parser.spec.ts +++ b/src/tv2-common/inewsConversion/converters/__tests__/body-parser.spec.ts @@ -1,5 +1,4 @@ import { - CueDefinitionBackgroundLoop, CueDefinitionGraphicDesign, getTransitionProperties, INewsFields, @@ -3431,22 +3430,6 @@ describe('Body parser', () => { expect(graphicCue.design).toBe(layoutDesign) }) - it('has layout background cue and regular background cue, remove regular background cue', () => { - const layoutBackground = 'layoutBackground' - const definitions: PartDefinition[] = [ - createPartDefinition([ - createBackgroundLoopCueDefinition(layoutBackground, true), - createBackgroundLoopCueDefinition('regularBackground') - ]) - ] - - const result: PartDefinition[] = stripRedundantCuesWhenFieldCueIsPresent(definitions) - - expect(result[0].cues).toHaveLength(1) - const backgroundCue: CueDefinitionBackgroundLoop = result[0].cues[0] as CueDefinitionBackgroundLoop - expect(backgroundCue.backgroundLoop).toBe(layoutBackground) - }) - it('only have a regular background cue, does nothing', () => { const definitions: PartDefinition[] = [ createPartDefinition([createBackgroundLoopCueDefinition('regularBackground')]) diff --git a/src/tv2-constants/enums.ts b/src/tv2-constants/enums.ts index f104eca7..a435c4bc 100644 --- a/src/tv2-constants/enums.ts +++ b/src/tv2-constants/enums.ts @@ -227,6 +227,7 @@ export enum SwitcherMediaPlayerLLayer { } export enum SharedCasparLLayer { + CasparCGDVELoop = 'casparcg_dve_loop', CasparCGLYD = 'casparcg_audio_lyd', CasparPlayerClipPending = 'casparcg_player_clip_pending', CasparPlayerJingle = 'casparcg_player_jingle' diff --git a/src/tv2_afvd_showstyle/__tests__/config-manifest.spec.ts b/src/tv2_afvd_showstyle/__tests__/config-manifest.spec.ts index 507701a3..261dce54 100644 --- a/src/tv2_afvd_showstyle/__tests__/config-manifest.spec.ts +++ b/src/tv2_afvd_showstyle/__tests__/config-manifest.spec.ts @@ -20,8 +20,8 @@ const blankShowStyleConfig: GalleryShowStyleConfig = { GfxDefaults: [ { DefaultSetupName: { value: '', label: '' }, - DefaultDesign: '', - DefaultSchema: '' + DefaultDesign: { value: '', label: '' }, + DefaultSchema: { value: '', label: '' } } ] } diff --git a/src/tv2_afvd_showstyle/__tests__/configs.ts b/src/tv2_afvd_showstyle/__tests__/configs.ts index bed0a9b0..5fb54888 100644 --- a/src/tv2_afvd_showstyle/__tests__/configs.ts +++ b/src/tv2_afvd_showstyle/__tests__/configs.ts @@ -278,15 +278,15 @@ export const defaultShowStyleConfig: GalleryShowStyleConfig = { GfxSchemaTemplatesName: 'SKEMA_NEWS', VizTemplate: 'NE', INewsSkemaColumn: 'SKEMA_NEWS', - CasparCgDesignValues: '' + CasparCgDesignValues: '[{}]' } ], GfxShowMapping: [], GfxDefaults: [ { DefaultSetupName: { value: 'SomeId', label: 'SomeProfile' }, - DefaultDesign: '', - DefaultSchema: '' + DefaultDesign: { value: '', label: '' }, + DefaultSchema: { value: 'SKEMA_NEWS', label: 'SKEMA_NEWS' } } ] } diff --git a/src/tv2_afvd_showstyle/getRundown.ts b/src/tv2_afvd_showstyle/getRundown.ts index 386b8cdb..fb4b9d23 100644 --- a/src/tv2_afvd_showstyle/getRundown.ts +++ b/src/tv2_afvd_showstyle/getRundown.ts @@ -34,6 +34,8 @@ import { } from 'tv2-common' import { AdlibTags, CONSTANTS, SharedGraphicLLayer, SharedOutputLayer, SwitcherAuxLLayer } from 'tv2-constants' import * as _ from 'underscore' +import { GfxSchemaGenerator } from '../tv2-common/cues/gfx-schema-generator' +import { GfxSchemaGeneratorFacade } from '../tv2-common/cues/gfx-schema-generator-facade' import { getMixEffectBaseline } from '../tv2_afvd_studio/getBaseline' import { CasparLLayer, SisyfosLLAyer } from '../tv2_afvd_studio/layers' import { SisyfosChannel, sisyfosChannels } from '../tv2_afvd_studio/sisyfosChannels' @@ -43,6 +45,8 @@ import { GlobalAdlibActionsGenerator } from './GlobalAdlibActionsGenerator' import { GalleryBlueprintConfig } from './helpers/config' import { SourceLayer } from './layers' +const gfxSchemaGenerator: GfxSchemaGenerator = GfxSchemaGeneratorFacade.create() + export function getRundown(coreContext: IShowStyleUserContext, ingestRundown: IngestRundown): BlueprintResultRundown { const context = new ShowStyleContextImpl(coreContext, GALLERY_UNIFORM_CONFIG) return { @@ -623,23 +627,7 @@ function getBaseline(context: ShowStyleContext): Bluepri } } }), - literal({ - id: '', - enable: { while: '1' }, - priority: 0, - layer: CasparLLayer.CasparCGDVELoop, - content: { - deviceType: TSR.DeviceType.CASPARCG, - type: TSR.TimelineContentTypeCasparCg.MEDIA, - file: 'empty', - transitions: { - inTransition: { - type: TSR.Transition.CUT, - duration: CONSTANTS.DefaultClipFadeOut - } - } - } - }), + ...gfxSchemaGenerator.createTimelineObjectsFromGfxDefaults(context), literal({ id: '', enable: { while: 1 }, diff --git a/src/tv2_afvd_showstyle/helpers/pieces/evaluateCues.ts b/src/tv2_afvd_showstyle/helpers/pieces/evaluateCues.ts index 527fa793..1bd2778a 100644 --- a/src/tv2_afvd_showstyle/helpers/pieces/evaluateCues.ts +++ b/src/tv2_afvd_showstyle/helpers/pieces/evaluateCues.ts @@ -15,7 +15,7 @@ import { PartDefinition, ShowStyleContext } from 'tv2-common' -import { EvaluateCueGraphicSchema } from '../../../tv2-common/cues/evaluate-cue-graphic-schema' +import { GfxSchemaGeneratorFacade } from '../../../tv2-common/cues/gfx-schema-generator-facade' import { GalleryBlueprintConfig } from '../../../tv2_afvd_showstyle/helpers/config' import { EvaluateAdLib } from './adlib' import { EvaluateClearGrafiks } from './clearGrafiks' @@ -51,7 +51,8 @@ export async function EvaluateCues( EvaluateCueGraphic, EvaluateCueBackgroundLoop, EvaluateCueGraphicDesign: EvaluateCueDesign, - EvaluateCueGraphicSchema, + EvaluateCueGraphicSchema: (c, p, partId, parsedCue) => + GfxSchemaGeneratorFacade.create().createBlueprintPieceFromGfxSchemaCue(c, p, partId, parsedCue), EvaluateCueRouting, EvaluateCueMixMinus, EvaluateCueRobotCamera diff --git a/src/tv2_afvd_studio/layers.ts b/src/tv2_afvd_studio/layers.ts index a3d6b2c5..eb218cc2 100644 --- a/src/tv2_afvd_studio/layers.ts +++ b/src/tv2_afvd_studio/layers.ts @@ -4,7 +4,6 @@ import * as _ from 'underscore' export enum VirtualAbstractLLayer {} enum AFVDCasparLLayer { - CasparCGDVELoop = 'casparcg_dve_loop', CasparCGFullBg = 'casparcg_full_bg', CasparCGDVEKey = 'casparcg_dve_key', CasparCGDVEFrame = 'casparcg_dve_frame', diff --git a/src/tv2_offtube_showstyle/getRundown.ts b/src/tv2_offtube_showstyle/getRundown.ts index ac0ad6ae..b2335f33 100644 --- a/src/tv2_offtube_showstyle/getRundown.ts +++ b/src/tv2_offtube_showstyle/getRundown.ts @@ -57,6 +57,8 @@ import { TallyTags } from 'tv2-constants' import * as _ from 'underscore' +import { GfxSchemaGenerator } from '../tv2-common/cues/gfx-schema-generator' +import { GfxSchemaGeneratorFacade } from '../tv2-common/cues/gfx-schema-generator-facade' import { OfftubeBlueprintConfig } from '../tv2_offtube_showstyle/helpers/config' import { OfftubeCasparLLayer, OfftubeSisyfosLLayer } from '../tv2_offtube_studio/layers' import { SisyfosChannel, sisyfosChannels } from '../tv2_offtube_studio/sisyfosChannels' @@ -64,6 +66,8 @@ import { QBOX_UNIFORM_CONFIG } from '../tv2_offtube_studio/uniformConfig' import { NUMBER_OF_DVE_BOXES } from './content/OfftubeDVEContent' import { OfftubeOutputLayers, OfftubeSourceLayer } from './layers' +const gfxSchemaGenerator: GfxSchemaGenerator = GfxSchemaGeneratorFacade.create() + export function getRundown(coreContext: IShowStyleUserContext, ingestRundown: IngestRundown): BlueprintResultRundown { const context = new ShowStyleContextImpl(coreContext, QBOX_UNIFORM_CONFIG) @@ -709,23 +713,7 @@ function getBaseline(context: ShowStyleContext): Bluepri } } }), - literal({ - id: '', - enable: { while: '1' }, - priority: 0, - layer: OfftubeCasparLLayer.CasparCGDVELoop, - content: { - deviceType: TSR.DeviceType.CASPARCG, - type: TSR.TimelineContentTypeCasparCg.MEDIA, - file: 'empty', - transitions: { - inTransition: { - type: TSR.Transition.CUT, - duration: CONSTANTS.DefaultClipFadeOut - } - } - } - }), + ...gfxSchemaGenerator.createTimelineObjectsFromGfxDefaults(context), literal({ id: '', diff --git a/src/tv2_offtube_showstyle/helpers/EvaluateCues.ts b/src/tv2_offtube_showstyle/helpers/EvaluateCues.ts index a0506843..d70fd3e0 100644 --- a/src/tv2_offtube_showstyle/helpers/EvaluateCues.ts +++ b/src/tv2_offtube_showstyle/helpers/EvaluateCues.ts @@ -13,7 +13,7 @@ import { PartDefinition, SegmentContext } from 'tv2-common' -import { EvaluateCueGraphicSchema } from '../../tv2-common/cues/evaluate-cue-graphic-schema' +import { GfxSchemaGeneratorFacade } from '../../tv2-common/cues/gfx-schema-generator-facade' import { OfftubeEvaluateAdLib } from '../cues/OfftubeAdlib' import { OfftubeEvaluateDVE } from '../cues/OfftubeDVE' import { OfftubeEvaluateEkstern } from '../cues/OfftubeEkstern' @@ -46,7 +46,8 @@ export async function OfftubeEvaluateCues( EvaluateCueGraphicDesign: OfftubeEvaluateGraphicDesign, EvaluateCuePgmClean: OfftubeEvaluatePgmClean, EvaluateCueLYD: EvaluateLYD, - EvaluateCueGraphicSchema + EvaluateCueGraphicSchema: (c, p, partId, parsedCue) => + GfxSchemaGeneratorFacade.create().createBlueprintPieceFromGfxSchemaCue(c, p, partId, parsedCue) }, context, part, diff --git a/src/tv2_offtube_studio/layers.ts b/src/tv2_offtube_studio/layers.ts index 1f1ff004..02dbd957 100644 --- a/src/tv2_offtube_studio/layers.ts +++ b/src/tv2_offtube_studio/layers.ts @@ -26,7 +26,6 @@ enum CasparLLayer { /** Maps to the same Caspar layer as CasparPlayerJingle but its lookahead preloads the first frame */ CasparPlayerJinglePreload = 'casparcg_player_jingle_preload', CasparGraphicsFullLoop = 'casparcg_graphics_full_loop', - CasparCGDVELoop = 'casparcg_dve_loop', CasparCGDVEKeyedLoop = 'casparcg_dve_keyed_loop', CasparCGDVEKey = 'casparcg_dve_key', CasparCGDVEFrame = 'casparcg_dve_frame' From eb5ce09c398e8db788b812c4ef5d9bcf5b3e76a3 Mon Sep 17 00:00:00 2001 From: Rasmus Lindved Date: Fri, 16 Jun 2023 13:00:50 +0200 Subject: [PATCH 25/35] SOF-1420 Refactor all creation of DesignTimelineObjects to the same file. --- src/tv2-common/cues/gfx-schema-generator.ts | 2 +- .../helpers/graphics/caspar/util.ts | 29 +--- .../helpers/graphics/design/index.ts | 162 +++++++++++------- src/tv2-common/helpers/graphics/util.ts | 2 - src/tv2_afvd_showstyle/getRundown.ts | 57 +----- 5 files changed, 106 insertions(+), 146 deletions(-) diff --git a/src/tv2-common/cues/gfx-schema-generator.ts b/src/tv2-common/cues/gfx-schema-generator.ts index 48c013c1..08c35eb7 100644 --- a/src/tv2-common/cues/gfx-schema-generator.ts +++ b/src/tv2-common/cues/gfx-schema-generator.ts @@ -55,7 +55,7 @@ export class GfxSchemaGenerator { }, outputLayerId: SharedOutputLayer.SEC, sourceLayerId: SharedSourceLayer.PgmSchema, - lifespan: PieceLifespan.OutOnShowStyleEnd, + lifespan: PieceLifespan.OutOnRundownChange, content: literal>({ fileName: cue.schema, path: cue.schema, diff --git a/src/tv2-common/helpers/graphics/caspar/util.ts b/src/tv2-common/helpers/graphics/caspar/util.ts index 17604168..46d536ba 100644 --- a/src/tv2-common/helpers/graphics/caspar/util.ts +++ b/src/tv2-common/helpers/graphics/caspar/util.ts @@ -1,9 +1,9 @@ import { TSR } from 'blueprints-integration' import { + getCasparCgBaselineDesignTimelineObject, getTimelineLayerForGraphic, joinAssetToFolder, layerToHTMLGraphicSlot, - NON_BASELINE_DESIGN_ID, Slots, TV2ShowStyleConfig } from 'tv2-common' @@ -68,7 +68,7 @@ export function getHtmlGraphicBaseline(config: TV2ShowStyleConfig): TSR.TSRTimel return [ ...getSlotBaselineTimelineObjects(templateName, partiallyUpdatableLayerMappings), getCompoundSlotBaselineTimelineObject(templateName, partiallyUpdatableLayerMappings), - getDesignBaselineTimelineObject(config, templateName), + getCasparCgBaselineDesignTimelineObject(config, templateName), getFullPilotBaselineTimelineObject(templateName) ] } @@ -141,31 +141,6 @@ function getCompoundSlotBaselineTimelineObject( } } -function getDesignBaselineTimelineObject(config: TV2ShowStyleConfig, templateName: string): TSR.TimelineObjCCGTemplate { - const design: string = config.showStyle.GfxDefaults[0].DefaultDesign.label - return { - id: '', - enable: { - while: `!.${NON_BASELINE_DESIGN_ID}` - }, - priority: 10, - classes: [`${design}`], - layer: SharedGraphicLLayer.GraphicLLayerDesign, - content: { - deviceType: TSR.DeviceType.CASPARCG, - type: TSR.TimelineContentTypeCasparCg.TEMPLATE, - templateType: 'html', - name: templateName, - data: { - display: 'program', - design, - partialUpdate: true - }, - useStopCommand: false - } - } -} - function getFullPilotBaselineTimelineObject(templateName: string): TSR.TimelineObjCCGTemplate { return { id: '', diff --git a/src/tv2-common/helpers/graphics/design/index.ts b/src/tv2-common/helpers/graphics/design/index.ts index a1830a9b..12bef619 100644 --- a/src/tv2-common/helpers/graphics/design/index.ts +++ b/src/tv2-common/helpers/graphics/design/index.ts @@ -12,12 +12,13 @@ import { CueDefinitionGraphicDesign, getHtmlTemplateName, literal, - NON_BASELINE_DESIGN_ID, ShowStyleContext, TV2ShowStyleConfig } from 'tv2-common' import { SharedGraphicLLayer, SharedOutputLayer, SharedSourceLayer } from 'tv2-constants' +const NON_BASELINE_DESIGN_ID = 'NON_BASELINE_DESIGN_ID' + export function EvaluateDesignBase( context: ShowStyleContext, pieces: IBlueprintPiece[], @@ -28,80 +29,104 @@ export function EvaluateDesignBase( adlib?: boolean, rank?: number ) { - const start = (parsedCue.start ? calculateTime(parsedCue.start) : 0) ?? 0 if (!parsedCue.design || !parsedCue.design.length) { context.core.notifyUserWarning(`No valid design found for ${parsedCue.design}`) return } - if (adlib) { - adlibPieces.push({ - _rank: rank || 0, - externalId: partId, - name: parsedCue.design, - outputLayerId: SharedOutputLayer.SEC, - sourceLayerId: SharedSourceLayer.PgmDesign, - lifespan: PieceLifespan.OutOnShowStyleEnd, - content: literal>({ - fileName: parsedCue.design, - path: parsedCue.design, - ignoreMediaObjectStatus: true, - timelineObjects: designTimeline(context.config, parsedCue) - }) - }) - } else { - pieces.push({ - externalId: partId, - name: parsedCue.design, - enable: { - start - }, - outputLayerId: SharedOutputLayer.SEC, - sourceLayerId: SharedSourceLayer.PgmDesign, - lifespan: PieceLifespan.OutOnShowStyleEnd, - content: literal>({ - fileName: parsedCue.design, - path: parsedCue.design, - ignoreMediaObjectStatus: true, - timelineObjects: designTimeline(context.config, parsedCue) - }) - }) + adlibPieces.push(createDesignAdlibPiece(context, partId, parsedCue, rank)) + return + } + pieces.push(createDesignPiece(context, partId, parsedCue)) +} + +function createDesignAdlibPiece( + context: ShowStyleContext, + partId: string, + cue: CueDefinitionGraphicDesign, + rank?: number +): IBlueprintAdLibPiece { + return { + _rank: rank || 0, + externalId: partId, + name: cue.design, + outputLayerId: SharedOutputLayer.SEC, + sourceLayerId: SharedSourceLayer.PgmDesign, + lifespan: PieceLifespan.OutOnShowStyleEnd, + content: createDesignPieceContent(context, cue) } } -function designTimeline(config: TV2ShowStyleConfig, parsedCue: CueDefinitionGraphicDesign): TSR.TSRTimelineObj[] { - switch (config.studio.GraphicsType) { +function createDesignPiece( + context: ShowStyleContext, + partId: string, + cue: CueDefinitionGraphicDesign +): IBlueprintPiece { + const start = (cue.start ? calculateTime(cue.start) : 0) ?? 0 + return { + externalId: partId, + name: cue.design, + enable: { + start + }, + outputLayerId: SharedOutputLayer.SEC, + sourceLayerId: SharedSourceLayer.PgmDesign, + lifespan: PieceLifespan.OutOnShowStyleEnd, + content: createDesignPieceContent(context, cue) + } +} + +function createDesignPieceContent( + context: ShowStyleContext, + cue: CueDefinitionGraphicDesign +): WithTimeline { + return { + fileName: cue.design, + path: cue.design, + ignoreMediaObjectStatus: true, + timelineObjects: designTimeline(context, cue) + } +} + +function designTimeline(context: ShowStyleContext, parsedCue: CueDefinitionGraphicDesign): TSR.TSRTimelineObj[] { + switch (context.config.studio.GraphicsType) { case 'HTML': - return [ - literal({ - id: '', - enable: { - start: 0 - }, - priority: 1, - classes: [`${parsedCue.design}`, NON_BASELINE_DESIGN_ID], - layer: SharedGraphicLLayer.GraphicLLayerDesign, - content: { - deviceType: TSR.DeviceType.CASPARCG, - type: TSR.TimelineContentTypeCasparCg.TEMPLATE, - templateType: 'html', - name: getHtmlTemplateName(config), - data: { - display: 'program', - design: parsedCue.design, - partialUpdate: true - }, - useStopCommand: false - } - }) - ] + return [getNonBaselineCasparCgDesignTimelineObject(context, parsedCue)] case 'VIZ': - return [getNonBaselineVizDesignTimelineObject(config, parsedCue.design)] + return [getNonBaselineVizDesignTimelineObject(context.config, parsedCue.design)] default: return [] } } +function getNonBaselineCasparCgDesignTimelineObject(context: ShowStyleContext, parsedCue: CueDefinitionGraphicDesign) { + return literal({ + id: '', + enable: { + start: 0 + }, + priority: 100, + classes: [`${parsedCue.design}`, NON_BASELINE_DESIGN_ID], + layer: SharedGraphicLLayer.GraphicLLayerDesign, + content: createCasparCgDesignContent(parsedCue.design, getHtmlTemplateName(context.config)) + }) +} + +function createCasparCgDesignContent(design: string, templateName: string): TSR.TimelineObjCCGTemplate['content'] { + return { + deviceType: TSR.DeviceType.CASPARCG, + type: TSR.TimelineContentTypeCasparCg.TEMPLATE, + templateType: 'html', + name: templateName, + data: { + display: 'program', + design, + partialUpdate: true + }, + useStopCommand: false + } +} + function getNonBaselineVizDesignTimelineObject(config: TV2ShowStyleConfig, design: string) { const vizDesignTimelineObject = getVizDesignTimelineObject(config, design) vizDesignTimelineObject.classes!.push(NON_BASELINE_DESIGN_ID) @@ -129,3 +154,20 @@ export function getVizBaselineDesignTimelineObject(config: TV2ShowStyleConfig) { const design = config.showStyle.GfxDefaults[0].DefaultDesign.label return getVizDesignTimelineObject(config, design) } + +export function getCasparCgBaselineDesignTimelineObject( + config: TV2ShowStyleConfig, + templateName: string +): TSR.TimelineObjCCGTemplate { + const design: string = config.showStyle.GfxDefaults[0].DefaultDesign.label + return { + id: '', + enable: { + while: `!.${NON_BASELINE_DESIGN_ID}` + }, + priority: 1, + classes: [`${design}`], + layer: SharedGraphicLLayer.GraphicLLayerDesign, + content: createCasparCgDesignContent(design, templateName) + } +} diff --git a/src/tv2-common/helpers/graphics/util.ts b/src/tv2-common/helpers/graphics/util.ts index e5e4b4d5..00a1d69e 100644 --- a/src/tv2-common/helpers/graphics/util.ts +++ b/src/tv2-common/helpers/graphics/util.ts @@ -1,8 +1,6 @@ import { IBlueprintPart, TSR } from 'blueprints-integration' import { getHtmlGraphicBaseline, getVizBaselineDesignTimelineObject, TV2ShowStyleConfig } from 'tv2-common' -export const NON_BASELINE_DESIGN_ID = 'NON_BASELINE_DESIGN_ID' - export function applyFullGraphicPropertiesToPart(config: TV2ShowStyleConfig, part: IBlueprintPart) { const keepAliveDuration = config.studio.GraphicsType === 'HTML' diff --git a/src/tv2_afvd_showstyle/getRundown.ts b/src/tv2_afvd_showstyle/getRundown.ts index fb4b9d23..29078338 100644 --- a/src/tv2_afvd_showstyle/getRundown.ts +++ b/src/tv2_afvd_showstyle/getRundown.ts @@ -1,15 +1,12 @@ import { BlueprintResultBaseline, BlueprintResultRundown, - GraphicsContent, IBlueprintAdLibPiece, IngestRundown, IShowStyleUserContext, PieceLifespan, PlaylistTimingType, - TimelineObjectCoreExt, - TSR, - WithTimeline + TSR } from 'blueprints-integration' import { CasparPlayerClipLoadingLoop, @@ -98,58 +95,6 @@ class GlobalAdLibPiecesGenerator { return adLibPieces } - // viz styles and dve backgrounds - public makeDesignAdLib(): IBlueprintAdLibPiece { - const timelineObjects: TimelineObjectCoreExt[] = [ - literal({ - id: '', - enable: { start: 0 }, - priority: 110, - layer: CasparLLayer.CasparCGDVELoop, - content: { - deviceType: TSR.DeviceType.CASPARCG, - type: TSR.TimelineContentTypeCasparCg.MEDIA, - file: 'dve/BG_LOADER_SC', - loop: true - } - }) - ] - // @todo: use GraphicsGenerator - if (this.config.studio.GraphicsType === 'VIZ') { - timelineObjects.push( - literal({ - id: '', - enable: { start: 0 }, - priority: 110, - layer: SharedGraphicLLayer.GraphicLLayerDesign, - content: { - deviceType: TSR.DeviceType.VIZMSE, - type: TSR.TimelineContentTypeVizMSE.ELEMENT_INTERNAL, - templateName: 'BG_LOADER_SC', - templateData: [], - showName: this.config.selectedGfxSetup.OvlShowName - } - }) - ) - } - const adLibPiece: IBlueprintAdLibPiece = { - _rank: 301, - externalId: 'dve-design-sc', - name: 'DVE Design SC', - outputLayerId: SharedOutputLayer.SEC, - sourceLayerId: SourceLayer.PgmDesign, - lifespan: PieceLifespan.OutOnShowStyleEnd, - tags: [AdlibTags.ADLIB_DESIGN_STYLE_SC], - content: literal>({ - fileName: 'BG_LOADER_SC', - path: 'BG_LOADER_SC', - ignoreMediaObjectStatus: true, - timelineObjects - }) - } - return adLibPiece - } - private makeEvsAdLib(info: SourceInfo, rank: number, vo: boolean): IBlueprintAdLibPiece { return { externalId: 'delayed', From 137c33045ae803ef72fb53b420f82bb2a71dd1c9 Mon Sep 17 00:00:00 2001 From: ianshade Date: Mon, 3 Jul 2023 09:12:25 +0200 Subject: [PATCH 26/35] feat: SOF-1420 make schema baseline behave like designs; change lifespans --- src/tv2-common/__tests__/frame-time.spec.ts | 2 +- .../__tests__/gfx-schema-generator.spec.ts | 41 ++++--- src/tv2-common/blueprintConfig.ts | 2 + src/tv2-common/cueTiming.ts | 2 +- src/tv2-common/cues/gfx-schema-generator.ts | 77 ++++++++++--- src/tv2-common/evaluateCues.ts | 38 ++----- src/tv2-common/helpers/graphics/Graphic.ts | 2 +- .../__tests__/dve-loop-generator.spec.ts | 2 +- .../graphics/caspar/dve-loop-generator.ts | 27 ++++- .../helpers/graphics/caspar/index.ts | 1 + .../helpers/graphics/caspar/util.ts | 9 +- .../helpers/graphics/design/index.ts | 106 +++++++++++------- src/tv2-common/helpers/graphics/util.ts | 15 ++- .../inewsConversion/converters/ParseCue.ts | 12 +- src/tv2-common/migrations/index.ts | 6 +- .../migrations/renameConfigurationHelper.ts | 5 +- src/tv2-common/showstyle/config-manifests.ts | 2 +- src/tv2_afvd_showstyle/__tests__/configs.ts | 4 +- src/tv2_afvd_showstyle/getRundown.ts | 4 +- .../pieces/__tests__/grafikViz.spec.ts | 2 +- .../helpers/pieces/design.ts | 6 +- .../helpers/pieces/graphicBackgroundLoop.ts | 68 +++++------ .../__tests__/graphics.spec.ts | 38 ++++++- .../cues/OfftubeGraphicBackgroundLoop.ts | 67 ++++------- .../cues/OfftubeGraphicDesign.ts | 6 +- src/tv2_offtube_showstyle/getRundown.ts | 4 +- 26 files changed, 310 insertions(+), 238 deletions(-) diff --git a/src/tv2-common/__tests__/frame-time.spec.ts b/src/tv2-common/__tests__/frame-time.spec.ts index 17d9a160..dc065b3c 100644 --- a/src/tv2-common/__tests__/frame-time.spec.ts +++ b/src/tv2-common/__tests__/frame-time.spec.ts @@ -197,7 +197,7 @@ describe('CreateTiming', () => { enable: { start: 0 }, - lifespan: PieceLifespan.OutOnShowStyleEnd + lifespan: PieceLifespan.OutOnRundownChange }) ) }) diff --git a/src/tv2-common/__tests__/gfx-schema-generator.spec.ts b/src/tv2-common/__tests__/gfx-schema-generator.spec.ts index d797e97c..3bc56a2a 100644 --- a/src/tv2-common/__tests__/gfx-schema-generator.spec.ts +++ b/src/tv2-common/__tests__/gfx-schema-generator.spec.ts @@ -14,7 +14,7 @@ import { MockContextBuilder } from './mock-context-builder' // tslint:disable:no-object-literal-type-assertion describe('GfxSchemaGenerator', () => { describe('createTimelineObjectsFromGfxDefaults', () => { - it('has no schema configured - throws error', () => { + it('has no schema configured - notifies about error', () => { const context = new MockContextBuilder() .setGfxDefaults({ DefaultSchema: { label: 'someSchema' } @@ -26,9 +26,10 @@ describe('GfxSchemaGenerator', () => { const testee = createTestee() - expect(() => { - testee.createTimelineObjectsFromGfxDefaults(instance(context)) - }).toThrow() + const result = testee.createBaselineTimelineObjectsFromGfxDefaults(instance(context)) + expect(result).toBeTruthy() + + verify(core.notifyUserError(anything())).once() }) function createTestee(dveLoopGenerator?: DveLoopGenerator) { @@ -39,10 +40,10 @@ describe('GfxSchemaGenerator', () => { return new GfxSchemaGenerator(instance(dveLoopGenerator)) } - it('has schema configured, but no match from default schema - throws error', () => { + it('has schema configured, but no match from default schema - notifies about error', () => { const context = new MockContextBuilder() .setGfxDefaults({ - DefaultSchema: { label: 'someSchema' } + DefaultSchema: { value: 'someSchema' } } as TableConfigItemGfxDefaults) .setGfxSchemaTemplates([ { @@ -55,20 +56,22 @@ describe('GfxSchemaGenerator', () => { when(context.core).thenReturn(instance(core)) const testee = createTestee() - expect(() => { - testee.createTimelineObjectsFromGfxDefaults(instance(context)) - }).toThrow() + const result = testee.createBaselineTimelineObjectsFromGfxDefaults(instance(context)) + expect(result).toBeTruthy() + + verify(core.notifyUserError(anything())).once() }) - it('has correctly configured schema - does not throw', () => { - const schemaName = 'someSchema' + it('has correctly configured schema - does not notify about error', () => { + const schemaId = 'someSchemaId' const context = new MockContextBuilder() .setGfxDefaults({ - DefaultSchema: { label: schemaName } + DefaultSchema: { value: schemaId } } as TableConfigItemGfxDefaults) .setGfxSchemaTemplates([ { - GfxSchemaTemplatesName: schemaName, + _id: schemaId, + GfxSchemaTemplatesName: 'SomeSchema', CasparCgDesignValues: '[{}]' } ] as TableConfigGfxSchema[]) @@ -78,8 +81,10 @@ describe('GfxSchemaGenerator', () => { when(context.core).thenReturn(instance(core)) const testee = createTestee() - const result = testee.createTimelineObjectsFromGfxDefaults(instance(context)) + const result = testee.createBaselineTimelineObjectsFromGfxDefaults(instance(context)) expect(result).toBeTruthy() + + verify(core.notifyUserError(anything())).never() }) describe('graphicsType is "VIZ"', () => { @@ -105,7 +110,7 @@ describe('GfxSchemaGenerator', () => { .build() const testee = createTestee() - const result = testee.createTimelineObjectsFromGfxDefaults(instance(context)) + const result = testee.createBaselineTimelineObjectsFromGfxDefaults(instance(context)) const schemaTimelineObject = result.find( (timelineObject) => timelineObject.layer === SharedGraphicLLayer.GraphicLLayerSchema ) @@ -148,7 +153,7 @@ describe('GfxSchemaGenerator', () => { ) const testee = createTestee(dveLoopGenerator) - const result = testee.createTimelineObjectsFromGfxDefaults(instance(context)) + const result = testee.createBaselineTimelineObjectsFromGfxDefaults(instance(context)) verify(dveLoopGenerator.createCasparCgDveLoopsFromCue(anything(), anything(), anyNumber())).called() expect(result.includes(casparCgDveLoopTimelineObjects[0])).toBeTruthy() @@ -182,7 +187,7 @@ describe('GfxSchemaGenerator', () => { .build() const testee = createTestee() - const result = testee.createTimelineObjectsFromGfxDefaults(instance(context)) + const result = testee.createBaselineTimelineObjectsFromGfxDefaults(instance(context)) const schemaTimelineObject = result.find( (timelineObject) => timelineObject.layer === SharedGraphicLLayer.GraphicLLayerSchema ) @@ -225,7 +230,7 @@ describe('GfxSchemaGenerator', () => { ) const testee = createTestee(dveLoopGenerator) - const result = testee.createTimelineObjectsFromGfxDefaults(instance(context)) + const result = testee.createBaselineTimelineObjectsFromGfxDefaults(instance(context)) verify(dveLoopGenerator.createCasparCgDveLoopsFromCue(anything(), anything(), anyNumber())).called() expect(result.includes(casparCgDveLoopTimelineObjects[0])).toBeTruthy() diff --git a/src/tv2-common/blueprintConfig.ts b/src/tv2-common/blueprintConfig.ts index 471e0635..7b658ca9 100644 --- a/src/tv2-common/blueprintConfig.ts +++ b/src/tv2-common/blueprintConfig.ts @@ -31,6 +31,7 @@ export interface TableConfigItemGfxTemplate { } export interface TableConfigItemGfxDesignTemplate { + _id: string INewsName: string INewsStyleColumn: string /** Name of the Viz template trigering design change. For HTML graphics it coresponds to a CSS class. */ @@ -54,6 +55,7 @@ export interface TableConfigItemAdLibTransitions { } export interface TableConfigGfxSchema { + _id: string GfxSchemaTemplatesName: string INewsSkemaColumn: string VizTemplate: string diff --git a/src/tv2-common/cueTiming.ts b/src/tv2-common/cueTiming.ts index b0e550bf..292a2745 100644 --- a/src/tv2-common/cueTiming.ts +++ b/src/tv2-common/cueTiming.ts @@ -48,7 +48,7 @@ export function getLifeSpan(mode: 'B' | 'S' | 'O'): PieceLifespan { case 'S': return PieceLifespan.OutOnSegmentEnd case 'O': - return PieceLifespan.OutOnShowStyleEnd + return PieceLifespan.OutOnRundownChange } } diff --git a/src/tv2-common/cues/gfx-schema-generator.ts b/src/tv2-common/cues/gfx-schema-generator.ts index 08c35eb7..2fb60c00 100644 --- a/src/tv2-common/cues/gfx-schema-generator.ts +++ b/src/tv2-common/cues/gfx-schema-generator.ts @@ -11,19 +11,21 @@ import { import { CueType, SharedGraphicLLayer, SharedOutputLayer, SharedSourceLayer } from 'tv2-constants' import { DveLoopGenerator } from '../helpers/graphics/caspar/dve-loop-generator' +const NON_BASELINE_SCHEMA = 'NON_BASELINE_SCHEMA' + export class GfxSchemaGenerator { constructor(private dveLoopGenerator: DveLoopGenerator) {} - public createTimelineObjectsFromGfxDefaults(context: ShowStyleContext): TSR.TSRTimelineObjBase[] { - const schemaName = context.config.showStyle.GfxDefaults[0].DefaultSchema.label + public createBaselineTimelineObjectsFromGfxDefaults(context: ShowStyleContext): TSR.TSRTimelineObjBase[] { + const schemaId = context.config.showStyle.GfxDefaults[0].DefaultSchema.value const schema: TableConfigGfxSchema | undefined = context.config.showStyle.GfxSchemaTemplates.find( - (s) => s.GfxSchemaTemplatesName === schemaName + (s) => s._id === schemaId ) if (!schema || !schema.CasparCgDesignValues) { context.core.notifyUserError( `Unable to create baseline DVE loops for CasparCG. Check GfxDefaults Schema is configured.` ) - throw new Error(`Incorrectly configured GfxDefaults.schema`) + return [] } const cue: CueDefinitionGfxSchema = { @@ -32,7 +34,7 @@ export class GfxSchemaGenerator { CasparCgDesignValues: schema.CasparCgDesignValues ? JSON.parse(schema.CasparCgDesignValues) : [], iNewsCommand: '' } - return this.createTimelineObjects(context, cue, 10) + return this.createBaselineTimelineObjects(context, cue, 10) } public createBlueprintPieceFromGfxSchemaCue( @@ -55,7 +57,8 @@ export class GfxSchemaGenerator { }, outputLayerId: SharedOutputLayer.SEC, sourceLayerId: SharedSourceLayer.PgmSchema, - lifespan: PieceLifespan.OutOnRundownChange, + // @ts-ignore + lifespan: cue.isFromField ? 'rundown-change-segment-lookback' : PieceLifespan.OutOnRundownChange, content: literal>({ fileName: cue.schema, path: cue.schema, @@ -89,13 +92,37 @@ export class GfxSchemaGenerator { } } - private createVizSchemaTimelineObject( + private createBaselineTimelineObjects( + context: ShowStyleContext, + cue: CueDefinitionGfxSchema, + priority?: number + ): TSR.TSRTimelineObjBase[] { + switch (context.config.studio.GraphicsType) { + case 'VIZ': { + return [ + this.createBaselineVizSchemaTimelineObject(context.config, cue), + ...this.dveLoopGenerator.createCasparCgDveLoopsFromCue(context, cue, priority) + ] + } + case 'HTML': { + return [ + this.createBaselineCasparCgSchemaTimelineObject(context, cue), + ...this.dveLoopGenerator.createCasparCgDveLoopsFromCue(context, cue, priority) + ] + } + default: { + return [] + } + } + } + + private createBaselineVizSchemaTimelineObject( config: TV2ShowStyleConfig, cue: CueDefinitionGfxSchema ): TSR.TimelineObjVIZMSEElementInternal { - return literal({ + return { id: '', - enable: { start: 0 }, + enable: { while: `!.${NON_BASELINE_SCHEMA}` }, priority: 100, classes: [cue.schema], layer: SharedGraphicLLayer.GraphicLLayerSchema, @@ -106,16 +133,27 @@ export class GfxSchemaGenerator { templateData: [], showName: config.selectedGfxSetup.OvlShowName ?? '' } - }) + } } - private createCasparCgSchemaTimelineObject( + private createVizSchemaTimelineObject( + config: TV2ShowStyleConfig, + cue: CueDefinitionGfxSchema + ): TSR.TimelineObjVIZMSEElementInternal { + return { + ...this.createBaselineVizSchemaTimelineObject(config, cue), + enable: { start: 0 }, + classes: [cue.schema, NON_BASELINE_SCHEMA] + } + } + + private createBaselineCasparCgSchemaTimelineObject( context: ShowStyleContext, cue: CueDefinitionGfxSchema - ): TSR.TimelineObjCasparCGBase { - return literal({ + ): TSR.TimelineObjCCGTemplate { + return { id: '', - enable: { start: 0 }, + enable: { while: `!.${NON_BASELINE_SCHEMA}` }, priority: 100, classes: [cue.schema], layer: SharedGraphicLLayer.GraphicLLayerSchema, @@ -127,6 +165,17 @@ export class GfxSchemaGenerator { data: this.createCasparCgHtmlSchemaData(context, cue), useStopCommand: false } + } + } + + private createCasparCgSchemaTimelineObject( + context: ShowStyleContext, + cue: CueDefinitionGfxSchema + ): TSR.TimelineObjCasparCGBase { + return literal({ + ...this.createBaselineCasparCgSchemaTimelineObject(context, cue), + enable: { start: 0 }, + classes: [cue.schema, NON_BASELINE_SCHEMA] }) } diff --git a/src/tv2-common/evaluateCues.ts b/src/tv2-common/evaluateCues.ts index ebd6257d..9c52d82c 100644 --- a/src/tv2-common/evaluateCues.ts +++ b/src/tv2-common/evaluateCues.ts @@ -63,24 +63,18 @@ export interface EvaluateCuesShowstyleOptions { ) => EvaluateCueResult EvaluateCueBackgroundLoop?: ( context: ShowStyleContext, - pieces: IBlueprintPiece[], - adlibPieces: IBlueprintAdLibPiece[], - actions: IBlueprintActionManifest[], partId: string, parsedCue: CueDefinitionBackgroundLoop, adlib?: boolean, rank?: number - ) => void + ) => EvaluateCueResult EvaluateCueGraphicDesign?: ( context: ShowStyleContext, - pieces: IBlueprintPiece[], - adlibPieces: IBlueprintAdLibPiece[], - actions: IBlueprintActionManifest[], partId: string, parsedCue: CueDefinitionGraphicDesign, adlib?: boolean, rank?: number - ) => void + ) => EvaluateCueResult EvaluateCueGraphicSchema?: ( context: ShowStyleContext, pieces: IBlueprintPiece[], @@ -287,15 +281,8 @@ export async function EvaluateCuesBase( break case CueType.GraphicDesign: if (showStyleOptions.EvaluateCueGraphicDesign) { - showStyleOptions.EvaluateCueGraphicDesign( - context, - pieces, - adLibPieces, - actions, - partDefinition.externalId, - cue, - shouldAdlib, - adLibRank + result.push( + showStyleOptions.EvaluateCueGraphicDesign(context, partDefinition.externalId, cue, shouldAdlib, adLibRank) ) } break @@ -319,15 +306,14 @@ export async function EvaluateCuesBase( break case CueType.BackgroundLoop: if (showStyleOptions.EvaluateCueBackgroundLoop) { - showStyleOptions.EvaluateCueBackgroundLoop( - context, - pieces, - adLibPieces, - actions, - partDefinition.externalId, - cue, - shouldAdlib, - adLibRank + result.push( + showStyleOptions.EvaluateCueBackgroundLoop( + context, + partDefinition.externalId, + cue, + shouldAdlib, + adLibRank + ) ) } break diff --git a/src/tv2-common/helpers/graphics/Graphic.ts b/src/tv2-common/helpers/graphics/Graphic.ts index 077f7bf5..18122496 100644 --- a/src/tv2-common/helpers/graphics/Graphic.ts +++ b/src/tv2-common/helpers/graphics/Graphic.ts @@ -106,7 +106,7 @@ export abstract class Graphic { protected getPieceLifespan(): PieceLifespan { if (IsTargetingWall(this.engine)) { - return PieceLifespan.OutOnShowStyleEnd + return PieceLifespan.OutOnRundownChange } if (IsTargetingTLF(this.engine)) { return PieceLifespan.WithinPart diff --git a/src/tv2-common/helpers/graphics/caspar/__tests__/dve-loop-generator.spec.ts b/src/tv2-common/helpers/graphics/caspar/__tests__/dve-loop-generator.spec.ts index 9bdb6b92..557aac7e 100644 --- a/src/tv2-common/helpers/graphics/caspar/__tests__/dve-loop-generator.spec.ts +++ b/src/tv2-common/helpers/graphics/caspar/__tests__/dve-loop-generator.spec.ts @@ -1,4 +1,4 @@ -import { Timeline, TSR } from '@sofie-automation/blueprints-integration' +import { Timeline, TSR } from 'blueprints-integration' import { mock } from 'ts-mockito' import { CueDefinitionGfxSchema, ShowStyleContext } from 'tv2-common' import { CueType, SharedCasparLLayer } from 'tv2-constants' diff --git a/src/tv2-common/helpers/graphics/caspar/dve-loop-generator.ts b/src/tv2-common/helpers/graphics/caspar/dve-loop-generator.ts index 0c5ba532..33896ae0 100644 --- a/src/tv2-common/helpers/graphics/caspar/dve-loop-generator.ts +++ b/src/tv2-common/helpers/graphics/caspar/dve-loop-generator.ts @@ -1,4 +1,4 @@ -import { TSR } from '@sofie-automation/blueprints-integration' +import { TSR } from 'blueprints-integration' import { CasparCgGfxDesignValues, CueDefinitionGfxSchema, ShowStyleContext } from 'tv2-common' import { SharedCasparLLayer } from 'tv2-constants' @@ -8,17 +8,17 @@ export class DveLoopGenerator { cue: CueDefinitionGfxSchema, priority?: number ): TSR.TimelineObjCCGMedia[] { - if (!cue.CasparCgDesignValues || !cue.CasparCgDesignValues.length || cue.CasparCgDesignValues.length === 0) { + if (!cue.CasparCgDesignValues || !cue.CasparCgDesignValues.length) { const errorMessage = `No CasparCgDesignValues configured for Schema {${cue.schema}}` context.core.notifyUserError(errorMessage) - throw new Error(errorMessage) + return [] } return cue.CasparCgDesignValues.map((designValues) => - this.createDveLoopTimelineObject(cue.schema, designValues, priority) + this.createDveLoopTimelineObjectForSchema(cue.schema, designValues, priority) ) } - private createDveLoopTimelineObject( + private createDveLoopTimelineObjectForSchema( schemaName: string, design: CasparCgGfxDesignValues, priority?: number @@ -38,4 +38,21 @@ export class DveLoopGenerator { } } } + + public createDveLoopTimelineObject(fileName: string): TSR.TimelineObjCCGMedia[] { + return [ + { + id: '', + enable: { start: 0 }, + priority: 100, + layer: SharedCasparLLayer.CasparCGDVELoop, + content: { + deviceType: TSR.DeviceType.CASPARCG, + type: TSR.TimelineContentTypeCasparCg.MEDIA, + file: `dve/${fileName}`, + loop: true + } + } + ] + } } diff --git a/src/tv2-common/helpers/graphics/caspar/index.ts b/src/tv2-common/helpers/graphics/caspar/index.ts index 810f82a8..8ac97ac6 100644 --- a/src/tv2-common/helpers/graphics/caspar/index.ts +++ b/src/tv2-common/helpers/graphics/caspar/index.ts @@ -2,3 +2,4 @@ export * from './slotMappings' export * from './htmlPilotGraphicGenerator' export * from './HtmlInternalGraphic' export * from './util' +export * from './dve-loop-generator' diff --git a/src/tv2-common/helpers/graphics/caspar/util.ts b/src/tv2-common/helpers/graphics/caspar/util.ts index 46d536ba..00223d0e 100644 --- a/src/tv2-common/helpers/graphics/caspar/util.ts +++ b/src/tv2-common/helpers/graphics/caspar/util.ts @@ -1,9 +1,10 @@ import { TSR } from 'blueprints-integration' import { - getCasparCgBaselineDesignTimelineObject, + getCasparCgBaselineDesignTimelineObjects, getTimelineLayerForGraphic, joinAssetToFolder, layerToHTMLGraphicSlot, + ShowStyleContext, Slots, TV2ShowStyleConfig } from 'tv2-common' @@ -55,8 +56,8 @@ export function getHtmlTemplateContent( } } -export function getHtmlGraphicBaseline(config: TV2ShowStyleConfig): TSR.TSRTimelineObj[] { - const templateName = getHtmlTemplateName(config) +export function getHtmlGraphicBaseline(context: ShowStyleContext): TSR.TSRTimelineObj[] { + const templateName = getHtmlTemplateName(context.config) const partiallyUpdatableLayerMappings = [ SharedGraphicLLayer.GraphicLLayerOverlayIdent, SharedGraphicLLayer.GraphicLLayerOverlayLower, @@ -68,7 +69,7 @@ export function getHtmlGraphicBaseline(config: TV2ShowStyleConfig): TSR.TSRTimel return [ ...getSlotBaselineTimelineObjects(templateName, partiallyUpdatableLayerMappings), getCompoundSlotBaselineTimelineObject(templateName, partiallyUpdatableLayerMappings), - getCasparCgBaselineDesignTimelineObject(config, templateName), + ...getCasparCgBaselineDesignTimelineObjects(context, templateName), getFullPilotBaselineTimelineObject(templateName) ] } diff --git a/src/tv2-common/helpers/graphics/design/index.ts b/src/tv2-common/helpers/graphics/design/index.ts index 12bef619..30a5df89 100644 --- a/src/tv2-common/helpers/graphics/design/index.ts +++ b/src/tv2-common/helpers/graphics/design/index.ts @@ -1,6 +1,5 @@ import { GraphicsContent, - IBlueprintActionManifest, IBlueprintAdLibPiece, IBlueprintPiece, PieceLifespan, @@ -10,34 +9,34 @@ import { import { calculateTime, CueDefinitionGraphicDesign, + EvaluateCueResult, getHtmlTemplateName, - literal, + PieceMetaData, ShowStyleContext, TV2ShowStyleConfig } from 'tv2-common' import { SharedGraphicLLayer, SharedOutputLayer, SharedSourceLayer } from 'tv2-constants' -const NON_BASELINE_DESIGN_ID = 'NON_BASELINE_DESIGN_ID' +const NON_BASELINE_DESIGN = 'NON_BASELINE_DESIGN' export function EvaluateDesignBase( context: ShowStyleContext, - pieces: IBlueprintPiece[], - adlibPieces: IBlueprintAdLibPiece[], - _actions: IBlueprintActionManifest[], partId: string, parsedCue: CueDefinitionGraphicDesign, adlib?: boolean, rank?: number -) { - if (!parsedCue.design || !parsedCue.design.length) { - context.core.notifyUserWarning(`No valid design found for ${parsedCue.design}`) - return +): EvaluateCueResult { + const result = new EvaluateCueResult() + if (!parsedCue.design) { + context.core.notifyUserWarning(`No valid design found for ${JSON.stringify(parsedCue)}`) + return result } if (adlib) { - adlibPieces.push(createDesignAdlibPiece(context, partId, parsedCue, rank)) - return + result.adlibPieces.push(createDesignAdlibPiece(context, partId, parsedCue, rank)) + return result } - pieces.push(createDesignPiece(context, partId, parsedCue)) + result.pieces.push(createDesignPiece(context, partId, parsedCue)) + return result } function createDesignAdlibPiece( @@ -45,14 +44,15 @@ function createDesignAdlibPiece( partId: string, cue: CueDefinitionGraphicDesign, rank?: number -): IBlueprintAdLibPiece { +): IBlueprintAdLibPiece { return { - _rank: rank || 0, + _rank: rank ?? 0, externalId: partId, name: cue.design, outputLayerId: SharedOutputLayer.SEC, sourceLayerId: SharedSourceLayer.PgmDesign, - lifespan: PieceLifespan.OutOnShowStyleEnd, + // @ts-ignore + lifespan: cue.isFromField ? 'rundown-change-segment-lookback' : PieceLifespan.OutOnRundownChange, content: createDesignPieceContent(context, cue) } } @@ -61,7 +61,7 @@ function createDesignPiece( context: ShowStyleContext, partId: string, cue: CueDefinitionGraphicDesign -): IBlueprintPiece { +): IBlueprintPiece { const start = (cue.start ? calculateTime(cue.start) : 0) ?? 0 return { externalId: partId, @@ -71,7 +71,8 @@ function createDesignPiece( }, outputLayerId: SharedOutputLayer.SEC, sourceLayerId: SharedSourceLayer.PgmDesign, - lifespan: PieceLifespan.OutOnShowStyleEnd, + // @ts-ignore + lifespan: cue.isFromField ? 'rundown-change-segment-lookback' : PieceLifespan.OutOnRundownChange, content: createDesignPieceContent(context, cue) } } @@ -99,17 +100,20 @@ function designTimeline(context: ShowStyleContext, parsedCue: CueDefinitionGraph } } -function getNonBaselineCasparCgDesignTimelineObject(context: ShowStyleContext, parsedCue: CueDefinitionGraphicDesign) { - return literal({ +function getNonBaselineCasparCgDesignTimelineObject( + context: ShowStyleContext, + parsedCue: CueDefinitionGraphicDesign +): TSR.TimelineObjCCGTemplate { + return { id: '', enable: { start: 0 }, priority: 100, - classes: [`${parsedCue.design}`, NON_BASELINE_DESIGN_ID], + classes: [`${parsedCue.design}`, NON_BASELINE_DESIGN], layer: SharedGraphicLLayer.GraphicLLayerDesign, content: createCasparCgDesignContent(parsedCue.design, getHtmlTemplateName(context.config)) - }) + } } function createCasparCgDesignContent(design: string, templateName: string): TSR.TimelineObjCCGTemplate['content'] { @@ -129,16 +133,16 @@ function createCasparCgDesignContent(design: string, templateName: string): TSR. function getNonBaselineVizDesignTimelineObject(config: TV2ShowStyleConfig, design: string) { const vizDesignTimelineObject = getVizDesignTimelineObject(config, design) - vizDesignTimelineObject.classes!.push(NON_BASELINE_DESIGN_ID) + vizDesignTimelineObject.classes!.push(NON_BASELINE_DESIGN) return vizDesignTimelineObject } -function getVizDesignTimelineObject(config: TV2ShowStyleConfig, design: string) { - return literal({ +function getVizDesignTimelineObject(config: TV2ShowStyleConfig, design: string): TSR.TimelineObjVIZMSEElementInternal { + return { id: '', enable: { start: 0 }, priority: 100, - classes: [`${design}`], + classes: [design], layer: SharedGraphicLLayer.GraphicLLayerDesign, content: { deviceType: TSR.DeviceType.VIZMSE, @@ -147,27 +151,43 @@ function getVizDesignTimelineObject(config: TV2ShowStyleConfig, design: string) templateData: [], showName: config.selectedGfxSetup.OvlShowName ?? '' // @todo: improve types at the junction of HTML and Viz } - }) + } } -export function getVizBaselineDesignTimelineObject(config: TV2ShowStyleConfig) { - const design = config.showStyle.GfxDefaults[0].DefaultDesign.label - return getVizDesignTimelineObject(config, design) +export function getVizBaselineDesignTimelineObject(context: ShowStyleContext) { + const designReference = context.config.showStyle.GfxDefaults[0].DefaultDesign + const design = context.config.showStyle.GfxDesignTemplates.find( + (designTemplate) => designTemplate._id === designReference.value + ) + if (!design) { + context.core.notifyUserWarning(`Design ${designReference.label} not found in GFX Design Templates`) + return [] + } + return [getVizDesignTimelineObject(context.config, design.VizTemplate)] } -export function getCasparCgBaselineDesignTimelineObject( - config: TV2ShowStyleConfig, +export function getCasparCgBaselineDesignTimelineObjects( + context: ShowStyleContext, templateName: string -): TSR.TimelineObjCCGTemplate { - const design: string = config.showStyle.GfxDefaults[0].DefaultDesign.label - return { - id: '', - enable: { - while: `!.${NON_BASELINE_DESIGN_ID}` - }, - priority: 1, - classes: [`${design}`], - layer: SharedGraphicLLayer.GraphicLLayerDesign, - content: createCasparCgDesignContent(design, templateName) +): TSR.TimelineObjCCGTemplate[] { + const designReference = context.config.showStyle.GfxDefaults[0].DefaultDesign + const design = context.config.showStyle.GfxDesignTemplates.find( + (designTemplate) => designTemplate._id === designReference.value + ) + if (!design) { + context.core.notifyUserWarning(`Design ${designReference.label} not found in GFX Design Templates`) + return [] } + return [ + { + id: '', + enable: { + while: `!.${NON_BASELINE_DESIGN}` + }, + priority: 1, + classes: [design.VizTemplate], + layer: SharedGraphicLLayer.GraphicLLayerDesign, + content: createCasparCgDesignContent(design.VizTemplate, templateName) + } + ] } diff --git a/src/tv2-common/helpers/graphics/util.ts b/src/tv2-common/helpers/graphics/util.ts index 00a1d69e..02ab3f4c 100644 --- a/src/tv2-common/helpers/graphics/util.ts +++ b/src/tv2-common/helpers/graphics/util.ts @@ -1,5 +1,10 @@ import { IBlueprintPart, TSR } from 'blueprints-integration' -import { getHtmlGraphicBaseline, getVizBaselineDesignTimelineObject, TV2ShowStyleConfig } from 'tv2-common' +import { + getHtmlGraphicBaseline, + getVizBaselineDesignTimelineObject, + ShowStyleContext, + TV2ShowStyleConfig +} from 'tv2-common' export function applyFullGraphicPropertiesToPart(config: TV2ShowStyleConfig, part: IBlueprintPart) { const keepAliveDuration = @@ -17,10 +22,10 @@ export function applyFullGraphicPropertiesToPart(config: TV2ShowStyleConfig, par } } -export function getGraphicBaseline(config: TV2ShowStyleConfig): TSR.TSRTimelineObj[] { - if (config.studio.GraphicsType === 'VIZ') { - return [getVizBaselineDesignTimelineObject(config)] +export function getGraphicBaseline(context: ShowStyleContext): TSR.TSRTimelineObj[] { + if (context.config.studio.GraphicsType === 'VIZ') { + return getVizBaselineDesignTimelineObject(context) } else { - return getHtmlGraphicBaseline(config) + return getHtmlGraphicBaseline(context) } } diff --git a/src/tv2-common/inewsConversion/converters/ParseCue.ts b/src/tv2-common/inewsConversion/converters/ParseCue.ts index d39d6103..0521f034 100644 --- a/src/tv2-common/inewsConversion/converters/ParseCue.ts +++ b/src/tv2-common/inewsConversion/converters/ParseCue.ts @@ -116,12 +116,20 @@ export interface CueDefinitionUnpairedPilot extends CueDefinitionBase { engineNumber?: number } -export interface CueDefinitionBackgroundLoop extends CueDefinitionBase, CueDefinitionFromField { +export interface CueDefinitionDveBackgroundLoop extends CueDefinitionBase, CueDefinitionFromField { type: CueType.BackgroundLoop - target: 'FULL' | 'DVE' + target: 'DVE' backgroundLoop: string } +export interface CueDefinitionFullBackgroundLoop extends CueDefinitionBase, CueDefinitionFromField { + type: CueType.BackgroundLoop + target: 'FULL' + backgroundLoop: string +} + +export type CueDefinitionBackgroundLoop = CueDefinitionDveBackgroundLoop | CueDefinitionFullBackgroundLoop + export interface CueDefinitionGraphicDesign extends CueDefinitionBase, CueDefinitionFromField { type: CueType.GraphicDesign design: string diff --git a/src/tv2-common/migrations/index.ts b/src/tv2-common/migrations/index.ts index 765a9c7e..e12a8306 100644 --- a/src/tv2-common/migrations/index.ts +++ b/src/tv2-common/migrations/index.ts @@ -1,4 +1,4 @@ -import { BasicConfigItemValue, IBlueprintShowStyleVariant } from '@sofie-automation/blueprints-integration' +import { BasicConfigItemValue, IBlueprintShowStyleVariant } from 'blueprints-integration' import { BlueprintMappings, ConfigItemValue, @@ -160,8 +160,8 @@ export function mapGfxTemplateToDesignTemplateAndDeleteOriginals( gfxTemplates .filter((template) => template.IsDesign) - .map((template) => { - designTemplates.push({ ...template, INewsStyleColumn: '' }) + .forEach((template, index) => { + designTemplates.push({ _id: `${index}`, ...template, INewsStyleColumn: '' }) }) const newGfxTemplates = gfxTemplates.filter((template) => !template.IsDesign) diff --git a/src/tv2-common/migrations/renameConfigurationHelper.ts b/src/tv2-common/migrations/renameConfigurationHelper.ts index 3297af30..6017d60c 100644 --- a/src/tv2-common/migrations/renameConfigurationHelper.ts +++ b/src/tv2-common/migrations/renameConfigurationHelper.ts @@ -1,9 +1,10 @@ -import { BasicConfigItemValue, IBlueprintShowStyleVariant } from '@sofie-automation/blueprints-integration' import { + BasicConfigItemValue, + IBlueprintShowStyleVariant, MigrationContextShowStyle, MigrationStepShowStyle, TableConfigItemValue -} from '../../types/blueprints-integration' +} from 'blueprints-integration' /** * "Renames" the id of a table by copying over all values over into a new table which has the new id - then removes the values from the old table diff --git a/src/tv2-common/showstyle/config-manifests.ts b/src/tv2-common/showstyle/config-manifests.ts index d675ffb1..d49e9530 100644 --- a/src/tv2-common/showstyle/config-manifests.ts +++ b/src/tv2-common/showstyle/config-manifests.ts @@ -137,7 +137,7 @@ export const gfxSchemaTemplates: ConfigManifestEntry[] = [ 'A JSON array containing color values and background loops for the different Designs of the Schema used by CasparCG', type: ConfigManifestEntryType.JSON, required: false, - defaultVal: '', + defaultVal: '[]', rank: 3 } ] diff --git a/src/tv2_afvd_showstyle/__tests__/configs.ts b/src/tv2_afvd_showstyle/__tests__/configs.ts index 5fb54888..fbfab2ed 100644 --- a/src/tv2_afvd_showstyle/__tests__/configs.ts +++ b/src/tv2_afvd_showstyle/__tests__/configs.ts @@ -232,6 +232,7 @@ export const defaultShowStyleConfig: GalleryShowStyleConfig = { ], GfxDesignTemplates: [ { + _id: '', INewsName: 'DESIGN_FODBOLD_22', INewsStyleColumn: 'F_22', VizTemplate: 'DESIGN_FODBOLD_22' @@ -275,6 +276,7 @@ export const defaultShowStyleConfig: GalleryShowStyleConfig = { ShowstyleTransition: 'CUT', GfxSchemaTemplates: [ { + _id: 'SkemaNewsId', GfxSchemaTemplatesName: 'SKEMA_NEWS', VizTemplate: 'NE', INewsSkemaColumn: 'SKEMA_NEWS', @@ -286,7 +288,7 @@ export const defaultShowStyleConfig: GalleryShowStyleConfig = { { DefaultSetupName: { value: 'SomeId', label: 'SomeProfile' }, DefaultDesign: { value: '', label: '' }, - DefaultSchema: { value: 'SKEMA_NEWS', label: 'SKEMA_NEWS' } + DefaultSchema: { value: 'SkemaNewsId', label: 'SKEMA_NEWS' } } ] } diff --git a/src/tv2_afvd_showstyle/getRundown.ts b/src/tv2_afvd_showstyle/getRundown.ts index 29078338..56909425 100644 --- a/src/tv2_afvd_showstyle/getRundown.ts +++ b/src/tv2_afvd_showstyle/getRundown.ts @@ -403,7 +403,8 @@ function getBaseline(context: ShowStyleContext): Bluepri return { timelineObjects: _.compact([ - ...getGraphicBaseline(context.config), + ...getGraphicBaseline(context), + ...gfxSchemaGenerator.createBaselineTimelineObjectsFromGfxDefaults(context), // Default timeline ...getMixEffectBaseline(context, context.config.studio.SwitcherSource.Default), @@ -572,7 +573,6 @@ function getBaseline(context: ShowStyleContext): Bluepri } } }), - ...gfxSchemaGenerator.createTimelineObjectsFromGfxDefaults(context), literal({ id: '', enable: { while: 1 }, diff --git a/src/tv2_afvd_showstyle/helpers/pieces/__tests__/grafikViz.spec.ts b/src/tv2_afvd_showstyle/helpers/pieces/__tests__/grafikViz.spec.ts index ed43b987..09ad233a 100644 --- a/src/tv2_afvd_showstyle/helpers/pieces/__tests__/grafikViz.spec.ts +++ b/src/tv2_afvd_showstyle/helpers/pieces/__tests__/grafikViz.spec.ts @@ -482,7 +482,7 @@ describe('grafik piece', () => { enable: { start: 10000 }, - lifespan: PieceLifespan.OutOnShowStyleEnd + lifespan: PieceLifespan.OutOnRundownChange }) expect( result.pieces[0].content.timelineObjects.find((tlObject) => tlObject.content.deviceType === TSR.DeviceType.VIZMSE) diff --git a/src/tv2_afvd_showstyle/helpers/pieces/design.ts b/src/tv2_afvd_showstyle/helpers/pieces/design.ts index a9015fee..3e768e2a 100644 --- a/src/tv2_afvd_showstyle/helpers/pieces/design.ts +++ b/src/tv2_afvd_showstyle/helpers/pieces/design.ts @@ -1,16 +1,12 @@ -import { IBlueprintActionManifest, IBlueprintAdLibPiece, IBlueprintPiece } from 'blueprints-integration' import { CueDefinitionGraphicDesign, EvaluateDesignBase, ShowStyleContext } from 'tv2-common' import * as _ from 'underscore' export function EvaluateCueDesign( context: ShowStyleContext, - pieces: IBlueprintPiece[], - adlibPieces: IBlueprintAdLibPiece[], - actions: IBlueprintActionManifest[], partId: string, parsedCue: CueDefinitionGraphicDesign, adlib?: boolean, rank?: number ) { - EvaluateDesignBase(context, pieces, adlibPieces, actions, partId, parsedCue, adlib, rank) + return EvaluateDesignBase(context, partId, parsedCue, adlib, rank) } diff --git a/src/tv2_afvd_showstyle/helpers/pieces/graphicBackgroundLoop.ts b/src/tv2_afvd_showstyle/helpers/pieces/graphicBackgroundLoop.ts index 84682cd8..383d9b95 100644 --- a/src/tv2_afvd_showstyle/helpers/pieces/graphicBackgroundLoop.ts +++ b/src/tv2_afvd_showstyle/helpers/pieces/graphicBackgroundLoop.ts @@ -1,49 +1,47 @@ +import { GraphicsContent, PieceLifespan, TSR, WithTimeline } from 'blueprints-integration' import { - GraphicsContent, - IBlueprintActionManifest, - IBlueprintAdLibPiece, - IBlueprintPiece, - PieceLifespan, - TSR, - WithTimeline -} from 'blueprints-integration' -import { calculateTime, CueDefinitionBackgroundLoop, literal, ShowStyleContext, TV2ShowStyleConfig } from 'tv2-common' + calculateTime, + CueDefinitionBackgroundLoop, + DveLoopGenerator, + EvaluateCueResult, + literal, + ShowStyleContext, + TV2ShowStyleConfig +} from 'tv2-common' import { SharedGraphicLLayer, SharedOutputLayer } from 'tv2-constants' -import { CasparLLayer } from '../../../tv2_afvd_studio/layers' import { SourceLayer } from '../../layers' export function EvaluateCueBackgroundLoop( context: ShowStyleContext, - pieces: IBlueprintPiece[], - adlibPieces: IBlueprintAdLibPiece[], - _actions: IBlueprintActionManifest[], partId: string, parsedCue: CueDefinitionBackgroundLoop, adlib?: boolean, rank?: number -) { +): EvaluateCueResult { + const result = new EvaluateCueResult() const start = (parsedCue.start ? calculateTime(parsedCue.start) : 0) ?? 0 if (parsedCue.target === 'DVE') { + const dveLoopGenerator = new DveLoopGenerator() // todo: where to instantiate it? const fileName = parsedCue.backgroundLoop const path = `dve/${fileName}` if (adlib) { - adlibPieces.push({ - _rank: rank || 0, + result.adlibPieces.push({ + _rank: rank ?? 0, externalId: partId, name: fileName, outputLayerId: SharedOutputLayer.SEC, sourceLayerId: SourceLayer.PgmDVEBackground, - lifespan: PieceLifespan.OutOnShowStyleEnd, + lifespan: PieceLifespan.OutOnRundownChange, content: literal>({ fileName, path, ignoreMediaObjectStatus: true, - timelineObjects: dveLoopTimeline(path) + timelineObjects: dveLoopGenerator.createDveLoopTimelineObject(fileName) }) }) } else { - pieces.push({ + result.pieces.push({ externalId: partId, name: fileName, enable: { @@ -51,25 +49,25 @@ export function EvaluateCueBackgroundLoop( }, outputLayerId: SharedOutputLayer.SEC, sourceLayerId: SourceLayer.PgmDVEBackground, - lifespan: PieceLifespan.OutOnShowStyleEnd, + lifespan: PieceLifespan.OutOnRundownChange, content: literal>({ fileName, path, ignoreMediaObjectStatus: true, - timelineObjects: dveLoopTimeline(path) + timelineObjects: dveLoopGenerator.createDveLoopTimelineObject(fileName) }) }) } } else { // Full if (adlib) { - adlibPieces.push({ - _rank: rank || 0, + result.adlibPieces.push({ + _rank: rank ?? 0, externalId: partId, name: parsedCue.backgroundLoop, outputLayerId: SharedOutputLayer.SEC, sourceLayerId: SourceLayer.PgmFullBackground, - lifespan: PieceLifespan.OutOnShowStyleEnd, + lifespan: PieceLifespan.OutOnRundownChange, content: literal>({ fileName: parsedCue.backgroundLoop, path: parsedCue.backgroundLoop, @@ -78,7 +76,7 @@ export function EvaluateCueBackgroundLoop( }) }) } else { - pieces.push({ + result.pieces.push({ externalId: partId, name: parsedCue.backgroundLoop, enable: { @@ -86,7 +84,7 @@ export function EvaluateCueBackgroundLoop( }, outputLayerId: SharedOutputLayer.SEC, sourceLayerId: SourceLayer.PgmFullBackground, - lifespan: PieceLifespan.OutOnShowStyleEnd, + lifespan: PieceLifespan.OutOnRundownChange, content: literal>({ fileName: parsedCue.backgroundLoop, path: parsedCue.backgroundLoop, @@ -96,23 +94,7 @@ export function EvaluateCueBackgroundLoop( }) } } -} - -function dveLoopTimeline(path: string): TSR.TSRTimelineObj[] { - return [ - literal({ - id: '', - enable: { start: 0 }, - priority: 100, - layer: CasparLLayer.CasparCGDVELoop, - content: { - deviceType: TSR.DeviceType.CASPARCG, - type: TSR.TimelineContentTypeCasparCg.MEDIA, - file: path, - loop: true - } - }) - ] + return result } function fullLoopTimeline(config: TV2ShowStyleConfig, parsedCue: CueDefinitionBackgroundLoop): TSR.TSRTimelineObj[] { diff --git a/src/tv2_afvd_studio/__tests__/graphics.spec.ts b/src/tv2_afvd_studio/__tests__/graphics.spec.ts index 309f81ab..bfe7983a 100644 --- a/src/tv2_afvd_studio/__tests__/graphics.spec.ts +++ b/src/tv2_afvd_studio/__tests__/graphics.spec.ts @@ -188,7 +188,7 @@ describe('Graphics', () => { expect(piece.outputLayerId).toBe(SharedOutputLayer.OVERLAY) expect(piece.enable).toEqual({ start: 2000 }) expect(piece.prerollDuration).toBe(context.config.studio.VizPilotGraphics.PrerollDuration) - expect(piece.lifespan).toBe(PieceLifespan.OutOnShowStyleEnd) + expect(piece.lifespan).toBe(PieceLifespan.OutOnRundownChange) const content = piece.content! const timeline = content.timelineObjects as TSR.TSRTimelineObj[] expect(timeline).toHaveLength(1) @@ -244,7 +244,7 @@ describe('Graphics', () => { expect(piece.outputLayerId).toBe(SharedOutputLayer.SEC) expect(piece.enable).toEqual({ start: 0 }) expect(piece.prerollDuration).toBe(context.config.studio.VizPilotGraphics.PrerollDuration) - expect(piece.lifespan).toBe(PieceLifespan.OutOnShowStyleEnd) + expect(piece.lifespan).toBe(PieceLifespan.OutOnRundownChange) const content = piece.content! const timeline = content.timelineObjects as TSR.TSRTimelineObj[] expect(timeline).toHaveLength(1) @@ -397,10 +397,40 @@ describe('Graphics', () => { expect(piece).toBeTruthy() expect(piece.outputLayerId).toBe(SharedOutputLayer.SEC) expect(piece.sourceLayerId).toBe(SourceLayer.PgmDesign) - expect(piece.lifespan).toBe(PieceLifespan.OutOnShowStyleEnd) + expect(piece.lifespan).toBe(PieceLifespan.OutOnRundownChange) expect(piece.enable).toEqual({ start: 0 }) }) + test('Design from field(column) has OutOnRundownChangeWithSegmentLookback lifespan', async () => { + const context = makeMockGalleryContext() + + const cues: CueDefinition[] = [ + literal({ + type: CueType.GraphicDesign, + design: 'DESIGN_FODBOLD', + iNewsCommand: 'KG', + isFromField: true + }) + ] + + const partDefinition: PartDefinition = literal({ + type: PartType.Unknown, + externalId: '', + segmentExternalId: SEGMENT_EXTERNAL_ID, + rawType: '', + cues, + script: '', + fields: {}, + modified: 0, + storyName: '' + }) + + const result = await CreatePartUnknown(context, partDefinition, 0) + expect(result.pieces).toHaveLength(1) + const piece = result.pieces[0] + expect(piece.lifespan).toBe('rundown-change-segment-lookback') + }) + it('Creates background loop', async () => { const context = makeMockGalleryContext() @@ -432,7 +462,7 @@ describe('Graphics', () => { expect(piece.name).toBe('DESIGN_SC') expect(piece.outputLayerId).toBe(SharedOutputLayer.SEC) expect(piece.sourceLayerId).toBe(SourceLayer.PgmDVEBackground) - expect(piece.lifespan).toBe(PieceLifespan.OutOnShowStyleEnd) + expect(piece.lifespan).toBe(PieceLifespan.OutOnRundownChange) const tlObj = (piece.content?.timelineObjects as TSR.TSRTimelineObj[]).find( (obj) => obj.content.deviceType === TSR.DeviceType.CASPARCG && obj.content.type === TSR.TimelineContentTypeCasparCg.MEDIA diff --git a/src/tv2_offtube_showstyle/cues/OfftubeGraphicBackgroundLoop.ts b/src/tv2_offtube_showstyle/cues/OfftubeGraphicBackgroundLoop.ts index 57db7ea1..d643d7eb 100644 --- a/src/tv2_offtube_showstyle/cues/OfftubeGraphicBackgroundLoop.ts +++ b/src/tv2_offtube_showstyle/cues/OfftubeGraphicBackgroundLoop.ts @@ -1,62 +1,45 @@ +import { GraphicsContent, PieceLifespan, WithTimeline } from 'blueprints-integration' import { - GraphicsContent, - IBlueprintActionManifest, - IBlueprintAdLibPiece, - IBlueprintPiece, - PieceLifespan, - TSR, - WithTimeline -} from 'blueprints-integration' -import { calculateTime, CueDefinitionBackgroundLoop, literal, SegmentContext } from 'tv2-common' + calculateTime, + CueDefinitionBackgroundLoop, + DveLoopGenerator, + EvaluateCueResult, + literal, + SegmentContext +} from 'tv2-common' import { SharedOutputLayer } from 'tv2-constants' -import _ = require('underscore') -import { OfftubeCasparLLayer } from '../../tv2_offtube_studio/layers' import { OfftubeBlueprintConfig } from '../helpers/config' import { OfftubeSourceLayer } from '../layers' export function OfftubeEvaluateCueBackgroundLoop( _context: SegmentContext, - pieces: IBlueprintPiece[], - adlibPieces: IBlueprintAdLibPiece[], - _actions: IBlueprintActionManifest[], partId: string, parsedCue: CueDefinitionBackgroundLoop, adlib?: boolean, rank?: number -) { +): EvaluateCueResult { + const result = new EvaluateCueResult() + const dveLoopGenerator = new DveLoopGenerator() const fileName = parsedCue.backgroundLoop const path = `dve/${fileName}` const start = (parsedCue.start ? calculateTime(parsedCue.start) : 0) ?? 0 if (adlib) { - adlibPieces.push({ - _rank: rank || 0, + result.adlibPieces.push({ + _rank: rank ?? 0, externalId: partId, name: fileName, outputLayerId: SharedOutputLayer.SEC, sourceLayerId: OfftubeSourceLayer.PgmDVEBackground, - lifespan: PieceLifespan.OutOnShowStyleEnd, + lifespan: PieceLifespan.OutOnRundownChange, content: literal>({ fileName, path, ignoreMediaObjectStatus: true, - timelineObjects: _.compact([ - literal({ - id: '', - enable: { start: 0 }, - priority: 100, - layer: OfftubeCasparLLayer.CasparCGDVELoop, - content: { - deviceType: TSR.DeviceType.CASPARCG, - type: TSR.TimelineContentTypeCasparCg.MEDIA, - file: path, - loop: true - } - }) - ]) + timelineObjects: dveLoopGenerator.createDveLoopTimelineObject(fileName) }) }) } else { - pieces.push({ + result.pieces.push({ externalId: partId, name: fileName, enable: { @@ -64,26 +47,14 @@ export function OfftubeEvaluateCueBackgroundLoop( }, outputLayerId: SharedOutputLayer.SEC, sourceLayerId: OfftubeSourceLayer.PgmDVEBackground, - lifespan: PieceLifespan.OutOnShowStyleEnd, + lifespan: PieceLifespan.OutOnRundownChange, content: literal>({ fileName, path, ignoreMediaObjectStatus: true, - timelineObjects: _.compact([ - literal({ - id: '', - enable: { start: 0 }, - priority: 100, - layer: OfftubeCasparLLayer.CasparCGDVELoop, - content: { - deviceType: TSR.DeviceType.CASPARCG, - type: TSR.TimelineContentTypeCasparCg.MEDIA, - file: path, - loop: true - } - }) - ]) + timelineObjects: dveLoopGenerator.createDveLoopTimelineObject(fileName) }) }) } + return result } diff --git a/src/tv2_offtube_showstyle/cues/OfftubeGraphicDesign.ts b/src/tv2_offtube_showstyle/cues/OfftubeGraphicDesign.ts index 2b75fe10..0b57ab91 100644 --- a/src/tv2_offtube_showstyle/cues/OfftubeGraphicDesign.ts +++ b/src/tv2_offtube_showstyle/cues/OfftubeGraphicDesign.ts @@ -1,16 +1,12 @@ -import { IBlueprintActionManifest, IBlueprintAdLibPiece, IBlueprintPiece } from 'blueprints-integration' import { CueDefinitionGraphicDesign, EvaluateDesignBase, SegmentContext } from 'tv2-common' import { OfftubeBlueprintConfig } from '../helpers/config' export function OfftubeEvaluateGraphicDesign( context: SegmentContext, - pieces: IBlueprintPiece[], - adlibPieces: IBlueprintAdLibPiece[], - actions: IBlueprintActionManifest[], partId: string, parsedCue: CueDefinitionGraphicDesign, adlib?: boolean, rank?: number ) { - EvaluateDesignBase(context, pieces, adlibPieces, actions, partId, parsedCue, adlib, rank) + return EvaluateDesignBase(context, partId, parsedCue, adlib, rank) } diff --git a/src/tv2_offtube_showstyle/getRundown.ts b/src/tv2_offtube_showstyle/getRundown.ts index b2335f33..19310312 100644 --- a/src/tv2_offtube_showstyle/getRundown.ts +++ b/src/tv2_offtube_showstyle/getRundown.ts @@ -587,7 +587,7 @@ function getGlobalAdlibActionsOfftube( function getBaseline(context: ShowStyleContext): BlueprintResultBaseline { return { timelineObjects: _.compact([ - ...getGraphicBaseline(context.config), + ...getGraphicBaseline(context), // Default timeline context.videoSwitcher.getMixEffectTimelineObject({ layer: context.uniformConfig.mixEffects.program.mixEffectLayer, @@ -713,7 +713,7 @@ function getBaseline(context: ShowStyleContext): Bluepri } } }), - ...gfxSchemaGenerator.createTimelineObjectsFromGfxDefaults(context), + ...gfxSchemaGenerator.createBaselineTimelineObjectsFromGfxDefaults(context), literal({ id: '', From 7c50e0d93f5610d1fc901aa6ab84d31347c8ce83 Mon Sep 17 00:00:00 2001 From: ianshade Date: Mon, 3 Jul 2023 09:16:49 +0200 Subject: [PATCH 27/35] chore: SOF-1420 remove duplicated import --- src/tv2-common/migrations/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tv2-common/migrations/index.ts b/src/tv2-common/migrations/index.ts index e12a8306..66868c3d 100644 --- a/src/tv2-common/migrations/index.ts +++ b/src/tv2-common/migrations/index.ts @@ -1,5 +1,6 @@ -import { BasicConfigItemValue, IBlueprintShowStyleVariant } from 'blueprints-integration' import { + BasicConfigItemValue, + IBlueprintShowStyleVariant, BlueprintMappings, ConfigItemValue, MigrationContextShowStyle, From cc3652c077ce021a7ad6ab72467bc218683ddba8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Frederik=20J=C3=B8rgensen?= Date: Mon, 10 Jul 2023 08:18:47 +0200 Subject: [PATCH 28/35] refactor: Changd variable names c and p to cue and part for EvaluateCueGraphicSchema. --- src/tv2-common/migrations/index.ts | 2 +- src/tv2_afvd_showstyle/helpers/pieces/evaluateCues.ts | 4 ++-- src/tv2_offtube_showstyle/helpers/EvaluateCues.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tv2-common/migrations/index.ts b/src/tv2-common/migrations/index.ts index 66868c3d..36dd4a12 100644 --- a/src/tv2-common/migrations/index.ts +++ b/src/tv2-common/migrations/index.ts @@ -1,8 +1,8 @@ import { BasicConfigItemValue, - IBlueprintShowStyleVariant, BlueprintMappings, ConfigItemValue, + IBlueprintShowStyleVariant, MigrationContextShowStyle, MigrationContextStudio, MigrationStepShowStyle, diff --git a/src/tv2_afvd_showstyle/helpers/pieces/evaluateCues.ts b/src/tv2_afvd_showstyle/helpers/pieces/evaluateCues.ts index 1bd2778a..52e5ae2d 100644 --- a/src/tv2_afvd_showstyle/helpers/pieces/evaluateCues.ts +++ b/src/tv2_afvd_showstyle/helpers/pieces/evaluateCues.ts @@ -51,8 +51,8 @@ export async function EvaluateCues( EvaluateCueGraphic, EvaluateCueBackgroundLoop, EvaluateCueGraphicDesign: EvaluateCueDesign, - EvaluateCueGraphicSchema: (c, p, partId, parsedCue) => - GfxSchemaGeneratorFacade.create().createBlueprintPieceFromGfxSchemaCue(c, p, partId, parsedCue), + EvaluateCueGraphicSchema: (cue, piece, partId, parsedCue) => + GfxSchemaGeneratorFacade.create().createBlueprintPieceFromGfxSchemaCue(cue, piece, partId, parsedCue), EvaluateCueRouting, EvaluateCueMixMinus, EvaluateCueRobotCamera diff --git a/src/tv2_offtube_showstyle/helpers/EvaluateCues.ts b/src/tv2_offtube_showstyle/helpers/EvaluateCues.ts index d70fd3e0..38b8e788 100644 --- a/src/tv2_offtube_showstyle/helpers/EvaluateCues.ts +++ b/src/tv2_offtube_showstyle/helpers/EvaluateCues.ts @@ -46,8 +46,8 @@ export async function OfftubeEvaluateCues( EvaluateCueGraphicDesign: OfftubeEvaluateGraphicDesign, EvaluateCuePgmClean: OfftubeEvaluatePgmClean, EvaluateCueLYD: EvaluateLYD, - EvaluateCueGraphicSchema: (c, p, partId, parsedCue) => - GfxSchemaGeneratorFacade.create().createBlueprintPieceFromGfxSchemaCue(c, p, partId, parsedCue) + EvaluateCueGraphicSchema: (cue, piece, partId, parsedCue) => + GfxSchemaGeneratorFacade.create().createBlueprintPieceFromGfxSchemaCue(cue, piece, partId, parsedCue) }, context, part, From 6d139c9de386037c6b89891180db831d77a7126f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Frederik=20J=C3=B8rgensen?= Date: Mon, 10 Jul 2023 08:58:12 +0200 Subject: [PATCH 29/35] fix: Renamed CasparCgGfxDesignValues cssRules to properties. cssRules is the old attribute, which used a list of rules. The new properties attribute uses a record to model css attributes more accurately. --- src/tv2-common/__tests__/gfx-schema-generator.spec.ts | 6 +++++- src/tv2-common/blueprintConfig.ts | 4 ++-- src/tv2-common/cues/gfx-schema-generator.ts | 1 + .../caspar/__tests__/dve-loop-generator.spec.ts | 10 ++++++---- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/tv2-common/__tests__/gfx-schema-generator.spec.ts b/src/tv2-common/__tests__/gfx-schema-generator.spec.ts index 3bc56a2a..ac2bd1ba 100644 --- a/src/tv2-common/__tests__/gfx-schema-generator.spec.ts +++ b/src/tv2-common/__tests__/gfx-schema-generator.spec.ts @@ -169,7 +169,11 @@ describe('GfxSchemaGenerator', () => { { name: 'designName', backgroundLoop: 'someBackgroundLoop', - cssRules: ['rule1', 'rule2', 'rule3'] + properties: { + attribute1: 'value1', + attribute2: 'value2', + attribute3: 'value3' + } } ] const context = new MockContextBuilder() diff --git a/src/tv2-common/blueprintConfig.ts b/src/tv2-common/blueprintConfig.ts index 7b658ca9..37395595 100644 --- a/src/tv2-common/blueprintConfig.ts +++ b/src/tv2-common/blueprintConfig.ts @@ -34,7 +34,7 @@ export interface TableConfigItemGfxDesignTemplate { _id: string INewsName: string INewsStyleColumn: string - /** Name of the Viz template trigering design change. For HTML graphics it coresponds to a CSS class. */ + /** Name of the Viz template triggering design change. For HTML graphics it corresponds to a CSS class. */ VizTemplate: string } @@ -72,7 +72,7 @@ export interface TableConfigGfxSetup { export interface CasparCgGfxDesignValues { name: string - cssRules: string[] + properties: Record backgroundLoop: string } diff --git a/src/tv2-common/cues/gfx-schema-generator.ts b/src/tv2-common/cues/gfx-schema-generator.ts index 2fb60c00..a89932fa 100644 --- a/src/tv2-common/cues/gfx-schema-generator.ts +++ b/src/tv2-common/cues/gfx-schema-generator.ts @@ -179,6 +179,7 @@ export class GfxSchemaGenerator { }) } + // TODO: Remove any from return type private createCasparCgHtmlSchemaData(context: ShowStyleContext, cue: CueDefinitionGfxSchema): any { if (!cue.CasparCgDesignValues || cue.CasparCgDesignValues.length === 0) { context.core.notifyUserError( diff --git a/src/tv2-common/helpers/graphics/caspar/__tests__/dve-loop-generator.spec.ts b/src/tv2-common/helpers/graphics/caspar/__tests__/dve-loop-generator.spec.ts index 557aac7e..6b09c842 100644 --- a/src/tv2-common/helpers/graphics/caspar/__tests__/dve-loop-generator.spec.ts +++ b/src/tv2-common/helpers/graphics/caspar/__tests__/dve-loop-generator.spec.ts @@ -41,7 +41,9 @@ describe('DveLoopGenerator', () => { type: CueType.GraphicSchema, schema: 'randomSchema', iNewsCommand: '', - CasparCgDesignValues: [{ name: 'someName', backgroundLoop: 'someLoop', cssRules: ['someRule'] }] + CasparCgDesignValues: [ + { name: 'someName', backgroundLoop: 'someLoop', properties: { someAttribute: 'someValue' } } + ] } const testee: DveLoopGenerator = new DveLoopGenerator() @@ -60,7 +62,7 @@ describe('DveLoopGenerator', () => { CasparCgDesignValues: new Array(5).map(() => ({ name: 'someName', backgroundLoop: 'someLoop', - cssRules: ['someRule'] + properties: { someAttribute: 'someValue' } })) } @@ -83,7 +85,7 @@ describe('DveLoopGenerator', () => { { name: designName, backgroundLoop: 'someLoop', - cssRules: [] + properties: {} } ] } @@ -118,7 +120,7 @@ describe('DveLoopGenerator', () => { { name: 'someDesignName', backgroundLoop: 'someLoop', - cssRules: [] + properties: {} } ] } From 764fbfcd0d7c4a4d4f04d1afaf0ba33cbbcfc851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Frederik=20J=C3=B8rgensen?= Date: Wed, 12 Jul 2023 10:27:11 +0200 Subject: [PATCH 30/35] chore: Updated package version to 1.8.3. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cd9998b8..77d35b99 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tv2-sofie-blueprints-inews", - "version": "1.8.2", + "version": "1.8.3", "repository": "https://github.com/olzzon/tv2-sofie-blueprints-inews", "license": "MIT", "private": true, From a3de3316ef6374b63c5ec6b2f8ff67ec9c16298e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Frederik=20J=C3=B8rgensen?= Date: Thu, 13 Jul 2023 10:05:38 +0200 Subject: [PATCH 31/35] chore: Updated missing version numbers for version integration test. --- config/webpack.config.js | 2 +- package.json | 2 +- yarn.lock | 28 ++++++++++++++-------------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/config/webpack.config.js b/config/webpack.config.js index 03dc7248..87482897 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -30,7 +30,7 @@ module.exports = env => { // versionIntegration = versionIntegration.replace(/[^\d.]/g, '') || '0.0.0' versionTSRTypes = '1.3.0' - versionIntegration = '46.2.0' + versionIntegration = '46.3.0' const entrypoints = env.bundle ? GetEntrypointsForBundle(env.bundle) : BlueprintEntrypoints diff --git a/package.json b/package.json index 77d35b99..eadfc7ca 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "webpack-cli": "^3.1.2" }, "dependencies": { - "@sofie-automation/blueprints-integration": "npm:@tv2media/blueprints-integration@46.2.2", + "@sofie-automation/blueprints-integration": "npm:@tv2media/blueprints-integration@46.3.0-staging", "ts-mockito": "^2.6.1", "underscore": "^1.12.1" }, diff --git a/yarn.lock b/yarn.lock index 6be711a0..6bf2a93e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -560,20 +560,20 @@ dependencies: "@sinonjs/commons" "^1.7.0" -"@sofie-automation/blueprints-integration@npm:@tv2media/blueprints-integration@46.2.2": - version "46.2.2" - resolved "https://registry.yarnpkg.com/@tv2media/blueprints-integration/-/blueprints-integration-46.2.2.tgz#74c0533e79585755cd61397b531689754fd2ef6d" - integrity sha512-IjHlVaE0IuLp7R8mxIWz5AbnlC8EExtHHWt/F8tbbjSyKYeBahxS0asnbMFnDYMlOZznNrunT4nBy8vwGfTHhg== +"@sofie-automation/blueprints-integration@npm:@tv2media/blueprints-integration@46.3.0-staging": + version "46.3.0-staging" + resolved "https://registry.yarnpkg.com/@tv2media/blueprints-integration/-/blueprints-integration-46.3.0-staging.tgz#2aaf96cae9876e051016fb5c6f386d35fbfc1e99" + integrity sha512-VC+/iM8QKq95rGMsb4kQU3MVPnt9R3wxgll/Ni5KtWeZ+qgfJPz4d6Cqj16ADYiHppUyBehEyU27Z8euMG1f6Q== dependencies: - "@sofie-automation/shared-lib" "npm:@tv2media/shared-lib@46.2.0" - timeline-state-resolver-types "npm:@tv2media/timeline-state-resolver-types@3.5.0" + "@sofie-automation/shared-lib" "npm:@tv2media/shared-lib@46.3.0-staging" + timeline-state-resolver-types "npm:@tv2media/timeline-state-resolver-types@3.5.1" tslib "^2.4.0" type-fest "^2.19.0" -"@sofie-automation/shared-lib@npm:@tv2media/shared-lib@46.2.0": - version "46.2.0" - resolved "https://registry.yarnpkg.com/@tv2media/shared-lib/-/shared-lib-46.2.0.tgz#934f14339330bd0064d7a0c3046dae10e2754099" - integrity sha512-ojW4qHQ+dfQDzFotqq4kEJNb6nVSDk/KU2u1S2fzXkZzgXW4Xq2hNUyk5lfUMcd7nBsiRVnPzmVSqxbRfUd1MQ== +"@sofie-automation/shared-lib@npm:@tv2media/shared-lib@46.3.0-staging": + version "46.3.0-staging" + resolved "https://registry.yarnpkg.com/@tv2media/shared-lib/-/shared-lib-46.3.0-staging.tgz#405ddd324e7f66f472635258915eb026054b8406" + integrity sha512-LYPJEXNO4NNiCxB/PsqW1BedtLjvBFntIUfVKwfuizkZJkYnJ8JZQdrUcxgyCJg5/SYeSccUrhorI4YKDqcx8w== dependencies: tslib "^2.4.0" type-fest "^2.19.0" @@ -6228,10 +6228,10 @@ through@2, "through@>=2.2.7 <3", through@^2.3.8: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= -"timeline-state-resolver-types@npm:@tv2media/timeline-state-resolver-types@3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@tv2media/timeline-state-resolver-types/-/timeline-state-resolver-types-3.5.0.tgz#02ca12128cbc0df84e630d0da8c66a1d44e5f172" - integrity sha512-DdKXdQf+By8ssBKqd3qfAAhERehlKTorWb3tYK6G6z1vmADSJT+MVAvY0GpLx5/qKe0e3HbutQ2CMlh7b00QIA== +"timeline-state-resolver-types@npm:@tv2media/timeline-state-resolver-types@3.5.1": + version "3.5.1" + resolved "https://registry.yarnpkg.com/@tv2media/timeline-state-resolver-types/-/timeline-state-resolver-types-3.5.1.tgz#9362ec90183babb8183914a7e36664218e2eb5e1" + integrity sha512-yBAI2d7cnWPuDtFsYkbFB/GBkN4p0Q7Ne4TqKCDl4haqFU4PNtIzI1shH9AOEMOcsmNh0ZxHTzgAG85zrGGXpQ== dependencies: tslib "^2.3.1" From a5ee3b6315d4d494a9991c2ec03a7e1b4e22dfca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Frederik=20J=C3=B8rgensen?= Date: Tue, 18 Jul 2023 15:45:51 +0200 Subject: [PATCH 32/35] fix: Make DVE locators layer mapping available for locators. Currently the locators are mapped as an ident, which causes the locators to not being displayed. --- src/tv2_afvd_showstyle/config-manifests.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tv2_afvd_showstyle/config-manifests.ts b/src/tv2_afvd_showstyle/config-manifests.ts index 319fbc8e..2c679470 100644 --- a/src/tv2_afvd_showstyle/config-manifests.ts +++ b/src/tv2_afvd_showstyle/config-manifests.ts @@ -275,7 +275,7 @@ export const showStyleConfigManifest: ConfigManifestEntry[] = [ 'The Sofie Layer mapping to use in playback. This will ensure proper viz transition logic by matching the viz layers.', type: ConfigManifestEntryType.LAYER_MAPPINGS, filters: { - deviceTypes: [TSR.DeviceType.VIZMSE] + deviceTypes: [TSR.DeviceType.VIZMSE, TSR.DeviceType.CASPARCG] }, multiple: false, required: true, From 6ceb2b45edaedebb27db903fbf684571745071d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Frederik=20J=C3=B8rgensen?= Date: Tue, 18 Jul 2023 15:48:07 +0200 Subject: [PATCH 33/35] wip: Set locator type in DVE templates.DveGraphicsTemplateJson. --- src/tv2-common/content/dve.ts | 45 ++++++++++++++----- .../helpers/graphics/caspar/util.ts | 20 ++++++--- 2 files changed, 50 insertions(+), 15 deletions(-) diff --git a/src/tv2-common/content/dve.ts b/src/tv2-common/content/dve.ts index 493e3cd7..447fda0f 100644 --- a/src/tv2-common/content/dve.ts +++ b/src/tv2-common/content/dve.ts @@ -114,6 +114,8 @@ export interface DVEOptions { type BoxConfig = DVEConfigBox & { source: number } type BoxSources = Array<(VTContent | CameraContent | RemoteContent | GraphicsContent) & SplitsContentBoxProperties> +const DEFAULT_LOCATOR_TYPE = 'locators' + export function MakeContentDVEBase< StudioConfig extends TV2StudioConfigBase, ShowStyleConfig extends TV2BlueprintConfigBase @@ -275,14 +277,9 @@ export function MakeContentDVE2< } }) - let graphicsTemplateStyle: any = '' - try { - if (dveConfig.DVEGraphicsTemplateJSON) { - graphicsTemplateStyle = JSON.parse(dveConfig.DVEGraphicsTemplateJSON.toString()) - } - } catch { - context.core.notifyUserWarning(`DVE Graphics Template JSON is not valid for ${dveConfig.DVEName}`) - } + const graphicsTemplate = getDveGraphicsTemplate(dveConfig, context.core.notifyUserWarning) + const graphicsTemplateStyle = getDveGraphicsTemplateStyle(graphicsTemplate) + const locatorType = getDveLocatorType(graphicsTemplate) let keyFile = dveConfig.DVEGraphicsKey ? dveConfig.DVEGraphicsKey.toString() : undefined let frameFile = dveConfig.DVEGraphicsFrame ? dveConfig.DVEGraphicsFrame.toString() : undefined @@ -331,9 +328,9 @@ export function MakeContentDVE2< enable: { start: 0 }, priority: 1, layer: SharedGraphicLLayer.GraphicLLayerLocators, - content: CreateHTMLRendererContent(context.config, 'locators', { + content: CreateHTMLRendererContent(context.config, locatorType, { ...graphicsTemplateContent, - style: graphicsTemplateStyle ?? {} + style: graphicsTemplateStyle }) }), ...(keyFile @@ -378,6 +375,34 @@ export function MakeContentDVE2< } } +function getDveGraphicsTemplate(dveConfigInput: DVEConfigInput, notifyUserWarning: (message: string) => void): object { + try { + const dveGraphicsTemplate = JSON.parse(dveConfigInput.DVEGraphicsTemplateJSON) + if (!isValidDveGraphicsTemplate(dveGraphicsTemplate)) { + notifyUserWarning(`DVE Graphics Template for ${dveConfigInput.DVEName} is invalid.`) + return {} + } + return dveGraphicsTemplate + } catch { + notifyUserWarning(`DVE Graphics Template JSON for ${dveConfigInput.DVEName} is ill-formed.`) + return {} + } +} + +function isValidDveGraphicsTemplate(dveGraphicsTemplate: unknown): dveGraphicsTemplate is object { + return typeof dveGraphicsTemplate === 'object' && dveGraphicsTemplate !== null +} + +function getDveGraphicsTemplateStyle(dveGraphicsTemplate: { locatorType?: string }): object { + const { locatorType, ...dveGraphicsTemplateStyle } = dveGraphicsTemplate + return dveGraphicsTemplateStyle +} + +function getDveLocatorType(dveGraphicsTemplate: { locatorType?: string }): string { + const { locatorType } = dveGraphicsTemplate + return locatorType ?? DEFAULT_LOCATOR_TYPE +} + const setBoxSource = ( boxConfig: BoxConfig, boxSources: BoxSources, diff --git a/src/tv2-common/helpers/graphics/caspar/util.ts b/src/tv2-common/helpers/graphics/caspar/util.ts index 00223d0e..29e64662 100644 --- a/src/tv2-common/helpers/graphics/caspar/util.ts +++ b/src/tv2-common/helpers/graphics/caspar/util.ts @@ -12,7 +12,7 @@ import { SharedGraphicLLayer } from 'tv2-constants' export function CreateHTMLRendererContent( config: TV2ShowStyleConfig, - mappedTemplate: string, + graphicTemplateName: string, data: object ): TSR.TimelineObjCCGTemplate['content'] { return { @@ -22,7 +22,7 @@ export function CreateHTMLRendererContent( name: getHtmlTemplateName(config), data: { display: 'program', - slots: getHtmlTemplateContent(config, mappedTemplate, data), + slots: getHtmlTemplateContent(config, graphicTemplateName, data), partialUpdate: true }, useStopCommand: false, @@ -32,12 +32,22 @@ export function CreateHTMLRendererContent( } } +function getMappedGraphicsTemplateName(templateName: string): string { + switch (templateName) { + case 'locators-afvb': + return 'locators' + default: + return templateName + } +} + export function getHtmlTemplateContent( config: TV2ShowStyleConfig, - graphicTemplate: string, + graphicTemplateName: string, data: object ): Partial { - const layer = getTimelineLayerForGraphic(config, graphicTemplate) + const mappedGraphicTemplateName = getMappedGraphicsTemplateName(graphicTemplateName) + const layer = getTimelineLayerForGraphic(config, mappedGraphicTemplateName) const slot = layerToHTMLGraphicSlot[layer] @@ -49,7 +59,7 @@ export function getHtmlTemplateContent( [slot]: { display: 'program', payload: { - type: graphicTemplate, + type: graphicTemplateName, ...data } } From 83b4bad85df79021ef88539215e1c821b5a93243 Mon Sep 17 00:00:00 2001 From: Rasmus Lindved Date: Wed, 22 Nov 2023 07:05:14 +0100 Subject: [PATCH 34/35] SOF-1705 If the Design is 'N/A' we no longer show an error message --- src/tv2-common/helpers/graphics/design/index.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/tv2-common/helpers/graphics/design/index.ts b/src/tv2-common/helpers/graphics/design/index.ts index 30a5df89..e2f1eb5b 100644 --- a/src/tv2-common/helpers/graphics/design/index.ts +++ b/src/tv2-common/helpers/graphics/design/index.ts @@ -17,7 +17,8 @@ import { } from 'tv2-common' import { SharedGraphicLLayer, SharedOutputLayer, SharedSourceLayer } from 'tv2-constants' -const NON_BASELINE_DESIGN = 'NON_BASELINE_DESIGN' +const NON_BASELINE_DESIGN: string = 'NON_BASELINE_DESIGN' +const VALID_EMPTY_DESIGN_VALUE: string = 'N/A' export function EvaluateDesignBase( context: ShowStyleContext, @@ -156,6 +157,9 @@ function getVizDesignTimelineObject(config: TV2ShowStyleConfig, design: string): export function getVizBaselineDesignTimelineObject(context: ShowStyleContext) { const designReference = context.config.showStyle.GfxDefaults[0].DefaultDesign + if (VALID_EMPTY_DESIGN_VALUE === designReference.value) { + return [] + } const design = context.config.showStyle.GfxDesignTemplates.find( (designTemplate) => designTemplate._id === designReference.value ) @@ -171,6 +175,9 @@ export function getCasparCgBaselineDesignTimelineObjects( templateName: string ): TSR.TimelineObjCCGTemplate[] { const designReference = context.config.showStyle.GfxDefaults[0].DefaultDesign + if (VALID_EMPTY_DESIGN_VALUE === designReference.value) { + return [] + } const design = context.config.showStyle.GfxDesignTemplates.find( (designTemplate) => designTemplate._id === designReference.value ) From f9bd632bc68b86c516ec5172ad6f73487baf0eee Mon Sep 17 00:00:00 2001 From: Rasmus Lindved Date: Wed, 22 Nov 2023 07:14:17 +0100 Subject: [PATCH 35/35] SOF-1709 When the Schema value is 'N/A' we no longer throw en error --- src/tv2-common/cues/gfx-schema-generator.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/tv2-common/cues/gfx-schema-generator.ts b/src/tv2-common/cues/gfx-schema-generator.ts index a89932fa..020487d3 100644 --- a/src/tv2-common/cues/gfx-schema-generator.ts +++ b/src/tv2-common/cues/gfx-schema-generator.ts @@ -11,13 +11,17 @@ import { import { CueType, SharedGraphicLLayer, SharedOutputLayer, SharedSourceLayer } from 'tv2-constants' import { DveLoopGenerator } from '../helpers/graphics/caspar/dve-loop-generator' -const NON_BASELINE_SCHEMA = 'NON_BASELINE_SCHEMA' +const NON_BASELINE_SCHEMA: string = 'NON_BASELINE_SCHEMA' +const VALID_EMPTY_SCHEMA_VALUE: string = 'N/A' export class GfxSchemaGenerator { constructor(private dveLoopGenerator: DveLoopGenerator) {} public createBaselineTimelineObjectsFromGfxDefaults(context: ShowStyleContext): TSR.TSRTimelineObjBase[] { - const schemaId = context.config.showStyle.GfxDefaults[0].DefaultSchema.value + const schemaId: string = context.config.showStyle.GfxDefaults[0].DefaultSchema.value + if (VALID_EMPTY_SCHEMA_VALUE === schemaId) { + return [] + } const schema: TableConfigGfxSchema | undefined = context.config.showStyle.GfxSchemaTemplates.find( (s) => s._id === schemaId )