Skip to content

Commit

Permalink
Merge pull request #213 from tv2/SOF-1420/sendSchemaDesignValuesToCas…
Browse files Browse the repository at this point in the history
…parCG

feat: SOF-1420 send schema design values to CasparCG
  • Loading branch information
KvelaGorrrrnio authored Jul 10, 2023
2 parents 3f4f3bd + 6d139c9 commit f141a3b
Show file tree
Hide file tree
Showing 44 changed files with 1,300 additions and 682 deletions.
2 changes: 1 addition & 1 deletion src/tv2-common/__tests__/frame-time.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ describe('CreateTiming', () => {
enable: {
start: 0
},
lifespan: PieceLifespan.OutOnShowStyleEnd
lifespan: PieceLifespan.OutOnRundownChange
})
)
})
Expand Down
245 changes: 245 additions & 0 deletions src/tv2-common/__tests__/gfx-schema-generator.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
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 - notifies about error', () => {
const context = new MockContextBuilder()
.setGfxDefaults({
DefaultSchema: { label: 'someSchema' }
} as TableConfigItemGfxDefaults)
.build()

const core = mock<IShowStyleUserContext>()
when(context.core).thenReturn(instance(core))

const testee = createTestee()

const result = testee.createBaselineTimelineObjectsFromGfxDefaults(instance(context))
expect(result).toBeTruthy()

verify(core.notifyUserError(anything())).once()
})

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 - notifies about error', () => {
const context = new MockContextBuilder()
.setGfxDefaults({
DefaultSchema: { value: 'someSchema' }
} as TableConfigItemGfxDefaults)
.setGfxSchemaTemplates([
{
GfxSchemaTemplatesName: 'someOtherSchema'
}
] as TableConfigGfxSchema[])
.build()

const core = mock<IShowStyleUserContext>()
when(context.core).thenReturn(instance(core))

const testee = createTestee()
const result = testee.createBaselineTimelineObjectsFromGfxDefaults(instance(context))
expect(result).toBeTruthy()

verify(core.notifyUserError(anything())).once()
})

it('has correctly configured schema - does not notify about error', () => {
const schemaId = 'someSchemaId'
const context = new MockContextBuilder()
.setGfxDefaults({
DefaultSchema: { value: schemaId }
} as TableConfigItemGfxDefaults)
.setGfxSchemaTemplates([
{
_id: schemaId,
GfxSchemaTemplatesName: 'SomeSchema',
CasparCgDesignValues: '[{}]'
}
] as TableConfigGfxSchema[])
.build()

const core = mock<IShowStyleUserContext>()
when(context.core).thenReturn(instance(core))

const testee = createTestee()
const result = testee.createBaselineTimelineObjectsFromGfxDefaults(instance(context))
expect(result).toBeTruthy()

verify(core.notifyUserError(anything())).never()
})

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.createBaselineTimelineObjectsFromGfxDefaults(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.createBaselineTimelineObjectsFromGfxDefaults(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',
properties: {
attribute1: 'value1',
attribute2: 'value2',
attribute3: 'value3'
}
}
]
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.createBaselineTimelineObjectsFromGfxDefaults(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.createBaselineTimelineObjectsFromGfxDefaults(instance(context))

verify(dveLoopGenerator.createCasparCgDveLoopsFromCue(anything(), anything(), anyNumber())).called()
expect(result.includes(casparCgDveLoopTimelineObjects[0])).toBeTruthy()
expect(result.includes(casparCgDveLoopTimelineObjects[1])).toBeTruthy()
})
})
})
})
62 changes: 62 additions & 0 deletions src/tv2-common/__tests__/mock-context-builder.ts
Original file line number Diff line number Diff line change
@@ -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<ShowStyleContext>()

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
}
}
16 changes: 13 additions & 3 deletions src/tv2-common/blueprintConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ 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. */
/** Name of the Viz template triggering design change. For HTML graphics it corresponds to a CSS class. */
VizTemplate: string
}

Expand All @@ -45,18 +46,20 @@ 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 {
Transition: string
}

export interface TableConfigGfxSchema {
_id: string
GfxSchemaTemplatesName: string
INewsSkemaColumn: string
VizTemplate: string
CasparCgDesignValues: string
}

export interface TableConfigGfxSetup {
Expand All @@ -67,6 +70,12 @@ export interface TableConfigGfxSetup {
FullShowName?: string
}

export interface CasparCgGfxDesignValues {
name: string
properties: Record<string, string>
backgroundLoop: string
}

export interface ProcessedStudioConfig {
sources: SourceMapping
mediaPlayers: MediaPlayerConfig // Atem Input Ids
Expand Down Expand Up @@ -180,6 +189,7 @@ export interface TV2ShowstyleBlueprintConfigBase {
LYDConfig: TableConfigItemValue
GfxSchemaTemplates: TableConfigGfxSchema[]
GfxSetups: TableConfigGfxSetup[]
GfxShowMapping: TableConfigItemGfxShowMapping[]
GfxDefaults: TableConfigItemGfxDefaults[]
}

Expand Down
2 changes: 1 addition & 1 deletion src/tv2-common/cueTiming.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}

Expand Down
8 changes: 8 additions & 0 deletions src/tv2-common/cues/gfx-schema-generator-facade.ts
Original file line number Diff line number Diff line change
@@ -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())
}
}
Loading

0 comments on commit f141a3b

Please sign in to comment.