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 committed Nov 14, 2023
1 parent 1c2ffde commit f00f277
Show file tree
Hide file tree
Showing 63 changed files with 4,624 additions and 1,366 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)
}
})
```
109 changes: 61 additions & 48 deletions internal/e2e-realtime-api/src/playwright/video.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { test, expect } from '@playwright/test'
import { uuid } from '@signalwire/core'
import { Video } from '@signalwire/realtime-api'
import { SignalWire, Video } from '@signalwire/realtime-api'
import {
createRoomAndRecordPlay,
createRoomSession,
Expand All @@ -10,8 +10,7 @@ import { SERVER_URL } from '../../utils'

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 @@ -23,19 +22,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 @@ -77,47 +77,55 @@ 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()))
})

await new Promise<void>(async (resolve, reject) => {
rs.on('room.updated', (roomSession) => {
if (roomSession.locked === true) {
resolve()
} else {
reject(new Error('Not locked'))
}
const unsub = await rs.listen({
onRoomUpdated: async (roomSession) => {
if (roomSession.locked === true) {
resolve()
await unsub()
} else {
reject(new Error('Not locked'))
}
},
})
await rs.lock()
})

await new Promise<void>(async (resolve, reject) => {
rs.on('room.updated', (roomSession) => {
if (roomSession.locked === false) {
resolve()
} else {
reject(new Error('Still locked'))
}
const unsub = await rs.listen({
onRoomUpdated: async (roomSession) => {
if (roomSession.locked === false) {
resolve()
await unsub()
} else {
reject(new Error('Not locked'))
}
},
})
await rs.unlock()
})
Expand All @@ -132,32 +140,35 @@ test.describe('Video', () => {
test('should join the room and set hand raise priority', async ({
browser,
}) => {
const page = await browser.newPage()
await page.goto(SERVER_URL)
enablePageLogs(page, '[pageOne]')

// Create a realtime-api Video client
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,
debug: { logWsTraffic: true },
})

const page = await browser.newPage()
await page.goto(SERVER_URL)
enablePageLogs(page, '[pageOne]')

const prefix = uuid()
const roomName = `${prefix}-hand-raise-priority-e2e`

const findRoomSession = async () => {
const { roomSessions } = await videoClient.getRoomSessions()
const { roomSessions } = await client.video.getRoomSessions()
return roomSessions.filter((r) => r.name.startsWith(prefix))
}

// Listen for realtime-api event
videoClient.on('room.started', (room) => {
room.on('room.updated', (room) => {
console.log('>> room.updated', room.name)
})
await client.video.listen({
onRoomStarted: async (roomSession) => {
console.log('>> room.started', roomSession.name)
await roomSession.listen({
onRoomUpdated: (room) => {
console.log('>> room.updated', room.name)
},
})
},
})

// Room length should be 0 before start
Expand Down Expand Up @@ -203,8 +214,10 @@ test.describe('Video', () => {
// Set the hand raise prioritization via Node SDK
const roomSessionNodeUpdated = await new Promise<Video.RoomSession>(
async (resolve, _reject) => {
roomSessionNode.on('room.updated', (room) => {
resolve(room)
await roomSessionNode.listen({
onRoomUpdated: (room) => {
resolve(room)
},
})
await roomSessionNode.setPrioritizeHandraise(true)
}
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
Loading

0 comments on commit f00f277

Please sign in to comment.