function dayjs() { return new Date(); } function goLive(uid) { return Promise.resolve({ startedAt: dayjs() }); } function stopLive(uid) { return Promise.resolve(); } const makeCountdownMachine = counter => Machine({ id: 'countdown', initial: 'running', context: { counter, }, states: { running: { invoke: { id: 'countdownInterval', src: () => callback => { const intervalId = setInterval(() => { callback('TICK'); }, 1000); return () => clearInterval(intervalId); }, }, on: { '': { target: 'done', cond: ctx => ctx.counter <= 0, }, TICK: { actions: ['decrement', 'notify'], }, }, }, done: { type: 'final', }, }, }, { actions: { decrement: assign({ counter: ctx => ctx.counter - 1 }), notify: sendParent(ctx => ({ type: 'COUNTDOWN.UPDATE', counter: ctx.counter, })), }, }); const webcastMachine = { id: 'webcast', initial: 'default', states: { default: { on: { '': [ { target: 'live', cond: 'isLive' }, { target: 'upcoming' }, ], }, }, upcoming: { initial: 'default', states: { default: { on: { '': [ { target: 'starting', cond: 'isStarting' }, { target: 'pre', cond: 'isPre' }, { target: 'preparing', cond: 'isPreparing' }, { target: 'idle' }, ], }, }, idle: { on: { PREPARE: 'preparing', }, }, preparing: { type: 'parallel', states: { pexip: { initial: 'default', states: { default: { on: { '': [ { target: 'ready', cond: 'isPexipReady' }, { target: 'preparing' }, ], }, }, preparing: { on: { PEXIP_VA_READY: { target: 'ready', actions: assign({ isPexipReady: true, }), }, }, }, ready: { type: 'final', } }, }, mediaLive: { initial: 'default', states: { default: { on: { '': [ { target: 'ready', cond: 'isMediaLiveReady' }, { target: 'preparing', cond: 'isPreparingMediaLive' }, { target: 'idle' }, ], }, }, idle: { on: { PREPARE_WEBCAST: 'preparing', }, }, preparing: { on: { ML_CHANNEL_READY: { target: 'ready', actions: assign({ isMediaLiveReady: true, }), }, }, }, ready: { type: 'final', }, }, }, }, on: { READY: { target: 'pre', cond: ctx => [ctx.isPexipReady, ctx.isMediaLiveReady].every(Boolean), }, }, }, pre: { on: { GO_LIVE: 'starting', }, }, starting: { invoke: { id: 'goLive', src: ctx => goLive(ctx.uid), }, on: { COUNTDOWN: { actions: 'setLiveContext', target: '#countdown', }, }, }, countdown: { id: 'countdown', invoke: { id: 'countdown', src: (ctx) => makeCountdownMachine(ctx.countdownCounter), onDone: '#live', }, on: { 'COUNTDOWN.UPDATE': { actions: 'setCountdownCounter', }, }, }, }, }, live: { id: 'live', initial: 'streaming', on: { 'ELAPSED.UPDATE': { actions: 'setElapsed', }, 'STATS.UPDATE': { actions: 'setViewingSessionStats', }, STOP_LIVE: 'after', }, invoke: [ // { id: 'elapsed', src: ctx => makeElapsedMachine(ctx.startedAt) }, // { id: 'stats', src: ctx => makeStatsMachine(ctx.uid) }, ], states: { playback: { on: { STOP_VIDEO: 'streaming', }, }, streaming: { on: { PLAY_VIDEO: 'playback', }, }, }, }, after: { actions: [ send('DONE', { to: 'elapsed' }), send('DONE', { to: 'stats' }), ], invoke: { id: 'stopLive', src: ctx => stopLive(ctx.uid), onDone: { target: '#controlRoom.done', }, }, }, }, }; const makeControlRoomMachine = event => Machine({ id: 'controlRoom', initial: 'default', context: { countdownCounter: undefined, elapsed: undefined, hasFinished: event.event_mode === 'after', isLive: event.event_mode === 'live', isMediaLiveReady: event.media_live_state === 'ready', isPexipReady: event.pexip_state === 'ready', isPreparing: event.event_mode === 'preparing', isPreparingMediaLive: event.media_live_state === 'preparing', isPre: event.event_mode === 'pre', isStarting: event.event_mode === 'starting', startedAt: event.start_timecode ?? null, uid: event.uid, viewingSessions: { online: 0, peak: 0, total: 0, }, }, states: { default: { on: { '': [ { target: 'done', cond: 'hasFinished' }, { target: 'interactive' }, ], }, }, interactive: { type: 'parallel', states: { webcast: { ...webcastMachine }, }, }, done: { type: 'final', }, }, }, { actions: { setCountdownCounter: assign({ countdownCounter: (ctx, event) => event.counter }), setElapsed: assign({ elapsed: (ctx, event) => event.elapsed }), setLiveContext: assign({ startedAt: (ctx, event) => dayjs(event.starts_at), countdownCounter: (ctx, event) => event?.countdown ?? 10, }), setViewingSessionStats: assign({ viewingSessions: (ctx, event) => event.viewingSessions }), }, guards: { hasFinished: ctx => ctx.hasFinished, isLive: ctx => ctx.isLive, isMediaLiveReady: ctx => ctx.isMediaLiveReady, isPexipReady: ctx => ctx.isPexipReady, isPreparing: ctx => ctx.isPreparing, isPreparingMediaLive: ctx => ctx.isPreparingMediaLive, isPre: ctx => ctx.isPre, isStarting: ctx => ctx.isStarting, }, }); makeControlRoomMachine({ uid: 'abc123', event_mode: 'preparing', pexip_state: 'ready', media_live_state: 'idle', });