Skip to content

Commit

Permalink
Realtime Video SDK with new interface (#886)
Browse files Browse the repository at this point in the history
* Realtime Video SDK with new interface

* room session with the new interface

* remove auto subscribe consumer

* fix unit tests for video and room session

* room member instance

* unit tests for room session member

* fix stack test

* room session playback realtime-api instance

* room session recording realtime-api instance

* room session stream realtime-api instance

* explicit methods for the realtime-api

* fix build issue

* separate workers for playback, recording and stream

* video playground with the new interface

* decorated promise for room session playback api

* decorated promise for room session recording api

* decorated promise for room session stream api

* fix unit test cases

* unit tests for decorated promises

* update video play ground with decorated promise

* fix e2e test case for the video

* fix unit test

* do not unsubscribe events

* fix unit test

* include changeset

* streaming getter for room session

* rename types
  • Loading branch information
iAmmar7 authored Oct 2, 2023
1 parent 02106e1 commit 73528c0
Show file tree
Hide file tree
Showing 64 changed files with 4,596 additions and 1,441 deletions.
33 changes: 33 additions & 0 deletions .changeset/fluffy-birds-yawn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
'@signalwire/realtime-api': major
'@signalwire/core': major
---

- New interface for the realtime-api Video SDK.
- Listen function with _video_, _room_, _playback_, _recording_, and _stream_ objects.
- Listen param with `room.play`, `room.startRecording`, and `room.startStream` functions.
- Decorated promise for `room.play`, `room.startRecording`, and `room.startStream` functions.

```js
import { SignalWire } from '@signalwire/realtime-api'

const client = await SignalWire({ project, token })

const unsub = await client.video.listen({
onRoomStarted: async (roomSession) => {
console.log('room session started', roomSession)

await roomSession.listen({
onPlaybackStarted: (playback) => {
console.log('plyaback started', playback)
}
})

// Promise resolves when playback ends.
await roomSession.play({ url: "http://.....", listen: { onEnded: () => {} } })
},
onRoomEnded: (roomSession) => {
console.log('room session ended', roomSession)
}
})
```
46 changes: 24 additions & 22 deletions internal/e2e-realtime-api/src/playwright/video.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { test, expect } from '@playwright/test'
import { uuid } from '@signalwire/core'
import { Video } from '@signalwire/realtime-api'
import { SignalWire } from '@signalwire/realtime-api'
import { createNewTabRoomSession } from './videoUtils'

test.describe('Video', () => {
test('should join the room and listen for events', async ({ browser }) => {
const videoClient = new Video.Client({
// @ts-expect-error
const client = await SignalWire({
host: process.env.RELAY_HOST,
project: process.env.RELAY_PROJECT as string,
token: process.env.RELAY_TOKEN as string,
Expand All @@ -18,19 +17,20 @@ test.describe('Video', () => {

const roomSessionCreated = new Map<string, any>()
const findRoomSessionsByPrefix = async () => {
const { roomSessions } = await videoClient.getRoomSessions()
const { roomSessions } = await client.video.getRoomSessions()
return roomSessions.filter((r) => r.name.startsWith(prefix))
}

videoClient.on('room.started', async (roomSession) => {
console.log('Room started', roomSession.id)
if (roomSession.name.startsWith(prefix)) {
roomSessionCreated.set(roomSession.id, roomSession)
}
})

videoClient.on('room.ended', async (roomSession) => {
console.log('Room ended', roomSession.id)
await client.video.listen({
onRoomStarted: (roomSession) => {
console.log('Room started', roomSession.id)
if (roomSession.name.startsWith(prefix)) {
roomSessionCreated.set(roomSession.id, roomSession)
}
},
onRoomEnded: (roomSession) => {
console.log('Room ended', roomSession.id)
},
})

const roomSessionsAtStart = await findRoomSessionsByPrefix()
Expand Down Expand Up @@ -72,24 +72,26 @@ test.describe('Video', () => {
for (let index = 0; index < roomSessionsRunning.length; index++) {
const rs = roomSessionsRunning[index]

await new Promise((resolve) => {
rs.on('recording.ended', noop)
rs.on('playback.ended', noop)
rs.on('room.updated', noop)
rs.on('room.subscribed', resolve)
await new Promise(async (resolve) => {
await rs.listen({
onRecordingEnded: noop,
onPlaybackEnded: noop,
onRoomUpdated: noop,
onRoomSubscribed: resolve,
})
})

await new Promise<void>(async (resolve) => {
rs.on('recording.ended', () => {
resolve()
await rs.listen({
onRecordingEnded: () => resolve(),
})
const { recordings } = await rs.getRecordings()
await Promise.all(recordings.map((r) => r.stop()))
})

await new Promise<void>(async (resolve) => {
rs.on('playback.ended', () => {
resolve()
await rs.listen({
onPlaybackEnded: () => resolve(),
})
const { playbacks } = await rs.getPlaybacks()
await Promise.all(playbacks.map((p) => p.stop()))
Expand Down
102 changes: 76 additions & 26 deletions internal/playground-realtime-api/src/with-events/index.ts
Original file line number Diff line number Diff line change
@@ -1,58 +1,108 @@
import { Video } from '@signalwire/realtime-api'
import { Video, SignalWire } from '@signalwire/realtime-api'

async function run() {
try {
const video = new Video.Client({
// @ts-expect-error
const client = await SignalWire({
host: process.env.HOST || 'relay.swire.io',
project: process.env.PROJECT as string,
token: process.env.TOKEN as string,
debug: {
logWsTraffic: true,
// logWsTraffic: true,
},
})

const roomSessionHandler = (room: Video.RoomSession) => {
console.log('Room started --->', room.id, room.name, room.members)
room.on('room.subscribed', (room) => {
console.log('Room Subscribed --->', room.id, room.members)
})
const unsubVideo = await client.video.listen({
onRoomStarted(room) {
console.log('🟢 onRoomStarted 🟢', room.id, room.name)
roomSessionHandler(room)
},
onRoomEnded(room) {
console.log('🔴 onRoomEnded 🔴', room.id, room.name)
},
})

room.on('member.updated', () => {
console.log('Member updated --->')
})
const roomSessionHandler = async (room: Video.RoomSession) => {
const unsubRoom = await room.listen({
onRoomSubscribed: (room) => {
console.log('onRoomSubscribed', room.id, room.name)
},
onRoomStarted: (room) => {
console.log('onRoomStarted', room.id, room.name)
},
onRoomUpdated: (room) => {
console.log('onRoomUpdated', room.id, room.name)
},
onRoomEnded: (room) => {
console.log('onRoomEnded', room.id, room.name)
},
onMemberJoined: async (member) => {
console.log('onMemberJoined --->', member.id, member.name)

room.on('member.joined', (member) => {
console.log('Member joined --->', member.id, member.name)
})
const play = await room
.play({
url: 'https://cdn.signalwire.com/default-music/welcome.mp3',
listen: {
onStarted: (playback) => {
console.log('onStarted', playback.id, playback.url)
},
onUpdated: (playback) => {
console.log('onUpdated', playback.id, playback.url)
},
onEnded: (playback) => {
console.log('onEnded', playback.id, playback.url)
},
},
})
.onStarted()
console.log('play', play.id)

setTimeout(async () => {
await play.pause()

room.on('member.left', (member) => {
console.log('Member left --->', member.id, member.name)
setTimeout(async () => {
await play.stop()
}, 5000)
}, 10000)
},
onMemberUpdated: (member) => {
console.log('onMemberUpdated', member.id, member.name)
},
onMemberTalking: (member) => {
console.log('onMemberTalking', member.id, member.name)
},
onMemberLeft: (member) => {
console.log('onMemberLeft', member.id, member.name)
},
onPlaybackStarted: (playback) => {
console.log('onPlaybackStarted', playback.id, playback.url)
},
onPlaybackUpdated: (playback) => {
console.log('onPlaybackUpdated', playback.id, playback.url)
},
onPlaybackEnded: (playback) => {
console.log('onPlaybackEnded', playback.id, playback.url)
},
})
}
video.on('room.started', roomSessionHandler)

video.on('room.ended', (room) => {
console.log('🔴 ROOOM ENDED 🔴', `${room}`, room.name)
})

video._session.on('session.connected', () => {
// @ts-expect-error
client.video._client.session.on('session.connected', () => {
console.log('SESSION CONNECTED!')
})

console.log('Client Running..')

const { roomSessions } = await video.getRoomSessions()
const { roomSessions } = await client.video.getRoomSessions()

roomSessions.forEach(async (room: any) => {
roomSessions.forEach(async (room: Video.RoomSession) => {
console.log('>> Room Session: ', room.id, room.displayName)
roomSessionHandler(room)

const r = await room.getMembers()
console.log('Members:', r)
// await room.removeAllMembers()

const { roomSession } = await video.getRoomSessionById(room.id)
const { roomSession } = await client.video.getRoomSessionById(room.id)
console.log('Room Session By ID:', roomSession.displayName)
})
} catch (error) {
Expand Down
18 changes: 10 additions & 8 deletions internal/stack-tests/src/video/app.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import { Video } from '@signalwire/realtime-api'
import { SignalWire } from '@signalwire/realtime-api'
import tap from 'tap'

async function run() {
try {
const video = new Video.Client({
// @ts-expect-error
const client = await SignalWire({
host: process.env.RELAY_HOST || 'relay.swire.io',
project: process.env.RELAY_PROJECT as string,
token: process.env.RELAY_TOKEN as string,
})

tap.ok(video.on, 'video.on is defined')
tap.ok(video.once, 'video.once is defined')
tap.ok(video.off, 'video.off is defined')
tap.ok(video.subscribe, 'video.subscribe is defined')
tap.ok(video.removeAllListeners, 'video.removeAllListeners is defined')
tap.ok(client.video, 'client.video is defined')
tap.ok(client.video.listen, 'client.video.listen is defined')
tap.ok(client.video.getRoomSessions, 'video.getRoomSessions is defined')
tap.ok(
client.video.getRoomSessionById,
'video.getRoomSessionById is defined'
)
tap.ok(client.disconnect, 'video.disconnect is defined')

process.exit(0)
} catch (error) {
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export type {
SDKActions,
ReduxComponent,
} from './redux/interfaces'
export type { SDKStore } from './redux'
export type { ToExternalJSONResult } from './utils'
export * as actions from './redux/actions'
export * as sagaHelpers from './redux/utils/sagaHelpers'
Expand Down
6 changes: 6 additions & 0 deletions packages/core/src/types/videoLayout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@ import { VideoPosition } from '..'
import type { CamelToSnakeCase, ToInternalVideoEvent } from './utils'

export type LayoutChanged = 'layout.changed'
export type OnLayoutChanged = 'onLayoutChanged'

/**
* List of public event names
*/
export type VideoLayoutEventNames = LayoutChanged

/**
* List of public listener names
*/
export type VideoLayoutListenerNames = OnLayoutChanged

/**
* List of internal events
* @internal
Expand Down
45 changes: 42 additions & 3 deletions packages/core/src/types/videoMember.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,15 @@ export type MemberTalking = 'member.talking'
export type MemberPromoted = 'member.promoted'
export type MemberDemoted = 'member.demoted'

// Generated by the SDK
/**
* Public listener types
*/
export type OnMemberJoined = 'onMemberJoined'
export type OnMemberLeft = 'onMemberLeft'
export type OnMemberUpdated = 'onMemberUpdated'
export type OnMemberTalking = 'onMemberTalking'
export type OnMemberPromoted = 'onMemberPromoted'
export type OnMemberDemoted = 'onMemberDemoted'

/**
* @privateRemarks
Expand All @@ -72,13 +80,25 @@ export type MemberDemoted = 'member.demoted'
* room.
*/
export type MemberListUpdated = 'memberList.updated'
export type OnMemberListUpdated = 'onMemberListUpdated'

/**
* See {@link MEMBER_UPDATED_EVENTS} for the full list of events.
*/
export type MemberUpdatedEventNames = typeof MEMBER_UPDATED_EVENTS[number]
export type MemberUpdatedEventNames = (typeof MEMBER_UPDATED_EVENTS)[number]
export type MemberTalkingStarted = 'member.talking.started'
export type MemberTalkingEnded = 'member.talking.ended'

export type OnMemberDeaf = 'onMemberDeaf'
export type OnMemberVisible = 'onMemberVisible'
export type OnMemberAudioMuted = 'onMemberAudioMuted'
export type OnMemberVideoMuted = 'onMemberVideoMuted'
export type OnMemberInputVolume = 'onMemberInputVolume'
export type OnMemberOutputVolume = 'onMemberOutputVolume'
export type OnMemberInputSensitivity = 'onMemberInputSensitivity'
export type OnMemberTalkingStarted = 'onMemberTalkingStarted'
export type OnMemberTalkingEnded = 'onMemberTalkingEnded'

/**
* Use `member.talking.started` instead
* @deprecated
Expand All @@ -97,6 +117,11 @@ export type MemberTalkingEventNames =
| MemberTalkingStart
| MemberTalkingStop

export type MemberTalkingListenerNames =
| OnMemberTalking
| OnMemberTalkingStarted
| OnMemberTalkingEnded

/**
* List of public events
*/
Expand All @@ -108,8 +133,22 @@ export type VideoMemberEventNames =
| MemberTalkingEventNames
| MemberListUpdated

export type VideoMemberListenerNames =
| OnMemberJoined
| OnMemberLeft
| OnMemberUpdated
| OnMemberDeaf
| OnMemberVisible
| OnMemberAudioMuted
| OnMemberVideoMuted
| OnMemberInputVolume
| OnMemberOutputVolume
| OnMemberInputSensitivity
| MemberTalkingListenerNames
| OnMemberListUpdated

export type InternalMemberUpdatedEventNames =
typeof INTERNAL_MEMBER_UPDATED_EVENTS[number]
(typeof INTERNAL_MEMBER_UPDATED_EVENTS)[number]

/**
* List of internal events
Expand Down
Loading

0 comments on commit 73528c0

Please sign in to comment.