Skip to content

Commit

Permalink
feat: refactor singular-live device SOFIE-2492 (#337)
Browse files Browse the repository at this point in the history
  • Loading branch information
Julusian authored Jun 27, 2024
1 parent d220c78 commit 2d08c7c
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 264 deletions.
14 changes: 3 additions & 11 deletions packages/timeline-state-resolver/src/conductor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
DeviceOptionsSofieChef,
DeviceOptionsPharos,
DeviceOptionsTriCaster,
DeviceOptionsSingularLive,
} from 'timeline-state-resolver-types'

import { DoOnTime } from './devices/doOnTime'
Expand All @@ -47,7 +48,6 @@ import { DeviceContainer } from './devices/deviceContainer'

import { CasparCGDevice, DeviceOptionsCasparCGInternal } from './integrations/casparCG'
import { SisyfosMessageDevice, DeviceOptionsSisyfosInternal } from './integrations/sisyfos'
import { SingularLiveDevice, DeviceOptionsSingularLiveInternal } from './integrations/singularLive'
import { VMixDevice, DeviceOptionsVMixInternal } from './integrations/vmix'
import { VizMSEDevice, DeviceOptionsVizMSEInternal } from './integrations/vizMSE'
import { DeviceOptionsMultiOSCInternal, MultiOSCMessageDevice } from './integrations/multiOsc'
Expand Down Expand Up @@ -527,15 +527,6 @@ export class Conductor extends EventEmitter<ConductorEvents> {
getCurrentTime,
threadedClassOptions
)
case DeviceType.SINGULAR_LIVE:
return DeviceContainer.create<DeviceOptionsSingularLiveInternal, typeof SingularLiveDevice>(
'../../dist/integrations/singularLive/index.js',
'SingularLiveDevice',
deviceId,
deviceOptions,
getCurrentTime,
threadedClassOptions
)
case DeviceType.VMIX:
return DeviceContainer.create<DeviceOptionsVMixInternal, typeof VMixDevice>(
'../../dist/integrations/vmix/index.js',
Expand Down Expand Up @@ -565,6 +556,7 @@ export class Conductor extends EventEmitter<ConductorEvents> {
case DeviceType.PANASONIC_PTZ:
case DeviceType.PHAROS:
case DeviceType.SHOTOKU:
case DeviceType.SINGULAR_LIVE:
case DeviceType.SOFIE_CHEF:
case DeviceType.TCPSEND:
case DeviceType.TELEMETRICS:
Expand Down Expand Up @@ -1487,7 +1479,7 @@ export type DeviceOptionsAnyInternal =
| DeviceOptionsSisyfosInternal
| DeviceOptionsSofieChef
| DeviceOptionsQuantel
| DeviceOptionsSingularLiveInternal
| DeviceOptionsSingularLive
| DeviceOptionsVMixInternal
| DeviceOptionsShotoku
| DeviceOptionsVizMSEInternal
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Conductor } from '../../../conductor'
import { SingularLiveDevice } from '..'
import { SingularLiveControlNodeCommandContent, SingularLiveDevice } from '..'
import {
SomeMappingSingularLive,
Mapping,
Expand All @@ -8,21 +7,11 @@ import {
TimelineContentTypeSingularLive,
MappingSingularLiveType,
} from 'timeline-state-resolver-types'
import { MockTime } from '../../../__tests__/mockTime'
import { ThreadedClass } from 'threadedclass'
import { getMockCall } from '../../../__tests__/lib'
import { getDeviceContext } from '../../__tests__/testlib'
import { makeTimelineObjectResolved } from '../../../__mocks__/objects'

// let nowActual = Date.now()
describe('Singular.Live', () => {
const mockTime = new MockTime()
beforeEach(() => {
mockTime.init()
})

test('POST message', async () => {
const commandReceiver0: any = jest.fn(async () => {
return Promise.resolve()
})
const myLayerMapping0: Mapping<SomeMappingSingularLive> = {
device: DeviceType.SINGULAR_LIVE,
deviceId: 'mySingular',
Expand All @@ -35,68 +24,62 @@ describe('Singular.Live', () => {
myLayer0: myLayerMapping0,
}

const myConductor = new Conductor({
multiThreadedResolver: false,
getCurrentTime: mockTime.getCurrentTime,
})
await myConductor.init()
await myConductor.addDevice('mySingular', {
type: DeviceType.SINGULAR_LIVE,
options: {
accessToken: 'DUMMY_TOKEN',
},
commandReceiver: commandReceiver0,
})
myConductor.setTimelineAndMappings([], myLayerMapping)
await mockTime.advanceTimeToTicks(10100)

const deviceContainer = myConductor.getDevice('mySingular')
const device = deviceContainer!.device as ThreadedClass<SingularLiveDevice>
const device = new SingularLiveDevice(getDeviceContext())

// Check that no commands has been scheduled:
expect(await device.queue).toHaveLength(0)

myConductor.setTimelineAndMappings([
const deviceState = device.convertTimelineStateToDeviceState(
{
id: 'obj0',
enable: {
start: mockTime.now + 1000, // in 1 second
duration: 2000,
},
layer: 'myLayer0',
content: {
deviceType: DeviceType.SINGULAR_LIVE,
type: TimelineContentTypeSingularLive.COMPOSITION,
controlNode: {
state: 'In',
payload: {
Name: 'Thomas',
Title: 'Foreperson',
time: 1000,
layers: {
myLayer0: makeTimelineObjectResolved({
id: 'obj0',
enable: {
start: 1000,
duration: 2000,
},
},
layer: 'myLayer0',
content: {
deviceType: DeviceType.SINGULAR_LIVE,
type: TimelineContentTypeSingularLive.COMPOSITION,
controlNode: {
state: 'In',
payload: {
Name: 'Thomas',
Title: 'Foreperson',
},
},
},
}),
},
nextEvents: [],
},
])
await mockTime.advanceTimeToTicks(10990)
expect(commandReceiver0).toHaveBeenCalledTimes(0)
await mockTime.advanceTimeToTicks(11100)
myLayerMapping
)

expect(commandReceiver0).toHaveBeenCalledTimes(1)
expect(commandReceiver0).toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({
subCompositionName: 'Lower Third',
state: 'In',
payload: {
Name: 'Thomas',
Title: 'Foreperson',
},
}),
expect.anything(),
expect.stringContaining('obj0')
const commands = device.diffStates(undefined, deviceState, myLayerMapping, 1000)
expect(commands).toHaveLength(1)
expect(commands[0].command.content).toEqual<SingularLiveControlNodeCommandContent>({
subCompositionName: 'Lower Third',
state: 'In',
payload: {
Name: 'Thomas',
Title: 'Foreperson',
},
})

const deviceState2 = device.convertTimelineStateToDeviceState(
{
time: 2000,
layers: {},
nextEvents: [],
},
myLayerMapping
)
expect(getMockCall(commandReceiver0, 0, 2)).toMatch(/added/) // context
await mockTime.advanceTimeToTicks(16000)
expect(commandReceiver0).toHaveBeenCalledTimes(2)

const commands2 = device.diffStates(deviceState, deviceState2, myLayerMapping, 2000)
expect(commands2).toHaveLength(1)
expect(commands2[0].command.content).toEqual<SingularLiveControlNodeCommandContent>({
subCompositionName: 'Lower Third',
state: 'Out',
})
})
})
Loading

0 comments on commit 2d08c7c

Please sign in to comment.