We're adding a new way for developers to receive information about a meeting in real-time!
Introducing the "Meeting Observer", a one-way WebSocket connection that allows to boost your application's meeting controls.
It is easier to use than the existing WebHooks and even more powerful, since a lot more information can be observed.
If you want to skip the introduction and get your hands on, head over to the observer documentation.
Setup
The WebSocket connection is based on the rails actioncable protocol.
To connect to the observer, you need to authenticate with your API key. If you don't have it already, here's how to get your API key. Depending on your preferred programming language, you might already find a useful library for "Actioncable" or its successor "AnyCable".
The URL is the API's origin "https://api.eyeson.team", followed by "/rt" (for "realtime") and "room_id" as query parameter.
https://api.eyeson.team/rt?room_id=651b0c4c3ae7400ea35db31fs
room_id
is required, since each observer connects to a single meeting room and receives all events related to the given room_id
.
There are 3 ways to include the API key.
- "Authorization" header
- API key as WebSocket protocol
- Query parameter "api_key=<YOUR_API_KEY>"
And last but not least, you have to subscribe to the RoomChannel
to make it work.
As soon as the meeting shuts down, the connection is closed automatically.
Events
In contrast to WebHooks, the observer provides a lot more event types.
Besides room_update
and recording_update
you will get notified for
- snapshots,
- broadcasts,
- participants,
- chat messages,
- playbacks,
- and layout changes.
All events, including examples, are listed in the documentation.
Here are some examples to give you an idea.
Room update
All meeting room options and its ready and shutdown states are included in the room_update
event.
{
"type": "room_update",
"content": {
"id": "6576daef1f138900153d0762",
"name": "Observer demo",
"ready": true,
"started_at": "2023-12-11T09:48:32.031Z",
"shutdown": false,
"guest_token": "jCWzhzCWONkbIubUYF7PD3hP",
"options": {
"show_names": true,
"show_label": true,
"exit_url": null,
"recording_available": true,
"broadcast_available": true,
"layout_available": true,
"layout": "auto",
"reaction_available": true,
"suggest_guest_names": true,
"lock_available": true,
"kick_available": true,
"sfu_mode": "ptp",
"layout_users": null,
"layout_name": null,
"layout_map": null,
"voice_activation": false,
"custom_fields": {},
"widescreen": true,
"background_color": "#121212"
},
"participants": [],
"playbacks": [],
"presentation": null,
"broadcasts": [],
"recording": null
}
}
Example room_update
event where ready state is true
Participant update
You can find out when a participant joins or leaves the meeting in real-time. With the given participant ids, you can easily adjust the meeting layout, for example.
{
"type": "participant_update",
"participant": {
"id": "test@observer.com",
"room_id": "6576daef1f138900153d0762",
"name": "Observer",
"avatar": null,
"guest": false,
"online": true
}
}
Example participant_update
event with online state true
Chat message
Messages via messages API are forwarded to the observer. With this in place, your app can log the chat, or even react to it.
{
"type": "chat",
"content": "Hello world!",
"cid": "6576daf01f138900153d0764",
"user_id": "test@observer.com",
"created_at": "2023-12-11T09:59:33.892Z"
}
Example chat
event
Podium update
There's even one more incredibly useful event, called podium_update
, that provides all information about the current video podium.
The exact position and size of each of the layout boxes and its current content. So you know which user or playback is visible and on which position.
{
"type": "podium_update",
"podium": [
{
"user_id": null,
"play_id": "presentation-video",
"width": 1280,
"height": 600,
"left": 0,
"top": 0,
"z-index": 0
},
{
"user_id": "userA@myawesomeapp.com",
"play_id": null,
"width": 214,
"height": 120,
"left": 0,
"top": 600,
"z-index": 1
},
{
"user_id": "userB@myawesomeapp.com",
"play_id": null,
"width": 213,
"height": 120,
"left": 214,
"top": 600,
"z-index": 2
},
{
"user_id": "userC@myawesomeapp.com",
"play_id": null,
"width": 213,
"height": 120,
"left": 427,
"top": 600,
"z-index": 3
},
{
"user_id": "userD@myawesomeapp.com",
"play_id": null,
"width": 213,
"height": 120,
"left": 640,
"top": 600,
"z-index": 4
},
{
"user_id": "userE@myawesomeapp.com",
"play_id": null,
"width": 213,
"height": 120,
"left": 853,
"top": 600,
"z-index": 5
},
{
"user_id": "userF@myawesomeapp.com",
"play_id": null,
"width": 214,
"height": 120,
"left": 1066,
"top": 600,
"z-index": 6
}
]
}
Example podium_update
event with layout "present-upper-6" including a playback
The complete list of events is in the documentation.
Example code
Here are simplified examples that demonstrate the usage of the meeting observer.
- node
- go
import WebSocket from 'ws';
import { createCable } from '@anycable/core';
let cable = null;
const connect = (room_id) => {
const url = `https://api.eyeson.team/rt?room_id=${room_id}`;
cable = createCable(url, {
websocketImplementation: WebSocket,
websocketOptions: {
headers: {
'Authorization': process.env.API_KEY,
},
},
});
const channel = cable.subscribeTo('RoomChannel');
channel.on('message', onMessage);
channel.on('connect', onConnected);
channel.on('disconnect', onDisconnected);
};
const disconnect = () => {
if (cable) {
cable.disconnect();
cable = null;
}
};
const onMessage = message => {
if (message.type === 'room_update') {
if (message.content.ready === true) {
console.log('Meeting ready');
}
if (message.content.shutdown === true) {
console.log('Meeting shutdown - disconnect');
disconnect();
}
}
};
const onConnected = () => {
console.log('Connected');
};
const onDisconnected = ({ name, message, reason }) => {
console.log('Disconnected', name, message, reason);
if (reason === 'unauthorized') {
disconnect();
}
};
The observer is already integrated in the eyeson-go library.
client, _ := eyeson.NewClient(eyesonApiKey)
room, _ := client.Rooms.Join("standup meeting", "mike")
msgCh, _ := client.Observer.Connect(context.Background(), room.Data.Room.ID)
for {
select {
case msg, ok := <-msgCh:
if !ok {
fmt.Println("Channel closed. Probably disconnected")
return
}
fmt.Println("Received event type: ", msg.GetType())
switch m := msg.(type) {
case *eyeson.Chat:
fmt.Printf("Chat: %s - %s\n", m.ClientID, m.Content)
}
}
}
Contact
We'll be happy to hear from you!
If you have a question or want to share any feedback, do not hesitate to create a ticket on GitHub.